Technical Specifications
Attestation Verification

Attestation Verification

Attestation Verification as defined in the Architecture Reference Framework (ARF) is based on the OpenID for Verifiable Presentations (OID4VP (opens in a new tab)) protocol. This page outlines the specific flows that need to be supported for Attestation Verification within Company Passport.

OpenID for Verifiable Presentations

Attestations presented in an online scenario MUST be presented using the OID4VP (opens in a new tab) protocol. In addition implementations MUST support the Batch Credential Endpoint. An Issuer MAY support deferred credential issuance, which MUST be supported by the Wallet. Both the Issuer and Wallet MUST support the Notification Endpoint for updates from the Wallet to the Issuer.

The Organizational Wallet can act as both Verifier and Wallet.

Attestation Formats

Both the Verifier and Wallet MUST support the vc+sd-jwt (for the SD-JWT VC Attestation Format) and mso_mdoc (for the ISO/IEC 18013-5 mDL Attestation Format) formats as defined in Appendix A of OID4VP (opens in a new tab). Other attestation formats MAY be supported. See Attestation Formats for requirements around supported Attestation Formats.

High Assurance

For additional security, requirements as defined in Section 5. of High Assurance Interoperability Profile (HAIP) (opens in a new tab) MUST be supported in Company Passport solutions. In addition to the client_id_scheme as defined in HAIP, Company Passport Solutions can also use client_id_scheme value did for using a Decentralized Identifier.

Support might be added for the entity_id as client_id_scheme value for integration with OpenID Federation as Trust Management

Cross Device Flow

Cross Device Flow

