<!-- PDF-show
\newpage
-->

# PID Option C'': Signed Credential with eID Card

## Basic Idea

In this design, PID credentials are issued on-demand in ISO mdoc / SD-JWT VC
format using the OpenID4VCI protocol and directly presented using the OpenID4VP
protocol. While this Option is similar to Option B, the PID credentials are
signed by the PID Provider for the specific transaction. The user is
authenticated by utilizing the eID card towards the PID Provider in every
presentation.

In this design, the device key for signing the presentation is generated by the
PID Provider in order to achieve a high level of assurance and perform critical
key management outside the Wallet. To ensure that the PID Provider does not get
to know where a PID credential is used and which claims are provided, the
payload of the presentation part (deviceAuth for mdoc or KB-JWT for SD-JWT) is
created by the Wallet and only its hash is sent to the PID Provider for signing.
This design therefore results in the following properties:

- The complexity of implementation is lower for the Wallet, as it does not need
   to handle and secure the device key.
- The PID Provider can enforce that there is exactly one presentation for the
   PID credential, ensuring a tight binding between the eID Card process and the
   PID presentation.
- It is expected that certification for LoA High is easier for this design, as
   the PID Provider has full control over all keys and a secure authentication
   is guaranteed each time via the usage of the eID.

Note that this design assumes that the signing algorithm performs a hashing step
on the data first; this step is happening at the Wallet, while the rest of the
signing is done at the PID Provider. In particular, this works well with ECDSA.

## Credential formats

Two solutions are described:

- **ISO mdoc:** The ISO mdoc credential format is used with
    - issuerAuth as issuer data authentication, a COSE_Sign1 signature over the
    MobileSecurityObject (see ISO 23220-4 7.1.3.4.2.1); containing
        - an ephemeral key in `deviceKeyInfo`
        - signed hashes in the `valueDigests`
    - deviceSignature as mdoc authentication method, a COSE_Sign1 signature over
    the deviceAuthentication data (see ISO 18013-5 9.1.3.6); containing
        - the PID data
- **SD-JWT VC:** The SD-JWT VC credential format is used with
    - SD-JWT signed with JWS by the PID Provider; containing
        - an ephemeral key in `cnf`
        - the signed hashes in `_sd` arrays
    - KB-JWT signed by the PID Provider with the Hash; containing
        - nonce and audience of the Relying Party
        - a hash of the SD-JWT and the selected disclosures
    - Disclosures containing the PID data

## Cryptographic Formats

(to be defined)

### Issuance

Long-Term keys:

Transaction-specific keys:

Artifacts:

### Presentation

Long-Term keys:

Transaction-specific keys:

Artifacts:

### Dependencies

## Sequence Diagram

### Issuance

