Web Smart Card API

Unofficial Draft

More details about this document
Latest published version:
https://www.w3.org/web-smart-card/
Latest editor's draft:
https://wicg.github.io/web-smart-card/
History:
Commit history
Editor:
(Google)
Feedback:
GitHub WICG/web-smart-card (pull requests, new issue, open issues)

Abstract

The objective of this API is to enable smart card (PC/SC) applications to move to the Web Platform. It gives them access to the PC/SC implementation (and card reader drivers) available in the host OS. There is also a companion explainer document.

Status of This Document

This document is a draft of a potential specification. It has no official standing of any kind and does not represent the support or consensus of any standards organization.

1. Extensions to the Navigator interface

WebIDL[Exposed=Window, SecureContext]
partial interface Navigator {
  [SameObject] readonly attribute SmartCardResourceManager smartCard;
};

1.1 smartCard attribute

When getting, the smartCard attribute always returns the same instance of the SmartCardResourceManager object.

2. Extensions to the WorkerNavigator interface

WebIDL[Exposed=(DedicatedWorker, SharedWorker), SecureContext]
partial interface WorkerNavigator {
  [SameObject] readonly attribute SmartCardResourceManager smartCard;
};

2.1 smartCard attribute

When getting, the smartCard attribute always returns the same instance of the SmartCardResourceManager object.

3. SmartCardResourceManager interface

WebIDL[Exposed=(DedicatedWorker, SharedWorker, Window), SecureContext]
interface SmartCardResourceManager {
  Promise<SmartCardContext> establishContext();
};

Methods on this interface complete asynchronously, queuing work on the smart card task source.

3.1 establishContext() method

Requests a PC/SC context from the platform's PC/SC stack.

The establishContext() method steps are:
  1. If this's relevant global object's associated Document is not allowed to use the policy-controlled feature named "smart-card", throw a "SecurityError" DOMException.
  2. Let promise be a new promise.
  3. Run the following steps in parallel:
    1. Let resourceManager be a new instance of the platform's [PCSC5] RESOURCEMANAGER class.
    2. Invoke the EstablishContext method of resourceManager with a "system" Scope parameter.
    3. If the returned RESPONSECODE is not SCARD_S_SUCCESS, perform the following steps:
      1. Destroy resourceManager.
      2. Queue a global task on the relevant global object of this using the smart card task source to reject promise with a corresponding exception.
    4. Otherwise, perform the following steps:
      1. Let context be a new SmartCardContext whose [[resourceManager]] internal slot is set to resourceManager.
      2. Queue a global task on the relevant global object of this using the smart card task source to resolve promise with context.
  4. Return promise.

4. SmartCardContext interface

A context for communicating with the PC/SC resource manager.

WebIDL[Exposed=(DedicatedWorker, SharedWorker, Window), SecureContext]
interface SmartCardContext {
  Promise<sequence<DOMString>> listReaders();

  Promise<sequence<SmartCardReaderStateOut>> getStatusChange(
      sequence<SmartCardReaderStateIn> readerStates,
      optional SmartCardGetStatusChangeOptions options = {});

  Promise<SmartCardConnectResult> connect(
      DOMString readerName,
      SmartCardAccessMode accessMode,
      optional SmartCardConnectOptions options = {});
};

Instances of SmartCardContext are created with the internal slots described in the following table:

Internal slot Initial value Description (non-normative)
[[resourceManager]] null The platform's [PCSC5] RESOURCEMANAGER to be used.
[[operationInProgress]] false Whether there is an ongoing PC/SC operation in this context.
[[connections]] An empty ordered set The existing SmartCardConnections created by this context.
[[tracker]] null A [PCSC5] SCARDTRACK instance.
[[signal]] null The AbortSignal of the outstanding getStatusChange() call, if any.

4.1 listReaders() method

The listReaders() method steps are:

  1. Let promise be a new promise.
  2. If this.[[operationInProgress]] is true, reject promise with a "InvalidStateError" DOMException and return promise.
  3. Set this.[[operationInProgress]] to true.
  4. Run the following steps in parallel:
    1. Let resourceQuery be a new instance of the platform's [PCSC5] RESOURCEQUERY class, with this.[[resourceManager]] as its constructor input parameter.
    2. Let groups be the platform's [PCSC5] STR[] containing the list of group names that is equivalent to "all readers in the system" in that platform.
      Note
    3. Let pcscReaders be an empty STR[].
    4. Invoke the ListReaders method of resourceQuery with groups as input and pcscReaders as output parameters.
    5. Let responseCode be the returned RESPONSECODE.
    6. Destroy resourceQuery.
    7. Queue a global task on the relevant global object of this using the smart card task source which performs the following steps:
      1. Clear the operationInProgress of this.
      2. If responseCode is not SCARD_S_SUCCESS:
        1. If responseCode is SCARD_E_NO_READERS_AVAILABLE, resolve promise with an empty sequence of DOMString.
          Note
        2. Otherwise, reject promise with an exception corresponding to responseCode.
      3. Otherwise, resolve promise with a sequence of DOMString equivalent to pcscReaders.
  5. Return promise.

4.2 getStatusChange() method

