Web Smart Card API

Unofficial Proposal Draft,

This version:
https://wicg.github.io/web-smart-card/
Issue Tracking:
GitHub
Editors:
(Google)
(Google)

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 specification was published by the Web Platform Incubator Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

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

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

2. Extensions to the WorkerNavigator interface

[Exposed=(DedicatedWorker, SharedWorker), SecureContext, IsolatedContext]
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

[Exposed=(DedicatedWorker, SharedWorker, Window), SecureContext, IsolatedContext]
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.

[Exposed=(DedicatedWorker, SharedWorker, Window), SecureContext, IsolatedContext]
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.
[[activeReaderTransactions]] An empty map A map of reader names to the SmartCardConnection that currently holds an active transaction on that reader in this context, if any.
[[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.

    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.

        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

dictionary SmartCardReaderStateIn {
  required DOMString readerName;
  required SmartCardReaderStateFlagsIn currentState;
  unsigned long currentCount;
};
readerName

Name of the smart card reader.

currentState

The current state of that smart card reader as known by the application.

currentCount

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 of type SmartCardReaderStateIn 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
dictionary 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

The application is unaware of the current state, and would like to know.

ignore

The application is not interested in this reader, and it should not be considered during monitoring operations.

unavailable

The application believes that this reader is not available for use.

empty

The application believes that there is not a card in the reader.

present

The application believes that there is a card in the reader.

exclusive

The application believes that the card in the reader is allocated for exclusive use by another application.

inuse

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

The application believes that there is an unresponsive card in the reader.

unpowered

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.

4.2.2. SmartCardReaderStateOut dictionary

The actual state of a smart card reader.
dictionary SmartCardReaderStateOut {
  required DOMString readerName;
  required SmartCardReaderStateFlagsOut eventState;
  required unsigned long eventCount;
  ArrayBuffer answerToReset;
};
readerName

Name of the smart card reader.

eventState

The actual state of that smart card reader.

eventCount

The actual number of card insertion and removal events in this reader.

answerToReset

The inserted card’s [ISO7816-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 of type SCARD_READERSTATE 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.

    5. If the platform’s SCARD_READERSTATE structure has a member containing the card’s [ISO7816-3] Answer To Reset, set stateOut["answerToReset"] to that value.

    6. Append stateOut to readerStatesOut.

  3. Return readerStatesOut.

4.2.2.1. SmartCardReaderStateFlagsOut dictionary
dictionary 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

The application requested that this reader be ignored.

changed

There is a difference between the state input by the calling application, and the actual state.

unavailable

This reader is not available for use.

unknown

The reader name given by the application is not known.

empty

There is no card in the reader.

present

There is a card in the reader.

exclusive

The card in the reader is allocated for exclusive use by another application.

inuse

The card in the reader is in use by one or more other applications, but may be connected to in shared mode.

mute

There is an unresponsive card in the reader.

unpowered

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.

4.2.3. SmartCardGetStatusChangeOptions dictionary

dictionary SmartCardGetStatusChangeOptions {
  DOMHighResTimeStamp timeout;
  AbortSignal signal;
};
timeout

Timeout parameter for the GetStatusChange() [PCSC5] method. If not specified, a timeout value of INFINITE (which is defined system dependent) will be used.

signal

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. If this.[[activeReaderTransactions]][readerName] exists, reject promise with a "InvalidStateError" DOMException and return promise.

  4. Set this.[[operationInProgress]] to true.

  5. 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.[[readerName]] to readerName.

      8. Set connection.[[context]] to this.

      9. Set connection.[[activeProtocol]] to activeProtocol.

      10. Set result["connection"] to connection.

      11. If activeProtocol is a valid protocol value, set result["activeProtocol"] to the corresponding SmartCardProtocol.

      12. Resolve promise with result.

  6. Return promise.

4.3.1. SmartCardProtocol enum

enum 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"

[ISO7816-3] T=0. Asynchronous half duplex character transmission protocol. Corresponds to a [PCSC5] SCARD_PROTOCOL_T0 DWORD.

"t1"

[ISO7816-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 of type SmartCardProtocol in protocols, add the corresponding DWORD of protocol to flags.

  3. Return flags.

4.3.2. SmartCardConnectResult dictionary

dictionary SmartCardConnectResult {
  required SmartCardConnection connection;
  SmartCardProtocol activeProtocol;
};
connection

An interface to the connection created.

activeProtocol

The protocol actually in use.

4.3.3. SmartCardAccessMode enum

enum 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

dictionary 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 type SmartCardConnection of context.[[connections]]:

    1. end any settled transaction of connection.

    2. If context.[[operationInProgress]] is true, abort there steps.

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

[Exposed=(DedicatedWorker, SharedWorker, Window), SecureContext, IsolatedContext]
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.
[[readerName]] null The name of the reader this connection is associated with.
[[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.[[context]].[[activeReaderTransactions]][this.[[readerName]]] exists and is not equal to this, reject promise with a "InvalidStateError" DOMException and return promise.

  4. If this.[[comm]] is null, reject promise with a "InvalidStateError" DOMException and return promise.

  5. Set this.[[context]].[[operationInProgress]] to true.

  6. 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.

  7. Return promise.

5.1.1. SmartCardDisposition enum

enum 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.[[context]].[[activeReaderTransactions]][this.[[readerName]]] exists and is not equal to this, reject promise with a "InvalidStateError" DOMException and return promise.

  4. If this.[[comm]] is null, reject promise with a "InvalidStateError" DOMException and return promise.

  5. Let protocol be a [PCSC5] DWORD set to this.[[activeProtocol]].

  6. If options["protocol"] exists, set protocol to the DWORD corresponding to options["protocol"].

  7. If protocol is not a valid protocol value, reject promise with a "InvalidStateError" DOMException and return promise.

  8. Set this.[[context]].[[operationInProgress]] to true.

  9. Let sendPci be the platform’s [PCSC5] SCARD_IO_HEADER corresponding to this.[[activeProtocol]].

  10. Let pcscSendBuffer be a [PCSC5] BYTE[] containing sendBuffer.

  11. Let recvPci be the platform’s SCARD_IO_HEADER equivalent of empty or null.

  12. Let recvBuffer be a BYTE[] big enough to hold the largest [ISO7816-3] extended response APDU (65538 bytes).

  13. Let recvLength be a DWORD set to 0.

  14. 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.

  15. Return promise.

5.2.1. SmartCardTransmitOptions dictionary

dictionary SmartCardTransmitOptions {
  SmartCardProtocol protocol;
};
protocol

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.[[context]].[[activeReaderTransactions]][this.[[readerName]]] exists, reject promise with a "InvalidStateError" DOMException and return promise.

  4. If this.[[comm]] is null, reject promise with a "InvalidStateError" DOMException and return promise.

  5. If this.[[transactionState]] is not null, reject promise with a "InvalidStateError" DOMException and return promise.

  6. Let signal be an AbortSignal set to null.

  7. 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.

  8. Set this.[[context]].[[operationInProgress]] to true.

  9. 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.

  10. Return promise.

5.3.1. SmartCardTransactionOptions dictionary

dictionary SmartCardTransactionOptions {
  AbortSignal signal;
};
signal

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:

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. Set connection.[[context]].[[activeReaderTransactions]][connection.[[readerName]]] to connection.

  8. Let callbackPromise be the result of invoking transaction.

  9. 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 connection.[[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. Remove connection.[[readerName]] from connection.[[context]].[[activeReaderTransactions]].

      3. Let exception be connection.[[transactionState]]’s pendingException.

      4. 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.

      5. Otherwise, reject transactionPromise with exception.

      6. 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.[[context]].[[activeReaderTransactions]][this.[[readerName]]] exists and is not equal to this, reject promise with a "InvalidStateError" DOMException and return promise.

  4. If this.[[comm]] is null, reject promise with a "InvalidStateError" DOMException and return promise.

  5. Set this.[[context]].[[operationInProgress]] to true.

  6. 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 [ISO7816-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.

  7. Return promise.

5.4.1. SmartCardConnectionStatus dictionary

dictionary SmartCardConnectionStatus {
  required DOMString readerName;
  required SmartCardConnectionState state;
  ArrayBuffer answerToReset;
};
readerName

Name of the connected reader.

state

Current state of the connection.

answerToReset

The answer to reset (ATR) string from the card, if applicable.

5.4.1.1. SmartCardConnectionState enum
enum 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 [ISO7816-3] T=0 protocol mode and a new protocol may not be negotiated.

"t1"

The card is in [ISO7816-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.[[context]].[[activeReaderTransactions]][this.[[readerName]]] exists and is not equal to this, reject promise with a "InvalidStateError" DOMException and return promise.

  4. If this.[[comm]] is null, reject promise with a "InvalidStateError" DOMException and return promise.

  5. Set this.[[context]].[[operationInProgress]] to true.

  6. Let pcscControlCode be a [PCSC5] DWORD containing controlCode.

  7. Get a copy of the buffer source data and save the result in a [PCSC5] BYTE[] inBuffer.

  8. Let outBuffer be a [PCSC5] BYTE[] large enough to hold any control command response.

  9. Let outBufferLength be a DWORD set to 0.

  10. 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.

  11. 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.[[context]].[[activeReaderTransactions]][this.[[readerName]]] exists and is not equal to this, reject promise with a "InvalidStateError" DOMException and return promise.

  4. If this.[[comm]] is null, reject promise with a "InvalidStateError" DOMException and return promise.

  5. Set this.[[context]].[[operationInProgress]] to true.

  6. 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.

  7. Return promise.

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.[[context]].[[activeReaderTransactions]][this.[[readerName]]] exists and is not equal to this, reject promise with a "InvalidStateError" DOMException and return promise.

  4. If this.[[comm]] is null, reject promise with a "InvalidStateError" DOMException and return promise.

  5. Set this.[[context]].[[operationInProgress]] to true.

  6. Let pcscTag be a [PCSC5] DWORD containing tag.

  7. Get a copy of the buffer source value and save the result in a [PCSC5] BYTE[] buffer.

  8. 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.

  9. Return promise.

6. SmartCardError interface

[
  Exposed=(DedicatedWorker, SharedWorker, Window),
  SecureContext,
  IsolatedContext
] 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:

  1. Let pcscCode be that RESPONSECODE.

  2. If pcscCode is SCARD_E_NO_SERVICE, return a new "no-service" SmartCardError.

  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

dictionary SmartCardErrorOptions {
  required SmartCardResponseCode responseCode;
};

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

6.2. SmartCardResponseCode enum

enum 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. Security and Privacy Considerations

This API provides web applications with access to the host’s PC/SC smart card subsystem. This is a powerful feature that, if misused, could have significant negative impacts on a user’s security and privacy. This section outlines the threats considered and the normative requirements for user agents to mitigate them.

Access to a smart card reader and any card present within it is a powerful feature. A user agent MUST NOT allow a web application to gain access to a SmartCardConnection object without express permission.

User consent MUST be obtained for a specific origin. The request for consent MUST be triggered by a call to the connect() method. The user agent MUST display a permission prompt that clearly indicates which origin is requesting access and provides the user with enough information to make an informed decision (for example, by displaying the name of the smart card reader).

User agents SHOULD provide options for both transient (e.g., "for this session only") and persistent permission. To mitigate the risk of users forgetting they have granted persistent access, transient permission SHOULD be the default and more prominent option.

Users MUST be provided with a mechanism to view and revoke any previously granted permissions for this API.

7.2. Fingerprinting

The listReaders() method and the answerToReset member of the SmartCardReaderStateOut dictionary expose information that can be used for passive fingerprinting. The presence and model of smart card readers can reveal information about the user, such as whether they are in a corporate environment. The Answer to Reset (ATR) can further identify the type and issuer of a smart card.

While this specification does not require a permission prompt before calling listReaders(), access to the entire API is controlled by the "smart-card" policy-controlled feature. This allows administrators or users to disable the API for specific origins, mitigating the fingerprinting risk.

7.3. Device and Data Integrity

The control() and setAttribute() methods provide direct, low-level access to the smart card reader hardware. A malicious site could potentially use these methods to upload malicious firmware, render the device inoperable, or otherwise interfere with its normal operation.

Similarly, a malicious site with a connection to a smart card could repeatedly attempt PIN verification to permanently block the card, or access or overwrite sensitive, unprotected data.

The primary mitigation for these threats is the requirement for express permission before a SmartCardConnection object is created, as this gates access to all subsequent powerful methods.

7.4. Authentication and Spoofing

For authentication use cases, developers SHOULD prefer using the Web Authentication API wherever possible.

7.5. Cross-Origin Communication

A smart card with writable memory could be used as a side-channel for different origins to exchange data, bypassing other same-origin policies. The mitigation for this is that express permission is granted to a specific origin. An attack would require the user to grant smart card access to multiple, potentially malicious, origins.

7.6. Isolated Context

This API MUST only be exposed in an isolated context.

7.7. Document Lifecycle

To prevent documents from holding connections to sensitive hardware while not in the user’s direct control, a user agent MUST dispose of all active SmartCardContext objects and their associated SmartCardConnections when a document is no longer fully active. This includes automatically disconnecting any active connections as if disconnect() were called.

8. Integrations

8.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 'none'. User agents MAY override this to 'self' for particular origins (for example based on user decision).

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[HR-TIME-3]
Yoav Weiss. High Resolution Time. URL: https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[ISO7816-3]
Identification cards - Integrated circuit cards; Part 3: Cards with contacts - Electrical interface and transmission protocols. 1 November 2006. Published. URL: https://www.iso.org/standard/38770.html
[ISOLATED-CONTEXTS]
Isolated Contexts. Draft Community Group Report. URL: https://wicg.github.io/isolated-web-apps/isolated-contexts.html
[PCSC5]
Interoperability Specification for ICCs and Personal Computer Systems; Part 5. ICC Resource Manager Definition. 30 September 2005. Published. URL: https://pcscworkgroup.com/Download/Specifications/pcsc5_v2.01.01.pdf
[PERMISSIONS]
Marcos Caceres; Mike Taylor. Permissions. URL: https://w3c.github.io/permissions/
[PERMISSIONS-POLICY-1]
Ian Clelland. Permissions Policy. URL: https://w3c.github.io/webappsec-permissions-policy/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

IDL Index

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

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

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

[Exposed=(DedicatedWorker, SharedWorker, Window), SecureContext, IsolatedContext]
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 = {});
};

dictionary SmartCardReaderStateIn {
  required DOMString readerName;
  required SmartCardReaderStateFlagsIn currentState;
  unsigned long currentCount;
};

dictionary 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;
};

dictionary SmartCardReaderStateOut {
  required DOMString readerName;
  required SmartCardReaderStateFlagsOut eventState;
  required unsigned long eventCount;
  ArrayBuffer answerToReset;
};

dictionary 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;
};

dictionary SmartCardGetStatusChangeOptions {
  DOMHighResTimeStamp timeout;
  AbortSignal signal;
};

enum SmartCardProtocol {
  "raw",
  "t0",
  "t1"
};

dictionary SmartCardConnectResult {
  required SmartCardConnection connection;
  SmartCardProtocol activeProtocol;
};

enum SmartCardAccessMode {
  "shared",
  "exclusive",
  "direct"
};

dictionary SmartCardConnectOptions {
  sequence<SmartCardProtocol> preferredProtocols;
};

[Exposed=(DedicatedWorker, SharedWorker, Window), SecureContext, IsolatedContext]
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?> ();

enum SmartCardDisposition {
  "leave",
  "reset",
  "unpower",
  "eject"
};

dictionary SmartCardTransmitOptions {
  SmartCardProtocol protocol;
};

dictionary SmartCardTransactionOptions {
  AbortSignal signal;
};

dictionary SmartCardConnectionStatus {
  required DOMString readerName;
  required SmartCardConnectionState state;
  ArrayBuffer answerToReset;
};

enum SmartCardConnectionState {
  "absent",
  "present",
  "swallowed",
  "powered",
  "negotiable",
  "t0",
  "t1",
  "raw"
};

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

dictionary SmartCardErrorOptions {
  required SmartCardResponseCode responseCode;
};

enum 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"
};