From 7782a4661a5d1fc90df340620d0539954cd6d8f3 Mon Sep 17 00:00:00 2001 From: Alexander Sukhachev Date: Fri, 19 Jan 2024 14:50:49 +0500 Subject: [PATCH 1/7] removed the 'leak' function call Signed-off-by: Alexander Sukhachev --- src/issuer.rs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/issuer.rs b/src/issuer.rs index 97a0b6d..3fc1279 100644 --- a/src/issuer.rs +++ b/src/issuer.rs @@ -80,17 +80,7 @@ impl<'a> SDJWTClaimsStrategy<'a> { let next_sd_keys = sd_keys .iter() .filter_map(|str| { - str.strip_prefix(key).as_mut().map(|claim| { - if let Some(next_claim) = claim.strip_prefix('.') { - 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 - *claim = claim.replace(['[', ']'], "").leak(); - claim - } - }) + str.strip_prefix(key).and_then(|str| str.strip_prefix('.').or(Some(&str))) }) .collect(); Self::Partial(next_sd_keys) @@ -232,7 +222,7 @@ impl SDJWTIssuer { fn create_sd_claims_list(&mut self, list: &[Value], sd_strategy: SDJWTClaimsStrategy) -> Value { let mut claims = Vec::new(); for (idx, object) in list.iter().enumerate() { - let key = idx.to_string(); + let key = format!("[{idx}]"); let strategy_for_child = sd_strategy.next_level(&key); let subtree = self.create_sd_claims(object, strategy_for_child); From 677aad8933a787054f6ac420aee85fd840b68694 Mon Sep 17 00:00:00 2001 From: Alexander Sukhachev Date: Fri, 19 Jan 2024 17:47:24 +0500 Subject: [PATCH 2/7] added unit tests for the SDJWTClaimsStrategy::next_level method Signed-off-by: Alexander Sukhachev --- src/issuer.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/issuer.rs b/src/issuer.rs index 3fc1279..4e04557 100644 --- a/src/issuer.rs +++ b/src/issuer.rs @@ -37,6 +37,7 @@ pub struct SDJWTIssuer { } /// SDJWTClaimsStrategy is used to determine which claims can be selectively disclosed later by the holder. +#[derive(PartialEq, Debug)] pub enum SDJWTClaimsStrategy<'a> { /// No claims can be selectively disclosed. Full disclosure. No, @@ -386,4 +387,35 @@ mod tests { .unwrap(); trace!("{:?}", sd_jwt) } + + #[test] + fn test_next_level_array() { + let strategy = SDJWTClaimsStrategy::Partial(vec![ + "name", + "addresses[1]", + "addresses[1].country", + "nationalities[0]", + ]); + + let next_strategy = strategy.next_level("addresses").next_level("[1]"); + assert_eq!(&next_strategy, &SDJWTClaimsStrategy::Partial(vec!["", "country"])); + } + + #[test] + fn test_next_level_object() { + let strategy = SDJWTClaimsStrategy::Partial(vec![ + "address.street_address", + "address.locality", + "address.region", + "address.country", + ]); + + let next_strategy = strategy.next_level("address"); + assert_eq!(&next_strategy, &SDJWTClaimsStrategy::Partial(vec![ + "street_address", + "locality", + "region", + "country" + ])); + } } From f501644ab7ef890f5cbf2e8780461d2cd9f0afa9 Mon Sep 17 00:00:00 2001 From: Alexander Sukhachev Date: Fri, 19 Jan 2024 20:02:51 +0500 Subject: [PATCH 3/7] added the SDJWTSerializationFormat enum Signed-off-by: Alexander Sukhachev --- src/holder.rs | 32 ++++++++++++-------------------- src/issuer.rs | 15 ++++++++------- src/lib.rs | 17 ++++++++++++++--- src/verifier.rs | 25 +++++++++++++------------ tests/demos.rs | 6 +++--- 5 files changed, 50 insertions(+), 45 deletions(-) diff --git a/src/holder.rs b/src/holder.rs index 79639fd..d198463 100644 --- a/src/holder.rs +++ b/src/holder.rs @@ -1,4 +1,4 @@ -use crate::{error, SDJWTJson}; +use crate::{error, SDJWTJson, SDJWTSerializationFormat}; use error::{Error, Result}; use jsonwebtoken::{Algorithm, EncodingKey, Header}; use serde_json::{Map, Value}; @@ -29,7 +29,7 @@ impl SDJWTHolder { /// /// # Arguments /// * `sd_jwt_with_disclosures` - SD JWT with disclosures in the format specified by `serialization_format` - /// * `serialization_format` - Serialization format of the SD JWT. Supported values are `compact` and `json` + /// * `serialization_format` - Serialization format of the SD JWT. /// /// # Returns /// * `SDJWTHolder` - Instance of SDJWTHolder @@ -38,18 +38,10 @@ impl SDJWTHolder { /// * `InvalidInput` - If the serialization format is not supported /// * `InvalidState` - If the SD JWT data is not valid /// * `DeserializationError` - If the SD JWT serialization is not valid - pub fn new(sd_jwt_with_disclosures: String, serialization_format: String) -> Result { - let serialization_format = serialization_format.to_lowercase(); - if serialization_format != "compact" && serialization_format != "json" { - return Err(Error::InvalidInput(format!( - "Serialization format \"{}\" is not supported", - serialization_format - ))); - } - + pub fn new(sd_jwt_with_disclosures: String, serialization_format: SDJWTSerializationFormat) -> Result { let mut holder = SDJWTHolder { sd_jwt_engine: SDJWTCommon { - serialization_format: serialization_format.clone(), + serialization_format: serialization_format, ..Default::default() }, hs_disclosures: Vec::new(), @@ -119,7 +111,7 @@ impl SDJWTHolder { } } - let sd_jwt_presentation = if self.sd_jwt_engine.serialization_format == "compact" { + let sd_jwt_presentation = if self.sd_jwt_engine.serialization_format == SDJWTSerializationFormat::Compact { let mut combined: Vec<&str> = Vec::with_capacity(self.hs_disclosures.len() + 2); combined.push(&self.serialized_sd_jwt); combined.extend(self.hs_disclosures.iter().map(|s| s.as_str())); @@ -350,7 +342,7 @@ impl SDJWTHolder { #[cfg(test)] mod tests { use crate::issuer::SDJWTClaimsStrategy; - use crate::{SDJWTHolder, SDJWTIssuer, COMBINED_SERIALIZATION_FORMAT_SEPARATOR}; + use crate::{SDJWTHolder, SDJWTIssuer, COMBINED_SERIALIZATION_FORMAT_SEPARATOR, SDJWTSerializationFormat}; use jsonwebtoken::EncodingKey; use serde_json::{json, Map, Value}; use std::collections::HashSet; @@ -378,12 +370,12 @@ mod tests { SDJWTClaimsStrategy::Full, None, false, - "compact".to_owned(), + SDJWTSerializationFormat::Compact, ) .unwrap(); let presentation = SDJWTHolder::new( sd_jwt.clone(), - "compact".to_ascii_lowercase(), + SDJWTSerializationFormat::Compact, ) .unwrap() .create_presentation( @@ -418,13 +410,13 @@ mod tests { SDJWTClaimsStrategy::Full, None, false, - "compact".to_owned(), + SDJWTSerializationFormat::Compact, ) .unwrap(); let issued = sd_jwt.clone(); user_claims["address"] = Value::Object(Map::new()); let presentation = - SDJWTHolder::new(sd_jwt, "compact".to_ascii_lowercase()) + SDJWTHolder::new(sd_jwt, SDJWTSerializationFormat::Compact) .unwrap() .create_presentation( user_claims.as_object().unwrap().clone(), @@ -486,7 +478,7 @@ mod tests { strategy, None, false, - "compact".to_owned(), + SDJWTSerializationFormat::Compact, ) .unwrap(); // Choose what to reveal @@ -496,7 +488,7 @@ mod tests { let issued = sd_jwt.clone(); println!("{}", issued); let presentation = - SDJWTHolder::new(sd_jwt, "compact".to_ascii_lowercase()) + SDJWTHolder::new(sd_jwt, SDJWTSerializationFormat::Compact) .unwrap() .create_presentation( user_claims.as_object().unwrap().clone(), diff --git a/src/issuer.rs b/src/issuer.rs index 4e04557..38a5d8a 100644 --- a/src/issuer.rs +++ b/src/issuer.rs @@ -16,6 +16,7 @@ use crate::utils::{base64_hash, generate_salt}; use crate::{ SDJWTCommon, CNF_KEY, COMBINED_SERIALIZATION_FORMAT_SEPARATOR, DEFAULT_DIGEST_ALG, DEFAULT_SIGNING_ALG, DIGEST_ALG_KEY, JWK_KEY, SD_DIGESTS_KEY, SD_LIST_PREFIX, + SDJWTSerializationFormat, }; pub struct SDJWTIssuer { @@ -143,7 +144,7 @@ impl SDJWTIssuer { /// * `sd_strategy` - The strategy to be used to determine which claims to be selectively disclosed. See [SDJWTClaimsStrategy] for more details. /// * `holder_key` - The key used to sign the SD-JWT. If not provided, no key binding is added to the SD-JWT. /// * `add_decoy_claims` - If true, decoy claims are added to the SD-JWT. - /// * `serialization_format` - The serialization format to be used for the SD-JWT. Only "compact" and "json" formats are supported. + /// * `serialization_format` - The serialization format to be used for the SD-JWT. /// /// # Returns /// The issued SD-JWT as a string in the requested serialization format. @@ -153,7 +154,7 @@ impl SDJWTIssuer { mut sd_strategy: SDJWTClaimsStrategy, holder_key: Option, add_decoy_claims: bool, - serialization_format: String, + serialization_format: SDJWTSerializationFormat, // extra_header_parameters: Option>, ) -> Result { let inner = SDJWTCommon { @@ -299,7 +300,7 @@ impl SDJWTIssuer { } fn create_combined(&mut self) -> Result<()> { - if self.inner.serialization_format == "compact" { + if self.inner.serialization_format == SDJWTSerializationFormat::Compact { let mut disclosures: VecDeque = self .all_disclosures .iter() @@ -314,7 +315,7 @@ impl SDJWTIssuer { disclosures.join(COMBINED_SERIALIZATION_FORMAT_SEPARATOR), COMBINED_SERIALIZATION_FORMAT_SEPARATOR, ); - } else if self.inner.serialization_format == "json" { + } else if self.inner.serialization_format == SDJWTSerializationFormat::JSON { let jwt: Vec<&str> = self.signed_sd_jwt.split('.').collect(); if jwt.len() != 3 { return Err(Error::InvalidInput(format!( @@ -337,7 +338,7 @@ impl SDJWTIssuer { .map_err(|e| Error::DeserializationError(e.to_string()))?; } else { return Err(Error::InvalidInput( - format!("Unknown serialization format {}, only \"compact\" or \"json\" formats are supported", self.inner.serialization_format) + format!("Unknown serialization format {}, only \"Compact\" or \"JSON\" formats are supported", self.inner.serialization_format) )); } @@ -357,7 +358,7 @@ mod tests { use serde_json::json; use crate::issuer::SDJWTClaimsStrategy; - use crate::SDJWTIssuer; + use crate::{SDJWTIssuer, SDJWTSerializationFormat}; const PRIVATE_ISSUER_PEM: &str = "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgUr2bNKuBPOrAaxsR\nnbSH6hIhmNTxSGXshDSUD1a1y7ihRANCAARvbx3gzBkyPDz7TQIbjF+ef1IsxUwz\nX1KWpmlVv+421F7+c1sLqGk4HUuoVeN8iOoAcE547pJhUEJyf5Asc6pP\n-----END PRIVATE KEY-----\n"; @@ -382,7 +383,7 @@ mod tests { SDJWTClaimsStrategy::Full, None, false, - "compact".to_owned(), + SDJWTSerializationFormat::Compact, ) .unwrap(); trace!("{:?}", sd_jwt) diff --git a/src/lib.rs b/src/lib.rs index 4c30cbb..6ef7b36 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ use crate::utils::{base64_hash, base64url_decode, jwt_payload_decode}; use error::Result; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; +use strum::Display; use std::collections::HashMap; pub use {holder::SDJWTHolder, issuer::SDJWTIssuer, issuer::SDJWTClaimsStrategy, verifier::SDJWTVerifier}; @@ -32,10 +33,20 @@ pub(crate) struct SDJWTHasSDClaimException(String); impl SDJWTHasSDClaimException {} +/// SDJWTSerializationFormat is used to determine how an SD-JWT is serialized to String +#[derive(Default, Clone, PartialEq, Debug, Display)] +pub enum SDJWTSerializationFormat { + /// JSON-encoded representation + #[default] + JSON, + /// Base64-encoded representation + Compact, +} + #[derive(Default)] pub(crate) struct SDJWTCommon { typ: Option, - serialization_format: String, + serialization_format: SDJWTSerializationFormat, unverified_input_key_binding_jwt: Option, unverified_sd_jwt: Option, unverified_sd_jwt_json: Option, @@ -114,7 +125,7 @@ impl SDJWTCommon { } fn parse_sd_jwt(&mut self, sd_jwt_with_disclosures: String) -> Result<()> { - if self.get_serialization_format() == "compact" { + if self.get_serialization_format() == &SDJWTSerializationFormat::Compact { let parts: Vec<&str> = sd_jwt_with_disclosures .split(COMBINED_SERIALIZATION_FORMAT_SEPARATOR) .collect(); @@ -177,7 +188,7 @@ impl SDJWTCommon { } } - fn get_serialization_format(&self) -> &str { + fn get_serialization_format(&self) -> &SDJWTSerializationFormat { &self.serialization_format } } diff --git a/src/verifier.rs b/src/verifier.rs index 876f941..fcd0e8d 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -1,3 +1,4 @@ +use crate::SDJWTSerializationFormat; use crate::error::Error; use crate::error::Result; use jsonwebtoken::jwk::Jwk; @@ -45,7 +46,7 @@ impl SDJWTVerifier { cb_get_issuer_key: Box, expected_aud: Option, expected_nonce: Option, - serialization_format: String, + serialization_format: SDJWTSerializationFormat, ) -> Result { let mut verifier = SDJWTVerifier { sd_jwt_payload: serde_json::Map::new(), @@ -180,7 +181,7 @@ impl SDJWTVerifier { if key_binding_jwt.claims.get("nonce") != Some(&Value::String(expected_nonce)) { return Err(Error::InvalidInput("Invalid nonce".to_string())); } - if self.sd_jwt_engine.serialization_format == "compact" { + if self.sd_jwt_engine.serialization_format == SDJWTSerializationFormat::Compact { let sd_hash = self._get_key_binding_digest_hash()?; if key_binding_jwt.claims.get(KB_DIGEST_KEY) != Some(&Value::String(sd_hash)) { return Err(Error::InvalidInput("Invalid digest in KB-JWT".to_string())); @@ -309,7 +310,7 @@ impl SDJWTVerifier { #[cfg(test)] mod tests { use crate::issuer::SDJWTClaimsStrategy; - use crate::{SDJWTHolder, SDJWTIssuer, SDJWTVerifier}; + use crate::{SDJWTHolder, SDJWTIssuer, SDJWTVerifier, SDJWTSerializationFormat}; use jsonwebtoken::{DecodingKey, EncodingKey}; use serde_json::{json, Value}; @@ -337,10 +338,10 @@ mod tests { SDJWTClaimsStrategy::Full, None, false, - "compact".to_owned(), + SDJWTSerializationFormat::Compact, ) .unwrap(); - let presentation = SDJWTHolder::new(sd_jwt.clone(), "compact".to_owned()) + let presentation = SDJWTHolder::new(sd_jwt.clone(), SDJWTSerializationFormat::Compact) .unwrap() .create_presentation( user_claims.as_object().unwrap().clone(), @@ -359,7 +360,7 @@ mod tests { }), None, None, - "compact".to_owned(), + SDJWTSerializationFormat::Compact, ) .unwrap() .verified_claims; @@ -387,11 +388,11 @@ mod tests { SDJWTClaimsStrategy::No, None, false, - "compact".to_owned(), + SDJWTSerializationFormat::Compact, ) .unwrap(); - let presentation = SDJWTHolder::new(sd_jwt.clone(), "compact".to_owned()) + let presentation = SDJWTHolder::new(sd_jwt.clone(), SDJWTSerializationFormat::Compact) .unwrap() .create_presentation( user_claims.as_object().unwrap().clone(), @@ -410,7 +411,7 @@ mod tests { }), None, None, - "compact".to_owned(), + SDJWTSerializationFormat::Compact, ) .unwrap() .verified_claims; @@ -459,7 +460,7 @@ mod tests { strategy, None, false, - "compact".to_owned(), + SDJWTSerializationFormat::Compact, ) .unwrap(); @@ -467,7 +468,7 @@ mod tests { claims_to_disclose["addresses"] = Value::Array(vec![Value::Bool(true), Value::Bool(true)]); claims_to_disclose["nationalities"] = Value::Array(vec![Value::Bool(true), Value::Bool(true)]); - let presentation = SDJWTHolder::new(sd_jwt, "compact".to_owned()) + let presentation = SDJWTHolder::new(sd_jwt, SDJWTSerializationFormat::Compact) .unwrap() .create_presentation( claims_to_disclose.as_object().unwrap().clone(), @@ -486,7 +487,7 @@ mod tests { }), None, None, - "compact".to_owned(), + SDJWTSerializationFormat::Compact, ) .unwrap() .verified_claims; diff --git a/tests/demos.rs b/tests/demos.rs index 662563b..8f7df33 100644 --- a/tests/demos.rs +++ b/tests/demos.rs @@ -8,7 +8,7 @@ use jsonwebtoken::jwk::Jwk; use jsonwebtoken::{DecodingKey, EncodingKey}; use rstest::{fixture, rstest}; use sd_jwt_rs::issuer::SDJWTClaimsStrategy; -use sd_jwt_rs::{SDJWTHolder, SDJWTIssuer, SDJWTJson, SDJWTVerifier}; +use sd_jwt_rs::{SDJWTHolder, SDJWTIssuer, SDJWTJson, SDJWTVerifier, SDJWTSerializationFormat}; use sd_jwt_rs::{COMBINED_SERIALIZATION_FORMAT_SEPARATOR, DEFAULT_SIGNING_ALG}; use serde_json::{json, Map, Value}; use std::collections::HashSet; @@ -287,7 +287,7 @@ fn demo_positive_cases( Option, Option, ), - #[values("compact".to_string(), "json".to_string())] format: String, + #[values(SDJWTSerializationFormat::Compact, SDJWTSerializationFormat::JSON)] format: SDJWTSerializationFormat, #[values(None, Some(DEFAULT_SIGNING_ALG.to_owned()))] sign_algo: Option, #[values(true, false)] add_decoy: bool, ) { @@ -315,7 +315,7 @@ fn demo_positive_cases( ) .unwrap(); - if format == "compact" { + if format == SDJWTSerializationFormat::Compact { let mut issued_parts: HashSet<&str> = issued .split(COMBINED_SERIALIZATION_FORMAT_SEPARATOR) .collect(); From e9ae44f3a2121337d72d50ca6e2354583654ae0f Mon Sep 17 00:00:00 2001 From: Alexander Sukhachev Date: Fri, 19 Jan 2024 20:24:01 +0500 Subject: [PATCH 4/7] renamed SDJWTClaimsStrategy -> ClaimsForSelectiveDisclosureStrategy Signed-off-by: Alexander Sukhachev --- README.md | 2 +- src/holder.rs | 8 +++---- src/issuer.rs | 62 ++++++++++++++++++++++++------------------------- src/lib.rs | 2 +- src/verifier.rs | 8 +++---- tests/demos.rs | 36 ++++++++++++++-------------- 6 files changed, 59 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index dafee63..0ace5eb 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Proposals about API improvements are highly appreciated. ```rust fn demo() { let mut issuer = SDJWTIssuer::new(issuer_key, None); - let sd_jwt = issuer.issue_sd_jwt(claims, SDJWTClaimsStrategy::Full, holder_key, add_decoy, "compact".to_owned()).unwrap(); + let sd_jwt = issuer.issue_sd_jwt(claims, ClaimsForSelectiveDisclosureStrategy::AllLevels, holder_key, add_decoy, "compact".to_owned()).unwrap(); let mut holder = SDJWTHolder::new(sd_jwt, "compact".to_owned()).unwrap(); let presentation = holder.create_presentation(claims_to_disclosure, None, None, None, None).unwrap(); diff --git a/src/holder.rs b/src/holder.rs index d198463..3141382 100644 --- a/src/holder.rs +++ b/src/holder.rs @@ -341,7 +341,7 @@ impl SDJWTHolder { #[cfg(test)] mod tests { - use crate::issuer::SDJWTClaimsStrategy; + use crate::issuer::ClaimsForSelectiveDisclosureStrategy; use crate::{SDJWTHolder, SDJWTIssuer, COMBINED_SERIALIZATION_FORMAT_SEPARATOR, SDJWTSerializationFormat}; use jsonwebtoken::EncodingKey; use serde_json::{json, Map, Value}; @@ -367,7 +367,7 @@ mod tests { let issuer_key = EncodingKey::from_ec_pem(private_issuer_bytes).unwrap(); let sd_jwt = SDJWTIssuer::new(issuer_key, None).issue_sd_jwt( user_claims.clone(), - SDJWTClaimsStrategy::Full, + ClaimsForSelectiveDisclosureStrategy::AllLevels, None, false, SDJWTSerializationFormat::Compact, @@ -407,7 +407,7 @@ mod tests { let sd_jwt = SDJWTIssuer::new(issuer_key, None).issue_sd_jwt( user_claims.clone(), - SDJWTClaimsStrategy::Full, + ClaimsForSelectiveDisclosureStrategy::AllLevels, None, false, SDJWTSerializationFormat::Compact, @@ -464,7 +464,7 @@ mod tests { ] } ); - let strategy = SDJWTClaimsStrategy::Partial(vec![ + let strategy = ClaimsForSelectiveDisclosureStrategy::Custom(vec![ "$.name", "$.addresses[1]", "$.addresses[1].country", diff --git a/src/issuer.rs b/src/issuer.rs index 38a5d8a..fbba36c 100644 --- a/src/issuer.rs +++ b/src/issuer.rs @@ -37,29 +37,29 @@ pub struct SDJWTIssuer { pub serialized_sd_jwt: String, } -/// SDJWTClaimsStrategy is used to determine which claims can be selectively disclosed later by the holder. +/// ClaimsForSelectiveDisclosureStrategy is used to determine which claims can be selectively disclosed later by the holder. #[derive(PartialEq, Debug)] -pub enum SDJWTClaimsStrategy<'a> { +pub enum ClaimsForSelectiveDisclosureStrategy<'a> { /// No claims can be selectively disclosed. Full disclosure. - No, + NoSDClaims, /// Top-level claims can be selectively disclosed, nested objects are fully disclosed. - Flat, + TopLevel, /// All claims can be selectively disclosed (recursively including nested objects). - Full, + AllLevels, /// Claims can be selectively disclosed based on the provided JSONPaths. Other claims are fully disclosed. /// # Examples /// ``` - /// use sd_jwt_rs::issuer::SDJWTClaimsStrategy; + /// use sd_jwt_rs::issuer::ClaimsForSelectiveDisclosureStrategy; /// - /// let strategy = SDJWTClaimsStrategy::Partial(vec!["$.address", "$.address.street_address"]); + /// let strategy = ClaimsForSelectiveDisclosureStrategy::Custom(vec!["$.address", "$.address.street_address"]); /// ``` - Partial(Vec<&'a str>), + Custom(Vec<&'a str>), } -impl<'a> SDJWTClaimsStrategy<'a> { +impl<'a> ClaimsForSelectiveDisclosureStrategy<'a> { fn finalize_input(&mut self) -> Result<()> { match self { - SDJWTClaimsStrategy::Partial(keys) => { + ClaimsForSelectiveDisclosureStrategy::Custom(keys) => { for key in keys.iter_mut() { if let Some(new_key) = key.strip_prefix("$.") { *key = new_key; @@ -75,27 +75,27 @@ impl<'a> SDJWTClaimsStrategy<'a> { fn next_level(&self, key: &str) -> Self { match self { - Self::No => Self::No, - Self::Flat => Self::No, - Self::Full => Self::Full, - Self::Partial(sd_keys) => { + Self::NoSDClaims => Self::NoSDClaims, + Self::TopLevel => Self::NoSDClaims, + Self::AllLevels => Self::AllLevels, + Self::Custom(sd_keys) => { let next_sd_keys = sd_keys .iter() .filter_map(|str| { str.strip_prefix(key).and_then(|str| str.strip_prefix('.').or(Some(&str))) }) .collect(); - Self::Partial(next_sd_keys) + Self::Custom(next_sd_keys) } } } fn sd_for_key(&self, key: &str) -> bool { match self { - Self::No => false, - Self::Flat => true, - Self::Full => true, - Self::Partial(sd_keys) => sd_keys.contains(&key), + Self::NoSDClaims => false, + Self::TopLevel => true, + Self::AllLevels => true, + Self::Custom(sd_keys) => sd_keys.contains(&key), } } } @@ -141,7 +141,7 @@ impl SDJWTIssuer { /// /// # Arguments /// * `user_claims` - The claims to be included in the SD-JWT. - /// * `sd_strategy` - The strategy to be used to determine which claims to be selectively disclosed. See [SDJWTClaimsStrategy] for more details. + /// * `sd_strategy` - The strategy to be used to determine which claims to be selectively disclosed. See [ClaimsForSelectiveDisclosureStrategy] for more details. /// * `holder_key` - The key used to sign the SD-JWT. If not provided, no key binding is added to the SD-JWT. /// * `add_decoy_claims` - If true, decoy claims are added to the SD-JWT. /// * `serialization_format` - The serialization format to be used for the SD-JWT. @@ -151,7 +151,7 @@ impl SDJWTIssuer { pub fn issue_sd_jwt( &mut self, user_claims: Value, - mut sd_strategy: SDJWTClaimsStrategy, + mut sd_strategy: ClaimsForSelectiveDisclosureStrategy, holder_key: Option, add_decoy_claims: bool, serialization_format: SDJWTSerializationFormat, @@ -181,7 +181,7 @@ impl SDJWTIssuer { fn assemble_sd_jwt_payload( &mut self, mut user_claims: Value, - sd_strategy: SDJWTClaimsStrategy, + sd_strategy: ClaimsForSelectiveDisclosureStrategy, ) -> Result<()> { let claims_obj_ref = user_claims .as_object_mut() @@ -213,7 +213,7 @@ impl SDJWTIssuer { Ok(()) } - fn create_sd_claims(&mut self, user_claims: &Value, sd_strategy: SDJWTClaimsStrategy) -> Value { + fn create_sd_claims(&mut self, user_claims: &Value, sd_strategy: ClaimsForSelectiveDisclosureStrategy) -> Value { match user_claims { Value::Array(list) => self.create_sd_claims_list(list, sd_strategy), Value::Object(object) => self.create_sd_claims_object(object, sd_strategy), @@ -221,7 +221,7 @@ impl SDJWTIssuer { } } - fn create_sd_claims_list(&mut self, list: &[Value], sd_strategy: SDJWTClaimsStrategy) -> Value { + fn create_sd_claims_list(&mut self, list: &[Value], sd_strategy: ClaimsForSelectiveDisclosureStrategy) -> Value { let mut claims = Vec::new(); for (idx, object) in list.iter().enumerate() { let key = format!("[{idx}]"); @@ -242,7 +242,7 @@ impl SDJWTIssuer { fn create_sd_claims_object( &mut self, user_claims: &SJMap, - sd_strategy: SDJWTClaimsStrategy, + sd_strategy: ClaimsForSelectiveDisclosureStrategy, ) -> Value { let mut claims = SJMap::new(); let mut sd_claims = Vec::new(); @@ -357,7 +357,7 @@ mod tests { use log::trace; use serde_json::json; - use crate::issuer::SDJWTClaimsStrategy; + use crate::issuer::ClaimsForSelectiveDisclosureStrategy; use crate::{SDJWTIssuer, SDJWTSerializationFormat}; const PRIVATE_ISSUER_PEM: &str = "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgUr2bNKuBPOrAaxsR\nnbSH6hIhmNTxSGXshDSUD1a1y7ihRANCAARvbx3gzBkyPDz7TQIbjF+ef1IsxUwz\nX1KWpmlVv+421F7+c1sLqGk4HUuoVeN8iOoAcE547pJhUEJyf5Asc6pP\n-----END PRIVATE KEY-----\n"; @@ -380,7 +380,7 @@ mod tests { let issuer_key = EncodingKey::from_ec_pem(private_issuer_bytes).unwrap(); let sd_jwt = SDJWTIssuer::new(issuer_key, None).issue_sd_jwt( user_claims, - SDJWTClaimsStrategy::Full, + ClaimsForSelectiveDisclosureStrategy::AllLevels, None, false, SDJWTSerializationFormat::Compact, @@ -391,7 +391,7 @@ mod tests { #[test] fn test_next_level_array() { - let strategy = SDJWTClaimsStrategy::Partial(vec![ + let strategy = ClaimsForSelectiveDisclosureStrategy::Custom(vec![ "name", "addresses[1]", "addresses[1].country", @@ -399,12 +399,12 @@ mod tests { ]); let next_strategy = strategy.next_level("addresses").next_level("[1]"); - assert_eq!(&next_strategy, &SDJWTClaimsStrategy::Partial(vec!["", "country"])); + assert_eq!(&next_strategy, &ClaimsForSelectiveDisclosureStrategy::Custom(vec!["", "country"])); } #[test] fn test_next_level_object() { - let strategy = SDJWTClaimsStrategy::Partial(vec![ + let strategy = ClaimsForSelectiveDisclosureStrategy::Custom(vec![ "address.street_address", "address.locality", "address.region", @@ -412,7 +412,7 @@ mod tests { ]); let next_strategy = strategy.next_level("address"); - assert_eq!(&next_strategy, &SDJWTClaimsStrategy::Partial(vec![ + assert_eq!(&next_strategy, &ClaimsForSelectiveDisclosureStrategy::Custom(vec![ "street_address", "locality", "region", diff --git a/src/lib.rs b/src/lib.rs index 6ef7b36..a7fe3da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; use strum::Display; use std::collections::HashMap; -pub use {holder::SDJWTHolder, issuer::SDJWTIssuer, issuer::SDJWTClaimsStrategy, verifier::SDJWTVerifier}; +pub use {holder::SDJWTHolder, issuer::SDJWTIssuer, issuer::ClaimsForSelectiveDisclosureStrategy, verifier::SDJWTVerifier}; mod disclosure; pub mod error; diff --git a/src/verifier.rs b/src/verifier.rs index fcd0e8d..0128a66 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -309,7 +309,7 @@ impl SDJWTVerifier { #[cfg(test)] mod tests { - use crate::issuer::SDJWTClaimsStrategy; + use crate::issuer::ClaimsForSelectiveDisclosureStrategy; use crate::{SDJWTHolder, SDJWTIssuer, SDJWTVerifier, SDJWTSerializationFormat}; use jsonwebtoken::{DecodingKey, EncodingKey}; use serde_json::{json, Value}; @@ -335,7 +335,7 @@ mod tests { let issuer_key = EncodingKey::from_ec_pem(private_issuer_bytes).unwrap(); let sd_jwt = SDJWTIssuer::new(issuer_key, None).issue_sd_jwt( user_claims.clone(), - SDJWTClaimsStrategy::Full, + ClaimsForSelectiveDisclosureStrategy::AllLevels, None, false, SDJWTSerializationFormat::Compact, @@ -385,7 +385,7 @@ mod tests { let issuer_key = EncodingKey::from_ec_pem(private_issuer_bytes).unwrap(); let sd_jwt = SDJWTIssuer::new(issuer_key, None).issue_sd_jwt( user_claims.clone(), - SDJWTClaimsStrategy::No, + ClaimsForSelectiveDisclosureStrategy::NoSDClaims, None, false, SDJWTSerializationFormat::Compact, @@ -449,7 +449,7 @@ mod tests { ); let private_issuer_bytes = PRIVATE_ISSUER_PEM.as_bytes(); let issuer_key = EncodingKey::from_ec_pem(private_issuer_bytes).unwrap(); - let strategy = SDJWTClaimsStrategy::Partial(vec![ + let strategy = ClaimsForSelectiveDisclosureStrategy::Custom(vec![ "$.name", "$.addresses[1]", "$.addresses[1].country", diff --git a/tests/demos.rs b/tests/demos.rs index 8f7df33..f9dfb39 100644 --- a/tests/demos.rs +++ b/tests/demos.rs @@ -7,7 +7,7 @@ use crate::utils::fixtures::{ use jsonwebtoken::jwk::Jwk; use jsonwebtoken::{DecodingKey, EncodingKey}; use rstest::{fixture, rstest}; -use sd_jwt_rs::issuer::SDJWTClaimsStrategy; +use sd_jwt_rs::issuer::ClaimsForSelectiveDisclosureStrategy; use sd_jwt_rs::{SDJWTHolder, SDJWTIssuer, SDJWTJson, SDJWTVerifier, SDJWTSerializationFormat}; use sd_jwt_rs::{COMBINED_SERIALIZATION_FORMAT_SEPARATOR, DEFAULT_SIGNING_ALG}; use serde_json::{json, Map, Value}; @@ -40,7 +40,7 @@ fn _address_claims() -> serde_json::Value { #[fixture] fn address_flat<'a>() -> ( serde_json::Value, - SDJWTClaimsStrategy<'a>, + ClaimsForSelectiveDisclosureStrategy<'a>, Map, usize, ) { @@ -48,7 +48,7 @@ fn address_flat<'a>() -> ( let number_of_revealed_sds = 1; ( value.clone(), - SDJWTClaimsStrategy::Flat, + ClaimsForSelectiveDisclosureStrategy::TopLevel, value.as_object().unwrap().clone(), number_of_revealed_sds, ) @@ -57,7 +57,7 @@ fn address_flat<'a>() -> ( #[fixture] fn address_full_recursive<'a>() -> ( serde_json::Value, - SDJWTClaimsStrategy<'a>, + ClaimsForSelectiveDisclosureStrategy<'a>, Map, usize, ) { @@ -66,7 +66,7 @@ fn address_full_recursive<'a>() -> ( let number_of_revealed_sds = 5; ( value, - SDJWTClaimsStrategy::Full, + ClaimsForSelectiveDisclosureStrategy::AllLevels, claims_to_disclose, number_of_revealed_sds, ) @@ -75,7 +75,7 @@ fn address_full_recursive<'a>() -> ( #[fixture] fn address_only_structured<'a>() -> ( serde_json::Value, - SDJWTClaimsStrategy<'a>, + ClaimsForSelectiveDisclosureStrategy<'a>, Map, usize, ) { @@ -92,7 +92,7 @@ fn address_only_structured<'a>() -> ( ( value.clone(), - SDJWTClaimsStrategy::Partial(ADDRESS_ONLY_STRUCTURED_JSONPATH.to_vec()), + ClaimsForSelectiveDisclosureStrategy::Custom(ADDRESS_ONLY_STRUCTURED_JSONPATH.to_vec()), claims_to_disclose, number_of_revealed_sds, ) @@ -101,7 +101,7 @@ fn address_only_structured<'a>() -> ( #[fixture] fn address_only_structured_one_open<'a>() -> ( serde_json::Value, - SDJWTClaimsStrategy<'a>, + ClaimsForSelectiveDisclosureStrategy<'a>, Map, usize, ) { @@ -117,7 +117,7 @@ fn address_only_structured_one_open<'a>() -> ( ( value, - SDJWTClaimsStrategy::Partial(ADDRESS_ONLY_STRUCTURED_ONE_OPEN_JSONPATH.to_vec()), + ClaimsForSelectiveDisclosureStrategy::Custom(ADDRESS_ONLY_STRUCTURED_ONE_OPEN_JSONPATH.to_vec()), claims_to_disclose, number_of_revealed_sds, ) @@ -126,7 +126,7 @@ fn address_only_structured_one_open<'a>() -> ( #[fixture] fn arrayed_claims<'a>() -> ( serde_json::Value, - SDJWTClaimsStrategy<'a>, + ClaimsForSelectiveDisclosureStrategy<'a>, Map, usize, ) { @@ -140,7 +140,7 @@ fn arrayed_claims<'a>() -> ( ( value, - SDJWTClaimsStrategy::Partial(ARRAYED_CLAIMS_JSONPATH.to_vec()), + ClaimsForSelectiveDisclosureStrategy::Custom(ARRAYED_CLAIMS_JSONPATH.to_vec()), claims_to_disclose, number_of_revealed_sds, ) @@ -149,7 +149,7 @@ fn arrayed_claims<'a>() -> ( #[fixture] fn nested_array<'a>() -> ( serde_json::Value, - SDJWTClaimsStrategy<'a>, + ClaimsForSelectiveDisclosureStrategy<'a>, Map, usize, ) { @@ -162,7 +162,7 @@ fn nested_array<'a>() -> ( ( value.clone(), - SDJWTClaimsStrategy::Partial(NESTED_ARRAY_JSONPATH.to_vec()), + ClaimsForSelectiveDisclosureStrategy::Custom(NESTED_ARRAY_JSONPATH.to_vec()), claims_to_disclose, number_of_revealed_sds, ) @@ -171,7 +171,7 @@ fn nested_array<'a>() -> ( #[fixture] fn complex_eidas<'a>() -> ( serde_json::Value, - SDJWTClaimsStrategy<'a>, + ClaimsForSelectiveDisclosureStrategy<'a>, Map, usize, ) { @@ -209,7 +209,7 @@ fn complex_eidas<'a>() -> ( ( value.clone(), - SDJWTClaimsStrategy::Partial(COMPLEX_EIDAS_JSONPATH.to_vec()), + ClaimsForSelectiveDisclosureStrategy::Custom(COMPLEX_EIDAS_JSONPATH.to_vec()), claims_to_disclose, number_of_revealed_sds, ) @@ -218,7 +218,7 @@ fn complex_eidas<'a>() -> ( #[fixture] fn w3c_vc<'a>() -> ( serde_json::Value, - SDJWTClaimsStrategy<'a>, + ClaimsForSelectiveDisclosureStrategy<'a>, Map, usize, ) { @@ -243,7 +243,7 @@ fn w3c_vc<'a>() -> ( ( value.clone(), - SDJWTClaimsStrategy::Partial(W3C_VC_JSONPATH.to_vec()), + ClaimsForSelectiveDisclosureStrategy::Custom(W3C_VC_JSONPATH.to_vec()), claims_to_disclose, number_of_revealed_sds, ) @@ -277,7 +277,7 @@ fn demo_positive_cases( issuer_key: EncodingKey, #[case] data: ( serde_json::Value, - SDJWTClaimsStrategy, + ClaimsForSelectiveDisclosureStrategy, Map, usize, ), From fc661928e7f75089e8aeee93fcc9bdd39348365d Mon Sep 17 00:00:00 2001 From: Sergey Minaev Date: Sat, 20 Jan 2024 11:25:43 +0500 Subject: [PATCH 5/7] Simplify SDJWTCommon helpers code structure. Signed-off-by: Sergey Minaev --- src/lib.rs | 133 ++++++++++++++++++++++++++++------------------------- 1 file changed, 70 insertions(+), 63 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a7fe3da..6ab3bbc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,72 +124,79 @@ impl SDJWTCommon { Ok(()) } - fn parse_sd_jwt(&mut self, sd_jwt_with_disclosures: String) -> Result<()> { - if self.get_serialization_format() == &SDJWTSerializationFormat::Compact { - let parts: Vec<&str> = sd_jwt_with_disclosures - .split(COMBINED_SERIALIZATION_FORMAT_SEPARATOR) - .collect(); - if parts.len() < 2 { // minimal number of SD-JWT parts according to the standard - return Err(Error::InvalidInput(format!( - "Invalid SD-JWT length: {}", - parts.len() - ))); - } - let idx = parts.len(); - let mut parts = parts.into_iter(); - let sd_jwt = parts.next().ok_or(Error::IndexOutOfBounds { - idx: 0, - length: parts.len(), - msg: format!("Invalid SD-JWT: {}", sd_jwt_with_disclosures), - })?; - self.unverified_input_key_binding_jwt = Some( - parts - .next_back() - .ok_or(Error::IndexOutOfBounds { - idx: idx - 1, - length: idx, - msg: format!( - "Invalid SD-JWT. Key binding not found: {}", - sd_jwt_with_disclosures - ), - })? - .to_owned(), - ); - self.input_disclosures = parts.map(str::to_owned).collect(); - self.unverified_sd_jwt = Some(sd_jwt.to_owned()); - - let mut sd_jwt = sd_jwt.split(JWT_SEPARATOR); - sd_jwt.next(); - let jwt_body = sd_jwt.next().ok_or(Error::IndexOutOfBounds { - idx: 1, - length: 3, - msg: format!( - "Invalid JWT: Cannot extract JWT payload: {}", - self.unverified_sd_jwt.to_owned().unwrap_or("".to_string()) - ), - })?; - self.unverified_input_sd_jwt_payload = Some(jwt_payload_decode(jwt_body)?); - Ok(()) - } else { - let parsed_sd_jwt_json: SDJWTJson = serde_json::from_str(&sd_jwt_with_disclosures) - .map_err(|e| Error::DeserializationError(e.to_string()))?; - self.unverified_sd_jwt_json = Some(parsed_sd_jwt_json.clone()); - self.unverified_input_key_binding_jwt = parsed_sd_jwt_json.kb_jwt; - self.input_disclosures = parsed_sd_jwt_json.disclosures; - self.unverified_input_sd_jwt_payload = - Some(jwt_payload_decode(&parsed_sd_jwt_json.payload)?); - self.unverified_sd_jwt = Some(format!( - "{}.{}.{}", - parsed_sd_jwt_json.protected, - parsed_sd_jwt_json.payload, - parsed_sd_jwt_json.signature - )); - Ok(()) + fn parse_compact_sd_jwt(&mut self, sd_jwt_with_disclosures: String) -> Result<()> { + let parts: Vec<&str> = sd_jwt_with_disclosures + .split(COMBINED_SERIALIZATION_FORMAT_SEPARATOR) + .collect(); + if parts.len() < 2 { // minimal number of SD-JWT parts according to the standard + return Err(Error::InvalidInput(format!( + "Invalid SD-JWT length: {}", + parts.len() + ))); } + let idx = parts.len(); + let mut parts = parts.into_iter(); + let sd_jwt = parts.next().ok_or(Error::IndexOutOfBounds { + idx: 0, + length: parts.len(), + msg: format!("Invalid SD-JWT: {}", sd_jwt_with_disclosures), + })?; + self.unverified_input_key_binding_jwt = Some( + parts + .next_back() + .ok_or(Error::IndexOutOfBounds { + idx: idx - 1, + length: idx, + msg: format!( + "Invalid SD-JWT. Key binding not found: {}", + sd_jwt_with_disclosures + ), + })? + .to_owned(), + ); + self.input_disclosures = parts.map(str::to_owned).collect(); + self.unverified_sd_jwt = Some(sd_jwt.to_owned()); + + let mut sd_jwt = sd_jwt.split(JWT_SEPARATOR); + sd_jwt.next(); + let jwt_body = sd_jwt.next().ok_or(Error::IndexOutOfBounds { + idx: 1, + length: 3, + msg: format!( + "Invalid JWT: Cannot extract JWT payload: {}", + self.unverified_sd_jwt.to_owned().unwrap_or("".to_string()) + ), + })?; + self.unverified_input_sd_jwt_payload = Some(jwt_payload_decode(jwt_body)?); + Ok(()) + } + + fn parse_json_sd_jwt(&mut self, sd_jwt_with_disclosures: String) -> Result<()> { + let parsed_sd_jwt_json: SDJWTJson = serde_json::from_str(&sd_jwt_with_disclosures) + .map_err(|e| Error::DeserializationError(e.to_string()))?; + self.unverified_sd_jwt_json = Some(parsed_sd_jwt_json.clone()); + self.unverified_input_key_binding_jwt = parsed_sd_jwt_json.kb_jwt; + self.input_disclosures = parsed_sd_jwt_json.disclosures; + self.unverified_input_sd_jwt_payload = + Some(jwt_payload_decode(&parsed_sd_jwt_json.payload)?); + self.unverified_sd_jwt = Some(format!( + "{}.{}.{}", + parsed_sd_jwt_json.protected, + parsed_sd_jwt_json.payload, + parsed_sd_jwt_json.signature + )); + Ok(()) } - fn get_serialization_format(&self) -> &SDJWTSerializationFormat { - &self.serialization_format + fn parse_sd_jwt(&mut self, sd_jwt_with_disclosures: String) -> Result<()> { + match self.serialization_format { + SDJWTSerializationFormat::Compact => { + self.parse_compact_sd_jwt(sd_jwt_with_disclosures) + } + SDJWTSerializationFormat::JSON => { + self.parse_json_sd_jwt(sd_jwt_with_disclosures) + } + } } } From 29eb89a807fc97c85d573754e309d0d3255a986a Mon Sep 17 00:00:00 2001 From: Sergey Minaev Date: Sat, 20 Jan 2024 11:31:38 +0500 Subject: [PATCH 6/7] Minor cleanup. Signed-off-by: Sergey Minaev --- src/holder.rs | 2 +- src/issuer.rs | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/holder.rs b/src/holder.rs index 3141382..fa270e5 100644 --- a/src/holder.rs +++ b/src/holder.rs @@ -41,7 +41,7 @@ impl SDJWTHolder { pub fn new(sd_jwt_with_disclosures: String, serialization_format: SDJWTSerializationFormat) -> Result { let mut holder = SDJWTHolder { sd_jwt_engine: SDJWTCommon { - serialization_format: serialization_format, + serialization_format, ..Default::default() }, hs_disclosures: Vec::new(), diff --git a/src/issuer.rs b/src/issuer.rs index fbba36c..28f0b96 100644 --- a/src/issuer.rs +++ b/src/issuer.rs @@ -82,7 +82,13 @@ impl<'a> ClaimsForSelectiveDisclosureStrategy<'a> { let next_sd_keys = sd_keys .iter() .filter_map(|str| { - str.strip_prefix(key).and_then(|str| str.strip_prefix('.').or(Some(&str))) + str.strip_prefix(key).and_then(|str| + match str.chars().next() { + Some('.') => Some(&str[1..]), // next token + Some('[') => Some(str), // array index + _ => None, + } + ) }) .collect(); Self::Custom(next_sd_keys) @@ -398,8 +404,10 @@ mod tests { "nationalities[0]", ]); - let next_strategy = strategy.next_level("addresses").next_level("[1]"); - assert_eq!(&next_strategy, &ClaimsForSelectiveDisclosureStrategy::Custom(vec!["", "country"])); + let next_strategy = strategy.next_level("addresses"); + assert_eq!(&next_strategy, &ClaimsForSelectiveDisclosureStrategy::Custom(vec!["[1]", "[1].country"])); + let next_strategy = next_strategy.next_level("[1]"); + assert_eq!(&next_strategy, &ClaimsForSelectiveDisclosureStrategy::Custom(vec!["country"])); } #[test] From 49de93b76515c41471860e9e9e3d69a1ae11d267 Mon Sep 17 00:00:00 2001 From: Sergey Minaev Date: Mon, 22 Jan 2024 12:00:32 +0500 Subject: [PATCH 7/7] Minor doc cleanup. Signed-off-by: Sergey Minaev --- src/holder.rs | 2 +- src/issuer.rs | 9 +++++---- src/verifier.rs | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/holder.rs b/src/holder.rs index fa270e5..46cac8c 100644 --- a/src/holder.rs +++ b/src/holder.rs @@ -29,7 +29,7 @@ impl SDJWTHolder { /// /// # Arguments /// * `sd_jwt_with_disclosures` - SD JWT with disclosures in the format specified by `serialization_format` - /// * `serialization_format` - Serialization format of the SD JWT. + /// * `serialization_format` - Serialization format of the SD JWT, see [SDJWTSerializationFormat]. /// /// # Returns /// * `SDJWTHolder` - Instance of SDJWTHolder diff --git a/src/issuer.rs b/src/issuer.rs index 28f0b96..c8164d8 100644 --- a/src/issuer.rs +++ b/src/issuer.rs @@ -40,13 +40,14 @@ pub struct SDJWTIssuer { /// ClaimsForSelectiveDisclosureStrategy is used to determine which claims can be selectively disclosed later by the holder. #[derive(PartialEq, Debug)] pub enum ClaimsForSelectiveDisclosureStrategy<'a> { - /// No claims can be selectively disclosed. Full disclosure. + /// No claims can be selectively disclosed, so all claims are always disclosed in presentations generated by the holder. NoSDClaims, - /// Top-level claims can be selectively disclosed, nested objects are fully disclosed. + /// Top-level claims can be selectively disclosed, nested objects are fully disclosed, if a parent claim is disclosed. TopLevel, /// All claims can be selectively disclosed (recursively including nested objects). AllLevels, - /// Claims can be selectively disclosed based on the provided JSONPaths. Other claims are fully disclosed. + /// Claims can be selectively disclosed based on the provided JSONPaths. + /// Other claims are always disclosed in presentation generated by the holder. /// # Examples /// ``` /// use sd_jwt_rs::issuer::ClaimsForSelectiveDisclosureStrategy; @@ -150,7 +151,7 @@ impl SDJWTIssuer { /// * `sd_strategy` - The strategy to be used to determine which claims to be selectively disclosed. See [ClaimsForSelectiveDisclosureStrategy] for more details. /// * `holder_key` - The key used to sign the SD-JWT. If not provided, no key binding is added to the SD-JWT. /// * `add_decoy_claims` - If true, decoy claims are added to the SD-JWT. - /// * `serialization_format` - The serialization format to be used for the SD-JWT. + /// * `serialization_format` - The serialization format to be used for the SD-JWT, see [SDJWTSerializationFormat]. /// /// # Returns /// The issued SD-JWT as a string in the requested serialization format. diff --git a/src/verifier.rs b/src/verifier.rs index 0128a66..5eacc6d 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -37,7 +37,7 @@ impl SDJWTVerifier { /// * `cb_get_issuer_key` - A callback function that takes the issuer and the header of the SD-JWT and returns the public key of the issuer. /// * `expected_aud` - The expected audience of the SD-JWT. /// * `expected_nonce` - The expected nonce of the SD-JWT. - /// * `serialization_format` - The serialization format of the SD-JWT. + /// * `serialization_format` - The serialization format of the SD-JWT, see [SDJWTSerializationFormat]. /// /// # Returns /// * `SDJWTVerifier` - The SDJWTVerifier instance. The verified claims can be accessed via the `verified_claims` property.