Skip to content
This repository was archived by the owner on Aug 11, 2025. It is now read-only.
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 133 additions & 91 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
- [@dcrousso](https://github.com/dcrousso)
- [@hlozi](https://github.com/hlozi)
- [@martijnharing](https://github.com/martijnharing)

- [@agl](https://github.com/agl)
- [@samuelgoto](https://github.com/samuelgoto)

## Participate

Expand All @@ -15,47 +16,115 @@

## Introduction

This is a proposal for a new web API that allows websites to request a mobile document as defined in ISO/IEC 18013-5 using structures defined in the same ISO in order to achieve an online presentment. The API would protect the mobile document credential data, supports selective disclosure, and allow the user to control the release of credential data.

The ISO/IEC 18013-5:2021 standard specifies how a driver license (mDL) is represented on a mobile device and how it is presented to a physical reader. Users who chose to have their mDL on device can present it to compatible readers instead of the physical driver license. The ISO standard defines the protocols and the exchanges between the mDL and the mDL reader for different physical uses case (e.g. TSA, Hotel, store,... ). The ISO standard provides building blocks to allow remote presentment of the the mobile driver’s license over the Web such as data structures and operational phases. The ISO standard ISO/IEC 18013-5:2021 for mobile driver’s license does not define a web API to allow presentement via user agent.
This is a proposal for a new web API that allows websites to request a mobile document as defined in ISO/IEC 18013-5 using structures defined in the same ISO standard in order to achieve an online presentment. The API protects the mobile document credential data, supports selective disclosure, and allows the user to control the release of credential data.

The mechanisms defined by ISO/IEC 18013-5 are designed to support any type of mobile document, called mdoc. Besides the mDL, this API can therefore be used to request and transmit any mobile document that implements the ISO mdoc specification.
The ISO/IEC 18013-5:2021 standard specifies how a mobile driver's license (mDL) is represented on a mobile device and how it is presented to a physical reader. Users who choose to have their mDL on device can present it to compatible readers instead of the physical driver's license. The ISO standard defines the protocols and the exchanges between the mDL and the mDL reader for different physical uses case (e.g. TSA, hotels, stores, …). The ISO standard provides building blocks to allow remote presentment of the the mobile driver’s license over the Web such as data structures and operational phases, however it does not define a web API to allow presentement via user agent.

The mechanisms defined by ISO/IEC 18013-5 are designed to support any type of mobile document, generically called “mdocs”. The API defined in this document can therefore be used to request and transmit any mobile document that implements the ISO mdoc specification.


## Goals

- Allow websites to request the presentment of a mobile document in mdoc format such as driver’s license.
- Enable the user to have a consent based control of what data is released to the website.
- Allow websites to request the presentment of a mobile document in mdoc format, such as driver’s license.
- Enable the user to have control of what data is released to the website.
- Protect the released data using encryption to ensure only the website the data is intended for can access the data.
- Don't preclude the addition of other credential types in the future, if needed.
- Integrate with other types of identity requests which websites may want to let users choose between.


## Non-goals

- Support requests spanning identity and authentication mechanisms.
- Define issuance and provisioning of an mdoc.
- Define the communication between the User Agent and the website server is out of scope.
- Define the communication between the User Agent and the website server
- Define the native communication between the User Agent and the application holding the mdoc.
- Define how the response to the request is used by the server is out of scope.
- Requesting multiple mdocs in one API call.

- Define how the response to the request is used by the server.
- Support requesting multiple mdocs in one API call.

## Proposed Solution

The API request should contain all the information needed to build up and encrypt the response. The response data from the mdoc should be a CBOR-encoded blob encrypted end-to-end for the intended relying party using Hybrid Public Key Encryption (HPKE) defined in RFC 9180. Only the targeted relying party, holder of the private part corresponding to the public key used for the response encryption, should be able to decrypt the response (with the assumption that the server always holds and protects the private key). The API should allow the server to pass as a parameter a requester identity object that allows the mdoc device to extract the public key material needed for the encryption. The passed object can be anything that allows the mdoc to determine which public key to encrypt the response to, like a certificate, a key or a key identifier.
This API would be a new identity scheme within [IdentityCredential](https://github.com/agl/identity-credential) using the scheme identifier `mdoc`.

### Requests

### Request
```webidl
partial dictionary IdentityProviderConfig {
optional MdocIdentityProviderConfig mdoc;
}

The website would need to provide a document type and desired data elements, as well as a requester identity object that allows the device to extract the encryption public key (and the mdoc to optionally verify the eligibility of the website to request the mdoc presentment).
dictionary MdocIdentityProviderConfig {
required DOMString nonce;

// The mdoc document type requested. See ISO 18013-5 section 7.1.
required DOMString documentType;

// The elements of the mdoc requested.
required sequence<MdocElement> requestedElements;

// The number of days that the requester will keep the data for.
// Not providing this is equivalent to 0, meaning not asking to store.
// Infinity means forever.
double retentionDays;

// A set of reader identities. Each value is a base64-encoded sequence of one
// or more X.509 certificates in ASN.1 DER encoding (a “chain” of
// certificates). At least one value must be provided. The first element of
// each X.509 chain MUST be the end-entity certificate. Subsequent X.509
// certificates in a chain are intermediate certificates as may help the wallet
// verify the end-entity certificate.
//
// If the requester has no applicable X.509 chain then it may use a
// single, self-signed certificate.
//
// The underlying wallet will evaluate these certificate chains
// and pick one, at its discretion, to identify the requesting entity. The
// SubjectPublicKeyInfo of the end-entity certificate of the selected chain
// is used to encrypt the response.
required sequence<DOMString> readerAuthentication;
};

dictionary MdocElement {
required DOMString namespace; // As defined in ISO 18013-5 section 7.
required DOMString name;
};
```

An example request:

```js
let request = {
identity: {
providers: [{
mdoc: {
nonce: "gf69kepV+m5tGxMyMF0cnn9NCnRHez/LUIsFtLi6pwg=",
documentType: "org.iso.18013.5.1.mDL",
retentionDays: 90,
readerPublicKey: ["ftl+VEHPB17r2oi6it3ENaqhOOB0AZbAkb5f4VlCPakpdNioc9QZ7X/6w..."],
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks base64-encoded, but do we know any more than that? Is it a SEC 1 uncompressed EC key? Something else?

requestedElements: [
{ namespace: "org.iso.18013.5.1", name: "document_number" },
{ namespace: "org.iso.18013.5.1", name: "portrait" },
{ namespace: "org.iso.18013.5.1", name: "driving_privileges" },
{ namespace: "org.iso.18013.5.1.aamva", name: "organ_donor" },
{ namespace: "org.iso.18013.5.1.aamva", name: "hazmat_endorsement_expiration_date" },
],
},
}],
}
};
navigator.credentials.get(request).then(() => {
});
```

Once mdoc application verifies the server identity object to make sure it is a legitimate request (if needed), it would build up the response according to ISO/IEC 18013-5, including the credential data itself, the authentication by the issuer, and the authentication by the device, encrypting everything with the key derived from the requester identity object using HKPE so that only the server can decrypt the binary blob (assuming the server keeps and protects the private key).
### Processing

After sending the encrypted data to the server, it would be decrypted to verify the presented mdoc issuer signature and device signature according to ISO/IEC 18013-5.
If the user rejects the request, or if no applicable mdocs are available, or if the identity in `readerPublicKey` is not authorized to request the applicable mdoc, then a "[NotAllowedError](https://heycam.github.io/webidl/#notallowederror)" [DOMException](https://heycam.github.io/webidl/#idl-DOMException) is raised.

Otherwise the application holding the responsive mdoc builds a response according to ISO/IEC 18013-5, including the credential data itself, the authentication by the issuer, and the authentication by the device, encrypting everything with the key taken from the selected `readerPublicKey` value.

### Response

The response from the server would be the `DeviceResponse` object as defined in ISO/IEC 18013-5 clause 8.3.2.1.2.2 encoded with CBOR according to ISO/IEC 18013-5, encrypted using HPKE with the following settings:
A positive result from an mdoc request will be an [IdentityCredential](https://github.com/agl/identity-credential) where the `scheme` will be `mdoc` and the `result` with be the base64-encoding of the `DeviceResponse` object as defined in ISO/IEC 18013-5 clause 8.3.2.1.2.2 encoded with CBOR according to ISO/IEC 18013-5, encrypted using HPKE with the following settings:

```
mode: mode_base
KEM: DHKEM(P-256, HKDF-SHA256)
Expand All @@ -64,7 +133,8 @@ AEAD: AES-128-GCM
```

Part of the signature generated for `DeviceAuth` is done using the `SessionTranscript` structure as defined in ISO/IEC 18103-5 section 9.1.5.1. The `DeviceEngagementBytes` and the `EReaderKeyBytes` are both not present, and therefore have a value of `null`. The `Handover` element shall be as defined below:
```

```cddl
BrowserHandover = [
"BrowserHandoverv1",
Nonce,
Expand All @@ -73,10 +143,12 @@ BrowserHandover = [
pkEm
]
```
where `Nonce` is the nonce provided by the request, `OriginInfoBytes` contains the origin of the request and is defined in ISO/IEC 18013-7, and `RequesterIdentity` is the requester identity object from the request.

where `Nonce` is the nonce provided by the request, `OriginInfoBytes` contains the origin of the request and is defined in ISO/IEC 18013-7, and `RequesterIdentity` is the requester identity object from the request. (**TODO**: tighen the definition of `RequesterIdentity`, should it be the SubjectPublicKeyInfo?)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT RequesterIdentity isn't in the API anymore and should be removed from this handover array.


This is all included inside a CBOR encoded `CredentialDocument` with the following structure:
```

```cddl
CredentialDocument = {
"version": tstr,
"encryptionParamaters": EncryptionParamaters,
Expand All @@ -91,88 +163,60 @@ EncryptionParamaters = {
```


### API

One idea a new `CredentialRequest` (et al)
```
dictionary CredentialElement {
required DOMString namespace; // As defined in ISO 18013-5 clause 8.
required DOMString name;
};

dictionary CredentialStorageDuration {
// At least one of these is required.

boolean forever; // Cannot be used with any other properties.

long days; // Cannot (currently) be used with any other properties.
};

dictionary CredentialDocumentDescriptor {
required DOMString documentType; // As defined in ISO 18013-5 clause 8.

required sequence<CredentialElement> requestedElements;

CredentialStorageDuration desiredStorageDuration; // Not providing this is equivalent to not asking to store.
};

dictionary CredentialDocument {
object data; // The CBOR encoded `CredentialDocument` defined above.
};

dictionary RequestConfiguration {
required DOMString nonce;
};

[
SecureContext,
Exposed=Window,
] interface CredentialRequest {
constructor(DOMString requesterIdentity, CredentialDocumentDescriptor documentDescriptor); // This throws if anything in the `documentDescriptor` is not recognized (e.g. an invalid `documentType`).

Promise<CredentialDocument> requestDocument(RequestConfiguration configuration);

Promise<undefined> abort();
};
```


## Examples

Requesting specific attributes from a driver’s license:

```js
// Driver's License
let mDLCredentialRequest = new CredentialRequest(certificate, {
documentType: "org.iso.18013.5.1.mDL",
requestedElements: [
{ namespace: "org.iso.18013.5.1", name: "document_number" },
{ namespace: "org.iso.18013.5.1", name: "portrait" },
{ namespace: "org.iso.18013.5.1", name: "driving_privileges" },
{ namespace: "org.iso.18013.5.1.aamva", name: "organ_donor" },
{ namespace: "org.iso.18013.5.1.aamva", name: "hazmat_endorsement_expiration_date" },
],
desiredStorageDuration: {
days: 7,
},
let request = {
identity: {
providers: [{
mdoc: {
nonce: "gf69kepV+m5tGxMyMF0cnn9NCnRHez/LUIsFtLi6pwg=",
readerPublicKey: ["ftl+VEHPB17r2oi6it3ENaqhOOB0AZbAkb5f4VlCPakpdNioc9QZ7X…"],
retentionDays: 90,
documentType: "org.iso.18013.5.1.mDL",
requestedElements: [
{ namespace: "org.iso.18013.5.1", name: "document_number" },
{ namespace: "org.iso.18013.5.1", name: "portrait" },
{ namespace: "org.iso.18013.5.1", name: "driving_privileges" },
{ namespace: "org.iso.18013.5.1.aamva", name: "organ_donor" },
{ namespace: "org.iso.18013.5.1.aamva", name: "hazmat_endorsement_expiration_date" },
],
}
}]
}
};
navigator.credentials.get(request).then(() => {
});
mDLCredentialRequest.request({ nonce }).then((credentialDocument) => { ... });
```

Requesting specific mDL attributes from a COVID card:

```js
// Vaccination Card
let micovCredentialRequest = new CredentialRequest(certificate, {
documentType: "org.micov.1",
requestedElements: [
{ namespace: "org.micov.attestation.1", name: "PersonId_dl" },
{ namespace: "org.micov.attestation.1", name: "portrait" },
],
});
micovCredentialRequest.request({ nonce }).then((credentialDocument) => { ... });
let request = {
identity: {
providers: [{
mdoc: {
nonce: "gf69kepV+m5tGxMyMF0cnn9NCnRHez/LUIsFtLi6pwg=",
readerPublicKey: ["ftl+VEHPB17r2oi6it3ENaqhOOB0AZbAkb5f4VlCPakpdNioc9QZ7X…"],
retentionDays: 90,
documentType: "org.micov.1",
requestedElements: [
{ namespace: "org.micov.attestation.1", name: "PersonId_dl" },
{ namespace: "org.micov.attestation.1", name: "portrait" },
],
},
}],
}
};
```


## Privacy & Security Considerations

This API provides the ability for websites to retrieve an ISO 18013-5 mDoc already on the device. An important part of the security and privacy mechanisms are provided by ISO 18013-5:

- Protection against forgery with passive data authentication defined in ISO 18013-5 based on Mobile Security Object (MSO).
- Protection against cloning using active authentication leveraging a signature over session data. The signature is produced by the device using the device private key as defined in ISO 18013-5. The Device Public key is part of the MSO signed by the issuer.
- The ability for the user to see the requested data before disclosure, in addition to being able to choose what to disclose.
Expand All @@ -186,13 +230,11 @@ This API provides the ability for websites to retrieve an ISO 18013-5 mDoc alrea
Many thanks for valuable feedback and advice from:

- [@15characterlimi](https://github.com/15characterlimi)
- [@agl](https://github.com/agl)
- [@balfanz](https://github.com/balfanz)
- [@bslassey](https://github.com/bslassey)
- [@cwilso](https://github.com/cwilso)
- [@davidz25](https://github.com/davidz25)
- [@divegeek](https://github.com/divegeek)
- [@hober](https://github.com/hober)
- [@jyasskin](https://github.com/jyasskin)
- [@samuelgoto](https://github.com/samuelgoto)
- [@sethmoo](https://github.com/sethmoo)