Skip to content

PID Option C': Signed Credential with Cloud Support

Basic Idea

The PID Provider issues a signed DPoP-bound refresh token to the Wallet, which can be stored for a longer period. This token represents a Seed Credential that is used to acquire a set of single-use Batch Credentials from the Provider. The Wallet can then present these Batch Credentials (including a proof of possession by signing over a wallet-backend-held key) to the Relying Party. This way the user only needs to present the physical ID card once - in order to acquire the Seed Credential - instead of every presentation. 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.

Cryptographic Formats

Long-Term keys:

  • Wallet app generates device key pair \((wallet\_auth\_pub, wallet\_auth\_priv)\) which is used
  • to generate proof of possession of wallet attestation
  • to bind the PIN proof to the device
  • Wallet app generates AES key \((pin\_salt)\) which is used
  • to derive the pin_derived_eph_priv used as PoP of Wallet PIN
  • Wallet Backend has long-term key pair \((wallet\_backend\_pub, wallet\_backend\_priv)\) which is used
  • to sign wallet instance's wallet attestations
  • Wallet Backend generates HSM bound long-term key pair \((device\_pub, device\_priv)\) which is used
  • as the DPoP key
  • PID Provider has HSM bound long-term key pair \((pp\_pub, pp\_priv)\)
  • to sign over the issued Credential
  • PID Provider has symmetric long-term key \((pp\_data)\) which is used
  • to encrypt eID data inside the refresh token and to MAC the contents 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