```plantuml
@startuml
'Lets define some common colors globally
!$C_PRT = "#118888"
!$C_ARG = "#daa520"
!$C_VAR = "#daa520"

'Ensure messages are not too wide
skinparam maxMessageSize 200
skinparam wrapWidth 300

'Macro for colored [TLS] block
!function tls()
!return "<color " + $C_PRT + ">[TLS]</color>"
!endfunction

'Macro for colored [TLSPSK] block
!function tlspsk()
!return "<color " + $C_PRT + ">[TLS-PSK]</color>"
!endfunction

'Macro for colored <Param1, Param2> block
!function params($p)
!return "<color " + $C_ARG + ">"+ $p + "</color>"
!endfunction

'Align text on arrows to center
skinparam sequenceMessageAlign center

'padding between boxes
skinparam BoxPadding 100

autonumber "<b>(000)"

title PID presentation over OpenID4VP and On-the-fly PID Issuance over OpenID4VCI with SD-JWT

actor u as "User\nOpenID Holder"
participant b as "Browser App\n(same device)"
participant v [
                Relying Party
                ----
                ""Long-term Key: (//rp_pub//, //rp_priv//)""
]
participant w as "User's EUDI Wallet Instance\n(eID-Client)"
participant i  [
                PID Provider
                (eService+eID Server)
                ----
                ""Long-term Key: (//pp_pub//, //pp_priv//)""
            ]


u --> b : browse to application
hnote over b #dfd: Screen: same_device_relying_party_start
b -> v : tls() HTTP GET <rp-website>

v -> v : generate ephemeral key pair (//rp_eph_pub//, //rp_eph_priv//)

v -> v : create OpenID4VP Authorization Request,\n sign with //rp_priv//,\n store under <request_uri>
note left: Authorization Request includes:\n- presentation_definition\n- purpose\n- state\n- nonce\n- //rp_eph_pub//\n- response_uri

v -> v : generate new browser session <color:#909>session_id</color> and bind the authorization request to it

v -> b : tls() HTTP 200 HTML containing wallet-link openid4vp://authorize?")\nclient_id=..&request_uri=<request_uri>\nSet-Cookie: sid=<color:#909>session_id</color>
u --> b : action to start flow/launch wallet
b -> w : launch with wallet-link openid4vp://
note right #fc7: Potential security risk: Wallet app may be spoofed by malicious app
hnote over w #dfd: Screen: launch_wallet

u --> w : unlock wallet
note right: may be moved to later point in flow or removed, see notes.
hnote over w #dfd: Screen: unlock_wallet

'newpage

w -> v : tls() HTTP GET <request_uri>
note right #fc7: Potential privacy risk: RP learns existence of wallet app and potentially identifying information (e.g., headers)
v -> w : tls() HTTP 200 <JWT-Secured Authorization Request>

w -> w : validate Authorization Request JWT using //rp_pub//

u <--> w : user consent to present PID to Relying Party for given purpose
hnote over w #dfd: Screen: consent_present_credential

group PID provisioning
  note over w,i: PID Issuer and EUDI Wallet have inherent trust relationship, metadata may be pre-configured or retrieved

  w -> w : Wallet fetches fresh wallet attestation from backend
  w -> i : tls() HTTP POST </session_endpoint> wallet attestation nonce
  i -> i : generate and store nonce
  i -> w : tls() HTTP 200 <wallet attestation nonce>
  w -> w : sign wallet attestation PoP JWT (incl. wallet attestation nonce)
  w -> i : tls() HTTP POST PAR (PKCE code_challenge, wallet attestation JWT, wallet attestation PoP JWT, redirect_uri, either scope or authorization_details
  i -> i : verify wallet attestation & PoP \ncheck Wallet Provider solution status on trust list
  i -> w : tls() HTTP 200 request_uri
  note right : Attestation guarantees with high certainty that Wallet is trustworthy and not manipulated
  w -> i : tls() HTTP GET <OpenID4VCI Authorization Request(request_uri)>

'newpage
  hnote over w #dfd: Screen: eid_start

  group Read eID or Smart eID acc. to BSI TR-03130
    i --> w : tls() HTTP 200 starting the eID Process
    w <-> i : eID Process
    u <--> w : <eID-PIN>
    hnote over w #dfd: Screen: eid_pin
    w <-> i : eID Process
    w -> i : tls() HTTP GET finishing the eID process with refreshUrl
    hnote over w #dfd: Screen: eid_nfc_data
  end

  i --> w : tls() HTTP 302 Authorization Response (code)

  group Generate initial DPoP nonce
    w -> w : generate ephemeral DPoP key pair //dpop_eph_pub//, //dpop_eph_priv//

    w -> w : generate placeholder DPoP proof using ephemeral DPoP key pair //dpop_eph_pub//, //dpop_eph_priv//

    w -> i : tls() HTTP POST <Token Request(DPoP Header with placeholder proof, authorization_code, PKCE code_verifier)>
    i -> i: generate and store dpop_nonce
    i -> w : tls() HTTP 400 <Bad Request (DPoP nonce Header with dpop_nonce, error: "use_dpop_nonce")>

    note left : The Wallet should check at this point, whether the Token Endpoint delivered the expected error and nonce. If not, this needs to be handled (retry or abort gracefully).
    w -> w: store dpop_nonce
  end

  w -> w: prepare DPoP proof JWT with //dpop_eph_pub//, dpop_nonce, iat and sign with //dpop_eph_priv//

  w -> i : tls() HTTP POST Token Request(code, code_verifier, DPoP header)
  i -> i : generate and store dpop_nonce
  i -> i : lookup authorization code\ngenerate Token Response with DPoP-bound access token\nverify PKCE challenge
  i --> w : tls() HTTP 200 <Token Response(DPoP nonce header with dpop_nonce, DPoP-bound access_token, c_nonce, optional authorization_details)>

  w -> w: prepare DPoP proof JWT with //dpop_eph_pub//, dpop_nonce, iat and sign with //dpop_eph_priv//

'newpage

  alt #ddf C'': ISO mdoc
    w -> i : HTTP POST <Credential Request(DPoP Header with proof, DPoP-bound access token)>
    i -> i : lookup access token
    i -> i : generate ephemeral device key pair (//device_pub//, //device_priv//)
    i -> i : create mdoc with all eID data attributes and //device_pub//, signed by //pp_priv//, and matching NameSpaceBytes
    i --> w : tls() HTTP 200 Credential Response(<color blue>issuerSigned</color>)
    note right: issuerSigned contains issuerAuth (the signed part, also known as MSO) and nameSpaces (the data elements)
    w -> w : calculate SessionTranscript (mDocGeneratedNonce, clientId, responseUri, nonce)
    w -> w : calculate hash of deviceAuth using SessionTranscript and including only selected data elements in nameSpaces
    w -> i : tls() HTTP POST Presentation Signing Request (deviceAuth hash, DPoP-bound access token)
    i -> i : create signature over deviceAuth hash using //device_priv//
    i --> w : tls() HTTP 200 Presentation Signing Response(<color blue>deviceAuth signature</color>)
    w -> w : assemble mdoc using deviceAuth and its signature
    note right: mdoc is ISO18013-5 Document containing issuerAuth and deviceAuth

  else #dfd C'': SD-JWT VC
    w -> i : tls() HTTP POST <Credential Request(DPoP Header with proof, DPoP-bound access token)>
    i -> i : lookup access token
    i -> i : generate ephemeral device key pair (//device_pub//, //device_priv//)
    i -> i : create SD-JWT with eID data and //device_pub//, signed by //pp_priv// and matching Disclosures
    i --> w : tls() HTTP 200 Credential Response(<color blue>SD-JWT</color>)

    w -> w : calculate hash of KB-JWT using nonce, audience, and sd_hash
    note right: the SD-JWT+Disclosures are required by the Wallet to calculate sd_hash
    w -> i : tls() HTTP POST Presentation Signing Request (kb_hash, DPoP-bound access token)
    note right: Issuer ensures that each device key is only used once and deletes it afterwards
    i -> i : create KB-JWT signature over kb_hash with //device_priv//
    i --> w : tls() HTTP 200 Presentation Signing Response(<color blue>KB-JWT signature</color>)
    w -> w : assemble KB-JWT using the payload and its signature
  end

'newpage

end

w -> w : create vp_token and presentation_submission
w -> w : add mDL presentation according to <presentation_definition> with <nonce> to vp_token and presentation_submission
note left #AAFFAA: Wallet may add presentations with keys under its own control as the \ncommunication channel between Relying Part and PID Provider is not E2EE


w -> v : tls() HTTP POST encrypted <Authorization Response(presentation_submission, vp_token, state)>

v -> v : look up state in existing sessions\ncreate & store response_code for session

v --> w : tls() HTTP 200 <redirect_uri incl. response_code>

'newpage

w -> b : launch browser with <redirect_uri with response_code>
hnote over w #dfd: Screen: success_redirect

b -> v : tls() HTTP GET <redirect_uri with response_code>\nCookie: sid=<color:#909>session_id</color>

v -> v : look up session with <color:#909>session_id</color> and match response_code

alt #ddf B.1.1: ISO mdoc
  v -> v : verify contents of <vp_token>:\n- verify mdoc issuerAuth PID\n- verify deviceAuth with //device_priv// from issuerAuth\n- calculate and validate correct SessionTranscript
else #dfd B.1.2: SD-JWT VC
  v -> v : verify contents of <vp_token>:\n- verify SD-JWT PID\n- verify KB-JWT with //device_priv// from SD-JWT\n- validate nonce and audience from KB-JWT
end

v --> b : tls() HTTP 200 <HTML with continued UX flow>
hnote over b #dfd: Screen: same_device_relying_party_identified

@enduml

```

