kych

OAuth 2.0 API for Swiyu to enable Taler integration of Swiyu for KYC (experimental)
Log | Files | Refs

commit 08fd9d2624a60ef7b6b757b0378a67c48007758e
parent 355f3ef7eec2a6d5e0b54e78447ed6b5775e284b
Author: Henrique Chan Carvalho Machado <henriqueccmachado@tecnico.ulisboa.pt>
Date:   Mon, 24 Nov 2025 23:25:21 +0100

documentation: add VC and VP explanation, add KyCH logo used in demo verifier

Diffstat:
Adocumentation/kych_logo.svg | 26++++++++++++++++++++++++++
Adocumentation/vc.md | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adocumentation/vp.md | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 210 insertions(+), 0 deletions(-)

diff --git a/documentation/kych_logo.svg b/documentation/kych_logo.svg @@ -0,0 +1,26 @@ +<svg width="180" height="200" viewBox="0 0 180 200" xmlns="http://www.w3.org/2000/svg"> + <defs> + <linearGradient id="shieldGrad" x1="0" x2="0" y1="0" y2="1"> + <stop offset="0%" stop-color="#ff4b4b"/> + <stop offset="100%" stop-color="#c40000"/> + </linearGradient> + </defs> + + <!-- Shield --> + <path d="M90 10 + L150 40 + L150 100 + C150 145 120 175 90 190 + C60 175 30 145 30 100 + L30 40 Z" + fill="url(#shieldGrad)" + stroke="#820000" stroke-width="4"/> + + <!-- Swiss cross --> + <rect x="78" y="55" width="24" height="70" fill="white"/> + <rect x="55" y="78" width="70" height="24" fill="white"/> + + <!-- Digital checkmark --> + <path d="M115 130 L130 145 L155 115" + stroke="#2affd3" stroke-width="10" stroke-linecap="round" stroke-linejoin="round"/> +</svg> diff --git a/documentation/vc.md b/documentation/vc.md @@ -0,0 +1,117 @@ +## Swiyu VC Format +The Verifiable Credential is generated by the Swiyu Verifier. +The process is orchestrated by the `getCredential method` in the `issuer-service/src/main/java/ch/admin/bj/swiyu/issuer/service/SdJwtCredential.java` file. It uses the functions `addTechnicalData`, `prepareDisclosures`, `addHolderBinding` and `addStatusReferences`. + +This is JWT the format of a VC generated by the swiyu-issuer: +`<header>.<payload>.<signature>~<disclosure1>~<disclosure2>~<disclosure3>` + +#### JWT Header +```JSON +{ "alg":"ES256", "kid":"did:example:issuer123#key-1", "typ":"vc+sd-jwt" } +``` + +#### JWT Payload +```JSON +{ + "iss": "did:tdw:example", + "vct": "http://localhost:8080/oid4vci/vct/my-vct-v01", + "vct#integrity": "sha256-SVHLfKf...d0ysMck=", + "iat": 1754581412, + "nbf": 1754581378, + "exp": 1754581508, + "_sd": [ + "2EG8ny65OcTc061HkTmn73CIWxGOlEUZLmI1spv5Sf0", + "sJ5sZgl2SUHS_38bp9rX2zGCHcpyWCA-qJNhMTZVo_8", + "zmnGEGSwsVvSaTZOg_3GD5Xr2o7pKasFqzLXmDd3Oio" + ], + "_sd_alg": "sha-256", + "cnf": { + "jwk": { + "crv": "P-256", + "iat": 1754581388, + "kid": "Test-Key", + "kty": "EC", + "use": "sig", + "x": "fyLxOVZJjNvunwQ2_-grg1jVpIc5dXHGppRT5QuUWI4", + "y": "lX1vKE9ytAt2FSk4JWcpqoTo49mnv0jokCh1FWua2jk" + } + }, + "status": { + "status_list": { + "idx": 0, + "type": "SwissTokenStatusList-1.0", + "uri": "https://localhost:8080/status" + } + } +} +``` +##### Field Explanation +* iss (Issuer) + * Identifies the entity that issued the JWT. + * Mandatory (per JWT specification RFC 7519). + +* vct (Verifiable Credential Type) + * A URI that identifies the type of VC. This helps verifiers understand the schema, context, and expected claims of the credential. + * Mandatory (per SD-JWT VC specification). + +* vct#integrity + * A hash of the content found at the vct URI. This provides an integrity check, ensuring that the credential type definition has not been tampered with or changed since the credential was issued. + * Optional (per SD-JWT VC specification). + +* iat (Issued At) + * Represents the time at which the JWT was issued. Unix timestamp. + * Mandatory (per JWT specification RFC 7519). + +* nbf (Not Before) + * Specifies the time before which the JWT MUST NOT be accepted for processing. Unix timestamp. + * Optional (per JWT specification RFC 7519). + +* exp (Expiration Time) + * Defines the expiration time on or after which the JWT MUST NOT be accepted for processing. Unix timestamp. + * Optional (per JWT specification RFC 7519). + +* \_sd (Selective Disclosure Digests) + * An array containing Base64url-encoded SHA-256 hashes of the claims that can be selectively disclosed. + * Mandatory. + +* \_sd_alg (Selective Disclosure Algorithm) + * Indicates the hashing algorithm used to generate the digests found in the \_sd array. + * Mandatory if \_sd is present (per SD-JWT specification). + +* cnf (Confirmation) + * Used to cryptographically bind the credential to a specific holder. It contains a JSON Web Key (JWK) representing the holder's public key. The holder uses this key to sign Verifiable Presentations, proving they are the legitimate owner of the credential. + * Optional, (per SD-JWT VC specification), in practice Mandatory. + +* status + * Provides information about the current status of the credential, such as revoked or suspended. Points to a status list. + * Optional (per SD-JWT VC specification). + +#### Full SD-JWT VC +The final output is a string encoded in base 64 with the format: `<header>.<payload>.<signature>~<disclosure1>~<disclosure2>~<disclosure3>` + +For example with the JWT defined above and the disclosures: +```JSON +["salt_123", "given_name", "John"] +["salt_456", "family_name", "Doe"] +["salt_789", "birthdate", "1990-01-01"] +``` + +``` +ewogICJhbGciOiJFUzI1NiIsIAogICJraWQiOiJkaWQ6ZXhhbXBsZTppc3N1ZXIxMjMja2V5LTEiLCAKICAidHlwIjoidmMrc2Qtand0Igp9 +. +ewogICJpc3MiOiAiZGlkOnRkdzpleGFtcGxlIiwKICAidmN0IjogImh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9vaWQ0dmNpL3ZjdC9teS12Y3QtdjAxIiwKICAidmN0I2ludGVncml0eSI6ICJzaGEyNTYtU1ZITGZLZi4uLmQweXNNY2s9IiwKICAiaWF0IjogMTc1NDU4MTQxMiwKICAibmJmIjogMTc1NDU4MTM3OCwKICAiZXhwIjogMTc1NDU4MTUwOCwKICAiX3NkIjogWwogICAgIjJFRzhueTY1T2NUYzA2MUhrVG1uNzNDSVd4R09sRVVaTG1JMXNwdjVTZjAiLAogICAgInNKNXNaZ2wyU1VIU18zOGJwOXJYMnpHQ0hjcHlXQ0EtcUpOaE1UWlZvXzgiLAogICAgInptbkdFR1N3c1Z2U2FUWk9nXzNHRDVYcjJvN3BLYXNGcXpMWG1EZDNPaW8iCiAgXSwKICAiX3NkX2FsZyI6ICJzaGEtMjU2IiwKICAiY25mIjogewogICAgImp3ayI6IHsKICAgICAgImNydiI6ICJQLTI1NiIsCiAgICAgICJpYXQiOiAxNzU0NTgxMzg4LAogICAgICAia2lkIjogIlRlc3QtS2V5IiwKICAgICAgImt0eSI6ICJFQyIsCiAgICAgICJ1c2UiOiAic2lnIiwKICAgICAgIngiOiAiZnlMeE9WWkpqTnZ1bndRMl8tZ3JnMWpWcEljNWRYSEdwcFJUNVF1VVdJNCIsCiAgICAgICJ5IjogImxYMXZLRTl5dEF0MkZTazRKV2NwcW9UbzQ5bW52MGpva0NoMUZXdWEyamsiCiAgICB9CiAgfSwKICAic3RhdHVzIjogewogICAgInN0YXR1c19saXN0IjogewogICAgICAiaWR4IjogMCwKICAgICAgInR5cGUiOiAiU3dpc3NUb2tlblN0YXR1c0xpc3QtMS4wIiwKICAgICAgInVyaSI6ICJodHRwczovL2xvY2FsaG9zdDo4MDgwL3N0YXR1cyIKICAgIH0KICB9Cn0 +. +SIGNATURE +~ +WyJzYWx0XzEyMyIsICJnaXZlbl9uYW1lIiwgIkpvaG4iXQo= +~ +WyJzYWx0XzQ1NiIsICJmYW1pbHlfbmFtZSIsICJEb2UiXQ== +~ +WyJzYWx0Xzc4OSIsICJiaXJ0aGRhdGUiLCAiMTk5MC0wMS0wMSJd +``` +Note: The example above has newlines (`\n`) to highlight the separation between each component of the sd-jwt VC. + +The final string would actually be: +``` +ewogICJhbGciOiJFUzI1NiIsIAogICJraWQiOiJkaWQ6ZXhhbXBsZTppc3N1ZXIxMjMja2V5LTEiLCAKICAidHlwIjoidmMrc2Qtand0Igp9.ewogICJpc3MiOiAiZGlkOnRkdzpleGFtcGxlIiwKICAidmN0IjogImh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9vaWQ0dmNpL3ZjdC9teS12Y3QtdjAxIiwKICAidmN0I2ludGVncml0eSI6ICJzaGEyNTYtU1ZITGZLZi4uLmQweXNNY2s9IiwKICAiaWF0IjogMTc1NDU4MTQxMiwKICAibmJmIjogMTc1NDU4MTM3OCwKICAiZXhwIjogMTc1NDU4MTUwOCwKICAiX3NkIjogWwogICAgIjJFRzhueTY1T2NUYzA2MUhrVG1uNzNDSVd4R09sRVVaTG1JMXNwdjVTZjAiLAogICAgInNKNXNaZ2wyU1VIU18zOGJwOXJYMnpHQ0hjcHlXQ0EtcUpOaE1UWlZvXzgiLAogICAgInptbkdFR1N3c1Z2U2FUWk9nXzNHRDVYcjJvN3BLYXNGcXpMWG1EZDNPaW8iCiAgXSwKICAiX3NkX2FsZyI6ICJzaGEtMjU2IiwKICAiY25mIjogewogICAgImp3ayI6IHsKICAgICAgImNydiI6ICJQLTI1NiIsCiAgICAgICJpYXQiOiAxNzU0NTgxMzg4LAogICAgICAia2lkIjogIlRlc3QtS2V5IiwKICAgICAgImt0eSI6ICJFQyIsCiAgICAgICJ1c2UiOiAic2lnIiwKICAgICAgIngiOiAiZnlMeE9WWkpqTnZ1bndRMl8tZ3JnMWpWcEljNWRYSEdwcFJUNVF1VVdJNCIsCiAgICAgICJ5IjogImxYMXZLRTl5dEF0MkZTazRKV2NwcW9UbzQ5bW52MGpva0NoMUZXdWEyamsiCiAgICB9CiAgfSwKICAic3RhdHVzIjogewogICAgInN0YXR1c19saXN0IjogewogICAgICAiaWR4IjogMCwKICAgICAgInR5cGUiOiAiU3dpc3NUb2tlblN0YXR1c0xpc3QtMS4wIiwKICAgICAgInVyaSI6ICJodHRwczovL2xvY2FsaG9zdDo4MDgwL3N0YXR1cyIKICAgIH0KICB9Cn0.SIGNATURE~WyJzYWx0XzEyMyIsICJnaXZlbl9uYW1lIiwgIkpvaG4iXQo=~WyJzYWx0XzQ1NiIsICJmYW1pbHlfbmFtZSIsICJEb2UiXQ==~WyJzYWx0Xzc4OSIsICJiaXJ0aGRhdGUiLCAiMTk5MC0wMS0wMSJd +``` diff --git a/documentation/vp.md b/documentation/vp.md @@ -0,0 +1,67 @@ +## Swiyu VP Format +The Verifiable Presentation is generated by the Swiyu Wallet. +The process of creating a Verifiable Presentation (VP) is orchestrated by the `CreateVcSdJwtVerifiablePresentationImpl.kt` file, located at `openid4vc/src/main/java/ch/admin/foitt/openid4vc/domain/usecase/vcSdJwt/implementation/` in the swiyu wallet codebase. The primary methods involved are `invoke` and `createKeyBindingJwt`. + +The resulting VP is a string containing the original SD-JWT Verifiable Credential (VC) and, if required by the verifier, a newly created Key Binding JWT. The Key Binding JWT proves possession of the private key associated with the public key in the VC's `cnf` claim. + +The final VP string is structured as follows: +``` +<vc_sd_jwt>~<kb_header>.<kb_payload>.<kb_signature> +``` +Where `<vc_sd_jwt>` is constituted by +``` +<header>.<payload›.<signature>~<disclosures> +``` +See the Verifiable Credential [breakdown](vc.md) for more details. + +##### Key Binding Header (kb_header) +```JSON +{ "alg": "ES256", "typ": "kb+jwt" } +``` + +#### Key Binding Payload (kb_payload) +The payload contains claims that bind the presentation to the specific transaction. + +```JSON +{ + "sd_hash": [hash of sd-jwt] + "aud": "https://verifier.example.com/callback", + "nonce": "1234567890", + "iat": 1754581412 +} +``` +Where: +* sd_hash (SD-JWT Digest) + * A Base64url-encoded SHA-256 of the SD-JWT VC (including disclosures). This cryptographically links the Key Binding JWT to the credential being presented. + * Mandatory. + +* aud (Audience) + * Identifies the recipient for which the JWT is intended. + * Mandatory. + +* nonce (Nonce) + * Mandatory. + +* iat (Issued At) + * Represents the time at which the Key Binding JWT was issued. Unix timestamp. + * Mandatory. + +### Full SD-JWT VP Example (b64 Encoded) +The final output is a string. It starts with the full SD-JWT VC (header, payload, signature, separated by `.`, and disclosures, separated by `~`), followed by another `~` and the signed Key Binding JWT. +The final output is a string encoded in base 64 with the format: `<header>.<payload›.<signature>~<disclosures>~<kb_header>.<kb_payload>.<kb_signature>` + +``` +ewogICJhbGciOiJFUzI1NiIsIAogICJraWQiOiJkaWQ6ZXhhbXBsZTppc3N1ZXIxMjMja2V5LTEiLCAKICAidHlwIjoidmMrc2Qtand0Igp9.ewogICJpc3MiOiAiZGlkOnRkdzpleGFtcGxlIiwKICAidmN0IjogImh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9vaWQ0dmNpL3ZjdC9teS12Y3QtdjAxIiwKICAidmN0I2ludGVncml0eSI6ICJzaGEyNTYtU1ZITGZLZi4uLmQweXNNY2s9IiwKICAiaWF0IjogMTc1NDU4MTQxMiwKICAibmJmIjogMTc1NDU4MTM3OCwKICAiZXhwIjogMTc1NDU4MTUwOCwKICAiX3NkIjogWwogICAgIjJFRzhueTY1T2NUYzA2MUhrVG1uNzNDSVd4R09sRVVaTG1JMXNwdjVTZjAiLAogICAgInNKNXNaZ2wyU1VIU18zOGJwOXJYMnpHQ0hjcHlXQ0EtcUpOaE1UWlZvXzgiLAogICAgInptbkdFR1N3c1Z2U2FUWk9nXzNHRDVYcjJvN3BLYXNGcXpMWG1EZDNPaW8iCiAgXSwKICAiX3NkX2FsZyI6ICJzaGEtMjU2IiwKICAiY25mIjogewogICAgImp3ayI6IHsKICAgICAgImNydiI6ICJQLTI1NiIsCiAgICAgICJpYXQiOiAxNzU0NTgxMzg4LAogICAgICAia2lkIjogIlRlc3QtS2V5IiwKICAgICAgImt0eSI6ICJFQyIsCiAgICAgICJ1c2UiOiAic2lnIiwKICAgICAgIngiOiAiZnlMeE9WWkpqTnZ1bndRMl8tZ3JnMWpWcEljNWRYSEdwcFJUNVF1VVdJNCIsCiAgICAgICJ5IjogImxYMXZLRTl5dEF0MkZTazRKV2NwcW9UbzQ5bW52MGpva0NoMUZXdWEyamsiCiAgICB9CiAgfSwKICAic3RhdHVzIjogewogICAgInN0YXR1c19saXN0IjogewogICAgICAiaWR4IjogMCwKICAgICAgInR5cGUiOiAiU3dpc3NUb2tlblN0YXR1c0xpc3QtMS4wIiwKICAgICAgInVyaSI6ICJodHRwczovL2xvY2FsaG9zdDo4MDgwL3N0YXR1cyIKICAgIH0KICB9Cn0.SIGNATURE~WyJzYWx0XzEyMyIsICJnaXZlbl9uYW1lIiwgIkpvaG4iXQo=~WyJzYWx0XzQ1NiIsICJmYW1pbHlfbmFtZSIsICJEb2UiXQ==~WyJzYWx0Xzc4OSIsICJiaXJ0aGRhdGUiLCAiMTk5MC0wMS0wMSJd +~ +eyAiYWxnIjogIkVTMjU2IiwgInR5cCI6ICJrYitqd3QiIH0= +. +ewogICJzZF9oYXNoIjogW2hhc2ggb2Ygc2Qtand0XQogICJhdWQiOiAiaHR0cHM6Ly92ZXJpZmllci5leGFtcGxlLmNvbS9jYWxsYmFjayIsCiAgIm5vbmNlIjogIjEyMzQ1Njc4OTAiLAogICJpYXQiOiAxNzU0NTgxNDEyCn0= +. +KB_SIGNATURE +``` +Note: The example above has newlines (`\n`) to highlight the separation between each component of the VP. + +The final string would actually be: +``` +ewogICJhbGciOiJFUzI1NiIsIAogICJraWQiOiJkaWQ6ZXhhbXBsZTppc3N1ZXIxMjMja2V5LTEiLCAKICAidHlwIjoidmMrc2Qtand0Igp9.ewogICJpc3MiOiAiZGlkOnRkdzpleGFtcGxlIiwKICAidmN0IjogImh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9vaWQ0dmNpL3ZjdC9teS12Y3QtdjAxIiwKICAidmN0I2ludGVncml0eSI6ICJzaGEyNTYtU1ZITGZLZi4uLmQweXNNY2s9IiwKICAiaWF0IjogMTc1NDU4MTQxMiwKICAibmJmIjogMTc1NDU4MTM3OCwKICAiZXhwIjogMTc1NDU4MTUwOCwKICAiX3NkIjogWwogICAgIjJFRzhueTY1T2NUYzA2MUhrVG1uNzNDSVd4R09sRVVaTG1JMXNwdjVTZjAiLAogICAgInNKNXNaZ2wyU1VIU18zOGJwOXJYMnpHQ0hjcHlXQ0EtcUpOaE1UWlZvXzgiLAogICAgInptbkdFR1N3c1Z2U2FUWk9nXzNHRDVYcjJvN3BLYXNGcXpMWG1EZDNPaW8iCiAgXSwKICAiX3NkX2FsZyI6ICJzaGEtMjU2IiwKICAiY25mIjogewogICAgImp3ayI6IHsKICAgICAgImNydiI6ICJQLTI1NiIsCiAgICAgICJpYXQiOiAxNzU0NTgxMzg4LAogICAgICAia2lkIjogIlRlc3QtS2V5IiwKICAgICAgImt0eSI6ICJFQyIsCiAgICAgICJ1c2UiOiAic2lnIiwKICAgICAgIngiOiAiZnlMeE9WWkpqTnZ1bndRMl8tZ3JnMWpWcEljNWRYSEdwcFJUNVF1VVdJNCIsCiAgICAgICJ5IjogImxYMXZLRTl5dEF0MkZTazRKV2NwcW9UbzQ5bW52MGpva0NoMUZXdWEyamsiCiAgICB9CiAgfSwKICAic3RhdHVzIjogewogICAgInN0YXR1c19saXN0IjogewogICAgICAiaWR4IjogMCwKICAgICAgInR5cGUiOiAiU3dpc3NUb2tlblN0YXR1c0xpc3QtMS4wIiwKICAgICAgInVyaSI6ICJodHRwczovL2xvY2FsaG9zdDo4MDgwL3N0YXR1cyIKICAgIH0KICB9Cn0.SIGNATURE~WyJzYWx0XzEyMyIsICJnaXZlbl9uYW1lIiwgIkpvaG4iXQo=~WyJzYWx0XzQ1NiIsICJmYW1pbHlfbmFtZSIsICJEb2UiXQ==~WyJzYWx0Xzc4OSIsICJiaXJ0aGRhdGUiLCAiMTk5MC0wMS0wMSJd~eyAiYWxnIjogIkVTMjU2IiwgInR5cCI6ICJrYitqd3QiIH0=.ewogICJzZF9oYXNoIjogW2hhc2ggb2Ygc2Qtand0XQogICJhdWQiOiAiaHR0cHM6Ly92ZXJpZmllci5leGFtcGxlLmNvbS9jYWxsYmFjayIsCiAgIm5vbmNlIjogIjEyMzQ1Njc4OTAiLAogICJpYXQiOiAxNzU0NTgxNDEyCn0=.KB_SIGNATURE +```