Transaction-specific keys:

  • Wallet Backend generates cryptographic seed c_seed for batch key derivation
  • Wallet Backend generates X batch key pairs for credential signing \((batch\_kb\_X\_pub, batch\_kb\_X\_priv)\) from c_seed and keyIDx (X in the key name stands for the key's index)
  • Wallet app generates ephemeral key derived from user PIN \((pin\_derived\_eph\_pub, pin\_derived\_eph\_priv)\) which is used
  • to generate PIN proof of possession
  • (This key is ephemeral since it is never stored and instead derived from Wallet PIN and pin_salt each time)

Artifacts

Wallet Initialization

  • Wallet app creates wallet attestation: \(client\_attestation := \text{app\_attestation}(client\_att\_nonce)_\text{OS\_key}\)

Issuance of Issuer specific Wallet Attestations

  • Wallet Backend creates wallet app attestation: \(wallet\_app\_attestation := \text{attestation}(device\_pub)_\text{wallet\_backend\_priv}\)

Issuance of the Refresh Token

  • PID Provider issues a refresh token containing the encrypted eID data in a JWT: \(refresh\_token := \text{MAC}(\text{Enc}(eID\_data)_{pp\_data}, expiration)_{pp\_data}\). The refresh token itself is bound to \(device\_priv\) via DPoP token binding mechanisms. Note that this is not the only way to protect the refresh token. E.g., the MAC could be replaced by a signature instead.

Issuance PID Batch Credential

  • Wallet Backend generates \(CloudWalletBatchKey := \text{sign}(\text{X tuples of} (keyIDx, batch\_kb\_X\_pub))_{wallet\_backend\_priv}\) and sends it to Wallet app
  • 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

G cluster_pid_provider PID Provider cluster_wallet_backend Wallet Backend cluster_wallet Wallet seed_C refresh token device_key 🗝 (device_priv, device_pub) seed_C->device_key bound to batch_C batch credential X over user eID Data batch_key 🗝 (batch_kb_X_priv, batch_kb_X_pub) batch_C->batch_key bound to pp 🗝 (pp_priv, pp_pub) pp->batch_C sign pp_data 🗝 pp_data pp_data->seed_C MAC pp_data->seed_C encrypt eID data w_attest wallet attestation (device_pub) pid_issuer_session_id PID issuer session id seed cryptographic seed seed->batch_key derive X times dpop DPoP proof (dpop_nonce) device_key->dpop sign w_attest_POP_n_auth wallet attestation PoP (pid_issuer_session_id, aud, exp) HSM-bound key PoP(c_nonce, aud) device_key->w_attest_POP_n_auth sign wallet_backend 🗝 (wallet_backend_priv, wallet_backend_pub) wallet_backend->w_attest sign cr_and_kb Credential Request proof (batch_kb_X_pub, aud, c_nonce) KB-JWT/devAuth batch_key->cr_and_kb sign pin_salt 🗝 PIN salt key pin_key 🗝 (pin_derived_eph_priv, pin_derived_eph_pub) pin_salt->pin_key derive (with PIN) pin_key->pid_issuer_session_id sign wallet_auth 🗝 (wallet_auth_priv, wallet_auth_pub) wallet_auth->pid_issuer_session_id sign

Sequence Diagram

Wallet Initialization

User Journey: Wallet Initialization - Issuer Signed - Cloud

Wallet InitializationHolderHolderUser's EUDI Wallet BackendLong-term Key: (wallet_backend_pub,wallet_backend_priv)User's EUDI Wallet BackendLong-term Key: (wallet_backend_pub,wallet_backend_priv)User's EUDI Wallet Instance (eID-Client)Device Key: (wallet_auth_pub,wallet_auth_priv)Wallet Attestion bound todevice_pubPIN Salt:pin_saltUser's EUDI Wallet Instance (eID-Client)Device Key: (wallet_auth_pub,wallet_auth_priv)Wallet Attestion bound todevice_pubPIN Salt:pin_salt(001)open walletScreen: launch_wallet(002)request wallet initalizationScreen: initialization_startScreen: initialization(003)[TLS] HTTP POST</session_endpoint> walletattestation nonce(004)generate and store walletattestation nonce,wallet_backend_session_id(005)[TLS] HTTP 200 <walletattestation nonce,wallet_backend_session_id>(006)perform and get walletattestation from OSattestation service (walletattestation nonce)Platform (Android/iOS) specific mechanismsare used for  attestation. This guaranteeswith high certainty that the Wallet app andunderlying OS is trustworthy and notmanipulated(007)generate and store AES key:pin_salt(008)set Wallet PINScreen: initialization_set_wallet_pin(009)generate key pair(pin_derived_eph_pub,pin_derived_eph_priv) fromKDF(Wallet PIN,encrypt_AES(pin_salt, WalletPIN))used as knowledge factor for authenticationwith wallet backend, keys are discarded afterevery use(010)generate device bound keypair (wallet_auth_pub,wallet_auth_priv)used as possession factor for authenticationwith wallet backend(011)generate PoP for pin derivedephemeral key -sign(wallet_backend_session_id|wallet_auth_pub)pin_derived_eph_priv(012)generate PoP for wallet app'skey -sign(wallet_backend_session_id|pin_derived_eph_pub)wallet_auth_privproof of ownership must be built from boththe device bound key and the key derivedfrom the user PIN(013)[TLS] HTTP POST<pin_derived_eph_pub,wallet_auth_pub, PoP for pinderived ephemeral key, PoPfor wallet app's key, walletattestation>(014)verify wallet attestationcheck device black list(Vulnerability Management)store security relevant deviceattributes in session(015)check signatures(016)generate UUID as identifier forthe wallet instance(017)set up retry counter and storeretry counter,pin_derived_eph_pub,wallet_auth_pub, securityrelevant device attributesfrom session under UUID(018)[TLS] HTTP 200 <UUID,SessionID>session has to be short lived(019)store UUID and SessionIDScreen: home

Issuance of Issuer specific Wallet Attestations

Issuance of Issuer specific Wallet AttestationsHolderHolderUser's EUDI Wallet BackendLong-term Key: (wallet_backend_pub,wallet_backend_priv)Long-term Key: (device_pub,device_priv)User's EUDI Wallet BackendLong-term Key: (wallet_backend_pub,wallet_backend_priv)Long-term Key: (device_pub,device_priv)User's EUDI Wallet Instance (eID-Client)Device Key: (wallet_auth_pub,wallet_auth_priv)Wallet Attestion bound todevice_pubPIN Salt:pin_saltUser's EUDI Wallet Instance (eID-Client)Device Key: (wallet_auth_pub,wallet_auth_priv)Wallet Attestion bound todevice_pubPIN Salt:pin_saltalt[Session from Wallet Initialization still Valid](001)[TLS] HTTP POST<getWalletAttestation(UUID,SessionID (from WalletInitialization))>[Session from Wallet Initialization no longer Valid](002)[TLS] HTTP POST<getWalletAttestation(UUID,SessionID (from WalletInitialization))>(003)generate and storepid_issuer_session_id(004)[TLS] HTTP 440<pid_issuer_session_id>(005)input Wallet PINScreen: wallet_pin(006)generate key pair(pin_derived_eph_pub,pin_derived_eph_priv) fromKDF(Wallet PIN,encrypt_AES(pin_salt, WalletPIN))(007)generate PoP for pin derivedephemeral key -sign(pid_issuer_session_id |wallet_auth_pub)pin_derived_eph_priv(008)generate PoP for wallet app'skey -sign(pid_issuer_session_id |pin_derived_eph_pub)wallet_auth_priv(009)[TLS] HTTP POST <UUID, PoPfor pin derived ephemeralkey, PoP for wallet app's key>(010)load instance information forUUIDInstance Information: retry counter,pin_derived_eph_pub, device_pub, securityrelevant device attributes(011)check user pin retryrequest would be refused in case of lockedpin(012)check security of deviceagainst VulnerabilityManagement informationPIN will not be validated if device is known tobe insecure(013)check signaturesuser pin retry counter would be increased incase of failed pin signature check, user pinwould be locked after threshold is exeeded(014)generate HSM bound key pair(device_pub, device_priv) andassociate with UUID andissuer_IDissuer_ID is a random value generated by thewallet backend(015)Generate wallet attestationwith device_pub and sign withwallet_backend_privwallet attestation as defined in "OpenID4VCHigh Assurance Interoperability Profile withSD-JWT VC - draft 00"(016)[TLS] HTTP 200<walletAttestation, issuer_ID>(017)store walletAttestation underissuer_ID

Issuance of the Refresh Token

User Journey: PID Issuance - Issuer Signed - Cloud

PID Issuance of the Refresh Token over OpenID4VCIPID HolderPIN: (user_pin)PID HolderPIN: (user_pin)User's EUDI Wallet BackendLong-term Key: (wallet_backend_pub,wallet_backend_priv)Long-term Key: (device_pub,device_priv)User's EUDI Wallet BackendLong-term Key: (wallet_backend_pub,wallet_backend_priv)Long-term Key: (device_pub,device_priv)User's EUDI Wallet Instance (eID-Client)Device Key: (wallet_auth_pub,wallet_auth_priv)Wallet Attestion bound todevice_pubPIN Salt:pin_saltUser's EUDI Wallet Instance (eID-Client)Device Key: (wallet_auth_pub,wallet_auth_priv)Wallet Attestion bound todevice_pubPIN Salt:pin_saltPID Provider(eService+eID Server)Long-term Key: (pp_pub,pp_priv)eID data encryption Key: (pp_data)PID Provider(eService+eID Server)Long-term Key: (pp_pub,pp_priv)eID data encryption Key: (pp_data)(001)open wallet, unlock walletScreen: launch_walletScreen: unlock_wallet(002)request issuance of PIDScreen: credential_catalogPID Issuer and EUDI Wallet have inherenttrust relationship, metadata may bepre-configured or retrieved(003)[TLS] HTTP POST</session_endpoint> RequestIssuer session(004)generate and storeissuer_session_id(005)[TLS] HTTP 200<issuer_session_id>Screen: consent_add_credentialcheck pin(006)[TLS] HTTP POST</session_endpoint> WalletBackend session id(007)generate and storewallet_backend_session_id(008)[TLS] HTTP 200<wallet_backend_session_id>(009)enter Wallet PINScreen: wallet_pin(010)derive key pair(pin_derived_eph_pub,pin_derived_eph_priv) fromKDF(Wallet PIN,encrypt_AES(pin_salt, WalletPIN))(011)generate PoP for pin derivedephemeral key -sign(wallet_backend_session_id|wallet_auth_pub)pin_derived_eph_priv(012)generate PoP for wallet app'skey -sign(wallet_backend_session_id|pin_derived_eph_pub)wallet_auth_privproof must be built from both the device keyand the key derived from the user PIN, mightrequire new proof typeRemote HSM(013)prepare wallet attestation POPwith audience, expiration timeand pid_issuer_session_id(014)hash wallet attestation PoP(015)[TLS] HTTP POST <UUID,issuer_ID, PoP for pin derivedephemeral key, PoP for walletapp's key, hash of walletattestation PoP>(016)load instance information forUUIDInstance Information: retry counter,pin_derived_eph_pub, wallet_auth_pub,security relevant device attributes(017)check user pin retryrequest would be refused in case of lockedpin(018)check security of deviceagainst VulnerabilityManagement informationPIN will not be validated if device is known tobe insecure(019)check signaturesuser pin retry counter would be increased incase of failed pin signature check, user pinwould be locked after threshold is exeeded(020)sign hash of wallet attestationPoP with device_priv(021)[TLS] HTTP 200 <send signedhash of wallet attestation PoP,SessionID>(022)assemble wallet attestationPoP(023)[TLS] HTTP POST PAR (PKCEcode_challenge, WalletAttestation + PoP, client_id)(024)verify wallet attestation & PoPcheck Wallet Provider solutionstatus on trust list(025)[TLS] HTTP 200 request_uriAttestation guarantees with high certaintythat Wallet is trustworthy and notmanipulated(026)[TLS] HTTP GET <OpenID4VCIAuthorizationRequest(request_uri)>Screen: eid_startRead eID or Smart eID acc. to BSI TR-03130(027)[TLS] HTTP 200 starting theeID Process(028)eID Process(029)<eID-PIN>Screen: eid_pin(030)eID Process(031)[TLS] HTTP GET finishing theeID process with refreshUrlScreen: eid_nfc_data(032)[TLS] HTTP 302 <OpenID4VCIAuthorizationResponse(authorizationcode)>(033)generate placeholder DPoPproof with generic (notHSM-bound) keypair(034)[TLS] HTTP POST <TokenRequest(DPoP Header withplaceholder proof,authorization code, PKCEcode_verifier)>(035)[TLS] HTTP 400 <Bad Request(dpop_nonce, error:"use_dpop_nonce")>The Wallet should check at this point,whether the Token Endpoint delivered theexpected error and nonce. If not, this needsto be handled (retry or abort gracefully).(036)store dpop_nonceRemote HSM(037)prepare DPoP-proof JWT forHSM bound key incl.dpop_nonce and iat(038)hash(DPoP-proof JWT for HSMbound key)(039)[TLS] HTTP POST <hash ofPoP, SessionID>(040)check SessionID(041)sign hash of PoP withdevice_priv (associated withUUID & Issuer_ID)(042)[TLS] HTTP 200 <signed hashof proof>(043)assemble DPoP-proof JWT forHSM bound key with signature(044)[TLS] HTTP POST <TokenRequest(DPoP Header withproof JWT incl. device_pub,authorization code, PKCEcode_verifier)>(045)lookup authorization code verify PKCE challenge verify DPoP proof(046)create refresh_token bound todevice_priv in JWT format with eID data symmetricallyencrypted with pp_data andits expiration date and MAC the JWT withpp_data(047)generate TokenResponse withaccess and refresh tokenDPoP bound to device_priv(048)[TLS] HTTP 200 <TokenResponse(DPoP-boundaccess_token, DPoP-boundrefresh_token)>The access token and the refresh token areboth bound to the same key.(049)store DPoP bound refreshtokenScreen: successScreen: home

Issuance PID Batch Credentials

PID Batch Credential Issuance over OpenID4VCIPID HolderPIN: (user_pin)PID HolderPIN: (user_pin)User's EUDI Wallet BackendLong-term Key: (wallet_backend_pub,wallet_backend_priv)Long-term Key: (device_pub,device_priv)User's EUDI Wallet BackendLong-term Key: (wallet_backend_pub,wallet_backend_priv)Long-term Key: (device_pub,device_priv)User's EUDI Wallet Instance (eID-Client)Device Key: (wallet_auth_pub,wallet_auth_priv)Wallet Attestion bound towallet_auth_pubPIN Salt:pin_saltUser's EUDI Wallet Instance (eID-Client)Device Key: (wallet_auth_pub,wallet_auth_priv)Wallet Attestion bound towallet_auth_pubPIN Salt:pin_saltPID Provider(eService+eID Server)Long-term Key: (pp_pub,pp_priv)eID data encryption Key: (pp_data)PID Provider(eService+eID Server)Long-term Key: (pp_pub,pp_priv)eID data encryption Key: (pp_data)(001)open wallet, unlock walletScreen: launch_walletScreen: unlock_wallet(002)request issuance of PID BatchCredentialscheck pin and generate batch keys(003)[TLS] HTTP POST</session_endpoint> RequestIssuer session(004)generate and storeissuer_session_id(005)[TLS] HTTP 200<issuer_session_id>(006)[TLS] HTTP POST</session_endpoint> WalletBackend session id(007)generate and storewallet_backend_session_id(008)[TLS] HTTP 200<wallet_backend_session_id>(009)enter Wallet PINScreen: wallet_pin(010)derive key pair(pin_derived_eph_pub,pin_derived_eph_priv) fromKDF(Wallet PIN,encrypt_AES(pin_salt, WalletPIN))(011)generate PoP for pin derivedephemeral key -sign(wallet_backend_session_id|wallet_auth_pub)pin_derived_eph_priv(012)generate PoP for wallet app'skey -sign(wallet_backend_session_id|pin_derived_eph_pub)wallet_auth_privproof must be built from both the device keyand the key derived from the user PIN, mightrequire new proof type(013)[TLS] HTTP POST<CredentialRequest(UUID,issuer_ID, PoP for pin derivedephemeral key, PoP for walletapp's key)>(014)load instance information forUUIDInstance Information: retry counter,pin_derived_eph_pub, device_pub, securityrelevant device attributes(015)check user pin retryrequest would be refused in case of lockedpin(016)check security of deviceagainst VulnerabilityManagement informationPIN will not be validated if device is known tobe insecure(017)check signaturesuser pin retry counter would be increased incase of failed pin signature check, user pinwould be locked after threshold is exeeded(018)generate cryptographic seed(c_seed) for batch credentialsand associate with issuer_ID(019)derive X key pairs (incl. keyIDs) (keyIDx,(batch_kb_X_pub,batch_kb_X_priv))deterministically from c_seed(020)assembleCloudWalletBatchKey claimwith X tuples (keyIDx,batch_kb_X_pub) and signwith wallet_backend_priv(021)[TLS] HTTP 200 <signedCloudWalletBatchKey,SessionID>Remote HSM(022)generate wallet attestationPOP with audience, expirationtime, CloudWalletBatchKeyand pid_issuer_session_id(023)hash wallet attestation PoP(024)[TLS] HTTP POST <hash ofwallet attestation PoP, SessionID>(025)validate SessionID and loadsession context(026)sign hash of wallet attestationPoP with device_priv(027)[TLS] HTTP 200 <signature forhash of wallet attestationPoP>(028)assemble wallet attestationPoP with signaturePID batch issuanceRemote HSM(029)prepare DPoP-proof JWT forHSM bound key incl.dpop_nonceWhether or not a fresh dpop_nonce needs tobe retrieved here is dependet on how quicklythe previously issued nonce expires. Thisdepends on the implementation of the TokenEndpoint.(030)hash(DPoP-proof JWT for HSMbound key)(031)[TLS] HTTP POST <hash ofPoP, SessionID>(032)check SessionID(033)sign hash of PoP withdevice_priv (associated withUUID & Issuer_ID)(034)[TLS] HTTP 200 <signed hashof proof>(035)assemble DPoP-proof JWT forHSM bound key with signature(036)[TLS] HTTP POST <TokenRequest(wallet_attestationand wallet attestation PoP(includingpid_issuer_session_id), DPoP-proof JWT overdevice_pub, grant_type=refresh_token,DPoP bound refresh token)>wallet attestation Header includes Walletattestation and PoP. DPoP Header includesDPoP proof JWT. refresh_token contains theeID data encrypted with pp_pub(037)verify wallet attestation & PoPcheck Wallet Provider solutionstatus on trust list(038)verify the validity of therefresh token (incl.DPoP-binding)(039)verify that the walletattestation PoP belongs to therefresh token (bound to thesame public key)(040)decrypt refresh tokencontents with pp_data andstore user data in session(041)generate TokenResponse withDPoP-bound access_token(042)[TLS] HTTP 200 <TokenResponse(DPop boundaccess_token, c_nonce)>(043)prepare X batch credentialkey PoPs (incl. audience,batch_kb_X_pub, c_nonce)and hash them(044)[TLS] HTTP POST<BatchSigningRequest(Xhashed PoPs, sessionID)>(045)validate SessionID and loadsession context(046)sign hashed PoPs X withbatch_kb_X_priv(047)[TLS] HTTP 200 <X signedhashed PoPs>(048)assemble XCredentialRequests withsigned batch credential keyPoPsRemote HSM(049)prepare DPoP-proof JWT forHSM bound key incl.dpop_nonceWhether or not a fresh dpop_nonce needs tobe retrieved here is dependet on how quicklythe previously issued nonce expires. Thisdepends on the implementation of the TokenEndpoint.(050)hash(DPoP-proof JWT for HSMbound key)(051)[TLS] HTTP POST <hash ofPoP, SessionID>(052)check SessionID(053)sign hash of PoP withdevice_priv (associated withUUID & Issuer_ID)(054)[TLS] HTTP 200 <signed hashof proof>(055)assemble DPoP-proof JWT forHSM bound key with signature(056)generate credential responseencryption key pair(cre_eph_pub, cre_eph_priv)(057)createcredential_response_encryptionobject with jwk containingcre_eph_pub(058)[TLS] HTTP POST <BatchCredential Request (DPoPproof JWT, XCredentialRequests,credential_response_encryptionobject, DPoP-boundaccess_token)>(059)validate DPoP-boundaccess_token and checksignatures of theCredentialRequest proofsensure that the batch_kb_X_pub from theCredentialRequests match to the public keysin the CloudWalletBatchKey claim from thewallet attestation PoPalt[ISO mdoc](060)create mdoc with eID dataand batch_kb_X_pub, signedby pp_priv, and matchingNameSpaceBytes(061)generate encrypted credentialresponse JWT using the valuesreceived in thecredential_response_encryptionobject(062)[TLS] HTTP 200 <JWT(BatchCredential Response(mdoc))>[SD-JWT VC](063)create SD-JWT VC with eIDdata and batch_kb_X_pub,signed by pp_priv, andmatching Disclosures(064)generate encrypted credentialresponse JWT using the valuesreceived in thecredential_response_encryptionobject(065)[TLS] HTTP 200 JWT(BatchCredential Response(SD-JWTVC, Disclosures))(066)decrypt batch credentialresponse JWT and retrieve PID(067)match keyIDx fromCloudWalletBatchKey claim toSD-JWT/mdoc Credentials bytheir respective public key(batch_kb_X_pub) and storeas tuple inBatchCredentialStore withissuer_ID

