diff --git a/.gitignore b/.gitignore index 53a0aa38..ca78e4e0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ *.pdf *.redxml *.swp -*.txt *.upload *~ .tags diff --git a/draft-ietf-oauth-selective-disclosure-jwt.md b/draft-ietf-oauth-selective-disclosure-jwt.md index c7d3422a..5510bf97 100644 --- a/draft-ietf-oauth-selective-disclosure-jwt.md +++ b/draft-ietf-oauth-selective-disclosure-jwt.md @@ -67,21 +67,16 @@ tamper-evident credential with a cryptographically verifiable authorship that contains claims about a subject. SD-JWTs defined in this document enable such selective disclosure of claims. -In an SD-JWT, claim values are hidden, but cryptographically protected +In an SD-JWT, claims can be hidden, but cryptographically protected against undetected modification. When issuing the SD-JWT to the Holder, -the Issuer also sends a JSON object that contains a mapping between -hidden claim values and their cleartext counterparts, the so-called -Disclosures. This JSON object is therefore called the Issuer-Issued -Disclosures (II-Disclosures) object. - -The Holder decides which claims to disclose to a Verifier. This -specification defines a format for conveying the selected subset of the -II-Disclosures to the Verifier. This subset is called the -Holder-Selected Disclosures (HS-Disclosures) and is transported in a JWT, the -HS-Disclosures JWT, for presentation alongside the SD-JWT. The Verifier -can (and has to) verify that all disclosed claim values were part of the original, +the Issuer also sends the cleartext counterparts of all hidden claims, the so-called +Disclosures, separate from the SD-JWT itself. + +The Holder decides which claims to disclose to a Verifier and forwards the respective +Disclosures together with the SD-JWT to the Verifier. The Verifier +has to verify that all disclosed claim values were part of the original, Issuer-signed SD-JWT. The Verifier will not, however, learn any claim -values not disclosed in HS-Disclosures. +values not disclosed in the Disclosures. While JWTs for claims describing natural persons are a common use case, the mechanisms defined in this document can be used for many other use @@ -106,10 +101,7 @@ wherever possible. as well as more complex, nested data structures. * This specification enables combining selectively disclosable claims with clear-text claims that are always disclosed. -* Optionally, this specification allows to also hide ("blind") the claim names, - not only the claim values. -* When claim names are blinded, this specification enables combining claims with - blinded and unblinded names in the same SD-JWT. +* For selectively disclosable claims, claim names are always blinded. ## Conventions and Terminology @@ -133,24 +125,13 @@ Selectively Disclosable JWT (SD-JWT): that supports selective disclosure as defined in this document and can contain both regular claims and digests of selectively-disclosable claims. Disclosure: -: A combination of a cleartext claim value, a cleartext claim name, a salt, and - optionally a blinded claim name value that is used to calculate a digest for a certain claim. - -Issuer-Issued Disclosures Object (II-Disclosures Object): -: A JSON object created by the Issuer that contains Disclosures for all selectively-disclosable claims in an SD-JWT. - -Holder-Selected Disclosures JWT (HS-Disclosures JWT): -: A JWT created by the Holder that contains the Disclosures from an Issuer-Issued Disclosures Object that the Holder is disclosing to the Verifier. In addition to the Disclosures, it can contain other properties and may be signed by the Holder. +: A combination of a salt, a cleartext claim name, and a cleartext claim value that is used to calculate a digest for a certain claim. -Holder Binding: +Cryptographic Holder Binding: : Ability of the Holder to prove legitimate possession of an SD-JWT by proving control over the same private key during the issuance and presentation. An SD-JWT with Holder Binding contains a public key or a reference to a public key that matches to the private key controlled by the Holder. -Claim Name Blinding: -: Feature that enables to blind not only claim values, but also claim names of the claims - that are included in SD-JWT but are not disclosed to the Verifier in the HS-Disclosures JWT. - Issuer: : An entity that creates SD-JWTs. @@ -158,7 +139,7 @@ Holder: : An entity that received SD-JWTs from the Issuer and has control over them. Verifier: -: An entity that requests, checks and extracts the claims from HS-Disclosures JWT. +: An entity that requests, checks and extracts the claims from an SD-JWT and respective Disclosures. Note: discuss if we want to include Client, Authorization Server for the purpose of ensuring continuity and separating the entity from the actor. @@ -173,7 +154,7 @@ ensuring continuity and separating the entity from the actor. +------------+ | Issues SD-JWT - and Issuer-Issued Disclosures Object + and Issuer-Issued Disclosures | v +------------+ @@ -183,7 +164,7 @@ ensuring continuity and separating the entity from the actor. +------------+ | Presents SD-JWT - and Holder-Selected Disclosures JWT + and Holder-Selected Disclosures | v +-------------+ @@ -198,16 +179,17 @@ Figure: SD-JWT Issuance and Presentation Flow # Concepts -In the following, the contents of SD-JWTs and HS-Disclosures JWTs are described at a +In the following, the contents of SD-JWTs and Disclosures are described at a conceptual level, abstracting from the data formats described afterwards. ## Creating an SD-JWT -An SD-JWT, at its core, is a digitally signed document containing digests over the claim values with random salts and other metadata. +An SD-JWT, at its core, is a digitally signed document containing digests over the claims (per claim: a random salt, the claim name and the claim value). +It MAY further contain clear-text claims that are always disclosed to the Verifier. It MUST be digitally signed using the Issuer's private key. ``` -SD-JWT-DOC = (METADATA, SD-CLAIMS) +SD-JWT-DOC = (METADATA, SD-CLAIMS, NON-SD-CLAIMS) SD-JWT = SD-JWT-DOC | SIG(SD-JWT-DOC, ISSUER-PRIV-KEY) ``` @@ -215,7 +197,7 @@ SD-JWT = SD-JWT-DOC | SIG(SD-JWT-DOC, ISSUER-PRIV-KEY) ``` SD-CLAIMS = ( - CLAIM-NAME: DIGEST-DERIVATION(SALT, CLAIM-VALUE) + DIGEST-DERIVATION(SALT, CLAIM-NAME, CLAIM-VALUE) )* ``` @@ -226,93 +208,81 @@ However, the term "salt" is used throughout this document for brevity. `SD-JWT` is sent from the Issuer to the Holder, together with the mapping of the plain-text claim values, the salt values, and potentially some other information. -## Creating a Holder-Selected Disclosures JWT +The Issuer further creates a set of Disclosures for all claims in the SD-JWT. The Disclosures are sent to the Holder together with the SD-JWT: + +``` +DISCLOSURES = ( + (SALT, CLAIM-NAME, CLAIM-VALUE) +)* +``` -To disclose to a Verifier a subset of the SD-JWT claim values, a Holder creates a JWT such as the -following: +The SD-JWT and the Disclosures are sent to the Holder by the Issuer: ``` -HOLDER-SELECTED-DISCLOSURES-DOC = (METADATA, SD-DISCLOSURES) -HOLDER-SELECTED-DISCLOSURES-JWT = HOLDER-SELECTED-DISCLOSURES-DOC +COMBINED-ISSUANCE = SD-JWT | DISCLOSURES ``` +## Creating Holder-Selected Disclosures -`SD-DISCLOSURES` follows the structure of `SD-CLAIMS` and can be a simple object with claim names mapped to values and salts: +To disclose to a Verifier a subset of the SD-JWT claim values, a Holder selects a subset of the Disclosures and sends it to the Verifier along with the SD-JWT. ``` -SD-DISCLOSURES = ( - CLAIM-NAME: (DISCLOSED-SALT, DISCLOSED-VALUE) -) +HOLDER-SELECTED-DISCLOSURES = ( + (SALT, CLAIM-NAME, CLAIM-VALUE) +)* ``` -Just as `SD-CLAIMS`, `SD-DISCLOSURES` can be more complex as well. - -`HOLDER-SELECTED-DISCLOSURES-JWT` is sent together with `SD-JWT` from the Holder to the -Verifier. +``` +COMBINED-PRESENTATION = SD-JWT | HOLDER-SELECTED-DISCLOSURES +``` ## Optional Holder Binding Some use-cases may require Holder Binding. -If Holder Binding is desired, `SD-JWT` must contain information about key material controlled by the Holder: +Cryptographic Holder Binding is an optional feature, but when it is desired, `SD-JWT` must contain information about key material controlled by the Holder: ``` -SD-JWT-DOC = (METADATA, HOLDER-PUBLIC-KEY, SD-CLAIMS) +SD-JWT-DOC = (METADATA, HOLDER-PUBLIC-KEY, SD-CLAIMS, NON-SD-CLAIMS) ``` Note: How the public key is included in SD-JWT is out of scope of this document. It can be passed by value or by reference. -With Holder Binding, the `HOLDER-SELECTED-DISCLOSURES-JWT` is signed by the Holder using its private key. It therefore looks as follows: +The Holder can then create a signed document `HOLDER-BINDING-JWT` using its private key. This document contains some +data provided by the Verifier (out of scope of this document) to ensure the freshness of the signature, for example, a nonce and an indicator of the +intended audience for the document. ``` -HOLDER-SELECTED-DISCLOSURES = HOLDER-SELECTED-DISCLOSURES-DOC | - SIG(HOLDER-SELECTED-DISCLOSURES-DOC, HOLDER-PRIV-KEY) +HOLDER-BINDING-JWT-DOC = (NONCE, AUDIENCE) +HOLDER-BINDING-JWT = HOLDER-BINDING-JWT-DOC | + SIG(HOLDER-BINDING-JWT-DOC, HOLDER-PRIV-KEY) ``` -### Optional Claim Name Blinding +The Holder Binding JWT is sent to the Verifier along with the SD-JWT and the Holder-Selected Disclosures. -If Claim Name Blinding is used, `SD-CLAIMS` is created as follows: ``` -SD-CLAIMS = ( - CLAIM-NAME-PLACEHOLDER: DIGEST-DERIVATION(SALT, - CLAIM-VALUE, CLAIM-NAME) -)* +COMBINED-PRESENTATION = SD-JWT | HOLDER-SELECTED-DISCLOSURES | HOLDER-BINDING-JWT ``` -`CLAIM-NAME-PLACEHOLDER` is a placeholder used instead of the original claim -name, chosen such that it does not leak information about the claim name (e.g., -randomly). - -The contents of `SD-DISCLOSURES` are modified as follows: -``` -SD-DISCLOSURES = ( - CLAIM-NAME-PLACEHOLDER: (DISCLOSED-SALT, - DISCLOSED-VALUE, DISCLOSED-CLAIM-NAME) -) -``` -Note that blinded and unblinded claim names can be mixed in `SD-CLAIMS` and accordingly in `SD-DISCLOSURES`. +Note that there may be other ways to send the Holder Binding JWT to the Verifier or to prove Holder Binding. In these cases, inclusion of the Holder Binding JWT in the `COMBINED-PRESENTATION` is not required. -## Verifying a Holder-Selected Disclosures JWT +## Verifying Holder-Selected Disclosures -A Verifier checks that +At a high level, the Verifier - * for each claim in `HOLDER-SELECTED-DISCLOSURES`, the digest over the disclosed values - matches the digest under the given claim name in `SD-JWT`, - * if Holder Binding is used, the `HOLDER-SELECTED-DISCLOSURES` was signed by the private key - belonging to `HOLDER-PUBLIC-KEY`. + * receives the `COMBINED-PRESENTATION` from the Holder and verifies the signature of the SD-JWT using the Issuer's public key, + * verifies the Holder Binding JWT, if Holder Binding is required by the Verifier's policy, using the public key included in the SD-JWT, + * calculates the hash digests over the Holder-Selected Disclosures and verifies that each digest is contained in the SD-JWT. -The detailed algorithm is described in (#verifier-verification). +The detailed algorithm is described in (#verifier_verification). # Data Formats -This section defines data formats for SD-JWT (containing digests of the salted -claim values), Issuer-Issued Disclosures (containing the mapping of the -plain-text claim values and the salt values), and HS-Disclosures -(containing a subset of the same mapping). +This section defines data formats for SD-JWTs, Disclosures, Holder Binding JWTs and formats for combining these elements for transport. ## The Challenge of Canonicalization {#canonicalization} -When receiving an SD-JWT with associated HS-Disclosures, a Verifier must +When receiving an SD-JWT with associated Disclosures, a Verifier must be able to re-compute digests of the disclosed claim values and, given the same input values, obtain the same digest values as signed by the Issuer. @@ -333,7 +303,7 @@ However, a problem arises when computation over the data need to be performed an signature verification as was used for creating the signature. In the digest derivation approach outlined above, the same problem exists: for the Issuer and the Verifier to arrive at the same digest, the same byte string must be hashed. -JSON, however, does not prescribe a unique encoding for data, but allows for variations in the encoded string. The data above, for example, can be encoded as +JSON [@!RFC7159], however, does not prescribe a unique encoding for data, but allows for variations in the encoded string. The data above, for example, can be encoded as ``` ... @@ -372,7 +342,7 @@ There are generally two approaches to deal with this problem: canonical form before computing a digest. Both the Issuer and the Verifier must use the same canonicalization algorithm to arrive at the same byte string for computing a digest. -2. Source string encoding: Instead of transferring data in a format that +2. Source string hardening: Instead of transferring data in a format that may introduce variations, a representation of the data is serialized. This representation is then used as the digest input at the Verifier, but also transferred to the Verifier and used for the same digest @@ -385,128 +355,162 @@ plus a string suitable for computing a digest, but such approaches can easily le undetected inconsistencies resulting in time-of-check-time-of-use type security vulnerabilities. -In this specification, the source string encoding approach is used, as +In this specification, the source string hardening approach is used, as it allows for simple and reliable interoperability without the -requirement for a canonicalization library. To encode the source string, +requirement for a canonicalization library. To harden the source string, any serialization format that supports the necessary data types could be used in theory, like protobuf, msgpack, or pickle. In this -specification, JSON is used, as it is human-readable and used in JWTs as -well. This approach means that SD-JWTs can be implemented purely based -on widely available JWT and JSON encoding and decoding libraries. - -To produce a source string to compute a digest, the data is put into a JSON object -together with the salt value, like so (non-normative example, see -(#sd_digests_claim) for details): - -``` -{"s": "6qMQvRL5haj", "v": "Möbius"} -``` - -Or, for the address example above: -``` -{"s": "al1N3Zom221", "v": - {"locality": "Schulpforta", "street_address": "Schulstr. 12"}} -``` -(Line break and indentation of the second line for presentation only!) - -This object is then JSON-encoded and used as the source string. The JSON-encoded value is transferred in the HS-Disclosures instead of the original JSON data: - -``` -"family_name": "{\"s\": \"6qMQvRL5haj\", \"v\": \"M\\u00f6bius\"}" -``` - -Or, for the address example: -``` -"address": "{\"s\": \"al1N3Zom221\", \"v\": - {\"locality\": \"Schulpforta\", - \"street_address\": \"Schulstr. 12\"}}" -``` -(Line break and indentation of the second and third line for presentation only!) +specification, JSON is used and plain text values of each Disclosure is encoded using base64url-encoding +for transport. This approach means that SD-JWTs can be implemented purely based +on widely available JWT, JSON, and Base64 encoding and decoding libraries. A Verifier can then easily check the digest over the source string before extracting the original JSON data. Variations in the encoding of the source string are implicitly tolerated by the Verifier, as the digest is computed over a predefined byte string and not over a JSON object. -Since the encoding is based on JSON, all value types that are allowed in JSON -are also allowed in the `v` property in the source string. This includes -numbers, strings, booleans, arrays, and objects. - -It is important to note that the HS-Disclosures object containing the -source string is neither intended nor suitable for direct consumption by -an application that needs to access the disclosed claim values. The -HS-Disclosures object is only intended to be used by a Verifier to check +It is important to note that the Disclosures are neither intended nor +suitable for direct consumption by +an application that needs to access the disclosed claim values after the verification by the Verifier. The +Disclosures are only intended to be used by a Verifier to check the digests over the source strings and to extract the original JSON data. The original JSON data is then used by the application. See -(#processing_model) for details. +(#verifier_verification) for details. ## Format of an SD-JWT An SD-JWT is a JWT that MUST be signed using the Issuer's private key. The -payload of an SD-JWT MUST contain the `sd_digests` and `sd_digest_derivation_alg` claims -described in the following, and MAY contain a Holder's public key or a reference +payload of an SD-JWT MUST contain the `sd_digest_derivation_alg` claim +described in the following, MAY contain one or more selectively disclosable claims, and MAY contain a Holder's public key or a reference thereto, as well as further claims such as `iss`, `iat`, etc. as defined or required by the application using SD-JWTs. -### `sd_digests` Claim (Digests of Selectively Disclosable Claims) {#sd_digests_claim} +### Selectively Disclosable Claims -The property `sd_digests` MUST be used by the Issuer to include digests of the salted claim values for any claim that is intended to be selectively disclosable. +For each claim that is to be selectively disclosed, the Issuer creates a Disclosure, hashes it, and includes the hash instead of the original claim in the SD-JWT, as described next. The Disclosures are then sent to the Holder. -The Issuer MUST choose a new, cryptographically random salt value for -each claim value. The salt value MUST then be encoded as a string. It is -RECOMMENDED to base64url-encode the salt value. +#### Creating Disclosures {#creating_disclosures} +The Issuer MUST create a Disclosure for each selectively disclosable claim as follows: -The Issuer MUST generate the digests over a JSON literal according to -[@!RFC8259] that is formed by -JSON-encoding an object with the following contents: + * Create an array of three elements in this order: + 1. A salt value. See (#salt-entropy) and (#salt_minlength) for security considerations. The salt value MUST be unique for each claim that is to be selectively disclosed. It is RECOMMENDED to base64url-encode the salt value, producing a string. Any other type that is allowed in JSON MAY be used, e.g., a number. + 2. The claim name, or key, as it would be used in a regular JWT body. This MUST be a string. + 3. The claim's value, as it would be used in a regular JWT body. The value MAY be of any type that is allowed in JSON, including numbers, strings, booleans, arrays, and objects. + * JSON-encode the array such that an UTF-8 string is produced. + * base64url-encode the byte representation of the UTF-8 string, producing a US-ASCII [@RFC0020] string. This string is the Disclosure. - * REQUIRED with the key `s`: the salt value, - * REQUIRED with the key `v`: the claim value (either a string or a more complex object, e.g., for the [@OIDC] `address` claim), - * OPTIONAL, with the key `n`: the claim name (if Claim Name Blinding is to be used for this claim). +The order is decided based on the readability considerations: salts would have a constant length within the SD-JWT, claim names would be around the same length all the time, and claim values would vary in size, potentially being large objects. -The following is an example for a JSON literal without Claim Name Blinding: +The following example illustrates the steps described above. -``` -{"s": "6qMQvRL5haj", "v": "Peter"} +The array is created as follows: +```json +["6qMQvRL5haj", "family_name", "Möbius"] ``` -The following is an example for a JSON literal with Claim Name Blinding: +The resulting Disclosure would be `WyI2cU1RdlJMNWhhaiIsICJmYW1pbHlfbmFtZSIsICJNw7ZiaXVzIl0`. -``` -{"s": "6qMQvRL5haj", "v": "Peter", "n": "given_name"} -``` +Note that the JSON encoding of the object is not canonicalized, so variations in white space, encoding +of Unicode characters, and ordering of object properties are allowed. For example, the following strings +are all valid and encode the same claim value: + + * A different way to encode the umlaut (two dots `¨` placed over the letter): `WyI2cU1RdlJMNWhhaiIsICJmYW1pbHlfbmFtZSIsICJNXHUwMGY2Yml1cyJd` + * No white space: `WyI2cU1RdlJMNWhhaiIsImZhbWlseV9uYW1lIiwiTcO2Yml1cyJd` + * Newline characters between elements: `WwoiNnFNUXZSTDVoYWoiLAoiZmFtaWx5X25hbWUiLAoiTcO2Yml1cyIKXQ` + +#### Hashing Disclosures {#hashing_disclosures} + +For embedding the Disclosures in the SD-JWT, the Disclosures are hashed using the digest algorithm specified in the `sd_digest_derivation_alg` claim described below. The resulting hash is then included in the SD-JWT instead of the original claim value, as described next. + +The hash digest MUST be taken over the US-ASCII bytes of the base64url-encoded Disclosure. This follows the convention in JWS [@RFC7515] and JWE [@RFC7516]. The bytes of the hash digest MUST then be base64url-encoded. + +It is important to note that: + + * The input to the hash function is the base64url-encoded Disclosure, not the bytes encoded by the base64url string. + * The bytes of the output of the hash function are base64url-encoded, not the bytes making up the (often used) hex representation of the bytes of the hash digest. + +For example, the +SHA-256 hash digest of the Disclosure `WyI2cU1RdlJMNWhhaiIsICJmYW1pbHlfbmFtZSIsICJNw7ZiaXVzIl0` would be +`uutlBuYeMDyjLLTpf6Jxi7yNkEF35jdyWMn9U7b_RYY`. + +#### Decoy Digests {#decoy_digests} + +An Issuer MAY add additional hash digests to the SD-JWT that are not associated with any claim. The purpose of such "decoy" digests is to make it more difficult for an attacker to see the original number of claims contained in the SD-JWT. It is RECOMMENDED to create the decoy digests by hashing over a cryptographically secure random number. The bytes of the hash digest MUST then be base64url-encoded as above. The same digest function as for the Disclosures MUST be used. + +For decoy digests, no Disclosure is sent to the Holder, i.e., the Holder will see hash digests that do not correspond to any Disclosure. See (#decoy_digests_privacy) for additional privacy considerations. + +To ensure readability and replicability, the examples in this specification do not contain decoy digests unless explicitly stated. + +#### Creating an SD-JWT {#creating_sd_jwt} + +An SD-JWT is a JWT that MUST be signed using the Issuer's private key. + +An SD-JWT MAY contain both selectively disclosable claims and non-selectively disclosable claims, i.e., claims that are always contained in the SD-JWT in plaintext and are always visible to a Verifier. + +It is the Issuer who decides which claims are selectively disclosable and which are not. However, claims controlling the validity of the SD-JWT, such as `iss`, `exp`, or `nbf` are usually included in plaintext. End-User claims MAY be included as plaintext as well, e.g., if hiding the particular claims from the Verifier does not make sense in the intended use case. + +Claims that are not selectively disclosable are included in the SD-JWT in plaintext just as they would be in any other JWT. + +Selectively disclosable claims are omitted from the SD-JWT. Instead, the hash digests of the respective Disclosures and potentially decoy digests are contained as an array in a new JWT claim, `_sd`. + +The `_sd` claim MUST be an array of strings, each string being a hash digests of a Disclosure or a decoy digest as described above. +The array MAY be empty in case the Issuer decided not to selectively disclose any of the claims at that level. However, it is RECOMMENDED to omit `_sd` claim in this case to save space. + +The Issuer MUST hide the original order of the claims in the array. To ensure this, it is RECOMMENDED to shuffle the array of hashes, e.g., by sorting it alphanumerically or randomly. The precise method does not matter as long as it does not depend on the original order of elements. + +Issuers MUST NOT issue SD-JWTs where + + * the key `_sd` is already used for the purpose other than to contain the array of hash digests, or + * the claim value contained in a Disclosure contains (at the top level or nested deeper) an object with an `_sd` key, or + * the same Disclosure value appears more than once (in the same array or in different arrays). + + +#### Nested Data in SD-JWTs {#nested_data} + +Just like any JWT, an SD-JWT MAY contain key value pairs where the value is an object. For any object in an SD-JWT, the Issuer MAY decide to either make the entire object selectively disclosable or to make its properties selectively disclosable individually. In the latter case, the Issuer MAY even choose to make some some of the object's properties selectively disclosable and others not. + +In any case, the `_sd` claim MUST be included in the SD-JWT at the same level as the original claim and therefore MAY appear multiple times in an SD-JWT. + +The following examples show some of the options an Issuer has when producing an SD-JWT with the following End-User data. + +<{{examples/address_only_flat/user_claims.json}} + +Important: Throughout the examples in this document, line breaks had to +be added to JSON strings and base64-encoded strings (as shown in the +next example) to adhere to the 72 character limit for lines in RFCs and +for readability. JSON does not allow line breaks in strings. + +##### Option 1: Flat SD-JWT + +The Issuer can decide to treat the `address` claim as a block that can either be disclosed completely or not at all. The following example shows that in this case, the entire `address` claim is treated as an object in the Disclosure. + +<{{examples/address_only_flat/sd_jwt_payload.json}} + +The Issuer would create the following Disclosure: + +{{examples/address_only_flat/disclosures.md}} + +##### Option 2: Structured SD-JWT + +The Issuer may instead decide to make the `address` claim contents selectively disclosable individually: + +<{{examples/address_only_structured/sd_jwt_payload.json}} + +In this case, the Issuer would use the following data in the Disclosures for the `address` sub-claims: -The `sd_digests` claim contains an object where claim names are mapped -to the respective digests. If a claim name is to be blinded, the digests -MUST contain the `n` key as described above and the claim name in -`sd_digests` MUST be replaced by a placeholder name that does not leak -information about the claim's original name. The same placeholder name -will be used in the II-Disclosures (`sd_ii_disclosures`) and -HS-Disclosures (`sd_hs_disclosures`) described below. +{{examples/address_only_structured/disclosures.md}} -To this end, the Issuer MUST choose a random placeholder name for each -claim that is to be blinded. It is RECOMMENDED to do so by -base64url-encoding a cryptographically secure nonce. See -(#blinding-claim-names) for further requirements. +##### Option 3: Structured SD-JWT, only some properties selectively disclosable -#### Flat and Structured `sd_digests` objects +The Issuer may also make one sub-claim of `address` non-selectively disclosable and hide only the other sub-claims: -The `sd_digests` object can be a 'flat' object, directly containing all claim -names and digests without any deeper structure. The `sd_digests` -object can also be a 'structured' object, where some claims and their respective -digests are contained in places deeper in the structure. It is at the Issuer's -discretion whether to use a 'flat' or 'structured' `sd_digests` SD-JWT object, -and how to structure it such that it is suitable for the use case. +<{{examples/address_only_structured_one_open/sd_jwt_payload.json}} -Example 1 in (#example-1) is a non-normative example of an SD-JWT using a 'flat' -`sd_digests` object and Example 2a in (#example-simple-structured-sd-jwt) shows a non-normative example -of an SD-JWT using a 'structured' `sd_digests` object. The difference between -the examples is how the `address` claim is disclosed. +In this case, the Issuer would issue the following Disclosures: -(#example-complex-structured-sd-jwt) shows a more complex example using claims -from OpenID Connect for Identity Assurance [@OIDC.IDA]. +{{examples/address_only_structured_one_open/disclosures.md}} -### Digest Derivation Function Claim +### Digest Derivation Function Claim {#digest_derivation_function_claim} The claim `sd_digest_derivation_alg` indicates the digest derivation algorithm used by the Issuer to generate the digests over the salts and the @@ -523,7 +527,7 @@ To promote interoperability, implementations MUST support the SHA-256 hash algor See (#security_considerations) for requirements regarding entropy of the salt, minimum length of the salt, and choice of a digest derivation algorithm. -### Holder Public Key Claim +### Holder Public Key Claim {#holder_public_key_claim} If the Issuer wants to enable Holder Binding, it MAY include a public key associated with the Holder, or a reference thereto. @@ -537,447 +541,173 @@ Note: Examples in this document use `cnf` Claim defined in [@RFC7800] to include ## Example 1: SD-JWT {#example-1} -This example and Example 2a in (#example-simple-structured-sd-jwt) use the following object as the set -of claims that the Issuer is issuing: +This example uses the following object as the set of claims that the Issuer is issuing: -{#example-simple-user_claims} -```json -{ - "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", - "given_name": "John", - "family_name": "Doe", - "email": "johndoe@example.com", - "phone_number": "+1-202-555-0101", - "address": { - "street_address": "123 Main St", - "locality": "Anytown", - "region": "Anystate", - "country": "US" - }, - "birthdate": "1940-01-01" -} -``` +<{{examples/simple/user_claims.json}} The following non-normative example shows the payload of an SD-JWT. The Issuer -is using a flat structure, i.e., all of the claims in the `address` claim can only +is using a flat structure in this case, i.e., all of the claims in the `address` claim can only be disclosed in full. -{#example-simple-sd_jwt_payload} -```json -{ - "iss": "https://example.com/issuer", - "cnf": { - "jwk": { - "kty": "RSA", - "n": "pm4bOHBg-oYhAyPWzR56AWX3rUIXp11_ICDkGgS6W3ZWLts-hzwI3x65 - 659kg4hVo9dbGoCJE3ZGF_eaetE30UhBUEgpGwrDrQiJ9zqprmcFfr3qvvkG - jtth8Zgl1eM2bJcOwE7PCBHWTKWYs152R7g6Jg2OVph-a8rq-q79MhKG5QoW - _mTz10QT_6H4c7PjWG1fjh8hpWNnbP_pv6d1zSwZfc5fl6yVRL0DV0V3lGHK - e2Wqf_eNGjBrBLVklDTk8-stX_MWLcR-EGmXAOv0UBWitS_dXJKJu-vXJyw1 - 4nHSGuxTIK2hx1pttMft9CsvqimXKeDTU14qQL1eE7ihcw", - "e": "AQAB" - } - }, - "iat": 1516239022, - "exp": 1516247022, - "sd_digest_derivation_alg": "sha-256", - "sd_digests": { - "sub": "2EDXXZ1JcE6aTcM70fZopFneYAS9-hY3lalaoLuWD1s", - "given_name": "pC56LWpTgec18Ll1kps3koXapnw6SOiI0d1ba34t-mY", - "family_name": "EySQc316Ln3ZGJXwioELWSyylm_6OXV6rcL6LyPb7oI", - "email": "qHv6gGaq4oFmIXyKh9ZlFjQ5rOClS-dXHiPMZyl2FaU", - "phone_number": "jhr_PsauT4xsYZS_OxBW8y_1MLULOovKseRvF9CE0TM", - "address": "eQXgmowqkT_ORkedoqeW0wBUy4vzkWG1VhvOjh3tl_o", - "birthdate": "qgDxFuNpf83MkKe4GCaiLuL_XZdzO4pYD7lQKbv4zos" - } -} -``` - -Important: Throughout the examples in this document, line breaks had to -be added to JSON strings and base64-encoded strings (as shown in the -next example) to adhere to the 72 character limit for lines in RFCs and -for readability. JSON does not allow line breaks in strings. +<{{examples/simple/sd_jwt_payload.json}} The SD-JWT is then signed by the Issuer to create a JWT like the following: -{#example-simple-serialized_sd_jwt} -``` -eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ -UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJpc3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc -3N1ZXIiLCAiY25mIjogeyJqd2siOiB7Imt0eSI6ICJSU0EiLCAibiI6ICJwbTRiT0hCZ -y1vWWhBeVBXelI1NkFXWDNyVUlYcDExX0lDRGtHZ1M2VzNaV0x0cy1oendJM3g2NTY1O -WtnNGhWbzlkYkdvQ0pFM1pHRl9lYWV0RTMwVWhCVUVncEd3ckRyUWlKOXpxcHJtY0Zmc -jNxdnZrR2p0dGg4WmdsMWVNMmJKY093RTdQQ0JIV1RLV1lzMTUyUjdnNkpnMk9WcGgtY -ThycS1xNzlNaEtHNVFvV19tVHoxMFFUXzZINGM3UGpXRzFmamg4aHBXTm5iUF9wdjZkM -XpTd1pmYzVmbDZ5VlJMMERWMFYzbEdIS2UyV3FmX2VOR2pCckJMVmtsRFRrOC1zdFhfT -VdMY1ItRUdtWEFPdjBVQldpdFNfZFhKS0p1LXZYSnl3MTRuSFNHdXhUSUsyaHgxcHR0T -WZ0OUNzdnFpbVhLZURUVTE0cVFMMWVFN2loY3ciLCAiZSI6ICJBUUFCIn19LCAiaWF0I -jogMTUxNjIzOTAyMiwgImV4cCI6IDE1MTYyNDcwMjIsICJzZF9kaWdlc3RfZGVyaXZhd -Glvbl9hbGciOiAic2hhLTI1NiIsICJzZF9kaWdlc3RzIjogeyJzdWIiOiAiMkVEWFhaM -UpjRTZhVGNNNzBmWm9wRm5lWUFTOS1oWTNsYWxhb0x1V0QxcyIsICJnaXZlbl9uYW1lI -jogInBDNTZMV3BUZ2VjMThMbDFrcHMza29YYXBudzZTT2lJMGQxYmEzNHQtbVkiLCAiZ -mFtaWx5X25hbWUiOiAiRXlTUWMzMTZMbjNaR0pYd2lvRUxXU3l5bG1fNk9YVjZyY0w2T -HlQYjdvSSIsICJlbWFpbCI6ICJxSHY2Z0dhcTRvRm1JWHlLaDlabEZqUTVyT0NsUy1kW -EhpUE1aeWwyRmFVIiwgInBob25lX251bWJlciI6ICJqaHJfUHNhdVQ0eHNZWlNfT3hCV -zh5XzFNTFVMT292S3NlUnZGOUNFMFRNIiwgImFkZHJlc3MiOiAiZVFYZ21vd3FrVF9PU -mtlZG9xZVcwd0JVeTR2emtXRzFWaHZPamgzdGxfbyIsICJiaXJ0aGRhdGUiOiAicWdEe -EZ1TnBmODNNa0tlNEdDYWlMdUxfWFpkek80cFlEN2xRS2J2NHpvcyJ9fQ.0w8PQ_tg2K -6Q82XhXn3-Nmi7uGeXkOFFMSfp_8iMKRRlfg-HXXdoZWv8UECv1B2PIJITjH2RAz_egY -j-dLkPopnJ-0vIDKjKhvMCIIo0FEnTV3qQct-8s6NifR2exU1TuyF66Z9Jekk1V3M4Bn -KxCc6-mEf7_d1K-EfQ34dI-6XJFh05s1_sE7ePFvLRGtj4tHHQlwWGm7wQJqPRYtA_F0 -N10jIlyFbw4B6T59TpI8ZjHgucCxF9p1IUb-RYb6P1dYF4sVdQT258jAJVCAPz62JoRn --cPPwV-QbpAKD7npkk7pTxkYg0T9_iyvMcq_RdXGqqANkJn8qxEffwp_OsgA -``` +<{{examples/simple/sd_jwt_serialized.txt}} +The Issuer creates the following Disclosures: -## Format of an Issuer-Issued Disclosures Object - -Besides the SD-JWT itself, the Holder needs to learn the raw claim values that -are contained in the SD-JWT, along with the precise input to the digest -calculation and the salts. There MAY be other information the Issuer needs to -communicate to the Holder, such as a private key if the Issuer selected the -Holder key pair. +{{examples/simple/disclosures.md}} -An Issuer-Issued Disclosures Object (II-Disclosures Object) is a JSON object containing at least the -top-level property `sd_ii_disclosures`. Its structure mirrors the one of `sd_digests` in -the SD-JWT, but the values are the inputs to the digest calculations the Issuer -used (the Disclosures), as strings. -The II-Disclosures Object MAY contain further properties, for example, to transport the Holder -private key. +## Combined Format for Issuance -## Example: Issuer-Issued Disclosures Object for the Flat SD-JWT in Example 1 +Besides the SD-JWT itself, the Holder needs to learn the raw claim values that +are contained in the SD-JWT, along with the precise input to the digest +calculation and the salts. To this end, the Issuer sends the Disclosure objects +that were also used for the hash calculation, as described in (#creating_disclosures), +to the Holder. -The II-Disclosures Object for Example 1 is as follows: +The data format for sending the SD-JWT and the Disclosures to the Holder is +as follows: -{#example-simple-iid_payload} -```json -{ - "sd_ii_disclosures": { - "sub": "{\"s\": \"YZSmzeu7lFHUbZ8Z1QqH9Q\", \"v\": - \"6c5c0a49-b589-431d-bae7-219122a9ec2c\"}", - "given_name": "{\"s\": \"kHHp91-tAZt8m9E4Jl4XbQ\", \"v\": - \"John\"}", - "family_name": "{\"s\": \"PjIqpGWl4eB4QroDhqQw0w\", \"v\": - \"Doe\"}", - "email": "{\"s\": \"QRamZSB5Ky0MeJyz4EAleA\", \"v\": - \"johndoe@example.com\"}", - "phone_number": "{\"s\": \"xniP4JZtNWIH-Lk_Dt-o-A\", \"v\": - \"+1-202-555-0101\"}", - "address": "{\"s\": \"KtfsxxTm2mw0YLUcKZU8tA\", \"v\": - {\"street_address\": \"123 Main St\", \"locality\": - \"Anytown\", \"region\": \"Anystate\", \"country\": \"US\"}}", - "birthdate": "{\"s\": \"Ozd4wBLBwqGzJhJvTmQwdQ\", \"v\": - \"1940-01-01\"}" - } -} ``` +~~~...~ +``` +This is called the Combined Format for Issuance. -Important: As described in (#canonicalization), digests are calculated -over the JSON literal formed by serializing an object containing the -salt, the claim value, and optionally the claim name. This ensures that -the Issuer and Verifier use the same input to their digest derivation -algorithms and avoids issues with canonicalization of JSON values that -would lead to different digests. The II-Disclosures Object therefore -maps claim names to JSON-encoded arrays. +The Disclosures and SD-JWT are implicitly linked through the +digest values of the Disclosures included in the SD-JWT. -## Combined Format for Issuance +### Example -For transporting the II-Disclosures Object together with the SD-JWT from -the Issuer to the Holder, the II-Disclosures Object is base64url-encoded -and appended to the SD-JWT using a period character `.` as the -separator. This means that the resulting string consists of four dot-separated parts as follows: +For Example 1, the Combined Format for Issuance looks as follows: + +<{{examples/simple/combined_issuance.txt}} -``` - -. - -. - -. - -``` (Line breaks for presentation only.) -This is called the Combined Format for Issuance. +## Combined Format for Presentation -The II-Disclosures Object and SD-JWT are implicitly linked through the -digest values of the claims in the II-Disclosures Object that is -included in the SD-JWT. To ensure that the correct II-Disclosures Object -and SD-JWT pairings are being used, the Holder SHOULD verify the binding -between II-Disclosures Object and SD-JWT as defined in -(#holder-verification). +For presentation to a Verifier, the Holder sends the SD-JWT and a selected +subset of the Disclosures to the Verifier. -For Example 1, the Combined Format for Issuance looks as follows: +The data format for sending the SD-JWT and the Disclosures to the Verifier is +as follows (line break added for readability): -{#example-simple-combined_sd_jwt_svc} ``` -eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZU -kF6VmRsZnhOMjgwTmdIYUEifQ.eyJpc3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc3N -1ZXIiLCAiY25mIjogeyJqd2siOiB7Imt0eSI6ICJSU0EiLCAibiI6ICJwbTRiT0hCZy1v -WWhBeVBXelI1NkFXWDNyVUlYcDExX0lDRGtHZ1M2VzNaV0x0cy1oendJM3g2NTY1OWtnN -GhWbzlkYkdvQ0pFM1pHRl9lYWV0RTMwVWhCVUVncEd3ckRyUWlKOXpxcHJtY0ZmcjNxdn -ZrR2p0dGg4WmdsMWVNMmJKY093RTdQQ0JIV1RLV1lzMTUyUjdnNkpnMk9WcGgtYThycS1 -xNzlNaEtHNVFvV19tVHoxMFFUXzZINGM3UGpXRzFmamg4aHBXTm5iUF9wdjZkMXpTd1pm -YzVmbDZ5VlJMMERWMFYzbEdIS2UyV3FmX2VOR2pCckJMVmtsRFRrOC1zdFhfTVdMY1ItR -UdtWEFPdjBVQldpdFNfZFhKS0p1LXZYSnl3MTRuSFNHdXhUSUsyaHgxcHR0TWZ0OUNzdn -FpbVhLZURUVTE0cVFMMWVFN2loY3ciLCAiZSI6ICJBUUFCIn19LCAiaWF0IjogMTUxNjI -zOTAyMiwgImV4cCI6IDE1MTYyNDcwMjIsICJzZF9oYXNoX2FsZyI6ICJzaGEtMjU2Iiwg -InNkX2RpZ2VzdHMiOiB7InN1YiI6ICJPTWR3a2sySFB1aUluUHlwV1VXTXhvdDFZMnRTd -EdzTHVJY0RNaktkWE1VIiwgImdpdmVuX25hbWUiOiAiQWZLS0g0YTBJWmtpOE1GRHl0aE -ZhRlNfWHF6bi13UnZBTWZpeV9WallwRSIsICJmYW1pbHlfbmFtZSI6ICJlVW1YbXJ5MzJ -KaUtfNzZ4TWFzYWdrQVFRc21TVmRXNTdBamsxOHJpU0YwIiwgImVtYWlsIjogIi1SY3I0 -ZkR5andsTV9pdGNNeG9RWkNFMVFBRXd5TEpjaWJFcEgxMTRLaUUiLCAicGhvbmVfbnVtY -mVyIjogIkp2Mm53MEMxd1A1QVN1dFlOQXhyV0VuYURSSXBpRjBlVFVBa1VPcDhGNlkiLC -AiYWRkcmVzcyI6ICJacmpLcy1SbUVBVmVBWVN6U3c2R1BGck1wY2djdENmYUo2dDlxUWh -iZko0IiwgImJpcnRoZGF0ZSI6ICJxWFBSUlBkcE5hZWJQOGp0YkVwTy1za0Y0bjd2N0FT -VGg4b0xnMG1rQWRRIn19.QgoJn9wkjFvM9bAr0hTDHLspuqdA21WzfBRVHkASa2ck4PFD -3TC9MiZSi3AiRytRbYT4ZzvkH3BSbm6vy68y62gj0A6OYvZ1Z60Wxho14bxZQveJZgw3u -_lMvYj6GKiUtskypFEHU-Kd-LoDVqEpf6lPQHdpsac__yQ_JL24oCEBlVQRXB-T-6ZNZf -ID6JafSkNNCYQbI8nXbzIEp1LBFm0fE8eUd4G4yPYOj1SeuR6Gy92T0vAoL5QtpIAHo49 -oAmiSIj6DQNl2cNYs74jhrBIcNZyt4l8H1lV20wS5OS3T0vXaYD13fgm0p4iWD9cVg3HK -ShUVulEyrSbq94jIKg.eyJzZF9yZWxlYXNlIjogeyJzdWIiOiAie1wic1wiOiBcIjJHTE -M0MnNLUXZlQ2ZHZnJ5TlJOOXdcIiwgXCJ2XCI6IFwiNmM1YzBhNDktYjU4OS00MzFkLWJ -hZTctMjE5MTIyYTllYzJjXCJ9IiwgImdpdmVuX25hbWUiOiAie1wic1wiOiBcIjZJajd0 -TS1hNWlWUEdib1M1dG12VkFcIiwgXCJ2XCI6IFwiSm9oblwifSIsICJmYW1pbHlfbmFtZ -SI6ICJ7XCJzXCI6IFwiUWdfTzY0enFBeGU0MTJhMTA4aXJvQVwiLCBcInZcIjogXCJEb2 -VcIn0iLCAiZW1haWwiOiAie1wic1wiOiBcIlBjMzNKTTJMY2hjVV9sSGdndl91ZlFcIiw -gXCJ2XCI6IFwiam9obmRvZUBleGFtcGxlLmNvbVwifSIsICJwaG9uZV9udW1iZXIiOiAi -e1wic1wiOiBcImxrbHhGNWpNWWxHVFBVb3ZNTkl2Q0FcIiwgXCJ2XCI6IFwiKzEtMjAyL -TU1NS0wMTAxXCJ9IiwgImFkZHJlc3MiOiAie1wic1wiOiBcIjViUHMxSXF1Wk5hMGhrYU -Z6enpaTndcIiwgXCJ2XCI6IHtcInN0cmVldF9hZGRyZXNzXCI6IFwiMTIzIE1haW4gU3R -cIiwgXCJsb2NhbGl0eVwiOiBcIkFueXRvd25cIiwgXCJyZWdpb25cIjogXCJBbnlzdGF0 -ZVwiLCBcImNvdW50cnlcIjogXCJVU1wifX0iLCAiYmlydGhkYXRlIjogIntcInNcIjogX -CJ5MXNWVTV3ZGZKYWhWZGd3UGdTN1JRXCIsIFwidlwiOiBcIjE5NDAtMDEtMDFcIn0ifX -0 +~~~...~~ ``` +This is called the Combined Format for Presentation. -(Line breaks for presentation only.) - -## Format of a Holder-Selected Disclosures JWT - -The HS-Disclosures JWT contains the Disclosures of the claims the Holder -has consented to disclose to the Verifier. This enables the Verifier to -verify the claims received from the Holder by computing the digests of -the claim values, salts, and potentially cleartext claim names revealed -in the HS-Disclosures JWT using the digest derivation algorithm -specified in SD-JWT and comparing them to the digests included in -SD-JWT. - -The Disclosures are contained in the `sd_hs_disclosures` object. The -structure of the `sd_hs_disclosures` object in the HS-Disclosures JWT is -the same as the structure of the `sd_ii_disclosures` object in the -II-Disclosures Object, but any claims the Holder wishes not to disclose -are omitted. +The Holder MAY send any subset of the Disclosures to the Verifier, i.e., +none, multiple, or all Disclosures. -The HS-Disclosures JWT MAY contain further claims, for example, to ensure a binding -to a concrete transaction (in the example below, the `nonce` and `aud` claims). +A Holder MUST NOT send a Disclosure that was not included in the SD-JWT or send +a Disclosure more than once. -When the Holder sends the HS-Disclosures JWT to the Verifier, the HS-Disclosures JWT MUST be a JWS -represented as the JWS Compact Serialization as described in -Section 7.1 of [@!RFC7515]. +### Enabling Holder Binding -If Holder Binding is desired, the HS-Disclosures JWT is signed by the Holder. If no -Holder Binding is to be used, the `none` algorithm is used, i.e., the document -is not signed. +The Holder MAY add an optional JWT to prove Holder Binding to the Verifier. +The precise contents of the JWT are out of scope of this specification. +Usually, a `nonce` and `aud` claim are included to show that the proof is +intended for the Verifier and to prevent replay attacks. How the `nonce` or +other claims are obtained by the Holder is out of scope of this specification. -Whether to check the signature of the HS-Disclosures JWT is up to the Verifier's policy, -based on the set of trust requirements such as trust frameworks it belongs to. -As described in (#verifier-verification), the Verifier MUST NOT accept HS-Disclosures -JWTs using "none" algorithm, when the Verifier's policy requires a signed -HS-Disclosures JWT. See also (#holder_binding_security). +Example Holder Binding JWT payload: -## Example: Holder-Selected Disclosures JWT for Example 1 +<{{examples/simple/hb_jwt_payload.json}} -The following is a non-normative example of the contents of a HS-Disclosures JWT for Example 1: +Which is then signed by the Holder to create a JWT like the following: -{#example-simple-hsd_jwt_payload} -```json -{ - "nonce": "XZOUco1u_gEPknxS78sWWg", - "aud": "https://example.com/verifier", - "sd_hs_disclosures": { - "given_name": "{\"s\": \"kHHp91-tAZt8m9E4Jl4XbQ\", \"v\": - \"John\"}", - "family_name": "{\"s\": \"PjIqpGWl4eB4QroDhqQw0w\", \"v\": - \"Doe\"}", - "address": "{\"s\": \"KtfsxxTm2mw0YLUcKZU8tA\", \"v\": - {\"street_address\": \"123 Main St\", \"locality\": - \"Anytown\", \"region\": \"Anystate\", \"country\": \"US\"}}" - } -} -``` +<{{examples/simple/hb_jwt_serialized.txt}} -For each disclosed claim, a JSON literal that decodes to an object with the digest and the claim -value (plus optionally the claim name) is contained in the `sd_hs_disclosures` object. +Whether to require Holder Binding is up to the Verifier's policy, +based on the set of trust requirements such as trust frameworks it belongs to. -Again, the HS-Disclosures JWT follows the same structure as the `sd_digests` in the SD-JWT. +Other ways of proving Holder Binding are possible, e.g., when the Combined Format +for Presentation is itself embedded in a signed JWT. -Below is a non-normative example of a representation of the HS-Disclosures JWT using JWS Compact -Serialization: +If no Holder Binding JWT is included, the Combined Format for Presentation ends with +the `~` character after the last Disclosure. -{#example-simple-serialized_sd_jwt_release} -``` -eyJhbGciOiAiUlMyNTYiLCAia2lkIjogIkxkeVRYd0F5ZnJpcjRfVjZORzFSYzEwVThKZ -ExZVHJFQktKaF9oNWlfclUifQ.eyJub25jZSI6ICJYWk9VY28xdV9nRVBrbnhTNzhzV1d -nIiwgImF1ZCI6ICJodHRwczovL2V4YW1wbGUuY29tL3ZlcmlmaWVyIiwgInNkX3JlbGVh -c2UiOiB7ImdpdmVuX25hbWUiOiAie1wic1wiOiBcIjZJajd0TS1hNWlWUEdib1M1dG12V -kFcIiwgXCJ2XCI6IFwiSm9oblwifSIsICJmYW1pbHlfbmFtZSI6ICJ7XCJzXCI6IFwiUW -dfTzY0enFBeGU0MTJhMTA4aXJvQVwiLCBcInZcIjogXCJEb2VcIn0iLCAiYWRkcmVzcyI -6ICJ7XCJzXCI6IFwiNWJQczFJcXVaTmEwaGthRnp6elpOd1wiLCBcInZcIjoge1wic3Ry -ZWV0X2FkZHJlc3NcIjogXCIxMjMgTWFpbiBTdFwiLCBcImxvY2FsaXR5XCI6IFwiQW55d -G93blwiLCBcInJlZ2lvblwiOiBcIkFueXN0YXRlXCIsIFwiY291bnRyeVwiOiBcIlVTXC -J9fSJ9fQ.fw4xRl7m1mDPCZvCTn3GOr2PgBZ--fTKfy7s-GuEifNvzW5KsJaBBFvzdZzt -m25XGhk29uw-XwEw00r0hyxXLBvWfA0XbDK3JBmdpOSW1bEyNBdSHPJoeq9Xyts2JN40v -JzU2UxNaLKDaEheWf3F_E52yhHxvMLNdvZJ9FksJdSMK6ZCyGfRJadPN2GhNltqph52sW -iFKUyUk_4RtwXmT_lF49tWOMZqtG-akN9wrBoMsleM0soA0BXIK10rG5cKZoSNr-u2luz -bdZx3CFdAenaqScIkluPPcrXBZGYyX2zYUbGQs2RRXnBmox_yl6CvLbb0qTTYhDnDEo_M -H-ZtWw -``` +### Example -## Combined Format for Presentation +The following is a non-normative example of the contents of a Presentation for Example 1, disclosing +the claims `given_name`, `family_name`, and `address`, as it would be sent from the Holder to the Verifier. The Holder Binding JWT as shown before is included as the last element. -The SD-JWT and the HS-Disclosures JWT can be combined into one document -using period character `.` as a separator. This means that the resulting string consists of -six dot-separated parts as described below. +<{{examples/simple/combined_presentation.txt}} - The last part (HSD Signature) may be empty when Holder Binding is not used and - HS-Disclosures JWT is not signed. +# Verification and Processing -``` - -. - -. - -. - -. - -. - -``` -(Line breaks for presentation only.) +## Processing by the Holder {#holder_verification} -This is called the Combined Format for Presentation. +The Holder MUST perform the following (or equivalent) steps when receiving +a Combined Format for Issuance: -For Example 1, the Combined Format for Presentation looks as follows: + 1. Separate the SD-JWT and the Disclosures in the Combined Format for Issuance. + 2. Hash all of the Disclosures separately. + 3. Find the places in the SD-JWT where the digests of the Disclosures are + included. If any of the digests cannot be found in the SD-JWT, the + Holder MUST reject the SD-JWT. + 4. Decode Disclosures and obtain plaintext of the claim values. -{#example-simple-combined_sd_jwt_sd_jwt_release} -``` -eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZU -kF6VmRsZnhOMjgwTmdIYUEifQ.eyJpc3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc3N -1ZXIiLCAiY25mIjogeyJqd2siOiB7Imt0eSI6ICJSU0EiLCAibiI6ICJwbTRiT0hCZy1v -WWhBeVBXelI1NkFXWDNyVUlYcDExX0lDRGtHZ1M2VzNaV0x0cy1oendJM3g2NTY1OWtnN -GhWbzlkYkdvQ0pFM1pHRl9lYWV0RTMwVWhCVUVncEd3ckRyUWlKOXpxcHJtY0ZmcjNxdn -ZrR2p0dGg4WmdsMWVNMmJKY093RTdQQ0JIV1RLV1lzMTUyUjdnNkpnMk9WcGgtYThycS1 -xNzlNaEtHNVFvV19tVHoxMFFUXzZINGM3UGpXRzFmamg4aHBXTm5iUF9wdjZkMXpTd1pm -YzVmbDZ5VlJMMERWMFYzbEdIS2UyV3FmX2VOR2pCckJMVmtsRFRrOC1zdFhfTVdMY1ItR -UdtWEFPdjBVQldpdFNfZFhKS0p1LXZYSnl3MTRuSFNHdXhUSUsyaHgxcHR0TWZ0OUNzdn -FpbVhLZURUVTE0cVFMMWVFN2loY3ciLCAiZSI6ICJBUUFCIn19LCAiaWF0IjogMTUxNjI -zOTAyMiwgImV4cCI6IDE1MTYyNDcwMjIsICJzZF9oYXNoX2FsZyI6ICJzaGEtMjU2Iiwg -InNkX2RpZ2VzdHMiOiB7InN1YiI6ICJPTWR3a2sySFB1aUluUHlwV1VXTXhvdDFZMnRTd -EdzTHVJY0RNaktkWE1VIiwgImdpdmVuX25hbWUiOiAiQWZLS0g0YTBJWmtpOE1GRHl0aE -ZhRlNfWHF6bi13UnZBTWZpeV9WallwRSIsICJmYW1pbHlfbmFtZSI6ICJlVW1YbXJ5MzJ -KaUtfNzZ4TWFzYWdrQVFRc21TVmRXNTdBamsxOHJpU0YwIiwgImVtYWlsIjogIi1SY3I0 -ZkR5andsTV9pdGNNeG9RWkNFMVFBRXd5TEpjaWJFcEgxMTRLaUUiLCAicGhvbmVfbnVtY -mVyIjogIkp2Mm53MEMxd1A1QVN1dFlOQXhyV0VuYURSSXBpRjBlVFVBa1VPcDhGNlkiLC -AiYWRkcmVzcyI6ICJacmpLcy1SbUVBVmVBWVN6U3c2R1BGck1wY2djdENmYUo2dDlxUWh -iZko0IiwgImJpcnRoZGF0ZSI6ICJxWFBSUlBkcE5hZWJQOGp0YkVwTy1za0Y0bjd2N0FT -VGg4b0xnMG1rQWRRIn19.QgoJn9wkjFvM9bAr0hTDHLspuqdA21WzfBRVHkASa2ck4PFD -3TC9MiZSi3AiRytRbYT4ZzvkH3BSbm6vy68y62gj0A6OYvZ1Z60Wxho14bxZQveJZgw3u -_lMvYj6GKiUtskypFEHU-Kd-LoDVqEpf6lPQHdpsac__yQ_JL24oCEBlVQRXB-T-6ZNZf -ID6JafSkNNCYQbI8nXbzIEp1LBFm0fE8eUd4G4yPYOj1SeuR6Gy92T0vAoL5QtpIAHo49 -oAmiSIj6DQNl2cNYs74jhrBIcNZyt4l8H1lV20wS5OS3T0vXaYD13fgm0p4iWD9cVg3HK -ShUVulEyrSbq94jIKg.eyJhbGciOiAiUlMyNTYiLCAia2lkIjogIkxkeVRYd0F5ZnJpcj -RfVjZORzFSYzEwVThKZExZVHJFQktKaF9oNWlfclUifQ.eyJub25jZSI6ICJYWk9VY28x -dV9nRVBrbnhTNzhzV1dnIiwgImF1ZCI6ICJodHRwczovL2V4YW1wbGUuY29tL3Zlcmlma -WVyIiwgInNkX3JlbGVhc2UiOiB7ImdpdmVuX25hbWUiOiAie1wic1wiOiBcIjZJajd0TS -1hNWlWUEdib1M1dG12VkFcIiwgXCJ2XCI6IFwiSm9oblwifSIsICJmYW1pbHlfbmFtZSI -6ICJ7XCJzXCI6IFwiUWdfTzY0enFBeGU0MTJhMTA4aXJvQVwiLCBcInZcIjogXCJEb2Vc -In0iLCAiYWRkcmVzcyI6ICJ7XCJzXCI6IFwiNWJQczFJcXVaTmEwaGthRnp6elpOd1wiL -CBcInZcIjoge1wic3RyZWV0X2FkZHJlc3NcIjogXCIxMjMgTWFpbiBTdFwiLCBcImxvY2 -FsaXR5XCI6IFwiQW55dG93blwiLCBcInJlZ2lvblwiOiBcIkFueXN0YXRlXCIsIFwiY29 -1bnRyeVwiOiBcIlVTXCJ9fSJ9fQ.fw4xRl7m1mDPCZvCTn3GOr2PgBZ--fTKfy7s-GuEi -fNvzW5KsJaBBFvzdZztm25XGhk29uw-XwEw00r0hyxXLBvWfA0XbDK3JBmdpOSW1bEyNB -dSHPJoeq9Xyts2JN40vJzU2UxNaLKDaEheWf3F_E52yhHxvMLNdvZJ9FksJdSMK6ZCyGf -RJadPN2GhNltqph52sWiFKUyUk_4RtwXmT_lF49tWOMZqtG-akN9wrBoMsleM0soA0BXI -K10rG5cKZoSNr-u2luzbdZx3CFdAenaqScIkluPPcrXBZGYyX2zYUbGQs2RRXnBmox_yl -6CvLbb0qTTYhDnDEo_MH-ZtWw -``` +It is up to the Holder how to maintain the mapping between the Disclosures and the plaintext claim values to be able to display them to the End-User when needed. -# Verification and Processing +For presentation to a Verifier, the Holder MUST perform the following (or equivalent) steps: -## Verification by the Holder when Receiving SD-JWT and Issuer-Issued Disclosures Object {#holder-verification} + 1. Decide which Disclosures to release to the Verifier, obtaining proper End-User consent if necessary. + 2. If Holder Binding is required, create a Holder Binding JWT. + 3. Create the Combined Format for Presentation, including the selected Disclosures and, if applicable, the Holder Binding JWT. + 4. Send the Presentation to the Verifier. -The Holder SHOULD verify the binding between SD-JWT and II-Disclosures Object by performing the following steps: - 1. Check that all the claims in the II-Disclosures Object are present in the SD-JWT and that there are no claims in the SD-JWT that are not in the II-Disclosures Object - 2. Check that the digests of the claims in the II-Disclosures Object match those in the SD-JWT +## Verification by the Verifier {#verifier_verification} -## Verification by the Verifier when Receiving SD-JWT and Holder-Selected Disclosures JWT {#verifier-verification} +Upon receiving a Presentation, Verifiers MUST ensure that -Verifiers MUST follow [@RFC8725] for checking the SD-JWT and, if signed, the -HS-Disclosures JWT. + * the SD-JWT is valid, i.e., it is signed by the Issuer and the signature is valid, + * all Disclosures are correct, i.e., their digests are referenced in the SD-JWT, and + * if Holder Binding is required, the Holder Binding JWT is signed by the Holder and valid. -Verifiers MUST go through (at least) the following steps before -trusting/using any of the contents of an SD-JWT: +To this end, Verifiers MUST follow the following steps (or equivalent): 1. Determine if Holder Binding is to be checked according to the Verifier's policy for the use case at hand. This decision MUST NOT be based on whether - the HS-Disclosures - JWT is signed or not. Refer to (#holder_binding_security) for + a Holder Binding JWT is provided by the Holder or not. Refer to (#holder_binding_security) for details. - 2. Check that the presentation consists of six period-separated (`.`) elements; if Holder Binding is not required, the last element can be empty. - 3. Separate the SD-JWT from the HS-Disclosures JWT. - 4. Validate the SD-JWT: - 1. Ensure that a signing algorithm was used that was deemed secure for the application. Refer to [@RFC8725], Sections 3.1 and 3.2 for details. `none` MUST NOT be accepted. + 2. Separate the Presentation into the SD-JWT, the Disclosures (if any), and the Holder Binding JWT (if provided). + 3. Validate the SD-JWT: + 1. Ensure that a signing algorithm was used that was deemed secure for the application. Refer to [@RFC8725], Sections 3.1 and 3.2 for details. The `none` algorithm MUST NOT be accepted. 2. Validate the signature over the SD-JWT. 3. Validate the Issuer of the SD-JWT and that the signing key belongs to this Issuer. - 4. Check that the SD-JWT is valid using `nbf`, `iat`, and `exp` claims, if provided in the SD-JWT. - 5. Check that the claim `sd_digests` is present in the SD-JWT. - 6. Check that the `sd_digest_derivation_alg` claim is present and its value is understood and the digest derivation algorithm is deemed secure. - 5. Validate the HS-Disclosures JWT: - 1. If Holder Binding is required, validate the signature over the HS-Disclosures JWT using the same steps as for the SD-JWT plus the following steps: - 1. Determine that the public key for the private key that used to sign the HS-Disclosures JWT is bound to the SD-JWT, i.e., the SD-JWT either contains a reference to the public key or contains the public key itself. - 2. Determine that the HS-Disclosures JWT is bound to the current transaction and was created for this Verifier (replay protection). This is usually achieved by a `nonce` and `aud` field within the HS-Disclosures JWT. - 2. For each claim in `sd_hs_disclosures` in the HS-Disclosures JWT: - 3. Ensure that the claim is present as well in `sd_digests` in the SD-JWT. - If `sd_digests` is structured, the claim MUST be present at the same - place within the structure. - 4. Compute the base64url-encoded digest of the JSON literal disclosed - by the Holder using the `sd_digest_derivation_alg` in SD-JWT. - 5. Compare the digests computed in the previous step with the one of - the same claim in the SD-JWT. Accept the claim only when the two - digests match. - 6. Ensure that the claim value in the HS-Disclosures JWT is a JSON-encoded - object containing at least the keys `s` and `v`, and optionally `n`. - 7. Store the value of the key `v` as the claim value. If `n` is contained - in the object, use the value of the key `n` as the claim name. - 3. Once all necessary claims have been verified, their values can be - validated and used according to the requirements of the application. It - MUST be ensured that all claims required for the application have been - disclosed. - -If any step fails, the input is not valid and processing MUST be aborted. - -## Processing Model {#processing_model} - -Neither an SD-JWT nor an HS-Disclosures JWT is suitable for direct use by an application. -Besides the REQUIRED verification steps listed above, it is further RECOMMENDED -that an application-consumable format is generated from the data released in -the HS-Disclosures. The RECOMMENDED way is to merge the released claims and any -plaintext claims in the SD-JWT recursively: - - * Objects from the released claims must be merged into existing objects from the SD-JWT. - * If a key is present in both objects: - * If the value in the released claims is an object and the value in the - SD-JWT claims is an object, the two objects MUST be merged recursively. - * Else, the value in the released claims MUST be used. - -The keys `sd_digests` and `sd_digest_derivation_alg` SHOULD be removed prior to further -processing. - -The processing is shown in Examples 2b and 3 in the Appendix. + 4. Check that the SD-JWT is valid using `nbf`, `iat`, and `exp` claims, if provided in the SD-JWT, and not selectively disclosed. + 5. Check that the `sd_digest_derivation_alg` claim is present and its value is understood and the digest derivation algorithm is deemed secure. + 4. Create a copy of the SD-JWT payload, if required for further processing. + 5. Process the Disclosures. For each Disclosure provided: + 1. Calculate the hash digest over the base64url string as described in (#hashing_disclosures). + 2. Find all `_sd` keys in the SD-JWT payload that contain a hash digest calculated in the previous step. Note that there might be more than one `_sd` arrays in on SD-JWT. + 1. If the digest cannot be found in the SD-JWT payload, the Verifier MUST reject the Presentation. + 2. If there is more than one place where the digest is included, the Verifier MUST reject the Presentation. + 3. If there is a key `_sd` that does not refer to an array, the Verifier MUST reject the Presentation. + 4. Otherwise, insert, at the level of the `_sd` claim, the claim described by the Disclosure with the claim name and claim value provided in the Disclosure. + 1. If the Disclosure is not a JSON-encoded array of three elements, the Verifier MUST reject the Presentation. + 2. If the claim name already exists at the same level, the Verifier MUST reject the Presentation. Note that this also means that if a Holder sends the same Disclosure multiple times, the Verifier MUST reject the Presentation. + 3. If the claim value contains an object with an `_sd` key (at the top level or nested deeper), the Verifier MUST reject the Presentation. + 3. Remove all `_sd` claims from the SD-JWT payload. + 4. Remove the claim `sd_digest_derivation_alg` from the SD-JWT payload. + 6. If Holder Binding is required: + 1. If Holder Binding is provided by means not defined in this specification, verify the Holder Binding according to the method used. + 2. Otherwise, verify the Holder Binding JWT as follows: + 1. If Holder Binding JWT is not provided, the Verifier MUST reject the Presentation. + 2. Determine the public key for the Holder from the SD-JWT. + 3. Ensure that a signing algorithm was used that was deemed secure for the application. Refer to [@RFC8725], Sections 3.1 and 3.2 for details. The `none` algorithm MUST NOT be accepted. + 4. Validate the signature over the Holder Binding JWT. + 5. Check that the Holder Binding JWT is valid using `nbf`, `iat`, and `exp` claims, if provided in the Holder Binding JWT. + 6. Determine that the Holder Binding JWT is bound to the current transaction and was created for this Verifier (replay protection). This is usually achieved by a `nonce` and `aud` field within the Holder Binding JWT. + +If any step fails, the Presentation is not valid and processing MUST be aborted. + +Otherwise, the processed SD-JWT payload can be passed to the application to be used for the intended purpose. # Security Considerations {#security_considerations} @@ -1002,7 +732,23 @@ The Verifier MUST always check the SD-JWT signature to ensure that the SD-JWT has not been tampered with since its issuance. If the signature on the SD-JWT cannot be verified, the SD-JWT MUST be rejected. -## Entropy of the salt +## Manipulation of Disclosures + +Holders can manipulate the Disclosures by changing the values of the claims +before sending them to the Issuer. The Issuer MUST check the Disclosures to +ensure that the values of the claims are correct, i.e., the hash digests of the Disclosures are actually present in the signed SD-JWT. + +A naive Issuer that extracts +all claim values from the Disclosures (without checking the hashes) and inserts them into the SD-JWT payload +is vulnerable to this attack. However, in a structured SD-JWT, without comparing the digests of the +Disclosures, such an implementation could not determine the correct place in a +nested object where a claim needs to be inserted. Therefore, the naive implementation +would not only be insecure, but also incorrect. + +The steps described in (#verifier_verification) ensure that the Verifier +checks the Disclosures correctly. + +## Entropy of the salt {#salt-entropy} The security model relies on the fact that the salt is not learned or guessed by the attacker. It is vitally important to adhere to this principle. As such, the @@ -1010,7 +756,7 @@ salt MUST be created in such a manner that it is cryptographically random, long enough and has high entropy that it is not practical for the attacker to guess. A new salt MUST be chosen for each claim. -## Minimum length of the salt {#salt-minlength} +## Minimum length of the salt {#salt_minlength} The RECOMMENDED minimum length of the randomly-generated portion of the salt is 128 bits. @@ -1048,7 +794,7 @@ This can be showcased based on two scenarios for a mobile driver's license use c stopped by a police officer for exceeding a speed limit, Holder Binding may be necessary to ensure that the person driving the car and presenting the license is the actual Holder of the license. The Verifier (e.g., the software used by the police officer) -will ensure that the HS-Disclosures JWT is signed by the Holder's private +will ensure that a Holder Binding JWT is present and signed with the Holder's private key. **Scenario B:** A rental car agency may want to ensure, for insurance @@ -1064,117 +810,36 @@ decisions based on data that can be influenced by an attacker or that can be misinterpreted. For this reason, when deciding whether Holder binding is required or not, Verifiers MUST NOT take into account - * whether an HS-Disclosure JWT is signed or not, as an attacker can - remove the signature from any HS-Disclosure JWT and present it to the + * whether an Holder Binding JWT is present or not, as an attacker can + remove the Holder Binding JWT from any Presentation and present it to the Verifier, or * whether a key reference is present in the SD-JWT or not, as the Issuer might have added the key to the SD-JWT in a format/claim that is not recognized by the Verifier. If a Verifier has decided that Holder Binding is required for a -particular use case and the HS-Disclosure is unsigned or no recognized +particular use case and the Holder Binding is not present, does not fulfill the requirements +(e.g., on the signing algorithm), or no recognized key reference is present in the SD-JWT, the Verifier will reject the -presentation, as described in (#verifier-verification). +presentation, as described in (#verifier_verification). ## Blinding Claim Names {#blinding-claim-names} -Issuers that chose to blind claim names MUST ensure not to inadvertently leak -information about the blinded claim names to Verifiers. - -It is RECOMMENDED to use cryptographically random numbers with at least 128 bits -of entropy as placeholder claim names. - -The order of elements in JSON-encoded objects is generally not relevant -to applications, but it may reveal information about a blinded claim -name to the verifier. For example, assume the following two clear-text -claim sets created by the same Issuer: - -(A) -``` -{ - "given_name": "Doe", - "secret_club_membership_no": 42 -} -``` - -(B) -``` -{ - "is_secret_agent": true, - "given_name": "Doe" -} -``` - -When naively blinding the claim names, the order of the elements might -be preserved in the SD-JWT (depending on implementation details of the -programming language): - - -(A) -``` -{ - "given_name": "Doe", - "3DOgmo7w7MDZNh1Zjvmwpg": - "OXZKGG7Ltar4vz_L7sAtWIkVXVf5r9xONFKZdyoNlco" -} -``` - -(B) -``` -{ - "CwiB46IUgi4NydIfgGTRwg": - "4miZg7O_JaidVJyjGiPpc4FXAMN16e1SBZfOMlYg3hQ", - "given_name": "Doe" -} -``` - -A verifier, even if it does not learn any blinded claim names, can -distinguish what claim name has been hidden just by observing the order -of blinded and unblinded claim names. It is therefore RECOMMENDED, if at -least one claim name is blinded, to either - - * randomize the order of all claims (blinded/unblinded, selectively disclosed/not-selectively disclosed), - * or sort the claims by the property name (i.e., the placeholder claim - name for blinded claim names and the plaintext claim name for - unblinded claim names). The precise order does not matter. For - example, ordering by unicode code points or by lexicographic order is - sufficient to hide the original order of claims. - -This applies to Issuers (SD-JWT and II-Disclosures document) and -Holders (HS-Disclosures JWT). - -With the approach chosen in this specification, claim names of objects -that are not themselves selectively disclosable are not blinded. This -can be seen in Example 6 in the Appendix, where even in the blinded -SD-JWT, `address` and `delivery_address` are visible. This limitation +SD-JWT ensures that names of claims that are selectively disclosable are +always blinded. This prevents an attacker from learning the names of the +disclosable claims. However, the names of the claims that are not +disclosable are not blinded. This includes the keys of objects that themselves +are not blinded, but contain disclosable claims. This limitation needs to be taken into account by Issuers when creating the structure of the SD-JWT. -The Issuer MUST ensure that a new random placeholder name is chosen for -each claim, including when the same claim name occurs at different -places in the structure of the SD-JWT. This can be seen in Example 6 in -the Appendix, where multiple claims with same name appear below -`address` and `delivery_address`, but each of them has a different -blinded claim name. For each credential issued, new random placeholder names -MUST be chosen by the Issuer. - # Privacy Considerations {#privacy_considerations} -## Claim Names - -By default, claim names are not blinded in an SD-JWT. In this case, even when -the claim's value is not known to a Verifier, the claim name can disclose some -information to the Verifier. For example, if the SD-JWT contains a claim named -`super_secret_club_membership_no`, the Verifier might assume that the end-user -is a member of the Super Secret Club. +## Decoy Digests {#decoy_digests_privacy} -Blinding claim names can help to avoid this potential privacy issue. In many -cases, however, Verifiers can already deduce this or similar information just -from the identification of the Issuer and the schema used for the SD-JWT. -Blinding claim names might not provide additional privacy if this is the case. - -Furthermore, re-using the same value to blind a claim name may limit the privacy benefits. +The use of decoy digests is RECOMMENDED when the number of claims (or the existence of particular claims) can be a side-channel disclosing information about otherwise undisclosed claims. In particular, if a claim in an SD-JWT is present only if a certain condition is met (e.g., a membership number is only contained if the End-User is a member of a group), the Issuer SHOULD add decoy digests when the condition is not met. +Decoy digests increase the size of the SD-JWT. The number of decoy digests (or whether to use them at all) is a trade-off between the size of the SD-JWT and the privacy of the End-User's data. ## Unlinkability @@ -1195,11 +860,13 @@ Arjan Geluk, Brian Campbell, Christian Paquin, David Bakker, +David Waite, Fabian Hauck, Giuseppe De Marco, Kushal Das, Mike Jones, Nat Sakimura, +Orie Steele, Pieter Kasselman, Ryosuke Abe, Shawn Butterfield, and @@ -1305,490 +972,63 @@ TBD All of the following examples are non-normative. -## Structured SD-JWT +## Example 2a: Handling Structured Claims {#example-simple_structured} -The following examples show variations of a structured SD-JWT. +This example uses the following object as the set of claims that the Issuer is issuing: -### Example 2a - Simple Structured SD-JWT {#example-simple-structured-sd-jwt} -This non-normative example is based on the same claim values as Example 1, but -here the Issuer decided to create a structured object for the digests. This -allows for the disclosure of individual members of the `address` claim separately. +<{{examples/simple_structured/user_claims.json}} -{#example-simple_structured-sd_jwt_payload} -```json -{ - "iss": "https://example.com/issuer", - "cnf": { - "jwk": { - "kty": "RSA", - "n": "pm4bOHBg-oYhAyPWzR56AWX3rUIXp11_ICDkGgS6W3ZWLts-hzwI3x65 - 659kg4hVo9dbGoCJE3ZGF_eaetE30UhBUEgpGwrDrQiJ9zqprmcFfr3qvvkG - jtth8Zgl1eM2bJcOwE7PCBHWTKWYs152R7g6Jg2OVph-a8rq-q79MhKG5QoW - _mTz10QT_6H4c7PjWG1fjh8hpWNnbP_pv6d1zSwZfc5fl6yVRL0DV0V3lGHK - e2Wqf_eNGjBrBLVklDTk8-stX_MWLcR-EGmXAOv0UBWitS_dXJKJu-vXJyw1 - 4nHSGuxTIK2hx1pttMft9CsvqimXKeDTU14qQL1eE7ihcw", - "e": "AQAB" - } - }, - "iat": 1516239022, - "exp": 1516247022, - "sd_digest_derivation_alg": "sha-256", - "sd_digests": { - "sub": "p7GDm8_lnxCJUsQojBatCJQgPCZOVBGxU-eX_lUIcC4", - "given_name": "BrmUer7nGIRyk3sbHHcZk43M9Oy_BQar0VE3NMOGk9w", - "family_name": "8voOnlh20GGzTInd6T9-Vcu2l6Q4_Kc-keedo7_3VY8", - "email": "b9DpmK8_xwhR4PX_MiIsQc1TyB_1NN40lI5Kj8SSNl4", - "phone_number": "0LFRbHdtG1eze9ET1rDEtSIrPI0poCM3J0EYBt2iwVg", - "address": { - "street_address": - "qYDFWJxdl_OQDdn_lxX1-E9r5H2juwqonoWM8A76X_w", - "locality": "3mLauig0JJyjJbdMvf3jLJGSBAIt0tdvq7F_VL1gqXw", - "region": "qRa_XKvVxCzUK8buAsxg9ylzyQlfvUgSwqATQV74z6c", - "country": "DjbYtjTT3PAQHtVkcpvrnRboYVUfXMro6Y4oEGdHW_0" - }, - "birthdate": "rXv8RpBXYOy9WtYf2Bg-KIdO0a3KnYGCAhL53iCsLJA" - } -} -``` +Note that in contrast to Example 1, here the Issuer decided to create a structured object for the `address` claim, allowing for separate disclosure of the individual members of the claim. -The II-Disclosures Object for this SD-JWT is as follows: +<{{examples/simple_structured/sd_jwt_payload.json}} -{#example-simple_structured-iid_payload} -```json -{ - "sd_ii_disclosures": { - "sub": "{\"s\": \"2iFrkb5skOft_gSL6BhdBg\", \"v\": - \"6c5c0a49-b589-431d-bae7-219122a9ec2c\"}", - "given_name": "{\"s\": \"AbA1MKJ1Oyqtff2JoFKNXA\", \"v\": - \"John\"}", - "family_name": "{\"s\": \"vGk9hg40yrI1qazJn8qaKw\", \"v\": - \"Doe\"}", - "email": "{\"s\": \"6Ilb1QXTN4Qdv-1qGcQdbw\", \"v\": - \"johndoe@example.com\"}", - "phone_number": "{\"s\": \"-F5a6ZAOKHwUsYPDS383pQ\", \"v\": - \"+1-202-555-0101\"}", - "address": { - "street_address": "{\"s\": \"t6GqrdbiTFbJYh4D38aLjA\", \"v\": - \"123 Main St\"}", - "locality": "{\"s\": \"B0G5ap7hsAPIYOJ21rUjgg\", \"v\": - \"Anytown\"}", - "region": "{\"s\": \"YTPF0rUHYtvldv1Df63WXQ\", \"v\": - \"Anystate\"}", - "country": "{\"s\": \"mVZ4hCTnVdpu_GN-Rb9wNw\", \"v\": - \"US\"}" - }, - "birthdate": "{\"s\": \"T6-5A3xYsyy2MnwnUWbW3w\", \"v\": - \"1940-01-01\"}" - } -} -``` +The Disclosures for this SD-JWT are as follows: -An HS-Disclosures JWT for the SD-JWT above that discloses only `region` -and `country` of the `address` property could look as follows: +{{examples/simple_structured/disclosures.md}} -{#example-simple_structured-hsd_jwt_payload} -```json -{ - "nonce": "XZOUco1u_gEPknxS78sWWg", - "aud": "https://example.com/verifier", - "sd_hs_disclosures": { - "given_name": "{\"s\": \"AbA1MKJ1Oyqtff2JoFKNXA\", \"v\": - \"John\"}", - "family_name": "{\"s\": \"vGk9hg40yrI1qazJn8qaKw\", \"v\": - \"Doe\"}", - "birthdate": "{\"s\": \"T6-5A3xYsyy2MnwnUWbW3w\", \"v\": - \"1940-01-01\"}", - "address": { - "region": "{\"s\": \"YTPF0rUHYtvldv1Df63WXQ\", \"v\": - \"Anystate\"}", - "country": "{\"s\": \"mVZ4hCTnVdpu_GN-Rb9wNw\", \"v\": - \"US\"}" - } - } -} -``` +A Presentation for the SD-JWT that discloses only `region` +and `country` of the `address` property and without a Holder Binding JWT could look as follows: -### Example 2b - Mixing SD and Non-SD Claims in a Structured SD-JWT {#example-mixed-structured-sd-jwt} +<{{examples/simple_structured/combined_presentation.txt}} -In this example, a variant of Example 2a, the Issuer decided to apply selective -disclosure only to some of the claims. In particular, the `country` component of -the `address` is contained in the JWT as a regular claim, whereas the rest of -the claims can be disclosed selectively. Note that the processing model -described in (#processing_model) allows for merging the selectively disclosable -claims with the regular claims. +## Example 2b: Adding Decoys {#example-simple_structured_with_decoys} -The JSON-payload of the SD-JWT that contains both selectively -disclosable claims in the `sd_digests` object and not-selectively -disclosable claims in a top-level JWT claim would look as follows: +This example is based on the same set of user data as Example 2a, but +here, the Issuer decided to add decoy digests to the SD-JWT. -{#example-simple_structured_merging-sd_jwt_payload} -```json -{ - "iss": "https://example.com/issuer", - "cnf": { - "jwk": { - "kty": "RSA", - "n": "pm4bOHBg-oYhAyPWzR56AWX3rUIXp11_ICDkGgS6W3ZWLts-hzwI3x65 - 659kg4hVo9dbGoCJE3ZGF_eaetE30UhBUEgpGwrDrQiJ9zqprmcFfr3qvvkG - jtth8Zgl1eM2bJcOwE7PCBHWTKWYs152R7g6Jg2OVph-a8rq-q79MhKG5QoW - _mTz10QT_6H4c7PjWG1fjh8hpWNnbP_pv6d1zSwZfc5fl6yVRL0DV0V3lGHK - e2Wqf_eNGjBrBLVklDTk8-stX_MWLcR-EGmXAOv0UBWitS_dXJKJu-vXJyw1 - 4nHSGuxTIK2hx1pttMft9CsvqimXKeDTU14qQL1eE7ihcw", - "e": "AQAB" - } - }, - "iat": 1516239022, - "exp": 1516247022, - "sd_digest_derivation_alg": "sha-256", - "sd_digests": { - "sub": "m6f849XozrOu1dDvaoGfzp_FwJ0Jpcm8LBt8BeZdxkc", - "given_name": "CEBrXkrUZcZ3njZE46q_CEdSASdcEP0qoGrjNcPJx8g", - "family_name": "j5ZcRWCSTbdtevKIp8L1XMunNHXZHOEDLtkJ3By4rms", - "email": "AXm5JzGxUAfQaqTAz5hZGrhL7ZEM_J3ljKRK4wSpRvU", - "phone_number": "Vkehj3w1-X9Ssz96tWl8lvap8EaIy9pi9q4qWzWAWNo", - "address": { - "street_address": - "MVldFr-b-NKmQSLyHbnnq9ciMFGcb4GuhLtKLtmmnwk", - "locality": "aAusTIjJS8e9QwaGs53OaHqngMZ142uDScfW41hqFm0", - "region": "o6d8Kv-xOL3fidw5t0QF1StAlw5YLSN3Rco1aHsiWn8" - }, - "birthdate": "_l2Sr5D08premyjfkmrnxMV6aFnEH8qMXme0BFGFGqk" - }, - "address": { - "country": "US" - } -} -``` +The SD-JWT payload is as follows: -The Holder can now, for example, release the rest of the components of the `address` claim in the HS-Disclosures: +<{{examples/simple_structured_with_decoys/sd_jwt_payload.json}} +Since the Disclosures or Presentation are not affected by the decoy digests (other than a slightly larger SD-JWT), they are omitted here. -{#example-simple_structured_merging-hsd_jwt_payload} -```json -{ - "nonce": "XZOUco1u_gEPknxS78sWWg", - "aud": "https://example.com/verifier", - "sd_hs_disclosures": { - "given_name": "{\"s\": \"juf0vRMI_5aHaGZQfl5o5A\", \"v\": - \"John\"}", - "family_name": "{\"s\": \"mJXFsX6E6IvqR6vMd_un5A\", \"v\": - \"Doe\"}", - "birthdate": "{\"s\": \"vnc31gtRYVh_zW8RrqSbaw\", \"v\": - \"1940-01-01\"}", - "address": { - "region": "{\"s\": \"4mt7paa9SIEuEgWIm-10kg\", \"v\": - \"Anystate\"}", - "street_address": "{\"s\": \"4r1Y7ivPIQzkp8rKF_BUTQ\", \"v\": - \"123 Main St\"}", - "locality": "{\"s\": \"v5I5nfxYin0IB2mWP1oj6Q\", \"v\": - \"Anytown\"}" - } - } -} -``` -The Verifier, after verifying the SD-JWT and applying the HS-Disclosures, would -process the result according to (#processing_model) and pass the following data -to the application: +## Example 3 - Complex Structured SD-JWT {#example-complex-structured-sd-jwt} +In this example, an SD-JWT with a complex object is demonstrated. Here, the data +structures defined in OIDC4IDA [@OIDC.IDA] are used. -{#example-simple_structured_merging-merged} -```json -{ - "given_name": "John", - "family_name": "Doe", - "birthdate": "1940-01-01", - "address": { - "region": "Anystate", - "street_address": "123 Main St", - "locality": "Anytown", - "country": "US" - }, - "iss": "https://example.com/issuer", - "cnf": { - "jwk": { - "kty": "RSA", - "n": "pm4bOHBg-oYhAyPWzR56AWX3rUIXp11_ICDkGgS6W3ZWLts-hzwI3x65 - 659kg4hVo9dbGoCJE3ZGF_eaetE30UhBUEgpGwrDrQiJ9zqprmcFfr3qvvkG - jtth8Zgl1eM2bJcOwE7PCBHWTKWYs152R7g6Jg2OVph-a8rq-q79MhKG5QoW - _mTz10QT_6H4c7PjWG1fjh8hpWNnbP_pv6d1zSwZfc5fl6yVRL0DV0V3lGHK - e2Wqf_eNGjBrBLVklDTk8-stX_MWLcR-EGmXAOv0UBWitS_dXJKJu-vXJyw1 - 4nHSGuxTIK2hx1pttMft9CsvqimXKeDTU14qQL1eE7ihcw", - "e": "AQAB" - } - }, - "iat": 1516239022, - "exp": 1516247022 -} -``` - +The Issuer is using the following user data: -### Example 3 - Complex Structured SD-JWT {#example-complex-structured-sd-jwt} +<{{examples/complex-ekyc/user_claims.json}} -In this example, a complex object such as those defined in OIDC4IDA -[@OIDC.IDA] is used. Here, the Issuer is using the following user data: +The Issuer in this example sends the two claims `birthdate` and `place_of_birth` in the `claims` element in plain text. The following shows the resulting SD-JWT payload: -{#example-complex-user_claims} -```json -{ - "verified_claims": { - "verification": { - "trust_framework": "de_aml", - "time": "2012-04-23T18:25Z", - "verification_process": "f24c6f-6d3f-4ec5-973e-b0d8506f3bc7", - "evidence": [ - { - "type": "document", - "method": "pipp", - "time": "2012-04-22T11:30Z", - "document": { - "type": "idcard", - "issuer": { - "name": "Stadt Augsburg", - "country": "DE" - }, - "number": "53554554", - "date_of_issuance": "2010-03-23", - "date_of_expiry": "2020-03-22" - } - } - ] - }, - "claims": { - "given_name": "Max", - "family_name": "Meier", - "nationalities": [ - "DE" - ], - "address": { - "locality": "Maxstadt", - "postal_code": "12344", - "country": "DE", - "street_address": "An der Weide 22" - } - } - }, - "birth_middle_name": "Timotheus", - "salutation": "Dr.", - "msisdn": "49123456789" -} -``` - -The Issuer in this example further adds the two claims `birthdate` and `place_of_birth` to the `claims` element in plain text. The following shows the resulting SD-JWT payload: - -{#example-complex-sd_jwt_payload} -```json -{ - "iss": "https://example.com/issuer", - "cnf": { - "jwk": { - "kty": "RSA", - "n": "pm4bOHBg-oYhAyPWzR56AWX3rUIXp11_ICDkGgS6W3ZWLts-hzwI3x65 - 659kg4hVo9dbGoCJE3ZGF_eaetE30UhBUEgpGwrDrQiJ9zqprmcFfr3qvvkG - jtth8Zgl1eM2bJcOwE7PCBHWTKWYs152R7g6Jg2OVph-a8rq-q79MhKG5QoW - _mTz10QT_6H4c7PjWG1fjh8hpWNnbP_pv6d1zSwZfc5fl6yVRL0DV0V3lGHK - e2Wqf_eNGjBrBLVklDTk8-stX_MWLcR-EGmXAOv0UBWitS_dXJKJu-vXJyw1 - 4nHSGuxTIK2hx1pttMft9CsvqimXKeDTU14qQL1eE7ihcw", - "e": "AQAB" - } - }, - "iat": 1516239022, - "exp": 1516247022, - "sd_digest_derivation_alg": "sha-256", - "sd_digests": { - "verified_claims": { - "verification": { - "trust_framework": - "fkIW-4iUZgTeIeDg_Z_6oFHU-wyWwazSpuaiQbc5QKw", - "time": "VRF-G_LfTzSaYkLelVzry82l1zQxGwk1RfGcnUUWukc", - "verification_process": - "9OpDml4eRBM6Usfk3MF2i7kBl1xGGkzPq5Ncs1mvbPo", - "evidence": [ - { - "type": "HucanHhQwb-TJNg_rVpaonNSDtzPrCEebb3LfXTuLSM", - "method": "aU7IO7ooT8vArMkqpOfkIAlKw8BNcfRyw3NXs3ZS128", - "time": "LHcH98bV3-ZNUa00HNnqOf8W5IdijY1aEnpVzDNVBwA", - "document": { - "type": "3ITIlfkbUI0NveviEJBw-_VEaGiPtCDcXy9uD9orWFA", - "issuer": { - "name": - "AY7wW63Vbcd7RnKDb39sSXpLgyiVNxWgoRnV6xZD5C8", - "country": - "Kd3aUmm6XHjpWp6OYiJeEZUrD5J7nIRU3SlTc-E53gs" - }, - "number": - "8gKpksl66fN9F2Zxs1PRPgD8kHi8dGC2JzpqtrPZavs", - "date_of_issuance": - "GfIEhOGWwe8J7lx6HSAPpC-Qvx0ihwWkEE0_LZ-r_DI", - "date_of_expiry": - "_fdljKRdp5wptGi7DwKNZEsSX6AnniVqmDE0aSznH74" - } - } - ] - }, - "claims": { - "given_name": "sx4wGd6-ONAsiq7dN16GHeg4RAyOshRBdoXWE_E751w", - "family_name": - "Ldbea0SibAQDiZJlBigptwWXZ9QA8a0dKK7jipSn2K8", - "nationalities": - "tr8SXHdYS0rzAio_IhFp2lzlta4kDzKCM7hUxItCU2U", - "address": { - "locality": "VFgKHPXnNrZHeoBwcu61b5VCoFVX0rQjtH5aOiiLz0E", - "postal_code": - "G8XHi8sCPc45WATery6RSvnEcdypnrjypjBl4LBd5YE", - "country": "YyG4Nhyfjitpo6-yMDRTARSVAnZNvkYqRY3XepoQ_j8", - "street_address": - "NwAKfAtjQcN_XbV3kuHt3gbUMvQ83n02C1EexI9Ro2A" - } - } - }, - "birth_middle_name": - "M5GhkvNcGjGONRey2pRORuL2yCfYz5jo0XqF6K0tUWk", - "salutation": "8m0-sBNA8I88_LDc05C7gE31pTm_CXQfewiwlL1Sn1Y", - "msisdn": "dLQVMDIkEHnmPVvuHNYiv7WwAqGE7mbyJMh5EfbjM1Q" - }, - "verified_claims": { - "claims": { - "birthdate": "1956-01-28", - "place_of_birth": { - "country": "DE", - "locality": "Musterstadt" - } - } - } -} -``` +<{{examples/complex-ekyc/sd_jwt_payload.json}} -The SD-JWT is then signed by the Issuer to create a document like the following: +With the following Disclosures: -{#example-complex-serialized_sd_jwt} -``` -eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ -UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJpc3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc -3N1ZXIiLCAiY25mIjogeyJqd2siOiB7Imt0eSI6ICJSU0EiLCAibiI6ICJwbTRiT0hCZ -y1vWWhBeVBXelI1NkFXWDNyVUlYcDExX0lDRGtHZ1M2VzNaV0x0cy1oendJM3g2NTY1O -WtnNGhWbzlkYkdvQ0pFM1pHRl9lYWV0RTMwVWhCVUVncEd3ckRyUWlKOXpxcHJtY0Zmc -jNxdnZrR2p0dGg4WmdsMWVNMmJKY093RTdQQ0JIV1RLV1lzMTUyUjdnNkpnMk9WcGgtY -ThycS1xNzlNaEtHNVFvV19tVHoxMFFUXzZINGM3UGpXRzFmamg4aHBXTm5iUF9wdjZkM -XpTd1pmYzVmbDZ5VlJMMERWMFYzbEdIS2UyV3FmX2VOR2pCckJMVmtsRFRrOC1zdFhfT -VdMY1ItRUdtWEFPdjBVQldpdFNfZFhKS0p1LXZYSnl3MTRuSFNHdXhUSUsyaHgxcHR0T -WZ0OUNzdnFpbVhLZURUVTE0cVFMMWVFN2loY3ciLCAiZSI6ICJBUUFCIn19LCAiaWF0I -jogMTUxNjIzOTAyMiwgImV4cCI6IDE1MTYyNDcwMjIsICJzZF9kaWdlc3RfZGVyaXZhd -Glvbl9hbGciOiAic2hhLTI1NiIsICJzZF9kaWdlc3RzIjogeyJ2ZXJpZmllZF9jbGFpb -XMiOiB7InZlcmlmaWNhdGlvbiI6IHsidHJ1c3RfZnJhbWV3b3JrIjogImZrSVctNGlVW -mdUZUllRGdfWl82b0ZIVS13eVd3YXpTcHVhaVFiYzVRS3ciLCAidGltZSI6ICJWUkYtR -19MZlR6U2FZa0xlbFZ6cnk4MmwxelF4R3drMVJmR2NuVVVXdWtjIiwgInZlcmlmaWNhd -Glvbl9wcm9jZXNzIjogIjlPcERtbDRlUkJNNlVzZmszTUYyaTdrQmwxeEdHa3pQcTVOY -3MxbXZiUG8iLCAiZXZpZGVuY2UiOiBbeyJ0eXBlIjogIkh1Y2FuSGhRd2ItVEpOZ19yV -nBhb25OU0R0elByQ0VlYmIzTGZYVHVMU00iLCAibWV0aG9kIjogImFVN0lPN29vVDh2Q -XJNa3FwT2ZrSUFsS3c4Qk5jZlJ5dzNOWHMzWlMxMjgiLCAidGltZSI6ICJMSGNIOThiV -jMtWk5VYTAwSE5ucU9mOFc1SWRpalkxYUVucFZ6RE5WQndBIiwgImRvY3VtZW50Ijoge -yJ0eXBlIjogIjNJVElsZmtiVUkwTnZldmlFSkJ3LV9WRWFHaVB0Q0RjWHk5dUQ5b3JXR -kEiLCAiaXNzdWVyIjogeyJuYW1lIjogIkFZN3dXNjNWYmNkN1JuS0RiMzlzU1hwTGd5a -VZOeFdnb1JuVjZ4WkQ1QzgiLCAiY291bnRyeSI6ICJLZDNhVW1tNlhIanBXcDZPWWlKZ -UVaVXJENUo3bklSVTNTbFRjLUU1M2dzIn0sICJudW1iZXIiOiAiOGdLcGtzbDY2Zk45R -jJaeHMxUFJQZ0Q4a0hpOGRHQzJKenBxdHJQWmF2cyIsICJkYXRlX29mX2lzc3VhbmNlI -jogIkdmSUVoT0dXd2U4SjdseDZIU0FQcEMtUXZ4MGlod1drRUUwX0xaLXJfREkiLCAiZ -GF0ZV9vZl9leHBpcnkiOiAiX2ZkbGpLUmRwNXdwdEdpN0R3S05aRXNTWDZBbm5pVnFtR -EUwYVN6bkg3NCJ9fV19LCAiY2xhaW1zIjogeyJnaXZlbl9uYW1lIjogInN4NHdHZDYtT -05Bc2lxN2ROMTZHSGVnNFJBeU9zaFJCZG9YV0VfRTc1MXciLCAiZmFtaWx5X25hbWUiO -iAiTGRiZWEwU2liQVFEaVpKbEJpZ3B0d1dYWjlRQThhMGRLSzdqaXBTbjJLOCIsICJuY -XRpb25hbGl0aWVzIjogInRyOFNYSGRZUzByekFpb19JaEZwMmx6bHRhNGtEektDTTdoV -XhJdENVMlUiLCAiYWRkcmVzcyI6IHsibG9jYWxpdHkiOiAiVkZnS0hQWG5OclpIZW9Cd -2N1NjFiNVZDb0ZWWDByUWp0SDVhT2lpTHowRSIsICJwb3N0YWxfY29kZSI6ICJHOFhIa -ThzQ1BjNDVXQVRlcnk2UlN2bkVjZHlwbnJqeXBqQmw0TEJkNVlFIiwgImNvdW50cnkiO -iAiWXlHNE5oeWZqaXRwbzYteU1EUlRBUlNWQW5aTnZrWXFSWTNYZXBvUV9qOCIsICJzd -HJlZXRfYWRkcmVzcyI6ICJOd0FLZkF0alFjTl9YYlYza3VIdDNnYlVNdlE4M24wMkMxR -WV4STlSbzJBIn19fSwgImJpcnRoX21pZGRsZV9uYW1lIjogIk01R2hrdk5jR2pHT05SZ -XkycFJPUnVMMnlDZll6NWpvMFhxRjZLMHRVV2siLCAic2FsdXRhdGlvbiI6ICI4bTAtc -0JOQThJODhfTERjMDVDN2dFMzFwVG1fQ1hRZmV3aXdsTDFTbjFZIiwgIm1zaXNkbiI6I -CJkTFFWTURJa0VIbm1QVnZ1SE5ZaXY3V3dBcUdFN21ieUpNaDVFZmJqTTFRIn0sICJ2Z -XJpZmllZF9jbGFpbXMiOiB7ImNsYWltcyI6IHsiYmlydGhkYXRlIjogIjE5NTYtMDEtM -jgiLCAicGxhY2Vfb2ZfYmlydGgiOiB7ImNvdW50cnkiOiAiREUiLCAibG9jYWxpdHkiO -iAiTXVzdGVyc3RhZHQifX19fQ.57pncJcJ6cQt2fSARbQLlj6e6nYMpWqHNvI2Ep45Wm -NGuTtI3htmodK8svpgbrT-RaLL25WF7J3CqP1ElzpZSgVFs2VXCxGXgnTG6dQIvk2qPP -fP-45hrZiMWyiwRFBr7Di68J01N90yFGbsMH5hh8kGGqFnCpTSQwvk--6aG_03l0nGmL -DjOFyauCF_Tl-SlOHzNGYoP3MOOX9jU25T8z2e3EmLVLTa5KEmNis0GbpfSHUthbtZCC -Taq-bSYaPDUHi22ZNqeoW1Y4v8nSaNIyrV9IxfPJNb37kYN6NLn5zwI33sxE_nCd8wOx -vuI0rtFtmpS_-DNgwPTnLphzUNKA -``` +{{examples/complex-ekyc/disclosures.md}} -An HS-Disclosures JWT for some of the claims may look as follows: +The Verifier would receive the Issuer-signed SD-JWT together with a selection +of the Disclosures. The Presentation in this example would look as follows: -{#example-complex-hsd_jwt_payload} -```json -{ - "nonce": "XZOUco1u_gEPknxS78sWWg", - "aud": "https://example.com/verifier", - "sd_hs_disclosures": { - "verified_claims": { - "verification": { - "trust_framework": "{\"s\": \"SJKr-Pydh8RqHomXCOiVwQ\", - \"v\": \"de_aml\"}", - "time": "{\"s\": \"CrxH2Ez8uu2t7tEPQqwZig\", \"v\": - \"2012-04-23T18:25Z\"}", - "evidence": [ - { - "type": "{\"s\": \"sPCCbZtOdjnQjfOiPBxOYA\", \"v\": - \"document\"}" - } - ] - }, - "claims": { - "given_name": "{\"s\": \"kqwnbB6oHhaBD3F3t-KUGw\", \"v\": - \"Max\"}", - "family_name": "{\"s\": \"_6Do5glcgEQDMVJoPArGSA\", \"v\": - \"Meier\"}" - } - } - } -} -``` +<{{examples/complex-ekyc/combined_presentation.txt}} -After verifying the SD-JWT and HS-Disclosures, the Verifier merges the selectively -disclosed claims into the other data contained in the JWT. The Verifier will -then pass the result on to the application for further processing: +After the verification of the data, the Verifier will +pass the following result on to the application for further processing: -{#example-complex-merged} -```json -{ - "verified_claims": { - "verification": { - "trust_framework": "de_aml", - "time": "2012-04-23T18:25Z", - "evidence": [ - { - "type": "document" - } - ] - }, - "claims": { - "given_name": "Max", - "family_name": "Meier", - "birthdate": "1956-01-28", - "place_of_birth": { - "country": "DE", - "locality": "Musterstadt" - } - } - }, - "iss": "https://example.com/issuer", - "cnf": { - "jwk": { - "kty": "RSA", - "n": "pm4bOHBg-oYhAyPWzR56AWX3rUIXp11_ICDkGgS6W3ZWLts-hzwI3x65 - 659kg4hVo9dbGoCJE3ZGF_eaetE30UhBUEgpGwrDrQiJ9zqprmcFfr3qvvkG - jtth8Zgl1eM2bJcOwE7PCBHWTKWYs152R7g6Jg2OVph-a8rq-q79MhKG5QoW - _mTz10QT_6H4c7PjWG1fjh8hpWNnbP_pv6d1zSwZfc5fl6yVRL0DV0V3lGHK - e2Wqf_eNGjBrBLVklDTk8-stX_MWLcR-EGmXAOv0UBWitS_dXJKJu-vXJyw1 - 4nHSGuxTIK2hx1pttMft9CsvqimXKeDTU14qQL1eE7ihcw", - "e": "AQAB" - } - }, - "iat": 1516239022, - "exp": 1516247022 -} -``` +<{{examples/complex-ekyc/verified_contents.json}} ## Example 4 - W3C Verifiable Credentials Data Model (work in progress) @@ -1796,448 +1036,37 @@ This example illustrates how the artifacts defined in this specification can be represented using W3C Verifiable Credentials Data Model as defined in [@VC_DATA]. -SD-JWT is equivalent to an Issuer-signed W3C Verifiable Credential (W3C VC). II-Disclosures Object is sent alongside a VC. - -HS-Disclosures JWT is equivalent to a Holder-signed W3C Verifiable Presentation (W3C VP). +SD-JWT is equivalent to an Issuer-signed W3C Verifiable Credential (W3C VC). Disclosures are sent alongside a VC. -Holder Binding is applied and HS-Disclosures JWT is signed using a raw public key passed in a `cnf` Claim in a W3C VC (SD-JWT). +A Presentation with a Holder Binding JWT is equivalent to a Holder-signed W3C Verifiable Presentation (W3C VP). -HS-Disclosures JWT as a W3C VP contains a `verifiableCredential` claim inside a `vp` claim that is a string array of an SD-JWT as a W3C VC using JWT compact serialization. +Holder Binding is applied and the Holder Binding JWT is signed using a raw public key passed in a `cnf` Claim in a W3C VC (SD-JWT). Below is a non-normative example of an SD-JWT represented as a verifiable credential encoded as JSON and signed as JWS compliant to [@VC_DATA]. -II-Disclosures Object is the same as in Example 1. +The following data will be used in this example: -```json -{ - "sub": "urn:ietf:params:oauth:jwk-thumbprint:sha-256:NzbLsXh8uDCc - d-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs", - "jti": "http://example.edu/credentials/3732", - "iss": "https://example.com/keys/foo.jwk", - "nbf": 1541493724, - "iat": 1541493724, - "exp": 1573029723, - "cnf": { - "jwk": { - "kty":"RSA", - "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx - 4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMs - tn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2 - QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbI - SD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqb - w0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", - "e":"AQAB" - } - }, - "vc": { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "type": [ - "VerifiableCredential", - "UniversityDegreeCredential" - ], - "credentialSubject": { - "first_name": "Jane", - "last_name": "Doe" - } - }, - "sd_digests": { - "vc": { - "credentialSubject": { - "email": "-Rcr4fDyjwlM_itcMxoQZCE1QAEwyLJcibEpH114KiE", - "phone_number": "Jv2nw0C1wP5ASutYNAxrWEnaDRIpiF0eTUAkUOp8F6Y", - "address": "ZrjKs-RmEAVeAYSzSw6GPFrMpcgctCfaJ6t9qQhbfJ4", - "birthdate": "qXPRRPdpNaebP8jtbEpO-skF4n7v7ASTh8oLg0mkAdQ" - } - } - } -} -``` +<{{examples/w3c-vc/user_claims.json}} -Below is a non-normative example of a HS-Disclosures JWT represented as a verifiable presentation -encoded as JSON and signed as a JWS compliant to [@VC_DATA]. +The encoded SD-JWT looks as follows: +Header: ```json { + "typ": "sd-jwt-vc", "alg": "RS256", - "typ": "JWT", - "jwk": { - "kty":"RSA", - "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx - 4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMs - tn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2 - QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbI - SD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqb - w0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", - "e":"AQAB" - } -}.{ - "iss": "urn:ietf:params:oauth:jwk-thumbprint:sha-256:NzbLsXh8uDCc - d-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs", - "aud": "s6BhdRkqt3", - "nbf": 1560415047, - "iat": 1560415047, - "exp": 1573029723, - "nonce": "660!6345FSer", - "vp": { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "type": [ - "VerifiablePresentation" - ], - "verifiableCredential": ["eyJhb...npyXw"] - }, - "sd_hs_disclosures": { - "vc": { - "credentialSubject": { - "email": "{\"s\": \"Pc33JM2LchcU_lHggv_ufQ\", \"v\": - \"johndoe@example.com\"}", - "phone_number": "{\"s\": \"lklxF5jMYlGTPUovMNIvCA\", \"v\": - \"+1-202-555-0101\"}", - "address": "{\"s\": \"5bPs1IquZNa0hkaFzzzZNw\", \"v\": - {\"street_address\": \"123 Main St\", \"locality\": - \"Anytown\", \"region\": \"Anystate\", \"country\": - \"US\"}}", - "birthdate": "{\"s\": \"y1sVU5wdfJahVdgwPgS7RQ\", \"v\": - \"1940-01-01\"}" - } - } - - } -} -``` - -## Blinding Claim Names - -The following examples show the use of blinded claim names. - -### Example 5: Some Blinded Claims - -The following shows the user information used in this example, included a claim named `secret_club_membership_no`: - -{#example-simple_structured_some_blinded-user_claims} -```json -{ - "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", - "given_name": "John", - "family_name": "Doe", - "email": "johndoe@example.com", - "phone_number": "+1-202-555-0101", - "secret_club_membership_no": "23", - "other_secret_club_membership_no": "42", - "address": { - "street_address": "123 Main St", - "locality": "Anytown", - "region": "Anystate", - "country": "US" - }, - "birthdate": "1940-01-01" -} -``` - -Hiding just the claim `secret_club_membership_no`, the SD-JWT payload shown in the following would -result. Note that the claims are sorted (here by unicode code point -numbers) as described in (#blinding-claim-names). - -{#example-simple_structured_some_blinded-sd_jwt_payload} -```json -{ - "cnf": { - "jwk": { - "e": "AQAB", - "kty": "RSA", - "n": "pm4bOHBg-oYhAyPWzR56AWX3rUIXp11_ICDkGgS6W3ZWLts-hzwI3x65 - 659kg4hVo9dbGoCJE3ZGF_eaetE30UhBUEgpGwrDrQiJ9zqprmcFfr3qvvkG - jtth8Zgl1eM2bJcOwE7PCBHWTKWYs152R7g6Jg2OVph-a8rq-q79MhKG5QoW - _mTz10QT_6H4c7PjWG1fjh8hpWNnbP_pv6d1zSwZfc5fl6yVRL0DV0V3lGHK - e2Wqf_eNGjBrBLVklDTk8-stX_MWLcR-EGmXAOv0UBWitS_dXJKJu-vXJyw1 - 4nHSGuxTIK2hx1pttMft9CsvqimXKeDTU14qQL1eE7ihcw" - } - }, - "exp": 1516247022, - "iat": 1516239022, - "iss": "https://example.com/issuer", - "sd_digest_derivation_alg": "sha-256", - "sd_digests": { - "HS4QoeE9ty-I8BZTEupSzw": - "emp2qhunGPulOGvtgor5dFwNSasDewLqNdqXCkYl4Nw", - "address": { - "country": "Bktf3gG1tXbn0XObrZT53RUr_lxMLZGEguLYwCvsaIg", - "locality": "NeWRh4B9JLRfEODwno3UOXg9Pg3gtZEo45cK9pr4eZk", - "region": "qpgFbdX1Az4Hm_E63K3J94oMzazHLCqqFb0Damo2eFE", - "street_address": - "6Ex8b2gEeACuMal74_OBH_ROVNM7wvzjSck08EC9eSs" - }, - "birthdate": "1IjWWzdrXEs7iXUbsahdx_-8CIJsz2bcHHH_ccwgTBg", - "email": "gszmttjNfSw7_uL31KyJRvWgL1gHM6O3LFAzqxluWDQ", - "family_name": "Xbz5qK4Fqg-bS_CdwQYd_7qiNS9W810mRn42-FTHMPo", - "given_name": "asBCBSyK-B45q79qxGMe6j4MijK4lZsHHCD8O_jsDdc", - "other_secret_club_membership_no": - "3RP5qguZWamNuvdrFS-sqqYq_MaCIzx6Zn_bOZyE9BY", - "phone_number": "lB98F2RApo-ifhA3lwJGdqV-PAURkstN-oHmCv4LmxA", - "sub": "sJ88WF6Q05a2eyPnLJHXzZ8bbiQXWlXl44Nss7Ywk0E" - } -} -``` - -In the II-Disclosures Object, it can be seen that the blinded claim's original name is `secret_club_membership_no`. Note that the claims are sorted alphabetically as described in (#blinding-claim-names). - - -{#example-simple_structured_some_blinded-iid_payload} -```json -{ - "sd_ii_disclosures": { - "HS4QoeE9ty-I8BZTEupSzw": "{\"s\": \"iq6rolXF0SyWSsdCeaETNg\", - \"v\": \"23\", \"n\": \"secret_club_membership_no\"}", - "address": { - "country": "{\"s\": \"l-6DlGlNloOsAUlBhMOt_Q\", \"v\": - \"US\"}", - "locality": "{\"s\": \"c6kc69Gmh04VVNPRlhOV_g\", \"v\": - \"Anytown\"}", - "region": "{\"s\": \"qwybxKQUee9A0mMhzGC-Pg\", \"v\": - \"Anystate\"}", - "street_address": "{\"s\": \"qNsw9K05ZngcEqXLEGalHA\", \"v\": - \"123 Main St\"}" - }, - "birthdate": "{\"s\": \"OErzfd2Gy6jw1atlcCpr6A\", \"v\": - \"1940-01-01\"}", - "email": "{\"s\": \"woZIMokulfwyF_do1czRaA\", \"v\": - \"johndoe@example.com\"}", - "family_name": "{\"s\": \"ZXPEdf3K8mtRBKDAMjEcBQ\", \"v\": - \"Doe\"}", - "given_name": "{\"s\": \"btsLJCwSb0B7gtVLPMjjqA\", \"v\": - \"John\"}", - "other_secret_club_membership_no": "{\"s\": - \"Fj8RxKoVno-9SOVOEUoMpw\", \"v\": \"42\"}", - "phone_number": "{\"s\": \"YJSPlYo_aenthOCkapFRTg\", \"v\": - \"+1-202-555-0101\"}", - "sub": "{\"s\": \"Rj94TRxr3nvOw2WKtujLSA\", \"v\": - \"6c5c0a49-b589-431d-bae7-219122a9ec2c\"}" - } -} -``` - -The Verifier would learn this information via the HS-Disclosures JWT: - -{#example-simple_structured_some_blinded-hsd_jwt_payload} -```json -{ - "nonce": "XZOUco1u_gEPknxS78sWWg", - "aud": "https://example.com/verifier", - "sd_hs_disclosures": { - "given_name": "{\"s\": \"btsLJCwSb0B7gtVLPMjjqA\", \"v\": - \"John\"}", - "family_name": "{\"s\": \"ZXPEdf3K8mtRBKDAMjEcBQ\", \"v\": - \"Doe\"}", - "birthdate": "{\"s\": \"OErzfd2Gy6jw1atlcCpr6A\", \"v\": - \"1940-01-01\"}", - "address": { - "region": "{\"s\": \"qwybxKQUee9A0mMhzGC-Pg\", \"v\": - \"Anystate\"}", - "country": "{\"s\": \"l-6DlGlNloOsAUlBhMOt_Q\", \"v\": - \"US\"}" - }, - "HS4QoeE9ty-I8BZTEupSzw": "{\"s\": \"iq6rolXF0SyWSsdCeaETNg\", - \"v\": \"23\", \"n\": \"secret_club_membership_no\"}" - } -} -``` - -The Verifier would decode the data as follows: - - -{#example-simple_structured_some_blinded-verified_contents} -```json -{ - "given_name": "John", - "family_name": "Doe", - "birthdate": "1940-01-01", - "address": { - "region": "Anystate", - "country": "US" - }, - "secret_club_membership_no": "23" -} -``` -### Example 6: All Claim Names Blinded - -In this example, all claim names are blinded. The user data includes a -non-standard `delivery_address` claim to show that even though the same -claim name appears at different places within the structure, different -salts and blinded claim names are used for them: - -{#example-simple_structured_all_blinded-user_claims} -```json -{ - "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", - "given_name": "John", - "family_name": "Doe", - "email": "johndoe@example.com", - "phone_number": "+1-202-555-0101", - "secret_club_membership_no": "23", - "address": { - "street_address": "123 Main St", - "locality": "Anytown", - "region": "Anystate", - "country": "US" - }, - "delivery_address": { - "street_address": "123 Main St", - "locality": "Anytown", - "region": "Anystate", - "country": "US" - }, - "birthdate": "1940-01-01" -} -``` - - -The resulting SD-JWT payload: - -{#example-simple_structured_all_blinded-sd_jwt_payload} -```json -{ - "cnf": { - "jwk": { - "e": "AQAB", - "kty": "RSA", - "n": "pm4bOHBg-oYhAyPWzR56AWX3rUIXp11_ICDkGgS6W3ZWLts-hzwI3x65 - 659kg4hVo9dbGoCJE3ZGF_eaetE30UhBUEgpGwrDrQiJ9zqprmcFfr3qvvkG - jtth8Zgl1eM2bJcOwE7PCBHWTKWYs152R7g6Jg2OVph-a8rq-q79MhKG5QoW - _mTz10QT_6H4c7PjWG1fjh8hpWNnbP_pv6d1zSwZfc5fl6yVRL0DV0V3lGHK - e2Wqf_eNGjBrBLVklDTk8-stX_MWLcR-EGmXAOv0UBWitS_dXJKJu-vXJyw1 - 4nHSGuxTIK2hx1pttMft9CsvqimXKeDTU14qQL1eE7ihcw" - } - }, - "exp": 1516247022, - "iat": 1516239022, - "iss": "https://example.com/issuer", - "sd_digest_derivation_alg": "sha-256", - "sd_digests": { - "2lrQaXAeV85isgBjuHAOfw": - "2kT2ohIPzxb8Mt2aa8YJ7Rj_SmTUrSIfzCz8zVXix5E", - "HTQvLIU4zz7NkMr5p4KDWw": - "4Hyw9wnR-uEvbJPSyQdrZMz6JY99mLqR_9m_lntD4_s", - "HmNQxl6SFAx_Su6uDR94lg": - "tmuay_zrl23ZdDX1hIyI48a4huCiTf70chBEvAl2Qf0", - "_ZzazarE9UrZHTv3BnHJ1w": - "uW7QffEkT_Hw4q_LrsIDV5vcQGh7ubQdKSOJc5qXRiQ", - "address": { - "MXDpEmt5sRxRxlDw8YAdfA": - "9Lu5UyimpvSrpJU9R80aEpzemufK8eRH5QEKo5xLJj0", - "SdQneYafbrvTMuPyyQhj2A": - "f54HJqBhU7gC1MHPaMYzzlr9vg96qE3eo4kP2zXoKTc", - "abIR4EGTTgQKNXnmxoY5qA": - "QDUyK4ACX0mVPfCm7uVQFwbBPNo6_xI6-3fHZaHEQW0", - "zno2BBCk2a7pk49dcZYnqw": - "Ho4wYzUNdQow3TBdPmH5Fbq-4Me_fx8pECok3NJMfFM" - }, - "aqdLloEIUy4FVDdmmSo48w": - "C5XJjFnU9CX-k_xIo7qxX_CsLKcR5GDqJJ3MBy_o1Zg", - "cIsqMhsylJDPtEpoqVGLvQ": - "wWR4GNiwmcPPbTfuIwphr3j4Vs95TCjUzytdiPC7434", - "delivery_address": { - "Q7vBvGQFVCw8keOQLY1SVg": - "sPeIeivTQeApuZ2piXouWMEm1xA_liTae8BsEOQ7z9M", - "bMMGdJM0qO_zqVo75zun1w": - "xTM2Ojec5bxvHY6sOt5c47LeMErCR7TSc1tJ51v28tQ", - "bxNsq8p-Jobl47JNkhNOMA": - "AF3X_wkKrY4KHiajZ5vhv7CzUp-ATXe-Jtl5x7QUAcg", - "kR7kfLZF-3YiQ5VRgsY3yA": - "crbT6qlk8nmEkwqO_GsFUUQHNq7DxoU0ziMh22Cxe7M" - }, - "vEA0i5_1JvuDfS7hH7TWZw": - "d9Wa_qCEbikmrXt_1refkreitUPIbZWNn5miQGZWPKg" - } -} -``` - -The II-Disclosures Object: -{#example-simple_structured_all_blinded-iid_payload} -```json -{ - "sd_ii_disclosures": { - "2lrQaXAeV85isgBjuHAOfw": "{\"s\": \"PdxYWdt_MFsC6qce2uiVLQ\", - \"v\": \"+1-202-555-0101\", \"n\": \"phone_number\"}", - "HTQvLIU4zz7NkMr5p4KDWw": "{\"s\": \"353CLP3ZZFmxJQ6aZ_HDYg\", - \"v\": \"John\", \"n\": \"given_name\"}", - "HmNQxl6SFAx_Su6uDR94lg": "{\"s\": \"Qb5pmhvwzr4aRd7g7QVckA\", - \"v\": \"23\", \"n\": \"secret_club_membership_no\"}", - "_ZzazarE9UrZHTv3BnHJ1w": "{\"s\": \"yL66N684FNAao5hWfBqc6A\", - \"v\": \"1940-01-01\", \"n\": \"birthdate\"}", - "address": { - "MXDpEmt5sRxRxlDw8YAdfA": "{\"s\": \"3VzdS1O4wRgglXFk_ENJ2g\", - \"v\": \"Anytown\", \"n\": \"locality\"}", - "SdQneYafbrvTMuPyyQhj2A": "{\"s\": \"VQz3c8LhaQCy7hqEsusPPA\", - \"v\": \"US\", \"n\": \"country\"}", - "abIR4EGTTgQKNXnmxoY5qA": "{\"s\": \"BhX1rStOsN3_vk_Kx4IgOg\", - \"v\": \"Anystate\", \"n\": \"region\"}", - "zno2BBCk2a7pk49dcZYnqw": "{\"s\": \"u2jvKsy0g-inkL3RAcpssw\", - \"v\": \"123 Main St\", \"n\": \"street_address\"}" - }, - "aqdLloEIUy4FVDdmmSo48w": "{\"s\": \"Y3d_N7vZNfNp7KWDmCpJlA\", - \"v\": \"Doe\", \"n\": \"family_name\"}", - "cIsqMhsylJDPtEpoqVGLvQ": "{\"s\": \"bbdW6Rtr4YEaDvydH4Yerw\", - \"v\": \"johndoe@example.com\", \"n\": \"email\"}", - "delivery_address": { - "Q7vBvGQFVCw8keOQLY1SVg": "{\"s\": \"nBOOpTNOcCScA_MHr9P9SQ\", - \"v\": \"Anystate\", \"n\": \"region\"}", - "bMMGdJM0qO_zqVo75zun1w": "{\"s\": \"urI5m4JPtDbe9rRQbXgtEg\", - \"v\": \"US\", \"n\": \"country\"}", - "bxNsq8p-Jobl47JNkhNOMA": "{\"s\": \"LojbKO3mpEE6WTgSL5EzMg\", - \"v\": \"123 Main St\", \"n\": \"street_address\"}", - "kR7kfLZF-3YiQ5VRgsY3yA": "{\"s\": \"e925I1ajysz2xx9kzyzveg\", - \"v\": \"Anytown\", \"n\": \"locality\"}" - }, - "vEA0i5_1JvuDfS7hH7TWZw": "{\"s\": \"i_rQHJJUvGFdOgVVM8H8Ww\", - \"v\": \"6c5c0a49-b589-431d-bae7-219122a9ec2c\", \"n\": - \"sub\"}" - } + "kid": "cAEIUqJ0cmLzD1kzGzheiBag0YRAzVdlfxN280NgHaA" } ``` -Here, the Holder decided only to disclose a subset of the claims to the Verifier: +Body: -{#example-simple_structured_all_blinded-hsd_jwt_payload} -```json -{ - "nonce": "XZOUco1u_gEPknxS78sWWg", - "aud": "https://example.com/verifier", - "sd_hs_disclosures": { - "HTQvLIU4zz7NkMr5p4KDWw": "{\"s\": \"353CLP3ZZFmxJQ6aZ_HDYg\", - \"v\": \"John\", \"n\": \"given_name\"}", - "aqdLloEIUy4FVDdmmSo48w": "{\"s\": \"Y3d_N7vZNfNp7KWDmCpJlA\", - \"v\": \"Doe\", \"n\": \"family_name\"}", - "_ZzazarE9UrZHTv3BnHJ1w": "{\"s\": \"yL66N684FNAao5hWfBqc6A\", - \"v\": \"1940-01-01\", \"n\": \"birthdate\"}", - "address": { - "abIR4EGTTgQKNXnmxoY5qA": "{\"s\": \"BhX1rStOsN3_vk_Kx4IgOg\", - \"v\": \"Anystate\", \"n\": \"region\"}", - "SdQneYafbrvTMuPyyQhj2A": "{\"s\": \"VQz3c8LhaQCy7hqEsusPPA\", - \"v\": \"US\", \"n\": \"country\"}" - } - } -} -``` +<{{examples/w3c-vc/sd_jwt_payload.json}} -The Verifier would decode the HS-Disclosures JWT and SD-JWT as follows: +Disclosures: - -{#example-simple_structured_all_blinded-verified_contents} -```json -{ - "given_name": "John", - "family_name": "Doe", - "birthdate": "1940-01-01", - "address": { - "region": "Anystate", - "country": "US" - } -} -``` +{{examples/w3c-vc/disclosures.md}} @@ -2246,6 +1075,17 @@ The Verifier would decode the HS-Disclosures JWT and SD-JWT as follows: [[ To be removed from the final specification ]] + -03 + + * Disclosures are now delivered not as a JWT but as separate base64url-encoded JSON objects. + * In the SD-JWT, hash digests are collected under a `_sd` claim per level. + * Terms "II-Disclosures" and "HS-Disclosures" are replaced with "Disclosures". + * Holder Binding is now separate from delivering the Disclosures and implemented, if required, with a separate JWT. + * Examples updated and modified to properly explain the specifics of the new SD-JWT format. + * Examples are now pulled in from the examples directory, not inlined. + * Updated and automated the W3C VC example. + * Added examples with multibyte characters to show that the specification and demo code work well with UTF-8. + -02 * reformatted diff --git a/examples/address_only_flat/combined_issuance.txt b/examples/address_only_flat/combined_issuance.txt new file mode 100644 index 00000000..6b30447d --- /dev/null +++ b/examples/address_only_flat/combined_issuance.txt @@ -0,0 +1,14 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbInZOaGJMYlh4NlNGcUlKWm41bE5Ye +XJBU2pwYVhHVVQydU5HdXlfZ1NNYnciXSwgInN1YiI6ICI2YzVjMGE0OS1iNTg5LTQzM +WQtYmFlNy0yMTkxMjJhOWVjMmMiLCAiaXNzIjogImh0dHBzOi8vZXhhbXBsZS5jb20va +XNzdWVyIiwgImlhdCI6IDE1MTYyMzkwMjIsICJleHAiOiAxNTE2MjQ3MDIyLCAic2RfZ +GlnZXN0X2Rlcml2YXRpb25fYWxnIjogInNoYS0yNTYifQ.hC6INqxFSXZZXE3vGmDmHw +7kLocw3SGlpynogdhf5XhrcMYVlSq2gIXWuXZgxtafU7ibF63CcVYSsIXL2WVn1LL1FZ +1h7lxz8Bx1IcfE5l1YpVbF99msx54Y4XsK39A24xwARRfPDxRFtScxW_eo7O51Z7hUec +hpcqBS3OBtbzUDVVqdu5Sd8v4iP9PhokWoN3m5DWaUhT2-CwjFR0vL8tN6-6C73Qhd6Y +9LTObNqTiDGCmK16yHZy67Z-Dh0Cpvkj7iNX5WYq_FYMd1vlCKp5GNG6fhnyIQ4o4W0E +KBB4GkpeDIGOr-zSXdm_3hRaTp3P-nxplnj9YCaKeYSuQvnw~WyJEa1daaVFLdW1SYXI +zbHBmNHFFYlNBIiwgImFkZHJlc3MiLCB7InN0cmVldF9hZGRyZXNzIjogIlNjaHVsc3R +yLiAxMiIsICJsb2NhbGl0eSI6ICJTY2h1bHBmb3J0YSIsICJyZWdpb24iOiAiU2FjaHN +lbi1BbmhhbHQiLCAiY291bnRyeSI6ICJERSJ9XQ \ No newline at end of file diff --git a/examples/address_only_flat/combined_presentation.txt b/examples/address_only_flat/combined_presentation.txt new file mode 100644 index 00000000..fabb9c64 --- /dev/null +++ b/examples/address_only_flat/combined_presentation.txt @@ -0,0 +1,11 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbInZOaGJMYlh4NlNGcUlKWm41bE5Ye +XJBU2pwYVhHVVQydU5HdXlfZ1NNYnciXSwgInN1YiI6ICI2YzVjMGE0OS1iNTg5LTQzM +WQtYmFlNy0yMTkxMjJhOWVjMmMiLCAiaXNzIjogImh0dHBzOi8vZXhhbXBsZS5jb20va +XNzdWVyIiwgImlhdCI6IDE1MTYyMzkwMjIsICJleHAiOiAxNTE2MjQ3MDIyLCAic2RfZ +GlnZXN0X2Rlcml2YXRpb25fYWxnIjogInNoYS0yNTYifQ.hC6INqxFSXZZXE3vGmDmHw +7kLocw3SGlpynogdhf5XhrcMYVlSq2gIXWuXZgxtafU7ibF63CcVYSsIXL2WVn1LL1FZ +1h7lxz8Bx1IcfE5l1YpVbF99msx54Y4XsK39A24xwARRfPDxRFtScxW_eo7O51Z7hUec +hpcqBS3OBtbzUDVVqdu5Sd8v4iP9PhokWoN3m5DWaUhT2-CwjFR0vL8tN6-6C73Qhd6Y +9LTObNqTiDGCmK16yHZy67Z-Dh0Cpvkj7iNX5WYq_FYMd1vlCKp5GNG6fhnyIQ4o4W0E +KBB4GkpeDIGOr-zSXdm_3hRaTp3P-nxplnj9YCaKeYSuQvnw~ \ No newline at end of file diff --git a/examples/address_only_flat/disclosures.md b/examples/address_only_flat/disclosures.md new file mode 100644 index 00000000..5ae0d535 --- /dev/null +++ b/examples/address_only_flat/disclosures.md @@ -0,0 +1,17 @@ +__Disclosure for `address`:__ + +``` +WyJEa1daaVFLdW1SYXIzbHBmNHFFYlNBIiwgImFkZHJlc3MiLCB7InN0cmVldF9hZGRy +ZXNzIjogIlNjaHVsc3RyLiAxMiIsICJsb2NhbGl0eSI6ICJTY2h1bHBmb3J0YSIsICJy +ZWdpb24iOiAiU2FjaHNlbi1BbmhhbHQiLCAiY291bnRyeSI6ICJERSJ9XQ +``` + +Contents: + +``` +["DkWZiQKumRar3lpf4qEbSA", "address", {"street_address": "Schulstr. +12", "locality": "Schulpforta", "region": "Sachsen-Anhalt", +"country": "DE"}] +``` + +SHA-256 Hash: `vNhbLbXx6SFqIJZn5lNXyrASjpaXGUT2uNGuy_gSMbw` \ No newline at end of file diff --git a/examples/address_only_flat/hb_jwt_serialized.txt b/examples/address_only_flat/hb_jwt_serialized.txt new file mode 100644 index 00000000..e69de29b diff --git a/examples/address_only_flat/sd_jwt_payload.json b/examples/address_only_flat/sd_jwt_payload.json new file mode 100644 index 00000000..a23b0929 --- /dev/null +++ b/examples/address_only_flat/sd_jwt_payload.json @@ -0,0 +1,10 @@ +{ + "_sd": [ + "vNhbLbXx6SFqIJZn5lNXyrASjpaXGUT2uNGuy_gSMbw" + ], + "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", + "iss": "https://example.com/issuer", + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256" +} \ No newline at end of file diff --git a/examples/address_only_flat/sd_jwt_serialized.txt b/examples/address_only_flat/sd_jwt_serialized.txt new file mode 100644 index 00000000..2dccf0a6 --- /dev/null +++ b/examples/address_only_flat/sd_jwt_serialized.txt @@ -0,0 +1,11 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbInZOaGJMYlh4NlNGcUlKWm41bE5Ye +XJBU2pwYVhHVVQydU5HdXlfZ1NNYnciXSwgInN1YiI6ICI2YzVjMGE0OS1iNTg5LTQzM +WQtYmFlNy0yMTkxMjJhOWVjMmMiLCAiaXNzIjogImh0dHBzOi8vZXhhbXBsZS5jb20va +XNzdWVyIiwgImlhdCI6IDE1MTYyMzkwMjIsICJleHAiOiAxNTE2MjQ3MDIyLCAic2RfZ +GlnZXN0X2Rlcml2YXRpb25fYWxnIjogInNoYS0yNTYifQ.hC6INqxFSXZZXE3vGmDmHw +7kLocw3SGlpynogdhf5XhrcMYVlSq2gIXWuXZgxtafU7ibF63CcVYSsIXL2WVn1LL1FZ +1h7lxz8Bx1IcfE5l1YpVbF99msx54Y4XsK39A24xwARRfPDxRFtScxW_eo7O51Z7hUec +hpcqBS3OBtbzUDVVqdu5Sd8v4iP9PhokWoN3m5DWaUhT2-CwjFR0vL8tN6-6C73Qhd6Y +9LTObNqTiDGCmK16yHZy67Z-Dh0Cpvkj7iNX5WYq_FYMd1vlCKp5GNG6fhnyIQ4o4W0E +KBB4GkpeDIGOr-zSXdm_3hRaTp3P-nxplnj9YCaKeYSuQvnw \ No newline at end of file diff --git a/examples/address_only_flat/user_claims.json b/examples/address_only_flat/user_claims.json new file mode 100644 index 00000000..2ef9fb89 --- /dev/null +++ b/examples/address_only_flat/user_claims.json @@ -0,0 +1,9 @@ +{ + "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", + "address": { + "street_address": "Schulstr. 12", + "locality": "Schulpforta", + "region": "Sachsen-Anhalt", + "country": "DE" + } +} \ No newline at end of file diff --git a/examples/address_only_flat/verified_contents.json b/examples/address_only_flat/verified_contents.json new file mode 100644 index 00000000..7cfafa33 --- /dev/null +++ b/examples/address_only_flat/verified_contents.json @@ -0,0 +1,7 @@ +{ + "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", + "iss": "https://example.com/issuer", + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256" +} \ No newline at end of file diff --git a/examples/address_only_structured/combined_issuance.txt b/examples/address_only_structured/combined_issuance.txt new file mode 100644 index 00000000..43d9144a --- /dev/null +++ b/examples/address_only_structured/combined_issuance.txt @@ -0,0 +1,18 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJzdWIiOiAiNmM1YzBhNDktYjU4OS00MzFkLWJhZ +TctMjE5MTIyYTllYzJjIiwgImFkZHJlc3MiOiB7Il9zZCI6IFsiN3BIZTF1UTV1U0NsZ +0F4WGRHMEU2ZEtuQmdYY3hFTzF6dm9RTzlFNUxyNCIsICI5LVZkU252UlRaTkRvLTRCe +GNwM1gtVjlWdExPQ1JVa1I2b0xXWlFsODFJIiwgIm5UelBaM1E2OHoxS29fOWFvOUxLM +G1TWVhZNWdZNlVHNktFa1FfQmRxVTAiLCAicEV0a0t3b0ZLX0pITjd5TmJ5MExjX0pjM +TBCQXhDbTV5WEpqRGJWZWh2VSJdfSwgImlzcyI6ICJodHRwczovL2V4YW1wbGUuY29tL +2lzc3VlciIsICJpYXQiOiAxNTE2MjM5MDIyLCAiZXhwIjogMTUxNjI0NzAyMiwgInNkX +2RpZ2VzdF9kZXJpdmF0aW9uX2FsZyI6ICJzaGEtMjU2In0.C004tnalDWXrWO9G0lfrP +NFgfsXCc0QdnpOmJx_NPdDuOeGTLQ2sQg-ozrqNbvOqp08tt7DG4kUpkPvjdd-nXOiGw +GP27JoVgnNFfCzC_gwG4w7GtiDnurExDF9tPcUfR1zzIxca8xZfQCIoVPN_6Dc5K6zs4 +bRFcyrQWKewU-8eoJ-N9zXNKRSqEJcOYvndZzopW68dKa0pOxxBKbV1RzEG9HiJfV7_T +nKBt2teEOztwTIxpXYomGRbRqCIRbvZ5SR-lyaKk1GiZf3f0zQ9tDPv1LKC8QLb0lsDf +IvQ504JVxfkepx4zGwf-xOzCbtaXk6_f86-0eUdmXaIhgpcHA~WyI0d3dqUzlyMm4tbl +BxdzNpTHR0TkFBIiwgInN0cmVldF9hZGRyZXNzIiwgIlNjaHVsc3RyLiAxMiJd~WyJXc +EtIQmVTa3A5U2MyNVV4a1F1RmNRIiwgImxvY2FsaXR5IiwgIlNjaHVscGZvcnRhIl0~W +yIzSl9xWGctdUwxYzdtN1FoT0hUNTJnIiwgInJlZ2lvbiIsICJTYWNoc2VuLUFuaGFsd +CJd~WyIwN2U3bWY2YWpTUDJjZkQ3NmJCZE93IiwgImNvdW50cnkiLCAiREUiXQ \ No newline at end of file diff --git a/examples/address_only_structured/combined_presentation.txt b/examples/address_only_structured/combined_presentation.txt new file mode 100644 index 00000000..b779cf86 --- /dev/null +++ b/examples/address_only_structured/combined_presentation.txt @@ -0,0 +1,14 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJzdWIiOiAiNmM1YzBhNDktYjU4OS00MzFkLWJhZ +TctMjE5MTIyYTllYzJjIiwgImFkZHJlc3MiOiB7Il9zZCI6IFsiN3BIZTF1UTV1U0NsZ +0F4WGRHMEU2ZEtuQmdYY3hFTzF6dm9RTzlFNUxyNCIsICI5LVZkU252UlRaTkRvLTRCe +GNwM1gtVjlWdExPQ1JVa1I2b0xXWlFsODFJIiwgIm5UelBaM1E2OHoxS29fOWFvOUxLM +G1TWVhZNWdZNlVHNktFa1FfQmRxVTAiLCAicEV0a0t3b0ZLX0pITjd5TmJ5MExjX0pjM +TBCQXhDbTV5WEpqRGJWZWh2VSJdfSwgImlzcyI6ICJodHRwczovL2V4YW1wbGUuY29tL +2lzc3VlciIsICJpYXQiOiAxNTE2MjM5MDIyLCAiZXhwIjogMTUxNjI0NzAyMiwgInNkX +2RpZ2VzdF9kZXJpdmF0aW9uX2FsZyI6ICJzaGEtMjU2In0.C004tnalDWXrWO9G0lfrP +NFgfsXCc0QdnpOmJx_NPdDuOeGTLQ2sQg-ozrqNbvOqp08tt7DG4kUpkPvjdd-nXOiGw +GP27JoVgnNFfCzC_gwG4w7GtiDnurExDF9tPcUfR1zzIxca8xZfQCIoVPN_6Dc5K6zs4 +bRFcyrQWKewU-8eoJ-N9zXNKRSqEJcOYvndZzopW68dKa0pOxxBKbV1RzEG9HiJfV7_T +nKBt2teEOztwTIxpXYomGRbRqCIRbvZ5SR-lyaKk1GiZf3f0zQ9tDPv1LKC8QLb0lsDf +IvQ504JVxfkepx4zGwf-xOzCbtaXk6_f86-0eUdmXaIhgpcHA~ \ No newline at end of file diff --git a/examples/address_only_structured/disclosures.md b/examples/address_only_structured/disclosures.md new file mode 100644 index 00000000..4e487483 --- /dev/null +++ b/examples/address_only_structured/disclosures.md @@ -0,0 +1,58 @@ +__Disclosure for `street_address`:__ + +``` +WyI0d3dqUzlyMm4tblBxdzNpTHR0TkFBIiwgInN0cmVldF9hZGRyZXNzIiwgIlNjaHVs +c3RyLiAxMiJd +``` + +Contents: + +``` +["4wwjS9r2n-nPqw3iLttNAA", "street_address", "Schulstr. 12"] +``` + +SHA-256 Hash: `pEtkKwoFK_JHN7yNby0Lc_Jc10BAxCm5yXJjDbVehvU` + +__Disclosure for `locality`:__ + +``` +WyJXcEtIQmVTa3A5U2MyNVV4a1F1RmNRIiwgImxvY2FsaXR5IiwgIlNjaHVscGZvcnRh +Il0 +``` + +Contents: + +``` +["WpKHBeSkp9Sc25UxkQuFcQ", "locality", "Schulpforta"] +``` + +SHA-256 Hash: `nTzPZ3Q68z1Ko_9ao9LK0mSYXY5gY6UG6KEkQ_BdqU0` + +__Disclosure for `region`:__ + +``` +WyIzSl9xWGctdUwxYzdtN1FoT0hUNTJnIiwgInJlZ2lvbiIsICJTYWNoc2VuLUFuaGFs +dCJd +``` + +Contents: + +``` +["3J_qXg-uL1c7m7QhOHT52g", "region", "Sachsen-Anhalt"] +``` + +SHA-256 Hash: `9-VdSnvRTZNDo-4Bxcp3X-V9VtLOCRUkR6oLWZQl81I` + +__Disclosure for `country`:__ + +``` +WyIwN2U3bWY2YWpTUDJjZkQ3NmJCZE93IiwgImNvdW50cnkiLCAiREUiXQ +``` + +Contents: + +``` +["07e7mf6ajSP2cfD76bBdOw", "country", "DE"] +``` + +SHA-256 Hash: `7pHe1uQ5uSClgAxXdG0E6dKnBgXcxEO1zvoQO9E5Lr4` \ No newline at end of file diff --git a/examples/address_only_structured/hb_jwt_serialized.txt b/examples/address_only_structured/hb_jwt_serialized.txt new file mode 100644 index 00000000..e69de29b diff --git a/examples/address_only_structured/sd_jwt_payload.json b/examples/address_only_structured/sd_jwt_payload.json new file mode 100644 index 00000000..c53d9780 --- /dev/null +++ b/examples/address_only_structured/sd_jwt_payload.json @@ -0,0 +1,15 @@ +{ + "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", + "address": { + "_sd": [ + "7pHe1uQ5uSClgAxXdG0E6dKnBgXcxEO1zvoQO9E5Lr4", + "9-VdSnvRTZNDo-4Bxcp3X-V9VtLOCRUkR6oLWZQl81I", + "nTzPZ3Q68z1Ko_9ao9LK0mSYXY5gY6UG6KEkQ_BdqU0", + "pEtkKwoFK_JHN7yNby0Lc_Jc10BAxCm5yXJjDbVehvU" + ] + }, + "iss": "https://example.com/issuer", + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256" +} \ No newline at end of file diff --git a/examples/address_only_structured/sd_jwt_serialized.txt b/examples/address_only_structured/sd_jwt_serialized.txt new file mode 100644 index 00000000..cf57243a --- /dev/null +++ b/examples/address_only_structured/sd_jwt_serialized.txt @@ -0,0 +1,14 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJzdWIiOiAiNmM1YzBhNDktYjU4OS00MzFkLWJhZ +TctMjE5MTIyYTllYzJjIiwgImFkZHJlc3MiOiB7Il9zZCI6IFsiN3BIZTF1UTV1U0NsZ +0F4WGRHMEU2ZEtuQmdYY3hFTzF6dm9RTzlFNUxyNCIsICI5LVZkU252UlRaTkRvLTRCe +GNwM1gtVjlWdExPQ1JVa1I2b0xXWlFsODFJIiwgIm5UelBaM1E2OHoxS29fOWFvOUxLM +G1TWVhZNWdZNlVHNktFa1FfQmRxVTAiLCAicEV0a0t3b0ZLX0pITjd5TmJ5MExjX0pjM +TBCQXhDbTV5WEpqRGJWZWh2VSJdfSwgImlzcyI6ICJodHRwczovL2V4YW1wbGUuY29tL +2lzc3VlciIsICJpYXQiOiAxNTE2MjM5MDIyLCAiZXhwIjogMTUxNjI0NzAyMiwgInNkX +2RpZ2VzdF9kZXJpdmF0aW9uX2FsZyI6ICJzaGEtMjU2In0.C004tnalDWXrWO9G0lfrP +NFgfsXCc0QdnpOmJx_NPdDuOeGTLQ2sQg-ozrqNbvOqp08tt7DG4kUpkPvjdd-nXOiGw +GP27JoVgnNFfCzC_gwG4w7GtiDnurExDF9tPcUfR1zzIxca8xZfQCIoVPN_6Dc5K6zs4 +bRFcyrQWKewU-8eoJ-N9zXNKRSqEJcOYvndZzopW68dKa0pOxxBKbV1RzEG9HiJfV7_T +nKBt2teEOztwTIxpXYomGRbRqCIRbvZ5SR-lyaKk1GiZf3f0zQ9tDPv1LKC8QLb0lsDf +IvQ504JVxfkepx4zGwf-xOzCbtaXk6_f86-0eUdmXaIhgpcHA \ No newline at end of file diff --git a/examples/address_only_structured/user_claims.json b/examples/address_only_structured/user_claims.json new file mode 100644 index 00000000..2ef9fb89 --- /dev/null +++ b/examples/address_only_structured/user_claims.json @@ -0,0 +1,9 @@ +{ + "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", + "address": { + "street_address": "Schulstr. 12", + "locality": "Schulpforta", + "region": "Sachsen-Anhalt", + "country": "DE" + } +} \ No newline at end of file diff --git a/examples/address_only_structured/verified_contents.json b/examples/address_only_structured/verified_contents.json new file mode 100644 index 00000000..e7634838 --- /dev/null +++ b/examples/address_only_structured/verified_contents.json @@ -0,0 +1,8 @@ +{ + "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", + "address": {}, + "iss": "https://example.com/issuer", + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256" +} \ No newline at end of file diff --git a/examples/address_only_structured_one_open/combined_issuance.txt b/examples/address_only_structured_one_open/combined_issuance.txt new file mode 100644 index 00000000..61822bf3 --- /dev/null +++ b/examples/address_only_structured_one_open/combined_issuance.txt @@ -0,0 +1,17 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJzdWIiOiAiNmM1YzBhNDktYjU4OS00MzFkLWJhZ +TctMjE5MTIyYTllYzJjIiwgImFkZHJlc3MiOiB7Il9zZCI6IFsiVG9EOWZTTkdvX1NPQ +25UTTByMEFhR2pkSEVJaDRkb1hpbkNrS2pSMmZrNCIsICJiSVBuZnZ0ZzlRUVNHZDdXO +XNybllPVlRLLXNOVUZ6OWtyMUpzN1hhVTRFIiwgInhXcTQ3bGtrRy1LNUNZZmNXdEhxd +2k5Q2JMOUxDUDNxOHY1WXNTbG1vVVEiXSwgImNvdW50cnkiOiAiREUifSwgImlzcyI6I +CJodHRwczovL2V4YW1wbGUuY29tL2lzc3VlciIsICJpYXQiOiAxNTE2MjM5MDIyLCAiZ +XhwIjogMTUxNjI0NzAyMiwgInNkX2RpZ2VzdF9kZXJpdmF0aW9uX2FsZyI6ICJzaGEtM +jU2In0.ZPD6NQ4eXSj6kcvvzLpYSfSBVaIsqKA7jvYnCQJQh1L6kMRI7_7kWOvU_XmdQ +HTIvsDV1QGGdes3Wh0OyUms90GXxouqVgJyg-_krPcJC57PmJYgxCRV5d9VWqjep99Rc +UpBjs63_6W8hdhZpj7otO5dHwTr5i-eD-5IRa9zLgXeaVE86GloL13Yn5MTxWJI1AJl2 +BdumqBMTPLtsbkF5q_XhMUqI7V_m9Em4qxvRqH1BWFRq614VJxZ5OXYtfSQkvYyfssv6 +OG5Wr1e8RGfyuLQYuNsydH5SHjkq5Wfwj7Zc-jYdjNh4MEHSdpUocG7lpp-9wKBc0mDK +VB3EOAMPQ~WyJkTWZMOXBrOVFXUUNPajJRVDA0c21BIiwgInN0cmVldF9hZGRyZXNzIi +wgIlNjaHVsc3RyLiAxMiJd~WyJsNklkRC1FeDZ5eHFGck9DUjFNbktBIiwgImxvY2Fsa +XR5IiwgIlNjaHVscGZvcnRhIl0~WyI3WFB4R21ldC1vaWFoOEhDdmU3bTJBIiwgInJlZ +2lvbiIsICJTYWNoc2VuLUFuaGFsdCJd \ No newline at end of file diff --git a/examples/address_only_structured_one_open/combined_presentation.txt b/examples/address_only_structured_one_open/combined_presentation.txt new file mode 100644 index 00000000..bd95d0ec --- /dev/null +++ b/examples/address_only_structured_one_open/combined_presentation.txt @@ -0,0 +1,14 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJzdWIiOiAiNmM1YzBhNDktYjU4OS00MzFkLWJhZ +TctMjE5MTIyYTllYzJjIiwgImFkZHJlc3MiOiB7Il9zZCI6IFsiVG9EOWZTTkdvX1NPQ +25UTTByMEFhR2pkSEVJaDRkb1hpbkNrS2pSMmZrNCIsICJiSVBuZnZ0ZzlRUVNHZDdXO +XNybllPVlRLLXNOVUZ6OWtyMUpzN1hhVTRFIiwgInhXcTQ3bGtrRy1LNUNZZmNXdEhxd +2k5Q2JMOUxDUDNxOHY1WXNTbG1vVVEiXSwgImNvdW50cnkiOiAiREUifSwgImlzcyI6I +CJodHRwczovL2V4YW1wbGUuY29tL2lzc3VlciIsICJpYXQiOiAxNTE2MjM5MDIyLCAiZ +XhwIjogMTUxNjI0NzAyMiwgInNkX2RpZ2VzdF9kZXJpdmF0aW9uX2FsZyI6ICJzaGEtM +jU2In0.ZPD6NQ4eXSj6kcvvzLpYSfSBVaIsqKA7jvYnCQJQh1L6kMRI7_7kWOvU_XmdQ +HTIvsDV1QGGdes3Wh0OyUms90GXxouqVgJyg-_krPcJC57PmJYgxCRV5d9VWqjep99Rc +UpBjs63_6W8hdhZpj7otO5dHwTr5i-eD-5IRa9zLgXeaVE86GloL13Yn5MTxWJI1AJl2 +BdumqBMTPLtsbkF5q_XhMUqI7V_m9Em4qxvRqH1BWFRq614VJxZ5OXYtfSQkvYyfssv6 +OG5Wr1e8RGfyuLQYuNsydH5SHjkq5Wfwj7Zc-jYdjNh4MEHSdpUocG7lpp-9wKBc0mDK +VB3EOAMPQ~ \ No newline at end of file diff --git a/examples/address_only_structured_one_open/disclosures.md b/examples/address_only_structured_one_open/disclosures.md new file mode 100644 index 00000000..3af70c31 --- /dev/null +++ b/examples/address_only_structured_one_open/disclosures.md @@ -0,0 +1,44 @@ +__Disclosure for `street_address`:__ + +``` +WyJkTWZMOXBrOVFXUUNPajJRVDA0c21BIiwgInN0cmVldF9hZGRyZXNzIiwgIlNjaHVs +c3RyLiAxMiJd +``` + +Contents: + +``` +["dMfL9pk9QWQCOj2QT04smA", "street_address", "Schulstr. 12"] +``` + +SHA-256 Hash: `ToD9fSNGo_SOCnTM0r0AaGjdHEIh4doXinCkKjR2fk4` + +__Disclosure for `locality`:__ + +``` +WyJsNklkRC1FeDZ5eHFGck9DUjFNbktBIiwgImxvY2FsaXR5IiwgIlNjaHVscGZvcnRh +Il0 +``` + +Contents: + +``` +["l6IdD-Ex6yxqFrOCR1MnKA", "locality", "Schulpforta"] +``` + +SHA-256 Hash: `bIPnfvtg9QQSGd7W9srnYOVTK-sNUFz9kr1Js7XaU4E` + +__Disclosure for `region`:__ + +``` +WyI3WFB4R21ldC1vaWFoOEhDdmU3bTJBIiwgInJlZ2lvbiIsICJTYWNoc2VuLUFuaGFs +dCJd +``` + +Contents: + +``` +["7XPxGmet-oiah8HCve7m2A", "region", "Sachsen-Anhalt"] +``` + +SHA-256 Hash: `xWq47lkkG-K5CYfcWtHqwi9CbL9LCP3q8v5YsSlmoUQ` \ No newline at end of file diff --git a/examples/address_only_structured_one_open/hb_jwt_serialized.txt b/examples/address_only_structured_one_open/hb_jwt_serialized.txt new file mode 100644 index 00000000..e69de29b diff --git a/examples/address_only_structured_one_open/sd_jwt_payload.json b/examples/address_only_structured_one_open/sd_jwt_payload.json new file mode 100644 index 00000000..55090b35 --- /dev/null +++ b/examples/address_only_structured_one_open/sd_jwt_payload.json @@ -0,0 +1,15 @@ +{ + "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", + "address": { + "_sd": [ + "ToD9fSNGo_SOCnTM0r0AaGjdHEIh4doXinCkKjR2fk4", + "bIPnfvtg9QQSGd7W9srnYOVTK-sNUFz9kr1Js7XaU4E", + "xWq47lkkG-K5CYfcWtHqwi9CbL9LCP3q8v5YsSlmoUQ" + ], + "country": "DE" + }, + "iss": "https://example.com/issuer", + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256" +} \ No newline at end of file diff --git a/examples/address_only_structured_one_open/sd_jwt_serialized.txt b/examples/address_only_structured_one_open/sd_jwt_serialized.txt new file mode 100644 index 00000000..ef4b493a --- /dev/null +++ b/examples/address_only_structured_one_open/sd_jwt_serialized.txt @@ -0,0 +1,14 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJzdWIiOiAiNmM1YzBhNDktYjU4OS00MzFkLWJhZ +TctMjE5MTIyYTllYzJjIiwgImFkZHJlc3MiOiB7Il9zZCI6IFsiVG9EOWZTTkdvX1NPQ +25UTTByMEFhR2pkSEVJaDRkb1hpbkNrS2pSMmZrNCIsICJiSVBuZnZ0ZzlRUVNHZDdXO +XNybllPVlRLLXNOVUZ6OWtyMUpzN1hhVTRFIiwgInhXcTQ3bGtrRy1LNUNZZmNXdEhxd +2k5Q2JMOUxDUDNxOHY1WXNTbG1vVVEiXSwgImNvdW50cnkiOiAiREUifSwgImlzcyI6I +CJodHRwczovL2V4YW1wbGUuY29tL2lzc3VlciIsICJpYXQiOiAxNTE2MjM5MDIyLCAiZ +XhwIjogMTUxNjI0NzAyMiwgInNkX2RpZ2VzdF9kZXJpdmF0aW9uX2FsZyI6ICJzaGEtM +jU2In0.ZPD6NQ4eXSj6kcvvzLpYSfSBVaIsqKA7jvYnCQJQh1L6kMRI7_7kWOvU_XmdQ +HTIvsDV1QGGdes3Wh0OyUms90GXxouqVgJyg-_krPcJC57PmJYgxCRV5d9VWqjep99Rc +UpBjs63_6W8hdhZpj7otO5dHwTr5i-eD-5IRa9zLgXeaVE86GloL13Yn5MTxWJI1AJl2 +BdumqBMTPLtsbkF5q_XhMUqI7V_m9Em4qxvRqH1BWFRq614VJxZ5OXYtfSQkvYyfssv6 +OG5Wr1e8RGfyuLQYuNsydH5SHjkq5Wfwj7Zc-jYdjNh4MEHSdpUocG7lpp-9wKBc0mDK +VB3EOAMPQ \ No newline at end of file diff --git a/examples/address_only_structured_one_open/user_claims.json b/examples/address_only_structured_one_open/user_claims.json new file mode 100644 index 00000000..2ef9fb89 --- /dev/null +++ b/examples/address_only_structured_one_open/user_claims.json @@ -0,0 +1,9 @@ +{ + "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", + "address": { + "street_address": "Schulstr. 12", + "locality": "Schulpforta", + "region": "Sachsen-Anhalt", + "country": "DE" + } +} \ No newline at end of file diff --git a/examples/address_only_structured_one_open/verified_contents.json b/examples/address_only_structured_one_open/verified_contents.json new file mode 100644 index 00000000..a50971f8 --- /dev/null +++ b/examples/address_only_structured_one_open/verified_contents.json @@ -0,0 +1,10 @@ +{ + "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", + "address": { + "country": "DE" + }, + "iss": "https://example.com/issuer", + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256" +} \ No newline at end of file diff --git a/examples/complex_eidas/combined_issuance.txt b/examples/complex_eidas/combined_issuance.txt new file mode 100644 index 00000000..f6ed66c0 --- /dev/null +++ b/examples/complex_eidas/combined_issuance.txt @@ -0,0 +1,41 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbIlV4WWp4OGJZQTg2UkdBSE9qUmpud +Hp0SjE2ZG9JTDl0QUZSRUZlUUNLejAiXSwgInZlcmlmaWVkX2NsYWltcyI6IHsidmVya +WZpY2F0aW9uIjogeyJfc2QiOiBbIk9LYnBvT19SR3llRjFGUF9YNFJJNjd5dE9XNU9PN +XBqOGlBRUhQUG1HelEiXSwgInRydXN0X2ZyYW1ld29yayI6ICJlaWRhcyIsICJldmlkZ +W5jZSI6IFt7Il9zZCI6IFsiRHd0T3RZbFBjelAzajBDQUdtd0tfc0cxUFMxNUJlUEVXZ +W5ub3pKdG5jNCIsICJONVgtY21kdUFfaEVpRy0yLVRGSFdsWUZQbXVQQWhnTVNEQWQ1L +V8wQU53Il0sICJkb2N1bWVudCI6IHsiX3NkIjogWyIzMGpkYUE5T2ZHcGEzVGo1WEpid +FdpVnJsNGk2Nm15MjZtT25iSXV3WTB3IiwgIkZzdTFYTDlOVzF0SFZweXRyNDRqRmJYd +2RoWDFhdk5sUkFyT3pSUmo2RlEiLCAiUFB6dU96SXJibmVWSjQ5LUszemdxT1BESnZGZ +2JpcEdjVTFQc2cyQnFQWSIsICJ3blh1QmFZM1pWSHk3cWI4emh5VnFHanM1Wnl3dXdmR +HhKM3BJYzF2SFFvIl0sICJpc3N1ZXIiOiB7Il9zZCI6IFsiUy1LOFhIUTUtVGlwV2VXS +FpOY0tMZTRneXFvVFc4UVBsNlhYZG1uQlBpSSIsICJWQnNWWHhLSGJDNy02ZFZaY2RGZ +zlrVTd5Q05zUFdvYjRiRGNWYlpCNC13Il19fX1dfSwgImNsYWltcyI6IHsiX3NkIjogW +yI5QzNCckNNRHgyenNxbjlJaE43c2lCSlhTeVp2dkpvRFl6R3N5SGhoblpVIiwgIlFWd +E5qR3BMdGQ3TnBKZ2ZqQlRNaEt3d2VmNEVFMlZ1RUUtakJLUUp1ZUEiLCAielh3b2dtT +kw0YUpyaEw3T2lrZkpDcERZcFBOU0ExU3lGY2lESWNXUDE0ayJdLCAicGVyc29uX3Vua +XF1ZV9pZGVudGlmaWVyIjogIlRJTklULWZjMGQ5Njg0LTFiZjAtNDIyMC05NjQyLThmZ +TY1MmM4YzA0MCIsICJnaXZlbl9uYW1lIjogIlJhZmZhZWxsbyIsICJmYW1pbHlfbmFtZ +SI6ICJNYXNjZXR0aSIsICJuYXRpb25hbGl0aWVzIjogWyJJVCJdfX0sICJpc3MiOiAia +HR0cHM6Ly9leGFtcGxlLmNvbS9pc3N1ZXIiLCAiaWF0IjogMTUxNjIzOTAyMiwgImV4c +CI6IDE1MTYyNDcwMjIsICJzZF9kaWdlc3RfZGVyaXZhdGlvbl9hbGciOiAic2hhLTI1N +iJ9.lEAtbw2O4tySuZsIgSCUms7ilkgXsQFB9-5-V_W48bAfZd0faLlYWg7Mnf4XIDe8 +DybFzDntugxcEPNMycg9ssz9p83S5nRuEwCWHSXWWlBFAESR9zsvlgJ6zj1EE4avT7w3 +ZZcrbRnVyyzSeHl-8SdeFbhCldXVFb-wuwAE8v1vcPp3g94jq8IwhbWOhCql3KCIeuk8 +nU4izhd2ffpzrQ5nKL6oqHr3BQYeaoS2FgyBsGYdcfAJCMk-Iv_n_NfCirTWNYx-_Sl2 +QpJRJQ1iaYJ4Kqv0VR_m-acKsnvKhOVWInkZsppsMBYybAzeJS7KZE0ETUpLG2IHDqxD +G_IkcA~WyJrTjdPWk1fRElOdDlHb1FkUHFmczRRIiwgImFzc3VyYW5jZV9sZXZlbCIsI +CJoaWdoIl0~WyI5T1NRWjF4eFZxNERCNWlucFlzMkpnIiwgInR5cGUiLCAiZG9jdW1lb +nQiXQ~WyIwcTlpbzFyRmpqZWJicEE2TmVlNjJRIiwgInRpbWUiLCAiMjAyMi0wNC0yMl +QxMTozMFoiXQ~WyJYTzZJX1RtTkhVR3J2REZ6aWQtcjdBIiwgInR5cGUiLCAiaWRjYXJ +kIl0~WyJxODUwNXQtYVpCUHV6TDJwdmhKLUVRIiwgIm5hbWUiLCAiY19kNjEyIl0~WyJ +aNkF0aDg1ZGNXUzd0RjhRQnI1MGt3IiwgImNvdW50cnkiLCAiSVQiXQ~WyJMei0zUm9Y +dW5iSDJvTFpYTlJicUx3IiwgIm51bWJlciIsICIxNTQ1NTQiXQ~WyJ4SkhzUTZ3YWlXS +jVVUXB2dVVxNzN3IiwgImRhdGVfb2ZfaXNzdWFuY2UiLCAiMjAyMS0wMy0yMyJd~WyJs +VFB2T2FfdXk1VGEtSF83NURSVE5RIiwgImRhdGVfb2ZfZXhwaXJ5IiwgIjIwMzEtMDMt +MjIiXQ~WyJIOUZBUlZiTUdSSGJrY3l6ODhaX2J3IiwgImRhdGVfb2ZfYmlydGgiLCAiM +TkyMi0wMy0xMyJd~WyJ5eHNuRnJ6V3Y0VUExMFhLR1EyMTB3IiwgImdlbmRlciIsICJN +Il0~WyI1cF9vamx3S3gwUjZSNjFkS0xOOENRIiwgInBsYWNlX29mX2JpcnRoIiwgeyJj +b3VudHJ5IjogIklUIiwgImxvY2FsaXR5IjogIkZpcmVuemUifV0~WyJlSHhBZWQ5THNy +bG5ncGhKRE5aSXZnIiwgImJpcnRoX21pZGRsZV9uYW1lIiwgIkxlbGxvIl0 \ No newline at end of file diff --git a/examples/complex_eidas/combined_presentation.txt b/examples/complex_eidas/combined_presentation.txt new file mode 100644 index 00000000..724e3c77 --- /dev/null +++ b/examples/complex_eidas/combined_presentation.txt @@ -0,0 +1,30 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbIlV4WWp4OGJZQTg2UkdBSE9qUmpud +Hp0SjE2ZG9JTDl0QUZSRUZlUUNLejAiXSwgInZlcmlmaWVkX2NsYWltcyI6IHsidmVya +WZpY2F0aW9uIjogeyJfc2QiOiBbIk9LYnBvT19SR3llRjFGUF9YNFJJNjd5dE9XNU9PN +XBqOGlBRUhQUG1HelEiXSwgInRydXN0X2ZyYW1ld29yayI6ICJlaWRhcyIsICJldmlkZ +W5jZSI6IFt7Il9zZCI6IFsiRHd0T3RZbFBjelAzajBDQUdtd0tfc0cxUFMxNUJlUEVXZ +W5ub3pKdG5jNCIsICJONVgtY21kdUFfaEVpRy0yLVRGSFdsWUZQbXVQQWhnTVNEQWQ1L +V8wQU53Il0sICJkb2N1bWVudCI6IHsiX3NkIjogWyIzMGpkYUE5T2ZHcGEzVGo1WEpid +FdpVnJsNGk2Nm15MjZtT25iSXV3WTB3IiwgIkZzdTFYTDlOVzF0SFZweXRyNDRqRmJYd +2RoWDFhdk5sUkFyT3pSUmo2RlEiLCAiUFB6dU96SXJibmVWSjQ5LUszemdxT1BESnZGZ +2JpcEdjVTFQc2cyQnFQWSIsICJ3blh1QmFZM1pWSHk3cWI4emh5VnFHanM1Wnl3dXdmR +HhKM3BJYzF2SFFvIl0sICJpc3N1ZXIiOiB7Il9zZCI6IFsiUy1LOFhIUTUtVGlwV2VXS +FpOY0tMZTRneXFvVFc4UVBsNlhYZG1uQlBpSSIsICJWQnNWWHhLSGJDNy02ZFZaY2RGZ +zlrVTd5Q05zUFdvYjRiRGNWYlpCNC13Il19fX1dfSwgImNsYWltcyI6IHsiX3NkIjogW +yI5QzNCckNNRHgyenNxbjlJaE43c2lCSlhTeVp2dkpvRFl6R3N5SGhoblpVIiwgIlFWd +E5qR3BMdGQ3TnBKZ2ZqQlRNaEt3d2VmNEVFMlZ1RUUtakJLUUp1ZUEiLCAielh3b2dtT +kw0YUpyaEw3T2lrZkpDcERZcFBOU0ExU3lGY2lESWNXUDE0ayJdLCAicGVyc29uX3Vua +XF1ZV9pZGVudGlmaWVyIjogIlRJTklULWZjMGQ5Njg0LTFiZjAtNDIyMC05NjQyLThmZ +TY1MmM4YzA0MCIsICJnaXZlbl9uYW1lIjogIlJhZmZhZWxsbyIsICJmYW1pbHlfbmFtZ +SI6ICJNYXNjZXR0aSIsICJuYXRpb25hbGl0aWVzIjogWyJJVCJdfX0sICJpc3MiOiAia +HR0cHM6Ly9leGFtcGxlLmNvbS9pc3N1ZXIiLCAiaWF0IjogMTUxNjIzOTAyMiwgImV4c +CI6IDE1MTYyNDcwMjIsICJzZF9kaWdlc3RfZGVyaXZhdGlvbl9hbGciOiAic2hhLTI1N +iJ9.lEAtbw2O4tySuZsIgSCUms7ilkgXsQFB9-5-V_W48bAfZd0faLlYWg7Mnf4XIDe8 +DybFzDntugxcEPNMycg9ssz9p83S5nRuEwCWHSXWWlBFAESR9zsvlgJ6zj1EE4avT7w3 +ZZcrbRnVyyzSeHl-8SdeFbhCldXVFb-wuwAE8v1vcPp3g94jq8IwhbWOhCql3KCIeuk8 +nU4izhd2ffpzrQ5nKL6oqHr3BQYeaoS2FgyBsGYdcfAJCMk-Iv_n_NfCirTWNYx-_Sl2 +QpJRJQ1iaYJ4Kqv0VR_m-acKsnvKhOVWInkZsppsMBYybAzeJS7KZE0ETUpLG2IHDqxD +G_IkcA~WyJ5eHNuRnJ6V3Y0VUExMFhLR1EyMTB3IiwgImdlbmRlciIsICJNIl0~WyI1c +F9vamx3S3gwUjZSNjFkS0xOOENRIiwgInBsYWNlX29mX2JpcnRoIiwgeyJjb3VudHJ5I +jogIklUIiwgImxvY2FsaXR5IjogIkZpcmVuemUifV0~ \ No newline at end of file diff --git a/examples/complex_eidas/disclosures.md b/examples/complex_eidas/disclosures.md new file mode 100644 index 00000000..51f6c480 --- /dev/null +++ b/examples/complex_eidas/disclosures.md @@ -0,0 +1,189 @@ +__Disclosure for `assurance_level`:__ + +``` +WyJrTjdPWk1fRElOdDlHb1FkUHFmczRRIiwgImFzc3VyYW5jZV9sZXZlbCIsICJoaWdo +Il0 +``` + +Contents: + +``` +["kN7OZM_DINt9GoQdPqfs4Q", "assurance_level", "high"] +``` + +SHA-256 Hash: `OKbpoO_RGyeF1FP_X4RI67ytOW5OO5pj8iAEHPPmGzQ` + +__Disclosure for `type`:__ + +``` +WyI5T1NRWjF4eFZxNERCNWlucFlzMkpnIiwgInR5cGUiLCAiZG9jdW1lbnQiXQ +``` + +Contents: + +``` +["9OSQZ1xxVq4DB5inpYs2Jg", "type", "document"] +``` + +SHA-256 Hash: `DwtOtYlPczP3j0CAGmwK_sG1PS15BePEWennozJtnc4` + +__Disclosure for `time`:__ + +``` +WyIwcTlpbzFyRmpqZWJicEE2TmVlNjJRIiwgInRpbWUiLCAiMjAyMi0wNC0yMlQxMToz +MFoiXQ +``` + +Contents: + +``` +["0q9io1rFjjebbpA6Nee62Q", "time", "2022-04-22T11:30Z"] +``` + +SHA-256 Hash: `N5X-cmduA_hEiG-2-TFHWlYFPmuPAhgMSDAd5-_0ANw` + +__Disclosure for `type`:__ + +``` +WyJYTzZJX1RtTkhVR3J2REZ6aWQtcjdBIiwgInR5cGUiLCAiaWRjYXJkIl0 +``` + +Contents: + +``` +["XO6I_TmNHUGrvDFzid-r7A", "type", "idcard"] +``` + +SHA-256 Hash: `wnXuBaY3ZVHy7qb8zhyVqGjs5ZywuwfDxJ3pIc1vHQo` + +__Disclosure for `name`:__ + +``` +WyJxODUwNXQtYVpCUHV6TDJwdmhKLUVRIiwgIm5hbWUiLCAiY19kNjEyIl0 +``` + +Contents: + +``` +["q8505t-aZBPuzL2pvhJ-EQ", "name", "c_d612"] +``` + +SHA-256 Hash: `VBsVXxKHbC7-6dVZcdFg9kU7yCNsPWob4bDcVbZB4-w` + +__Disclosure for `country`:__ + +``` +WyJaNkF0aDg1ZGNXUzd0RjhRQnI1MGt3IiwgImNvdW50cnkiLCAiSVQiXQ +``` + +Contents: + +``` +["Z6Ath85dcWS7tF8QBr50kw", "country", "IT"] +``` + +SHA-256 Hash: `S-K8XHQ5-TipWeWHZNcKLe4gyqoTW8QPl6XXdmnBPiI` + +__Disclosure for `number`:__ + +``` +WyJMei0zUm9YdW5iSDJvTFpYTlJicUx3IiwgIm51bWJlciIsICIxNTQ1NTQiXQ +``` + +Contents: + +``` +["Lz-3RoXunbH2oLZXNRbqLw", "number", "154554"] +``` + +SHA-256 Hash: `30jdaA9OfGpa3Tj5XJbtWiVrl4i66my26mOnbIuwY0w` + +__Disclosure for `date_of_issuance`:__ + +``` +WyJ4SkhzUTZ3YWlXSjVVUXB2dVVxNzN3IiwgImRhdGVfb2ZfaXNzdWFuY2UiLCAiMjAy +MS0wMy0yMyJd +``` + +Contents: + +``` +["xJHsQ6waiWJ5UQpvuUq73w", "date_of_issuance", "2021-03-23"] +``` + +SHA-256 Hash: `PPzuOzIrbneVJ49-K3zgqOPDJvFgbipGcU1Psg2BqPY` + +__Disclosure for `date_of_expiry`:__ + +``` +WyJsVFB2T2FfdXk1VGEtSF83NURSVE5RIiwgImRhdGVfb2ZfZXhwaXJ5IiwgIjIwMzEt +MDMtMjIiXQ +``` + +Contents: + +``` +["lTPvOa_uy5Ta-H_75DRTNQ", "date_of_expiry", "2031-03-22"] +``` + +SHA-256 Hash: `Fsu1XL9NW1tHVpytr44jFbXwdhX1avNlRArOzRRj6FQ` + +__Disclosure for `date_of_birth`:__ + +``` +WyJIOUZBUlZiTUdSSGJrY3l6ODhaX2J3IiwgImRhdGVfb2ZfYmlydGgiLCAiMTkyMi0w +My0xMyJd +``` + +Contents: + +``` +["H9FARVbMGRHbkcyz88Z_bw", "date_of_birth", "1922-03-13"] +``` + +SHA-256 Hash: `zXwogmNL4aJrhL7OikfJCpDYpPNSA1SyFciDIcWP14k` + +__Disclosure for `gender`:__ + +``` +WyJ5eHNuRnJ6V3Y0VUExMFhLR1EyMTB3IiwgImdlbmRlciIsICJNIl0 +``` + +Contents: + +``` +["yxsnFrzWv4UA10XKGQ210w", "gender", "M"] +``` + +SHA-256 Hash: `9C3BrCMDx2zsqn9IhN7siBJXSyZvvJoDYzGsyHhhnZU` + +__Disclosure for `place_of_birth`:__ + +``` +WyI1cF9vamx3S3gwUjZSNjFkS0xOOENRIiwgInBsYWNlX29mX2JpcnRoIiwgeyJjb3Vu +dHJ5IjogIklUIiwgImxvY2FsaXR5IjogIkZpcmVuemUifV0 +``` + +Contents: + +``` +["5p_ojlwKx0R6R61dKLN8CQ", "place_of_birth", {"country": "IT", +"locality": "Firenze"}] +``` + +SHA-256 Hash: `QVtNjGpLtd7NpJgfjBTMhKwwef4EE2VuEE-jBKQJueA` + +__Disclosure for `birth_middle_name`:__ + +``` +WyJlSHhBZWQ5THNybG5ncGhKRE5aSXZnIiwgImJpcnRoX21pZGRsZV9uYW1lIiwgIkxl +bGxvIl0 +``` + +Contents: + +``` +["eHxAed9LsrlngphJDNZIvg", "birth_middle_name", "Lello"] +``` + +SHA-256 Hash: `UxYjx8bYA86RGAHOjRjntztJ16doIL9tAFREFeQCKz0` \ No newline at end of file diff --git a/examples/complex_eidas/hb_jwt_serialized.txt b/examples/complex_eidas/hb_jwt_serialized.txt new file mode 100644 index 00000000..e69de29b diff --git a/examples/complex_eidas/sd_jwt_payload.json b/examples/complex_eidas/sd_jwt_payload.json new file mode 100644 index 00000000..1339b4dd --- /dev/null +++ b/examples/complex_eidas/sd_jwt_payload.json @@ -0,0 +1,53 @@ +{ + "_sd": [ + "UxYjx8bYA86RGAHOjRjntztJ16doIL9tAFREFeQCKz0" + ], + "verified_claims": { + "verification": { + "_sd": [ + "OKbpoO_RGyeF1FP_X4RI67ytOW5OO5pj8iAEHPPmGzQ" + ], + "trust_framework": "eidas", + "evidence": [ + { + "_sd": [ + "DwtOtYlPczP3j0CAGmwK_sG1PS15BePEWennozJtnc4", + "N5X-cmduA_hEiG-2-TFHWlYFPmuPAhgMSDAd5-_0ANw" + ], + "document": { + "_sd": [ + "30jdaA9OfGpa3Tj5XJbtWiVrl4i66my26mOnbIuwY0w", + "Fsu1XL9NW1tHVpytr44jFbXwdhX1avNlRArOzRRj6FQ", + "PPzuOzIrbneVJ49-K3zgqOPDJvFgbipGcU1Psg2BqPY", + "wnXuBaY3ZVHy7qb8zhyVqGjs5ZywuwfDxJ3pIc1vHQo" + ], + "issuer": { + "_sd": [ + "S-K8XHQ5-TipWeWHZNcKLe4gyqoTW8QPl6XXdmnBPiI", + "VBsVXxKHbC7-6dVZcdFg9kU7yCNsPWob4bDcVbZB4-w" + ] + } + } + } + ] + }, + "claims": { + "_sd": [ + "9C3BrCMDx2zsqn9IhN7siBJXSyZvvJoDYzGsyHhhnZU", + "QVtNjGpLtd7NpJgfjBTMhKwwef4EE2VuEE-jBKQJueA", + "zXwogmNL4aJrhL7OikfJCpDYpPNSA1SyFciDIcWP14k" + ], + "person_unique_identifier": + "TINIT-fc0d9684-1bf0-4220-9642-8fe652c8c040", + "given_name": "Raffaello", + "family_name": "Mascetti", + "nationalities": [ + "IT" + ] + } + }, + "iss": "https://example.com/issuer", + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256" +} \ No newline at end of file diff --git a/examples/complex_eidas/sd_jwt_serialized.txt b/examples/complex_eidas/sd_jwt_serialized.txt new file mode 100644 index 00000000..922e8e06 --- /dev/null +++ b/examples/complex_eidas/sd_jwt_serialized.txt @@ -0,0 +1,28 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbIlV4WWp4OGJZQTg2UkdBSE9qUmpud +Hp0SjE2ZG9JTDl0QUZSRUZlUUNLejAiXSwgInZlcmlmaWVkX2NsYWltcyI6IHsidmVya +WZpY2F0aW9uIjogeyJfc2QiOiBbIk9LYnBvT19SR3llRjFGUF9YNFJJNjd5dE9XNU9PN +XBqOGlBRUhQUG1HelEiXSwgInRydXN0X2ZyYW1ld29yayI6ICJlaWRhcyIsICJldmlkZ +W5jZSI6IFt7Il9zZCI6IFsiRHd0T3RZbFBjelAzajBDQUdtd0tfc0cxUFMxNUJlUEVXZ +W5ub3pKdG5jNCIsICJONVgtY21kdUFfaEVpRy0yLVRGSFdsWUZQbXVQQWhnTVNEQWQ1L +V8wQU53Il0sICJkb2N1bWVudCI6IHsiX3NkIjogWyIzMGpkYUE5T2ZHcGEzVGo1WEpid +FdpVnJsNGk2Nm15MjZtT25iSXV3WTB3IiwgIkZzdTFYTDlOVzF0SFZweXRyNDRqRmJYd +2RoWDFhdk5sUkFyT3pSUmo2RlEiLCAiUFB6dU96SXJibmVWSjQ5LUszemdxT1BESnZGZ +2JpcEdjVTFQc2cyQnFQWSIsICJ3blh1QmFZM1pWSHk3cWI4emh5VnFHanM1Wnl3dXdmR +HhKM3BJYzF2SFFvIl0sICJpc3N1ZXIiOiB7Il9zZCI6IFsiUy1LOFhIUTUtVGlwV2VXS +FpOY0tMZTRneXFvVFc4UVBsNlhYZG1uQlBpSSIsICJWQnNWWHhLSGJDNy02ZFZaY2RGZ +zlrVTd5Q05zUFdvYjRiRGNWYlpCNC13Il19fX1dfSwgImNsYWltcyI6IHsiX3NkIjogW +yI5QzNCckNNRHgyenNxbjlJaE43c2lCSlhTeVp2dkpvRFl6R3N5SGhoblpVIiwgIlFWd +E5qR3BMdGQ3TnBKZ2ZqQlRNaEt3d2VmNEVFMlZ1RUUtakJLUUp1ZUEiLCAielh3b2dtT +kw0YUpyaEw3T2lrZkpDcERZcFBOU0ExU3lGY2lESWNXUDE0ayJdLCAicGVyc29uX3Vua +XF1ZV9pZGVudGlmaWVyIjogIlRJTklULWZjMGQ5Njg0LTFiZjAtNDIyMC05NjQyLThmZ +TY1MmM4YzA0MCIsICJnaXZlbl9uYW1lIjogIlJhZmZhZWxsbyIsICJmYW1pbHlfbmFtZ +SI6ICJNYXNjZXR0aSIsICJuYXRpb25hbGl0aWVzIjogWyJJVCJdfX0sICJpc3MiOiAia +HR0cHM6Ly9leGFtcGxlLmNvbS9pc3N1ZXIiLCAiaWF0IjogMTUxNjIzOTAyMiwgImV4c +CI6IDE1MTYyNDcwMjIsICJzZF9kaWdlc3RfZGVyaXZhdGlvbl9hbGciOiAic2hhLTI1N +iJ9.lEAtbw2O4tySuZsIgSCUms7ilkgXsQFB9-5-V_W48bAfZd0faLlYWg7Mnf4XIDe8 +DybFzDntugxcEPNMycg9ssz9p83S5nRuEwCWHSXWWlBFAESR9zsvlgJ6zj1EE4avT7w3 +ZZcrbRnVyyzSeHl-8SdeFbhCldXVFb-wuwAE8v1vcPp3g94jq8IwhbWOhCql3KCIeuk8 +nU4izhd2ffpzrQ5nKL6oqHr3BQYeaoS2FgyBsGYdcfAJCMk-Iv_n_NfCirTWNYx-_Sl2 +QpJRJQ1iaYJ4Kqv0VR_m-acKsnvKhOVWInkZsppsMBYybAzeJS7KZE0ETUpLG2IHDqxD +G_IkcA \ No newline at end of file diff --git a/examples/complex_eidas/user_claims.json b/examples/complex_eidas/user_claims.json new file mode 100644 index 00000000..2ac9d080 --- /dev/null +++ b/examples/complex_eidas/user_claims.json @@ -0,0 +1,40 @@ +{ + "verified_claims": { + "verification": { + "trust_framework": "eidas", + "assurance_level": "high", + "evidence": [ + { + "type": "document", + "time": "2022-04-22T11:30Z", + "document": { + "type": "idcard", + "issuer": { + "name": "c_d612", + "country": "IT" + }, + "number": "154554", + "date_of_issuance": "2021-03-23", + "date_of_expiry": "2031-03-22" + } + } + ] + }, + "claims": { + "person_unique_identifier": + "TINIT-fc0d9684-1bf0-4220-9642-8fe652c8c040", + "given_name": "Raffaello", + "family_name": "Mascetti", + "date_of_birth": "1922-03-13", + "gender": "M", + "place_of_birth": { + "country": "IT", + "locality": "Firenze" + }, + "nationalities": [ + "IT" + ] + } + }, + "birth_middle_name": "Lello" +} \ No newline at end of file diff --git a/examples/complex_eidas/verified_contents.json b/examples/complex_eidas/verified_contents.json new file mode 100644 index 00000000..14257c1a --- /dev/null +++ b/examples/complex_eidas/verified_contents.json @@ -0,0 +1,32 @@ +{ + "verified_claims": { + "verification": { + "trust_framework": "eidas", + "evidence": [ + { + "document": { + "issuer": {} + } + } + ] + }, + "claims": { + "person_unique_identifier": + "TINIT-fc0d9684-1bf0-4220-9642-8fe652c8c040", + "given_name": "Raffaello", + "family_name": "Mascetti", + "nationalities": [ + "IT" + ], + "gender": "M", + "place_of_birth": { + "country": "IT", + "locality": "Firenze" + } + } + }, + "iss": "https://example.com/issuer", + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256" +} \ No newline at end of file diff --git a/examples/complex_eidas_proposal/combined_issuance.txt b/examples/complex_eidas_proposal/combined_issuance.txt new file mode 100644 index 00000000..2db09d54 --- /dev/null +++ b/examples/complex_eidas_proposal/combined_issuance.txt @@ -0,0 +1,36 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbImpobWJwVEVzUU52bnpnc1IyRF9UV +lJsVkFtU016WU9iSWR5MTlVdmQ2MkEiXSwgInZlcmlmaWVkX2NsYWltcyI6IHsidmVya +WZpY2F0aW9uIjogeyJfc2QiOiBbIjJZUm1Va2x1SmhiZUV6TVEwcDRRSkNaWkh0MzhPO +VlwNXhReGotcy1vZjQiXSwgInRydXN0X2ZyYW1ld29yayI6ICJlaWRhcyIsICJldmlkZ +W5jZSI6IFt7Il9zZCI6IFsiT1NsaXpBQjJyZWFjQVFzMldONk45MXk4SnJvU1VqR0xlZ +mpib1ZjNjIyQSJdLCAicmVjb3JkIjogeyJfc2QiOiBbIkJTM3NfRU51ODJaLVdUOWpke +DFaWTl3ejVRTW5VcjFPZFM2bHI0U093YnciLCAiX3FiMVF3OTR5ZVM0dXpNSGVNSGpaS +DJuSF9ickR5TTdlVjVVZWt5M3hWWSIsICJpSmhlcGhhdXZNRmdIMTFxUzNBVDBHNGFFS +Xc4TnZnb1BPczVvTDE2eThvIl19fV19LCAiY2xhaW1zIjogeyJfc2QiOiBbImJTQVhjV +EpDV3NSLXJYMUVJVThYQ3hLQWkwZDRFTVZqU3VOd3pSbXBzSzAiLCAiY1dncGxPU1g4N +HBMT2JGUHRSby1RMDhFZzJfc200cE5FbVBfRGM1RlpsdyIsICJkMjNDMnQ1ajBRVkxkR +HZiUDdfMktkVllCLUtyTGJxMTRaRzVtcVRHMWlvIl0sICJwZXJzb25fdW5pcXVlX2lkZ +W50aWZpZXIiOiAiVElOSVQtZmMwZDk2ODQtMWJmMC00MjIwLTk2NDItOGZlNjUyYzhjM +DQwIiwgImdpdmVuX25hbWUiOiAiUmFmZmFlbGxvIiwgImZhbWlseV9uYW1lIjogIk1hc +2NldHRpIiwgIm5hdGlvbmFsaXRpZXMiOiBbIklUIl19fSwgImlzcyI6ICJodHRwczovL +2V4YW1wbGUuY29tL2lzc3VlciIsICJpYXQiOiAxNTE2MjM5MDIyLCAiZXhwIjogMTUxN +jI0NzAyMiwgInNkX2RpZ2VzdF9kZXJpdmF0aW9uX2FsZyI6ICJzaGEtMjU2In0.Zp18y +KuJgX6DUDJn2vOkJozTie5FpVRKNjW-Zzv6K7d6CV31-1hP0TH36oF94XfcZ5iD4bHNH +zQa6wRIxWSPo97u4SRCPh5tjmojmIzv4LSmlAYOY7uKnVhYnvaJPcC4vGTniAApEV6ir +1EIU380d9UOuPtYC0nHgnyNSjFJz6QeVZtSx_hK4ltDGkCAUNC8iXRcFngjuyg2aMM6g +J60vjtq6-RaZGjUv_d2Cm8Vr-sXs7pwTJ4AgpGm_2N6Y6CpdZ_MLuu0kr74CI4uiSRPd +oAoOotH2dojlaxQBXTeCYjy3wfCkGZjS8gwXYqcgXhWqG1kTRl4vgda6EljSgXeBA~Wy +J6NEsxd3VkaTQyV2ZFNldQZ2ZsazJnIiwgImFzc3VyYW5jZV9sZXZlbCIsICJoaWdoIl +0~WyJDcUpHU3NYdVpVaDZ1Tmt5ZWh4U0NRIiwgInR5cGUiLCAiZWxlY3Ryb25pY19yZW +NvcmQiXQ~WyJSOV9SQVZhTWY2Q0x2Um00Z3AzOS1nIiwgInR5cGUiLCAiZXUuZXVyb3B +hLmVjLmV1ZGl3LnBpZC4xIl0~WyJiRU9xOGo1NjllWXpsUzcteEhXRkJnIiwgInNvdXJ +jZSIsIHsib3JnYW5pemF0aW9uX25hbWUiOiAiQ29tdW5lIGRpIEZpcmVuemUiLCAib3J +nYW5pemF0aW9uX2lkIjogImNfZDYxMiIsICJjb3VudHJ5X2NvZGUiOiAiSVQiLCAiY29 +1bnRyeSI6ICJJdGFseSJ9XQ~WyJaaG9OdUVtNzA5ZGtPMFRVb2V5NFdBIiwgInBlcnNv +bmFsX251bWJlciIsICI5NjQyLThmZTY1MmM4YzA0MCJd~WyJ0eVVYQTZQUGh5RTBYSVF +6MUtYYUp3IiwgImRhdGVfb2ZfYmlydGgiLCAiMTkyMi0wMy0xMyJd~WyJ1cFJnSW5FUm +9OWUo0d2RRczhvTmVBIiwgImdlbmRlciIsICJNIl0~WyI1cGpGMkpMTmd4OWxJYlJfQm +9JWjN3IiwgInBsYWNlX29mX2JpcnRoIiwgeyJjb3VudHJ5IjogIklUIiwgImxvY2FsaX +R5IjogIkZpcmVuemUifV0~WyJhb2hWVEdYLXBreC1vMW1zZmZkd2tRIiwgImJpcnRoX2 +1pZGRsZV9uYW1lIiwgIkxlbGxvIl0 \ No newline at end of file diff --git a/examples/complex_eidas_proposal/combined_presentation.txt b/examples/complex_eidas_proposal/combined_presentation.txt new file mode 100644 index 00000000..f57602b2 --- /dev/null +++ b/examples/complex_eidas_proposal/combined_presentation.txt @@ -0,0 +1,28 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbImpobWJwVEVzUU52bnpnc1IyRF9UV +lJsVkFtU016WU9iSWR5MTlVdmQ2MkEiXSwgInZlcmlmaWVkX2NsYWltcyI6IHsidmVya +WZpY2F0aW9uIjogeyJfc2QiOiBbIjJZUm1Va2x1SmhiZUV6TVEwcDRRSkNaWkh0MzhPO +VlwNXhReGotcy1vZjQiXSwgInRydXN0X2ZyYW1ld29yayI6ICJlaWRhcyIsICJldmlkZ +W5jZSI6IFt7Il9zZCI6IFsiT1NsaXpBQjJyZWFjQVFzMldONk45MXk4SnJvU1VqR0xlZ +mpib1ZjNjIyQSJdLCAicmVjb3JkIjogeyJfc2QiOiBbIkJTM3NfRU51ODJaLVdUOWpke +DFaWTl3ejVRTW5VcjFPZFM2bHI0U093YnciLCAiX3FiMVF3OTR5ZVM0dXpNSGVNSGpaS +DJuSF9ickR5TTdlVjVVZWt5M3hWWSIsICJpSmhlcGhhdXZNRmdIMTFxUzNBVDBHNGFFS +Xc4TnZnb1BPczVvTDE2eThvIl19fV19LCAiY2xhaW1zIjogeyJfc2QiOiBbImJTQVhjV +EpDV3NSLXJYMUVJVThYQ3hLQWkwZDRFTVZqU3VOd3pSbXBzSzAiLCAiY1dncGxPU1g4N +HBMT2JGUHRSby1RMDhFZzJfc200cE5FbVBfRGM1RlpsdyIsICJkMjNDMnQ1ajBRVkxkR +HZiUDdfMktkVllCLUtyTGJxMTRaRzVtcVRHMWlvIl0sICJwZXJzb25fdW5pcXVlX2lkZ +W50aWZpZXIiOiAiVElOSVQtZmMwZDk2ODQtMWJmMC00MjIwLTk2NDItOGZlNjUyYzhjM +DQwIiwgImdpdmVuX25hbWUiOiAiUmFmZmFlbGxvIiwgImZhbWlseV9uYW1lIjogIk1hc +2NldHRpIiwgIm5hdGlvbmFsaXRpZXMiOiBbIklUIl19fSwgImlzcyI6ICJodHRwczovL +2V4YW1wbGUuY29tL2lzc3VlciIsICJpYXQiOiAxNTE2MjM5MDIyLCAiZXhwIjogMTUxN +jI0NzAyMiwgInNkX2RpZ2VzdF9kZXJpdmF0aW9uX2FsZyI6ICJzaGEtMjU2In0.Zp18y +KuJgX6DUDJn2vOkJozTie5FpVRKNjW-Zzv6K7d6CV31-1hP0TH36oF94XfcZ5iD4bHNH +zQa6wRIxWSPo97u4SRCPh5tjmojmIzv4LSmlAYOY7uKnVhYnvaJPcC4vGTniAApEV6ir +1EIU380d9UOuPtYC0nHgnyNSjFJz6QeVZtSx_hK4ltDGkCAUNC8iXRcFngjuyg2aMM6g +J60vjtq6-RaZGjUv_d2Cm8Vr-sXs7pwTJ4AgpGm_2N6Y6CpdZ_MLuu0kr74CI4uiSRPd +oAoOotH2dojlaxQBXTeCYjy3wfCkGZjS8gwXYqcgXhWqG1kTRl4vgda6EljSgXeBA~Wy +J6NEsxd3VkaTQyV2ZFNldQZ2ZsazJnIiwgImFzc3VyYW5jZV9sZXZlbCIsICJoaWdoIl +0~WyJSOV9SQVZhTWY2Q0x2Um00Z3AzOS1nIiwgInR5cGUiLCAiZXUuZXVyb3BhLmVjLm +V1ZGl3LnBpZC4xIl0~WyJ1cFJnSW5FUm9OWUo0d2RRczhvTmVBIiwgImdlbmRlciIsIC +JNIl0~WyI1cGpGMkpMTmd4OWxJYlJfQm9JWjN3IiwgInBsYWNlX29mX2JpcnRoIiwgey +Jjb3VudHJ5IjogIklUIiwgImxvY2FsaXR5IjogIkZpcmVuemUifV0~ \ No newline at end of file diff --git a/examples/complex_eidas_proposal/disclosures.md b/examples/complex_eidas_proposal/disclosures.md new file mode 100644 index 00000000..31ac3693 --- /dev/null +++ b/examples/complex_eidas_proposal/disclosures.md @@ -0,0 +1,137 @@ +__Disclosure for `assurance_level`:__ + +``` +WyJ6NEsxd3VkaTQyV2ZFNldQZ2ZsazJnIiwgImFzc3VyYW5jZV9sZXZlbCIsICJoaWdo +Il0 +``` + +Contents: + +``` +["z4K1wudi42WfE6WPgflk2g", "assurance_level", "high"] +``` + +SHA-256 Hash: `2YRmUkluJhbeEzMQ0p4QJCZZHt38O9Yp5xQxj-s-of4` + +__Disclosure for `type`:__ + +``` +WyJDcUpHU3NYdVpVaDZ1Tmt5ZWh4U0NRIiwgInR5cGUiLCAiZWxlY3Ryb25pY19yZWNv +cmQiXQ +``` + +Contents: + +``` +["CqJGSsXuZUh6uNkyehxSCQ", "type", "electronic_record"] +``` + +SHA-256 Hash: `OSlizAB2reacAQs2WN6N91y8JroSUjGLefjboVc622A` + +__Disclosure for `type`:__ + +``` +WyJSOV9SQVZhTWY2Q0x2Um00Z3AzOS1nIiwgInR5cGUiLCAiZXUuZXVyb3BhLmVjLmV1 +ZGl3LnBpZC4xIl0 +``` + +Contents: + +``` +["R9_RAVaMf6CLvRm4gp39-g", "type", "eu.europa.ec.eudiw.pid.1"] +``` + +SHA-256 Hash: `iJhephauvMFgH11qS3AT0G4aEIw8NvgoPOs5oL16y8o` + +__Disclosure for `source`:__ + +``` +WyJiRU9xOGo1NjllWXpsUzcteEhXRkJnIiwgInNvdXJjZSIsIHsib3JnYW5pemF0aW9u +X25hbWUiOiAiQ29tdW5lIGRpIEZpcmVuemUiLCAib3JnYW5pemF0aW9uX2lkIjogImNf +ZDYxMiIsICJjb3VudHJ5X2NvZGUiOiAiSVQiLCAiY291bnRyeSI6ICJJdGFseSJ9XQ +``` + +Contents: + +``` +["bEOq8j569eYzlS7-xHWFBg", "source", {"organization_name": "Comune +di Firenze", "organization_id": "c_d612", "country_code": "IT", +"country": "Italy"}] +``` + +SHA-256 Hash: `_qb1Qw94yeS4uzMHeMHjZH2nH_brDyM7eV5Ueky3xVY` + +__Disclosure for `personal_number`:__ + +``` +WyJaaG9OdUVtNzA5ZGtPMFRVb2V5NFdBIiwgInBlcnNvbmFsX251bWJlciIsICI5NjQy +LThmZTY1MmM4YzA0MCJd +``` + +Contents: + +``` +["ZhoNuEm709dkO0TUoey4WA", "personal_number", "9642-8fe652c8c040"] +``` + +SHA-256 Hash: `BS3s_ENu82Z-WT9jdx1ZY9wz5QMnUr1OdS6lr4SOwbw` + +__Disclosure for `date_of_birth`:__ + +``` +WyJ0eVVYQTZQUGh5RTBYSVF6MUtYYUp3IiwgImRhdGVfb2ZfYmlydGgiLCAiMTkyMi0w +My0xMyJd +``` + +Contents: + +``` +["tyUXA6PPhyE0XIQz1KXaJw", "date_of_birth", "1922-03-13"] +``` + +SHA-256 Hash: `cWgplOSX84pLObFPtRo-Q08Eg2_sm4pNEmP_Dc5FZlw` + +__Disclosure for `gender`:__ + +``` +WyJ1cFJnSW5FUm9OWUo0d2RRczhvTmVBIiwgImdlbmRlciIsICJNIl0 +``` + +Contents: + +``` +["upRgInERoNYJ4wdQs8oNeA", "gender", "M"] +``` + +SHA-256 Hash: `bSAXcTJCWsR-rX1EIU8XCxKAi0d4EMVjSuNwzRmpsK0` + +__Disclosure for `place_of_birth`:__ + +``` +WyI1cGpGMkpMTmd4OWxJYlJfQm9JWjN3IiwgInBsYWNlX29mX2JpcnRoIiwgeyJjb3Vu +dHJ5IjogIklUIiwgImxvY2FsaXR5IjogIkZpcmVuemUifV0 +``` + +Contents: + +``` +["5pjF2JLNgx9lIbR_BoIZ3w", "place_of_birth", {"country": "IT", +"locality": "Firenze"}] +``` + +SHA-256 Hash: `d23C2t5j0QVLdDvbP7_2KdVYB-KrLbq14ZG5mqTG1io` + +__Disclosure for `birth_middle_name`:__ + +``` +WyJhb2hWVEdYLXBreC1vMW1zZmZkd2tRIiwgImJpcnRoX21pZGRsZV9uYW1lIiwgIkxl +bGxvIl0 +``` + +Contents: + +``` +["aohVTGX-pkx-o1msffdwkQ", "birth_middle_name", "Lello"] +``` + +SHA-256 Hash: `jhmbpTEsQNvnzgsR2D_TVRlVAmSMzYObIdy19Uvd62A` \ No newline at end of file diff --git a/examples/complex_eidas_proposal/hb_jwt_serialized.txt b/examples/complex_eidas_proposal/hb_jwt_serialized.txt new file mode 100644 index 00000000..e69de29b diff --git a/examples/complex_eidas_proposal/sd_jwt_payload.json b/examples/complex_eidas_proposal/sd_jwt_payload.json new file mode 100644 index 00000000..a9a28772 --- /dev/null +++ b/examples/complex_eidas_proposal/sd_jwt_payload.json @@ -0,0 +1,45 @@ +{ + "_sd": [ + "jhmbpTEsQNvnzgsR2D_TVRlVAmSMzYObIdy19Uvd62A" + ], + "verified_claims": { + "verification": { + "_sd": [ + "2YRmUkluJhbeEzMQ0p4QJCZZHt38O9Yp5xQxj-s-of4" + ], + "trust_framework": "eidas", + "evidence": [ + { + "_sd": [ + "OSlizAB2reacAQs2WN6N91y8JroSUjGLefjboVc622A" + ], + "record": { + "_sd": [ + "BS3s_ENu82Z-WT9jdx1ZY9wz5QMnUr1OdS6lr4SOwbw", + "_qb1Qw94yeS4uzMHeMHjZH2nH_brDyM7eV5Ueky3xVY", + "iJhephauvMFgH11qS3AT0G4aEIw8NvgoPOs5oL16y8o" + ] + } + } + ] + }, + "claims": { + "_sd": [ + "bSAXcTJCWsR-rX1EIU8XCxKAi0d4EMVjSuNwzRmpsK0", + "cWgplOSX84pLObFPtRo-Q08Eg2_sm4pNEmP_Dc5FZlw", + "d23C2t5j0QVLdDvbP7_2KdVYB-KrLbq14ZG5mqTG1io" + ], + "person_unique_identifier": + "TINIT-fc0d9684-1bf0-4220-9642-8fe652c8c040", + "given_name": "Raffaello", + "family_name": "Mascetti", + "nationalities": [ + "IT" + ] + } + }, + "iss": "https://example.com/issuer", + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256" +} \ No newline at end of file diff --git a/examples/complex_eidas_proposal/sd_jwt_serialized.txt b/examples/complex_eidas_proposal/sd_jwt_serialized.txt new file mode 100644 index 00000000..3f47695f --- /dev/null +++ b/examples/complex_eidas_proposal/sd_jwt_serialized.txt @@ -0,0 +1,23 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbImpobWJwVEVzUU52bnpnc1IyRF9UV +lJsVkFtU016WU9iSWR5MTlVdmQ2MkEiXSwgInZlcmlmaWVkX2NsYWltcyI6IHsidmVya +WZpY2F0aW9uIjogeyJfc2QiOiBbIjJZUm1Va2x1SmhiZUV6TVEwcDRRSkNaWkh0MzhPO +VlwNXhReGotcy1vZjQiXSwgInRydXN0X2ZyYW1ld29yayI6ICJlaWRhcyIsICJldmlkZ +W5jZSI6IFt7Il9zZCI6IFsiT1NsaXpBQjJyZWFjQVFzMldONk45MXk4SnJvU1VqR0xlZ +mpib1ZjNjIyQSJdLCAicmVjb3JkIjogeyJfc2QiOiBbIkJTM3NfRU51ODJaLVdUOWpke +DFaWTl3ejVRTW5VcjFPZFM2bHI0U093YnciLCAiX3FiMVF3OTR5ZVM0dXpNSGVNSGpaS +DJuSF9ickR5TTdlVjVVZWt5M3hWWSIsICJpSmhlcGhhdXZNRmdIMTFxUzNBVDBHNGFFS +Xc4TnZnb1BPczVvTDE2eThvIl19fV19LCAiY2xhaW1zIjogeyJfc2QiOiBbImJTQVhjV +EpDV3NSLXJYMUVJVThYQ3hLQWkwZDRFTVZqU3VOd3pSbXBzSzAiLCAiY1dncGxPU1g4N +HBMT2JGUHRSby1RMDhFZzJfc200cE5FbVBfRGM1RlpsdyIsICJkMjNDMnQ1ajBRVkxkR +HZiUDdfMktkVllCLUtyTGJxMTRaRzVtcVRHMWlvIl0sICJwZXJzb25fdW5pcXVlX2lkZ +W50aWZpZXIiOiAiVElOSVQtZmMwZDk2ODQtMWJmMC00MjIwLTk2NDItOGZlNjUyYzhjM +DQwIiwgImdpdmVuX25hbWUiOiAiUmFmZmFlbGxvIiwgImZhbWlseV9uYW1lIjogIk1hc +2NldHRpIiwgIm5hdGlvbmFsaXRpZXMiOiBbIklUIl19fSwgImlzcyI6ICJodHRwczovL +2V4YW1wbGUuY29tL2lzc3VlciIsICJpYXQiOiAxNTE2MjM5MDIyLCAiZXhwIjogMTUxN +jI0NzAyMiwgInNkX2RpZ2VzdF9kZXJpdmF0aW9uX2FsZyI6ICJzaGEtMjU2In0.Zp18y +KuJgX6DUDJn2vOkJozTie5FpVRKNjW-Zzv6K7d6CV31-1hP0TH36oF94XfcZ5iD4bHNH +zQa6wRIxWSPo97u4SRCPh5tjmojmIzv4LSmlAYOY7uKnVhYnvaJPcC4vGTniAApEV6ir +1EIU380d9UOuPtYC0nHgnyNSjFJz6QeVZtSx_hK4ltDGkCAUNC8iXRcFngjuyg2aMM6g +J60vjtq6-RaZGjUv_d2Cm8Vr-sXs7pwTJ4AgpGm_2N6Y6CpdZ_MLuu0kr74CI4uiSRPd +oAoOotH2dojlaxQBXTeCYjy3wfCkGZjS8gwXYqcgXhWqG1kTRl4vgda6EljSgXeBA \ No newline at end of file diff --git a/examples/complex_eidas_proposal/user_claims.json b/examples/complex_eidas_proposal/user_claims.json new file mode 100644 index 00000000..cffde8fb --- /dev/null +++ b/examples/complex_eidas_proposal/user_claims.json @@ -0,0 +1,39 @@ +{ + "verified_claims": { + "verification": { + "trust_framework": "eidas", + "assurance_level": "high", + "evidence": [ + { + "type": "electronic_record", + "record": { + "type": "eu.europa.ec.eudiw.pid.1", + "source": { + "organization_name": "Comune di Firenze", + "organization_id": "c_d612", + "country_code": "IT", + "country": "Italy" + }, + "personal_number": "9642-8fe652c8c040" + } + } + ] + }, + "claims": { + "person_unique_identifier": + "TINIT-fc0d9684-1bf0-4220-9642-8fe652c8c040", + "given_name": "Raffaello", + "family_name": "Mascetti", + "date_of_birth": "1922-03-13", + "gender": "M", + "place_of_birth": { + "country": "IT", + "locality": "Firenze" + }, + "nationalities": [ + "IT" + ] + } + }, + "birth_middle_name": "Lello" +} \ No newline at end of file diff --git a/examples/complex_eidas_proposal/verified_contents.json b/examples/complex_eidas_proposal/verified_contents.json new file mode 100644 index 00000000..6a8be11a --- /dev/null +++ b/examples/complex_eidas_proposal/verified_contents.json @@ -0,0 +1,33 @@ +{ + "verified_claims": { + "verification": { + "trust_framework": "eidas", + "evidence": [ + { + "record": { + "type": "eu.europa.ec.eudiw.pid.1" + } + } + ], + "assurance_level": "high" + }, + "claims": { + "person_unique_identifier": + "TINIT-fc0d9684-1bf0-4220-9642-8fe652c8c040", + "given_name": "Raffaello", + "family_name": "Mascetti", + "nationalities": [ + "IT" + ], + "gender": "M", + "place_of_birth": { + "country": "IT", + "locality": "Firenze" + } + } + }, + "iss": "https://example.com/issuer", + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256" +} \ No newline at end of file diff --git a/examples/complex_ekyc/combined_issuance.txt b/examples/complex_ekyc/combined_issuance.txt new file mode 100644 index 00000000..97a05519 --- /dev/null +++ b/examples/complex_ekyc/combined_issuance.txt @@ -0,0 +1,54 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbIjBmR2hRZjZuRXZZSDlzVGNDdGt4Q +2ZBVktwOXFBWURWbUZzeEFEV0pzaW8iLCAiSDBadXhYc2EzOWd3eEhRMGNLX0hRQUlLa +lc0dDExWXdNMEd5XzFnVWx1VSIsICJnWHpxOXRWS2FkZGZRdnQ5NHJpVnplNkdsWmlXM +zVJdGlNbmdDNXBfaFhFIl0sICJ2ZXJpZmllZF9jbGFpbXMiOiB7InZlcmlmaWNhdGlvb +iI6IHsiX3NkIjogWyI0blFJNC15OXd0SnZ5Q3MyV3R0eUxtSVRZMDJLNVdCTUtLVDFxS +2VFaEhFIiwgIlp2eDNHWHFzakIxY1I1cm14cmx2M3hRdVR5a1dEWjdsS2xtYXh0QlZZb +zAiLCAiY0N6b2JnVWFsMHVfRG4wUGVuZ1U4WWNaZnZfUjN4aDBNVzFLMDFPa2cxbyJdL +CAiZXZpZGVuY2UiOiBbeyJfc2QiOiBbIjE4bzZhcGtfbUVMZV9SYVdqSTFPbTg3N2g5X +0preTVkcGd6dUJCTmwtZFEiLCAiRWQ3ZlRzZGJndjJFWjV1R3N1VUFrNVlCYU10Q2Z2V +EZrbVpac3pWUkpwayIsICJydkpkT2kySjZnRmVOSXZDZGdJcHJyam1pMXJLVERfUnU5N +mRpNUZTN2k0Il0sICJkb2N1bWVudCI6IHsiX3NkIjogWyJCaUtPSWttdEpmcjdJcFdzb +jhfWDRoYzZGeWdxYWVRUHVqb1Z1WEFIbldRIiwgIkYxdnRqTnlSN3ZhWThiTzZDSGRGc +jl0QWplMGx0U0xlN2JJRllscE5XN3MiLCAiSFh5aHlsaUdXQVA4dXZCZjNKaGRHU2g5U +GJnM0tOdTk2OTU4MWU4TVpQbyIsICJzVnpVSVhBenpNdzBFZkl3WnlMMjN4d2VlMzg3N +HUzYTdpWGNXeVpod3A0Il0sICJpc3N1ZXIiOiB7Il9zZCI6IFsiQzFzYUpWYzIxbjFZM +U8yczR5YTBVWUphYlRBb0d5Z19fUTM5T2Z5anFTTSIsICJMaTlCSlNvN0FGY0tWVGxyT +Ho2aXA2dDgybnFJdDJxclhjM3dwNnJDb2xNIl19fX1dfSwgImNsYWltcyI6IHsiX3NkI +jogWyIxcWIyNnROZzZPWnVaeVZEWXdLNC0tbVF4WGJacXdjUWJoVXhHSHJYZUxNIiwgI +kFIWDBFZ05wZF93YWswN2xLOEhYMml6RE5udHNVWm9qdXp5RVdkMkdKZGsiLCAiRnd6V +HowVEhhRU96ZXhnRXpMUlh1LXpzVFBORDdieTNhQkY1N0F3S0NaSSIsICJ4S2JUTWxPU +2pGamtKdzhrQU1aQXloRWZxYmFxMmIta2xhUEs4c3JwZ3ZzIl0sICJiaXJ0aGRhdGUiO +iAiMTk1Ni0wMS0yOCIsICJwbGFjZV9vZl9iaXJ0aCI6IHsiY291bnRyeSI6ICJJUyIsI +CJsb2NhbGl0eSI6ICJcdTAwZGV5a2t2YWJcdTAwZTZqYXJrbGF1c3R1ciJ9fX0sICJpc +3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc3N1ZXIiLCAiaWF0IjogMTUxNjIzOTAyM +iwgImV4cCI6IDE1MTYyNDcwMjIsICJzZF9kaWdlc3RfZGVyaXZhdGlvbl9hbGciOiAic +2hhLTI1NiJ9.Xv9M-gr_lNLecd8HkxOqS-qpKRo5d0o0lsFfCQvFJxi5gVS33BSCNVXJ +gJ2nrm2yyJziMZy_NpOR-mz4tIJyXyp57-rHEk9kMUmvhFK0uqCpZmPNh5-auRMVhFOw +pbNkyCsTc-k-Xw97jjOjMdIxAxEnAXsX3Hww3znJTx6VuFwZpXpnzXOWyHLohGZCfdRA +mfCwr8HKk_m6oV-CKv0ezAZ5k16djW4gKqB1-HVbjD4i3ONc2ggqvATSD28pjVGPpENm +PvcDLUi7doU-MlMLZdJI8xqS4B7tiwDbFP8y4wKC-fajKASDt4WkFObluXJ_1_VkMnfW +328gQMlh9bnw0g~WyI2eUNER1RSblBzUHowS3RxSVh3cjhRIiwgInRydXN0X2ZyYW1ld +29yayIsICJkZV9hbWwiXQ~WyI2TlpFYW9FUmVmdzJ2YTl3VWZDSlJBIiwgInRpbWUiLC +AiMjAxMi0wNC0yM1QxODoyNVoiXQ~WyJTWVhaS291anJYdXZPZ3hjbnVZS1J3IiwgInZ +lcmlmaWNhdGlvbl9wcm9jZXNzIiwgImYyNGM2Zi02ZDNmLTRlYzUtOTczZS1iMGQ4NTA +2ZjNiYzciXQ~WyJyNE1TdlZEY1hQMFlFSmNsd1BJTmRnIiwgInR5cGUiLCAiZG9jdW1l +bnQiXQ~WyJVYmI4MGZRYndOOWx0S0xYc0RvbkdnIiwgIm1ldGhvZCIsICJwaXBwIl0~W +yJaRW1vWTc0ZlNvUXdxOFlWMkxGMmt3IiwgInRpbWUiLCAiMjAxMi0wNC0yMlQxMTozM +FoiXQ~WyJtanBjRGkweHFZbTF4aFJyQ3lCeGZnIiwgInR5cGUiLCAiaWRjYXJkIl0~Wy +JEY0IzOXRwcHBqbGhjVFctLUhKYVJ3IiwgIm5hbWUiLCAiU3RhZHQgQXVnc2J1cmciXQ +~WyJGbWU0VVdmeDZBV0g1NS1BOHJXRF93IiwgImNvdW50cnkiLCAiREUiXQ~WyIzZ3NJ +RUs0NWM4bkZaT09YbWJELVRBIiwgIm51bWJlciIsICI1MzU1NDU1NCJd~WyJrNlZGS0I +3V29xeWZXT1M0RVZqU3d3IiwgImRhdGVfb2ZfaXNzdWFuY2UiLCAiMjAxMC0wMy0yMyJ +d~WyJqTEtGZlFYQk5FdHRFRzR1UGV6cnlnIiwgImRhdGVfb2ZfZXhwaXJ5IiwgIjIwMj +AtMDMtMjIiXQ~WyJ2WmN5ZDRDTDhFeDFBUGlSb0tVVFB3IiwgImdpdmVuX25hbWUiLCA +iTWF4Il0~WyJod0lFTklDMWwtQlRuZnNfMnZQV2ZBIiwgImZhbWlseV9uYW1lIiwgIk1 +cdTAwZmNsbGVyIl0~WyJHRFhMLTBfWWlvYUxNU1l0RzdCdkxnIiwgIm5hdGlvbmFsaXR +pZXMiLCBbIkRFIl1d~WyJVUjZsSC1jbUtIenh5VGlhazRmRVVRIiwgImFkZHJlc3MiLC +B7ImxvY2FsaXR5IjogIk1heHN0YWR0IiwgInBvc3RhbF9jb2RlIjogIjEyMzQ0IiwgIm +NvdW50cnkiOiAiREUiLCAic3RyZWV0X2FkZHJlc3MiOiAiV2VpZGVuc3RyYVx1MDBkZm +UgMjIifV0~WyJuNV9BU3lpa2FSNHBZMzRrVG9YTlBRIiwgImJpcnRoX21pZGRsZV9uYW +1lIiwgIlRpbW90aGV1cyJd~WyJFSGh2d1dnWGg2VUZKcVRmRjVRaE93IiwgInNhbHV0Y +XRpb24iLCAiRHIuIl0~WyJTMzF2RjdWZHNpUDhRTXB0b21PZW1BIiwgIm1zaXNkbiIsI +CI0OTEyMzQ1Njc4OSJd \ No newline at end of file diff --git a/examples/complex_ekyc/combined_presentation.txt b/examples/complex_ekyc/combined_presentation.txt new file mode 100644 index 00000000..e4910569 --- /dev/null +++ b/examples/complex_ekyc/combined_presentation.txt @@ -0,0 +1,40 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbIjBmR2hRZjZuRXZZSDlzVGNDdGt4Q +2ZBVktwOXFBWURWbUZzeEFEV0pzaW8iLCAiSDBadXhYc2EzOWd3eEhRMGNLX0hRQUlLa +lc0dDExWXdNMEd5XzFnVWx1VSIsICJnWHpxOXRWS2FkZGZRdnQ5NHJpVnplNkdsWmlXM +zVJdGlNbmdDNXBfaFhFIl0sICJ2ZXJpZmllZF9jbGFpbXMiOiB7InZlcmlmaWNhdGlvb +iI6IHsiX3NkIjogWyI0blFJNC15OXd0SnZ5Q3MyV3R0eUxtSVRZMDJLNVdCTUtLVDFxS +2VFaEhFIiwgIlp2eDNHWHFzakIxY1I1cm14cmx2M3hRdVR5a1dEWjdsS2xtYXh0QlZZb +zAiLCAiY0N6b2JnVWFsMHVfRG4wUGVuZ1U4WWNaZnZfUjN4aDBNVzFLMDFPa2cxbyJdL +CAiZXZpZGVuY2UiOiBbeyJfc2QiOiBbIjE4bzZhcGtfbUVMZV9SYVdqSTFPbTg3N2g5X +0preTVkcGd6dUJCTmwtZFEiLCAiRWQ3ZlRzZGJndjJFWjV1R3N1VUFrNVlCYU10Q2Z2V +EZrbVpac3pWUkpwayIsICJydkpkT2kySjZnRmVOSXZDZGdJcHJyam1pMXJLVERfUnU5N +mRpNUZTN2k0Il0sICJkb2N1bWVudCI6IHsiX3NkIjogWyJCaUtPSWttdEpmcjdJcFdzb +jhfWDRoYzZGeWdxYWVRUHVqb1Z1WEFIbldRIiwgIkYxdnRqTnlSN3ZhWThiTzZDSGRGc +jl0QWplMGx0U0xlN2JJRllscE5XN3MiLCAiSFh5aHlsaUdXQVA4dXZCZjNKaGRHU2g5U +GJnM0tOdTk2OTU4MWU4TVpQbyIsICJzVnpVSVhBenpNdzBFZkl3WnlMMjN4d2VlMzg3N +HUzYTdpWGNXeVpod3A0Il0sICJpc3N1ZXIiOiB7Il9zZCI6IFsiQzFzYUpWYzIxbjFZM +U8yczR5YTBVWUphYlRBb0d5Z19fUTM5T2Z5anFTTSIsICJMaTlCSlNvN0FGY0tWVGxyT +Ho2aXA2dDgybnFJdDJxclhjM3dwNnJDb2xNIl19fX1dfSwgImNsYWltcyI6IHsiX3NkI +jogWyIxcWIyNnROZzZPWnVaeVZEWXdLNC0tbVF4WGJacXdjUWJoVXhHSHJYZUxNIiwgI +kFIWDBFZ05wZF93YWswN2xLOEhYMml6RE5udHNVWm9qdXp5RVdkMkdKZGsiLCAiRnd6V +HowVEhhRU96ZXhnRXpMUlh1LXpzVFBORDdieTNhQkY1N0F3S0NaSSIsICJ4S2JUTWxPU +2pGamtKdzhrQU1aQXloRWZxYmFxMmIta2xhUEs4c3JwZ3ZzIl0sICJiaXJ0aGRhdGUiO +iAiMTk1Ni0wMS0yOCIsICJwbGFjZV9vZl9iaXJ0aCI6IHsiY291bnRyeSI6ICJJUyIsI +CJsb2NhbGl0eSI6ICJcdTAwZGV5a2t2YWJcdTAwZTZqYXJrbGF1c3R1ciJ9fX0sICJpc +3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc3N1ZXIiLCAiaWF0IjogMTUxNjIzOTAyM +iwgImV4cCI6IDE1MTYyNDcwMjIsICJzZF9kaWdlc3RfZGVyaXZhdGlvbl9hbGciOiAic +2hhLTI1NiJ9.Xv9M-gr_lNLecd8HkxOqS-qpKRo5d0o0lsFfCQvFJxi5gVS33BSCNVXJ +gJ2nrm2yyJziMZy_NpOR-mz4tIJyXyp57-rHEk9kMUmvhFK0uqCpZmPNh5-auRMVhFOw +pbNkyCsTc-k-Xw97jjOjMdIxAxEnAXsX3Hww3znJTx6VuFwZpXpnzXOWyHLohGZCfdRA +mfCwr8HKk_m6oV-CKv0ezAZ5k16djW4gKqB1-HVbjD4i3ONc2ggqvATSD28pjVGPpENm +PvcDLUi7doU-MlMLZdJI8xqS4B7tiwDbFP8y4wKC-fajKASDt4WkFObluXJ_1_VkMnfW +328gQMlh9bnw0g~WyI2eUNER1RSblBzUHowS3RxSVh3cjhRIiwgInRydXN0X2ZyYW1ld +29yayIsICJkZV9hbWwiXQ~WyI2TlpFYW9FUmVmdzJ2YTl3VWZDSlJBIiwgInRpbWUiLC +AiMjAxMi0wNC0yM1QxODoyNVoiXQ~WyJyNE1TdlZEY1hQMFlFSmNsd1BJTmRnIiwgInR +5cGUiLCAiZG9jdW1lbnQiXQ~WyJod0lFTklDMWwtQlRuZnNfMnZQV2ZBIiwgImZhbWls +eV9uYW1lIiwgIk1cdTAwZmNsbGVyIl0~WyJVUjZsSC1jbUtIenh5VGlhazRmRVVRIiwg +ImFkZHJlc3MiLCB7ImxvY2FsaXR5IjogIk1heHN0YWR0IiwgInBvc3RhbF9jb2RlIjog +IjEyMzQ0IiwgImNvdW50cnkiOiAiREUiLCAic3RyZWV0X2FkZHJlc3MiOiAiV2VpZGVu +c3RyYVx1MDBkZmUgMjIifV0~WyJ2WmN5ZDRDTDhFeDFBUGlSb0tVVFB3IiwgImdpdmVu +X25hbWUiLCAiTWF4Il0~ \ No newline at end of file diff --git a/examples/complex_ekyc/disclosures.md b/examples/complex_ekyc/disclosures.md new file mode 100644 index 00000000..e37150d3 --- /dev/null +++ b/examples/complex_ekyc/disclosures.md @@ -0,0 +1,279 @@ +__Disclosure for `trust_framework`:__ + +``` +WyI2eUNER1RSblBzUHowS3RxSVh3cjhRIiwgInRydXN0X2ZyYW1ld29yayIsICJkZV9h +bWwiXQ +``` + +Contents: + +``` +["6yCDGTRnPsPz0KtqIXwr8Q", "trust_framework", "de_aml"] +``` + +SHA-256 Hash: `Zvx3GXqsjB1cR5rmxrlv3xQuTykWDZ7lKlmaxtBVYo0` + +__Disclosure for `time`:__ + +``` +WyI2TlpFYW9FUmVmdzJ2YTl3VWZDSlJBIiwgInRpbWUiLCAiMjAxMi0wNC0yM1QxODoy +NVoiXQ +``` + +Contents: + +``` +["6NZEaoERefw2va9wUfCJRA", "time", "2012-04-23T18:25Z"] +``` + +SHA-256 Hash: `cCzobgUal0u_Dn0PengU8YcZfv_R3xh0MW1K01Okg1o` + +__Disclosure for `verification_process`:__ + +``` +WyJTWVhaS291anJYdXZPZ3hjbnVZS1J3IiwgInZlcmlmaWNhdGlvbl9wcm9jZXNzIiwg +ImYyNGM2Zi02ZDNmLTRlYzUtOTczZS1iMGQ4NTA2ZjNiYzciXQ +``` + +Contents: + +``` +["SYXZKoujrXuvOgxcnuYKRw", "verification_process", +"f24c6f-6d3f-4ec5-973e-b0d8506f3bc7"] +``` + +SHA-256 Hash: `4nQI4-y9wtJvyCs2WttyLmITY02K5WBMKKT1qKeEhHE` + +__Disclosure for `type`:__ + +``` +WyJyNE1TdlZEY1hQMFlFSmNsd1BJTmRnIiwgInR5cGUiLCAiZG9jdW1lbnQiXQ +``` + +Contents: + +``` +["r4MSvVDcXP0YEJclwPINdg", "type", "document"] +``` + +SHA-256 Hash: `rvJdOi2J6gFeNIvCdgIprrjmi1rKTD_Ru96di5FS7i4` + +__Disclosure for `method`:__ + +``` +WyJVYmI4MGZRYndOOWx0S0xYc0RvbkdnIiwgIm1ldGhvZCIsICJwaXBwIl0 +``` + +Contents: + +``` +["Ubb80fQbwN9ltKLXsDonGg", "method", "pipp"] +``` + +SHA-256 Hash: `18o6apk_mELe_RaWjI1Om877h9_Jky5dpgzuBBNl-dQ` + +__Disclosure for `time`:__ + +``` +WyJaRW1vWTc0ZlNvUXdxOFlWMkxGMmt3IiwgInRpbWUiLCAiMjAxMi0wNC0yMlQxMToz +MFoiXQ +``` + +Contents: + +``` +["ZEmoY74fSoQwq8YV2LF2kw", "time", "2012-04-22T11:30Z"] +``` + +SHA-256 Hash: `Ed7fTsdbgv2EZ5uGsuUAk5YBaMtCfvTFkmZZszVRJpk` + +__Disclosure for `type`:__ + +``` +WyJtanBjRGkweHFZbTF4aFJyQ3lCeGZnIiwgInR5cGUiLCAiaWRjYXJkIl0 +``` + +Contents: + +``` +["mjpcDi0xqYm1xhRrCyBxfg", "type", "idcard"] +``` + +SHA-256 Hash: `F1vtjNyR7vaY8bO6CHdFr9tAje0ltSLe7bIFYlpNW7s` + +__Disclosure for `name`:__ + +``` +WyJEY0IzOXRwcHBqbGhjVFctLUhKYVJ3IiwgIm5hbWUiLCAiU3RhZHQgQXVnc2J1cmci +XQ +``` + +Contents: + +``` +["DcB39tpppjlhcTW--HJaRw", "name", "Stadt Augsburg"] +``` + +SHA-256 Hash: `Li9BJSo7AFcKVTlrLz6ip6t82nqIt2qrXc3wp6rColM` + +__Disclosure for `country`:__ + +``` +WyJGbWU0VVdmeDZBV0g1NS1BOHJXRF93IiwgImNvdW50cnkiLCAiREUiXQ +``` + +Contents: + +``` +["Fme4UWfx6AWH55-A8rWD_w", "country", "DE"] +``` + +SHA-256 Hash: `C1saJVc21n1Y1O2s4ya0UYJabTAoGyg__Q39OfyjqSM` + +__Disclosure for `number`:__ + +``` +WyIzZ3NJRUs0NWM4bkZaT09YbWJELVRBIiwgIm51bWJlciIsICI1MzU1NDU1NCJd +``` + +Contents: + +``` +["3gsIEK45c8nFZOOXmbD-TA", "number", "53554554"] +``` + +SHA-256 Hash: `BiKOIkmtJfr7IpWsn8_X4hc6FygqaeQPujoVuXAHnWQ` + +__Disclosure for `date_of_issuance`:__ + +``` +WyJrNlZGS0I3V29xeWZXT1M0RVZqU3d3IiwgImRhdGVfb2ZfaXNzdWFuY2UiLCAiMjAx +MC0wMy0yMyJd +``` + +Contents: + +``` +["k6VFKB7WoqyfWOS4EVjSww", "date_of_issuance", "2010-03-23"] +``` + +SHA-256 Hash: `HXyhyliGWAP8uvBf3JhdGSh9Pbg3KNu969581e8MZPo` + +__Disclosure for `date_of_expiry`:__ + +``` +WyJqTEtGZlFYQk5FdHRFRzR1UGV6cnlnIiwgImRhdGVfb2ZfZXhwaXJ5IiwgIjIwMjAt +MDMtMjIiXQ +``` + +Contents: + +``` +["jLKFfQXBNEttEG4uPezryg", "date_of_expiry", "2020-03-22"] +``` + +SHA-256 Hash: `sVzUIXAzzMw0EfIwZyL23xwee3874u3a7iXcWyZhwp4` + +__Disclosure for `given_name`:__ + +``` +WyJ2WmN5ZDRDTDhFeDFBUGlSb0tVVFB3IiwgImdpdmVuX25hbWUiLCAiTWF4Il0 +``` + +Contents: + +``` +["vZcyd4CL8Ex1APiRoKUTPw", "given_name", "Max"] +``` + +SHA-256 Hash: `xKbTMlOSjFjkJw8kAMZAyhEfqbaq2b-klaPK8srpgvs` + +__Disclosure for `family_name`:__ + +``` +WyJod0lFTklDMWwtQlRuZnNfMnZQV2ZBIiwgImZhbWlseV9uYW1lIiwgIk1cdTAwZmNs +bGVyIl0 +``` + +Contents: + +``` +["hwIENIC1l-BTnfs_2vPWfA", "family_name", "M\u00fcller"] +``` + +SHA-256 Hash: `1qb26tNg6OZuZyVDYwK4--mQxXbZqwcQbhUxGHrXeLM` + +__Disclosure for `nationalities`:__ + +``` +WyJHRFhMLTBfWWlvYUxNU1l0RzdCdkxnIiwgIm5hdGlvbmFsaXRpZXMiLCBbIkRFIl1d +``` + +Contents: + +``` +["GDXL-0_YioaLMSYtG7BvLg", "nationalities", ["DE"]] +``` + +SHA-256 Hash: `FwzTz0THaEOzexgEzLRXu-zsTPND7by3aBF57AwKCZI` + +__Disclosure for `address`:__ + +``` +WyJVUjZsSC1jbUtIenh5VGlhazRmRVVRIiwgImFkZHJlc3MiLCB7ImxvY2FsaXR5Ijog +Ik1heHN0YWR0IiwgInBvc3RhbF9jb2RlIjogIjEyMzQ0IiwgImNvdW50cnkiOiAiREUi +LCAic3RyZWV0X2FkZHJlc3MiOiAiV2VpZGVuc3RyYVx1MDBkZmUgMjIifV0 +``` + +Contents: + +``` +["UR6lH-cmKHzxyTiak4fEUQ", "address", {"locality": "Maxstadt", +"postal_code": "12344", "country": "DE", "street_address": +"Weidenstra\u00dfe 22"}] +``` + +SHA-256 Hash: `AHX0EgNpd_wak07lK8HX2izDNntsUZojuzyEWd2GJdk` + +__Disclosure for `birth_middle_name`:__ + +``` +WyJuNV9BU3lpa2FSNHBZMzRrVG9YTlBRIiwgImJpcnRoX21pZGRsZV9uYW1lIiwgIlRp +bW90aGV1cyJd +``` + +Contents: + +``` +["n5_ASyikaR4pY34kToXNPQ", "birth_middle_name", "Timotheus"] +``` + +SHA-256 Hash: `gXzq9tVKaddfQvt94riVze6GlZiW35ItiMngC5p_hXE` + +__Disclosure for `salutation`:__ + +``` +WyJFSGh2d1dnWGg2VUZKcVRmRjVRaE93IiwgInNhbHV0YXRpb24iLCAiRHIuIl0 +``` + +Contents: + +``` +["EHhvwWgXh6UFJqTfF5QhOw", "salutation", "Dr."] +``` + +SHA-256 Hash: `H0ZuxXsa39gwxHQ0cK_HQAIKjW4t11YwM0Gy_1gUluU` + +__Disclosure for `msisdn`:__ + +``` +WyJTMzF2RjdWZHNpUDhRTXB0b21PZW1BIiwgIm1zaXNkbiIsICI0OTEyMzQ1Njc4OSJd +``` + +Contents: + +``` +["S31vF7VdsiP8QMptomOemA", "msisdn", "49123456789"] +``` + +SHA-256 Hash: `0fGhQf6nEvYH9sTcCtkxCfAVKp9qAYDVmFsxADWJsio` \ No newline at end of file diff --git a/examples/complex_ekyc/hb_jwt_serialized.txt b/examples/complex_ekyc/hb_jwt_serialized.txt new file mode 100644 index 00000000..e69de29b diff --git a/examples/complex_ekyc/sd_jwt_payload.json b/examples/complex_ekyc/sd_jwt_payload.json new file mode 100644 index 00000000..70265a19 --- /dev/null +++ b/examples/complex_ekyc/sd_jwt_payload.json @@ -0,0 +1,56 @@ +{ + "_sd": [ + "0fGhQf6nEvYH9sTcCtkxCfAVKp9qAYDVmFsxADWJsio", + "H0ZuxXsa39gwxHQ0cK_HQAIKjW4t11YwM0Gy_1gUluU", + "gXzq9tVKaddfQvt94riVze6GlZiW35ItiMngC5p_hXE" + ], + "verified_claims": { + "verification": { + "_sd": [ + "4nQI4-y9wtJvyCs2WttyLmITY02K5WBMKKT1qKeEhHE", + "Zvx3GXqsjB1cR5rmxrlv3xQuTykWDZ7lKlmaxtBVYo0", + "cCzobgUal0u_Dn0PengU8YcZfv_R3xh0MW1K01Okg1o" + ], + "evidence": [ + { + "_sd": [ + "18o6apk_mELe_RaWjI1Om877h9_Jky5dpgzuBBNl-dQ", + "Ed7fTsdbgv2EZ5uGsuUAk5YBaMtCfvTFkmZZszVRJpk", + "rvJdOi2J6gFeNIvCdgIprrjmi1rKTD_Ru96di5FS7i4" + ], + "document": { + "_sd": [ + "BiKOIkmtJfr7IpWsn8_X4hc6FygqaeQPujoVuXAHnWQ", + "F1vtjNyR7vaY8bO6CHdFr9tAje0ltSLe7bIFYlpNW7s", + "HXyhyliGWAP8uvBf3JhdGSh9Pbg3KNu969581e8MZPo", + "sVzUIXAzzMw0EfIwZyL23xwee3874u3a7iXcWyZhwp4" + ], + "issuer": { + "_sd": [ + "C1saJVc21n1Y1O2s4ya0UYJabTAoGyg__Q39OfyjqSM", + "Li9BJSo7AFcKVTlrLz6ip6t82nqIt2qrXc3wp6rColM" + ] + } + } + } + ] + }, + "claims": { + "_sd": [ + "1qb26tNg6OZuZyVDYwK4--mQxXbZqwcQbhUxGHrXeLM", + "AHX0EgNpd_wak07lK8HX2izDNntsUZojuzyEWd2GJdk", + "FwzTz0THaEOzexgEzLRXu-zsTPND7by3aBF57AwKCZI", + "xKbTMlOSjFjkJw8kAMZAyhEfqbaq2b-klaPK8srpgvs" + ], + "birthdate": "1956-01-28", + "place_of_birth": { + "country": "IS", + "locality": "Þykkvabæjarklaustur" + } + } + }, + "iss": "https://example.com/issuer", + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256" +} \ No newline at end of file diff --git a/examples/complex_ekyc/sd_jwt_serialized.txt b/examples/complex_ekyc/sd_jwt_serialized.txt new file mode 100644 index 00000000..8b93f2fb --- /dev/null +++ b/examples/complex_ekyc/sd_jwt_serialized.txt @@ -0,0 +1,32 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbIjBmR2hRZjZuRXZZSDlzVGNDdGt4Q +2ZBVktwOXFBWURWbUZzeEFEV0pzaW8iLCAiSDBadXhYc2EzOWd3eEhRMGNLX0hRQUlLa +lc0dDExWXdNMEd5XzFnVWx1VSIsICJnWHpxOXRWS2FkZGZRdnQ5NHJpVnplNkdsWmlXM +zVJdGlNbmdDNXBfaFhFIl0sICJ2ZXJpZmllZF9jbGFpbXMiOiB7InZlcmlmaWNhdGlvb +iI6IHsiX3NkIjogWyI0blFJNC15OXd0SnZ5Q3MyV3R0eUxtSVRZMDJLNVdCTUtLVDFxS +2VFaEhFIiwgIlp2eDNHWHFzakIxY1I1cm14cmx2M3hRdVR5a1dEWjdsS2xtYXh0QlZZb +zAiLCAiY0N6b2JnVWFsMHVfRG4wUGVuZ1U4WWNaZnZfUjN4aDBNVzFLMDFPa2cxbyJdL +CAiZXZpZGVuY2UiOiBbeyJfc2QiOiBbIjE4bzZhcGtfbUVMZV9SYVdqSTFPbTg3N2g5X +0preTVkcGd6dUJCTmwtZFEiLCAiRWQ3ZlRzZGJndjJFWjV1R3N1VUFrNVlCYU10Q2Z2V +EZrbVpac3pWUkpwayIsICJydkpkT2kySjZnRmVOSXZDZGdJcHJyam1pMXJLVERfUnU5N +mRpNUZTN2k0Il0sICJkb2N1bWVudCI6IHsiX3NkIjogWyJCaUtPSWttdEpmcjdJcFdzb +jhfWDRoYzZGeWdxYWVRUHVqb1Z1WEFIbldRIiwgIkYxdnRqTnlSN3ZhWThiTzZDSGRGc +jl0QWplMGx0U0xlN2JJRllscE5XN3MiLCAiSFh5aHlsaUdXQVA4dXZCZjNKaGRHU2g5U +GJnM0tOdTk2OTU4MWU4TVpQbyIsICJzVnpVSVhBenpNdzBFZkl3WnlMMjN4d2VlMzg3N +HUzYTdpWGNXeVpod3A0Il0sICJpc3N1ZXIiOiB7Il9zZCI6IFsiQzFzYUpWYzIxbjFZM +U8yczR5YTBVWUphYlRBb0d5Z19fUTM5T2Z5anFTTSIsICJMaTlCSlNvN0FGY0tWVGxyT +Ho2aXA2dDgybnFJdDJxclhjM3dwNnJDb2xNIl19fX1dfSwgImNsYWltcyI6IHsiX3NkI +jogWyIxcWIyNnROZzZPWnVaeVZEWXdLNC0tbVF4WGJacXdjUWJoVXhHSHJYZUxNIiwgI +kFIWDBFZ05wZF93YWswN2xLOEhYMml6RE5udHNVWm9qdXp5RVdkMkdKZGsiLCAiRnd6V +HowVEhhRU96ZXhnRXpMUlh1LXpzVFBORDdieTNhQkY1N0F3S0NaSSIsICJ4S2JUTWxPU +2pGamtKdzhrQU1aQXloRWZxYmFxMmIta2xhUEs4c3JwZ3ZzIl0sICJiaXJ0aGRhdGUiO +iAiMTk1Ni0wMS0yOCIsICJwbGFjZV9vZl9iaXJ0aCI6IHsiY291bnRyeSI6ICJJUyIsI +CJsb2NhbGl0eSI6ICJcdTAwZGV5a2t2YWJcdTAwZTZqYXJrbGF1c3R1ciJ9fX0sICJpc +3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc3N1ZXIiLCAiaWF0IjogMTUxNjIzOTAyM +iwgImV4cCI6IDE1MTYyNDcwMjIsICJzZF9kaWdlc3RfZGVyaXZhdGlvbl9hbGciOiAic +2hhLTI1NiJ9.Xv9M-gr_lNLecd8HkxOqS-qpKRo5d0o0lsFfCQvFJxi5gVS33BSCNVXJ +gJ2nrm2yyJziMZy_NpOR-mz4tIJyXyp57-rHEk9kMUmvhFK0uqCpZmPNh5-auRMVhFOw +pbNkyCsTc-k-Xw97jjOjMdIxAxEnAXsX3Hww3znJTx6VuFwZpXpnzXOWyHLohGZCfdRA +mfCwr8HKk_m6oV-CKv0ezAZ5k16djW4gKqB1-HVbjD4i3ONc2ggqvATSD28pjVGPpENm +PvcDLUi7doU-MlMLZdJI8xqS4B7tiwDbFP8y4wKC-fajKASDt4WkFObluXJ_1_VkMnfW +328gQMlh9bnw0g \ No newline at end of file diff --git a/examples/complex_ekyc/user_claims.json b/examples/complex_ekyc/user_claims.json new file mode 100644 index 00000000..de1671c1 --- /dev/null +++ b/examples/complex_ekyc/user_claims.json @@ -0,0 +1,47 @@ +{ + "verified_claims": { + "verification": { + "trust_framework": "de_aml", + "time": "2012-04-23T18:25Z", + "verification_process": "f24c6f-6d3f-4ec5-973e-b0d8506f3bc7", + "evidence": [ + { + "type": "document", + "method": "pipp", + "time": "2012-04-22T11:30Z", + "document": { + "type": "idcard", + "issuer": { + "name": "Stadt Augsburg", + "country": "DE" + }, + "number": "53554554", + "date_of_issuance": "2010-03-23", + "date_of_expiry": "2020-03-22" + } + } + ] + }, + "claims": { + "given_name": "Max", + "family_name": "Müller", + "nationalities": [ + "DE" + ], + "birthdate": "1956-01-28", + "place_of_birth": { + "country": "IS", + "locality": "Þykkvabæjarklaustur" + }, + "address": { + "locality": "Maxstadt", + "postal_code": "12344", + "country": "DE", + "street_address": "Weidenstraße 22" + } + } + }, + "birth_middle_name": "Timotheus", + "salutation": "Dr.", + "msisdn": "49123456789" +} \ No newline at end of file diff --git a/examples/complex_ekyc/verified_contents.json b/examples/complex_ekyc/verified_contents.json new file mode 100644 index 00000000..7bbcd4d2 --- /dev/null +++ b/examples/complex_ekyc/verified_contents.json @@ -0,0 +1,35 @@ +{ + "verified_claims": { + "verification": { + "evidence": [ + { + "document": { + "issuer": {} + }, + "type": "document" + } + ], + "trust_framework": "de_aml", + "time": "2012-04-23T18:25Z" + }, + "claims": { + "birthdate": "1956-01-28", + "place_of_birth": { + "country": "IS", + "locality": "Þykkvabæjarklaustur" + }, + "family_name": "Müller", + "address": { + "locality": "Maxstadt", + "postal_code": "12344", + "country": "DE", + "street_address": "Weidenstraße 22" + }, + "given_name": "Max" + } + }, + "iss": "https://example.com/issuer", + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256" +} \ No newline at end of file diff --git a/examples/simple/combined_issuance.txt b/examples/simple/combined_issuance.txt new file mode 100644 index 00000000..4a833a45 --- /dev/null +++ b/examples/simple/combined_issuance.txt @@ -0,0 +1,33 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbIk5ZQ29TUktFWXdYZHBlNXlkdUpYQ +3h4aHluRVU4ei1iNFR5TmlhcDc3VVkiLCAiU1k4bjJCYmtYOWxyWTNleEhsU3dQUkZYb +0QwOUdGOGE5Q1BPLUc4ajIwOCIsICJUUHNHTlBZQTQ2d21CeGZ2MnpuT0poZmRvTjVZM +UdrZXpicGFHWkNUMWFjIiwgIlprU0p4eGVHbHVJZFlCYjdDcWtaYkpWbTB3MlY1VXJSZ +U5UekFRQ1lCanciLCAibDlxSUo5SlRRd0xHN09MRUlDVEZCVnhtQXJ3OFBqeTY1ZEQ2b +XRRVkc1YyIsICJvMVNBc0ozM1lNaW9POXBYNVZlQU0xbHh1SEY2aFpXMmtHZGtLS0JuV +mxvIiwgInFxdmNxbmN6QU1nWXg3RXlrSTZ3d3RzcHl2eXZLNzkwZ2U3TUJiUS1OdXMiX +SwgImlzcyI6ICJodHRwczovL2V4YW1wbGUuY29tL2lzc3VlciIsICJpYXQiOiAxNTE2M +jM5MDIyLCAiZXhwIjogMTUxNjI0NzAyMiwgInNkX2RpZ2VzdF9kZXJpdmF0aW9uX2FsZ +yI6ICJzaGEtMjU2IiwgImNuZiI6IHsiandrIjogeyJrdHkiOiAiUlNBIiwgIm4iOiAic +G00Yk9IQmctb1loQXlQV3pSNTZBV1gzclVJWHAxMV9JQ0RrR2dTNlczWldMdHMtaHp3S +TN4NjU2NTlrZzRoVm85ZGJHb0NKRTNaR0ZfZWFldEUzMFVoQlVFZ3BHd3JEclFpSjl6c +XBybWNGZnIzcXZ2a0dqdHRoOFpnbDFlTTJiSmNPd0U3UENCSFdUS1dZczE1MlI3ZzZKZ +zJPVnBoLWE4cnEtcTc5TWhLRzVRb1dfbVR6MTBRVF82SDRjN1BqV0cxZmpoOGhwV05uY +lBfcHY2ZDF6U3daZmM1Zmw2eVZSTDBEVjBWM2xHSEtlMldxZl9lTkdqQnJCTFZrbERUa +zgtc3RYX01XTGNSLUVHbVhBT3YwVUJXaXRTX2RYSktKdS12WEp5dzE0bkhTR3V4VElLM +mh4MXB0dE1mdDlDc3ZxaW1YS2VEVFUxNHFRTDFlRTdpaGN3IiwgImUiOiAiQVFBQiJ9f +X0.rV7UrCdR64tA-G0_rjqYaKYukzIJi1RPgvX4eGSk5HHYWPl3-j8LEUnePSX-OlQXe +5RgsOqPo9DCj7-CLvKKxam-PbEQZurYX4qEzFyCJUOTMbOTQSLdsSe9tf1pD_yIrJhIs +cjXiq_mtJww_abdT8fIiS4h8kir2ClZifjNnLkKzqceVgrCipjbt76iZ6m_DGO2jFhU8 +Zzrat3UZALacw4fRD0l5MGIGHdQcipouhhvVBnWyl6lH8gtcOFMl4YSSoWP9ytFLoahC +SRrF5mCo1tSFg3I70v4mk2U59GfcU94GaEB0q6uF__fbEmOClUPX9yOrYryeqjJR0bBR +UK_NA~WyJkcVR2WE14UzBHYTNEb2FHbmU5eDBRIiwgInN1YiIsICJqb2huX2RvZV80Mi +Jd~WyIzanFjYjY3ejl3a3MwOHp3aUs3RXlRIiwgImdpdmVuX25hbWUiLCAiSm9obiJd~ +WyJxUVdtakpsMXMxUjRscWhFTkxScnJ3IiwgImZhbWlseV9uYW1lIiwgIkRvZSJd~WyJ +LVXhTNWhFX1hiVmFjckdBYzdFRnd3IiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBsZS5 +jb20iXQ~WyIzcXZWSjFCQURwSERTUzkzOVEtUml3IiwgInBob25lX251bWJlciIsICIr +MS0yMDItNTU1LTAxMDEiXQ~WyIweEd6bjNNaXFzY3RaSV9PcERsQWJRIiwgImFkZHJlc +3MiLCB7InN0cmVldF9hZGRyZXNzIjogIjEyMyBNYWluIFN0IiwgImxvY2FsaXR5IjogI +kFueXRvd24iLCAicmVnaW9uIjogIkFueXN0YXRlIiwgImNvdW50cnkiOiAiVVMifV0~W +yJFUktNMENOZUZKa2FENW1UWFZfWDh3IiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAxI +l0 \ No newline at end of file diff --git a/examples/simple/combined_presentation.txt b/examples/simple/combined_presentation.txt new file mode 100644 index 00000000..1040cf2e --- /dev/null +++ b/examples/simple/combined_presentation.txt @@ -0,0 +1,36 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbIk5ZQ29TUktFWXdYZHBlNXlkdUpYQ +3h4aHluRVU4ei1iNFR5TmlhcDc3VVkiLCAiU1k4bjJCYmtYOWxyWTNleEhsU3dQUkZYb +0QwOUdGOGE5Q1BPLUc4ajIwOCIsICJUUHNHTlBZQTQ2d21CeGZ2MnpuT0poZmRvTjVZM +UdrZXpicGFHWkNUMWFjIiwgIlprU0p4eGVHbHVJZFlCYjdDcWtaYkpWbTB3MlY1VXJSZ +U5UekFRQ1lCanciLCAibDlxSUo5SlRRd0xHN09MRUlDVEZCVnhtQXJ3OFBqeTY1ZEQ2b +XRRVkc1YyIsICJvMVNBc0ozM1lNaW9POXBYNVZlQU0xbHh1SEY2aFpXMmtHZGtLS0JuV +mxvIiwgInFxdmNxbmN6QU1nWXg3RXlrSTZ3d3RzcHl2eXZLNzkwZ2U3TUJiUS1OdXMiX +SwgImlzcyI6ICJodHRwczovL2V4YW1wbGUuY29tL2lzc3VlciIsICJpYXQiOiAxNTE2M +jM5MDIyLCAiZXhwIjogMTUxNjI0NzAyMiwgInNkX2RpZ2VzdF9kZXJpdmF0aW9uX2FsZ +yI6ICJzaGEtMjU2IiwgImNuZiI6IHsiandrIjogeyJrdHkiOiAiUlNBIiwgIm4iOiAic +G00Yk9IQmctb1loQXlQV3pSNTZBV1gzclVJWHAxMV9JQ0RrR2dTNlczWldMdHMtaHp3S +TN4NjU2NTlrZzRoVm85ZGJHb0NKRTNaR0ZfZWFldEUzMFVoQlVFZ3BHd3JEclFpSjl6c +XBybWNGZnIzcXZ2a0dqdHRoOFpnbDFlTTJiSmNPd0U3UENCSFdUS1dZczE1MlI3ZzZKZ +zJPVnBoLWE4cnEtcTc5TWhLRzVRb1dfbVR6MTBRVF82SDRjN1BqV0cxZmpoOGhwV05uY +lBfcHY2ZDF6U3daZmM1Zmw2eVZSTDBEVjBWM2xHSEtlMldxZl9lTkdqQnJCTFZrbERUa +zgtc3RYX01XTGNSLUVHbVhBT3YwVUJXaXRTX2RYSktKdS12WEp5dzE0bkhTR3V4VElLM +mh4MXB0dE1mdDlDc3ZxaW1YS2VEVFUxNHFRTDFlRTdpaGN3IiwgImUiOiAiQVFBQiJ9f +X0.rV7UrCdR64tA-G0_rjqYaKYukzIJi1RPgvX4eGSk5HHYWPl3-j8LEUnePSX-OlQXe +5RgsOqPo9DCj7-CLvKKxam-PbEQZurYX4qEzFyCJUOTMbOTQSLdsSe9tf1pD_yIrJhIs +cjXiq_mtJww_abdT8fIiS4h8kir2ClZifjNnLkKzqceVgrCipjbt76iZ6m_DGO2jFhU8 +Zzrat3UZALacw4fRD0l5MGIGHdQcipouhhvVBnWyl6lH8gtcOFMl4YSSoWP9ytFLoahC +SRrF5mCo1tSFg3I70v4mk2U59GfcU94GaEB0q6uF__fbEmOClUPX9yOrYryeqjJR0bBR +UK_NA~WyIweEd6bjNNaXFzY3RaSV9PcERsQWJRIiwgImFkZHJlc3MiLCB7InN0cmVldF +9hZGRyZXNzIjogIjEyMyBNYWluIFN0IiwgImxvY2FsaXR5IjogIkFueXRvd24iLCAicm +VnaW9uIjogIkFueXN0YXRlIiwgImNvdW50cnkiOiAiVVMifV0~WyJxUVdtakpsMXMxUj +RscWhFTkxScnJ3IiwgImZhbWlseV9uYW1lIiwgIkRvZSJd~WyIzanFjYjY3ejl3a3MwO +Hp3aUs3RXlRIiwgImdpdmVuX25hbWUiLCAiSm9obiJd~eyJhbGciOiAiUlMyNTYiLCAi +a2lkIjogIkxkeVRYd0F5ZnJpcjRfVjZORzFSYzEwVThKZExZVHJFQktKaF9oNWlfclUi +fQ.eyJub25jZSI6ICJYWk9VY28xdV9nRVBrbnhTNzhzV1dnIiwgImF1ZCI6ICJodHRwc +zovL2V4YW1wbGUuY29tL3ZlcmlmaWVyIiwgImlhdCI6IDE2NjkxOTM3MDR9.Y5xmEip1 +1zOmZR1xvgdbi9IanCNQ4ODJDNW8JKpNvBNOCP2j_DbsCMoYAv6RrOYSzEPZplJ8Mlfi +ZzC9ldcvByiAjWeJbMStZ1vf_K-AJnyNLwnA_v0zVwA0d8rkrIqSQomS5-GqN3zJtUpl +2BslLa799UpdCwg7Rfow2wFSfL6S2J9j2mb5Q5UVxTcU27OXcLUn0aN8slIr5AmYd-Ec +oAqWe8feV6plkHnYac6M9LclgInE5IzwFJTezNmERocADNB7ZGGoy9FzdNdepjnzjFVj +azYT0gtR0XgOg5epZoTeOrJwdYT0d5VFwO4ep9_Q591bxnNHfPbr5AhdYALCGg \ No newline at end of file diff --git a/examples/simple/disclosures.md b/examples/simple/disclosures.md new file mode 100644 index 00000000..36732ecf --- /dev/null +++ b/examples/simple/disclosures.md @@ -0,0 +1,103 @@ +__Disclosure for `sub`:__ + +``` +WyJkcVR2WE14UzBHYTNEb2FHbmU5eDBRIiwgInN1YiIsICJqb2huX2RvZV80MiJd +``` + +Contents: + +``` +["dqTvXMxS0Ga3DoaGne9x0Q", "sub", "john_doe_42"] +``` + +SHA-256 Hash: `ZkSJxxeGluIdYBb7CqkZbJVm0w2V5UrReNTzAQCYBjw` + +__Disclosure for `given_name`:__ + +``` +WyIzanFjYjY3ejl3a3MwOHp3aUs3RXlRIiwgImdpdmVuX25hbWUiLCAiSm9obiJd +``` + +Contents: + +``` +["3jqcb67z9wks08zwiK7EyQ", "given_name", "John"] +``` + +SHA-256 Hash: `qqvcqnczAMgYx7EykI6wwtspyvyvK790ge7MBbQ-Nus` + +__Disclosure for `family_name`:__ + +``` +WyJxUVdtakpsMXMxUjRscWhFTkxScnJ3IiwgImZhbWlseV9uYW1lIiwgIkRvZSJd +``` + +Contents: + +``` +["qQWmjJl1s1R4lqhENLRrrw", "family_name", "Doe"] +``` + +SHA-256 Hash: `l9qIJ9JTQwLG7OLEICTFBVxmArw8Pjy65dD6mtQVG5c` + +__Disclosure for `email`:__ + +``` +WyJLVXhTNWhFX1hiVmFjckdBYzdFRnd3IiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBs +ZS5jb20iXQ +``` + +Contents: + +``` +["KUxS5hE_XbVacrGAc7EFww", "email", "johndoe@example.com"] +``` + +SHA-256 Hash: `o1SAsJ33YMioO9pX5VeAM1lxuHF6hZW2kGdkKKBnVlo` + +__Disclosure for `phone_number`:__ + +``` +WyIzcXZWSjFCQURwSERTUzkzOVEtUml3IiwgInBob25lX251bWJlciIsICIrMS0yMDIt +NTU1LTAxMDEiXQ +``` + +Contents: + +``` +["3qvVJ1BADpHDSS939Q-Riw", "phone_number", "+1-202-555-0101"] +``` + +SHA-256 Hash: `SY8n2BbkX9lrY3exHlSwPRFXoD09GF8a9CPO-G8j208` + +__Disclosure for `address`:__ + +``` +WyIweEd6bjNNaXFzY3RaSV9PcERsQWJRIiwgImFkZHJlc3MiLCB7InN0cmVldF9hZGRy +ZXNzIjogIjEyMyBNYWluIFN0IiwgImxvY2FsaXR5IjogIkFueXRvd24iLCAicmVnaW9u +IjogIkFueXN0YXRlIiwgImNvdW50cnkiOiAiVVMifV0 +``` + +Contents: + +``` +["0xGzn3MiqsctZI_OpDlAbQ", "address", {"street_address": "123 Main +St", "locality": "Anytown", "region": "Anystate", "country": "US"}] +``` + +SHA-256 Hash: `TPsGNPYA46wmBxfv2znOJhfdoN5Y1GkezbpaGZCT1ac` + +__Disclosure for `birthdate`:__ + +``` +WyJFUktNMENOZUZKa2FENW1UWFZfWDh3IiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAx +Il0 +``` + +Contents: + +``` +["ERKM0CNeFJkaD5mTXV_X8w", "birthdate", "1940-01-01"] +``` + +SHA-256 Hash: `NYCoSRKEYwXdpe5yduJXCxxhynEU8z-b4TyNiap77UY` \ No newline at end of file diff --git a/examples/simple/hb_jwt_payload.json b/examples/simple/hb_jwt_payload.json new file mode 100644 index 00000000..7c945a16 --- /dev/null +++ b/examples/simple/hb_jwt_payload.json @@ -0,0 +1,5 @@ +{ + "nonce": "XZOUco1u_gEPknxS78sWWg", + "aud": "https://example.com/verifier", + "iat": 1669193704 +} \ No newline at end of file diff --git a/examples/simple/hb_jwt_serialized.txt b/examples/simple/hb_jwt_serialized.txt new file mode 100644 index 00000000..ce7e0da5 --- /dev/null +++ b/examples/simple/hb_jwt_serialized.txt @@ -0,0 +1,9 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogIkxkeVRYd0F5ZnJpcjRfVjZORzFSYzEwVThK +ZExZVHJFQktKaF9oNWlfclUifQ.eyJub25jZSI6ICJYWk9VY28xdV9nRVBrbnhTNzhzV +1dnIiwgImF1ZCI6ICJodHRwczovL2V4YW1wbGUuY29tL3ZlcmlmaWVyIiwgImlhdCI6I +DE2NjkxOTM3MDR9.Y5xmEip11zOmZR1xvgdbi9IanCNQ4ODJDNW8JKpNvBNOCP2j_Dbs +CMoYAv6RrOYSzEPZplJ8MlfiZzC9ldcvByiAjWeJbMStZ1vf_K-AJnyNLwnA_v0zVwA0 +d8rkrIqSQomS5-GqN3zJtUpl2BslLa799UpdCwg7Rfow2wFSfL6S2J9j2mb5Q5UVxTcU +27OXcLUn0aN8slIr5AmYd-EcoAqWe8feV6plkHnYac6M9LclgInE5IzwFJTezNmERocA +DNB7ZGGoy9FzdNdepjnzjFVjazYT0gtR0XgOg5epZoTeOrJwdYT0d5VFwO4ep9_Q591b +xnNHfPbr5AhdYALCGg \ No newline at end of file diff --git a/examples/simple/sd_jwt_payload.json b/examples/simple/sd_jwt_payload.json new file mode 100644 index 00000000..fc758222 --- /dev/null +++ b/examples/simple/sd_jwt_payload.json @@ -0,0 +1,27 @@ +{ + "_sd": [ + "NYCoSRKEYwXdpe5yduJXCxxhynEU8z-b4TyNiap77UY", + "SY8n2BbkX9lrY3exHlSwPRFXoD09GF8a9CPO-G8j208", + "TPsGNPYA46wmBxfv2znOJhfdoN5Y1GkezbpaGZCT1ac", + "ZkSJxxeGluIdYBb7CqkZbJVm0w2V5UrReNTzAQCYBjw", + "l9qIJ9JTQwLG7OLEICTFBVxmArw8Pjy65dD6mtQVG5c", + "o1SAsJ33YMioO9pX5VeAM1lxuHF6hZW2kGdkKKBnVlo", + "qqvcqnczAMgYx7EykI6wwtspyvyvK790ge7MBbQ-Nus" + ], + "iss": "https://example.com/issuer", + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256", + "cnf": { + "jwk": { + "kty": "RSA", + "n": "pm4bOHBg-oYhAyPWzR56AWX3rUIXp11_ICDkGgS6W3ZWLts-hzwI3x65 + 659kg4hVo9dbGoCJE3ZGF_eaetE30UhBUEgpGwrDrQiJ9zqprmcFfr3qvvkG + jtth8Zgl1eM2bJcOwE7PCBHWTKWYs152R7g6Jg2OVph-a8rq-q79MhKG5QoW + _mTz10QT_6H4c7PjWG1fjh8hpWNnbP_pv6d1zSwZfc5fl6yVRL0DV0V3lGHK + e2Wqf_eNGjBrBLVklDTk8-stX_MWLcR-EGmXAOv0UBWitS_dXJKJu-vXJyw1 + 4nHSGuxTIK2hx1pttMft9CsvqimXKeDTU14qQL1eE7ihcw", + "e": "AQAB" + } + } +} \ No newline at end of file diff --git a/examples/simple/sd_jwt_serialized.txt b/examples/simple/sd_jwt_serialized.txt new file mode 100644 index 00000000..1bb08f67 --- /dev/null +++ b/examples/simple/sd_jwt_serialized.txt @@ -0,0 +1,24 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbIk5ZQ29TUktFWXdYZHBlNXlkdUpYQ +3h4aHluRVU4ei1iNFR5TmlhcDc3VVkiLCAiU1k4bjJCYmtYOWxyWTNleEhsU3dQUkZYb +0QwOUdGOGE5Q1BPLUc4ajIwOCIsICJUUHNHTlBZQTQ2d21CeGZ2MnpuT0poZmRvTjVZM +UdrZXpicGFHWkNUMWFjIiwgIlprU0p4eGVHbHVJZFlCYjdDcWtaYkpWbTB3MlY1VXJSZ +U5UekFRQ1lCanciLCAibDlxSUo5SlRRd0xHN09MRUlDVEZCVnhtQXJ3OFBqeTY1ZEQ2b +XRRVkc1YyIsICJvMVNBc0ozM1lNaW9POXBYNVZlQU0xbHh1SEY2aFpXMmtHZGtLS0JuV +mxvIiwgInFxdmNxbmN6QU1nWXg3RXlrSTZ3d3RzcHl2eXZLNzkwZ2U3TUJiUS1OdXMiX +SwgImlzcyI6ICJodHRwczovL2V4YW1wbGUuY29tL2lzc3VlciIsICJpYXQiOiAxNTE2M +jM5MDIyLCAiZXhwIjogMTUxNjI0NzAyMiwgInNkX2RpZ2VzdF9kZXJpdmF0aW9uX2FsZ +yI6ICJzaGEtMjU2IiwgImNuZiI6IHsiandrIjogeyJrdHkiOiAiUlNBIiwgIm4iOiAic +G00Yk9IQmctb1loQXlQV3pSNTZBV1gzclVJWHAxMV9JQ0RrR2dTNlczWldMdHMtaHp3S +TN4NjU2NTlrZzRoVm85ZGJHb0NKRTNaR0ZfZWFldEUzMFVoQlVFZ3BHd3JEclFpSjl6c +XBybWNGZnIzcXZ2a0dqdHRoOFpnbDFlTTJiSmNPd0U3UENCSFdUS1dZczE1MlI3ZzZKZ +zJPVnBoLWE4cnEtcTc5TWhLRzVRb1dfbVR6MTBRVF82SDRjN1BqV0cxZmpoOGhwV05uY +lBfcHY2ZDF6U3daZmM1Zmw2eVZSTDBEVjBWM2xHSEtlMldxZl9lTkdqQnJCTFZrbERUa +zgtc3RYX01XTGNSLUVHbVhBT3YwVUJXaXRTX2RYSktKdS12WEp5dzE0bkhTR3V4VElLM +mh4MXB0dE1mdDlDc3ZxaW1YS2VEVFUxNHFRTDFlRTdpaGN3IiwgImUiOiAiQVFBQiJ9f +X0.rV7UrCdR64tA-G0_rjqYaKYukzIJi1RPgvX4eGSk5HHYWPl3-j8LEUnePSX-OlQXe +5RgsOqPo9DCj7-CLvKKxam-PbEQZurYX4qEzFyCJUOTMbOTQSLdsSe9tf1pD_yIrJhIs +cjXiq_mtJww_abdT8fIiS4h8kir2ClZifjNnLkKzqceVgrCipjbt76iZ6m_DGO2jFhU8 +Zzrat3UZALacw4fRD0l5MGIGHdQcipouhhvVBnWyl6lH8gtcOFMl4YSSoWP9ytFLoahC +SRrF5mCo1tSFg3I70v4mk2U59GfcU94GaEB0q6uF__fbEmOClUPX9yOrYryeqjJR0bBR +UK_NA \ No newline at end of file diff --git a/examples/simple/user_claims.json b/examples/simple/user_claims.json new file mode 100644 index 00000000..8dd6f189 --- /dev/null +++ b/examples/simple/user_claims.json @@ -0,0 +1,14 @@ +{ + "sub": "john_doe_42", + "given_name": "John", + "family_name": "Doe", + "email": "johndoe@example.com", + "phone_number": "+1-202-555-0101", + "address": { + "street_address": "123 Main St", + "locality": "Anytown", + "region": "Anystate", + "country": "US" + }, + "birthdate": "1940-01-01" +} \ No newline at end of file diff --git a/examples/simple/verified_contents.json b/examples/simple/verified_contents.json new file mode 100644 index 00000000..814dc5f1 --- /dev/null +++ b/examples/simple/verified_contents.json @@ -0,0 +1,26 @@ +{ + "iss": "https://example.com/issuer", + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256", + "cnf": { + "jwk": { + "kty": "RSA", + "n": "pm4bOHBg-oYhAyPWzR56AWX3rUIXp11_ICDkGgS6W3ZWLts-hzwI3x65 + 659kg4hVo9dbGoCJE3ZGF_eaetE30UhBUEgpGwrDrQiJ9zqprmcFfr3qvvkG + jtth8Zgl1eM2bJcOwE7PCBHWTKWYs152R7g6Jg2OVph-a8rq-q79MhKG5QoW + _mTz10QT_6H4c7PjWG1fjh8hpWNnbP_pv6d1zSwZfc5fl6yVRL0DV0V3lGHK + e2Wqf_eNGjBrBLVklDTk8-stX_MWLcR-EGmXAOv0UBWitS_dXJKJu-vXJyw1 + 4nHSGuxTIK2hx1pttMft9CsvqimXKeDTU14qQL1eE7ihcw", + "e": "AQAB" + } + }, + "address": { + "street_address": "123 Main St", + "locality": "Anytown", + "region": "Anystate", + "country": "US" + }, + "family_name": "Doe", + "given_name": "John" +} \ No newline at end of file diff --git a/examples/simple_structured/combined_issuance.txt b/examples/simple_structured/combined_issuance.txt new file mode 100644 index 00000000..c7c62e66 --- /dev/null +++ b/examples/simple_structured/combined_issuance.txt @@ -0,0 +1,32 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbIkQxMkZhYU1YQ09XT0Y0eEFVRzFzX +1RucWhaOTAzLUJrRGV3UGtoallJSFkiLCAiVnhRVlFkT0ZrNF9CT0hIWHJhTV9TRFNQU +HZ4VkNHVzZfNjl3YXdXcndOWSIsICJiVGF4REJSYnNCX2llNXpDU3JJZTR2VTIxMjdfb +2dBR0hGNlJ6d3VKNk9BIiwgImJ1WUNlQkp6YnIyWlJjMks5OXQ3MFBETDJTYVgtaVVtU +VJNWUlzOFlpVnMiLCAidFJnNXc2V2ZhdVZYVDJnRmMwWWpXZlJtOUdWNTBKSWNnQU80N +GtzLTBlayIsICJ5cmt1Q2E2ZzNyRndPX1ZtMzJldG5VMVF0NnRpTmtsQ282MnZYUVlFc +GljIl0sICJhZGRyZXNzIjogeyJfc2QiOiBbIkpPWDdfVWYtMlZtbGtBbE1sdzR4YzMxN +jduejVzaDdWOUVUZjFORkpEZTAiLCAiT05IMHdQNjFPNnBZUEtTVUlwTlNMYjA5emxrW +E1PaUlBa2QyNDczSmRVbyIsICJkNzhsQ1pzLXNERnB2c3pzbGtHRG8xbmN6dTZMeFZNe +G15QXExYl9tTk13IiwgInJlS0RqQ1QwMTlaTjFQdFZWSDJ4SnpyR00yZXMtMVJZNGF0Z +F9SV3Q2Q2siXX0sICJpc3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc3N1ZXIiLCAia +WF0IjogMTUxNjIzOTAyMiwgImV4cCI6IDE1MTYyNDcwMjIsICJzZF9kaWdlc3RfZGVya +XZhdGlvbl9hbGciOiAic2hhLTI1NiJ9.CLsoXjTsEoBfP668ZXCoKz0cpO-UqnBxDJqv +qDnkYpVdV2wg24ax9HFR5ubB7wICEM2dQGrO94qXlsOZ-dZIN3uT_TWqOGvydWeGcUdd +bmQTI0fP9KtDCKZ0MvikpyoKrKonjbJ70I6SOaiEa4kGh4Pbqf0_HaCBrNwacT0TArJ1 +8ITmvA1KWmkqzlqgYExWCPkcJIknGdniGi962dlTd0xEGfnIoQ-VWOxfzgoxDA43xkE8 +sPrjl2nzR_C7r3UywshltjKVvh6SGK3A88JNF1QdfszNdWyqGBf2kcZvJOK6r9UoIyEe +WDziiCQjZJkPM5Wa4mVOlmRt6mpn9zwyVQ~WyJCVzlzZUxQYllTUHpwZXJZNFQ0bnNBI +iwgInN1YiIsICI2YzVjMGE0OS1iNTg5LTQzMWQtYmFlNy0yMTkxMjJhOWVjMmMiXQ~Wy +JqYXRCZjJEYTNDbmYxQWRkZzFvSjZnIiwgImdpdmVuX25hbWUiLCAiXHU1OTJhXHU5MG +NlIl0~WyJabEItelBYQmFfai1HQlVZYmJYNjRRIiwgImZhbWlseV9uYW1lIiwgIlx1NW +M3MVx1NzUzMCJd~WyJpWDFuazhIZ3RXNVJzY1JTV3FCS2R3IiwgImVtYWlsIiwgIlwid +W51c3VhbCBlbWFpbCBhZGRyZXNzXCJAbmlob24uY29tIl0~WyJYYmZFSWRlNlR6T215M +1pERGJ4MVJ3IiwgInBob25lX251bWJlciIsICIxMjM0NTYiXQ~WyJONEhuU0ZmQTJtNG +N1MlJwNXdISEZRIiwgInN0cmVldF9hZGRyZXNzIiwgIlx1Njc3MVx1NGVhY1x1OTBmZF +x1NmUyZlx1NTMzYVx1ODI5ZFx1NTE2Y1x1NTcxMlx1ZmYxNFx1NGUwMVx1NzZlZVx1Zm +YxMlx1MjIxMlx1ZmYxOCJd~WyI5WmRLOTZwQVFNRThiUjR3ZXFiemZBIiwgImxvY2Fsa +XR5IiwgIlx1Njc3MVx1NGVhY1x1OTBmZCJd~WyJDdEJ3VmEzd2ZiUEVGekhWLU0zaUJ3 +IiwgInJlZ2lvbiIsICJcdTZlMmZcdTUzM2EiXQ~WyJqMjhmbThJVlhQN3Q1TlZJb3FOe +DBRIiwgImNvdW50cnkiLCAiSlAiXQ~WyJCT05tUFE4S2ZtUW5FdWx0aTF3UTd3IiwgIm +JpcnRoZGF0ZSIsICIxOTQwLTAxLTAxIl0 \ No newline at end of file diff --git a/examples/simple_structured/combined_presentation.txt b/examples/simple_structured/combined_presentation.txt new file mode 100644 index 00000000..1d02159e --- /dev/null +++ b/examples/simple_structured/combined_presentation.txt @@ -0,0 +1,26 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbIkQxMkZhYU1YQ09XT0Y0eEFVRzFzX +1RucWhaOTAzLUJrRGV3UGtoallJSFkiLCAiVnhRVlFkT0ZrNF9CT0hIWHJhTV9TRFNQU +HZ4VkNHVzZfNjl3YXdXcndOWSIsICJiVGF4REJSYnNCX2llNXpDU3JJZTR2VTIxMjdfb +2dBR0hGNlJ6d3VKNk9BIiwgImJ1WUNlQkp6YnIyWlJjMks5OXQ3MFBETDJTYVgtaVVtU +VJNWUlzOFlpVnMiLCAidFJnNXc2V2ZhdVZYVDJnRmMwWWpXZlJtOUdWNTBKSWNnQU80N +GtzLTBlayIsICJ5cmt1Q2E2ZzNyRndPX1ZtMzJldG5VMVF0NnRpTmtsQ282MnZYUVlFc +GljIl0sICJhZGRyZXNzIjogeyJfc2QiOiBbIkpPWDdfVWYtMlZtbGtBbE1sdzR4YzMxN +jduejVzaDdWOUVUZjFORkpEZTAiLCAiT05IMHdQNjFPNnBZUEtTVUlwTlNMYjA5emxrW +E1PaUlBa2QyNDczSmRVbyIsICJkNzhsQ1pzLXNERnB2c3pzbGtHRG8xbmN6dTZMeFZNe +G15QXExYl9tTk13IiwgInJlS0RqQ1QwMTlaTjFQdFZWSDJ4SnpyR00yZXMtMVJZNGF0Z +F9SV3Q2Q2siXX0sICJpc3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc3N1ZXIiLCAia +WF0IjogMTUxNjIzOTAyMiwgImV4cCI6IDE1MTYyNDcwMjIsICJzZF9kaWdlc3RfZGVya +XZhdGlvbl9hbGciOiAic2hhLTI1NiJ9.CLsoXjTsEoBfP668ZXCoKz0cpO-UqnBxDJqv +qDnkYpVdV2wg24ax9HFR5ubB7wICEM2dQGrO94qXlsOZ-dZIN3uT_TWqOGvydWeGcUdd +bmQTI0fP9KtDCKZ0MvikpyoKrKonjbJ70I6SOaiEa4kGh4Pbqf0_HaCBrNwacT0TArJ1 +8ITmvA1KWmkqzlqgYExWCPkcJIknGdniGi962dlTd0xEGfnIoQ-VWOxfzgoxDA43xkE8 +sPrjl2nzR_C7r3UywshltjKVvh6SGK3A88JNF1QdfszNdWyqGBf2kcZvJOK6r9UoIyEe +WDziiCQjZJkPM5Wa4mVOlmRt6mpn9zwyVQ~WyJabEItelBYQmFfai1HQlVZYmJYNjRRI +iwgImZhbWlseV9uYW1lIiwgIlx1NWM3MVx1NzUzMCJd~WyJpWDFuazhIZ3RXNVJzY1JT +V3FCS2R3IiwgImVtYWlsIiwgIlwidW51c3VhbCBlbWFpbCBhZGRyZXNzXCJAbmlob24u +Y29tIl0~WyJCT05tUFE4S2ZtUW5FdWx0aTF3UTd3IiwgImJpcnRoZGF0ZSIsICIxOTQw +LTAxLTAxIl0~WyJqYXRCZjJEYTNDbmYxQWRkZzFvSjZnIiwgImdpdmVuX25hbWUiLCAi +XHU1OTJhXHU5MGNlIl0~WyJqMjhmbThJVlhQN3Q1TlZJb3FOeDBRIiwgImNvdW50cnki +LCAiSlAiXQ~WyJDdEJ3VmEzd2ZiUEVGekhWLU0zaUJ3IiwgInJlZ2lvbiIsICJcdTZlM +mZcdTUzM2EiXQ~ \ No newline at end of file diff --git a/examples/simple_structured/disclosures.md b/examples/simple_structured/disclosures.md new file mode 100644 index 00000000..9e5b5b37 --- /dev/null +++ b/examples/simple_structured/disclosures.md @@ -0,0 +1,152 @@ +__Disclosure for `sub`:__ + +``` +WyJCVzlzZUxQYllTUHpwZXJZNFQ0bnNBIiwgInN1YiIsICI2YzVjMGE0OS1iNTg5LTQz +MWQtYmFlNy0yMTkxMjJhOWVjMmMiXQ +``` + +Contents: + +``` +["BW9seLPbYSPzperY4T4nsA", "sub", +"6c5c0a49-b589-431d-bae7-219122a9ec2c"] +``` + +SHA-256 Hash: `bTaxDBRbsB_ie5zCSrIe4vU2127_ogAGHF6RzwuJ6OA` + +__Disclosure for `given_name`:__ + +``` +WyJqYXRCZjJEYTNDbmYxQWRkZzFvSjZnIiwgImdpdmVuX25hbWUiLCAiXHU1OTJhXHU5 +MGNlIl0 +``` + +Contents: + +``` +["jatBf2Da3Cnf1Addg1oJ6g", "given_name", "\u592a\u90ce"] +``` + +SHA-256 Hash: `yrkuCa6g3rFwO_Vm32etnU1Qt6tiNklCo62vXQYEpic` + +__Disclosure for `family_name`:__ + +``` +WyJabEItelBYQmFfai1HQlVZYmJYNjRRIiwgImZhbWlseV9uYW1lIiwgIlx1NWM3MVx1 +NzUzMCJd +``` + +Contents: + +``` +["ZlB-zPXBa_j-GBUYbbX64Q", "family_name", "\u5c71\u7530"] +``` + +SHA-256 Hash: `D12FaaMXCOWOF4xAUG1s_TnqhZ903-BkDewPkhjYIHY` + +__Disclosure for `email`:__ + +``` +WyJpWDFuazhIZ3RXNVJzY1JTV3FCS2R3IiwgImVtYWlsIiwgIlwidW51c3VhbCBlbWFp +bCBhZGRyZXNzXCJAbmlob24uY29tIl0 +``` + +Contents: + +``` +["iX1nk8HgtW5RscRSWqBKdw", "email", "\"unusual email +address\"@nihon.com"] +``` + +SHA-256 Hash: `buYCeBJzbr2ZRc2K99t70PDL2SaX-iUmQRMYIs8YiVs` + +__Disclosure for `phone_number`:__ + +``` +WyJYYmZFSWRlNlR6T215M1pERGJ4MVJ3IiwgInBob25lX251bWJlciIsICIxMjM0NTYi +XQ +``` + +Contents: + +``` +["XbfEIde6TzOmy3ZDDbx1Rw", "phone_number", "123456"] +``` + +SHA-256 Hash: `VxQVQdOFk4_BOHHXraM_SDSPPvxVCGW6_69wawWrwNY` + +__Disclosure for `street_address`:__ + +``` +WyJONEhuU0ZmQTJtNGN1MlJwNXdISEZRIiwgInN0cmVldF9hZGRyZXNzIiwgIlx1Njc3 +MVx1NGVhY1x1OTBmZFx1NmUyZlx1NTMzYVx1ODI5ZFx1NTE2Y1x1NTcxMlx1ZmYxNFx1 +NGUwMVx1NzZlZVx1ZmYxMlx1MjIxMlx1ZmYxOCJd +``` + +Contents: + +``` +["N4HnSFfA2m4cu2Rp5wHHFQ", "street_address", "\u6771\u4eac\u90fd\u6e +2f\u533a\u829d\u516c\u5712\uff14\u4e01\u76ee\uff12\u2212\uff18"] +``` + +SHA-256 Hash: `reKDjCT019ZN1PtVVH2xJzrGM2es-1RY4atd_RWt6Ck` + +__Disclosure for `locality`:__ + +``` +WyI5WmRLOTZwQVFNRThiUjR3ZXFiemZBIiwgImxvY2FsaXR5IiwgIlx1Njc3MVx1NGVh +Y1x1OTBmZCJd +``` + +Contents: + +``` +["9ZdK96pAQME8bR4weqbzfA", "locality", "\u6771\u4eac\u90fd"] +``` + +SHA-256 Hash: `d78lCZs-sDFpvszslkGDo1nczu6LxVMxmyAq1b_mNMw` + +__Disclosure for `region`:__ + +``` +WyJDdEJ3VmEzd2ZiUEVGekhWLU0zaUJ3IiwgInJlZ2lvbiIsICJcdTZlMmZcdTUzM2Ei +XQ +``` + +Contents: + +``` +["CtBwVa3wfbPEFzHV-M3iBw", "region", "\u6e2f\u533a"] +``` + +SHA-256 Hash: `ONH0wP61O6pYPKSUIpNSLb09zlkXMOiIAkd2473JdUo` + +__Disclosure for `country`:__ + +``` +WyJqMjhmbThJVlhQN3Q1TlZJb3FOeDBRIiwgImNvdW50cnkiLCAiSlAiXQ +``` + +Contents: + +``` +["j28fm8IVXP7t5NVIoqNx0Q", "country", "JP"] +``` + +SHA-256 Hash: `JOX7_Uf-2VmlkAlMlw4xc3167nz5sh7V9ETf1NFJDe0` + +__Disclosure for `birthdate`:__ + +``` +WyJCT05tUFE4S2ZtUW5FdWx0aTF3UTd3IiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAx +Il0 +``` + +Contents: + +``` +["BONmPQ8KfmQnEulti1wQ7w", "birthdate", "1940-01-01"] +``` + +SHA-256 Hash: `tRg5w6WfauVXT2gFc0YjWfRm9GV50JIcgAO44ks-0ek` \ No newline at end of file diff --git a/examples/simple_structured/hb_jwt_serialized.txt b/examples/simple_structured/hb_jwt_serialized.txt new file mode 100644 index 00000000..e69de29b diff --git a/examples/simple_structured/sd_jwt_payload.json b/examples/simple_structured/sd_jwt_payload.json new file mode 100644 index 00000000..905e2074 --- /dev/null +++ b/examples/simple_structured/sd_jwt_payload.json @@ -0,0 +1,22 @@ +{ + "_sd": [ + "D12FaaMXCOWOF4xAUG1s_TnqhZ903-BkDewPkhjYIHY", + "VxQVQdOFk4_BOHHXraM_SDSPPvxVCGW6_69wawWrwNY", + "bTaxDBRbsB_ie5zCSrIe4vU2127_ogAGHF6RzwuJ6OA", + "buYCeBJzbr2ZRc2K99t70PDL2SaX-iUmQRMYIs8YiVs", + "tRg5w6WfauVXT2gFc0YjWfRm9GV50JIcgAO44ks-0ek", + "yrkuCa6g3rFwO_Vm32etnU1Qt6tiNklCo62vXQYEpic" + ], + "address": { + "_sd": [ + "JOX7_Uf-2VmlkAlMlw4xc3167nz5sh7V9ETf1NFJDe0", + "ONH0wP61O6pYPKSUIpNSLb09zlkXMOiIAkd2473JdUo", + "d78lCZs-sDFpvszslkGDo1nczu6LxVMxmyAq1b_mNMw", + "reKDjCT019ZN1PtVVH2xJzrGM2es-1RY4atd_RWt6Ck" + ] + }, + "iss": "https://example.com/issuer", + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256" +} \ No newline at end of file diff --git a/examples/simple_structured/sd_jwt_serialized.txt b/examples/simple_structured/sd_jwt_serialized.txt new file mode 100644 index 00000000..617d7a26 --- /dev/null +++ b/examples/simple_structured/sd_jwt_serialized.txt @@ -0,0 +1,19 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbIkQxMkZhYU1YQ09XT0Y0eEFVRzFzX +1RucWhaOTAzLUJrRGV3UGtoallJSFkiLCAiVnhRVlFkT0ZrNF9CT0hIWHJhTV9TRFNQU +HZ4VkNHVzZfNjl3YXdXcndOWSIsICJiVGF4REJSYnNCX2llNXpDU3JJZTR2VTIxMjdfb +2dBR0hGNlJ6d3VKNk9BIiwgImJ1WUNlQkp6YnIyWlJjMks5OXQ3MFBETDJTYVgtaVVtU +VJNWUlzOFlpVnMiLCAidFJnNXc2V2ZhdVZYVDJnRmMwWWpXZlJtOUdWNTBKSWNnQU80N +GtzLTBlayIsICJ5cmt1Q2E2ZzNyRndPX1ZtMzJldG5VMVF0NnRpTmtsQ282MnZYUVlFc +GljIl0sICJhZGRyZXNzIjogeyJfc2QiOiBbIkpPWDdfVWYtMlZtbGtBbE1sdzR4YzMxN +jduejVzaDdWOUVUZjFORkpEZTAiLCAiT05IMHdQNjFPNnBZUEtTVUlwTlNMYjA5emxrW +E1PaUlBa2QyNDczSmRVbyIsICJkNzhsQ1pzLXNERnB2c3pzbGtHRG8xbmN6dTZMeFZNe +G15QXExYl9tTk13IiwgInJlS0RqQ1QwMTlaTjFQdFZWSDJ4SnpyR00yZXMtMVJZNGF0Z +F9SV3Q2Q2siXX0sICJpc3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc3N1ZXIiLCAia +WF0IjogMTUxNjIzOTAyMiwgImV4cCI6IDE1MTYyNDcwMjIsICJzZF9kaWdlc3RfZGVya +XZhdGlvbl9hbGciOiAic2hhLTI1NiJ9.CLsoXjTsEoBfP668ZXCoKz0cpO-UqnBxDJqv +qDnkYpVdV2wg24ax9HFR5ubB7wICEM2dQGrO94qXlsOZ-dZIN3uT_TWqOGvydWeGcUdd +bmQTI0fP9KtDCKZ0MvikpyoKrKonjbJ70I6SOaiEa4kGh4Pbqf0_HaCBrNwacT0TArJ1 +8ITmvA1KWmkqzlqgYExWCPkcJIknGdniGi962dlTd0xEGfnIoQ-VWOxfzgoxDA43xkE8 +sPrjl2nzR_C7r3UywshltjKVvh6SGK3A88JNF1QdfszNdWyqGBf2kcZvJOK6r9UoIyEe +WDziiCQjZJkPM5Wa4mVOlmRt6mpn9zwyVQ \ No newline at end of file diff --git a/examples/simple_structured/user_claims.json b/examples/simple_structured/user_claims.json new file mode 100644 index 00000000..1aef4cc3 --- /dev/null +++ b/examples/simple_structured/user_claims.json @@ -0,0 +1,14 @@ +{ + "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", + "given_name": "太郎", + "family_name": "山田", + "email": "\"unusual email address\"@nihon.com", + "phone_number": "123456", + "address": { + "street_address": "東京都港区芝公園4丁目2−8", + "locality": "東京都", + "region": "港区", + "country": "JP" + }, + "birthdate": "1940-01-01" +} \ No newline at end of file diff --git a/examples/simple_structured/verified_contents.json b/examples/simple_structured/verified_contents.json new file mode 100644 index 00000000..902133a6 --- /dev/null +++ b/examples/simple_structured/verified_contents.json @@ -0,0 +1,14 @@ +{ + "address": { + "country": "JP", + "region": "港区" + }, + "iss": "https://example.com/issuer", + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256", + "family_name": "山田", + "email": "\"unusual email address\"@nihon.com", + "birthdate": "1940-01-01", + "given_name": "太郎" +} \ No newline at end of file diff --git a/examples/simple_structured_with_decoys/combined_issuance.txt b/examples/simple_structured_with_decoys/combined_issuance.txt new file mode 100644 index 00000000..e95fe29e --- /dev/null +++ b/examples/simple_structured_with_decoys/combined_issuance.txt @@ -0,0 +1,39 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbIjAtLWNnemtnTTkxSnlmSUFfaVpXX +2JrME5UTmprLS1Na011YUFfdHVJeFkiLCAiRzBvdkE3VE1DNEpkRER0NTRQNUVCZERjU +VNhdGI3N0g1enVmWmVIeXhJYyIsICJIZk84T0JENVRpTzlOa1ZNWXVIZjJ5MXRSUFJ6c +080dkQ0a1VPM2stLVJjIiwgIkk1TUlPM0xwcExBRW9PNGI5MXVmSDJPMDg3OGhzcHVKR +UJKU21BX1M1RnMiLCAiSWlBcGtoaWc4V2lJcE9iMzd1MWEtb0xoWXV5Rmt4T0VCWE5DN +0RTTXZMNCIsICJPZ3h5cXFwNmpGUnZmbURNekVkeWNoZms2RUtvRm04UVdNTmNCZDJBc +UFrIiwgIlNfbloxUFM2WXVhaTNCU0k3Vk1Va0RlaTA1eEVlaUNqNVJkV0FiZlBCLWMiL +CAiaHU2eWRMaDJZdks1aHNmZUxpRG0yZ2NCdVB4cGZGaEJaMV90UzlNN0xkMCIsICJpU +lMyZFZBbF9icFhseXZ3RFNTaEVKMjJ2MjhaRmZYb1RkX3ZwNVFOQ3pNIiwgImxLVE1He +mYwZDc5QmFpd3VFcE1ZQzI2aEFCZUlVdGNJN1dIbDd6Z2VYQ1UiLCAic2pFZ0E4LWo0N +FQtMlhYSlpzaHpET25MU2FMUUY4SXloYjRhN0JjWnJSWSJdLCAiYWRkcmVzcyI6IHsiX +3NkIjogWyIwSk9IX1pzakhwelU2UkJZQlR6U09XUnpUNkJQTllCUm8zZWFhMG1iNlFjI +iwgIk85Z0w3VG9mNFB0bVN6dUc2QzI3NTh1dnBxSmxKaXNNdEpYQUQ4RERTQlUiLCAiT +1MzOGtVMy1JcnhQcGJzMU93ZG1yMUJzWHc3bzZ2Nld2bzAtUDhHajRjRSIsICJiVHZid +jlfRjRFR01Ud09aOUJLZ3VFUzlwMldDR0MyeFh5WjF0aVBXdU5JIiwgImw4SnZnTEt1U +FYtSXBLUk5IZzE1RFZKLWYyeWZfRE5YTHJJVzVVam1qd3MiLCAic3JZTkhfa1otaDdWa +0kxT0FwOG5kejkxNTFxNHFRcGtGaVVlSDhUOF9ocyJdfSwgImlzcyI6ICJodHRwczovL +2V4YW1wbGUuY29tL2lzc3VlciIsICJpYXQiOiAxNTE2MjM5MDIyLCAiZXhwIjogMTUxN +jI0NzAyMiwgInNkX2RpZ2VzdF9kZXJpdmF0aW9uX2FsZyI6ICJzaGEtMjU2In0.QMvnE +-_WJ9kkBgbSE8pZdqUd1TLyNUcKSWEjxKebX2FMBAf6C30uJnY3s-0T74J2sQzr5qo0p +nasADs78A_wbjTLGmJxj1Yb-CyS35ApXXBqnOfYf87oDwhzslads1VJbP3VvXfqTt6JD +PZZiXnYpCpWXk31bacV8Py75h6k439_ESwZXK4K_PbTresjtgcSDOBVcI2kRNHKBniXu +sFMhrBlW-FdhrZKWGnewExCXz5PKyVNO0RXbYOQETcOUSdNHLevojtWx_jLbsmjJwzWH +bjloqkc-N7lKqXh1DUSBAl-lbj7maQNXDqg74Y7gyEnA2vsTEt3MQoPHbIDOAiVDg~Wy +JQZjVCMUsybEFDT0RjNXdHTjZ5SldRIiwgInN1YiIsICI2YzVjMGE0OS1iNTg5LTQzMW +QtYmFlNy0yMTkxMjJhOWVjMmMiXQ~WyI2SmJ6TUVxUk5PRy1LUE5ra180cXpBIiwgImd +pdmVuX25hbWUiLCAiXHU1OTJhXHU5MGNlIl0~WyJuM3g5cHRtMExHcEg1NDNpbkhubFN +nIiwgImZhbWlseV9uYW1lIiwgIlx1NWM3MVx1NzUzMCJd~WyJUcm5QcVpDZ2paQVUzZm +VIS09KNEFRIiwgImVtYWlsIiwgIlwidW51c3VhbCBlbWFpbCBhZGRyZXNzXCJAXHU2NW +U1XHU2NzJjLmNvbSJd~WyItRUstVlNEMFJNckhuWjNJdFdRR3dBIiwgInBob25lX251b +WJlciIsICIxMjM0NTYiXQ~WyJ5RjRSQVk0UGZMcnZQNUtwUWJ6bVZnIiwgInN0cmVldF +9hZGRyZXNzIiwgIlx1Njc3MVx1NGVhY1x1OTBmZFx1NmUyZlx1NTMzYVx1ODI5ZFx1NT +E2Y1x1NTcxMlx1ZmYxNFx1NGUwMVx1NzZlZVx1ZmYxMlx1MjIxMlx1ZmYxOCJd~WyIwR +W9HeFhhekdNLWhIam8xalR4TkRBIiwgImxvY2FsaXR5IiwgIlx1Njc3MVx1NGVhY1x1O +TBmZCJd~WyJLal9Wd0ZBa2ZiMmZabXM5ckoxUE1nIiwgInJlZ2lvbiIsICJcdTZlMmZc +dTUzM2EiXQ~WyIybTNwTExHNHhPWUxWR3B4WXJCakpBIiwgImNvdW50cnkiLCAiSlAiX +Q~WyJmdXVhOVBVVTJIRDI1aHRuOEF4bTlBIiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLT +AxIl0 \ No newline at end of file diff --git a/examples/simple_structured_with_decoys/combined_presentation.txt b/examples/simple_structured_with_decoys/combined_presentation.txt new file mode 100644 index 00000000..4dcdbd50 --- /dev/null +++ b/examples/simple_structured_with_decoys/combined_presentation.txt @@ -0,0 +1,32 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbIjAtLWNnemtnTTkxSnlmSUFfaVpXX +2JrME5UTmprLS1Na011YUFfdHVJeFkiLCAiRzBvdkE3VE1DNEpkRER0NTRQNUVCZERjU +VNhdGI3N0g1enVmWmVIeXhJYyIsICJIZk84T0JENVRpTzlOa1ZNWXVIZjJ5MXRSUFJ6c +080dkQ0a1VPM2stLVJjIiwgIkk1TUlPM0xwcExBRW9PNGI5MXVmSDJPMDg3OGhzcHVKR +UJKU21BX1M1RnMiLCAiSWlBcGtoaWc4V2lJcE9iMzd1MWEtb0xoWXV5Rmt4T0VCWE5DN +0RTTXZMNCIsICJPZ3h5cXFwNmpGUnZmbURNekVkeWNoZms2RUtvRm04UVdNTmNCZDJBc +UFrIiwgIlNfbloxUFM2WXVhaTNCU0k3Vk1Va0RlaTA1eEVlaUNqNVJkV0FiZlBCLWMiL +CAiaHU2eWRMaDJZdks1aHNmZUxpRG0yZ2NCdVB4cGZGaEJaMV90UzlNN0xkMCIsICJpU +lMyZFZBbF9icFhseXZ3RFNTaEVKMjJ2MjhaRmZYb1RkX3ZwNVFOQ3pNIiwgImxLVE1He +mYwZDc5QmFpd3VFcE1ZQzI2aEFCZUlVdGNJN1dIbDd6Z2VYQ1UiLCAic2pFZ0E4LWo0N +FQtMlhYSlpzaHpET25MU2FMUUY4SXloYjRhN0JjWnJSWSJdLCAiYWRkcmVzcyI6IHsiX +3NkIjogWyIwSk9IX1pzakhwelU2UkJZQlR6U09XUnpUNkJQTllCUm8zZWFhMG1iNlFjI +iwgIk85Z0w3VG9mNFB0bVN6dUc2QzI3NTh1dnBxSmxKaXNNdEpYQUQ4RERTQlUiLCAiT +1MzOGtVMy1JcnhQcGJzMU93ZG1yMUJzWHc3bzZ2Nld2bzAtUDhHajRjRSIsICJiVHZid +jlfRjRFR01Ud09aOUJLZ3VFUzlwMldDR0MyeFh5WjF0aVBXdU5JIiwgImw4SnZnTEt1U +FYtSXBLUk5IZzE1RFZKLWYyeWZfRE5YTHJJVzVVam1qd3MiLCAic3JZTkhfa1otaDdWa +0kxT0FwOG5kejkxNTFxNHFRcGtGaVVlSDhUOF9ocyJdfSwgImlzcyI6ICJodHRwczovL +2V4YW1wbGUuY29tL2lzc3VlciIsICJpYXQiOiAxNTE2MjM5MDIyLCAiZXhwIjogMTUxN +jI0NzAyMiwgInNkX2RpZ2VzdF9kZXJpdmF0aW9uX2FsZyI6ICJzaGEtMjU2In0.QMvnE +-_WJ9kkBgbSE8pZdqUd1TLyNUcKSWEjxKebX2FMBAf6C30uJnY3s-0T74J2sQzr5qo0p +nasADs78A_wbjTLGmJxj1Yb-CyS35ApXXBqnOfYf87oDwhzslads1VJbP3VvXfqTt6JD +PZZiXnYpCpWXk31bacV8Py75h6k439_ESwZXK4K_PbTresjtgcSDOBVcI2kRNHKBniXu +sFMhrBlW-FdhrZKWGnewExCXz5PKyVNO0RXbYOQETcOUSdNHLevojtWx_jLbsmjJwzWH +bjloqkc-N7lKqXh1DUSBAl-lbj7maQNXDqg74Y7gyEnA2vsTEt3MQoPHbIDOAiVDg~Wy +JmdXVhOVBVVTJIRDI1aHRuOEF4bTlBIiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAxIl +0~WyI2SmJ6TUVxUk5PRy1LUE5ra180cXpBIiwgImdpdmVuX25hbWUiLCAiXHU1OTJhXH +U5MGNlIl0~WyJUcm5QcVpDZ2paQVUzZmVIS09KNEFRIiwgImVtYWlsIiwgIlwidW51c3 +VhbCBlbWFpbCBhZGRyZXNzXCJAXHU2NWU1XHU2NzJjLmNvbSJd~WyJuM3g5cHRtMExHc +Eg1NDNpbkhubFNnIiwgImZhbWlseV9uYW1lIiwgIlx1NWM3MVx1NzUzMCJd~WyIybTNw +TExHNHhPWUxWR3B4WXJCakpBIiwgImNvdW50cnkiLCAiSlAiXQ~WyJLal9Wd0ZBa2ZiM +mZabXM5ckoxUE1nIiwgInJlZ2lvbiIsICJcdTZlMmZcdTUzM2EiXQ~ \ No newline at end of file diff --git a/examples/simple_structured_with_decoys/disclosures.md b/examples/simple_structured_with_decoys/disclosures.md new file mode 100644 index 00000000..585d63e8 --- /dev/null +++ b/examples/simple_structured_with_decoys/disclosures.md @@ -0,0 +1,152 @@ +__Disclosure for `sub`:__ + +``` +WyJQZjVCMUsybEFDT0RjNXdHTjZ5SldRIiwgInN1YiIsICI2YzVjMGE0OS1iNTg5LTQz +MWQtYmFlNy0yMTkxMjJhOWVjMmMiXQ +``` + +Contents: + +``` +["Pf5B1K2lACODc5wGN6yJWQ", "sub", +"6c5c0a49-b589-431d-bae7-219122a9ec2c"] +``` + +SHA-256 Hash: `I5MIO3LppLAEoO4b91ufH2O0878hspuJEBJSmA_S5Fs` + +__Disclosure for `given_name`:__ + +``` +WyI2SmJ6TUVxUk5PRy1LUE5ra180cXpBIiwgImdpdmVuX25hbWUiLCAiXHU1OTJhXHU5 +MGNlIl0 +``` + +Contents: + +``` +["6JbzMEqRNOG-KPNkk_4qzA", "given_name", "\u592a\u90ce"] +``` + +SHA-256 Hash: `IiApkhig8WiIpOb37u1a-oLhYuyFkxOEBXNC7DSMvL4` + +__Disclosure for `family_name`:__ + +``` +WyJuM3g5cHRtMExHcEg1NDNpbkhubFNnIiwgImZhbWlseV9uYW1lIiwgIlx1NWM3MVx1 +NzUzMCJd +``` + +Contents: + +``` +["n3x9ptm0LGpH543inHnlSg", "family_name", "\u5c71\u7530"] +``` + +SHA-256 Hash: `lKTMGzf0d79BaiwuEpMYC26hABeIUtcI7WHl7zgeXCU` + +__Disclosure for `email`:__ + +``` +WyJUcm5QcVpDZ2paQVUzZmVIS09KNEFRIiwgImVtYWlsIiwgIlwidW51c3VhbCBlbWFp +bCBhZGRyZXNzXCJAXHU2NWU1XHU2NzJjLmNvbSJd +``` + +Contents: + +``` +["TrnPqZCgjZAU3feHKOJ4AQ", "email", "\"unusual email +address\"@\u65e5\u672c.com"] +``` + +SHA-256 Hash: `iRS2dVAl_bpXlyvwDSShEJ22v28ZFfXoTd_vp5QNCzM` + +__Disclosure for `phone_number`:__ + +``` +WyItRUstVlNEMFJNckhuWjNJdFdRR3dBIiwgInBob25lX251bWJlciIsICIxMjM0NTYi +XQ +``` + +Contents: + +``` +["-EK-VSD0RMrHnZ3ItWQGwA", "phone_number", "123456"] +``` + +SHA-256 Hash: `HfO8OBD5TiO9NkVMYuHf2y1tRPRzsO4vD4kUO3k--Rc` + +__Disclosure for `street_address`:__ + +``` +WyJ5RjRSQVk0UGZMcnZQNUtwUWJ6bVZnIiwgInN0cmVldF9hZGRyZXNzIiwgIlx1Njc3 +MVx1NGVhY1x1OTBmZFx1NmUyZlx1NTMzYVx1ODI5ZFx1NTE2Y1x1NTcxMlx1ZmYxNFx1 +NGUwMVx1NzZlZVx1ZmYxMlx1MjIxMlx1ZmYxOCJd +``` + +Contents: + +``` +["yF4RAY4PfLrvP5KpQbzmVg", "street_address", "\u6771\u4eac\u90fd\u6e +2f\u533a\u829d\u516c\u5712\uff14\u4e01\u76ee\uff12\u2212\uff18"] +``` + +SHA-256 Hash: `O9gL7Tof4PtmSzuG6C2758uvpqJlJisMtJXAD8DDSBU` + +__Disclosure for `locality`:__ + +``` +WyIwRW9HeFhhekdNLWhIam8xalR4TkRBIiwgImxvY2FsaXR5IiwgIlx1Njc3MVx1NGVh +Y1x1OTBmZCJd +``` + +Contents: + +``` +["0EoGxXazGM-hHjo1jTxNDA", "locality", "\u6771\u4eac\u90fd"] +``` + +SHA-256 Hash: `OS38kU3-IrxPpbs1Owdmr1BsXw7o6v6Wvo0-P8Gj4cE` + +__Disclosure for `region`:__ + +``` +WyJLal9Wd0ZBa2ZiMmZabXM5ckoxUE1nIiwgInJlZ2lvbiIsICJcdTZlMmZcdTUzM2Ei +XQ +``` + +Contents: + +``` +["Kj_VwFAkfb2fZms9rJ1PMg", "region", "\u6e2f\u533a"] +``` + +SHA-256 Hash: `l8JvgLKuPV-IpKRNHg15DVJ-f2yf_DNXLrIW5Ujmjws` + +__Disclosure for `country`:__ + +``` +WyIybTNwTExHNHhPWUxWR3B4WXJCakpBIiwgImNvdW50cnkiLCAiSlAiXQ +``` + +Contents: + +``` +["2m3pLLG4xOYLVGpxYrBjJA", "country", "JP"] +``` + +SHA-256 Hash: `bTvbv9_F4EGMTwOZ9BKguES9p2WCGC2xXyZ1tiPWuNI` + +__Disclosure for `birthdate`:__ + +``` +WyJmdXVhOVBVVTJIRDI1aHRuOEF4bTlBIiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAx +Il0 +``` + +Contents: + +``` +["fuua9PUU2HD25htn8Axm9A", "birthdate", "1940-01-01"] +``` + +SHA-256 Hash: `0--cgzkgM91JyfIA_iZW_bk0NTNjk--MkMuaA_tuIxY` \ No newline at end of file diff --git a/examples/simple_structured_with_decoys/hb_jwt_serialized.txt b/examples/simple_structured_with_decoys/hb_jwt_serialized.txt new file mode 100644 index 00000000..e69de29b diff --git a/examples/simple_structured_with_decoys/sd_jwt_payload.json b/examples/simple_structured_with_decoys/sd_jwt_payload.json new file mode 100644 index 00000000..c4c59143 --- /dev/null +++ b/examples/simple_structured_with_decoys/sd_jwt_payload.json @@ -0,0 +1,29 @@ +{ + "_sd": [ + "0--cgzkgM91JyfIA_iZW_bk0NTNjk--MkMuaA_tuIxY", + "G0ovA7TMC4JdDDt54P5EBdDcQSatb77H5zufZeHyxIc", + "HfO8OBD5TiO9NkVMYuHf2y1tRPRzsO4vD4kUO3k--Rc", + "I5MIO3LppLAEoO4b91ufH2O0878hspuJEBJSmA_S5Fs", + "IiApkhig8WiIpOb37u1a-oLhYuyFkxOEBXNC7DSMvL4", + "Ogxyqqp6jFRvfmDMzEdychfk6EKoFm8QWMNcBd2AqAk", + "S_nZ1PS6Yuai3BSI7VMUkDei05xEeiCj5RdWAbfPB-c", + "hu6ydLh2YvK5hsfeLiDm2gcBuPxpfFhBZ1_tS9M7Ld0", + "iRS2dVAl_bpXlyvwDSShEJ22v28ZFfXoTd_vp5QNCzM", + "lKTMGzf0d79BaiwuEpMYC26hABeIUtcI7WHl7zgeXCU", + "sjEgA8-j44T-2XXJZshzDOnLSaLQF8Iyhb4a7BcZrRY" + ], + "address": { + "_sd": [ + "0JOH_ZsjHpzU6RBYBTzSOWRzT6BPNYBRo3eaa0mb6Qc", + "O9gL7Tof4PtmSzuG6C2758uvpqJlJisMtJXAD8DDSBU", + "OS38kU3-IrxPpbs1Owdmr1BsXw7o6v6Wvo0-P8Gj4cE", + "bTvbv9_F4EGMTwOZ9BKguES9p2WCGC2xXyZ1tiPWuNI", + "l8JvgLKuPV-IpKRNHg15DVJ-f2yf_DNXLrIW5Ujmjws", + "srYNH_kZ-h7VkI1OAp8ndz9151q4qQpkFiUeH8T8_hs" + ] + }, + "iss": "https://example.com/issuer", + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256" +} \ No newline at end of file diff --git a/examples/simple_structured_with_decoys/sd_jwt_serialized.txt b/examples/simple_structured_with_decoys/sd_jwt_serialized.txt new file mode 100644 index 00000000..ed11f3b6 --- /dev/null +++ b/examples/simple_structured_with_decoys/sd_jwt_serialized.txt @@ -0,0 +1,25 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJfc2QiOiBbIjAtLWNnemtnTTkxSnlmSUFfaVpXX +2JrME5UTmprLS1Na011YUFfdHVJeFkiLCAiRzBvdkE3VE1DNEpkRER0NTRQNUVCZERjU +VNhdGI3N0g1enVmWmVIeXhJYyIsICJIZk84T0JENVRpTzlOa1ZNWXVIZjJ5MXRSUFJ6c +080dkQ0a1VPM2stLVJjIiwgIkk1TUlPM0xwcExBRW9PNGI5MXVmSDJPMDg3OGhzcHVKR +UJKU21BX1M1RnMiLCAiSWlBcGtoaWc4V2lJcE9iMzd1MWEtb0xoWXV5Rmt4T0VCWE5DN +0RTTXZMNCIsICJPZ3h5cXFwNmpGUnZmbURNekVkeWNoZms2RUtvRm04UVdNTmNCZDJBc +UFrIiwgIlNfbloxUFM2WXVhaTNCU0k3Vk1Va0RlaTA1eEVlaUNqNVJkV0FiZlBCLWMiL +CAiaHU2eWRMaDJZdks1aHNmZUxpRG0yZ2NCdVB4cGZGaEJaMV90UzlNN0xkMCIsICJpU +lMyZFZBbF9icFhseXZ3RFNTaEVKMjJ2MjhaRmZYb1RkX3ZwNVFOQ3pNIiwgImxLVE1He +mYwZDc5QmFpd3VFcE1ZQzI2aEFCZUlVdGNJN1dIbDd6Z2VYQ1UiLCAic2pFZ0E4LWo0N +FQtMlhYSlpzaHpET25MU2FMUUY4SXloYjRhN0JjWnJSWSJdLCAiYWRkcmVzcyI6IHsiX +3NkIjogWyIwSk9IX1pzakhwelU2UkJZQlR6U09XUnpUNkJQTllCUm8zZWFhMG1iNlFjI +iwgIk85Z0w3VG9mNFB0bVN6dUc2QzI3NTh1dnBxSmxKaXNNdEpYQUQ4RERTQlUiLCAiT +1MzOGtVMy1JcnhQcGJzMU93ZG1yMUJzWHc3bzZ2Nld2bzAtUDhHajRjRSIsICJiVHZid +jlfRjRFR01Ud09aOUJLZ3VFUzlwMldDR0MyeFh5WjF0aVBXdU5JIiwgImw4SnZnTEt1U +FYtSXBLUk5IZzE1RFZKLWYyeWZfRE5YTHJJVzVVam1qd3MiLCAic3JZTkhfa1otaDdWa +0kxT0FwOG5kejkxNTFxNHFRcGtGaVVlSDhUOF9ocyJdfSwgImlzcyI6ICJodHRwczovL +2V4YW1wbGUuY29tL2lzc3VlciIsICJpYXQiOiAxNTE2MjM5MDIyLCAiZXhwIjogMTUxN +jI0NzAyMiwgInNkX2RpZ2VzdF9kZXJpdmF0aW9uX2FsZyI6ICJzaGEtMjU2In0.QMvnE +-_WJ9kkBgbSE8pZdqUd1TLyNUcKSWEjxKebX2FMBAf6C30uJnY3s-0T74J2sQzr5qo0p +nasADs78A_wbjTLGmJxj1Yb-CyS35ApXXBqnOfYf87oDwhzslads1VJbP3VvXfqTt6JD +PZZiXnYpCpWXk31bacV8Py75h6k439_ESwZXK4K_PbTresjtgcSDOBVcI2kRNHKBniXu +sFMhrBlW-FdhrZKWGnewExCXz5PKyVNO0RXbYOQETcOUSdNHLevojtWx_jLbsmjJwzWH +bjloqkc-N7lKqXh1DUSBAl-lbj7maQNXDqg74Y7gyEnA2vsTEt3MQoPHbIDOAiVDg \ No newline at end of file diff --git a/examples/simple_structured_with_decoys/user_claims.json b/examples/simple_structured_with_decoys/user_claims.json new file mode 100644 index 00000000..8c9a6910 --- /dev/null +++ b/examples/simple_structured_with_decoys/user_claims.json @@ -0,0 +1,14 @@ +{ + "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", + "given_name": "太郎", + "family_name": "山田", + "email": "\"unusual email address\"@日本.com", + "phone_number": "123456", + "address": { + "street_address": "東京都港区芝公園4丁目2−8", + "locality": "東京都", + "region": "港区", + "country": "JP" + }, + "birthdate": "1940-01-01" +} \ No newline at end of file diff --git a/examples/simple_structured_with_decoys/verified_contents.json b/examples/simple_structured_with_decoys/verified_contents.json new file mode 100644 index 00000000..897eee96 --- /dev/null +++ b/examples/simple_structured_with_decoys/verified_contents.json @@ -0,0 +1,14 @@ +{ + "address": { + "country": "JP", + "region": "港区" + }, + "iss": "https://example.com/issuer", + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256", + "birthdate": "1940-01-01", + "given_name": "太郎", + "email": "\"unusual email address\"@日本.com", + "family_name": "山田" +} \ No newline at end of file diff --git a/examples/w3c-vc/combined_issuance.txt b/examples/w3c-vc/combined_issuance.txt new file mode 100644 index 00000000..2c662631 --- /dev/null +++ b/examples/w3c-vc/combined_issuance.txt @@ -0,0 +1,43 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJpc3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc +3N1ZXIiLCAianRpIjogImh0dHA6Ly9leGFtcGxlLmNvbS9jcmVkZW50aWFscy8zNzMyI +iwgIm5iZiI6IDE1NDE0OTM3MjQsICJpYXQiOiAxNTE2MjM5MDIyLCAiY25mIjogeyJqd +2siOiB7Imt0eSI6ICJSU0EiLCAibiI6ICIwdng3YWdvZWJHY1FTdXVQaUxKWFpwdE45b +m5kclFtYlhFcHMyYWlBRmJXaE03OExoV3g0Y2JiZkFBdFZUODZ6d3UxUks3YVBGRnh1a +ERSMUw2dFNvY19CSkVDUGViV0tSWGpCWkNpRlY0bjNva25qaE1zdG42NHRaXzJXLTVKc +0dZNEhjNW45eUJYQXJ3bDkzbHF0N19STjV3NkNmMGg0UXlRNXYtNjVZR2pRUjBfRkRXM +lF2enFZMzY4UVFNaWNBdGFTcXpzOEtKWmduWWI5YzdkMHpnZEFaSHp1NnFNUXZSTDVoY +WpybjFuOTFDYk9wYklTRDA4cU5MeXJka3QtYkZUV2hBSTR2TVFGaDZXZVp1MGZNNGxGZ +DJOY1J3cjNYUGtzSU5IYVEtR194Qm5pSXFidzBMczFqRjQ0LWNzRkN1ci1rRWdVOGF3Y +XBKektucURLZ3ciLCAiZSI6ICJBUUFCIn19LCAidHlwZSI6ICJJZGVudGl0eUNyZWRlb +nRpYWwiLCAiY3JlZGVudGlhbFN1YmplY3QiOiB7Il9zZCI6IFsiOEJjcjJaR0ltSjlGb +HNCTm1HaUxnajdYbUhtLWc4UU91WVZTWE9Xb001OCIsICI4d2F3YVlUM1hObG5sMHpVZ +FA4WHJpMTI1MnlISzZwamdyOEpJbkJ6TVFnIiwgIk5WVG80b1F2STBrdXFkTVllYjZNX +3NwX0J5VVk3ZDR6Y0xmSEI0ODNhVlkiLCAiVVE2SUhVQXRIcVc4WGNpazdHcVZpa1d0b +3Zpc2VTNEJLanZDbW1vbjZ4QSIsICJVbTdMUk02T2w1MXdKWkxvYk1lUkVXNkhzTXlPM +0RDQ0huV3RhaWtELTQwIiwgImpJam85RWxRNE5nMm1NenMwU29XcTJtb2xweVU4ME9FX +0xFTmprMkhhXzgiLCAidzlyclduQTFSUTVkVU9CYUpES0dmQWd0aVcxMm9iaTFqb2VGX +29MVGFaMCIsICJ6T0pKU0FtY0IwT3FQbUZrT1plYlk3MGhISTNHcEpDMXg0YmVKUnRJU +GJNIl0sICJhZGRyZXNzIjogeyJfc2QiOiBbIjBtY1pOejBuSUdOWUJJNFdnS0pGeDlta +mFhQjEwRTU5QmNfcGVmbW01NDQiLCAiNHpJZnJCR2szVFN0UkR6X3dseGk0VmdZRHphO +DFtQnNfemVKODRjenNTNCIsICJIbF93cFNTU2FncFh0U2tfckdvRS14a1lHVm5JSEJPL +Vp6aFVXY2h5OGJvIiwgInlTb1FVVDNKbEtGNU5kVFBNV3ptbERhbUI0VFkyRU1tR0Nnc +np6SnppaDAiXX19LCAiZXhwIjogMTUxNjI0NzAyMiwgInNkX2RpZ2VzdF9kZXJpdmF0a +W9uX2FsZyI6ICJzaGEtMjU2In0.CqCpLQL7FHIIqbek7m4qW105vAVDj5YNv5FktMgxR +cTCr_CXYlIg_MjFsN_liBTf7sk6jvzAGe7RpkWzfgvMXffUAM2WJBcE7GmF9xl7fT6X9 +M62Nq81_7YTpda-tj7hB18LrZZvXlnBA5grMWRvSfPqInM1s1JA52AVKjPHn_ZDCOOqm +tQWkgf-POjga-tL3Lh0m_FgQ-EL3p99oXdMLoCH8_iQ7HHLGC6XBeruwdkbtfszqDu_k +cIm9I5M6ZSDrXzHo0GkG1zav3r5fCAyLZ7Ru9H5Yl8_73zRRNeknNI-926vu_wQZUof7 +F-EUvNMPnJKwhwP3pEjXXCnAYaW2w~WyI5S05NMUxWcU1PVXR6Rk9iSFV4Q2J3IiwgIm +dpdmVuX25hbWUiLCAiSm9obiJd~WyJJeC1jVTQzcXpBUFhvU2xZclF3RnRnIiwgImZhb +WlseV9uYW1lIiwgIkRvZSJd~WyJrbnFPOXJYYldqc2llOEtTMkJJSkFRIiwgImVtYWls +IiwgImpvaG5kb2VAZXhhbXBsZS5jb20iXQ~WyJSbVdDWEhxaE41TDg2SllqOUdYN0h3I +iwgInBob25lX251bWJlciIsICIrMS0yMDItNTU1LTAxMDEiXQ~WyJjTE84ZnZpNlVHYU +stbF91QmFJYTVRIiwgInN0cmVldF9hZGRyZXNzIiwgIjEyMyBNYWluIFN0Il0~WyJOSz +BZQU1MWUwtLXRVdi1EdGNoaU1RIiwgImxvY2FsaXR5IiwgIkFueXRvd24iXQ~WyI3c3F +oSU1aMlkzOHM4aWxaY012c2RRIiwgInJlZ2lvbiIsICJBbnlzdGF0ZSJd~WyJISXlPZX +kzNXEzX0ZTbDRVV0g2Mm5BIiwgImNvdW50cnkiLCAiVVMiXQ~WyJpUU5VWU9tblo1N1Z +5VHBRaGZnV2lnIiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAxIl0~WyJ0cmNEb1BJUDV +zeW5wWFhZU0dFc0FRIiwgImlzX292ZXJfMTgiLCB0cnVlXQ~WyJRMnhzRWpnUnBhSE5r +dUdEM2tUNUpnIiwgImlzX292ZXJfMjEiLCB0cnVlXQ~WyI0RDd2N1JWTTh2THUwN0tRV +UJqOF9RIiwgImlzX292ZXJfNjUiLCB0cnVlXQ \ No newline at end of file diff --git a/examples/w3c-vc/combined_presentation.txt b/examples/w3c-vc/combined_presentation.txt new file mode 100644 index 00000000..b6046f41 --- /dev/null +++ b/examples/w3c-vc/combined_presentation.txt @@ -0,0 +1,31 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJpc3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc +3N1ZXIiLCAianRpIjogImh0dHA6Ly9leGFtcGxlLmNvbS9jcmVkZW50aWFscy8zNzMyI +iwgIm5iZiI6IDE1NDE0OTM3MjQsICJpYXQiOiAxNTE2MjM5MDIyLCAiY25mIjogeyJqd +2siOiB7Imt0eSI6ICJSU0EiLCAibiI6ICIwdng3YWdvZWJHY1FTdXVQaUxKWFpwdE45b +m5kclFtYlhFcHMyYWlBRmJXaE03OExoV3g0Y2JiZkFBdFZUODZ6d3UxUks3YVBGRnh1a +ERSMUw2dFNvY19CSkVDUGViV0tSWGpCWkNpRlY0bjNva25qaE1zdG42NHRaXzJXLTVKc +0dZNEhjNW45eUJYQXJ3bDkzbHF0N19STjV3NkNmMGg0UXlRNXYtNjVZR2pRUjBfRkRXM +lF2enFZMzY4UVFNaWNBdGFTcXpzOEtKWmduWWI5YzdkMHpnZEFaSHp1NnFNUXZSTDVoY +WpybjFuOTFDYk9wYklTRDA4cU5MeXJka3QtYkZUV2hBSTR2TVFGaDZXZVp1MGZNNGxGZ +DJOY1J3cjNYUGtzSU5IYVEtR194Qm5pSXFidzBMczFqRjQ0LWNzRkN1ci1rRWdVOGF3Y +XBKektucURLZ3ciLCAiZSI6ICJBUUFCIn19LCAidHlwZSI6ICJJZGVudGl0eUNyZWRlb +nRpYWwiLCAiY3JlZGVudGlhbFN1YmplY3QiOiB7Il9zZCI6IFsiOEJjcjJaR0ltSjlGb +HNCTm1HaUxnajdYbUhtLWc4UU91WVZTWE9Xb001OCIsICI4d2F3YVlUM1hObG5sMHpVZ +FA4WHJpMTI1MnlISzZwamdyOEpJbkJ6TVFnIiwgIk5WVG80b1F2STBrdXFkTVllYjZNX +3NwX0J5VVk3ZDR6Y0xmSEI0ODNhVlkiLCAiVVE2SUhVQXRIcVc4WGNpazdHcVZpa1d0b +3Zpc2VTNEJLanZDbW1vbjZ4QSIsICJVbTdMUk02T2w1MXdKWkxvYk1lUkVXNkhzTXlPM +0RDQ0huV3RhaWtELTQwIiwgImpJam85RWxRNE5nMm1NenMwU29XcTJtb2xweVU4ME9FX +0xFTmprMkhhXzgiLCAidzlyclduQTFSUTVkVU9CYUpES0dmQWd0aVcxMm9iaTFqb2VGX +29MVGFaMCIsICJ6T0pKU0FtY0IwT3FQbUZrT1plYlk3MGhISTNHcEpDMXg0YmVKUnRJU +GJNIl0sICJhZGRyZXNzIjogeyJfc2QiOiBbIjBtY1pOejBuSUdOWUJJNFdnS0pGeDlta +mFhQjEwRTU5QmNfcGVmbW01NDQiLCAiNHpJZnJCR2szVFN0UkR6X3dseGk0VmdZRHphO +DFtQnNfemVKODRjenNTNCIsICJIbF93cFNTU2FncFh0U2tfckdvRS14a1lHVm5JSEJPL +Vp6aFVXY2h5OGJvIiwgInlTb1FVVDNKbEtGNU5kVFBNV3ptbERhbUI0VFkyRU1tR0Nnc +np6SnppaDAiXX19LCAiZXhwIjogMTUxNjI0NzAyMiwgInNkX2RpZ2VzdF9kZXJpdmF0a +W9uX2FsZyI6ICJzaGEtMjU2In0.CqCpLQL7FHIIqbek7m4qW105vAVDj5YNv5FktMgxR +cTCr_CXYlIg_MjFsN_liBTf7sk6jvzAGe7RpkWzfgvMXffUAM2WJBcE7GmF9xl7fT6X9 +M62Nq81_7YTpda-tj7hB18LrZZvXlnBA5grMWRvSfPqInM1s1JA52AVKjPHn_ZDCOOqm +tQWkgf-POjga-tL3Lh0m_FgQ-EL3p99oXdMLoCH8_iQ7HHLGC6XBeruwdkbtfszqDu_k +cIm9I5M6ZSDrXzHo0GkG1zav3r5fCAyLZ7Ru9H5Yl8_73zRRNeknNI-926vu_wQZUof7 +F-EUvNMPnJKwhwP3pEjXXCnAYaW2w~ \ No newline at end of file diff --git a/examples/w3c-vc/disclosures.md b/examples/w3c-vc/disclosures.md new file mode 100644 index 00000000..6a06e546 --- /dev/null +++ b/examples/w3c-vc/disclosures.md @@ -0,0 +1,171 @@ +__Disclosure for `given_name`:__ + +``` +WyI5S05NMUxWcU1PVXR6Rk9iSFV4Q2J3IiwgImdpdmVuX25hbWUiLCAiSm9obiJd +``` + +Contents: + +``` +["9KNM1LVqMOUtzFObHUxCbw", "given_name", "John"] +``` + +SHA-256 Hash: `jIjo9ElQ4Ng2mMzs0SoWq2molpyU80OE_LENjk2Ha_8` + +__Disclosure for `family_name`:__ + +``` +WyJJeC1jVTQzcXpBUFhvU2xZclF3RnRnIiwgImZhbWlseV9uYW1lIiwgIkRvZSJd +``` + +Contents: + +``` +["Ix-cU43qzAPXoSlYrQwFtg", "family_name", "Doe"] +``` + +SHA-256 Hash: `w9rrWnA1RQ5dUOBaJDKGfAgtiW12obi1joeF_oLTaZ0` + +__Disclosure for `email`:__ + +``` +WyJrbnFPOXJYYldqc2llOEtTMkJJSkFRIiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBs +ZS5jb20iXQ +``` + +Contents: + +``` +["knqO9rXbWjsie8KS2BIJAQ", "email", "johndoe@example.com"] +``` + +SHA-256 Hash: `NVTo4oQvI0kuqdMYeb6M_sp_ByUY7d4zcLfHB483aVY` + +__Disclosure for `phone_number`:__ + +``` +WyJSbVdDWEhxaE41TDg2SllqOUdYN0h3IiwgInBob25lX251bWJlciIsICIrMS0yMDIt +NTU1LTAxMDEiXQ +``` + +Contents: + +``` +["RmWCXHqhN5L86JYj9GX7Hw", "phone_number", "+1-202-555-0101"] +``` + +SHA-256 Hash: `zOJJSAmcB0OqPmFkOZebY70hHI3GpJC1x4beJRtIPbM` + +__Disclosure for `street_address`:__ + +``` +WyJjTE84ZnZpNlVHYUstbF91QmFJYTVRIiwgInN0cmVldF9hZGRyZXNzIiwgIjEyMyBN +YWluIFN0Il0 +``` + +Contents: + +``` +["cLO8fvi6UGaK-l_uBaIa5Q", "street_address", "123 Main St"] +``` + +SHA-256 Hash: `Hl_wpSSSagpXtSk_rGoE-xkYGVnIHBO-ZzhUWchy8bo` + +__Disclosure for `locality`:__ + +``` +WyJOSzBZQU1MWUwtLXRVdi1EdGNoaU1RIiwgImxvY2FsaXR5IiwgIkFueXRvd24iXQ +``` + +Contents: + +``` +["NK0YAMLYL--tUv-DtchiMQ", "locality", "Anytown"] +``` + +SHA-256 Hash: `4zIfrBGk3TStRDz_wlxi4VgYDza81mBs_zeJ84czsS4` + +__Disclosure for `region`:__ + +``` +WyI3c3FoSU1aMlkzOHM4aWxaY012c2RRIiwgInJlZ2lvbiIsICJBbnlzdGF0ZSJd +``` + +Contents: + +``` +["7sqhIMZ2Y38s8ilZcMvsdQ", "region", "Anystate"] +``` + +SHA-256 Hash: `ySoQUT3JlKF5NdTPMWzmlDamB4TY2EMmGCgrzzJzih0` + +__Disclosure for `country`:__ + +``` +WyJISXlPZXkzNXEzX0ZTbDRVV0g2Mm5BIiwgImNvdW50cnkiLCAiVVMiXQ +``` + +Contents: + +``` +["HIyOey35q3_FSl4UWH62nA", "country", "US"] +``` + +SHA-256 Hash: `0mcZNz0nIGNYBI4WgKJFx9mjaaB10E59Bc_pefmm544` + +__Disclosure for `birthdate`:__ + +``` +WyJpUU5VWU9tblo1N1Z5VHBRaGZnV2lnIiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAx +Il0 +``` + +Contents: + +``` +["iQNUYOmnZ57VyTpQhfgWig", "birthdate", "1940-01-01"] +``` + +SHA-256 Hash: `Um7LRM6Ol51wJZLobMeREW6HsMyO3DCCHnWtaikD-40` + +__Disclosure for `is_over_18`:__ + +``` +WyJ0cmNEb1BJUDVzeW5wWFhZU0dFc0FRIiwgImlzX292ZXJfMTgiLCB0cnVlXQ +``` + +Contents: + +``` +["trcDoPIP5synpXXYSGEsAQ", "is_over_18", true] +``` + +SHA-256 Hash: `8wawaYT3XNlnl0zUdP8Xri1252yHK6pjgr8JInBzMQg` + +__Disclosure for `is_over_21`:__ + +``` +WyJRMnhzRWpnUnBhSE5rdUdEM2tUNUpnIiwgImlzX292ZXJfMjEiLCB0cnVlXQ +``` + +Contents: + +``` +["Q2xsEjgRpaHNkuGD3kT5Jg", "is_over_21", true] +``` + +SHA-256 Hash: `UQ6IHUAtHqW8Xcik7GqVikWtoviseS4BKjvCmmon6xA` + +__Disclosure for `is_over_65`:__ + +``` +WyI0RDd2N1JWTTh2THUwN0tRVUJqOF9RIiwgImlzX292ZXJfNjUiLCB0cnVlXQ +``` + +Contents: + +``` +["4D7v7RVM8vLu07KQUBj8_Q", "is_over_65", true] +``` + +SHA-256 Hash: `8Bcr2ZGImJ9FlsBNmGiLgj7XmHm-g8QOuYVSXOWoM58` \ No newline at end of file diff --git a/examples/w3c-vc/hb_jwt_serialized.txt b/examples/w3c-vc/hb_jwt_serialized.txt new file mode 100644 index 00000000..e69de29b diff --git a/examples/w3c-vc/sd_jwt_payload.json b/examples/w3c-vc/sd_jwt_payload.json new file mode 100644 index 00000000..adeeda1d --- /dev/null +++ b/examples/w3c-vc/sd_jwt_payload.json @@ -0,0 +1,41 @@ +{ + "iss": "https://example.com/issuer", + "jti": "http://example.com/credentials/3732", + "nbf": 1541493724, + "iat": 1516239022, + "cnf": { + "jwk": { + "kty": "RSA", + "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbf + AAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMst + n64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_F + DW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n9 + 1CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHa + Q-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + "e": "AQAB" + } + }, + "type": "IdentityCredential", + "credentialSubject": { + "_sd": [ + "8Bcr2ZGImJ9FlsBNmGiLgj7XmHm-g8QOuYVSXOWoM58", + "8wawaYT3XNlnl0zUdP8Xri1252yHK6pjgr8JInBzMQg", + "NVTo4oQvI0kuqdMYeb6M_sp_ByUY7d4zcLfHB483aVY", + "UQ6IHUAtHqW8Xcik7GqVikWtoviseS4BKjvCmmon6xA", + "Um7LRM6Ol51wJZLobMeREW6HsMyO3DCCHnWtaikD-40", + "jIjo9ElQ4Ng2mMzs0SoWq2molpyU80OE_LENjk2Ha_8", + "w9rrWnA1RQ5dUOBaJDKGfAgtiW12obi1joeF_oLTaZ0", + "zOJJSAmcB0OqPmFkOZebY70hHI3GpJC1x4beJRtIPbM" + ], + "address": { + "_sd": [ + "0mcZNz0nIGNYBI4WgKJFx9mjaaB10E59Bc_pefmm544", + "4zIfrBGk3TStRDz_wlxi4VgYDza81mBs_zeJ84czsS4", + "Hl_wpSSSagpXtSk_rGoE-xkYGVnIHBO-ZzhUWchy8bo", + "ySoQUT3JlKF5NdTPMWzmlDamB4TY2EMmGCgrzzJzih0" + ] + } + }, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256" +} \ No newline at end of file diff --git a/examples/w3c-vc/sd_jwt_serialized.txt b/examples/w3c-vc/sd_jwt_serialized.txt new file mode 100644 index 00000000..ab2cf2af --- /dev/null +++ b/examples/w3c-vc/sd_jwt_serialized.txt @@ -0,0 +1,31 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJpc3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc +3N1ZXIiLCAianRpIjogImh0dHA6Ly9leGFtcGxlLmNvbS9jcmVkZW50aWFscy8zNzMyI +iwgIm5iZiI6IDE1NDE0OTM3MjQsICJpYXQiOiAxNTE2MjM5MDIyLCAiY25mIjogeyJqd +2siOiB7Imt0eSI6ICJSU0EiLCAibiI6ICIwdng3YWdvZWJHY1FTdXVQaUxKWFpwdE45b +m5kclFtYlhFcHMyYWlBRmJXaE03OExoV3g0Y2JiZkFBdFZUODZ6d3UxUks3YVBGRnh1a +ERSMUw2dFNvY19CSkVDUGViV0tSWGpCWkNpRlY0bjNva25qaE1zdG42NHRaXzJXLTVKc +0dZNEhjNW45eUJYQXJ3bDkzbHF0N19STjV3NkNmMGg0UXlRNXYtNjVZR2pRUjBfRkRXM +lF2enFZMzY4UVFNaWNBdGFTcXpzOEtKWmduWWI5YzdkMHpnZEFaSHp1NnFNUXZSTDVoY +WpybjFuOTFDYk9wYklTRDA4cU5MeXJka3QtYkZUV2hBSTR2TVFGaDZXZVp1MGZNNGxGZ +DJOY1J3cjNYUGtzSU5IYVEtR194Qm5pSXFidzBMczFqRjQ0LWNzRkN1ci1rRWdVOGF3Y +XBKektucURLZ3ciLCAiZSI6ICJBUUFCIn19LCAidHlwZSI6ICJJZGVudGl0eUNyZWRlb +nRpYWwiLCAiY3JlZGVudGlhbFN1YmplY3QiOiB7Il9zZCI6IFsiOEJjcjJaR0ltSjlGb +HNCTm1HaUxnajdYbUhtLWc4UU91WVZTWE9Xb001OCIsICI4d2F3YVlUM1hObG5sMHpVZ +FA4WHJpMTI1MnlISzZwamdyOEpJbkJ6TVFnIiwgIk5WVG80b1F2STBrdXFkTVllYjZNX +3NwX0J5VVk3ZDR6Y0xmSEI0ODNhVlkiLCAiVVE2SUhVQXRIcVc4WGNpazdHcVZpa1d0b +3Zpc2VTNEJLanZDbW1vbjZ4QSIsICJVbTdMUk02T2w1MXdKWkxvYk1lUkVXNkhzTXlPM +0RDQ0huV3RhaWtELTQwIiwgImpJam85RWxRNE5nMm1NenMwU29XcTJtb2xweVU4ME9FX +0xFTmprMkhhXzgiLCAidzlyclduQTFSUTVkVU9CYUpES0dmQWd0aVcxMm9iaTFqb2VGX +29MVGFaMCIsICJ6T0pKU0FtY0IwT3FQbUZrT1plYlk3MGhISTNHcEpDMXg0YmVKUnRJU +GJNIl0sICJhZGRyZXNzIjogeyJfc2QiOiBbIjBtY1pOejBuSUdOWUJJNFdnS0pGeDlta +mFhQjEwRTU5QmNfcGVmbW01NDQiLCAiNHpJZnJCR2szVFN0UkR6X3dseGk0VmdZRHphO +DFtQnNfemVKODRjenNTNCIsICJIbF93cFNTU2FncFh0U2tfckdvRS14a1lHVm5JSEJPL +Vp6aFVXY2h5OGJvIiwgInlTb1FVVDNKbEtGNU5kVFBNV3ptbERhbUI0VFkyRU1tR0Nnc +np6SnppaDAiXX19LCAiZXhwIjogMTUxNjI0NzAyMiwgInNkX2RpZ2VzdF9kZXJpdmF0a +W9uX2FsZyI6ICJzaGEtMjU2In0.CqCpLQL7FHIIqbek7m4qW105vAVDj5YNv5FktMgxR +cTCr_CXYlIg_MjFsN_liBTf7sk6jvzAGe7RpkWzfgvMXffUAM2WJBcE7GmF9xl7fT6X9 +M62Nq81_7YTpda-tj7hB18LrZZvXlnBA5grMWRvSfPqInM1s1JA52AVKjPHn_ZDCOOqm +tQWkgf-POjga-tL3Lh0m_FgQ-EL3p99oXdMLoCH8_iQ7HHLGC6XBeruwdkbtfszqDu_k +cIm9I5M6ZSDrXzHo0GkG1zav3r5fCAyLZ7Ru9H5Yl8_73zRRNeknNI-926vu_wQZUof7 +F-EUvNMPnJKwhwP3pEjXXCnAYaW2w \ No newline at end of file diff --git a/examples/w3c-vc/user_claims.json b/examples/w3c-vc/user_claims.json new file mode 100644 index 00000000..824fd77d --- /dev/null +++ b/examples/w3c-vc/user_claims.json @@ -0,0 +1,35 @@ +{ + "iss": "https://example.com", + "jti": "http://example.com/credentials/3732", + "nbf": 1541493724, + "iat": 1541493724, + "cnf": { + "jwk": { + "kty": "RSA", + "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbf + AAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMst + n64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_F + DW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n9 + 1CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHa + Q-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + "e": "AQAB" + } + }, + "type": "IdentityCredential", + "credentialSubject": { + "given_name": "John", + "family_name": "Doe", + "email": "johndoe@example.com", + "phone_number": "+1-202-555-0101", + "address": { + "street_address": "123 Main St", + "locality": "Anytown", + "region": "Anystate", + "country": "US" + }, + "birthdate": "1940-01-01", + "is_over_18": true, + "is_over_21": true, + "is_over_65": true + } +} \ No newline at end of file diff --git a/examples/w3c-vc/verified_contents.json b/examples/w3c-vc/verified_contents.json new file mode 100644 index 00000000..cd5893c0 --- /dev/null +++ b/examples/w3c-vc/verified_contents.json @@ -0,0 +1,24 @@ +{ + "iss": "https://example.com/issuer", + "jti": "http://example.com/credentials/3732", + "nbf": 1541493724, + "iat": 1516239022, + "cnf": { + "jwk": { + "kty": "RSA", + "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbf + AAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMst + n64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_F + DW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n9 + 1CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHa + Q-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + "e": "AQAB" + } + }, + "type": "IdentityCredential", + "credentialSubject": { + "address": {} + }, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256" +} \ No newline at end of file diff --git a/examples/w3c-vc_for_slide_deck/combined_issuance.txt b/examples/w3c-vc_for_slide_deck/combined_issuance.txt new file mode 100644 index 00000000..50a76c06 --- /dev/null +++ b/examples/w3c-vc_for_slide_deck/combined_issuance.txt @@ -0,0 +1,32 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJpc3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc +3N1ZXIiLCAiY25mIjogeyJqd2siOiB7Imt0eSI6ICJSU0EiLCAibiI6ICIwdng3YWdvZ +WJHY1FTdS4uLi4tY3NGQ3VyLWtFZ1U4YXdhcEp6S25xREtndyIsICJlIjogIkFRQUIif +X0sICJ0eXBlIjogIklkZW50aXR5Q3JlZGVudGlhbCIsICJjcmVkZW50aWFsU3ViamVjd +CI6IHsiX3NkIjogWyJFVzFvMGVncWE1bUdjYnl0VDVTLWtBdWJjRWpZRVV3UmtYbHUyd +kM1bDIwIiwgIkZFeC1JVEh0NDFJOF9jbjBTUy1odm9MbmVYX1JHbEpvXzhvMnhSTmhmZ +GsiLCAiUXhKVi0yVjFIOG1jbHRSNnZWQzRtM3JlVTVhTkg5d2RKejJVZG1Sb0kxRSIsI +CJhdFVuMVRZd1JBbDRHUTdQZUV0WGFNdzJmNHVJVGlKclg0ODV3TTh2NjdFIiwgImZUT +XczdmtrRUx3TDFYTnVZSzhIN3pCS0NIdV91aWY2MFNsRzFweVhJVVEiLCAiaWdnN0g1Z +m4yZUJFTUlFa0U1Q2tibTIzUXV3REpsVFlvS1JpcDA4ZFlJYyIsICJ0cFV0bDcwaHBVX +3hucnZaaTBHaEdvUlIxam10MXpZZ3Z2NUlZMEF4N0tjIl0sICJhZGRyZXNzIjogeyJfc +2QiOiBbImdxQjVrbUF3eXJ5ODhhSGphQWVPLVVTWDZKT01hb2p1a0tzaGVvMzhPMGMiL +CAidk9ubFl0Y2pyODcyZlAzV2E3NU96bDdjLTZfTU9WZElVTnR3TEtLeFp3MCIsICJ3O +EludnhzUFhkS29vd3VWcHlCTWdsMWI5X1IyYjZYcGEzT1lPSWpnUXJvIl19fSwgImlhd +CI6IDE1MTYyMzkwMjIsICJleHAiOiAxNTE2MjQ3MDIyLCAic2RfZGlnZXN0X2Rlcml2Y +XRpb25fYWxnIjogInNoYS0yNTYifQ.1UHEPtLLUXOT51jH3gg-3C-ZidWzsB9Un-VxmM +VdQtTbLLhwDTB6HJtt15p43yCXTzdpiZxtDI6fr07Tp0Dy_Umg3Q5_FxFj4WHnsVuVzu +ASU8cFlGPi6xgH9D3w1G2hqepBS8DyQ5bA_p5kN_tKJVoP1xWhcQujRJ8kkEKQsRia4F +hrBldl8f41wgu_ipPqh1Ix4BVI7GJClZNx94nWPT7JUFkI6Y6JkahLf3S6gB0MxtmLAe +Y0qkuz8VeOZNfl_CDog55kVTkArorfoL6D6TEjI__-w6YyU0PnIRJXJ0wrYfoyhNl8LK +AP38QYMpdR7z_rsvHpQHzFAPTmevnHDg~WyJHTzByMjZuTy1pVzUwWmNBb09pbEZ3Iiw +gImdpdmVuX25hbWUiLCAiTWF4Il0~WyJjU2xiUjEzNWkwTmpoc291TXhyampnIiwgImZ +hbWlseV9uYW1lIiwgIk11c3Rlcm1hbm4iXQ~WyJvSER0NDNWd3VocG84bXphcHJnQ2N3 +IiwgImVtYWlsIiwgIm11c3Rlcm1hbm5AZXhhbXBsZS5jb20iXQ~WyJyR2MwS3RZNldtZ +mx5d1RUS0VXSUVRIiwgInN0cmVldF9hZGRyZXNzIiwgIk11c3RlcnN0ci4gMjMiXQ~Wy +JwR1FNUXgtMnRIMlh3Q19lUUNGbjRnIiwgImxvY2FsaXR5IiwgIkJlcmxpbiJd~WyJUS +TE1TThHNVVJeFBpV05aLVZMWUJBIiwgImNvdW50cnkiLCAiREUiXQ~WyJwX09zVGpjcV +daTnYzeHQ4TXJCM2hRIiwgImJpcnRoZGF0ZSIsICIxOTcxLTEyLTIzIl0~WyJKLTZBVH +F0NzFkWXgxekt2cnVEcWRnIiwgImlzX292ZXJfMTgiLCB0cnVlXQ~WyJkTDNlRVVHcVZ +3NTZQMGV0cDJvN21nIiwgImlzX292ZXJfMjEiLCB0cnVlXQ~WyJ4TjBtYzZSWEJ4YTdt +cG03dmZud0d3IiwgImlzX292ZXJfNjUiLCBmYWxzZV0 \ No newline at end of file diff --git a/examples/w3c-vc_for_slide_deck/combined_presentation.txt b/examples/w3c-vc_for_slide_deck/combined_presentation.txt new file mode 100644 index 00000000..7f5f3cec --- /dev/null +++ b/examples/w3c-vc_for_slide_deck/combined_presentation.txt @@ -0,0 +1,22 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJpc3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc +3N1ZXIiLCAiY25mIjogeyJqd2siOiB7Imt0eSI6ICJSU0EiLCAibiI6ICIwdng3YWdvZ +WJHY1FTdS4uLi4tY3NGQ3VyLWtFZ1U4YXdhcEp6S25xREtndyIsICJlIjogIkFRQUIif +X0sICJ0eXBlIjogIklkZW50aXR5Q3JlZGVudGlhbCIsICJjcmVkZW50aWFsU3ViamVjd +CI6IHsiX3NkIjogWyJFVzFvMGVncWE1bUdjYnl0VDVTLWtBdWJjRWpZRVV3UmtYbHUyd +kM1bDIwIiwgIkZFeC1JVEh0NDFJOF9jbjBTUy1odm9MbmVYX1JHbEpvXzhvMnhSTmhmZ +GsiLCAiUXhKVi0yVjFIOG1jbHRSNnZWQzRtM3JlVTVhTkg5d2RKejJVZG1Sb0kxRSIsI +CJhdFVuMVRZd1JBbDRHUTdQZUV0WGFNdzJmNHVJVGlKclg0ODV3TTh2NjdFIiwgImZUT +XczdmtrRUx3TDFYTnVZSzhIN3pCS0NIdV91aWY2MFNsRzFweVhJVVEiLCAiaWdnN0g1Z +m4yZUJFTUlFa0U1Q2tibTIzUXV3REpsVFlvS1JpcDA4ZFlJYyIsICJ0cFV0bDcwaHBVX +3hucnZaaTBHaEdvUlIxam10MXpZZ3Z2NUlZMEF4N0tjIl0sICJhZGRyZXNzIjogeyJfc +2QiOiBbImdxQjVrbUF3eXJ5ODhhSGphQWVPLVVTWDZKT01hb2p1a0tzaGVvMzhPMGMiL +CAidk9ubFl0Y2pyODcyZlAzV2E3NU96bDdjLTZfTU9WZElVTnR3TEtLeFp3MCIsICJ3O +EludnhzUFhkS29vd3VWcHlCTWdsMWI5X1IyYjZYcGEzT1lPSWpnUXJvIl19fSwgImlhd +CI6IDE1MTYyMzkwMjIsICJleHAiOiAxNTE2MjQ3MDIyLCAic2RfZGlnZXN0X2Rlcml2Y +XRpb25fYWxnIjogInNoYS0yNTYifQ.1UHEPtLLUXOT51jH3gg-3C-ZidWzsB9Un-VxmM +VdQtTbLLhwDTB6HJtt15p43yCXTzdpiZxtDI6fr07Tp0Dy_Umg3Q5_FxFj4WHnsVuVzu +ASU8cFlGPi6xgH9D3w1G2hqepBS8DyQ5bA_p5kN_tKJVoP1xWhcQujRJ8kkEKQsRia4F +hrBldl8f41wgu_ipPqh1Ix4BVI7GJClZNx94nWPT7JUFkI6Y6JkahLf3S6gB0MxtmLAe +Y0qkuz8VeOZNfl_CDog55kVTkArorfoL6D6TEjI__-w6YyU0PnIRJXJ0wrYfoyhNl8LK +AP38QYMpdR7z_rsvHpQHzFAPTmevnHDg~ \ No newline at end of file diff --git a/examples/w3c-vc_for_slide_deck/disclosures.md b/examples/w3c-vc_for_slide_deck/disclosures.md new file mode 100644 index 00000000..bad91b9c --- /dev/null +++ b/examples/w3c-vc_for_slide_deck/disclosures.md @@ -0,0 +1,143 @@ +__Disclosure for `given_name`:__ + +``` +WyJHTzByMjZuTy1pVzUwWmNBb09pbEZ3IiwgImdpdmVuX25hbWUiLCAiTWF4Il0 +``` + +Contents: + +``` +["GO0r26nO-iW50ZcAoOilFw", "given_name", "Max"] +``` + +SHA-256 Hash: `EW1o0egqa5mGcbytT5S-kAubcEjYEUwRkXlu2vC5l20` + +__Disclosure for `family_name`:__ + +``` +WyJjU2xiUjEzNWkwTmpoc291TXhyampnIiwgImZhbWlseV9uYW1lIiwgIk11c3Rlcm1h +bm4iXQ +``` + +Contents: + +``` +["cSlbR135i0NjhsouMxrjjg", "family_name", "Mustermann"] +``` + +SHA-256 Hash: `FEx-ITHt41I8_cn0SS-hvoLneX_RGlJo_8o2xRNhfdk` + +__Disclosure for `email`:__ + +``` +WyJvSER0NDNWd3VocG84bXphcHJnQ2N3IiwgImVtYWlsIiwgIm11c3Rlcm1hbm5AZXhh +bXBsZS5jb20iXQ +``` + +Contents: + +``` +["oHDt43Vwuhpo8mzaprgCcw", "email", "mustermann@example.com"] +``` + +SHA-256 Hash: `igg7H5fn2eBEMIEkE5Ckbm23QuwDJlTYoKRip08dYIc` + +__Disclosure for `street_address`:__ + +``` +WyJyR2MwS3RZNldtZmx5d1RUS0VXSUVRIiwgInN0cmVldF9hZGRyZXNzIiwgIk11c3Rl +cnN0ci4gMjMiXQ +``` + +Contents: + +``` +["rGc0KtY6WmflywTTKEWIEQ", "street_address", "Musterstr. 23"] +``` + +SHA-256 Hash: `gqB5kmAwyry88aHjaAeO-USX6JOMaojukKsheo38O0c` + +__Disclosure for `locality`:__ + +``` +WyJwR1FNUXgtMnRIMlh3Q19lUUNGbjRnIiwgImxvY2FsaXR5IiwgIkJlcmxpbiJd +``` + +Contents: + +``` +["pGQMQx-2tH2XwC_eQCFn4g", "locality", "Berlin"] +``` + +SHA-256 Hash: `w8InvxsPXdKoowuVpyBMgl1b9_R2b6Xpa3OYOIjgQro` + +__Disclosure for `country`:__ + +``` +WyJUSTE1TThHNVVJeFBpV05aLVZMWUJBIiwgImNvdW50cnkiLCAiREUiXQ +``` + +Contents: + +``` +["TI15M8G5UIxPiWNZ-VLYBA", "country", "DE"] +``` + +SHA-256 Hash: `vOnlYtcjr872fP3Wa75Ozl7c-6_MOVdIUNtwLKKxZw0` + +__Disclosure for `birthdate`:__ + +``` +WyJwX09zVGpjcVdaTnYzeHQ4TXJCM2hRIiwgImJpcnRoZGF0ZSIsICIxOTcxLTEyLTIz +Il0 +``` + +Contents: + +``` +["p_OsTjcqWZNv3xt8MrB3hQ", "birthdate", "1971-12-23"] +``` + +SHA-256 Hash: `atUn1TYwRAl4GQ7PeEtXaMw2f4uITiJrX485wM8v67E` + +__Disclosure for `is_over_18`:__ + +``` +WyJKLTZBVHF0NzFkWXgxekt2cnVEcWRnIiwgImlzX292ZXJfMTgiLCB0cnVlXQ +``` + +Contents: + +``` +["J-6ATqt71dYx1zKvruDqdg", "is_over_18", true] +``` + +SHA-256 Hash: `QxJV-2V1H8mcltR6vVC4m3reU5aNH9wdJz2UdmRoI1E` + +__Disclosure for `is_over_21`:__ + +``` +WyJkTDNlRVVHcVZ3NTZQMGV0cDJvN21nIiwgImlzX292ZXJfMjEiLCB0cnVlXQ +``` + +Contents: + +``` +["dL3eEUGqVw56P0etp2o7mg", "is_over_21", true] +``` + +SHA-256 Hash: `fTMw3vkkELwL1XNuYK8H7zBKCHu_uif60SlG1pyXIUQ` + +__Disclosure for `is_over_65`:__ + +``` +WyJ4TjBtYzZSWEJ4YTdtcG03dmZud0d3IiwgImlzX292ZXJfNjUiLCBmYWxzZV0 +``` + +Contents: + +``` +["xN0mc6RXBxa7mpm7vfnwGw", "is_over_65", false] +``` + +SHA-256 Hash: `tpUtl70hpU_xnrvZi0GhGoRR1jmt1zYgvv5IY0Ax7Kc` \ No newline at end of file diff --git a/examples/w3c-vc_for_slide_deck/hb_jwt_serialized.txt b/examples/w3c-vc_for_slide_deck/hb_jwt_serialized.txt new file mode 100644 index 00000000..e69de29b diff --git a/examples/w3c-vc_for_slide_deck/sd_jwt_payload.json b/examples/w3c-vc_for_slide_deck/sd_jwt_payload.json new file mode 100644 index 00000000..2e1bf34e --- /dev/null +++ b/examples/w3c-vc_for_slide_deck/sd_jwt_payload.json @@ -0,0 +1,32 @@ +{ + "iss": "https://example.com/issuer", + "cnf": { + "jwk": { + "kty": "RSA", + "n": "0vx7agoebGcQSu....-csFCur-kEgU8awapJzKnqDKgw", + "e": "AQAB" + } + }, + "type": "IdentityCredential", + "credentialSubject": { + "_sd": [ + "EW1o0egqa5mGcbytT5S-kAubcEjYEUwRkXlu2vC5l20", + "FEx-ITHt41I8_cn0SS-hvoLneX_RGlJo_8o2xRNhfdk", + "QxJV-2V1H8mcltR6vVC4m3reU5aNH9wdJz2UdmRoI1E", + "atUn1TYwRAl4GQ7PeEtXaMw2f4uITiJrX485wM8v67E", + "fTMw3vkkELwL1XNuYK8H7zBKCHu_uif60SlG1pyXIUQ", + "igg7H5fn2eBEMIEkE5Ckbm23QuwDJlTYoKRip08dYIc", + "tpUtl70hpU_xnrvZi0GhGoRR1jmt1zYgvv5IY0Ax7Kc" + ], + "address": { + "_sd": [ + "gqB5kmAwyry88aHjaAeO-USX6JOMaojukKsheo38O0c", + "vOnlYtcjr872fP3Wa75Ozl7c-6_MOVdIUNtwLKKxZw0", + "w8InvxsPXdKoowuVpyBMgl1b9_R2b6Xpa3OYOIjgQro" + ] + } + }, + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256" +} \ No newline at end of file diff --git a/examples/w3c-vc_for_slide_deck/sd_jwt_serialized.txt b/examples/w3c-vc_for_slide_deck/sd_jwt_serialized.txt new file mode 100644 index 00000000..be52eb54 --- /dev/null +++ b/examples/w3c-vc_for_slide_deck/sd_jwt_serialized.txt @@ -0,0 +1,22 @@ +eyJhbGciOiAiUlMyNTYiLCAia2lkIjogImNBRUlVcUowY21MekQxa3pHemhlaUJhZzBZ +UkF6VmRsZnhOMjgwTmdIYUEifQ.eyJpc3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc +3N1ZXIiLCAiY25mIjogeyJqd2siOiB7Imt0eSI6ICJSU0EiLCAibiI6ICIwdng3YWdvZ +WJHY1FTdS4uLi4tY3NGQ3VyLWtFZ1U4YXdhcEp6S25xREtndyIsICJlIjogIkFRQUIif +X0sICJ0eXBlIjogIklkZW50aXR5Q3JlZGVudGlhbCIsICJjcmVkZW50aWFsU3ViamVjd +CI6IHsiX3NkIjogWyJFVzFvMGVncWE1bUdjYnl0VDVTLWtBdWJjRWpZRVV3UmtYbHUyd +kM1bDIwIiwgIkZFeC1JVEh0NDFJOF9jbjBTUy1odm9MbmVYX1JHbEpvXzhvMnhSTmhmZ +GsiLCAiUXhKVi0yVjFIOG1jbHRSNnZWQzRtM3JlVTVhTkg5d2RKejJVZG1Sb0kxRSIsI +CJhdFVuMVRZd1JBbDRHUTdQZUV0WGFNdzJmNHVJVGlKclg0ODV3TTh2NjdFIiwgImZUT +XczdmtrRUx3TDFYTnVZSzhIN3pCS0NIdV91aWY2MFNsRzFweVhJVVEiLCAiaWdnN0g1Z +m4yZUJFTUlFa0U1Q2tibTIzUXV3REpsVFlvS1JpcDA4ZFlJYyIsICJ0cFV0bDcwaHBVX +3hucnZaaTBHaEdvUlIxam10MXpZZ3Z2NUlZMEF4N0tjIl0sICJhZGRyZXNzIjogeyJfc +2QiOiBbImdxQjVrbUF3eXJ5ODhhSGphQWVPLVVTWDZKT01hb2p1a0tzaGVvMzhPMGMiL +CAidk9ubFl0Y2pyODcyZlAzV2E3NU96bDdjLTZfTU9WZElVTnR3TEtLeFp3MCIsICJ3O +EludnhzUFhkS29vd3VWcHlCTWdsMWI5X1IyYjZYcGEzT1lPSWpnUXJvIl19fSwgImlhd +CI6IDE1MTYyMzkwMjIsICJleHAiOiAxNTE2MjQ3MDIyLCAic2RfZGlnZXN0X2Rlcml2Y +XRpb25fYWxnIjogInNoYS0yNTYifQ.1UHEPtLLUXOT51jH3gg-3C-ZidWzsB9Un-VxmM +VdQtTbLLhwDTB6HJtt15p43yCXTzdpiZxtDI6fr07Tp0Dy_Umg3Q5_FxFj4WHnsVuVzu +ASU8cFlGPi6xgH9D3w1G2hqepBS8DyQ5bA_p5kN_tKJVoP1xWhcQujRJ8kkEKQsRia4F +hrBldl8f41wgu_ipPqh1Ix4BVI7GJClZNx94nWPT7JUFkI6Y6JkahLf3S6gB0MxtmLAe +Y0qkuz8VeOZNfl_CDog55kVTkArorfoL6D6TEjI__-w6YyU0PnIRJXJ0wrYfoyhNl8LK +AP38QYMpdR7z_rsvHpQHzFAPTmevnHDg \ No newline at end of file diff --git a/examples/w3c-vc_for_slide_deck/user_claims.json b/examples/w3c-vc_for_slide_deck/user_claims.json new file mode 100644 index 00000000..fb8d00cc --- /dev/null +++ b/examples/w3c-vc_for_slide_deck/user_claims.json @@ -0,0 +1,25 @@ +{ + "iss": "https://example.com", + "cnf": { + "jwk": { + "kty": "RSA", + "n": "0vx7agoebGcQSu....-csFCur-kEgU8awapJzKnqDKgw", + "e": "AQAB" + } + }, + "type": "IdentityCredential", + "credentialSubject": { + "given_name": "Max", + "family_name": "Mustermann", + "email": "mustermann@example.com", + "address": { + "street_address": "Musterstr. 23", + "locality": "Berlin", + "country": "DE" + }, + "birthdate": "1971-12-23", + "is_over_18": true, + "is_over_21": true, + "is_over_65": false + } +} \ No newline at end of file diff --git a/examples/w3c-vc_for_slide_deck/verified_contents.json b/examples/w3c-vc_for_slide_deck/verified_contents.json new file mode 100644 index 00000000..c425b160 --- /dev/null +++ b/examples/w3c-vc_for_slide_deck/verified_contents.json @@ -0,0 +1,17 @@ +{ + "iss": "https://example.com/issuer", + "cnf": { + "jwk": { + "kty": "RSA", + "n": "0vx7agoebGcQSu....-csFCur-kEgU8awapJzKnqDKgw", + "e": "AQAB" + } + }, + "type": "IdentityCredential", + "credentialSubject": { + "address": {} + }, + "iat": 1516239022, + "exp": 1516247022, + "sd_digest_derivation_alg": "sha-256" +} \ No newline at end of file diff --git a/sd_jwt/__init__.py b/sd_jwt/__init__.py index b5d9c057..4e226720 100644 --- a/sd_jwt/__init__.py +++ b/sd_jwt/__init__.py @@ -1,7 +1,5 @@ __version__ = "0.2.1" DEFAULT_SIGNING_ALG = "RS256" -SD_DIGESTS_KEY = "sd_digests" -SD_II_CLAIMS_KEY = "sd_ii_disclosures" -SD_HS_CLAIMS_KEY = "sd_hs_disclosures" +SD_DIGESTS_KEY = "_sd" DIGEST_ALG_KEY = "sd_digest_derivation_alg" \ No newline at end of file diff --git a/sd_jwt/bin/sd_jwt b/sd_jwt/bin/sd_jwt index 3306f6cb..a8908be0 100644 --- a/sd_jwt/bin/sd_jwt +++ b/sd_jwt/bin/sd_jwt @@ -2,18 +2,22 @@ import argparse import json import logging -import sys import pathlib +import random +import sys import yaml - from sd_jwt import __version__ -from sd_jwt.demo_utils import print_decoded_repr, get_jwk, print_repr -from sd_jwt.utils import generate_salt -from sd_jwt.operations import SDJWT +from sd_jwt.demo_utils import get_jwk, print_decoded_repr, print_repr +from sd_jwt.operations import SDJWTCommon, SDJWTHolder, SDJWTIssuer, SDJWTVerifier +from sd_jwt.replace_utils import textwrap_json, textwrap_text logger = logging.getLogger("sd_jwt") +# Generate a 16-bit random number +def generate_nonce(): + return bytes(random.getrandbits(8) for _ in range(16)).hex() + parser = argparse.ArgumentParser( description=f"{__file__} demo.", @@ -50,8 +54,8 @@ parser.add_argument( "--nonce", required=False, type=str, - default=generate_salt(), - help=("given example of a salt: 'XZOUco1u_gEPknxS78sWWg'"), + default=generate_nonce(), + help=("given example of a nonce: 'XZOUco1u_gEPknxS78sWWg'"), ) parser.add_argument( "--iat", required=False, type=int, help=("issued at, UTC Timestamp") @@ -72,12 +76,13 @@ parser.add_argument( default=4, help=("json output indentation level"), ) +# new option to put examples into a directory parser.add_argument( - "--replace-examples-in", + "--output-dir", required=False, type=pathlib.Path, help=( - "path/to/draft-ietf-oauth-selective-disclosure-jwt.md - Do not output examples, but replace all the examples in the specs" + "path/to/directory - Write all the examples into separate files in this directory" ), ) parser.add_argument( @@ -136,100 +141,130 @@ example_identifer = _args.example.stem with open(_args.example, "r") as f: example = yaml.load(f, Loader=yaml.FullLoader) -for property in ("user_claims", "claims_structure", "disclosed_claims"): +for property in ("user_claims", "non_sd_claims", "holder_disclosed_claims"): if property not in example: sys.exit(f"Example file must define '{property}'.") ### Produce SD-JWT and SVC for selected example - -sdjwt_at_issuer = SDJWT( +SDJWTCommon.unsafe_randomness = _args.no_randomness +sdjwt_at_issuer = SDJWTIssuer( example["user_claims"], - example.get('further_claims', {}), settings.ISSUER, ISSUER_KEY, - HOLDER_KEY, - example["claims_structure"], - example.get("blinded_claim_names", []), + HOLDER_KEY if example.get("holder_binding", False) else None, + example["non_sd_claims"], iat=_args.iat, exp=_args.exp, + add_decoy_claims=example.get("add_decoy_claims", False), ) ### Produce SD-JWT-R for selected example # Note: The only input from the issuer is the combined SD-JWT and SVC! -sdjwt_at_holder = SDJWT.from_combined_sd_jwt_svc(sdjwt_at_issuer.combined_sd_jwt_svc) - -sdjwt_at_holder.create_sd_jwt_release( - _args.nonce, - settings.VERIFIER, - example["disclosed_claims"], - HOLDER_KEY, +sdjwt_at_holder = SDJWTHolder(sdjwt_at_issuer.combined_sd_jwt_iid) +sdjwt_at_holder.create_presentation( + example["holder_disclosed_claims"], + _args.nonce if example.get("holder_binding", False) else None, + settings.VERIFIER if example.get("holder_binding", False) else None, + HOLDER_KEY if example.get("holder_binding", False) else None, ) ### Verify the SD-JWT using the SD-JWT-R # Note: The only input from the holder is the combined presentation! - -sdjwt_at_verifier = SDJWT.from_combined_presentation( - sdjwt_at_holder.combined_presentation -) -vc = sdjwt_at_verifier.verify( - ISSUER_PUBLIC_KEY, - settings.ISSUER, - HOLDER_KEY, - settings.VERIFIER, - _args.nonce, -) -merged = sdjwt_at_verifier.verify( +sdjwt_at_verifier = SDJWTVerifier( + sdjwt_at_holder.combined_presentation, ISSUER_PUBLIC_KEY, settings.ISSUER, - HOLDER_KEY, - settings.VERIFIER, - _args.nonce, - return_merged=True, + settings.VERIFIER if example.get("holder_binding", False) else None, + _args.nonce if example.get("holder_binding", False) else None, ) +verified = sdjwt_at_verifier.get_verified_payload() ### Done - now output everything to CLI (unless --replace-examples-in was used) +iid_payload = "" +for hash in sdjwt_at_holder._hash_to_decoded_disclosure: + salt, claim_name, claim_value = sdjwt_at_holder._hash_to_decoded_disclosure[hash] + b64 = sdjwt_at_holder._hash_to_disclosure[hash] + encoded_json = sdjwt_at_holder._base64url_decode(b64).decode("utf-8") + + iid_payload += f"__Disclosure for `{claim_name}`:__\n" + iid_payload += f"\n```\n{textwrap_text(b64)}\n```\n\n" + iid_payload += f"Contents:\n" + iid_payload += f"\n```\n{textwrap_text(encoded_json)}\n```\n\n" + iid_payload += f"SHA-256 Hash: `{hash}`\n\n" + +iid_payload = iid_payload.strip() + _artifacts = { - "user_claims": (example["user_claims"], "User Claims"), - "sd_jwt_payload": (sdjwt_at_issuer.sd_jwt_payload, "Payload of the SD-JWT"), - "serialized_sd_jwt": (sdjwt_at_issuer.serialized_sd_jwt, "Serialized SD-JWT"), - "iid_payload": ( - sdjwt_at_issuer.svc_payload, - "Payload of the Issuer-Issued Disclosures Object (II-Disclosures Object)", + "user_claims": (example["user_claims"], "User Claims", "json"), + "sd_jwt_payload": (sdjwt_at_issuer.sd_jwt_payload, "Payload of the SD-JWT", "json"), + "sd_jwt_serialized": ( + sdjwt_at_issuer.serialized_sd_jwt, + "Serialized SD-JWT", + "txt", ), - "serialized_iid": (sdjwt_at_issuer.serialized_svc, "Serialized II-Disclosures Object"), - "combined_sd_jwt_iid": ( - sdjwt_at_issuer.combined_sd_jwt_svc, - "Combined SD-JWT and II-Disclosures Object representation", + "disclosures": (iid_payload, "Payloads of the II-Disclosures", "md"), + "combined_issuance": ( + sdjwt_at_issuer.combined_sd_jwt_iid, + "Combined SD-JWT and Disclosures", + "txt", ), - "hsd_jwt_payload": ( - sdjwt_at_holder.sd_jwt_release_payload, - "Payload of the Holder-Selected Disclosures JWT (HS-Disclosures JWT)", + "hb_jwt_payload": ( + sdjwt_at_holder.holder_binding_jwt_payload if example.get("holder_binding") else None, + "Payload of the Holder Binding JWT", + "json", ), - "serialized_hsd_jwt": ( - sdjwt_at_holder.serialized_sd_jwt_release, - "Serialized HS-Disclosures JWT", + "hb_jwt_serialized": ( + sdjwt_at_holder.serialized_holder_binding_jwt, + "Serialized Holder Binding JWT", + "txt", ), - "combined_sd_jwt_hsd_jwt": ( + "combined_presentation": ( sdjwt_at_holder.combined_presentation, - "Combined representation of SD-JWT and HS-Disclosures JWT", + "Combined representation of SD-JWT and HS-Disclosures", + "txt", ), - "verified_contents": (vc, "Verified released contents of the SD-JWT"), - "merged": (merged, "Merged contents of the SD-JWT"), + "verified_contents": (verified, "Verified released contents of the SD-JWT", "json"), } +if _args.output_dir: + logger.info( + f"Writing all the examples into separate files in '{_args.output_dir}'." + ) -if not _args.replace_examples_in: + output_dir = _args.output_dir / example_identifer - for key, (data, description) in _artifacts.items(): + if not output_dir.exists(): + output_dir.mkdir(parents=True) + + for key, (data, _, ftype) in _artifacts.items(): + if data is None: + continue + + if ftype == "json": + out = textwrap_json(data) + elif ftype == "txt": + out = textwrap_text(data) + else: + out = data + + with open(output_dir / f"{key}.{ftype}", "w") as f: + f.write(out) + +else: + for key, (data, description, ftype) in _artifacts.items(): print(f"{description} ({key}):") - if isinstance(data, dict): - print_repr(json.dumps(data, indent=_args.indent)) + if ftype == "json": + out = textwrap_json(data) + elif ftype == "txt": + out = textwrap_text(data) else: - print_repr(data) + out = data + + print(out) # Small hack to display some values in decoded form if key.startswith("serialized_"): @@ -237,17 +272,3 @@ if not _args.replace_examples_in: print_decoded_repr(data) sys.exit(0) - - -### Otherwise, update examples in the spec - -from sd_jwt.replace_utils import replace_all_in_file - -logger.info(f"Replacing the placeholders in the '{_args.replace_examples_in}' file.") - -replace_all_in_file( - _args.replace_examples_in, - _artifacts, - f"example-{example_identifer}-", - ignore_missing_placeholders=True, -) diff --git a/sd_jwt/demo_utils.py b/sd_jwt/demo_utils.py index 7c7a4b4f..42ffd7e2 100644 --- a/sd_jwt/demo_utils.py +++ b/sd_jwt/demo_utils.py @@ -4,7 +4,6 @@ import random from jwcrypto.jwk import JWK -from sd_jwt.utils import pad_urlsafe_b64 from typing import Union logger = logging.getLogger(__name__) @@ -20,7 +19,8 @@ def print_decoded_repr(value: str, nlines=2): seq = [] for i in value.split("."): try: - seq.append(f"{base64.urlsafe_b64decode(pad_urlsafe_b64(i)).decode()}") + padded = f"{i}{'=' * divmod(len(i),4)[1]}" + seq.append(f"{base64.urlsafe_b64decode(padded).decode()}") except Exception as e: logging.debug(f"{e} - for value: {i}") seq.append(i) diff --git a/sd_jwt/examples/address_only_flat.yml b/sd_jwt/examples/address_only_flat.yml new file mode 100644 index 00000000..52fc8f37 --- /dev/null +++ b/sd_jwt/examples/address_only_flat.yml @@ -0,0 +1,18 @@ +user_claims: + { + "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", + "address": + { + "street_address": "Schulstr. 12", + "locality": "Schulpforta", + "region": "Sachsen-Anhalt", + "country": "DE", + }, + } + +non_sd_claims: {"sub": true} + +holder_disclosed_claims: + {} + +holder_binding: False \ No newline at end of file diff --git a/sd_jwt/examples/address_only_structured.yml b/sd_jwt/examples/address_only_structured.yml new file mode 100644 index 00000000..a70cf968 --- /dev/null +++ b/sd_jwt/examples/address_only_structured.yml @@ -0,0 +1,18 @@ +user_claims: + { + "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", + "address": + { + "street_address": "Schulstr. 12", + "locality": "Schulpforta", + "region": "Sachsen-Anhalt", + "country": "DE", + }, + } + +non_sd_claims: { "address": {}, "sub": true } + +holder_disclosed_claims: + {} + +holder_binding: False diff --git a/sd_jwt/examples/address_only_structured_one_open.yml b/sd_jwt/examples/address_only_structured_one_open.yml new file mode 100644 index 00000000..a1a1a1f8 --- /dev/null +++ b/sd_jwt/examples/address_only_structured_one_open.yml @@ -0,0 +1,18 @@ +user_claims: + { + "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", + "address": + { + "street_address": "Schulstr. 12", + "locality": "Schulpforta", + "region": "Sachsen-Anhalt", + "country": "DE", + }, + } + +non_sd_claims: { "address": { "country": true }, "sub": true } + +holder_disclosed_claims: + {} + +holder_binding: False diff --git a/sd_jwt/examples/complex_eidas.yml b/sd_jwt/examples/complex_eidas.yml index 04ebe0ac..4360c3e7 100644 --- a/sd_jwt/examples/complex_eidas.yml +++ b/sd_jwt/examples/complex_eidas.yml @@ -43,24 +43,24 @@ user_claims: "birth_middle_name": "Lello", } -claims_structure: +non_sd_claims: { "verified_claims": { "verification": { - "trust_framework": "", + "trust_framework": null, "evidence": [{ "document": { "issuer": {} } }] }, "claims": { - "person_unique_identifier": "", - "given_name": "", - "family_name": "", - "nationalities": "" + "person_unique_identifier": null, + "given_name": null, + "family_name": null, + "nationalities": null }, }, } -disclosed_claims: +holder_disclosed_claims: { "verified_claims": { diff --git a/sd_jwt/examples/complex_eidas_proposal.yml b/sd_jwt/examples/complex_eidas_proposal.yml index 3b79c96c..5eeefdee 100644 --- a/sd_jwt/examples/complex_eidas_proposal.yml +++ b/sd_jwt/examples/complex_eidas_proposal.yml @@ -40,7 +40,7 @@ user_claims: "birth_middle_name": "Lello", # other optional claim not covered by eu.europa.ec.eudiw.pid.1 } -claims_structure: +non_sd_claims: { "verified_claims": { @@ -57,7 +57,7 @@ claims_structure: }, } -disclosed_claims: +holder_disclosed_claims: { "verified_claims": { diff --git a/sd_jwt/examples/complex.yml b/sd_jwt/examples/complex_ekyc.yml similarity index 79% rename from sd_jwt/examples/complex.yml rename to sd_jwt/examples/complex_ekyc.yml index 9e933bb6..5f2a3465 100644 --- a/sd_jwt/examples/complex.yml +++ b/sd_jwt/examples/complex_ekyc.yml @@ -27,14 +27,17 @@ user_claims: "claims": { "given_name": "Max", - "family_name": "Meier", + "family_name": "Müller", "nationalities": ["DE"], + "birthdate": "1956-01-28", + "place_of_birth": + { "country": "IS", "locality": "Þykkvabæjarklaustur" }, "address": { "locality": "Maxstadt", "postal_code": "12344", "country": "DE", - "street_address": "An der Weide 22", + "street_address": "Weidenstraße 22", }, }, }, @@ -43,28 +46,20 @@ user_claims: "msisdn": "49123456789", } -further_claims: +non_sd_claims: { "verified_claims": { + "verification": { "evidence": [{ "document": { "issuer": {} } }] }, "claims": { - "birthdate": "1956-01-28", - "place_of_birth": { "country": "DE", "locality": "Musterstadt" }, + "birthdate": true, + "place_of_birth": { "country": true, "locality": true }, }, }, } -claims_structure: - { - "verified_claims": - { - "verification": { "evidence": [{ "document": { "issuer": {} } }] }, - "claims": { "address": {} }, - }, - } - -disclosed_claims: +holder_disclosed_claims: { "verified_claims": { @@ -74,10 +69,6 @@ disclosed_claims: "time": null, "evidence": [{ "type": null }], }, - "claims": - { - "given_name": null, - "family_name": null, - }, + "claims": { "given_name": null, "family_name": null, "address": null }, }, } diff --git a/sd_jwt/examples/simple.yml b/sd_jwt/examples/simple.yml index c837b2b6..613a3493 100644 --- a/sd_jwt/examples/simple.yml +++ b/sd_jwt/examples/simple.yml @@ -1,6 +1,6 @@ user_claims: { - "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", + "sub": "john_doe_42", "given_name": "John", "family_name": "Doe", "email": "johndoe@example.com", @@ -15,6 +15,9 @@ user_claims: "birthdate": "1940-01-01", } -claims_structure: { } +non_sd_claims: {} -disclosed_claims: { "given_name": null, "family_name": null, "address": null } +holder_disclosed_claims: + { "given_name": null, "family_name": null, "address": null } + +holder_binding: True \ No newline at end of file diff --git a/sd_jwt/examples/simple_structured.yml b/sd_jwt/examples/simple_structured.yml index 126df20b..01faa0c4 100644 --- a/sd_jwt/examples/simple_structured.yml +++ b/sd_jwt/examples/simple_structured.yml @@ -1,26 +1,27 @@ user_claims: { "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", - "given_name": "John", - "family_name": "Doe", - "email": "johndoe@example.com", - "phone_number": "+1-202-555-0101", + "given_name": "太郎", + "family_name": "山田", + "email": "\"unusual email address\"@nihon.com", + "phone_number": "08012345678", "address": { - "street_address": "123 Main St", - "locality": "Anytown", - "region": "Anystate", - "country": "US", + "street_address": "東京都港区芝公園4丁目2−8", + "locality": "東京都", + "region": "港区", + "country": "JP", }, "birthdate": "1940-01-01", } -claims_structure: { "address": {} } +non_sd_claims: { "address": {} } -disclosed_claims: +holder_disclosed_claims: { "given_name": null, "family_name": null, "birthdate": null, "address": { "region": null, "country": null }, + "email": null, } diff --git a/sd_jwt/examples/simple_structured_all_blinded.yml b/sd_jwt/examples/simple_structured_all_blinded.yml deleted file mode 100644 index 5dee5198..00000000 --- a/sd_jwt/examples/simple_structured_all_blinded.yml +++ /dev/null @@ -1,48 +0,0 @@ -user_claims: - { - "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", - "given_name": "John", - "family_name": "Doe", - "email": "johndoe@example.com", - "phone_number": "+1-202-555-0101", - "secret_club_membership_no": "23", - "address": - { - "street_address": "123 Main St", - "locality": "Anytown", - "region": "Anystate", - "country": "US", - }, - "delivery_address": - { - "street_address": "123 Main St", - "locality": "Anytown", - "region": "Anystate", - "country": "US", - }, - "birthdate": "1940-01-01", - } - -claims_structure: { "address": {}, "delivery_address": {} } - -blinded_claim_names: - - "sub" - - "given_name" - - "family_name" - - "email" - - "phone_number" - - "secret_club_membership_no" - - "address" - - "street_address" - - "locality" - - "region" - - "country" - - "birthdate" - -disclosed_claims: - { - "given_name": null, - "family_name": null, - "birthdate": null, - "address": { "region": null, "country": null }, - } diff --git a/sd_jwt/examples/simple_structured_merging.yml b/sd_jwt/examples/simple_structured_merging.yml deleted file mode 100644 index 69325d74..00000000 --- a/sd_jwt/examples/simple_structured_merging.yml +++ /dev/null @@ -1,34 +0,0 @@ -user_claims: - { - "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", - "given_name": "John", - "family_name": "Doe", - "email": "johndoe@example.com", - "phone_number": "+1-202-555-0101", - "address": - { - "street_address": "123 Main St", - "locality": "Anytown", - "region": "Anystate", - }, - "birthdate": "1940-01-01", - } - -claims_structure: { "address": {} } - -further_claims: - { - "address": - { - "country": "US" - } - } - - -disclosed_claims: - { - "given_name": null, - "family_name": null, - "birthdate": null, - "address": { "region": null, "street_address": null, "locality": null }, - } diff --git a/sd_jwt/examples/simple_structured_some_blinded.yml b/sd_jwt/examples/simple_structured_some_blinded.yml deleted file mode 100644 index dc305396..00000000 --- a/sd_jwt/examples/simple_structured_some_blinded.yml +++ /dev/null @@ -1,32 +0,0 @@ -user_claims: - { - "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", - "given_name": "John", - "family_name": "Doe", - "email": "johndoe@example.com", - "phone_number": "+1-202-555-0101", - "secret_club_membership_no": "23", - "other_secret_club_membership_no": "42", - "address": - { - "street_address": "123 Main St", - "locality": "Anytown", - "region": "Anystate", - "country": "US", - }, - "birthdate": "1940-01-01", - } - -claims_structure: { "address": {} } - -blinded_claim_names: - - secret_club_membership_no - -disclosed_claims: - { - "given_name": null, - "family_name": null, - "birthdate": null, - "address": { "region": null, "country": null }, - "secret_club_membership_no": null, - } diff --git a/sd_jwt/examples/simple_structured_with_decoys.yml b/sd_jwt/examples/simple_structured_with_decoys.yml new file mode 100644 index 00000000..ee9e4067 --- /dev/null +++ b/sd_jwt/examples/simple_structured_with_decoys.yml @@ -0,0 +1,29 @@ +user_claims: + { + "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", + "given_name": "太郎", + "family_name": "山田", + "email": "\"unusual email address\"@nihon.com", + "phone_number": "123456", + "address": + { + "street_address": "東京都港区芝公園4丁目2−8", + "locality": "東京都", + "region": "港区", + "country": "JP", + }, + "birthdate": "1940-01-01", + } + +non_sd_claims: { "address": {} } + +holder_disclosed_claims: + { + "given_name": null, + "family_name": null, + "birthdate": null, + "address": { "region": null, "country": null }, + "email": null, + } + +add_decoy_claims: true diff --git a/sd_jwt/examples/w3c-vc.yml b/sd_jwt/examples/w3c-vc.yml new file mode 100644 index 00000000..aa8c222d --- /dev/null +++ b/sd_jwt/examples/w3c-vc.yml @@ -0,0 +1,48 @@ +user_claims: + { + "iss": "https://example.com", + "jti": "http://example.com/credentials/3732", + "nbf": 1541493724, + "iat": 1541493724, + "cnf": + { + "jwk": + { + "kty": "RSA", + "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + "e": "AQAB", + }, + }, + "type": "IdentityCredential", + "credentialSubject": + { + "given_name": "John", + "family_name": "Doe", + "email": "johndoe@example.com", + "phone_number": "+1-202-555-0101", + "address": + { + "street_address": "123 Main St", + "locality": "Anytown", + "region": "Anystate", + "country": "US", + }, + "birthdate": "1940-01-01", + "is_over_18": true, + "is_over_21": true, + "is_over_65": true, + }, + } + +non_sd_claims: + { + "iss": true, + "jti": true, + "nbf": true, + "iat": true, + "cnf": { "jwk": { "kty": true, "n": true, "e": true } }, + "type": true, + "credentialSubject": { "address": {} }, + } + +holder_disclosed_claims: {} diff --git a/sd_jwt/examples/w3c-vc_for_slide_deck.yml b/sd_jwt/examples/w3c-vc_for_slide_deck.yml new file mode 100644 index 00000000..bc3e1409 --- /dev/null +++ b/sd_jwt/examples/w3c-vc_for_slide_deck.yml @@ -0,0 +1,45 @@ +user_claims: + { + "iss": "https://example.com", + "cnf": + { + "jwk": + { + "kty": "RSA", + "n": "0vx7agoebGcQSu....-csFCur-kEgU8awapJzKnqDKgw", + "e": "AQAB", + }, + }, + "type": "IdentityCredential", + "credentialSubject": + { + "given_name": "Max", + "family_name": "Mustermann", + "email": "mustermann@example.com", + "address": + { + "street_address": "Musterstr. 23", + "locality": "Berlin", + "country": "DE", + }, + "birthdate": "1971-12-23", + "is_over_18": true, + "is_over_21": true, + "is_over_65": false, + }, + } + +non_sd_claims: + { + "iss": true, + "cnf": { "jwk": { "kty": true, "n": true, "e": true } }, + "type": true, + "credentialSubject": { "address": {} }, + } + +holder_disclosed_claims: { + "given_name": null, + "family_name": null, + "address": null, + "is_over_18": null, +} diff --git a/sd_jwt/operations.py b/sd_jwt/operations.py index c83b58f6..21db154a 100644 --- a/sd_jwt/operations.py +++ b/sd_jwt/operations.py @@ -1,151 +1,228 @@ import datetime - +import random +import secrets from base64 import urlsafe_b64decode, urlsafe_b64encode from hashlib import sha256 from json import dumps, loads -from secrets import compare_digest +from time import time from typing import Dict, List, Optional, Tuple, Union from jwcrypto.jwk import JWK from jwcrypto.jws import JWS -from sd_jwt import DEFAULT_SIGNING_ALG, DIGEST_ALG_KEY, SD_II_CLAIMS_KEY, SD_HS_CLAIMS_KEY, SD_DIGESTS_KEY -from sd_jwt.utils import generate_salt, pad_urlsafe_b64, merge, sort_dict -from sd_jwt.walk import by_structure as walk_by_structure +from sd_jwt import DEFAULT_SIGNING_ALG, DIGEST_ALG_KEY, SD_DIGESTS_KEY + + +class SDJWTHasSDClaimException(Exception): + """Exception raised when input data contains the special _sd claim reserved for SD-JWT internal data.""" + def __init__(self, error_location: any): + super().__init__(f"Input data contains the special claim '{SD_DIGESTS_KEY}' reserved for SD-JWT internal data. Location: {error_location!r}") -class SDJWT: + +class SDJWTCommon: SD_JWT_HEADER = None # "sd+jwt" # WiP: https://github.com/oauthstuff/draft-selective-disclosure-jwt/issues/60 SD_JWT_R_HEADER = None # "sd+jwt-r" - DEFAULT_EXP_MINS = 15 # TODO: adopt a dynamic module/package loader, defs could be as string -> "fn": "hashlib.sha256" HASH_ALG = {"name": "sha-256", "fn": sha256} - HIDDEN_CLAIM_NAME_PREFIX = "" + COMBINED_FORMAT_SEPARATOR = "~" + + unsafe_randomness = False + + def _b64hash(self, raw): + # Calculate the SHA 256 hash and output it base64 encoded + return self._base64url_encode(self.HASH_ALG["fn"](raw).digest()) + + def _combine(self, *parts): + return self.COMBINED_FORMAT_SEPARATOR.join(parts) + + def _split(self, combined): + return combined.split(self.COMBINED_FORMAT_SEPARATOR) + + def _base64url_encode(self, data: bytes) -> str: + return urlsafe_b64encode(data).decode("ascii").strip("=") + + def _base64url_decode(self, b64data: str) -> bytes: + padded = f"{b64data}{'=' * divmod(len(b64data),4)[1]}" + return urlsafe_b64decode(padded) + + def _generate_salt(self): + if self.unsafe_randomness: + # This is not cryptographically secure, but it is deterministic + # and allows for repeatable output for the generation of the examples. + print( + "WARNING: Using unsafe randomness - output is not suitable for production use!" + ) + return self._base64url_encode( + bytes(random.getrandbits(8) for _ in range(16)) + ) + else: + return self._base64url_encode(secrets.token_bytes(16)) + + def _create_hash_mappings(self, disclosurses_list: List): + # Mapping from hash of disclosure to the decoded disclosure + self._hash_to_decoded_disclosure = {} + + # Mapping from hash of disclosure to the raw disclosure + self._hash_to_disclosure = {} + + for disclosure in disclosurses_list: + decoded_disclosure = loads( + self._base64url_decode(disclosure).decode("utf-8") + ) + hash = self._b64hash(disclosure.encode("ascii")) + if hash in self._hash_to_decoded_disclosure: + raise ValueError( + f"Duplicate disclosure hash {hash} for disclosure {decoded_disclosure}" + ) + + self._hash_to_decoded_disclosure[hash] = decoded_disclosure + self._hash_to_disclosure[hash] = disclosure + + def _check_for_sd_claim(self, the_object): + # Recursively check for the presence of the _sd claim, also + # works for arrays and nested objects. + if isinstance(the_object, dict): + for key, value in the_object.items(): + if key == SD_DIGESTS_KEY: + raise SDJWTHasSDClaimException(the_object) + else: + self._check_for_sd_claim(value) + elif isinstance(the_object, list): + for item in the_object: + self._check_for_sd_claim(item) + else: + return - SD_KEY_SALT = "s" - SD_KEY_VALUE = "v" - SD_KEY_CLAIM_NAME = "n" - # Issuer produces: - salts_and_blinded_claim_names: Dict +class SDJWTIssuer(SDJWTCommon): + DEFAULT_EXP_MINS = 15 + DECOY_MIN_ELEMENTS = 2 + DECOY_MAX_ELEMENTS = 5 + sd_jwt_payload: Dict sd_jwt: JWS serialized_sd_jwt: str - svc_payload: Dict - serialized_svc: str - combined_sd_jwt_svc: str - - # Holder produces: - sd_jwt_release_payload: Dict - sd_jwt_release: JWS - serialized_sd_jwt_release: str - combined_presentation: str + + ii_disclosures = [] + combined_sd_jwt_iid: str + + _debug_ii_disclosures_contents = [] def __init__( self, user_claims: Dict, - further_claims: Dict, issuer: str, issuer_key, - holder_key, + holder_key=None, claims_structure: Optional[Dict] = None, - blinded_claim_names: Optional[List] = None, iat: Optional[int] = None, exp: Optional[int] = None, sign_alg=None, + add_decoy_claims: bool = False, ): self._user_claims = user_claims - self._further_claims = further_claims self._issuer = issuer self._issuer_key = issuer_key self._holder_key = holder_key self._claims_structure = claims_structure or {} - self._blinded_claim_names = blinded_claim_names or [] self._iat = iat or int(datetime.datetime.utcnow().timestamp()) self._exp = exp or self._iat + (self.DEFAULT_EXP_MINS * 60) self._sign_alg = sign_alg or DEFAULT_SIGNING_ALG + self._add_decoy_claims = add_decoy_claims - self._create_salts_and_blinded_claim_names() + self._check_for_sd_claim(self._user_claims) self._assemble_sd_jwt_payload() self._create_signed_jwt() - self._create_svc() self._create_combined() - def _create_salts_and_blinded_claim_names(self): - """ - This function generates a structure that follows the claims structure, but for - each entry contains a tuple: the salt value plus a random string that can become - the blinded claim name. - """ - - # something like: {'sub': ('zyZQuxk2AUv5_Z_RAMxh9Q', 'EpCuoArhQK6MjmO6D-Bi6w'), 'given_name': ('EpCuoArhQK6MjmO6D-Bi6w', 'ArhQK6MjmO6D-Bi6wud62k'). ... - self.salts_and_blinded_claim_names = walk_by_structure( - self._claims_structure, - self._user_claims, - lambda key, __, ___=None: (key, (generate_salt(), generate_salt())), - ) - def _assemble_sd_jwt_payload(self): # Create the JWS payload - self.sd_jwt_payload = { - "iss": self._issuer, - "cnf": {"jwk": self._holder_key.export_public(as_dict=True)}, - "iat": self._iat, - "exp": self._exp, - DIGEST_ALG_KEY: self.HASH_ALG["name"], - SD_DIGESTS_KEY: walk_by_structure( - self.salts_and_blinded_claim_names, - self._user_claims, - self._create_sd_claim_entry, - ), - } - - # If any of the claim names are blinded, sort the SD-JWT contents. - if self._blinded_claim_names: - self.sd_jwt_payload = sort_dict(self.sd_jwt_payload) - - self.sd_jwt_payload.update(self._further_claims) - - def _hash_raw(self, raw): - # Calculate the SHA 256 hash and output it base64 encoded - return ( - urlsafe_b64encode(self.HASH_ALG["fn"](raw).digest()) - .decode("ascii") - .strip("=") + self.sd_jwt_payload = self._create_sd_claims( + self._user_claims, self._claims_structure ) - - def _hash_claim( - self, key, value, salt_and_blinded_claim_name, return_raw=False - ) -> Tuple[str, str]: - salt, blinded_claim_name = salt_and_blinded_claim_name - if key in self._blinded_claim_names: - raw = dumps( - { - self.SD_KEY_SALT: salt, - self.SD_KEY_VALUE: value, - self.SD_KEY_CLAIM_NAME: key, - } - ) - output_key = self.HIDDEN_CLAIM_NAME_PREFIX + blinded_claim_name + self.sd_jwt_payload.update( + { + "iss": self._issuer, + "iat": self._iat, + "exp": self._exp, + DIGEST_ALG_KEY: self.HASH_ALG["name"], + } + ) + if self._holder_key: + self.sd_jwt_payload["cnf"] = { + "jwk": self._holder_key.export_public(as_dict=True) + } + + def _hash_claim(self, key, value) -> Tuple[str, str]: + json = dumps([self._generate_salt(), key, value]).encode("utf-8") + self._debug_ii_disclosures_contents.append(json.decode("utf-8")) + + raw_b64 = self._base64url_encode(json) + hash = self._b64hash(raw_b64.encode("ascii")) + + return (hash, raw_b64) + + def _create_sd_claim_entry(self, key, value: any) -> str: + hash, raw_b64 = self._hash_claim(key, value) + self.ii_disclosures.append(raw_b64) + return hash + + def _create_decoy_claim_entry(self) -> str: + return self._b64hash(self._generate_salt().encode("ascii")) + + def _create_sd_claims(self, user_claims, non_sd_claims): + # If the user claims are a list, apply this function + # to each item in the list. The first element in non_sd_claims + # (which is assumed to be a list as well) is used as the + # structure for each item in the list. + if type(user_claims) is list: + if type(non_sd_claims) is not list or len(non_sd_claims) < 1: + reference = {} + else: + reference = non_sd_claims[0] + return [self._create_sd_claims(claim, reference) for claim in user_claims] + + # If the user claims are a dictionary, apply this function + # to each key/value pair in the dictionary. The structure + # for each key/value pair is found in the non_sd_claims + # dictionary. If the key is not found in the non_sd_claims + # dictionary, then the value is assumed to be a claims that + # should be selectively disclosable. + elif type(user_claims) is dict: + sd_claims = {SD_DIGESTS_KEY: []} + for key, value in user_claims.items(): + if key in non_sd_claims: + sd_claims[key] = self._create_sd_claims( + value, non_sd_claims.get(key, {}) + ) + else: + # Assemble all hash digests in the disclosures list. + sd_claims[SD_DIGESTS_KEY].append( + self._create_sd_claim_entry(key, value) + ) + + # Add decoy claims if requested + if self._add_decoy_claims: + for _ in range( + random.randint(self.DECOY_MIN_ELEMENTS, self.DECOY_MAX_ELEMENTS) + ): + sd_claims[SD_DIGESTS_KEY].append(self._create_decoy_claim_entry()) + + # Delete the SD_DIGESTS_KEY if it is empty + if len(sd_claims[SD_DIGESTS_KEY]) == 0: + del sd_claims[SD_DIGESTS_KEY] + else: + # Sort the hash digests otherwise + sd_claims[SD_DIGESTS_KEY].sort() + + return sd_claims + + # For other types, assume that the value can be disclosed. else: - raw = dumps({self.SD_KEY_SALT: salt, self.SD_KEY_VALUE: value}) - output_key = key - - if return_raw: - return (output_key, raw) - # Calculate the SHA 256 hash and output it base64 encoded - return (output_key, self._hash_raw(raw.encode())) - - def _create_sd_claim_entry( - self, key, value: str, salt_and_blinded_claim_name: str - ) -> Tuple[str, str]: - """ - returns the hashed and salted value string - key arg is not used here, it's just for compliance to other calls - """ - return self._hash_claim(key, value, salt_and_blinded_claim_name) + return user_claims def _create_signed_jwt(self): """ @@ -164,259 +241,246 @@ def _create_signed_jwt(self): ) self.serialized_sd_jwt = self.sd_jwt.serialize(compact=True) - def _create_svc(self): - # Create the SVC - self.svc_payload = { - SD_II_CLAIMS_KEY: walk_by_structure( - self.salts_and_blinded_claim_names, - self._user_claims, - self._create_svc_entry, - ), - # "cnf_private": issuer_key.export_private(as_dict=True), - } - - # If any of the claim names are blinded, sort the SVC contents. - if self._blinded_claim_names: - self.svc_payload = sort_dict(self.svc_payload) - - self.serialized_svc = ( - urlsafe_b64encode(dumps(self.svc_payload).encode()) - .decode("ascii") - .strip("=") + def _create_combined(self): + self.combined_sd_jwt_iid = self._combine( + self.serialized_sd_jwt, *self.ii_disclosures ) - def _create_svc_entry( - self, key, value: str, salt_and_blinded_claim_name: str - ) -> Tuple[str, str]: - """ - returns a string representation of a list - [hashed and salted value string, value string] - key arg is not used here, it's just for compliances to other calls - """ - return self._hash_claim( - key, value, salt_and_blinded_claim_name, return_raw=True - ) - def _create_combined(self): - self.combined_sd_jwt_svc = self.serialized_sd_jwt + "." + self.serialized_svc +class SDJWTHolder(SDJWTCommon): + hs_disclosures: List + holder_binding_jwt_payload: Dict + holder_binding_jwt: JWS + serialized_holder_binding_jwt: str = "" + combined_presentation: str - #### Holder Operations #### + _ii_disclosures: List + _hash_to_decoded_disclosure: Dict + _hash_to_disclosure: Dict - @classmethod - def from_combined_sd_jwt_svc(cls, combined): - sdjwt = cls.__new__(cls) - sdjwt._parse_combined_sd_jwt_svc(combined) - return sdjwt + def __init__(self, combined_sd_jwt_iid: str): + self._parse_combined_sd_jwt_iid(combined_sd_jwt_iid) + self._create_hash_mappings(self._ii_disclosures) + self._extract_payload_unverified() - def _parse_combined_sd_jwt_svc(self, combined): + def _parse_combined_sd_jwt_iid(self, combined): + self.serialized_sd_jwt, *self._ii_disclosures = self._split(combined) - parts = combined.split(".") - if len(parts) != 4: - raise ValueError("Invalid number of parts in the combined presentation") + def _extract_payload_unverified(self): + # TODO: This holder does not verify the SD-JWT yet - this + # is not strictly needed, but it would be nice to have. - self.serialized_sd_jwt = ".".join(parts[:3]) - self.serialized_svc = parts[3] + # Extract only the body from SD-JWT without verifying the signature + _, jwt_body, _ = self.serialized_sd_jwt.split(".") + self.sd_jwt_payload = loads(self._base64url_decode(jwt_body)) + + def create_presentation( + self, claims_to_disclose, nonce=None, aud=None, holder_key=None, sign_alg=None + ): + # Select the disclosures + self.hs_disclosures = [] + self._select_disclosures(self.sd_jwt_payload, claims_to_disclose) + + # Optional: Create a holder binding JWT + if nonce and aud and holder_key: + self._create_holder_binding_jwt(nonce, aud, holder_key, sign_alg) + + # Create the combined presentation + # Note: If the holder binding JWT is not created, then the + # last element is empty, matching the spec. + self.combined_presentation = self._combine( + self.serialized_sd_jwt, + *self.hs_disclosures, + self.serialized_holder_binding_jwt, + ) - # Reconstruct hash raw values (salt+claim value) from serialized_svc - self._input_hash_raw_values = loads( - urlsafe_b64decode(pad_urlsafe_b64(self.serialized_svc)) - )[SD_II_CLAIMS_KEY] + def _select_disclosures(self, sd_jwt_claims, claims_to_disclose): + # Recursively process the claims in sd_jwt_claims. In each + # object found therein, look at the SD_DIGESTS_KEY. If it + # contains hash digests for claims that should be disclosed, + # then add the corresponding disclosures to the claims_to_disclose. + + if type(sd_jwt_claims) is list: + if type(claims_to_disclose) is not list or len(claims_to_disclose) < 1: + reference = {} + else: + reference = claims_to_disclose[0] + return [ + self._select_disclosures(claim, reference) for claim in sd_jwt_claims + ] + + elif type(sd_jwt_claims) is dict: + for key, value in sd_jwt_claims.items(): + if key == SD_DIGESTS_KEY: + for digest in value: + if digest not in self._hash_to_decoded_disclosure: + # fake digest + continue + decoded = self._hash_to_decoded_disclosure[digest] + _, key, _ = decoded + if key in claims_to_disclose: + self.hs_disclosures.append(self._hash_to_disclosure[digest]) + else: + self._select_disclosures(value, claims_to_disclose.get(key, {})) - # TODO: Check that input_sd_jwt and input_svc match + else: + pass - def create_sd_jwt_release( - self, nonce, aud, disclosed_claims, holder_key, sign_alg: Optional[str] = None + def _create_holder_binding_jwt( + self, nonce, aud, holder_key, sign_alg: Optional[str] = None ): _alg = sign_alg or DEFAULT_SIGNING_ALG - def find_claim_by_blinded_name(structure, key): - if key in structure: - return key - for key_in_structure, value_in_structure in structure.items(): - if not key_in_structure.startswith(self.HIDDEN_CLAIM_NAME_PREFIX): - continue - if not isinstance(value_in_structure, str): - continue - parsed = loads(value_in_structure) - if parsed.get(self.SD_KEY_CLAIM_NAME, None) == key: - return key_in_structure - raise KeyError() - - sd_jwt_r_struct = walk_by_structure( - self._input_hash_raw_values, - disclosed_claims, - lambda key, __, raw="???": (key, raw), - find_claim_by_blinded_name, - ) - - self.sd_jwt_release_payload = { + self.holder_binding_jwt_payload = { "nonce": nonce, "aud": aud, - SD_HS_CLAIMS_KEY: sd_jwt_r_struct, + "iat": int(time()), } # Sign the SD-JWT-Release using the holder's key - self.sd_jwt_release = JWS(payload=dumps(self.sd_jwt_release_payload)) + self.holder_binding_jwt = JWS(payload=dumps(self.holder_binding_jwt_payload)) _data = {"alg": _alg, "kid": holder_key.thumbprint()} if self.SD_JWT_R_HEADER: _data["typ"] = self.SD_JWT_R_HEADER - self.sd_jwt_release.add_signature( + self.holder_binding_jwt.add_signature( holder_key, alg=_alg, protected=dumps(_data), ) - self.serialized_sd_jwt_release = self.sd_jwt_release.serialize(compact=True) - - self.combined_presentation = ( - self.serialized_sd_jwt + "." + self.serialized_sd_jwt_release + self.serialized_holder_binding_jwt = self.holder_binding_jwt.serialize( + compact=True ) - #### Verifier Operations #### - - @classmethod - def from_combined_presentation(cls, combined_presentation): - sdjwt = cls.__new__(cls) - sdjwt._parse_combined_presentation(combined_presentation) - return sdjwt - - def _parse_combined_presentation(self, combined_presentation): - parts = combined_presentation.split(".") - if len(parts) != 6: - raise ValueError("Invalid number of parts in the combined presentation") - # Extract the parts - self._unverified_input_sd_jwt = ".".join(parts[:3]) - self._unverified_input_sd_jwt_release = ".".join(parts[3:]) +class SDJWTVerifier(SDJWTCommon): + _hs_disclosures: List + _hash_to_decoded_disclosure: Dict + _hash_to_disclosure: Dict - def verify( + def __init__( self, + combined_presentation: str, issuer_public_key: dict, expected_issuer: str, - holder_public_key: Union[dict, None] = None, expected_aud: Union[str, None] = None, expected_nonce: Union[str, None] = None, - return_merged=False, ): + self._parse_combined_presentation(combined_presentation) + self._create_hash_mappings(self._hs_disclosures) + self._verify_sd_jwt(issuer_public_key, expected_issuer) - if holder_public_key and (not expected_aud or not expected_nonce): - raise ValueError( - "When holder binding is to be checked, aud and nonce need to be provided." - ) - - sd_jwt_claims, holder_public_key_payload = self._verify_sd_jwt( - self._unverified_input_sd_jwt, issuer_public_key, expected_issuer - ) + # expected aud and nonce either need to be both set or both None + if expected_aud or expected_nonce: + if not (expected_aud and expected_nonce): + raise ValueError( + "Either both expected_aud and expected_nonce must be set or both must be None" + ) - sd_jwt_sd_claims = sd_jwt_claims[SD_DIGESTS_KEY] + # Verify the SD-JWT-Release + self._verify_holder_binding_jwt( + expected_aud, + expected_nonce, + ) - # Verify the SD-JWT-Release - sd_jwt_release_claims = self._verify_sd_jwt_release( - self._unverified_input_sd_jwt_release, - holder_public_key, - expected_aud, - expected_nonce, - holder_public_key_payload, - ) + def get_verified_payload(self): + return self._extract_sd_claims() - _wbs = walk_by_structure( - sd_jwt_sd_claims, sd_jwt_release_claims, self._check_claim - ) - if not return_merged: - return _wbs - else: - del sd_jwt_claims[SD_DIGESTS_KEY] - del sd_jwt_claims[DIGEST_ALG_KEY] - return merge(_wbs, sd_jwt_claims) + def _parse_combined_presentation(self, combined): + ( + self._unverified_input_sd_jwt, + *self._hs_disclosures, + self._unverified_input_holder_binding_jwt, + ) = self._split(combined) def _verify_sd_jwt( self, - sd_jwt: str, issuer_public_key: dict, expected_issuer: str, sign_alg: str = None, ): parsed_input_sd_jwt = JWS() - parsed_input_sd_jwt.deserialize(sd_jwt) + parsed_input_sd_jwt.deserialize(self._unverified_input_sd_jwt) parsed_input_sd_jwt.verify(issuer_public_key, alg=sign_alg) - sd_jwt_payload = loads(parsed_input_sd_jwt.payload) - if sd_jwt_payload["iss"] != expected_issuer: + self._sd_jwt_payload = loads(parsed_input_sd_jwt.payload) + if self._sd_jwt_payload["iss"] != expected_issuer: raise ValueError("Invalid issuer") # TODO: Check exp/nbf/iat - if DIGEST_ALG_KEY not in sd_jwt_payload: - raise ValueError("Missing hash algorithm") - if sd_jwt_payload[DIGEST_ALG_KEY] != SDJWT.HASH_ALG["name"]: - raise ValueError("Invalid hash algorithm") - - if SD_DIGESTS_KEY not in sd_jwt_payload: - raise ValueError("No selective disclosure claims in SD-JWT") - - holder_public_key_payload = None - if "cnf" in sd_jwt_payload: - holder_public_key_payload = sd_jwt_payload["cnf"] + self._holder_public_key_payload = self._sd_jwt_payload.get("cnf", None) - return sd_jwt_payload, holder_public_key_payload - - def _verify_sd_jwt_release( + def _verify_holder_binding_jwt( self, - sd_jwt_release: Union[dict, str], # the release could be signed by the holder - holder_public_key: Union[dict, None] = None, expected_aud: Union[str, None] = None, expected_nonce: Union[str, None] = None, - holder_public_key_payload: Union[dict, None] = None, sign_alg: Union[str, None] = None, ): _alg = sign_alg or DEFAULT_SIGNING_ALG - parsed_input_sd_jwt_release = JWS() - parsed_input_sd_jwt_release.deserialize(sd_jwt_release) + parsed_input_holder_binding_jwt = JWS() + parsed_input_holder_binding_jwt.deserialize( + self._unverified_input_holder_binding_jwt + ) - if holder_public_key and holder_public_key_payload: - holder_public_key_payload_jwk = holder_public_key_payload.get('jwk', None) - if not holder_public_key_payload_jwk: - raise ValueError( - "The holder_public_key_payload is malformed. " - "It doesn't contain the claim jwk: " - f"{holder_public_key_payload}" - ) - - pubkey = JWK.from_json(dumps(holder_public_key_payload_jwk)) - # TODO: adopt an OrderedDict here - # Because of weird bug of failed != between two public keys - if not holder_public_key == pubkey: - raise ValueError("cnf is not matching with HOLDER Public Key.") - if holder_public_key: - parsed_input_sd_jwt_release.verify(holder_public_key, alg=_alg) + if not self._holder_public_key_payload: + raise ValueError("No holder public key in SD-JWT") - sd_jwt_release_payload = loads(parsed_input_sd_jwt_release.payload) + holder_public_key_payload_jwk = self._holder_public_key_payload.get("jwk", None) + if not holder_public_key_payload_jwk: + raise ValueError( + "The holder_public_key_payload is malformed. " + "It doesn't contain the claim jwk: " + f"{self._holder_public_key_payload}" + ) - if holder_public_key: - if sd_jwt_release_payload["aud"] != expected_aud: - raise ValueError("Invalid audience") - if sd_jwt_release_payload["nonce"] != expected_nonce: - raise ValueError("Invalid nonce") + pubkey = JWK.from_json(dumps(holder_public_key_payload_jwk)) - if SD_HS_CLAIMS_KEY not in sd_jwt_release_payload: - raise ValueError("No selective disclosure claims in SD-JWT-Release") + parsed_input_holder_binding_jwt.verify(pubkey, alg=_alg) - return sd_jwt_release_payload[SD_HS_CLAIMS_KEY] + holder_binding_jwt_payload = loads(parsed_input_holder_binding_jwt.payload) - def _check_claim( - self, claim_name: str, released_value: str, sd_jwt_claim_value: str - ): - # the hash of the release claim value must match the claim value in the sd_jwt - hashed_release_value = self._hash_raw(released_value.encode("utf-8")) - if not compare_digest(hashed_release_value, sd_jwt_claim_value): - raise ValueError( - "Claim release value does not match the claim value in the SD-JWT" - ) + if holder_binding_jwt_payload["aud"] != expected_aud: + raise ValueError("Invalid audience") + if holder_binding_jwt_payload["nonce"] != expected_nonce: + raise ValueError("Invalid nonce") - decoded = loads(released_value) - if not isinstance(decoded, dict): - raise ValueError("Claim release value is not a dictionary") + def _extract_sd_claims(self): + if DIGEST_ALG_KEY not in self._sd_jwt_payload: + raise ValueError("Missing hash algorithm") - output_claim_name = decoded.get(self.SD_KEY_CLAIM_NAME, claim_name) - return (output_claim_name, decoded[self.SD_KEY_VALUE]) + if self._sd_jwt_payload[DIGEST_ALG_KEY] != self.HASH_ALG["name"]: + # TODO: Support other hash algorithms + raise ValueError("Invalid hash algorithm") + + self._duplicate_hash_check = [] + return self._unpack_disclosed_claims(self._sd_jwt_payload) + + def _unpack_disclosed_claims(self, sd_jwt_claims): + # In a list, unpack each element individually + if type(sd_jwt_claims) is list: + return [self._unpack_disclosed_claims(c) for c in sd_jwt_claims] + + elif type(sd_jwt_claims) is dict: + output = { + k: self._unpack_disclosed_claims(v) + for k, v in sd_jwt_claims.items() + if k != SD_DIGESTS_KEY + } + + for digest in sd_jwt_claims.get(SD_DIGESTS_KEY, []): + if digest in self._duplicate_hash_check: + raise ValueError(f"Duplicate hash found in SD-JWT: {digest}") + self._duplicate_hash_check.append(digest) + + if digest in self._hash_to_decoded_disclosure: + _, key, value = self._hash_to_decoded_disclosure[digest] + if key in output: + raise ValueError(f"Duplicate key found when unpacking disclosed claim: '{key}' in {output}. This is not allowed.") + output[key] = value + + return output + + else: + return sd_jwt_claims diff --git a/sd_jwt/replace_utils.py b/sd_jwt/replace_utils.py index 445dcabf..5101d141 100644 --- a/sd_jwt/replace_utils.py +++ b/sd_jwt/replace_utils.py @@ -1,6 +1,5 @@ import json import logging -import re from textwrap import fill, wrap from pathlib import Path @@ -8,13 +7,15 @@ EXAMPLE_INDENT = 2 EXAMPLE_MAX_WIDTH = 68 +EXAMPLE_ENSURE_ASCII = False ####################################################################### -# Helper functions to replace the examples in the markdown file +# Helper functions to format examples ####################################################################### -def textwrap_json(text, width): +def textwrap_json(data, width=EXAMPLE_MAX_WIDTH): + text = json.dumps(data, indent=EXAMPLE_INDENT, ensure_ascii=EXAMPLE_ENSURE_ASCII) output = [] for line in text.splitlines(): if len(line) <= width: @@ -43,74 +44,9 @@ def textwrap_json(text, width): return output - -def replace_code_in_markdown_source(file_contents, placeholder_id, new_code): - """ - the markdown contains code blocks that look like this: - {#placeholder-id} - ``` - some-code - ``` - - This function replaces the code block with the replacement - """ - - def replacement(match): - return match.group(1) + new_code + "\n```" - - new_string, count = re.subn( - r"({#" + placeholder_id + r"}\n```[a-z-_]*\n)(?:[\s\S]*?)\n```", - replacement, - file_contents, - flags=re.MULTILINE, +def textwrap_text(text, width=EXAMPLE_MAX_WIDTH): + return fill( + text, + width=width, + break_on_hyphens=False, ) - if count == 0: - raise ValueError - - return new_string - - -def replace_all_in_file( - file: Path, - replacements: dict, - prefix: str, - ignore_missing_placeholders: bool = False, -): - """ - Replaces all the placeholders in the draft-ietf-oauth-selective-disclosure-jwt.md file - """ - - file_contents = file.read_text() - - # create backup - file.with_suffix(".bak").write_text(file_contents) - - for key, (data, _) in replacements.items(): - if isinstance(data, dict): - new_code = textwrap_json( - json.dumps(data, indent=EXAMPLE_INDENT), - width=EXAMPLE_MAX_WIDTH, - ) - - else: - new_code = fill( - data, - width=EXAMPLE_MAX_WIDTH, - break_on_hyphens=False, - ) - - placeholder_id = prefix + key - - try: - file_contents = replace_code_in_markdown_source( - file_contents, placeholder_id, new_code - ) - logger.info(f"Found and replace contents for placeholder {placeholder_id}") - except ValueError: - - if not ignore_missing_placeholders: - raise - else: - logger.warning(f"Could not find placeholder with id {placeholder_id}") - - file.write_text(file_contents) diff --git a/sd_jwt/utils.py b/sd_jwt/utils.py deleted file mode 100644 index 4700faa2..00000000 --- a/sd_jwt/utils.py +++ /dev/null @@ -1,39 +0,0 @@ -import random -from base64 import urlsafe_b64encode - - -def pad_urlsafe_b64(value: str): - return f"{value}{'=' * divmod(len(value),4)[1]}" - - -# The salts will be selected by the server, of course. -def generate_salt(): - return ( - urlsafe_b64encode(bytes(random.getrandbits(8) for _ in range(16))) - .decode("ascii") - .strip("=") - ) - - -# Deep merge two dicts, with the second dict taking precedence -def merge(dict_a, dict_b): - for key in dict_b: - if ( - key in dict_a - and isinstance(dict_a[key], dict) - and isinstance(dict_b[key], dict) - ): - merge(dict_a[key], dict_b[key]) - else: - dict_a[key] = dict_b[key] - return dict_a - - -# Python preserves the order of keys in dicts. This function sorts dicts by keys, recursively. -def sort_dict(d): - if isinstance(d, dict): - return {k: sort_dict(v) for k, v in sorted(d.items())} - elif isinstance(d, list): - return [sort_dict(v) for v in d] - else: - return d diff --git a/sd_jwt/walk.py b/sd_jwt/walk.py deleted file mode 100644 index 20c18478..00000000 --- a/sd_jwt/walk.py +++ /dev/null @@ -1,290 +0,0 @@ -import json -import logging -from typing import Any, Callable, Dict, Optional, Tuple - -logger = logging.getLogger("sd_jwt") - - -def simple_find_by_key(_: Dict, key: str): - """ - This function defines the default behavior for find_fn (see below), which is - finding elements in the dictionary `structure` simply by looking up the key - `key`. - """ - return key - - -def by_structure( - structure: Dict, - obj: Dict, - fn: Callable[[str, str, Optional[Any]], Tuple[str, Any]], - find_fn=simple_find_by_key, -): - """ - This helper function allows traversing a nested dictionary using a given - structure as the guide. A function that is passed as an argument is called - for every leaf node in obj that is not contained in the structure object. - - The function fn has the following signature: fn(key: str, value: str, - value_in_structure: Optional[Any]) -> Tuple[str, Any] - - value_in_structure is only passed if the structure already contains a value - at this point in the structure. - - The function must return a tuple consisting of a new key and a new value for - the output structure. - - The argument find_fn allows for a translation between keys in structure and - obj. It is called with structure and the key as found in obj as arguments - and is expected to return the key that is to be used to access the element - in structure. - - See examples below! - """ - logger.debug(f"Walking in: {obj} using {structure} on {fn}") - # was logger.debug(f"Walking in: {structure} using {obj} on {fn}") - out = {} - for key_in_obj, value_in_obj in obj.items(): - logger.debug(f"{key_in_obj}: {value_in_obj}") - try: - key_in_structure = find_fn(structure, key_in_obj) - value_in_structure = structure[key_in_structure] - - if isinstance(value_in_structure, dict): - out[key_in_structure] = by_structure( - value_in_structure, value_in_obj, fn, find_fn - ) - elif isinstance(value_in_structure, list): - out[key_in_structure] = list( - by_structure(value_in_structure[0], item, fn, find_fn) - for item in value_in_obj - ) - else: - new_key, new_value = fn( - key_in_structure, value_in_obj, value_in_structure - ) - out[new_key] = new_value - except KeyError: - new_key, new_value = fn(key_in_obj, value_in_obj) - out[new_key] = new_value - return out - - -if __name__ == "__main__": - # Example 1 - - def test_fn(key, value, value_in_structure=None): - return (key, f"called fn({key}, {value}, {value_in_structure})") - - structure0 = {} - - raw0 = { - "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", - "given_name": "John", - "family_name": "Doe", - "email": "johndoe@example.com", - "phone_number": "+1-202-555-0101", - "address": { - "street_address": "123 Main St", - "locality": "Anytown", - "region": "Anystate", - "country": "US", - }, - "birthdate": "1940-01-01", - } - - expected0 = { - "sub": "called fn(sub, 6c5c0a49-b589-431d-bae7-219122a9ec2c, None)", - "given_name": "called fn(given_name, John, None)", - "family_name": "called fn(family_name, Doe, None)", - "email": "called fn(email, johndoe@example.com, None)", - "phone_number": "called fn(phone_number, +1-202-555-0101, None)", - "address": "called fn(address, {'street_address': '123 Main St', 'locality': 'Anytown', 'region': 'Anystate', 'country': 'US'}, None)", - "birthdate": "called fn(birthdate, 1940-01-01, None)", - } - - output0 = by_structure(structure0, raw0, test_fn) - print(json.dumps(output0, indent=4)) - assert output0 == expected0 - - structure1 = { - "verified_claims": { - "verification": { - "evidence": [ - { - "document": { - "issuer": {}, - } - } - ] - }, - "claims": { - "place_of_birth": {}, - }, - } - } - - raw1 = { - "verified_claims": { - "verification": { - "trust_framework": "de_aml", - "time": "2012-04-23T18:25Z", - "verification_process": "f24c6f-6d3f-4ec5-973e-b0d8506f3bc7", - "evidence": [ - { - "type": "document", - "method": "pipp", - "time": "2012-04-22T11:30Z", - "document": { - "type": "idcard", - "issuer": {"name": "Stadt Augsburg", "country": "DE"}, - "number": "53554554", - "date_of_issuance": "2010-03-23", - "date_of_expiry": "2020-03-22", - }, - } - ], - }, - "claims": { - "given_name": "Max", - "family_name": "Meier", - "birthdate": "1956-01-28", - "place_of_birth": {"country": "DE", "locality": "Musterstadt"}, - "nationalities": ["DE"], - "address": { - "locality": "Maxstadt", - "postal_code": "12344", - "country": "DE", - "street_address": "An der Weide 22", - }, - }, - }, - "unverified_family_name": "Meier", - "unverified_birthdate": "1956-01-28", - } - - expected1 = { - "verified_claims": { - "verification": { - "trust_framework": "called fn(trust_framework, de_aml, None)", - "time": "called fn(time, 2012-04-23T18:25Z, None)", - "verification_process": "called fn(verification_process, f24c6f-6d3f-4ec5-973e-b0d8506f3bc7, None)", - "evidence": [ - { - "type": "called fn(type, document, None)", - "method": "called fn(method, pipp, None)", - "time": "called fn(time, 2012-04-22T11:30Z, None)", - "document": { - "type": "called fn(type, idcard, None)", - "issuer": { - "name": "called fn(name, Stadt Augsburg, None)", - "country": "called fn(country, DE, None)", - }, - "number": "called fn(number, 53554554, None)", - "date_of_issuance": "called fn(date_of_issuance, 2010-03-23, None)", - "date_of_expiry": "called fn(date_of_expiry, 2020-03-22, None)", - }, - } - ], - }, - "claims": { - "given_name": "called fn(given_name, Max, None)", - "family_name": "called fn(family_name, Meier, None)", - "birthdate": "called fn(birthdate, 1956-01-28, None)", - "place_of_birth": { - "country": "called fn(country, DE, None)", - "locality": "called fn(locality, Musterstadt, None)", - }, - "nationalities": "called fn(nationalities, ['DE'], None)", - "address": "called fn(address, {'locality': 'Maxstadt', 'postal_code': '12344', 'country': 'DE', 'street_address': 'An der Weide 22'}, None)", - }, - }, - "unverified_family_name": "called fn(unverified_family_name, Meier, None)", - "unverified_birthdate": "called fn(unverified_birthdate, 1956-01-28, None)", - } - - output1 = by_structure(structure1, raw1, test_fn) - print(json.dumps(output1, indent=2)) - assert output1 == expected1 - - # Example 2 - - expected2 = { - "verified_claims": { - "verification": { - "trust_framework": "called fn(trust_framework, de_aml, called fn(trust_framework, de_aml, None))", - "time": "called fn(time, 2012-04-23T18:25Z, called fn(time, 2012-04-23T18:25Z, None))", - "verification_process": "called fn(verification_process, f24c6f-6d3f-4ec5-973e-b0d8506f3bc7, called fn(verification_process, f24c6f-6d3f-4ec5-973e-b0d8506f3bc7, None))", - "evidence": [ - { - "type": "called fn(type, document, called fn(type, document, None))", - "method": "called fn(method, pipp, called fn(method, pipp, None))", - "time": "called fn(time, 2012-04-22T11:30Z, called fn(time, 2012-04-22T11:30Z, None))", - "document": { - "type": "called fn(type, idcard, called fn(type, idcard, None))", - "issuer": { - "name": "called fn(name, Stadt Augsburg, called fn(name, Stadt Augsburg, None))", - "country": "called fn(country, DE, called fn(country, DE, None))", - }, - "number": "called fn(number, 53554554, called fn(number, 53554554, None))", - "date_of_issuance": "called fn(date_of_issuance, 2010-03-23, called fn(date_of_issuance, 2010-03-23, None))", - "date_of_expiry": "called fn(date_of_expiry, 2020-03-22, called fn(date_of_expiry, 2020-03-22, None))", - }, - } - ], - }, - "claims": { - "given_name": "called fn(given_name, Max, called fn(given_name, Max, None))", - "family_name": "called fn(family_name, Meier, called fn(family_name, Meier, None))", - "birthdate": "called fn(birthdate, 1956-01-28, called fn(birthdate, 1956-01-28, None))", - "place_of_birth": { - "country": "called fn(country, DE, called fn(country, DE, None))", - "locality": "called fn(locality, Musterstadt, called fn(locality, Musterstadt, None))", - }, - "nationalities": "called fn(nationalities, ['DE'], called fn(nationalities, ['DE'], None))", - "address": "called fn(address, {'locality': 'Maxstadt', 'postal_code': '12344', 'country': 'DE', 'street_address': 'An der Weide 22'}, called fn(address, {'locality': 'Maxstadt', 'postal_code': '12344', 'country': 'DE', 'street_address': 'An der Weide 22'}, None))", - }, - }, - "unverified_family_name": "called fn(unverified_family_name, Meier, called fn(unverified_family_name, Meier, None))", - "unverified_birthdate": "called fn(unverified_birthdate, 1956-01-28, called fn(unverified_birthdate, 1956-01-28, None))", - } - - # Take the output of example 1 as the structure this time. - output2 = by_structure(output1, raw1, test_fn) - print(json.dumps(output2, indent=2)) - - assert output2 == expected2 - - def test_fn_with_renaming(key, value, value_in_structure=None): - return (key + "X", f"called fn({key}, {value}, {value_in_structure})") - - structure3 = {} - - raw3 = { - "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", - "given_name": "John", - "family_name": "Doe", - "email": "johndoe@example.com", - "phone_number": "+1-202-555-0101", - "address": { - "street_address": "123 Main St", - "locality": "Anytown", - "region": "Anystate", - "country": "US", - }, - "birthdate": "1940-01-01", - } - - expected3 = { - "subX": "called fn(sub, 6c5c0a49-b589-431d-bae7-219122a9ec2c, None)", - "given_nameX": "called fn(given_name, John, None)", - "family_nameX": "called fn(family_name, Doe, None)", - "emailX": "called fn(email, johndoe@example.com, None)", - "phone_numberX": "called fn(phone_number, +1-202-555-0101, None)", - "addressX": "called fn(address, {'street_address': '123 Main St', 'locality': 'Anytown', 'region': 'Anystate', 'country': 'US'}, None)", - "birthdateX": "called fn(birthdate, 1940-01-01, None)", - } - - output3 = by_structure(structure3, raw3, test_fn_with_renaming) - print(json.dumps(output3, indent=4)) - assert output3 == expected3 diff --git a/update-all-examples.sh b/update-all-examples.sh index dd7c1cbf..81ade4f4 100755 --- a/update-all-examples.sh +++ b/update-all-examples.sh @@ -1,7 +1,18 @@ #!/bin/sh -SDJWT_ARGS="--replace-examples-in draft-ietf-oauth-selective-disclosure-jwt.md --nonce XZOUco1u_gEPknxS78sWWg --iat 1516239022 --exp 1516247022 --no-randomness" + +# abort on errors +set -e + +OUTPUT_DIR="./examples" +SDJWT_ARGS="--output-dir $OUTPUT_DIR --nonce XZOUco1u_gEPknxS78sWWg --iat 1516239022 --exp 1516247022 --no-randomness" + +rm -r $OUTPUT_DIR/* for file in sd_jwt/examples/*.yml do + echo "Processing $file" sd_jwt $file $SDJWT_ARGS done +echo "Remember to add updated examples to git repository:" +echo "git add $OUTPUT_DIR" +