The getStatusChange(readerStates, options) method steps are:

  1. Let promise be a new promise.
  2. If this.[[operationInProgress]] is true, reject promise with a "InvalidStateError" DOMException and return promise.
  3. If options["signal"] exists, run the following steps:
    1. Let signal be options["signal"].
    2. If signal is aborted, reject promise with signal's abort reason and return promise.
    3. Set this.[[signal]] to signal
    4. Add the Cancel the outstanding GetStatusChange algorithm to signal.
  4. Let pcscTimeout be a [PCSC5] DWORD set to [PCSC5] INFINITE.
  5. If options["timeout"] exists, set pcscTimeout to options["timeout"].
  6. Let pcscReaderStates be a [PCSC5] SCARD_READERSTATE[] corresponding to readerStates.
  7. Set this.[[operationInProgress]] to true.
  8. Set this.[[tracker]] to a new instance of the platform's [PCSC5] SCARDTRACK class, with this.[[resourceManager]] as its constructor input parameter.
  9. Run the following steps in parallel:
    1. Call this.[[tracker]].GetStatusChange() with pcscReaderStates and pcscTimeout as input parameters.
    2. Let responseCode be the returned [PCSC5] RESPONSECODE.
    3. Queue a global task on the relevant global object of this using the smart card task source which performs the following steps:
      1. Set this.[[tracker]] to null.
      2. Clear the operationInProgress of this.
      3. Let abortReason be undefined.
      4. If this.[[signal]] is not null, run the following steps:
        1. If this.[[signal]] is aborted then set abortReason to this.[[signal]]'s abort reason.
        2. Remove the cancel the outstanding GetStatusChange algorithm from this.[[signal]].
        3. Set this.[[signal]] to null.
      5. If responseCode is not SCARD_S_SUCCESS, run the following steps:
        1. If responseCode is SCARD_E_CANCELLED and abortReason is not undefined then reject promise with abortReason.
        2. Otherwise, reject promise with an exception corresponding to responseCode.
        3. Return.
      6. Let readerStatesOut be a sequence of SmartCardReaderStateOut corresponding to pcscReaderStates.
      7. Resolve promise with readerStatesOut.
  10. Return promise.

4.2.1 SmartCardReaderStateIn dictionary

WebIDLdictionary SmartCardReaderStateIn {
  required DOMString readerName;
  required SmartCardReaderStateFlagsIn currentState;
  unsigned long currentCount;
};
readerName member
Name of the smart card reader.
currentState member
The current state of that smart card reader as known by the application.
currentCount member
The current number of card insertion and removal events in this reader, as known by the application.

Given a sequence of SmartCardReaderStateIn named readerStates, a corresponding [PCSC5] SCARD_READERSTATE[] is created with the following steps:

  1. Let pcscReaderStates be an empty SCARD_READERSTATE[].
  2. For each stateIn in readerStates:
    1. Let pcscState be a SCARD_READERSTATE.
    2. Set pcscState.Reader to stateIn["readerName"].
    3. Set pcscState.CurrentState to the DWORD corresponding to stateIn["currentState"].
    4. If stateIn["currentCount"] exists, set the high word of pcscState.CurrentState to stateIn["currentCount"].
    5. Set pcscState.EventState to zero.
    6. Append pcscState to pcscReaderStates.
  3. Return pcscReaderStates.
4.2.1.1 SmartCardReaderStateFlagsIn dictionary
WebIDLdictionary SmartCardReaderStateFlagsIn {
  boolean unaware = false;
  boolean ignore = false;
  boolean unavailable = false;
  boolean empty = false;
  boolean present = false;
  boolean exclusive = false;
  boolean inuse = false;
  boolean mute = false;
  boolean unpowered = false;
};
unaware member
The application is unaware of the current state, and would like to know.
ignore member
The application is not interested in this reader, and it should not be considered during monitoring operations.
unavailable member
The application believes that this reader is not available for use.
empty member
The application believes that there is not a card in the reader.
present member
The application believes that there is a card in the reader.
exclusive member
The application believes that the card in the reader is allocated for exclusive use by another application.
inuse member
The application believes that the card in the reader is in use by one or more other applications, but may be connected to in shared mode.
mute member
The application believes that there is an unresponsive card in the reader.
unpowered member
The application believes that the card in the reader has not been powered up.

The [PCSC5] DWORD corresponding to a given SmartCardReaderStateFlagsIn is created with the following steps:

  1. Let flagsIn be the given SmartCardReaderStateFlagsIn.
  2. Let pcscFlags be a DWORD set to zero.
  3. If flagsIn["unaware"] is true, add [PCSC5] SCARD_STATE_UNAWARE to pcscFlags.
  4. If flagsIn["ignore"] is true, add [PCSC5] SCARD_STATE_IGNORE to pcscFlags.
  5. If flagsIn["unavailable"] is true, add [PCSC5] SCARD_STATE_UNAVAILABLE to pcscFlags.
  6. If flagsIn["empty"] is true, add [PCSC5] SCARD_STATE_EMPTY to pcscFlags.
  7. If flagsIn["present"] is true, add [PCSC5] SCARD_STATE_PRESENT to pcscFlags.
  8. If flagsIn["exclusive"] is true, add [PCSC5] SCARD_STATE_EXCLUSIVE to pcscFlags.
  9. If flagsIn["inuse"] is true, add [PCSC5] SCARD_STATE_INUSE to pcscFlags.
  10. If flagsIn["mute"] is true, add SCARD_STATE_MUTE to pcscFlags.
  11. If flagsIn["unpowered"] is true, add SCARD_STATE_UNPOWERED to pcscFlags.
  12. Return pcscFlags.
Note

4.2.2 SmartCardReaderStateOut dictionary

The actual state of a smart card reader.