Presentation

User Journey: PID Presentation - Issuer Signed - Cloud

PID presentation over OpenID4VPPID HolderPIN: (user_pin)PID HolderPIN: (user_pin)Browser App(same device)Browser App(same device)Relying PartyLong-term Key: (rp_pub,rp_priv)Relying PartyLong-term Key: (rp_pub,rp_priv)User's EUDI Wallet Instance (eID-Client)Device Key: (wallet_auth_pub,wallet_auth_priv)Wallet Attestion bound towallet_auth_pubPIN Salt:pin_saltUser's EUDI Wallet Instance (eID-Client)Device Key: (wallet_auth_pub,wallet_auth_priv)Wallet Attestion bound towallet_auth_pubPIN Salt:pin_saltUser's EUDI Wallet BackendLong-term Key: (wallet_backend_pub,wallet_backend_priv)Long-term Key: (device_pub,device_priv)User's EUDI Wallet BackendLong-term Key: (wallet_backend_pub,wallet_backend_priv)Long-term Key: (device_pub,device_priv)(001)browse to applicationScreen: same_device_relying_party_start(002)[TLS] HTTP GET <rp-website>(003)generate ephemeral key pair(rp_eph_pub, rp_eph_priv)(004)create OpenID4VPAuthorization Request, sign with rp_priv, store under <request_uri>Authorization Request includes:- presentation_definition- purpose- state- nonce- rp_eph_pub- response_uri(005)generate new browser sessionsession_id and bind theauthorization request to it(006)[TLS] HTTP 200 HTMLcontaining wallet-linkopenid4vp://authorize?")client_id=..&request_uri=<request_uri>Set-Cookie: sid=session_id(007)action to start flow/launchwallet(008)launch with wallet-linkopenid4vp://Potential security risk: Wallet app may bespoofed by malicious appScreen: launch_wallet(009)unlock walletmay be moved to later point in flow orremoved, see notes.Screen: unlock_wallet(010)[TLS] HTTP GET<request_uri>Potential privacy risk: RP learns existance ofwallet app and potentially identifyinginformation (e.g., headers)(011)[TLS] HTTP 200 <JWT-SecuredAuthorization Request>(012)validate AuthorizationRequest JWT using rp_pub(013)user consent to present PID toRelying Party for givenpurposeScreen: consent_present_credential(014)pick batch credential tuple(Credential, keyIDx) frombatch credential storealt[ISO mdoc](015)prepare mdoc presentationaccording to<presentation_definition> byremoving unnecessaryNameSpaceBytes(016)calculate SessionTranscript(mDocGeneratedNonce,client_id, responseUri, nonce)and generate deviceAuthpayload and hash[SD-JWT VC](017)prepare SD-JWT presentationaccording to<presentation_definition> byremoving unnecessaryDisclosures(018)generate KB-JWT payload(nonce, audience, hash ofSD-JWT and disclosures) andhashcheck pin & sign in remote HSM(019)[TLS] HTTP POST</session_endpoint> PIDissuer session id(020)generate and storepid_issuer_session_id(021)[TLS] HTTP 200<pid_issuer_session_id>(022)input Wallet PINScreen: wallet_pin(023)generate key pair(pin_derived_eph_pub,pin_derived_eph_priv) fromKDF(Wallet PIN,encrypt_AES(pin_salt, WalletPIN))(024)generate PoP for pin derivedephemeral key -sign(pid_issuer_session_id |wallet_auth_pub)pin_derived_eph_priv(025)generate PoP for wallet app'skey -sign(pid_issuer_session_id |pin_derived_eph_pub)wallet_auth_priv(026)[TLS] HTTP POST <UUID,issuer_ID, PoP for pin derivedephemeral key, PoP for walletapp's key, hash ofKB-JWT/deviceAuth , keyIDx>(027)load instance information forUUIDInstance Information: retry counter,pin_derived_eph_pub, device_pub, securityrelevant device attributes(028)check user pin retryrequest would be refused in case of lockedpin(029)check security of deviceagainst VulnerabilityManagement informationPIN will not be validated if device is known tobe insecure(030)check signaturesuser pin retry counter would be increased incase of failed pin signature check, user pinwould be locked after threshold is exeeded(031)identify c_seed with issuer_IDand load(032)deterministically regenerate(batch_kb_X_pub,batch_kb_X_priv) from c_seedand keyIDx(033)sign hash ofKB-JWT/deviceAuth hash withbatch_kb_X_priv(034)[TLS] HTTP 200 <signedKB-JWT/deviceAuth hash>alt[ISO mdoc](035)assemble deviceAuth andbuild presentation withissuerSigned anddeviceSigned(deviceAuth)[SD-JWT VC](036)assemble KB-JWT and buildpresentation with SD-JWT,selected Disclosures andKB-JWT(037)create vp_token andpresentation_definition(038)add optionally otherpresentations according tothe presentation_definition(039)delete used batch credentialand matching keyIDx(040)[TLS] HTTP POST<AuthorizationResponse(presentation_submission,vp_token, state)>(041)look up state in existingsessionscreate & store response_codefor session(042)[TLS] HTTP 200 <redirect_uriincl. response_code>(043)launch browser with<redirect_uri withresponse_code>Screen: success_redirect(044)[TLS] HTTP GET <redirect_uriwith response_code>Cookie: sid=session_id(045)look up session withsession_id and matchresponse_codealt[ISO mdoc](046)verify contents of<vp_token>:- verify mdoc issuerAuth PID- verify deviceAuth withbatch_kb_X_pub fromissuerAuth- calculate and validatecorrect SessionTranscript[SD-JWT VC](047)verify contents of<vp_token>:- verify SD-JWT PID- verify KB-JWT withbatch_kb_X_pub from SD-JWT- validate nonce and audiencefrom KB-JWT(048)[TLS] HTTP 200 <HTML withcontinued UX flow>Screen: same_device_relying_party_identified