Cross Device Flow in-depth
  1. User browses to Relying Party (RP) website
  2. Browser on the user's device opens the Resource server/website associated with the RP on a device different from its wallet
  3. The RS/website asks the RP component via its management API to generate a new Authorization Request URI
  4. The RP generates a new state value which for instance can be used as a browser session using a secure HTTP only cookie, referred to as correlation id.
  5. RP generates an ephemeral key pair to be used for key agreement for response encryption (not supported yet)
  6. RP generates an OpenID4VP Authorization Request, binding it internally to the state and stores
  7. RP makes sure the Request is accessible using the unique request_uri ( e.g., https://rp.example.com/oid4vp/requests/abcde-abcde-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 presentation_definition containing the information about the requested (Q)EAAs
    • It contains RP's nonce and state
    • It contains the purpose given by the RP
  8. RP returns the Request to the RS/RP website
  9. Rs/RP website generate a QR code from the Authorization request
  10. Rs/RP website returns a HTML page to the browser containing a QR-Code to the wallet app ( e.g., openid4vp://authorize?client_id=..&request_uri=https://rp.example.com/oidc/request/1234). It also sets a Cookie with the state value.
  11. The user launches his wallet and hits a button to scan QR-Code from the RP's website.
  12. The wallet scans the QR code
  13. The Wallet starts the OID4VP flow
  14. The wallet decodes the Authorization Request from the QR-Code and extracts request_uri and client_id
  15. The wallet optionally evaluates the trust associated with the client_id of the RP (optional)
  16. The wallet app retrieves the Authorization Request Object from the RP website ( e.g., https://rp.example.com/oid4vp/requests/abcde-abcde-1234) Example signed JWT when client_id_scheme is did. This is a vp_token only, meaning only presentation definition and thus Verifiable Presentation is expected

header:

{
  "typ": "oauth-authz-req+jwt",
  "alg": "RS256",
  "kid": "did:example:123#1"
}

body/payload:

{
  "client_id": "did:example:123",
  "client_id_scheme": "did",
  "response_types": "vp_token",
  "redirect_uri": "https://relying-party.acme.org/callback",
  "nonce": "n-0S6_WzA2Mj",
  "presentation_definition": "..see PE chapter for examples..",
  "client_metadata": {
    "vp_formats": {
      "jwt_vp_json": {
        "alg": ["ES256", "ES256K"]
      },
      "mso_mdoc": {
        "proof_type": ["ES256"]
      }
    }
  }
}

See also: Presentation Exchange explanation

  1. The RP website return the signed Authorization Request Object JWT
  2. The wallet validates the Authorization Request signature (JWT) using the RP's public key
  • Was the signature valid and the key bound to the RP's metadata? This ensures that the Authorization Request was not tampered with;
  1. The wallet validates the Presentation Exchange's Presentation Definition to be valid (presentation_definition)
  2. The wallet optionally filters out any Verifiable Credentials that cannot satisfy the presentation_definition
  3. The wallet notifies the user in case credentials are missing. Or to be more precise, in case presentation_definition input descriptors cannot be satisfied.
  • In the future a trust framework could would list all issuers and their credential types, allowing for a nicer UX, by redirecting the user to (Q)EAA Providers in case credentials are missing.
  1. Consent: The Wallet displays information about the identity of the Relying Party and the purpose, the user gives consent to present the (Q)EAA.
  2. JSON-LD W3C (Q)EAA The wallet generates a W3C Verifiable Presentation containing one or more proofs using wallet/device private key(s)
  3. (SD-JWT (Q)EAA) The Wallet generates a KB-JWT by signing over nonce and audience with device private keyv
  4. (SD-JWT (Q)EAA) The Wallet creates the presentation according to presentation_definition by cutting out the unnecessary Disclosures and appending the KB-JWT
  5. (Mdoc (Q)EAA) The Wallet generates an Mdoc deviceAuth by signing the SessionTranscript with device private key
  6. (Mdoc (Q)EAA) The Wallet creates the presentation according to presentation_definition by cutting out the unnecessary Releases assembling the issuerAuth with deviceAuth
  7. JWT W3C VP The wallet generates a W3C VCDM 1.1 JWT encoded Verifiable Presentation containing one or more proofs using wallet/device private key(s)
  8. The wallet asks the user to authenticate, for instance using pin, face id or fingerprint
  9. The wallet creates a VP token and a presentation submission, describing the paths from the definition to the VC(s) contained in the VP.
  10. The wallet sends the VP token and presentation submission to the RP (encrypted to the RP's ephemeral public key, not yet supported).
  11. The RP optionally decrypts the Authorization Response with its ephemeral public key created during the Authorization Request.
  12. The RP matches the browser session with the state parameter.
  13. The RP verifies the content of the vp_token. verifies its signatures, including these of credentials contained in presentation(s)
  14. The RP verifies the submission_data and vp_token against its definition
  15. The RP responds with HTTP 200 and acknowledges the received vp_token.
  16. The browser refreshes the RP's website, either by polling regularly or by receiving a call from the RP, e.g. through websockets or webhooks.
  17. The RP matches the session_id from the Cookie
  18. matches this with the verification result.
  19. Returns the result to the browser or RS
  20. The browser continues the UX flow.
DIF Presentation Exchange in-depth

Whenever the response_type of the Request Object contains the value vp_token, code and a presentation_definition is present, the OP/Wallet is expected to return a so called VP Token, meaning a Verifiable Presentation in the Authorization Response (vp_token) or Token Response (code) with presentation_submission, binding the Presentation to the definition. Examples will be provided below.

Example JWT (Q)EAA

Below is an example (Q)EAA in JWT format, explaining how the JWT encoded (Q)EAA is being mapped onto the W3C Verifiable Credential Datamodel 1.1 (VCDM). It is important to understand that a JWT (Q)EAA is just a matter of encoding/decoding a 'regular' W3C Verifiable Credential

{
    "iss": "https://example.gov/issuers/565049", <-- Maps onto the issuer value of the W3C VDM
    "nbf": 1262304000, <-- Maps onto the issuance data of the W3C VDM
    "jti": "http://example.gov/credentials/3732", <-- Unique value, maps onto the credential Id of the W3C VDM
    "sub": "did:example:ebfeb1f712ebc6f1c276e12ec21", <-- maps onto the credentialSubject id of the W3C VDM, notice no id is present below,
    "vc": { <-- maps onto the W3C VCDM Verifiable Credential. The rest below is standard W3C VCDM,
       "crdentialSchema": {
          "id": "https://eu.com/claims/IDCredential.json"
       },
        "@context": [
            "https://www.w3.org/2018/credentials/v1",
            "https://www.w3.org/2018/credentials/examples/v1"
        ],
        "type": [
            "VerifiableCredential",
            "IDCredential"
        ],
        "credentialSubject": {
            "given_name": "Max",
            "family_name": "Mustermann",
            "birthdate": "1998-01-11",
            "address": {
                "street_address": "Sandanger 25",
                "locality": "Musterstadt",
                "postal_code": "123456",
                "country": "DE"
            }
        }
    }
}

Example Presentation Definitions

As you can see in the below examples the format of the actual credential has an influence on the definition. Both in terms of paths, formats and constraints

The global structure however is the same, and follows the DIF Presentation Exchange v2.0 specification (opens in a new tab) (PE) The input_descriptor array is mandatory. A wallet is supposed to satisfy every single input descriptor from the definition, unless specific constraints dictate otherwise.

Please be aware that the below are very minimal examples of what PE is capable of. Things like defining which claims should be used in selectively disclosed credentials, including whether the selective disclosure is mandatory or optional are for instance possible. It is also possible to add constraints to an input descriptor, allowing for instance to assign one or more groups to the descriptor. The in a so called submission_requirements object you can have rules, like a pick rule where you define that for instance 1 or 2 descriptors should be satisfied from a certain group, or for instance a min and max amount of descriptors

W3C Verifiable Credential Data Model 1.1 JWT Credential

The below example, expects a JWT VP signed using ES256 with a single JWT VC signed using either ES256 or ES256K. It also expect the type to be IDCredential exactly and requires a schema from 2 different locations.

{
    "id": "example_jwt_vc, a unique ID value for the whole definition",
    "name": "An optional human friendly readable name, to be displayed by a wallet typically",
    "purpose": "An optional string to be displayed by the wallet, describing to the user that the purpose of the RPs data request is",
    "input_descriptors": [
        {
            "id": "id_credential", <-- A Unique identifier in the scope of the definition for this input descriptor
            "format": {
                "jwt_vc_json": {
                    "proof_type": [ <-- VC signature suites supported for a JWT VP not using Linked Data Processing (@context)
                        "ES256K",
                        "ES256"
                    ]
                },
               "jwt_vp_json": {
                  "proof_type": [
                     "ES256" <-- VP signature suite supported for a JWT VP not using Linked Data Processing (@context)
                  ]
               }
            },
            "constraints": {
                "fields": [
                    {
                        "path": [
                            "$.vc.type" <-- Search for this path in the credential, using JSONPath
                        ],
                        "filter": { <-- Can be any JSON schema filter, so partial searches using regexes, matching against constants, singular values as well as arrays
                            "type": "array",
                            "contains": {
                                "const": "IDCredential" <-- The VC path vc.type should be an array containing the string literal IDCredential with an exact match
                            }
                        }
                    },
                   {
                      "path": ["$.vc.credentialSchema.id"],
                      "filter": {
                         "type": "string",
                         "pattern": ["https://eu.com/claims/IDCredential.json", "https://us.com/example/US-IDCredential.json"]
                      }
                   }
                ]
            }
        }
    ]
}

ISO mobile Driving License (mDL)

{
   "id": "mDL-example, a unique ID value for the whole definition",
   "name": "An optional human friendly readable name, to be displayed by a wallet typically",
   "purpose": "An optional string to be displayed by the wallet, describing to the user that the purpose of the RPs data request is",
    "input_descriptors":[
        {
            "id":"org.iso.18013.5.1.mDL", <-- namespace identifier, according to ISO spec
            "format":{
                "mso_mdoc":{
                    "alg":[
                        "EdDSA",
                        "ES256"
                    ]
                }
            },
            "constraints":{
                "limit_disclosure":"required", <-- Do not accept mDoc disclosures containing more claims than listed below
                "fields":[
                    {
                        "path":[
                            "$['org.iso.18013.5.1']['family_name']"
                        ],
                        "intent_to_retain":false <-- The RP is signaling it will not store these values long term, only used during the actial interaction
                    },
                    {
                        "path":[
                            "$['org.iso.18013.5.1']['portrait']"
                        ],
                        "intent_to_retain":false
                    },
                    {
                        "path":[
                            "$['org.iso.18013.5.1']['driving_privileges']"
                        ],
                        "intent_to_retain":false
                    },
                    {
                        "path":[
                            "$['domestic_namespace']['domestic_data_element_id']"
                        ],
                        "intent_to_retain":false
                    }
                ]
            }
        }
    ]
}

Presentation Submission

A descriptor called the presentation_submission needs to be provided in the Response to the RP. This descriptor is being created by the Wallet and User. The Wallet will use a PE library to filter out any non-conforming (Q)EAAs. If the Presentation Definition cannot be met, the user will be warned about it. If it can be met, the user will have to consent. In case multiple (Q)EAAs met a single criteria (input_descriptor) of the definition, the user should choose which (Q)EAA(s) to use. The PE library then will generate the presentation_submission, making a link between the definition input_descriptor ids and the JSONPaths of the (Q)EAAs in the Presentation. This ensures the RP can check the Presentation without any ambiguity.

Example presentation submission:

{
    "id": "Presentation example 1", <-- Unique value in scope of the holder/wallet
    "definition_id": "example_jwt_vc", <-- Matches the definition Id (see earlier)
    "descriptor_map": [
        {
            "id": "id_credential", <-- matches the ID of the descriptor from the definition
            "format": "jwt_vp", <-- Format used for the presentation
            "path": "$", <-- Since this is a nested descriptor where the outer part is the presentation at the toplevel, we use $ (toplevel)
            "path_nested": { <-- We need to point to the VC/(Q)EAA within the presentation
                "format": "jwt_vc", <-- format of the VC
                "path": "$.vp.verifiableCredential[0]" <-- Path to the (Q)EAA, relative to the path of the parent ($)
            }
        }
    ]
}