WebIDLdictionary SmartCardReaderStateOut {
  required DOMString readerName;
  required SmartCardReaderStateFlagsOut eventState;
  required unsigned long eventCount;
  ArrayBuffer answerToReset;
};
readerName member
Name of the smart card reader.
eventState member
The actual state of that smart card reader.
eventCount member
The actual number of card insertion and removal events in this reader.
answerToReset member
The inserted card's [ISO7186-3] Answer To Reset (ATR), if applicable.

Given a [PCSC5] SCARD_READERSTATE[] named pcscReaderStates, a corresponding sequence of SmartCardReaderStateOut is created with the following steps:

  1. Let readerStatesOut be an empty sequence of SmartCardReaderStateOut.
  2. For each pcscState in pcscReaderStates:
    1. Let stateOut be a SmartCardReaderStateOut.
    2. Set stateOut["readerName"] to pcscState.Reader.
    3. Set stateOut["eventState"] to the SmartCardReaderStateFlagsOut dictionary corresponding to pcscState.EventState.
    4. Set stateOut["eventCount"] to the high word of pcscState.EventState.
      Note: Number of card insertion and removal events
    5. If the platform's SCARD_READERSTATE structure has a member containing the card's [ISO7186-3] Answer To Reset, set stateOut["answerToReset"] to that value.
    6. Append stateOut to readerStatesOut.
  3. Return readerStatesOut.
4.2.2.1 SmartCardReaderStateFlagsOut dictionary
WebIDLdictionary SmartCardReaderStateFlagsOut {
  boolean ignore = false;
  boolean changed = false;
  boolean unavailable = false;
  boolean unknown = false;
  boolean empty = false;
  boolean present = false;
  boolean exclusive = false;
  boolean inuse = false;
  boolean mute = false;
  boolean unpowered = false;
};
ignore member
The application requested that this reader be ignored.
changed member
There is a difference between the state input by the calling application, and the actual state.
unavailable member
This reader is not available for use.
unknown member
The reader name given by the application is not known.
empty member
There is no card in the reader.
present member
There is a card in the reader.
exclusive member
The card in the reader is allocated for exclusive use by another application.
inuse member
The card in the reader is in use by one or more other applications, but may be connected to in shared mode.
mute member
There is an unresponsive card in the reader.
unpowered member
The card in the reader has not been powered up.

Given a [PCSC5] DWORD named pcscFlags, a corresponding SmartCardReaderStateFlagsOut dictionary is created with the following steps:

  1. Let flagsOut be a SmartCardReaderStateFlagsOut dictionary with default members.
  2. If pcscFlags has [PCSC5] SCARD_STATE_IGNORE, set flagsOut["ignore"] to true.
  3. If pcscFlags has [PCSC5] SCARD_STATE_CHANGED, set flagsOut["changed"] to true.
  4. If pcscFlags has [PCSC5] SCARD_STATE_UNAVAILABLE, set flagsOut["unavailable"] to true.
  5. If pcscFlags has [PCSC5] SCARD_STATE_UNKNOWN, set flagsOut["unknown"] to true.
  6. If pcscFlags has [PCSC5] SCARD_STATE_EMPTY, set flagsOut["empty"] to true.
  7. If pcscFlags has [PCSC5] SCARD_STATE_PRESENT, set flagsOut["present"] to true.
  8. If pcscFlags has [PCSC5] SCARD_STATE_EXCLUSIVE, set flagsOut["exclusive"] to true.
  9. If pcscFlags has [PCSC5] SCARD_STATE_INUSE, set flagsOut["inuse"] to true.
  10. If pcscFlags has SCARD_STATE_MUTE, set flagsOut["mute"] to true.
  11. If pcscFlags has SCARD_STATE_UNPOWERED, set flagsOut["unpowered"] to true.
  12. Return flagsOut.
Note

4.2.3 SmartCardGetStatusChangeOptions dictionary

WebIDLdictionary SmartCardGetStatusChangeOptions {
  DOMHighResTimeStamp timeout;
  AbortSignal signal;
};
timeout member
Timeout parameter for the GetStatusChange() [PCSC5] method. If not specified, a timeout value of INFINITE (which is defined system dependent) will be used.
signal member
When triggered, the platform's [PCSC5] Cancel() method is called.

4.3 connect() method

The connect(readerName, accessMode, options) method steps are:

  1. Let promise be a new promise.
  2. If this.[[operationInProgress]] is true, reject promise with a "InvalidStateError" DOMException and return promise.
  3. Set this.[[operationInProgress]] to true.
  4. Run the following steps in parallel:
    1. Let accessFlags be a [PCSC5] DWORD corresponding to accessMode.
    2. Let protocolFlags be a DWORD set to 0.
    3. If options["preferredProtocols"] exists, set protocolFlags to its corresponding flags.
    4. Let activeProtocol be a DWORD set to 0.
    5. Let comm be a new instance of the platform's [PCSC5] SCARDCOMM class, with this.[[resourceManager]] as its constructor parameter.
    6. Call comm.Connect() with readerName, accessFlags and protocolFlags as input and activeProtocol as output parameters.
    7. Let responseCode be the returned RESPONSECODE.
    8. Queue a global task on the relevant global object of this using the smart card task source which performs the following steps:
      1. Clear the operationInProgress of this.
      2. If responseCode is not SCARD_S_SUCCESS:
        1. Destroy comm.
        2. Reject promise with an exception corresponding to responseCode and abort these steps.
      3. Let result be an empty SmartCardConnectResult dictionary.
      4. Let connection be a new SmartCardConnection.
      5. Append connection to this.[[connections]].
      6. Set connection.[[comm]] to comm.
      7. Set connection.[[context]] to this.
      8. Set connection.[[activeProtocol]] to activeProtocol.
      9. Set result["connection"] to connection.
      10. If activeProtocol is a valid protocol value, set result["activeProtocol"] to the corresponding SmartCardProtocol.
      11. Resolve promise with result.
  5. Return promise.