Step-by-Step Description

Wallet Initialization

  1. The User opens the Wallet
  2. The User requests wallet initialization
  3. The Wallet requests fresh wallet attestation nonce and PID issuer session id
  4. The Wallet Backend generates and links them to the session
  5. The Wallet Backend returns the wallet attestation nonce and PID issuer session id to the Wallet
  6. 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
  7. 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
  8. The User enters the new Wallet PIN (most likely twice to ensure that the user has entered the intended PIN)
  9. The Wallet generates the key pair (pin_derived_eph_pub/priv) derived from the Wallet PIN
    • This key pair is used as knowledge factor for authentication with the Wallet Backend and is discarded after every use
    • Description of the key derivation:
      • encryption of the Wallet PIN with an AES key bound to the device -> pin_secret
        • Used to generate a salt for the KDF used 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 of pin_secret and Wallet PIN in a KDF -> pin_seed
        • Generates a cryptographic secret from the PIN and the salt (pin_secret) for use as a seed for creating the key pair in the next step
      • use pin_seed to seed the EC key generation -> pin_derived_eph_pub and pin_derived_eph_priv
  10. The Wallet generates and stores a device bound key pair (wallet_auth_pub/priv)
    • This key pair is used as possession factor for authentication with the Wallet Backend
  11. The Wallet signs the PID issuer session id and the device-bound public key wallet_auth_pub using the key pin_derived_eph_priv
    • This is a Proof of Knowledge (over the Wallet PIN) realized as a Proof of Possession
    • The PID issuer session id and the wallet_auth_pub key are included as JWT claims
  12. The Wallet signs over the PID issuer session id and the Wallet PIN derived public key pin_derived_eph_pub using the key wallet_auth_priv
    • This is a Proof of Possession over the device bound key
    • The PID issuer session id and the pin_derived_eph_pub key are included as JWT claims
  13. The Wallet sends a request to the Wallet Backend; containing
    • pin_derived_eph_pub and PoP (from Step 11)
    • wallet_auth_pub and PoP (from Step 12)
    • wallet attestation incl. wallet attestation nonce (from Step 6)
  14. 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
  15. The Wallet Backend validates the PoP for pin_derived_eph_pub and wallet_auth_pub
  16. The Wallet Backend generates a UUID as identifier for the Wallet instance
  17. The Wallet Backend initializes the retry counter for the wallet PIN and stores the retry counter, pin_derived_eph_pub, wallet_auth_pub, security-relevant device attributes (from session) under the UUID of the Wallet instance
  18. The Wallet Backend returns the UUID and a SessionID (to request a Wallet Attestation) to the Wallet