## Step-by-Step Description

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. 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 response encryption
4. 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 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
5. RP generates a new browser session and binds the generated Authorization
   Request to it
6. RP returns an HTML page to the browser containing a link to the wallet app
   (e.g.,
   `openid4vp://authorize?client_id=..&request_uri=https://rp.example.com/oidc/request/1234`);
   a cookie with the browser session id is set
7. The user clicks on the link
8. The RP website navigates to the custom scheme link to launch the wallet app
9. The user unlocks the wallet app (see notes below)
10. The wallet app retrieves the Authorization Request from the RP website
    (e.g., `https://rp.example.com/oidc/request/1234`)
11. The wallet app receives the Authorization Request
12. The wallet app 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.
13. The Wallet displays information about the identity of the Relying Party and
    the purpose, the user gives consent to present the PID.
14. The Wallet fetches fresh wallet attestation from the Wallet Provider
    backend.
15. The Wallet requests a fresh nonce for the wallet attestation nonce from the
    PID Provider (wallet attestation nonce).
16. The PID Provider generates a fresh nonce linked to the issuance session.
17. The PID Provider returns the wallet attestation nonce to the Wallet.
18. The Wallet generates a Wallet Attestation PoP and signs it with *dev_priv*;
    containing
    - audience
    - expiration time
    - wallet attestation nonce