4.3.1 SmartCardProtocol enum

WebIDLenum SmartCardProtocol {
  "raw",
  "t0",
  "t1"
};
raw
"Raw" mode. May be used to support arbitrary data exchange protocols for special-purpose requirements. Corresponds to a [PCSC5] SCARD_PROTOCOL_RAW DWORD.
t0
[ISO7186-3] T=0. Asynchronous half duplex character transmission protocol. Corresponds to a [PCSC5] SCARD_PROTOCOL_T0 DWORD.
t1
[ISO7186-3] T=1. Asynchronous half duplex block transmission protocol. Corresponds to a [PCSC5] SCARD_PROTOCOL_T1 DWORD.

A [PCSC5] DWORD is a valid protocol value if it is either [PCSC5] SCARD_PROTOCOL_T0, [PCSC5] SCARD_PROTOCOL_T1 or [PCSC5] SCARD_PROTOCOL_RAW.

Given a sequence of SmartCardProtocol named protocols, a [PCSC5] DWORD with the corresponding flags is created with the following steps:

  1. Let flags be a DWORD set to 0.
  2. For each protocol in protocols, add the corresponding DWORD of protocol to flags.
  3. Return flags.

4.3.2 SmartCardConnectResult dictionary

WebIDLdictionary SmartCardConnectResult {
  required SmartCardConnection connection;
  SmartCardProtocol activeProtocol;
};
connection member
An interface to the connection created.
activeProtocol member
The protocol actually in use.

4.3.3 SmartCardAccessMode enum

WebIDLenum SmartCardAccessMode {
  "shared",
  "exclusive",
  "direct"
};
shared
Application is willing to share access to card with other applications.
exclusive
Application requires exclusive access to the card.
direct
Application requires connection to reader whether or not card is present. Implies exclusive access.

Given a SmartCardAccessMode enum named accessMode, a corresponding [PCSC5] DWORD is created with the following steps:

  1. Let dword be a DWORD set to 0.
  2. If accessMode is "shared", set dword to [PCSC5] SCARD_SHARE_SHARED.
  3. If accessMode is "exclusive", set dword to [PCSC5] SCARD_SHARE_EXCLUSIVE.
  4. If accessMode is "direct", set dword to [PCSC5] SCARD_SHARE_DIRECT.
  5. Return dword.

4.3.4 SmartCardConnectOptions dictionary

WebIDLdictionary SmartCardConnectOptions {
  sequence<SmartCardProtocol> preferredProtocols;
};
preferredProtocols
Card communication protocols that may be used.

4.4 Auxiliary algorithms and definitions

To clear the operationInProgress of a SmartCardContext context, perform the following steps:

  1. Assert: context.[[operationInProgress]] is true.
  2. Set context.[[operationInProgress]] to false.
  3. For each connection of context.[[connections]]:
    1. End any settled transaction of connection.
    2. If context.[[operationInProgress]] is true, abort there steps.
Note

The cancel the outstanding GetStatusChange algorithm steps are:

  1. Call this.[[tracker]].Cancel().

The high word of a [PCSC5] DWORD is the result of an unsigned right shift of 16 bits on that DWORD.

To set the high word of a [PCSC5] DWORD named dword to a given number n, perform the following steps.

  1. Set dword to dword bitwise AND 0xFFFF.
  2. Let shiftedN be the result of a left shift of 16 bits on n.
  3. Set dword to dword bitwise OR shiftedN.

To add a flag f to a [PCSC5] DWORD flags, set flags to flags bitwise OR f.

A [PCSC5] DWORD flags has a flag f if flags bitwise AND f is f.

5. SmartCardConnection interface

WebIDL[Exposed=(DedicatedWorker, SharedWorker, Window), SecureContext]
interface SmartCardConnection {
  Promise<undefined> disconnect(optional SmartCardDisposition disposition = "leave");

  Promise<ArrayBuffer> transmit(BufferSource sendBuffer,
      optional SmartCardTransmitOptions options = {});

  Promise<undefined> startTransaction(SmartCardTransactionCallback transaction,
      optional SmartCardTransactionOptions options = {});

  Promise<SmartCardConnectionStatus> status();

  Promise<ArrayBuffer> control([EnforceRange] unsigned long controlCode,
      BufferSource data);

  Promise<ArrayBuffer> getAttribute([EnforceRange] unsigned long tag);
  Promise<undefined> setAttribute([EnforceRange] unsigned long tag, BufferSource value);
};

callback SmartCardTransactionCallback = Promise<SmartCardDisposition?> ();

Instances of SmartCardConnection are created with the internal slots described in the following table:

Internal slot Initial value Description (non-normative)
[[comm]] null The platform's [PCSC5] SCARDCOMM to be used.
[[context]] null The SmartCardContext that created this instance.
[[activeProtocol]] 0 The active protocol DWORD, as returned by the platform's [PCSC5] implementation.
[[transactionState]] null Holds the state of an ongoing transaction started with startTransaction(), if any.

5.1 disconnect() method