Issuance of Issuer specific Wallet Attestations

Case 1: SessionID from Wallet Initialization still valid

  1. The Wallet sends a GetWalletAttestation request to the Wallet Backend; containing
    • UUID
    • SessionID (from Wallet Initialization)

Case 2: SessionID from Wallet Initialization has expired

  1. The Wallet sends a GetWalletAttestation request to the Wallet Backend; containing
    • UUID
    • SessionID (from Wallet Initialization)
  2. The Wallet Backend generates and stores a new wallet_provider_session_id
  3. The Wallet Backend answers with a HTTP response code 440 (Login Time-out) containing a new PID issuer session id (required for authentication)
  4. The User enters the Wallet PIN
  5. The Wallet generates the key pair (pin_derived_eph_pub/priv) derived from the Wallet PIN
    • See Wallet Initialization Steps 9 to 12 for a detailed description of the authentication process
  6. The Wallet signs over the PID issuer session id and the device-bound public key wallet_auth_pub using the key pin_derived_eph_priv
  7. The Wallet signs over the PID issuer session id and the Wallet PIN derived public key pin_derived_eph_pub using the key wallet_auth_priv
  8. The Wallet sends a request to the Wallet Backend; containing
    • UUID
    • user_pin_derived_eph_priv signed pid_issuer_session_id
    • wallet_auth key signed pid_issuer_session_id
  9. The Wallet Backend loads the Wallet Instance information for UUID
  10. 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
  11. 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
  12. The Wallet Backend validates the PoP for pin_derived_eph_pub and wallet_auth_pub