19. The wallet sends the Pushed Authorization Request to the PID Provider;
    containing
    - PKCE code_challenge
    - wallet attestation + PoP
    - redirect_uri
    - either scope or an authorization_details parameter requesting the PID
20. 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.
21. The PID Provider returns a request_uri that is bound to the Pushed
    Authorization Request.
22. The Wallet sends the Authorization Request; containing
    - the PAR request_uri
23. The PID Provider responds with the first step to start the eID process with
    the wallet app, e.g. the tcToken. Note that this is the direct HTTP Response
    to Step 14.
24. Further communication is exchanged to perform the eID process
25. The user provides the eID PIN to the wallet app.
26. Further communication is exchanged to perform the eID process
27. 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.
28. The PID Provider responds to the Wallet with an Authorization Response;
    containing
    - the auth code
29. The wallet generates an ephemeral DPoP key pair *dpop_eph_pub*,
    *dpop_eph_priv*.
30. The Wallet generates a placeholder DPoP proof JWT using the ephemeral DPoP
    key pair to trigger an error response from the Token endpoint necessary to
    retrieve the `dpop_nonce`.
31. The Wallet sends a Token Request to the PID Provider, containing the
    placeholder DPoP proof JWT.
32. The PID Provider generates and stores a `dpop_nonce`.
33. The PID Provider responds with the expected error "use_dpop_nonce",
    containing the `dpop_nonce` to be used from now on in the DPoP nonce header.
34. The Wallet extracts and stores the `dpop_nonce`.
35. The Wallet now prepares the actual DPoP proof JWT for `dpop_eph_pub`
    including the `dpop_nonce` and `iat`.
36. The Wallet sends a Token Request to the PID Provider; containing:
    - the authorization_code from Authorization Response
    - the PKCE code_verifier matching the code_challenge from Authorization
      Request
    - the DPoP header
37. The PID Provider generates and stores a `dpop_nonce`.
38. The PID Provider matches the authorization_code and verifies the PKCE
    code_verifier to the previously received code_challenge. It then generates
    an access token bound to the DPoP key.
39. The PID Provider sends a Token Response; containing
    - DPoP-bound access token
    - a c_nonce
    - an authorization_details object, in case the authorization_details
      parameter was used in the Authorization Request
    - a fresh `dpop_nonce` in the DPoP nonce header
40. The Wallet prepares a DPoP proof JWT for `dpop_eph_pub` including the
    `dpop_nonce` and `iat`.
41. **(mdoc)** The Wallet send a Credential Request; containing
    - DPoP Header with proof
    - DPoP-bound access token
    - sessionTranscript
    - it does not contain a "proof"
42. **(mdoc)** The PID Provider looks up and validates the access token.
43. **(mdoc)** The PID Provider generate an ephemeral DeviceKey pair
    (*device_pub*,*device_priv*).
44. **(mdoc)** The PID Provider creates the mdoc issuerSigned; containing
    - issuerAuth (MSO) with the public part of DeviceKey and the hashes of the
      data elements and signs it with *pp_priv*
    - nameSpaces with the data elements
45. **(mdoc)** The PID Provider returns the issuerSigned to the Wallet.
46. **(mdoc)** The Wallet calculates the SessionTranscript according to
    ISO-18013-7 Annex B.4.4 from mDocGeneratedNonce, client_id, responseUri,
    nonce. The final result is a SHA-256 hash, thus not revealing the client_id
    and ResponseUri to the PID Provider.
47. **(mdoc)** The Wallet generates the deviceAuth utilizing the
    SessionTranscript and the requested data elements for nameSpaces and
    calculates the hash of it
48. **(mdoc)** The Wallet sends the hash of the deviceAuth to the Presentation
    Signing Endpoint of the PID Provider; the request is protected by a
    DPoP-bound access token.
49. **(mdoc)** The PID Provider creates the signature for the deviceAuth hash.
50. **(mdoc)** The PID Provider returns the signature to the Wallet.
51. **(mdoc)** The Wallet creates the mdoc using the issuerAuth, the deviceAuth
    payload and the deviceAuth signature returned by the PID Provider.