The disconnect(disposition) method steps are:

  1. Let promise be a new promise.
  2. If this.[[context]].[[operationInProgress]] is true, reject promise with a "InvalidStateError" DOMException and return promise.
  3. If this.[[comm]] is null, reject promise with a "InvalidStateError" DOMException and return promise.
  4. Set this.[[context]].[[operationInProgress]] to true.
  5. Run the following steps in parallel:
    1. Call this.[[comm]].Disconnect() with a DWORD corresponding to disposition as input parameter.
    2. Let responseCode be the returned RESPONSECODE.
    3. Queue a global task on the relevant global object of this using the smart card task source which performs the following steps:
      1. Clear the operationInProgress of this.[[context]].
      2. If responseCode is not SCARD_S_SUCCESS, reject promise with an exception corresponding to responseCode and abort these steps.
      3. Destroy this.[[comm]].
      4. Set this.[[comm]] to null.
      5. Resolve promise.
  6. Return promise.

5.1.1 SmartCardDisposition enum

WebIDLenum SmartCardDisposition {
  "leave",
  "reset",
  "unpower",
  "eject"
};
leave
Don't alter card state. Corresponds to a [PCSC5] SCARD_LEAVE_CARD DWORD.
reset
Reset the card. Corresponds to a [PCSC5] SCARD_RESET_CARD DWORD.
unpower
Unpower and terminate access to the card. Corresponds to a [PCSC5] SCARD_UNPOWER_CARD DWORD.
eject
Eject the card from the reader. Corresponds to a [PCSC5] SCARD_EJECT_CARD DWORD.

5.2 transmit() method

The transmit(sendBuffer, options) method steps are:

  1. Let promise be a new promise.
  2. If this.[[context]].[[operationInProgress]] is true, reject promise with a "InvalidStateError" DOMException and return promise.
  3. If this.[[comm]] is null, reject promise with a "InvalidStateError" DOMException and return promise.
  4. Let protocol be a [PCSC5] DWORD set to this.[[activeProtocol]].
  5. If options["protocol"] exists, set protocol to the DWORD corresponding to options["protocol"].
  6. If protocol is not a valid protocol value, reject promise with a "InvalidStateError" DOMException and return promise.
  7. Set this.[[context]].[[operationInProgress]] to true.
  8. Let sendPci be the platform's [PCSC5] SCARD_IO_HEADER corresponding to this.[[activeProtocol]].
  9. Let pcscSendBuffer be a [PCSC5] BYTE[] containing sendBuffer.
  10. Let recvPci be the platform's SCARD_IO_HEADER equivalent of empty or null.
  11. Let recvBuffer be a BYTE[] big enough to hold the largest [ISO7186-3] extended response APDU (65538 bytes).
  12. Let recvLength be a DWORD set to 0.
  13. Run the following steps in parallel:
    1. Call this.[[comm]].Transmit() with sendPci, pcscSendBuffer, recvPci, recvBuffer and recvLength as arguments.
    2. Let responseCode be the returned RESPONSECODE.
    3. Queue a global task on the relevant global object of this using the smart card task source which performs the following steps:
      1. Clear the operationInProgress of this.[[context]].
      2. If responseCode is not SCARD_S_SUCCESS, reject promise with an exception corresponding to responseCode and abort these steps.
      3. Resolve promise with an ArrayBuffer containing the first recvLength bytes of recvBuffer.
  14. Return promise.

5.2.1 SmartCardTransmitOptions dictionary

WebIDLdictionary SmartCardTransmitOptions {
  SmartCardProtocol protocol;
};
protocol member
The protocol to be used in the transmission.

5.3 startTransaction() method

The startTransaction(transaction, options) method steps are:

  1. Let promise be a new promise.
  2. If this.[[context]].[[operationInProgress]] is true, reject promise with a "InvalidStateError" DOMException and return promise.
  3. If this.[[comm]] is null, reject promise with a "InvalidStateError" DOMException and return promise.
  4. If this.[[transactionState]] is not null, reject promise with a "InvalidStateError" DOMException and return promise.
  5. Let signal be an AbortSignal set to null.
  6. If options["signal"] exists, run the following steps:
    1. If options["signal"] is aborted, reject promise with options["signal"]'s abort reason and return promise.
    2. Set signal to options["signal"].
    3. Add the cancel algorithm to signal.
  7. Set this.[[context]].[[operationInProgress]] to true.
  8. Run the following steps in parallel:
    1. Call this.[[comm]].BeginTransaction().
    2. Let responseCode be the returned [PCSC5] RESPONSECODE.
    3. Queue a global task on the relevant global object of this using the smart card task source to process the result of a BeginTransaction with this, responseCode, signal, transaction and promise.
  9. Return promise.

5.3.1 SmartCardTransactionOptions dictionary

WebIDLdictionary SmartCardTransactionOptions {
  AbortSignal signal;
};
signal member
When triggered, the platform's [PCSC5] Cancel() method is called.

5.3.2 Auxiliary algorithms and definitions

A transaction state is a struct with the following items:

Item Description (non-normative)
pendingDisposition If set, it means once the ongoing PC/SC operation finishes [PCSC5] EndTransaction() should be called with this value as the SmartCardDisposition parameter.
pendingException The exception to be used when rejecting promise.
promise The pending Promise returned by a startTransaction() call.

To process the result of a BeginTransaction given a SmartCardConnection connection, a [PCSC5] RESPONSECODE responseCode, an AbortSignal signal, a SmartCardTransactionCallback transaction and a Promise promise, perform the following steps:

  1. Clear the operationInProgress of connection.[[context]].
  2. Let abortReason be undefined.
  3. If signal is not null:
    1. Remove the cancel algorithm from signal.
    2. If signal is aborted then set abortReason to signal's abort reason.
  4. If responseCode is not SCARD_S_SUCCESS:
    1. If responseCode is SCARD_E_CANCELLED and abortReason is not undefined then reject promise with abortReason.
    2. Otherwise, reject promise with an exception corresponding to responseCode.
    3. Return.
  5. Let transactionState be a new transaction state with its promise item set to promise.
  6. Set connection.[[transactionState]] to transactionState.
  7. Let callbackPromise be the result of invoking transaction.
  8. React to callbackPromise:

To end the transaction of a SmartCardConnection connection with a SmartCardDisposition disposition, perform the following steps:

  1. Assert: connection.[[context]].[[operationInProgress]] is false.
  2. Assert: connection.[[transactionState]] is not null.
  3. Assert: connection.[[transactionState]]'s pendingDisposition is null.
  4. Let transactionPromise be connection.[[transactionState]]'s promise.
  5. If connection.[[comm]] is null:
    1. Reject transactionPromise with a "InvalidStateError" DOMException.
    2. Set connection.[[transactionState]] to null.
    3. Return.
  6. Set connection.[[context]].[[operationInProgress]] to true.
  7. Run the following steps in parallel:
    1. Call this.[[comm]].EndTransaction() with a DWORD corresponding to disposition as input parameter.
    2. Let responseCode be the returned [PCSC5] RESPONSECODE.
    3. Queue a global task on the relevant global object of this using the smart card task source which performs the following steps:
      1. Clear the operationInProgress of connection.[[context]].
      2. Let exception be connection.[[transactionState]]'s pendingException.
      3. If exception is null, perform the following steps:
        1. If responseCode is SCARD_S_SUCCESS, resolve transactionPromise.
        2. Otherwise, reject transactionPromise with an exception corresponding to responseCode.
      4. Otherwise, reject transactionPromise with exception.
      5. Set connection.[[transactionState]] to null.

To end any settled transaction of a SmartCardConnection connection, perform the following steps:

  1. If connection.[[transactionState]] is null, abort these steps.
  2. Let disposition be connection.[[transactionState]]'s pendingDisposition.
  3. If disposition is null, abort these steps.
  4. Set connection.[[transactionState]]'s pendingDisposition to null.
  5. End the transaction of connection with disposition.

To cancel outstanding [PCSC5] SCARDCOMM operations, call this.[[comm]].Cancel().

5.4 status() method

The status() method steps are:

  1. Let promise be a new promise.
  2. If this.[[context]].[[operationInProgress]] is true, reject promise with a "InvalidStateError" DOMException and return promise.
  3. If this.[[comm]] is null, reject promise with a "InvalidStateError" DOMException and return promise.
  4. Set this.[[context]].[[operationInProgress]] to true.
  5. Run the following steps in parallel:
    1. Let pcscReader be an empty STR[].
    2. Let pcscState be a [PCSC5] DWORD set to 0.
    3. Let activeProtocol be a [PCSC5] DWORD set to 0.
    4. Let pcscAtr be a BYTE[] big enough to hold any [ISO7186-3] Answer To Reset (ATR).
    5. Call this.[[comm]].Status() with pcscReader, pcscState, activeProtocol and pcscAtr as output parameters.
    6. Let responseCode be the returned RESPONSECODE.
    7. Queue a global task on the relevant global object of this using the smart card task source which performs the following steps:
      1. Clear the operationInProgress of this.[[context]].
      2. If responseCode is not SCARD_S_SUCCESS, reject promise with an exception corresponding to responseCode and abort these steps.
      3. Let state be a SmartCardConnectionState corresponding to pcscState and activeProtocol.
      4. If state is undefined, reject promise with an "UnknownError" DOMException and abort these steps.
      5. Let status be a new SmartCardConnectionStatus.
      6. Set status["readerName"] to pcscReader.
      7. Set status["state"] to state.
      8. Set status["answerToReset"] to an ArrayBuffer with the bytes that were written to pcscAtr.
      9. Resolve promise with status.
  6. Return promise.

5.4.1 SmartCardConnectionStatus dictionary

WebIDLdictionary SmartCardConnectionStatus {
  required DOMString readerName;
  required SmartCardConnectionState state;
  ArrayBuffer answerToReset;
};
readerName member
Name of the connected reader.
state member
Current state of the connection.
answerToReset member
The answer to reset (ATR) string from the card, if applicable.
5.4.1.1 SmartCardConnectionState enum
WebIDLenum SmartCardConnectionState {
  "absent",
  "present",
  "swallowed",
  "powered",
  "negotiable",
  "t0",
  "t1",
  "raw"
};
absent
There is no card in the reader.
present
There is a card in the reader, but it has not been moved into position for use.
swallowed
There is a card in the reader in position for use. The card is not powered.
powered
Power is being provided to the card, but the reader driver is unaware of the mode of the card.
negotiable
The card has been reset and is awaiting PTS (protocol type selection) negotiation.
t0
The card is in [ISO7186-3] T=0 protocol mode and a new protocol may not be negotiated.
t1
The card is in [ISO7186-3] T=1 protocol mode and a new protocol may not be negotiated.
raw
The card is in raw protocol mode and a new protocol may not be negotiated.