Both cases conclude with these steps

  1. The Wallet Backend generates a HSM bound key pair (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
  2. The Wallet Backend generates a Wallet Attestation including device_pub and signs it with wallet_backend_priv
  3. The Wallet Backend returns the Wallet Attestation and the corresponding Issuer_ID to the Wallet.
  4. The Wallet stores the Wallet Attestation and corresponding Issuer_ID

Issuance of the Refresh Token

  1. The user opens and unlocks the Wallet
  2. The user browses through the pre-configured credential catalog and chooses to request a PID
  3. The Wallet requests a fresh wallet attestation nonce from the PID Provider (pid_issuer_session_id)
  4. The PID Provider generates a fresh nonce linked to the issuance session
  5. The PID Provider returns the wallet attestation nonce to the Wallet
  6. The Wallet requests a fresh PID issuer session id from the Wallet Backend
  7. The Wallet Backend generates a fresh Wallet Backend session id linked to the session
  8. The Wallet Backend returns the PID issuer session id to the Wallet
  9. The User enters the Wallet PIN
  10. The Wallet generates the key pair (pin_derived_eph_pub/priv) derived from the Wallet PIN
    • See Wallet Initialization Steps 9 to 12 for a detailed description of the authentication process
  11. The Wallet signs over the PID issuer session id and the device-bound public key wallet_auth_pub using the key pin_derived_eph_priv
  12. The Wallet signs over the PID issuer session id and the Wallet PIN derived public key pin_derived_eph_pub using the key wallet_auth_priv
  13. The Wallet prepares a Wallet Attestation PoP; containing
    • audience
    • expiration time
    • pid_issuer_session_id
  14. The Wallet hashes the prepared Wallet Attestation PoP
  15. The Wallet sends a request to the Wallet Backend; containing
    • UUID & Issuer_ID
    • PoP for pin_derived_eph_priv
    • PoP for wallet_auth_priv
    • Hash of the Wallet Attestation PoP
  16. The Wallet Backend loads the Wallet Instance information for UUID
  17. 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
  18. 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
  19. The Wallet Backend validates the PoP for pin_derived_eph_pub and wallet_auth_pub
  20. The Wallet Backend signs the hash of Wallet Attestation PoP with device_priv
  21. The Wallet Backend returns the request to the Wallet; containing:
    • signed hash of Wallet Attestation PoP
    • SessionID
  22. The Wallet assembles the Wallet Attestation PoP with signature
  23. 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
  24. 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
  25. The PID Provider returns a request_uri that is bound to the Pushed Authorization Request
  26. The Wallet sends the Authorization Request; containing
    • the PAR request_uri
  27. The PID Provider responds with the first step to start the eID process with the Wallet, e.g. the tcToken.
  28. Further communication is exchanged to perform the eID process
  29. The user provides the eID PIN to the Wallet.
  30. Further communication is exchanged to perform the eID process
  31. 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.
  32. The PID Provider responds to the Wallet with an Authorization Response; containing
    • the auth code
  33. 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.
  34. The Wallet sends a Token Request to the PID Provider; containing the placeholder DPoP proof.
  35. The PID Provider responds with the expected error "use_dpop_nonce"; containing the dpop_nonce to be used from now in the header.
  36. The Wallet extracts and stores the dpop_nonce.
  37. The Wallet now prepares the actual DPoP-proof JWT for the HSM-bound //dev// key including the dpop_nonce and iat.
  38. The Wallet hashes the DPoP proof.
  39. 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
    • SessionID (from Step 21)
  40. The Wallet Backend validates the SessionID and loads session context
  41. The Wallet Backend signs the hash of the PoP for the HSM bound key with device_priv (associated with UUID & Issuer_ID) from the HSM
  42. The Wallet Backend returns the signed hash of the PoP for the HSM bound key to the Wallet.
  43. The Wallet assembles the DPoP-proof for the HSM bound key with the signature
  44. The Wallet sends a Token Request to the PID Provider; containing:
    • a DPoP Header with the DPoP-proof JWT incl. device_pub
    • the auth code from Authorization Response
    • the PKCE code_verifier matching the code_challenge from Authorization Request
  45. The PID Provider matches the code, verifies the PKCE code_verifier to the previously received code_challenge and verifies the DPoP proof.
  46. The PID Provider then generates an access token and a refresh token bound to the DPoP key device_priv in JWT format, MACed with pp_data; containing:
    • the eID data encrypted with pp_data
    • the expiration date of the refresh token
  47. The PID Provider generates a Token Response; containing
    • DPoP-bound access token (bound to device_priv)
    • DPoP-bound refresh token (bound to device_priv)
    • a c_nonce
  48. The PID Provider sends the Token Response to the Wallet
  49. The Wallet stores the refresh token.

Issuance of PID Batch Credentials

  1. The user opens and unlocks the Wallet.
  2. The Wallet requests issuance of PID Batch Credentials.
  3. The Wallet requests a fresh wallet attestation nonce from the PID Provider.
  4. The PID Provider generates a fresh nonce linked to the issuance session.
  5. The PID Provider returns the wallet attestation nonce to the Wallet.
  6. The Wallet requests a fresh PID issuer session id from the Wallet Backend
  7. The Wallet Backend generates a fresh Wallet Backend session id linked to the session
  8. The Wallet Backend returns the PID issuer session id to the Wallet
  9. The User enters the Wallet PIN
  10. The Wallet generates the key pair (pin_derived_eph_pub/priv) derived from the Wallet PIN
    • See Wallet Initialization Steps 9 to 12 for a detailed description of the authentication process
  11. The Wallet signs over the PID issuer session id and the device-bound public key wallet_auth_pub using the key pin_derived_eph_priv
  12. The Wallet signs over the PID issuer session id and the Wallet PIN derived public key pin_derived_eph_pub using the key wallet_auth_priv
  13. The Wallet sends a Credential Request to the Wallet Backend; containing
    • the UUID & the Issuer_ID,
    • the user_pin_derived_eph_priv signed pid_issuer_session_id and
    • the wallet_auth key signed pid_issuer_session_id
  14. The Wallet Backend loads the Wallet Instance information for UUID.
  15. 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
  16. 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
  17. The Wallet Backend validates the PoP for pin_derived_eph_pub and wallet_auth_pub.
  18. 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
  19. 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
  20. 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 wallet_backend_priv.
  21. The Wallet Backend returns the request to the Wallet; containing
    • the signed CloudWalletBatchKey and
    • the SessionID
  22. The Wallet prepares a Wallet Attestation PoP containing
    • the audience,
    • the signed CloudWalletBatchKey claim and
    • the pid_issuer_session_id.
  23. The Wallet hashes the Wallet Attestation PoP.
  24. The Wallet sends a signing request to the Wallet Backend containing
    • hash of the Wallet Attestation PoP
    • SessionID (from Step 21)
  25. The Wallet Backend validates the SessionID and loads the session context.
  26. The Wallet Backend signs the hash of the Wallet Attestation PoP with the respective device_priv.
  27. The Wallet Backend returns the request to the Wallet incl. the signed hash of the Wallet Attestation PoP.
  28. The Wallet assembles the Wallet Attestation PoP with a signature.
  29. The Wallet prepares the DPoP-proof JWT for the HSM-bound //dev// key including the dpop_nonce and iat.
  30. The Wallet hashes the DPoP proof.
  31. 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 SessionID (from Step 21).
  32. The Wallet Backend validates the SessionID and loads the session context.
  33. The Wallet Backend signs the hash of the PoP for the HSM bound key with device_priv (associated with UUID & Issuer_ID) from the HSM.
  34. The Wallet Backend returns the signed hash of the PoP for the HSM bound key to the Wallet.
  35. The Wallet assembles the DPoP-proof for the HSM bound key with the signature.
  36. The Wallet sends a Token Request to the PID Provider containing
    • the Wallet Attestation + PoP (incl. pid_issuer_session_id) in the wallet attestation Header,
    • the DPoP-proof JWT over dev key,
    • the grant_type = "refresh_token",
    • the DPoP-bound refresh token bound to the same key as the Wallet Attestation PoP.
  37. 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
  38. The PID Provider verifies the validity of the refresh token (incl. DPoP-binding).
  39. The PID Provider verifies that the Wallet Attestation PoP and the refresh token are bound to the same public key.
  40. The PID Provider decrypts the refresh token contents with //pp_data// and stores the users identity attributes in the session.
  41. The PID Provider generates a TokenResponse with a DPop bound access token.
  42. The PID Provider returns a TokenResponse to the Wallet containing
    • the DPoP bound access_token and
    • the c_nonce.
  43. The Wallet prepares the required number of PoP (incl. audience and respective public key) for the batch credential keys and hashes them.
  44. The Wallet sends a BatchSigningRequest to the Wallet Backend containing
    • the required number of hashed batch credential key PoPs and
    • the SessionID (from Step 21).
  45. The Wallet Backend validates the SessionID and loads the session context.
  46. The Wallet Backend signs the hashed batch credential key PoPs with the respective batch_kb_X_priv.
  47. The Wallet Backend returns the request to the Wallet incl. the signed hashes of the batch credential key PoPs.
  48. The Wallet assembles the required amount of CredentialRequests containing the signed batch credential key PoPs.
  49. The Wallet prepares the DPoP-proof JWT for the HSM-bound //dev// key including the dpop_nonce and iat.
  50. The Wallet hashes the DPoP proof.
  51. 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 SessionID (from Step 21).
  52. The Wallet Backend validates the SessionID and loads the session context.
  53. The Wallet Backend signs the hash of the PoP for the HSM bound key with device_priv (associated with UUID & Issuer_ID) from the HSM.
  54. The Wallet Backend returns the signed hash of the PoP for the HSM bound key to the Wallet.
  55. The Wallet assembles the DPoP-proof for the HSM bound key with the signature.
  56. The Wallet generates a new ephemeral keypair (cre_eph_pub, cre_eph_priv).
  57. 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
  58. 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
  59. 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.
  60. (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.
  61. (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.
  62. (mdoc) The PID Provider sends the Credential Response JWT containing
    • the required amount of PID credentials as mdoc.
  63. (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.
  64. (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.
  65. (SD-JWT) The PID Provider sends the Credential Response JWT containing
    • the required amount of PID credentials as SD-JWT VC.
  66. The Wallet decrypts the Credential Response JWT using the cre_eph_priv.
  67. 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.

  1. The user browses to Relying Party (RP) website
  2. Browser app on the user's device opens the RP website
  3. RP generates a key pair to be used for ECDH key agreement for SD-JWT HMAC'ing
  4. RP generates an OpenID4VP Authorization Request and stores it under a request_uri (e.g., https://rp.example.com/oidc/request/1234);
  5. The request is bound to the user's browser session
  6. It is signed using a key bound to the RP's metadata that can be retrieved using the RP's client_id
  7. It contains the ephemeral key for response encryption
  8. It contains RP's nonce and state
  9. It contains the RP's response_uri endpoint for sending the Authorization Response over POST
  10. RP generates a new browser session and binds the generated Authorization Request to it
  11. 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 session id is set
  12. The user clicks on the link
  13. The RP website navigates to the custom scheme link to launch the Wallet
  14. The user unlocks the Wallet (see notes below)
  15. The Wallet retrieves the Authorization Request from the RP website (e.g., https://rp.example.com/oidc/request/1234)
  16. The Wallet receives the Authorization Request
  17. 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.
  18. The Wallet displays information about the identity of the Relying Party and the purpose, the user gives consent to present the PID.
  19. The Wallet picks a batch credential tuple (Credential incl. possible disclosures, keyID) from the batch credential store
  20. (mdoc) The Wallet prepares an mdoc presentation according to the presentation_definition by removing NameSpaceBytes of not disclosed attributes.
  21. (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.
  22. (SD-JWT) The Wallet generates an SD-JWT VC presentation according to the presentation_definition by removing Disclosures of not disclosed attributes.
  23. (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.
  24. The Wallet requests a fresh PID issuer session id from the Wallet Backend
  25. The Wallet Backend generates a fresh Wallet Backend session id linked to the session
  26. The Wallet Backend returns the PID issuer session id to the Wallet
  27. The User enters the Wallet PIN
  28. The Wallet generates the key pair (pin_derived_eph_pub/priv) derived from the Wallet PIN
    • See Wallet Initialization Steps 9 to 12 for a detailed description of the authentication process
  29. The Wallet signs over the PID issuer session id and the device-bound public key wallet_auth_pub using the key pin_derived_eph_priv
  30. The Wallet signs over the PID issuer session id and the Wallet PIN derived public key pin_derived_eph_pub using the key wallet_auth_priv
  31. The Wallet sends a signing request to the Wallet Backend; containing
    • UUID & issuer_ID
    • user_pin_derived_eph_priv signed pid_issuer_session_id
    • wallet_auth key signed pid_issuer_session_id
    • hash of KB-JWT/deviceAuth
    • keyID (of the respective key that should be used to sign)
  32. The Wallet Backend loads the Wallet Instance information for UUID
  33. 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
  34. 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
  35. The Wallet Backend validates the PoP for pin_derived_eph_pub and wallet_auth_pub
  36. The Wallet Backend loads the c_seed associated with the issuer_ID
  37. 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
  38. The Wallet Backend signs the hash of KB-JWT/deviceAuth with the respective batch_kb_X_priv
  39. The Wallet Backend returns the signed hashes of KB-JWT/deviceAuth to the Wallet
  40. (mdoc) The Wallet assembles the deviceAuth from header, payload and signature and builds the presentation from deviceAuth and issuerSigned
  41. (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
  42. The Wallet creates a VP token and a presentation submission from the received credential.
  43. Optional: The Wallet can add further presentations
  44. The Wallet delets the used batch credential tuple (credential, keyID)
  45. The Wallet sends the VP token and presentation submission to the RP (encrypted to the RP's public key rp_eph_pub).
  46. The RP finds a session with the state and generates a response_code for this session
  47. The RP returns the redirect_uri with the response_code to the Wallet
  48. The Wallet launches the browser with the redirect_uri and response_code.
  49. The browser sends the redirect_uri and response code to the RP, attaching the browser session id as a cookie.
  50. The RP looks up whether there exists a session with the session id from the cookie and a matching response_code
  51. (mdoc) The RP verifies the PID in the VP token and verifies the SessionTranscript calculated from nonce, mDocGeneratedNonce, client_id, response_uri.
  52. (SD-JWT) The RP verifies the SD-JWT PID in the VP token, verifies the KB-JWT using the device_pub in the SD-JWT, and verifies the nonce and audience in the KB-JWT
  53. 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 PoPs to the session and prove their 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:

POST /session_endpoint HTTP/1.1
Host: server.example.com
Content-Type: application/json

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

OpenID4VCI Credential Issuer Metadata

Note that this extension is the same across multiple flows.

This document defines the following additional Credential Issuer Metadata parameters:

  • session_endpoint: REQUIRED. URL of the Credential Issuer's Session Endpoint, as defined in a previous section. This URL MUST use the https scheme and MAY contain port, path, and query parameter components.

Usability Considerations

Initialization

  • The Wallet needs to be initialized on a mandatory basis
  • During initialization, 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 initialization

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

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?