From 6accc2eea4a605bfaef2a6ef71e26c75d5fd3221 Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Wed, 13 Dec 2023 14:53:31 +0500 Subject: [PATCH 1/2] fixes: Fix holder selective disclosure - Enable issuing and verifying nested array SDs - Fix parts related to disclosing arrayed types - Refactoring Signed-off-by: Abdulbois --- .gitignore | 1 + Cargo.toml | 3 +++ src/holder.rs | 62 +++++++++++++++++++++++++++++++++++-------------- src/issuer.rs | 21 +++++------------ src/lib.rs | 19 +++++++-------- src/verifier.rs | 2 +- 6 files changed, 63 insertions(+), 45 deletions(-) diff --git a/.gitignore b/.gitignore index 6985cf1..a38b19f 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ Cargo.lock # MSVC Windows builds of rustc generate these, which store debugging information *.pdb +.idea/ \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 179438a..b75052a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,6 @@ log = "0.4" rand = "0.8" serde_json = { version = "1.0", features = ["preserve_order"] } sha2 = "0.10" + +[dev-dependencies] +rstest = "0.18.2" diff --git a/src/holder.rs b/src/holder.rs index a1c938c..c444a89 100644 --- a/src/holder.rs +++ b/src/holder.rs @@ -93,31 +93,40 @@ impl SDJWTHolder { claims_to_disclose: Map, ) -> Vec { let mut hash_to_disclosure = Vec::new(); - let default_list = Vec::new(); - let sd_map: HashMap<&str, (&Value, &str)> = sd_jwt_claims[SD_DIGESTS_KEY].as_array().unwrap_or(&default_list).iter().map(|digest| { + let sd_map: HashMap<&str, (&Value, &str)> = sd_jwt_claims.get(SD_DIGESTS_KEY).and_then(Value::as_array).unwrap_or(&default_list).iter().filter_map(|digest| { let digest = digest.as_str().unwrap(); - let disclosure = self.sd_jwt_engine.hash_to_decoded_disclosure[digest].as_array().unwrap(); - (disclosure[1].as_str().unwrap(), (&disclosure[2], digest)) + if let Some(Value::Array(disclosure)) = self.sd_jwt_engine.hash_to_decoded_disclosure.get(digest) { + return Some((disclosure[1].as_str().unwrap(), (&disclosure[2], digest))); + } + None }).collect(); //TODO split to 2 maps for (key_to_disclose, value_to_disclose) in claims_to_disclose { match value_to_disclose { - Value::Null | Value::Bool(true) | Value::Number(_) | Value::String(_) => { /* disclose without children */ } - Value::Array(arr_to_disclose) => { - if let Some(arr) = sd_jwt_claims.get(&key_to_disclose).and_then(Value::as_array) { - hash_to_disclosure.append(&mut self.select_disclosures_from_disclosed_list(&arr, &arr_to_disclose)) + Value::Bool(true) | Value::Number(_) | Value::String(_) => { /* disclose without children */ } + Value::Array(claims_to_disclose) => { + if let Some(sd_jwt_claims) = sd_jwt_claims + .get(&key_to_disclose) + .and_then(Value::as_array) + { + hash_to_disclosure.append(&mut self.select_disclosures_from_disclosed_list(&sd_jwt_claims, &claims_to_disclose)) + } else if let Some(sd_jwt_claims) = sd_map + .get(key_to_disclose.as_str()) + .and_then(|(sd, _)| sd.as_array()) + { + hash_to_disclosure.append(&mut self.select_disclosures_from_disclosed_list(&sd_jwt_claims, &claims_to_disclose)) } } - Value::Object(next_disclosure) if (!next_disclosure.is_empty()) => { - let next_sd_jwt_claims = if let Some(next) = sd_jwt_claims.get(&key_to_disclose).and_then(Value::as_object) { + Value::Object(claims_to_disclose) if (!claims_to_disclose.is_empty()) => { + let sd_jwt_claims = if let Some(next) = sd_jwt_claims.get(&key_to_disclose).and_then(Value::as_object) { next } else { sd_map[key_to_disclose.as_str()].0.as_object().unwrap() }; - hash_to_disclosure.append(&mut self.select_disclosures(next_sd_jwt_claims, next_disclosure)); + hash_to_disclosure.append(&mut self.select_disclosures(sd_jwt_claims, claims_to_disclose)); } Value::Object(_) => { /* disclose without children */ } - Value::Bool(false) => { + Value::Bool(false) | Value::Null => { // skip unrevealed continue } @@ -134,15 +143,32 @@ impl SDJWTHolder { fn select_disclosures_from_disclosed_list(&self, sd_jwt_claims: &Vec, claims_to_disclose: &Vec) -> Vec { let mut hash_to_disclosure: Vec = Vec::new(); - for (claim_to_disclose, claim) in claims_to_disclose.iter().zip(sd_jwt_claims) { - match (claim_to_disclose, claim) { - (Value::Bool(true), Value::Object(claim)) => { - if let Some(Value::String(digest)) = claim.get(SD_LIST_PREFIX) { + for (claim_to_disclose, sd_jwt_claims) in claims_to_disclose.iter().zip(sd_jwt_claims) { + match (claim_to_disclose, sd_jwt_claims) { + (Value::Bool(true), Value::Object(sd_jwt_claims)) => { + if let Some(Value::String(digest)) = sd_jwt_claims.get(SD_LIST_PREFIX) { hash_to_disclosure.push(self.sd_jwt_engine.hash_to_disclosure[digest].to_owned()); } } - (Value::Array(new_claims_to_disclose), Value::Array(claim)) => { - self.select_disclosures_from_disclosed_list(claim, new_claims_to_disclose); + (claim_to_disclose, Value::Object(sd_jwt_claims)) => { + if let Some(Value::String(digest)) = sd_jwt_claims.get(SD_LIST_PREFIX) { + let disclosure = self.sd_jwt_engine.hash_to_decoded_disclosure[digest].as_array().unwrap(); + match (claim_to_disclose, disclosure.get(1)) { + (Value::Array(claim_to_disclose), Some(Value::Array(sd_jwt_claims))) => { + hash_to_disclosure.append(&mut self.select_disclosures_from_disclosed_list(&sd_jwt_claims, claim_to_disclose)); + } + (Value::Object(claim_to_disclose), Some(Value::Object(sd_jwt_claims))) => { + hash_to_disclosure.push(self.sd_jwt_engine.hash_to_disclosure[digest].to_owned()); + hash_to_disclosure.append(&mut self.select_disclosures(&sd_jwt_claims, claim_to_disclose.to_owned())); + } + _ => {} + } + } else if let Some(claim_to_disclose) = claim_to_disclose.as_object() { + hash_to_disclosure.append(&mut self.select_disclosures(sd_jwt_claims, claim_to_disclose.to_owned())); + } + } + (Value::Array(claim_to_disclose), Value::Array(sd_jwt_claims)) => { + hash_to_disclosure.append(&mut self.select_disclosures_from_disclosed_list(sd_jwt_claims, claim_to_disclose)); } _ => {} } diff --git a/src/issuer.rs b/src/issuer.rs index be90224..0107c51 100644 --- a/src/issuer.rs +++ b/src/issuer.rs @@ -25,9 +25,9 @@ pub struct SDJWTIssuer { // internal fields inner: SDJWTCommon, all_disclosures: Vec, - sd_jwt_payload: SJMap, - signed_sd_jwt: String, - pub(crate) serialized_sd_jwt: String, + pub sd_jwt_payload: SJMap, + pub signed_sd_jwt: String, + pub serialized_sd_jwt: String, } pub enum SDJWTClaimsStrategy<'a> { @@ -72,21 +72,12 @@ impl<'a> SDJWTClaimsStrategy<'a> { str.strip_prefix(key).as_mut().and_then(|claim| { if let Some(next_claim) = claim.strip_prefix('.') { Some(next_claim) - } else if let Some(next_claim) = claim.strip_prefix('[').and_then(|str| str.strip_suffix(']')) { - Some(next_claim) } else { + // FIXME Replace to non-leackable impl // Removes "[", "]" symbols form "index" and returns "next_claim" as "index.remained_claims.." // For example: [0].street -> 0.street - if let Some(remainder) = claim.strip_prefix('[') { - *claim = remainder; - let remainder: Vec<&str> = claim.splitn(2, ']').collect(); - //FIXME Change to safe impl - *claim = remainder.join("").leak(); - - Some(claim) - } else { - None - } + *claim = claim.replace("[", "").replace("]","").leak(); + Some(claim) } }) }).collect(); diff --git a/src/lib.rs b/src/lib.rs index 5312865..186efe0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,30 +1,27 @@ use std::collections::HashMap; -use std::sync::Mutex; - -use base64::{Engine, engine::general_purpose}; +use base64::{engine::general_purpose, Engine}; use lazy_static::lazy_static; use rand::prelude::ThreadRng; use rand::RngCore; use serde_json::{Map, Value}; use sha2::Digest; - +use std::sync::Mutex; pub use {holder::SDJWTHolder, issuer::SDJWTIssuer, verifier::SDJWTVerifier}; - -mod issuer; -mod verifier; -mod holder; mod disclosure; +pub mod holder; +pub mod issuer; +pub mod verifier; -const DEFAULT_SIGNING_ALG: &str = "ES256"; +pub const DEFAULT_SIGNING_ALG: &str = "ES256"; const SD_DIGESTS_KEY: &str = "_sd"; const DIGEST_ALG_KEY: &str = "_sd_alg"; -const DEFAULT_DIGEST_ALG: &str = "sha-256"; +pub const DEFAULT_DIGEST_ALG: &str = "sha-256"; const SD_LIST_PREFIX: &str = "..."; const _SD_JWT_TYP_HEADER: &str = "sd+jwt"; const KB_JWT_TYP_HEADER: &str = "kb+jwt"; const JWS_KEY_DISCLOSURES: &str = "disclosures"; const JWS_KEY_KB_JWT: &str = "kb_jwt"; -const COMBINED_SERIALIZATION_FORMAT_SEPARATOR: &str = "~"; +pub const COMBINED_SERIALIZATION_FORMAT_SEPARATOR: &str = "~"; const JWT_SEPARATOR: &str = "."; #[derive(Debug)] diff --git a/src/verifier.rs b/src/verifier.rs index 26d64ae..1282d62 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -17,7 +17,7 @@ pub struct SDJWTVerifier { sd_jwt_payload: Map, _holder_public_key_payload: Option>, duplicate_hash_check: Vec, - verified_claims: Value, + pub verified_claims: Value, cb_get_issuer_key: Box, } From 52591cb20ae52e165cfd46ee7d2c1e8e5c27b661 Mon Sep 17 00:00:00 2001 From: Abdulbois Date: Wed, 13 Dec 2023 14:58:00 +0500 Subject: [PATCH 2/2] feat: Add e2e demos Signed-off-by: Abdulbois --- tests/demos.rs | 330 ++++++++++++++++++++++++++++++++++++++++ tests/utils/fixtures.rs | 163 ++++++++++++++++++++ tests/utils/mod.rs | 3 + 3 files changed, 496 insertions(+) create mode 100644 tests/demos.rs create mode 100644 tests/utils/fixtures.rs create mode 100644 tests/utils/mod.rs diff --git a/tests/demos.rs b/tests/demos.rs new file mode 100644 index 0000000..ef31d11 --- /dev/null +++ b/tests/demos.rs @@ -0,0 +1,330 @@ +use crate::utils::fixtures::{ + ADDRESS_CLAIMS, ADDRESS_ONLY_STRUCTURED_JSONPATH, ADDRESS_ONLY_STRUCTURED_ONE_OPEN_JSONPATH, + ARRAYED_CLAIMS, ARRAYED_CLAIMS_JSONPATH, COMPLEX_EIDAS_CLAIMS, COMPLEX_EIDAS_JSONPATH, + HOLDER_KEY, ISSUER_KEY, ISSUER_PUBLIC_KEY, NESTED_ARRAY_CLAIMS, NESTED_ARRAY_JSONPATH, + W3C_VC_CLAIMS, W3C_VC_JSONPATH, +}; +use jsonwebtoken::{DecodingKey, EncodingKey}; +use rstest::{fixture, rstest}; +use sd_jwt_rs::issuer::SDJWTClaimsStrategy; +use sd_jwt_rs::{SDJWTHolder, SDJWTIssuer, SDJWTVerifier}; +use sd_jwt_rs::{COMBINED_SERIALIZATION_FORMAT_SEPARATOR, DEFAULT_SIGNING_ALG}; +use serde_json::{json, Map, Value}; +use std::collections::HashSet; + +mod utils; + +#[fixture] +fn issuer_key() -> EncodingKey { + let private_issuer_bytes = ISSUER_KEY.as_bytes(); + EncodingKey::from_ec_pem(private_issuer_bytes).unwrap() +} + +#[allow(unused)] +fn holder_key() -> EncodingKey { + let private_issuer_bytes = HOLDER_KEY.as_bytes(); + EncodingKey::from_ec_pem(private_issuer_bytes).unwrap() +} + +fn _address_claims() -> serde_json::Value { + serde_json::from_str(ADDRESS_CLAIMS).unwrap() +} + +#[fixture] +fn address_flat<'a>() -> ( + serde_json::Value, + SDJWTClaimsStrategy<'a>, + Map, + usize, +) { + let value = _address_claims(); + let number_of_revealed_sds = 1; + ( + value.clone(), + SDJWTClaimsStrategy::Flat, + value.as_object().unwrap().clone(), + number_of_revealed_sds, + ) +} + +#[fixture] +fn address_full_recursive<'a>() -> ( + serde_json::Value, + SDJWTClaimsStrategy<'a>, + Map, + usize, +) { + let value = _address_claims(); + let claims_to_disclose = value.as_object().unwrap().clone(); + let number_of_revealed_sds = 5; + ( + value, + SDJWTClaimsStrategy::Full, + claims_to_disclose, + number_of_revealed_sds, + ) +} + +#[fixture] +fn address_only_structured<'a>() -> ( + serde_json::Value, + SDJWTClaimsStrategy<'a>, + Map, + usize, +) { + let value = _address_claims(); + let mut claims_to_disclose = value.clone(); + claims_to_disclose["address"] = json!({ + "street_address": "Schulstr. 12", + "region": "Sachsen-Anhalt", + "country": "DE" + }); + + let claims_to_disclose = claims_to_disclose.as_object().unwrap().clone(); + let number_of_revealed_sds = 3; + + ( + value.clone(), + SDJWTClaimsStrategy::Partial(ADDRESS_ONLY_STRUCTURED_JSONPATH.to_vec()), + claims_to_disclose, + number_of_revealed_sds, + ) +} + +#[fixture] +fn address_only_structured_one_open<'a>() -> ( + serde_json::Value, + SDJWTClaimsStrategy<'a>, + Map, + usize, +) { + let value = _address_claims(); + let mut claims_to_disclose = value.clone(); + claims_to_disclose["address"] = json!({ + "region": "Sachsen-Anhalt", + "country": "DE" + }); + + let claims_to_disclose = claims_to_disclose.as_object().unwrap().clone(); + let number_of_revealed_sds = 1; + + ( + value, + SDJWTClaimsStrategy::Partial(ADDRESS_ONLY_STRUCTURED_ONE_OPEN_JSONPATH.to_vec()), + claims_to_disclose, + number_of_revealed_sds, + ) +} + +#[fixture] +fn arrayed_claims<'a>() -> ( + serde_json::Value, + SDJWTClaimsStrategy<'a>, + Map, + usize, +) { + let value: serde_json::Value = serde_json::from_str(ARRAYED_CLAIMS).unwrap(); + let mut claims_to_disclose = value.clone(); + claims_to_disclose["addresses"] = json!([true, true]); + claims_to_disclose["nationalities"] = json!([false, true]); + + let claims_to_disclose = claims_to_disclose.as_object().unwrap().clone(); + let number_of_revealed_sds = 1; + + ( + value, + SDJWTClaimsStrategy::Partial(ARRAYED_CLAIMS_JSONPATH.to_vec()), + claims_to_disclose, + number_of_revealed_sds, + ) +} + +#[fixture] +fn nested_array<'a>() -> ( + serde_json::Value, + SDJWTClaimsStrategy<'a>, + Map, + usize, +) { + let value: serde_json::Value = serde_json::from_str(NESTED_ARRAY_CLAIMS).unwrap(); + let mut claims_to_disclose = value.clone(); + claims_to_disclose["nationalities"] = json!([[false, true]]); + + let claims_to_disclose = claims_to_disclose.as_object().unwrap().clone(); + let number_of_revealed_sds = 1; + + ( + value.clone(), + SDJWTClaimsStrategy::Partial(NESTED_ARRAY_JSONPATH.to_vec()), + claims_to_disclose, + number_of_revealed_sds, + ) +} + +#[fixture] +fn complex_eidas<'a>() -> ( + serde_json::Value, + SDJWTClaimsStrategy<'a>, + Map, + usize, +) { + let value: serde_json::Value = serde_json::from_str(COMPLEX_EIDAS_CLAIMS).unwrap(); + let mut claims_to_disclose = value.clone(); + claims_to_disclose["verified_claims"] = json!({ + "verification": { + "trust_framework": "eidas", + "assurance_level": "high", + "evidence": [ + { + "document": { + "type": "idcard", + "issuer": { + "name": "c_d612", + "country": "IT" + } + } + } + ] + }, + "claims": { + "place_of_birth": { + "country": "IT", + "locality": "Firenze" + }, + "nationalities": [ + "IT" + ] + } + }); + + let claims_to_disclose = claims_to_disclose.as_object().unwrap().clone(); + let number_of_revealed_sds = 5; + + ( + value.clone(), + SDJWTClaimsStrategy::Partial(COMPLEX_EIDAS_JSONPATH.to_vec()), + claims_to_disclose, + number_of_revealed_sds, + ) +} + +#[fixture] +fn w3c_vc<'a>() -> ( + serde_json::Value, + SDJWTClaimsStrategy<'a>, + Map, + usize, +) { + let value: serde_json::Value = serde_json::from_str(W3C_VC_CLAIMS).unwrap(); + let mut claims_to_disclose = value.clone(); + claims_to_disclose["credentialSubject"] = json!({ + "email": "johndoe@example.com", + "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 + }); + + let claims_to_disclose = claims_to_disclose.as_object().unwrap().clone(); + let number_of_revealed_sds = 6; + + ( + value.clone(), + SDJWTClaimsStrategy::Partial(W3C_VC_JSONPATH.to_vec()), + claims_to_disclose, + number_of_revealed_sds, + ) +} + +#[allow(unused)] +fn presentation_metadata() -> (Option, Option, Option) { + ( + Some("1234567890".to_owned()), + Some("https://verifier.example.org".to_owned()), + Some(holder_key()), + ) +} + +#[rstest] +#[case(address_flat())] +#[case(address_full_recursive())] +#[case(address_only_structured())] +#[case(address_only_structured_one_open())] +#[case(arrayed_claims())] +#[case(nested_array())] +#[case(complex_eidas())] +#[case(w3c_vc())] +fn demo_positive_cases( + issuer_key: EncodingKey, + #[case] data: ( + serde_json::Value, + SDJWTClaimsStrategy, + Map, + usize, + ), + #[values((None, None, None), /*presentation_metadata()*/)] presentation_metadata: ( + Option, + Option, + Option, + ), + #[values("compact".to_string())] format: String, + #[values(None, Some(DEFAULT_SIGNING_ALG.to_owned()))] sign_algo: Option, + #[values(true, false)] add_decoy: bool, +) { + let (user_claims, strategy, holder_disclosed_claims, number_of_revealed_sds) = data; + let (nonce, aud, holder_key) = presentation_metadata; + // Issuer issues SD-JWT + let sd_jwt = SDJWTIssuer::issue_sd_jwt( + user_claims.clone(), + strategy, + issuer_key, + holder_key.clone(), + sign_algo.clone(), + add_decoy, + format.clone(), + ); + let issued = sd_jwt.serialized_sd_jwt.clone(); + // Holder creates presentation + let holder = SDJWTHolder::new(sd_jwt.serialized_sd_jwt.clone(), format.clone()); + let presentation = holder.create_presentation( + holder_disclosed_claims, + nonce.clone(), + aud.clone(), + holder_key, + sign_algo, + ); + + let mut issued_parts: HashSet<&str> = issued + .split(COMBINED_SERIALIZATION_FORMAT_SEPARATOR) + .collect(); + issued_parts.remove(""); + + let mut revealed_parts: HashSet<&str> = presentation + .split(COMBINED_SERIALIZATION_FORMAT_SEPARATOR) + .collect(); + revealed_parts.remove(""); + + let intersected_parts: HashSet<_> = issued_parts.intersection(&revealed_parts).collect(); + // Compare that number of disclosed parts are equal + assert_eq!(intersected_parts.len(), revealed_parts.len()); + // here `+1` means adding issued jwt part also + assert_eq!(number_of_revealed_sds + 1, revealed_parts.len()); + // Verify presentation + let _verified = SDJWTVerifier::new( + presentation.clone(), + Box::new(|_, _| { + let public_issuer_bytes = ISSUER_PUBLIC_KEY.as_bytes(); + DecodingKey::from_ec_pem(public_issuer_bytes).unwrap() + }), + aud, + nonce, + format, + ) + .unwrap(); +} diff --git a/tests/utils/fixtures.rs b/tests/utils/fixtures.rs new file mode 100644 index 0000000..ee43795 --- /dev/null +++ b/tests/utils/fixtures.rs @@ -0,0 +1,163 @@ +pub const ISSUER_KEY: &str = "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgUr2bNKuBPOrAaxsR\nnbSH6hIhmNTxSGXshDSUD1a1y7ihRANCAARvbx3gzBkyPDz7TQIbjF+ef1IsxUwz\nX1KWpmlVv+421F7+c1sLqGk4HUuoVeN8iOoAcE547pJhUEJyf5Asc6pP\n-----END PRIVATE KEY-----\n"; +pub const ISSUER_PUBLIC_KEY: &str = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEb28d4MwZMjw8+00CG4xfnn9SLMVM\nM19SlqZpVb/uNtRe/nNbC6hpOB1LqFXjfIjqAHBOeO6SYVBCcn+QLHOqTw==\n-----END PUBLIC KEY-----\n"; +pub const HOLDER_KEY: &str = "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg5K5SCos8zf9zRemG\nGUl6yfok+/NiiryNZsvANWMhF+KhRANCAARMIARHX1m+7c4cXiPhbi99JWgcg/Ug\nuKUOWzu8J4Z6Z2cY4llm2TEBh1VilUOIW0iIq7FX7nnAhOreI0/Rdh2U\n-----END PRIVATE KEY-----\n"; +pub const ADDRESS_CLAIMS: &str = r#"{ + "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", + "iss": "https://example.com/issuer", + "iat": 1683000000, + "exp": 1883000000, + "address": { + "street_address": "Schulstr. 12", + "locality": "Schulpforta", + "region": "Sachsen-Anhalt", + "country": "DE" + } +}"#; +pub const ADDRESS_ONLY_STRUCTURED_JSONPATH: [&str; 4] = [ + "$.address.street_address", + "$.address.locality", + "$.address.region", + "$.address.country", +]; +pub const ADDRESS_ONLY_STRUCTURED_ONE_OPEN_JSONPATH: [&str; 3] = [ + "$.address.street_address", + "$.address.locality", + "$.address.region", +]; +pub const ARRAYED_CLAIMS: &str = r#" +{ + "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c", + "iss": "https://example.com/issuer", + "iat": 1683000000, + "exp": 1883000000, + "addresses": [ + { + "street_address": "Schulstr. 12", + "locality": "Schulpforta", + "region": "Sachsen-Anhalt", + "country": "DE" + }, + { + "street_address": "456 Main St", + "locality": "Anytown", + "region": "NY", + "country": "US" + } + ], + "nationalities": [ + "US", + "CA" + ] +}"#; +pub const ARRAYED_CLAIMS_JSONPATH: [&str; 3] = [ + "$.addresses[1]", + "$.addresses[1].country", + "$.nationalities[0]", +]; +pub const NESTED_ARRAY_CLAIMS: &str = r#"{ + "iss": "https://example.com/issuer", + "iat": 1683000000, + "exp": 1883000000, + "nationalities": [ + ["IT", "UZ"], + ["DE", "US"] + ] +}"#; +pub const NESTED_ARRAY_JSONPATH: [&str; 3] = [ + "$.nationalities[0]", + "$.nationalities[0][0]", + "$.nationalities[0][1]", +]; +pub const COMPLEX_EIDAS_CLAIMS: &str = r#"{ + "iss": "https://example.com/issuer", + "iat": 1683000000, + "exp": 1883000000, + "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" +}"#; +pub const COMPLEX_EIDAS_JSONPATH: [&str; 7] = [ + "$.verified_claims.verification.evidence[0].document.issuer", + "$.verified_claims.verification.evidence[0].document", + "$.verified_claims.verification.evidence", + "$.verified_claims.claims.date_of_birth", + "$.verified_claims.claims.gender", + "$.verified_claims.claims.place_of_birth", + "$.verified_claims.claims.nationalities", +]; +pub const W3C_VC_CLAIMS: &str = r#"{ + "iss": "https://example.com", + "jti": "http://example.com/credentials/3732", + "iat": 1683000000, + "exp": 1883000000, + "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 + } +}"#; +pub const W3C_VC_JSONPATH: [&str; 9] = [ + "$.credentialSubject.given_name", + "$.credentialSubject.family_name", + "$.credentialSubject.email", + "$.credentialSubject.phone_number", + "$.credentialSubject.address", + "$.credentialSubject.birthdate", + "$.credentialSubject.is_over_18", + "$.credentialSubject.is_over_21", + "$.credentialSubject.is_over_65" +]; \ No newline at end of file diff --git a/tests/utils/mod.rs b/tests/utils/mod.rs new file mode 100644 index 0000000..0559679 --- /dev/null +++ b/tests/utils/mod.rs @@ -0,0 +1,3 @@ +#![allow(unused)] + +pub mod fixtures;