Given a [PCSC5] DWORD pcscState and a DWORD activeProtocol, a corresponding SmartCardConnectionState is created with the following steps:

  1. If pcscState is [PCSC5] SCARD_ABSENT, return "absent".
  2. If pcscState is [PCSC5] SCARD_PRESENT, return "present".
  3. If pcscState is [PCSC5] SCARD_SWALLOWED, return "swallowed".
  4. If pcscState is [PCSC5] SCARD_POWERED, return "powered".
  5. If pcscState is [PCSC5] SCARD_NEGOTIABLE, return "negotiable".
  6. If pcscState is [PCSC5] SCARD_SPECIFIC, perform the following steps:
    1. If activeProtocol is [PCSC5] SCARD_PROTOCOL_T0, return "t0".
    2. If activeProtocol is [PCSC5] SCARD_PROTOCOL_T1, return "t1".
    3. If activeProtocol is [PCSC5] SCARD_PROTOCOL_RAW, return "raw".
  7. Return undefined.

5.5 control() method

The control(controlCode, data) method steps are:

  1. Let promise be a new promise.
  2. If this.[[context]].[[operationInProgress]] is true, reject promise with a "InvalidStateError" DOMException and return promise.
  3. If this.[[comm]] is null, reject promise with a "InvalidStateError" DOMException and return promise.
  4. Set this.[[context]].[[operationInProgress]] to true.
  5. Let pcscControlCode be a [PCSC5] DWORD containing controlCode.
  6. Get a copy of the buffer source data and save the result in a [PCSC5] BYTE[] inBuffer.
  7. Let outBuffer be a [PCSC5] BYTE[] large enough to hold any control command response.
  8. Let outBufferLength be a DWORD set to 0.
  9. Run the following steps in parallel:
    1. Call this.[[comm]].Control() with pcscControlCode, inBuffer, outBuffer and outBufferLength as arguments.
    2. Let responseCode be the returned RESPONSECODE.
    3. Queue a global task on the relevant global object of this using the smart card task source which performs the following steps:
      1. Clear the operationInProgress of this.[[context]].
      2. If responseCode is not SCARD_S_SUCCESS, reject promise with an exception corresponding to responseCode and abort these steps.
      3. Let resultBytes be the first outBufferLength bytes of outBuffer.
      4. Resolve promise with the result of creating an ArrayBuffer from resultBytes in this's relevant Realm.
  10. Return promise.

5.6 getAttribute() method

The getAttribute(tag) method steps are:

  1. Let promise be a new promise.
  2. If this.[[context]].[[operationInProgress]] is true, reject promise with a "InvalidStateError" DOMException and return promise.
  3. If this.[[comm]] is null, reject promise with a "InvalidStateError" DOMException and return promise.
  4. Set this.[[context]].[[operationInProgress]] to true.
  5. Run the following steps in parallel:
    1. Let pcscTag be a [PCSC5] DWORD containing tag.
    2. Let buffer be a [PCSC5] BYTE[] large enough to hold this reader attribute, as determined by the platform's [PCSC5] implementation.
    3. Call this.[[comm]].GetReaderCapabilities() with pcscTag and buffer as arguments.
    4. Let responseCode be the returned RESPONSECODE.
    5. Queue a global task on the relevant global object of this using the smart card task source which performs the following steps:
      1. Clear the operationInProgress of this.[[context]].
      2. If responseCode is not SCARD_S_SUCCESS, reject promise with an exception corresponding to responseCode and abort these steps.
      3. Let resultBytes be the bytes of buffer containing the attribute read.
      4. Resolve promise with the result of creating an ArrayBuffer from resultBytes in this's relevant Realm.
  6. Return promise.
Note: Output buffer size

5.7 setAttribute() method

The setAttribute(tag, value) method steps are:

  1. Let promise be a new promise.
  2. If this.[[context]].[[operationInProgress]] is true, reject promise with a "InvalidStateError" DOMException and return promise.
  3. If this.[[comm]] is null, reject promise with a "InvalidStateError" DOMException and return promise.
  4. Set this.[[context]].[[operationInProgress]] to true.
  5. Let pcscTag be a [PCSC5] DWORD containing tag.
  6. Get a copy of the buffer source value and save the result in a [PCSC5] BYTE[] buffer.
  7. Run the following steps in parallel:
    1. Call this.[[comm]].SetReaderCapabilities() with pcscTag and buffer as arguments.
    2. Let responseCode be the returned RESPONSECODE.
    3. Queue a global task on the relevant global object of this using the smart card task source which performs the following steps:
      1. Clear the operationInProgress of this.[[context]].
      2. If responseCode is not SCARD_S_SUCCESS, reject promise with an exception corresponding to responseCode and abort these steps.
      3. Resolve promise.
  8. Return promise.

6. SmartCardError interface

WebIDL[Exposed=(DedicatedWorker, SharedWorker, Window), Serializable]
interface SmartCardError : DOMException {
  constructor(optional DOMString message = "", SmartCardErrorOptions options);
  readonly attribute SmartCardResponseCode responseCode;
};

The responseCode attribute is the error or warning response code returned by the related [PCSC5] method.

Given a [PCSC5] RESPONSECODE different from SCARD_S_SUCCESS, a corresponding exception is created with the following steps:

Note: Unspecified response codes
  1. Let pcscCode be that RESPONSECODE.
  2. If pcscCode is SCARD_E_NO_SERVICE, return a new "no-service" SmartCardError.
    Note: Creating SmartCardError instances
  3. If pcscCode is SCARD_E_NO_SMARTCARD, return a new "no-smartcard" SmartCardError.
  4. If pcscCode is SCARD_E_NOT_READY, return a new "not-ready" SmartCardError.
  5. If pcscCode is SCARD_E_NOT_TRANSACTED, return a new "not-transacted" SmartCardError.
  6. If pcscCode is SCARD_E_PROTO_MISMATCH, return a new "proto-mismatch" SmartCardError.
  7. If pcscCode is SCARD_E_READER_UNAVAILABLE, return a new "reader-unavailable" SmartCardError.
  8. If pcscCode is SCARD_W_REMOVED_CARD, return a new "removed-card" SmartCardError.
  9. If pcscCode is SCARD_W_RESET_CARD, return a new "reset-card" SmartCardError.
  10. If pcscCode is SCARD_E_SERVER_TOO_BUSY, return a new "server-too-busy" SmartCardError.
  11. If pcscCode is SCARD_E_SHARING_VIOLATION, return a new "sharing-violation" SmartCardError.
  12. If pcscCode is SCARD_E_SYSTEM_CANCELLED, return a new "system-cancelled" SmartCardError.
  13. If pcscCode is SCARD_E_UNKNOWN_READER, return a new "unknown-reader" SmartCardError.
  14. If pcscCode is SCARD_W_UNPOWERED_CARD, return a new "unpowered-card" SmartCardError.
  15. If pcscCode is SCARD_W_UNRESPONSIVE_CARD, return a new "unresponsive-card" SmartCardError.
  16. If pcscCode is SCARD_W_UNSUPPORTED_CARD, return a new "unsupported-card" SmartCardError.
  17. If pcscCode is SCARD_E_UNSUPPORTED_FEATURE, return a new "unsupported-feature" SmartCardError.
  18. If pcscCode is SCARD_E_INVALID_PARAMETER, return a new TypeError.
  19. If pcscCode is SCARD_E_INVALID_HANDLE, return a new "InvalidStateError" DOMException.
  20. If pcscCode is SCARD_E_SERVICE_STOPPED, return a new "InvalidStateError" DOMException.
  21. If pcscCode is SCARD_P_SHUTDOWN, return a new "AbortError" DOMException.
  22. Otherwise return a new "UnknownError" DOMException.

6.1 SmartCardErrorOptions dictionary

WebIDLdictionary SmartCardErrorOptions {
  required SmartCardResponseCode responseCode;
};

The responseCode member is the value for SmartCardError's responseCode attribute.

6.2 SmartCardResponseCode enum

WebIDLenum SmartCardResponseCode {
  "no-service",
  "no-smartcard",
  "not-ready",
  "not-transacted",
  "proto-mismatch",
  "reader-unavailable",
  "removed-card",
  "reset-card",
  "server-too-busy",
  "sharing-violation",
  "system-cancelled",
  "unknown-reader",
  "unpowered-card",
  "unresponsive-card",
  "unsupported-card",
  "unsupported-feature"
};
no-service
SCARD_E_NO_SERVICE in the [PCSC5] spec.
no-smartcard
SCARD_E_NO_SMARTCARD in the [PCSC5] spec.
not-ready
SCARD_E_NOT_READY in the [PCSC5] spec.
not-transacted
SCARD_E_NOT_TRANSACTED in the [PCSC5] spec.
proto-mismatch
SCARD_E_PROTO_MISMATCH in the [PCSC5] spec.
reader-unavailable
SCARD_E_READER_UNAVAILABLE in the [PCSC5] spec.
removed-card
SCARD_W_REMOVED_CARD in the [PCSC5] spec.
reset-card
SCARD_W_RESET_CARD in the [PCSC5] spec.
server-too-busy
The smart card resource manager is too busy to complete this operation.
sharing-violation
SCARD_E_SHARING_VIOLATION in the [PCSC5] spec.
system-cancelled
SCARD_E_SYSTEM_CANCELLED in the [PCSC5] spec.
unknown-reader
SCARD_E_UNKNOWN_READER in the [PCSC5] spec.
unpowered-card
SCARD_W_UNPOWERED_CARD in the [PCSC5] spec.
unresponsive-card
SCARD_W_UNRESPONSIVE_CARD in the [PCSC5] spec.
unsupported-card
SCARD_W_UNSUPPORTED_CARD in the [PCSC5] spec.
unsupported-feature
SCARD_E_UNSUPPORTED_FEATURE in the [PCSC5] spec.

7. Integrations

7.1 Permissions Policy

This specification defines a feature that controls whether the methods exposed by the smartCard attribute on the Navigator object may be used.

The feature name for this feature is "smart-card".

The default allowlist for this feature is 'self'.

8. Conformance

As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.

A. References

A.1 Normative references

[dom]
DOM Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://dom.spec.whatwg.org/
[hr-time-3]
High Resolution Time. Yoav Weiss. W3C. 19 July 2023. W3C Working Draft. URL: https://www.w3.org/TR/hr-time-3/
[html]
HTML Standard. Anne van Kesteren; Domenic Denicola; Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[infra]
Infra Standard. Anne van Kesteren; Domenic Denicola. WHATWG. Living Standard. URL: https://infra.spec.whatwg.org/
[ISO7186-3]
Identification cards - Integrated circuit cards; Part 3: Cards with contacts - Electrical interface and transmission protocols. ISO/IEC. 1 November 2006. Published. URL: https://www.iso.org/standard/38770.html
[PCSC5]
Interoperability Specification for ICCs and Personal Computer Systems; Part 5. ICC Resource Manager Definition. PC/SC Workgroup. 30 September 2005. Published. URL: https://pcscworkgroup.com/Download/Specifications/pcsc5_v2.01.01.pdf
[permissions-policy]
Permissions Policy. Ian Clelland. W3C. 17 October 2023. W3C Working Draft. URL: https://www.w3.org/TR/permissions-policy-1/
[WEBIDL]
Web IDL Standard. Edgar Chen; Timothy Gu. WHATWG. Living Standard. URL: https://webidl.spec.whatwg.org/