PID Option C': Signed Credential with Cloud Support¶
Basic Idea¶
In this flow, long-lived, digitally signed PID credentials in ISO mdoc and IETF SD-JWT VC formats are issued to the Wallet Instance utilizing the OpenID4VP protocol. During the issuance, the user is authenticated using the eID card, so when these PID credentials are presented to the Relying Parties over OpenID4VP protocol, there is no need for the user to use physical eID card. This flow trusts using Wallet Backend to manage keys on behalf of the Wallet Instance, under the assumption that keys managed on the mobile device cannot be secured in a way that is appropriate for Level of Assurance High (unlike Option C).
To improve user experience and prevent the users from tapping an eID card every time a batch of Credentials needs to be refreshed and tapping an eID card as many times as there are Credentials in a batch, a concept of Seed Credential is introduced. A Seed Credential is used to acquire a set of single-use Batch Credentials from the Provider. The Wallet can then present these Batch Credentials to the Relying Parties. The user only needs to present the physical ID card once - to acquire the Seed Credential - instead of every presentation.
Such Seed Credential is issued by a PID Provider to the Wallet as a signed DPoP-bound refresh token and can be stored for a longer period. The key from the wallet backend, that the refresh token is bound to, is stored and managed by an HSM in the wallet backend (WSCD) instead of the wallet device itself.
To authenticate the wallet instance, the user must set up a new Wallet PIN, which is verified by the Wallet Backend.
Wallet Attestation PoP and DPoP Proof are bound to the keys managed by a wallet backend, but since they contain session specific information (audience and a nonce/session id) that should be known only to the wallet instance from a privacy perspective, to generate a signature, the wallet instance sends only a ready-to-be-signed hash to the wallet backend.
ToDo: need to act explanation for how DPoP is a PoP for seed credential used as a refresh token and key attestation is needed.
ToDo: explain the concept of a PIN
Cryptographic Keys¶
Where applicable, each key name start with the entity that manages that key:
wb_
stands for Wallet Backendpp_
stands for PID Providerrp_
stands for Relying Partydevice_
means Wallet Instance
Long-Term keys:
- Wallet Instance generates locally managed key pair \((device\_pub,
device\_priv)\) which is used
- to bind the PIN proof to the device
- Wallet Instance generates AES key \((pin\_salt)\) which is used
- to derive the
pin_derived_eph_priv
used as PoP of Wallet PIN
- to derive the
- Wallet Backend has key pairs represented as \((wb\_attestation\_pub_wa,
wb\_attestation\_priv_wa)\), \((wb\_attestation\_pub_kbp,
wb\_attestation\_priv_kbp)\), and \((wb\_attestation\_pub_ks,
wb\_attestation\_priv_ks)\) which are each used
- to sign wallet instance's wallet attestations, used with the postfix
_wa
- to sign key attestation for the key that a PID is bound to, used with the
postfix
_kbp
- to sign key attestation for the key that a seed credential is bound to,
used with the postfix
_ks
- to sign wallet instance's wallet attestations, used with the postfix
- Wallet Backend generates a key pair \((wb\_device\_pub, wb\_device\_priv)\)
which is used
- to sign proof of possession of wallet attestation
- Wallet Backend generates HSM bound key pair \((wb\_dpop\_pub, wb\_dpop\_priv)\)
which is used
- to generate DPoP proof (a key to which refresh token used as a seed credential is bound to)
- PID Provider has HSM bound long-term key pair \((pp\_pub, pp\_priv)\)
- to sign over the issued Credential
- to sign over the Refresh Token JWT
- PID Provider has symmetric long-term key \((pp\_data)\) which is used
- to encrypt eID data inside the refresh token
- Relying Party has long-term key pair \((rp\_pub, rp\_priv)\) which is used
- to sign over the authorization request, authenticating its contents
ToDo: need to add an HSM bound key that wallet backend uses to hash the PoP (associated with UUID & Issuer_ID)?
Transaction-specific keys:
- Wallet Backend generates cryptographic seed
c_seed
for batch key derivation - Wallet Backend generates X batch key pairs \((batch\_kb\_X\_pub,
batch\_kb\_X\_priv)\) from
c_seed
and keyIDx (X
in the key name stands for the key's index) which is used- to sign credential presentation (KB-JWT/devAuth)
- to sign proof sent in Credential Request during issuance
- Wallet Instance generates ephemeral key derived from user PIN
\((pin\_derived\_eph\_pub, pin\_derived\_eph\_priv)\) which is used
- to generate PIN proof of possession
Wallet attestation PoP is bound to a PID Issuer-generated session id to prove its freshness.
Two PoPs each used to bind the PIN proof to the device and to prove possession of a PIN are bound to a Wallet Backend-generated session id to prove their freshness.
Note: The key derived from user PIN is not stored by the Wallet Instance, instead it is generated every time the user enters the PIN.
Artifacts¶
Issuance of Issuer specific Wallet Attestations¶
- Wallet Backend creates wallet app attestation: \(wallet\_app\_attestation := \text{attestation}(wb\_device\_pub)_\text{wb\_attestation\_priv}\)
Issuance of the Refresh Token¶
- PID Provider issues a refresh token containing the encrypted eID data in a JWT: \(refresh\_token := \text{sign}(\text{Enc}(eID\_data)_{pp\_data}, expiration)_{pp\_priv}\). The refresh token itself is bound to \(wb\_dpop\_priv\) via DPoP token binding mechanisms. Note that this is not the only way to protect the refresh token.
Issuance PID Batch Credential¶
- Wallet Backend generates \(CloudWalletBatchKey := \text{sign}(\text{X tuples of} (keyIDx, batch\_kb\_X\_pub))_{wb\_attestation\_priv}\) and sends it to Wallet Instance
- PID Provider issues X
- \(sd\_jwt (and Disclosures) := \text{sign}(hashed\_eID\_data, batch\_kb\_X\_pub)_{pp\_priv}\) OR
- \(mdoc := \text{sign}(hashed\_eID\_data, batch\_kb\_X\_pub)_{pp\_priv}\)
- Wallet app stores \((CloudWalletBatchKey, Credential)\) tuples with issuer_ID
Presentation¶
- Wallet Backend creates \(kb\_jwt := \text{sign}(nonce, audience)_{batch\_kb\_X\_priv}\) OR
- \(device\_Auth\) for mdoc
Dependencies¶
TODO: May want to expand to include metadata. TODO: Beautify dependencies
diagram NOTE: The connection pin_pop2 -> wb_session_id [label="bound to"]
was removed because it let the image generator crash
Sequence Diagram¶
Wallet Activation¶
User Journey: Wallet Activation - Issuer Signed - Cloud
Issuance of Issuer specific Wallet Attestations¶
Issuance of a Refresh Token used as a Seed Credential¶
User Journey: PID Issuance - Issuer Signed - Cloud
Issuance of a Batch of PIDs¶
Presentation¶
User Journey: PID Presentation - Issuer Signed - Cloud
Modules¶
Wallet proving possession of the device-bound PIN¶
These steps are used during X and Y. They are intended to proof possession of Wallet PIN and ensure that PIN is used only by a certain device.
- The Wallet generates the key pair (pin_derived_eph_pub/priv) derived from the Wallet PIN following the steps defined below
- The Wallet generates and stores a device bound key pair (device_pub1/priv1)
- The Wallet signs the wallet_backend_session_id and the device-bound public
key device_pub1 using the key pin_derived_eph_priv
- This is a Proof of Knowledge of the Wallet PIN realized as a Proof of Possession
- The wallet_backend_session_id and the device_pub1 key are included as JWT claims
- The Wallet signs over the wallet_backend_session_id and the Wallet PIN
derived public key pin_derived_eph_pub using the key device_priv1
- This binds PIN to the device
- The wallet_backend_session_id and the pin_derived_eph_pub key are included as JWT claims
Derivation of pin_derived_eph_pub
and pin_derived_eph_priv
¶
- Encrypt Wallet PIN with an AES key bound to the device. This is a
pin_secret
, which is used to generate a salt for the KDF in the next step. - Could possibly be replaced by the generation & storage of a conventional salt (due to differences in AES in iOS/Android)
- Use
pin_secret
and Wallet PIN in a KDF. This generates apin_seed
which is a cryptographic secret from the PIN and the salt (pin_secret
) used as a seed for creating the key pair in the next step - Use
pin_seed
to seed the EC key generation. This results inpin_derived_eph_pub
andpin_derived_eph_priv
.
Wallet Backend checks the Wallet Instance¶
- The Wallet Backend loads the Wallet Instance information for UUID
- The Wallet Backend checks the users Wallet PIN retry counter
- If the PIN retry counter has exceeded the defined maximum value, the request is rejected
- The Wallet Backend checks the current security state of the Wallet device
against Vulnerability Management information
- If the device is no longer secure (e.g. has known vulnerabilities), the request is rejected
Step-by-Step Description¶
Wallet Activation¶
- The User opens the Wallet.
- The User requests wallet activation.
- The Wallet requests fresh wallet_backend_session_id
- The Wallet Backend generates and links it to the session
- The Wallet Backend returns the wallet_backend_session_id to the Wallet
- The Wallet performs a wallet attestation using the OS specific attestation
services
- this attestation provides a statement about the integrity and authenticity of the Wallet and the underlying software and hardware components of the smartphone
- The Wallet generates an AES key (pin_salt)
- This key will be used to generate a salt used by the Wallet PIN key derivation mechanism
- The User enters the new Wallet PIN (most likely twice to ensure that the user has entered the intended PIN)
- The Wallet proves possession of the Wallet PIN.
- See "Wallet proving possession of the PIN" module for a detailed description of the steps.
- part of an above module
- part of an above module
- part of an above module
- The Wallet sends a request to the Wallet Backend; containing
- pin_derived_eph_pub and PoP
- device_pub and PoP
- wallet attestation incl. wallet_backend_session_id
- The Wallet Backend verifies the wallet attestation and stores security
relevant details from the attestation in the current session.
- In addition, the Wallet Backend checks whether the smartphone is on the device blacklist (from Vulnerability Management). If this is the case, no wallet attestation will be issued
- The Wallet Backend validates the PoP for pin_derived_eph_pub and device_pub
- The Wallet Backend generates a UUID as an identifier for the Wallet instance
- The Wallet Backend initializes the retry counter for the wallet PIN and stores the retry counter, pin_derived_eph_pub, device_pub, security-relevant device attributes (from session) under the UUID of the Wallet instance
- The Wallet Backend returns the UUID and a
wallet_backend_session_id
(to request a Wallet Attestation) to the Wallet
Issuance of Issuer specific Wallet Attestations¶
Case 1: wallet_backend_session_id from Wallet Activation still valid¶
- The Wallet sends a GetWalletAttestation request to the Wallet Backend;
containing
- UUID
- wallet_backend_session_id (from Wallet Activation)
Case 2: wallet_backend_session_id from Wallet Activation has expired¶
- The Wallet sends a GetWalletAttestation request to the Wallet Backend;
containing
- UUID
- wallet_backend_session_id (from Wallet Activation)
- The Wallet Backend generates and stores a new wallet_backend_session_id
- The Wallet Backend answers with a HTTP response code 440 (Login Time-out) containing a new wallet_backend_session_id (required for authentication)
- The User enters the Wallet PIN
- The Wallet proves possession of the Wallet PIN
- See "Wallet proving possession of the PIN" module for a detailed description of the steps
- part of an above module
- part of an above module
- The Wallet sends a request to the Wallet Backend; containing
- UUID
- PoP for
pin_derived_eph_priv
- PoP for
device_priv key
- The Wallet Backend checks Wallet Instance.
- See "Wallet Backend checks the Wallet Instance" module for a detailed description of the steps.
- part of an above module
- part of an above module
- The Wallet Backend validates the PoP for pin_derived_eph_pub and device_pub
Both cases conclude with these steps¶
- The Wallet Backend generates a HSM bound key pair (wb_device_pub/priv) for
Wallet Attestation and associates it with UUID and Issuer_ID
- Issuer_ID is a unique random value that is used to identify which keys belong to which issuer, which is generated by the Wallet Backend
- The Wallet Backend generates a Wallet Attestation including wb_device_pub and signs it with wb_attestation_priv
- The Wallet Backend returns the Wallet Attestation and the corresponding Issuer_ID to the Wallet.
- The Wallet stores the Wallet Attestation and corresponding Issuer_ID
Issuance of a Refresh Token used as a Seed Credential¶
- The user opens and unlocks the Wallet
- The user browses through the pre-configured credential catalog and chooses to request a PID
- The Wallet requests a fresh pid_issuer_session_id from the PID Provider
- The PID Provider generates a fresh pid_issuer_session_id linked to the issuance session
- The PID Provider returns the pid_issuer_session_id to the Wallet
- The Wallet obtains fresh wallet_backend_session_id from the Wallet Backend's session endpoint.
- part of an above module
- part of an above module
- The User enters the Wallet PIN
- The Wallet proves possession of the Wallet PIN
- See "Wallet proving possession of the PIN" module for a detailed description of the steps
- part of an above module
- part of an above module
- The Wallet prepares a Wallet Attestation PoP; containing
- audience
- expiration time
- pid_issuer_session_id
- The Wallet hashes the prepared Wallet Attestation PoP
- The Wallet sends a request to the Wallet Backend; containing
- UUID & Issuer_ID
- PoP for pin_derived_eph_priv
- PoP for device_priv
- Hash of the Wallet Attestation PoP
- The Wallet Backend checks Wallet Instance.
- See "Wallet Backend checks the Wallet Instance" module for a detailed description of the steps.
- part of an above module
- part of an above module
- The Wallet Backend validates the PoP for pin_derived_eph_pub and device_pub
- The Wallet Backend signs the hash of Wallet Attestation PoP with wb_device_priv
- The Wallet Backend returns the request to the Wallet; containing:
- signed hash of Wallet Attestation PoP
- The Wallet assembles the Wallet Attestation PoP using the signature received in a previous step
- The Wallet sends the Pushed Authorization Request to the PID Provider;
containing
- the Wallet Provider's client_id
- authorization_details for PID
- a PKCE code_challenge
- a wallet attestation and proof of possession
- The PID Provider verifies the wallet attestation and its proof of possession and validates the certification status of the Wallet Solution on a trust list
- The PID Provider returns a
request_uri
that is bound to the Pushed Authorization Request - The Wallet sends the Authorization Request; containing the PAR
request_uri
- The PID Provider responds with the first step to start the eID process with the Wallet, e.g. the tcToken, to authenticate the user.
- Further communication is exchanged to perform the eID process
- The user provides the eID PIN to the Wallet.
- Further communication is exchanged to perform the eID process
- The eID process is finished and as a final step the Wallet sends a request to the PID Provider calling the refreshURL. From now on Wallet and PID Provider are using the TLS-PSK channel generated by the eID flow.
- The PID Provider responds to the Wallet with an Authorization Response; containing the authorization code.
- The Wallet generates (and stores) a placeholder DPoP proof with a generic
(not HSM-bound) keypair to trigger an error response from the Token endpoint
neccessary to retrieve the
dpop_nonce
. - The Wallet sends a Token Request to the PID Provider; containing the placeholder DPoP proof.
- The PID Provider responds with the expected error "use_dpop_nonce";
containing the
dpop_nonce
to be used from now in the header. - The Wallet extracts and stores the
dpop_nonce
. - The Wallet now prepares the actual DPoP-proof JWT for the HSM-bound
wb_dpop_pub
including thedpop_nonce
and iat. - The Wallet hashes the DPoP proof.
- The Wallet sends a request to the Wallet Backend to sign the hash of the
DPoP-proof for the HSM bound key; containing
- hash of the PoP for the HSM bound key
wallet_backend_session_id
- The Wallet Backend validates the wallet_backend_session_id and loads session context
- The Wallet Backend signs the hash of the PoP for the HSM bound key with wb_device_priv (associated with UUID & Issuer_ID) from the HSM
- The Wallet Backend returns the signed hash of the PoP for the HSM bound key to the Wallet.
- The Wallet assembles the DPoP-proof for the HSM bound key using the signature received in a previous step
- The Wallet sends a Token Request to the PID Provider; containing:
- a DPoP Header with the DPoP-proof JWT incl.
wb_dpop_pub
- the authorization code from Authorization Response
- the PKCE code_verifier matching the code_challenge from Authorization Request
- key attestation for
wb_dpop_pub
- a DPoP Header with the DPoP-proof JWT incl.
- The PID Provider matches the code, verifies the PKCE
code_verifier
to the previously receivedcode_challenge
and verifies the DPoP proof. - The PID Provider then generates an access token and a refresh token bound to
the DPoP key wb_dpop_priv in JWT format, signed with pp_priv;
containing:
- the eID data encrypted with pp_data
- the expiration date of the refresh token
- The PID Provider generates a Token Response; containing
- DPoP-bound access token (bound to wb_dpop_priv)
- DPoP-bound refresh token (bound to wb_dpop_priv)
- a c_nonce
- The PID Provider sends the Token Response to the Wallet
- The Wallet stores the refresh token.
Issuance of a Batch of PIDs¶
- The user opens and unlocks the Wallet.
- The Wallet requests issuance of PID Batch Credentials.
- The Wallet requests fresh wallet_backend_session_id
- The Wallet Backend generates and links it to the session
- The Wallet Backend returns the wallet_backend_session_id to the Wallet
- The Wallet requests a fresh pid_issuer_session_id from the Wallet Backend
- The Wallet Backend generates a fresh wallet_backend_session_id linked to the session
- The Wallet Backend returns the pid_issuer_session_id to the Wallet
- The user enters the Wallet PIN.
- The Wallet proves possession of the Wallet PIN
- See "Wallet proving possession of the PIN" module for a detailed description of the steps
- part of an above module
- part of an above module
- The Wallet sends a Credential Request to the Wallet Backend; containing
- the UUID & the Issuer_ID,
- the PoP for
pin_derived_eph_priv
and - the PoP for
device_priv
- The Wallet Backend checks Wallet Instance.
- See "Wallet Backend checks the Wallet Instance" module for a detailed description of the steps.
- part of an above module
- part of an above module
- part of an above module
- part of an above module
- The Wallet Backend validates the PoP for pin_derived_eph_pub and device_pub.
- The Wallet Backend generates a cryptographic seed (c_seed) for the batch
credential keys and associates it with Issuer_ID
- Each batch of credentials uses it's own c_seed, previous c_seeds are discarded when a new batch of credentials for a specific Issuer are requested
- The Wallet Backend deterministically derives the required number of key
pairs incl. key IDs from c_seed
- A mechanism for key derivation will be defined later
- The Wallet Backend assembles a claim called CloudWalletBatchKey containing the the public part of the just generated key pairs and the respective key IDs and signs the claim with wb_attestation_priv.
- The Wallet Backend returns the request to the Wallet; containing
- the signed CloudWalletBatchKey
- The Wallet prepares a Wallet Attestation PoP containing
- the audience,
- the signed CloudWalletBatchKey claim and
- the pid_issuer_session_id.
- The Wallet hashes the Wallet Attestation PoP.
- The Wallet sends a signing request to the Wallet Backend containing
- hash of the Wallet Attestation PoP
- wallet_backend_session_id
- The Wallet Backend validates the wallet_backend_session_id and loads the session context.
- The Wallet Backend signs the hash of the Wallet Attestation PoP with the respective wb_device_priv.
- The Wallet Backend returns the request to the Wallet incl. the signed hash of the Wallet Attestation PoP.
- The Wallet assembles the Wallet Attestation PoP using the signature received in a previous step.
- The Wallet prepares the DPoP-proof JWT for the HSM-bound
wb_attestation_pub
including thedpop_nonce
and iat. - The Wallet hashes the DPoP proof.
- The Wallet sends a request to the Wallet Backend to sign the hash of the
DPoP-proof for the HSM bound key containing
- a hash of the PoP for the HSM bound key
- The Wallet Backend validates the wallet_backend_session_id and loads the session context.
- The Wallet Backend signs the hash of the PoP for the HSM bound key with wb_device_priv (associated with UUID & Issuer_ID) from the HSM.
- The Wallet Backend returns the signed hash of the PoP for the HSM bound key to the Wallet.
- The Wallet assembles the DPoP-proof for the HSM bound key using the signature received in a previous step.
- The Wallet sends a Token Request to the PID Provider containing
- the Wallet Attestation + PoP in the wallet attestation Header,
- the DPoP-proof JWT over
wb_attestation_pub
, - the grant_type "refresh_token",
- the DPoP-bound refresh token
- The PID Provider verifies the wallet attestation and its proof of possession and validates the certification status of the Wallet Solution on a trust list
- The PID Provider verifies the validity of the refresh token (incl. DPoP-binding).
- The PID Provider decrypts the refresh token contents with //pp_data// and stores the users identity attributes in the session.
- The PID Provider generates a Token Response with a DPop bound access token.
- The PID Provider returns a Token Response to the Wallet containing
- the DPoP bound access_token and
- the c_nonce.
- The Wallet prepares the required number of PoP (incl. audience and respective public key) for the batch credential keys and hashes them.
- The Wallet sends a BatchSigningRequest to the Wallet Backend containing
- the required number of hashed batch credential key PoPs and
- the wallet_backend_session_id.
- The Wallet Backend validates the wallet_backend_session_id and loads the session context.
- The Wallet Backend signs the hashed batch credential key PoPs with the respective batch_kb_X_priv.
- The Wallet Backend returns the request to the Wallet incl. the signed hashes of the batch credential key PoPs.
- The Wallet assembles the required amount of CredentialRequests containing the signed batch credential key PoPs.
- The Wallet prepares the DPoP-proof JWT for the HSM-bound //dev// key
including the
dpop_nonce
and iat. - The Wallet hashes the DPoP proof.
- The Wallet sends a request to the Wallet Backend to sign the hash of the
DPoP-proof for the HSM bound key containing
- a hash of the PoP for the HSM bound key and
- the wallet_backend_session_id.
- The Wallet Backend validates the wallet_backend_session_id and loads the session context.
- The Wallet Backend signs the hash of the PoP for the HSM bound key with wb_dpop_priv (associated with UUID & Issuer_ID) from the HSM.
- The Wallet Backend returns the signed hash of the PoP for the HSM bound key to the Wallet.
- The Wallet assembles the DPoP-proof for the HSM bound key using the signature received in a previous step.
- The Wallet generates a new ephemeral keypair (cre_eph_pub, cre_eph_priv).
- The Wallet creates the
credential_response_encryption
JSON object containing the following information:- a jwk containing the cre_eph_pub
- the JWE alg parameter
- the JWE enc parameter
- The Wallet sends a BatchCredentialRequest to the PID Provider containing
- DPoP proof JWT
- required amount of CredentialRequests
credential_response_encryption
object- DPoP bound access_token
- The PID Provider validates the DPoP bound access_token and checks the
signatures of the CredentialRequests.
- The PID Provider has to ensure that the batch_kb_X_pub from the CredentialRequests match to the public keys in the CloudWalletBatchKey claim from the Wallet Attestation PoP.
- (mdoc) The PID Provider creates the mdoc containing
- the issuerAuth with the batch_kb_X_pub as deviceKey and hashes of the eID data and
- the eID data as NameSpaceBytes.
- (mdoc) The PID Provider creates an encrypted JWT (JWE) using the values
received in the
credential_response_encryption
object and adds (among others) the PID credential to the payload. - (mdoc) The PID Provider sends the Credential Response JWT containing
- the required amount of PID credentials as mdoc.
- (SD-JWT) The PID Provider creates SD-JWT VC containing
- the SD-JWT with the batch_kb_X_pub as confirmation claim and hashes of the eID data and
- the eID data as Disclosures.
- (SD-JWT) The PID Provider creates an encrypted JWT (JWE) using the
values received in the
credential_response_encryption
object and adds (among others) the PID credential to the payload. - (SD-JWT) The PID Provider sends the Credential Response JWT containing
- the required amount of PID credentials as SD-JWT VC.
- The Wallet decrypts the Credential Response JWT using the cre_eph_priv.
- The Wallet matches the key IDs (from the CloudWalletBatchKey claim) to the public keys in the Credential Response by their respective public keys and stores them as tuple in a BatchCredentialStore with the issuer_ID.
Presentation¶
Note: While certain assumptions about session management of the Relaying Party are made here, the concrete implementation is considered out of scope for this document. The usual security considerations for web session management apply.
- The user browses to Relying Party (RP) website
- Browser app on the user's device opens the RP website
- RP generates an OpenID4VP Authorization Request and stores it under a
request_uri
(e.g.,https://rp.example.com/oidc/request/1234
); - The request is bound to the user's browser session
- It is signed using a key bound to the RP's metadata that can be retrieved using the RP's client_id
- It contains the ephemeral key for response encryption
- It contains RP's nonce and state
- It contains the RP's response_uri endpoint for sending the Authorization Response over POST
- RP generates a new browser session and binds the generated Authorization Request to it
- RP returns a HTML page to the browser containing a link to the Wallet (e.g.,
openid4vp://authorize?client_id=..&request_uri=https://rp.example.com/oidc/request/1234
); a cookie with the browser sessionId is set - The user clicks on the link
- The RP website navigates to the custom scheme link to launch the Wallet
- The user unlocks the Wallet (see notes below)
- The Wallet retrieves the Authorization Request from the RP website (e.g.,
https://rp.example.com/oidc/request/1234
) - The Wallet receives the Authorization Request
- The Wallet validates the Authorization Request using the RP's public key
- Was the signature valid and the key bound to the RP's metadata?
- Security: This ensures that the Authorization Request was not tampered with; it does not ensure that the party that sent the Authorization Request is the RP.
- The Wallet displays information about the identity of the Relying Party and the purpose, the user gives consent to present the PID.
- The Wallet picks a batch credential tuple (Credential incl. possible disclosures, keyID) from the batch credential store
- (mdoc) The Wallet prepares an mdoc presentation according to the presentation_definition by removing NameSpaceBytes of not disclosed attributes.
- (mdoc) The Wallet calculates the SessionTranscript according to ISO 18013-7 Annex B.4.4 from mDocGeneratedNonce, client_id, responseUri, nonce and generates the deviceAuthentication structure from SessionTranscript and NameSpaceBytes for signing and hashes the deviceAuthentication structure.
- (SD-JWT) The Wallet generates an SD-JWT VC presentation according to the presentation_definition by removing Disclosures of not disclosed attributes.
- (SD-JWT) The Wallet creates the header and payload for the KB-JWT from audience, nonce, hash of issuer-signed JWT and Disclosures for signing and hashes the payload.
- The Wallet requests a fresh wallet_backend_session_id from the Wallet Backend
- The Wallet Backend generates a fresh wallet_backend_session_id linked to the session
- The Wallet Backend returns the wallet_backend_session_id to the Wallet
- The User enters the Wallet PIN
- The Wallet proves possession of the Wallet PIN
- See "Wallet proving possession of the PIN" module for a detailed description of the steps
- The Wallet signs over the wallet_backend_session_id and the device-bound public key device_pub using the key pin_derived_eph_priv
- The Wallet signs over the wallet_backend_session_id and the Wallet PIN derived public key pin_derived_eph_pub using the key device_priv
- The Wallet sends a signing request to the Wallet Backend; containing
- UUID & issuer_ID
- PoP for pin_derived_eph_priv
- PoP for device_priv
- hash of KB-JWT/deviceAuth
- keyID (of the respective key that should be used to sign)
- The Wallet Backend checks Wallet Instance.
- See "Wallet Backend checks the Wallet Instance" module for a detailed description of the steps.
- part of an above module
- part of an above module
- The Wallet Backend validates the PoP for pin_derived_eph_pub and device_pub
- The Wallet Backend loads the c_seed associated with the issuer_ID
- The Wallet Backend deterministically regenerates (batch_kb_X_pub,
batch_kb_X_priv) from c_seed and keyIDx
- A mechanism for key derivation will be defined later
- The Wallet Backend signs the hash of KB-JWT/deviceAuth with the respective batch_kb_X_priv
- The Wallet Backend returns the signed hashes of KB-JWT/deviceAuth to the Wallet
- (mdoc) The Wallet assembles the deviceAuth from header, payload and signature and builds the presentation from deviceAuth and issuerSigned
- (SD-JWT) The Wallet assembles the KB-JWT from header, payload and signature and builds the SD-JWT VC presentation from issuer-signed JWT, Disclosures and KB-JWT
- The Wallet creates a VP token and a presentation submission from the received credential.
- Optional: The Wallet can add further presentations
- The Wallet delets the used batch credential tuple (credential, keyID)
- The Wallet sends the VP token and presentation submission to the RP (encrypted to the RP's public key rp_eph_pub).
- The RP finds a session with the state and generates a response_code for this session
- The RP returns the redirect_uri with the response_code to the Wallet
- The Wallet launches the browser with the redirect_uri and response_code.
- The browser sends the redirect_uri and response code to the RP, attaching the browser sessionId as a cookie.
- The RP looks up whether there exists a session with the sessionId from the cookie and a matching response_code
- (mdoc) The RP verifies the PID in the VP token and verifies the SessionTranscript calculated from nonce, mDocGeneratedNonce, client_id, response_uri.
- (SD-JWT) The RP verifies the SD-JWT PID in the VP token, verifies the KB-JWT using the batch_kb_X_pub in the SD-JWT, and verifies the nonce and audience in the KB-JWT
- The RP considers the user to be identified in the session context and continues the UX flow.
Extensions to the Protocols¶
This section defines extensions to the protocols required to implement this flow (Option C').
Issuer Session Endpoint (at the PID Provider)¶
Note that this extension is the same across multiple flows.
This endpoint is used by the Wallet to obtain session_id
from the PID Provider
that is used to bind wallet attestaion PoP to the session and prove its
freshness. Support for this endpoint is REQUIRED.
To fetch the session_id
, the Wallet MUST send an HTTP request using the POST
method and the application/json
media type. The PID Provider MUST return the
HTTP Status Code 200 and a session_id
parameter defined below.
session_id
: REQUIRED. String that is a unique session identifier, chosen as a cryptographically random nonce with at least 128 bits of entropy.
Communication with the Session Endpoint MUST utilize TLS.
Below is a non-normative example of a request to a Session Endpoint:
Below is a non-normative example of a response from a Session Endpoint:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"session_id": "iOiJSUzI1NiIsInR"
}
Wallet Backend's Session Endpoint¶
This endpoint is used by the Wallet to obtain session_id
from the Wallet
Backend. It is used to 1) bind two PoPs (used to bind the PIN proof to the
device and to prove possession of a PIN) to the session to prove their
freshness, and 2) authenticate the wallet instance throughout a session. Support
for this endpoint is REQUIRED.
To fetch the session_id
, the Wallet MUST send an HTTP request using the POST
method and the application/json
media type. The Wallet Backend MUST return the
HTTP Status Code 200 and a session_id
parameter defined below.
session_id
: REQUIRED. String that is a unique session identifier, chosen as a cryptographically random nonce with at least 128 bits of entropy.
Communication with the Session Endpoint MUST utilize TLS.
Below is a non-normative example of a request to a Session Endpoint:
Below is a non-normative example of a response from a Session Endpoint:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"session_id": "iOiJSUzI1NiIsInR"
}
Wallet proving possession of the PIN¶
Generation of a Proof of Knowledge of the Wallet PIN¶
A PIN-derived ephemeral public key that is needed to validate the signature on
this JWT must be included in the jwk
header parameter. There must be a JWT
header parameter typ
with the value being pin_derived_eph_pub_pop
. The JWT
payload must contain an aud
claim whose value is equal to the Wallet backend's
Identifier. wallet_backend_session_id must be included in a
wallet_backend_session_id
parameter.
Below is a non-normative example of a JWT generated as a PoP for pin derived ephemeral key:
{
"alg": "...",
"typ": "pin_derived_eph_key_pop",
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "bPP7f...gW_ao",
"y": "38_Lg...VUCfW"
}
}.
{
"wallet_backend_session_id": "123456",
"aud": "https://pid-issuer.example.com",
"device_key": {
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "...",
"y": "..."
}
}
}.[signature by the pin derived ephemeral pub key]
The jwk
claim contains the public key of a PIN-derived ephemeral key pair.
Binding PIN to the device¶
A device public key that is needed to validate the signature on this JWT must be
included in the jwk
header parameter. There must be a JWT header parameter
typ
with the value being device_key_pop
. The JWT payload must contain an
aud
claim whose value is equal to the Wallet backend's Identifier.
wallet_backend_session_id must be included in a wallet_backend_session_id
parameter.
Below is a non-normative example of a JWT, which is a PoP for device_key
:
{
"alg": "...",
"typ": "device_key_pop",
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "...",
"y": "..."
}
}.
{
"wallet_provider_session_id": "123456",
"aud": "https://pid-issuer.example.com",
"pin_derived_eph_pub": {
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "V03QP...LHN53",
"y": "xjilK...i0_tT"
}
},
}.[signature by the device_key]
Here, the jwk
claim contains a public key of a device key pair.
Passing DPoP key attestation in the token request¶
Placeholder. needs to be defined.
Usability Considerations¶
Activation¶
- The Wallet needs to be initialized on a mandatory basis
- During Activation, the user must set a Wallet PIN
- Having only one Wallet PIN has the advantage that the same PIN (eID PIN excluded) could probably also be used for other credentials (also at the same LoA). From a UX perspective, this would be a great added value for users.
- The wallet can only be used after successful Activation
Issuance of a Refresh Token used as a Seed Credential¶
- Credential catalogue should inform users in advance of what is required for the successful issuance of the PID and what steps follow
- For reasons of transparency and to increase trust, PID Provider should provide sufficient information (metadata) for the consent screen. This allows users to learn everything relevant e.g. about the PID Provider itself
- User must confirm the issuing process with the Wallet
- eID process is integrated in Wallet. No context switch to the AusweisApp is required
- Physical ID card is required for issuing the PID credential
- Online-Ausweisfunktion must be activated
- eID PIN must be set by the user (replacement of the Transport PIN) and be known to them so that they can successfully confirm the process
- User must confirm the process with the eID PIN and the Wallet PIN. The distinction between the two PINs should be easier for users than the distinction between eID PIN and Wallet PIN
- User can have a PID credential derived from the eID on several end devices at the same time
Presentation¶
- The device must be online for the PID presentation
- Relying Party should inform users in advance of what is required for the process to be completed successfully and what steps follow
- It needs to be clarified whether the wallet app needs to be unlocked in this flow and how this should be implemented.
- For reasons of transparency and to increase trust, Relying Parties should provide sufficient information (metadata) for the consent screen. This allows users to learn everything relevant e.g. about the relying party itself, privacy and data retention
- The user's data values for the requested attributes can be displayed on the consent screen
- The user only needs to enter the Wallet PIN for each PID presentation
- Physical ID card is not required for PID presentation
- It must be ensured that users return to the correct tab in the correct browser in order to continue the process or know how to get there manually if necessary (especially for iOS devices, if the process was not started in the default browser)
Privacy Considerations¶
- Unobservability:
- Issuer: the issuer-signed credentials are under sole control of the Wallet, the usage of credentials is not recognized by the Issuer, even when revocation is used
- Wallet Provider: The wallet provider receives information about when the user creates a presentation or when the user requests a new credential from a new issuer. Apart from this information, the wallet provider does not gain any insight into the transaction-specific details.
- Unlinkability:
- Outsider: Both OpenID4VCI and OpenID4VP are entirely encrypted using TLS, an outsider is therefore not able to link any transactions apart from the low-level network traffic itself
- Relying Party: the same Relying Party will not be able to link transactions when batch-issued single use credentials are issued
- Issuer: the PID Issuer will be able to link the transactions, if they store the public keys used for Batch Credential Issuance.
- Colluding Relying Parties: colluding Relying Parties will not be able to link transactions when batch-issued single use credentials are issued
- Colluding Issuers: colluding issuers will not be able to link transactions when ephemeral or issuer-specific wallet attestations are used
- Colluding Relying Party and Issuer: colluding PID Provider and Relying Party will be able to link transactions due to the nature of SD-JWT VC and ISO mdoc cryptography, if the PID Provider stores the public keys used for Batch Credential Issuance
- Repudiation:
- Repudiation of Data Authenticity: issuer-signed credentials are not repudiable due to the nature of signature algorithms. This may be mitigated by key publication, but is currently out of scope
- Repudiation of User binding: in the current design presentations are not repudiable due to the use of signature algorithms for key binding. ISO mdoc has the option to use deviceMac, which is currently not in scope.
- Selective Disclosure: both SD-JWT VC and ISO mdoc achieve selective disclosure by using salted hashes inside the MSO/SD-JWT.
Security Considerations¶
- Level of Assurance:
- Unforgeability of Credentials: unforgeability is guaranteed by the signature of the issuer-signed credential, i.e. the SD-JWT or the issuerSigned
- Freshness of Presentations: freshness is guaranteed by the nonce provided by the Relying Party, which is integrated in the KB-JWT or deviceAuth, see replay prevention.
- User Binding of presentations: User Binding is achieved by multifactor authentication employing a device bound key and a PIN that is validated inside a secure area on the Wallet Backend.
- Session Integrity:
- Issuance: PID Provider and Wallet share a pre-configured trust relationship, established through certificate pinning and other mechanisms
- Presentation: Session Integrity is supported by OpenID4VP through browser redirect, see session fixation.
- further topics:
- In Presentation Step 008, a malicious app may spoof the wallet app; an
attacker may
- on the attacker's own device: Capture the request and replay it to a victim on another device, thus having the victim identify itself in a context of the attacker (Relaying Attack breaking Identification Context); or
- on the victim's device: Capture the request and spoof the whole identification process or parts of it (Wallet App Spoofing).
- In Presentation Step 008: An attacker acting as an RP can forward a
request from a different RP.
- As long as the request remains unchanged, we're in the Relaying Attack breaking Identification Context
- If the attacker changes anything in the request, this will break the
signature. The attacker could otherwise attempt to
- insert the attacker's own ephemeral key, leading to an SD-JWT artifact that could be used in a different flow between the attacker and some other RP.
- modify state or nonce or other data. Q: Any useful attacks resulting from this?
- In Presentation Step 041: The RP must not consider the user identified at this point; it is important to have the browser redirect in the later steps.
- In Presentation Step 008, a malicious app may spoof the wallet app; an
attacker may
Open Topics¶
- Security of PID Provider Interface in Issuance Step 3ff:
- Are additional steps required to protect the interface to the PID Provider?
- What exactly is the transaction binding?
- How is the eID process tied to the process at the PID provider?