52. **(SD-JWT)** The Wallet send a Credential Request; containing - DPoP Header
                     with proof - DPoP-bound access token - it does not contain
                     a "proof"
53. **(SD-JWT)** The PID Provider looks up and validates the access token.
54. **(SD-JWT)** The PID Provider generate an ephemeral DeviceKey pair
    (*device_pub*,*device_priv*).
55. **(SD-JWT)** The PID Provider creates the issuer-signed part of the SD-JWT
    and signs it with *pp_priv*; containing
    - eID as the user claims
    - *device_pub* as cnf claim
56. **(SD-JWT)** The PID Provider sends the Credential Response; containing:
    - SD-JWT VC PID with Disclosures
57. **(SD-JWT)** The Wallet creates the header and payload for the KB-JWT from
    audience, nonce, and the hash of SD-JWT and selected disclosures and hashes
    it. The Wallet appends the KB-JWT to the SD-JWT. Note that this step can
    only happen after the SD-JWT has been issued and received by the Wallet, as
    the KB-JWT payload includes the *sd_hash* parameter, that is a hash over
    issuerSigned JWT and the Disclosures.
58. **(SD-JWT)** The Wallet sends a Request to the Presentation Signing Endpoint
    transmitting the hash of the KB-JWT. The request is protected by a
    DPoP-bound access token.
59. **(SD-JWT)** The PID Provider recognizes the device key from the provided
    public key reference and uses *device_priv* to create the signature of the
    KB-JWT using the hash of the KB-JWT. Afterwards, it must ensure that the
    device key pair is deleted and not used again.
60. **(SD-JWT)** The PID Provider sends the response containing the signature of
    KB-JWT.
61. **(SD-JWT)** The Wallet assembles the PID presentation from SD-JWT and
    KB-JWT payload/signature.
62. The wallet app creates a VP token and a presentation submission from the
    received SD-JWT PID.
63. Optional: The wallet app can add further presentations with keys under its
    own control as the communication channel between Relying Part and PID
    Provider is not E2EE
64. The wallet app sends the VP token and presentation submission to the RP
    (encrypted to the RP's public key *rp_eph_pub*).
65. The RP finds a session with the state and generates a response_code for this
    session.
66. The RP returns the redirect_uri with the response_code to the wallet app.
67. The wallet app launches the browser with the redirect_uri and response_code.
68. The browser sends the redirect_uri and response code to the RP, attaching
    the browser session id as a cookie.
69. The RP looks up whether there exists a session with the session id from the
    cookie and a matching response_code
70. **(mdoc)** The RP verifies the PID in the VP token with the MAC key and
    verifies the SessionTranscript calculated from nonce, mDocGeneratedNonce,
    clientID, response_uri.
71. **(SD-JWT)** The RP verifies the SD-JWT PID in the VP token with the MAC
    key, verifies the KB-JWT using the *kb_eph_pub* in the SD-JWT, and verifies
    the nonce and audience in the KB-JWT
72. The RP considers the user to be identified in the session context and
    continues the UX flow.

## Extensions to the Protocols

### 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:

```http
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
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store

{
  "session_id": "iOiJSUzI1NiIsInR"
}
```

### Presentation Signing Endpoint

This endpoint is used by the Wallet to obtain a signature over the KB-JWT or
deviceAuth structure. Support for this endpoint is REQUIRED.

Communication with this endpoint MUST use TLS and this endpoint MUST be
protected using the DPoP-bound access token, similar to the Credential Endpoint.

To fetch the bytes of the signature, the Wallet MUST send an HTTP request using
the POST method and the `application/json` media type. The request contains a
JSON-encoded object with the following parameters:

- `hash_bytes`: REQUIRED. base64url-encoded bytes of the hash over the bytes of
  the deviceAuth structure or the KB-JWT structure, respectively. Note that the
  hash algorithm MUST be matched to the signing algorithm used by the PID
  Provider for signing the deviceAuth or KB-JWT structure.

The PID Provider MUST return the HTTP Status Code 200 and a `signature_bytes`
parameter defined below.

- `signature_bytes`: REQUIRED. base64url-encoded bytes of the signature over the
  deviceAuth or KB-JWT, respectively.

### 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.
- `presentation_signing_endpoint`: REQUIRED: URL of the Presentation Signing
  Endpoint, as defined above. This URL MUST use the `https` scheme and MAY
  contain port, path, and query parameter components.

## Usability Considerations

## Privacy Considerations

## Security Considerations

## Open Topics