diff --git a/ssi-sd-jwt/src/decode.rs b/ssi-sd-jwt/src/decode.rs index ba0c7b471..6f415ae03 100644 --- a/ssi-sd-jwt/src/decode.rs +++ b/ssi-sd-jwt/src/decode.rs @@ -18,9 +18,9 @@ pub struct ValidityClaims { pub fn decode_verify( serialized: &str, key: &JWK, -) -> Result<(ValidityClaims, Claims), Error> { - let deserialized = - deserialize_string_format(serialized).ok_or(Error::UnableToDeserializeStringFormat)?; +) -> Result<(ValidityClaims, Claims), DecodeError> { + let deserialized = deserialize_string_format(serialized) + .ok_or(DecodeError::UnableToDeserializeStringFormat)?; decode_verify_disclosure_array(deserialized.jwt, key, &deserialized.disclosures) } @@ -29,7 +29,7 @@ pub fn decode_verify_disclosure_array( jwt: &str, key: &JWK, disclosures: &[&str], -) -> Result<(ValidityClaims, Claims), Error> { +) -> Result<(ValidityClaims, Claims), DecodeError> { let mut payload_claims: serde_json::Value = ssi_jwt::decode_verify(jwt, key)?; let validity_claims: ValidityClaims = serde_json::from_value(payload_claims.clone())?; @@ -46,17 +46,17 @@ pub fn decode_verify_disclosure_array( for (_, disclosure) in disclosures { if !disclosure.found { - return Err(Error::UnusedDisclosure); + return Err(DecodeError::UnusedDisclosure); } } Ok((validity_claims, serde_json::from_value(payload_claims)?)) } -fn sd_alg(claims: &serde_json::Value) -> Result { +fn sd_alg(claims: &serde_json::Value) -> Result { let alg_name = claims[SD_ALG_CLAIM_NAME] .as_str() - .ok_or(Error::MissingSdAlg)?; + .ok_or(DecodeError::MissingSdAlg)?; SdAlg::try_from(alg_name) } @@ -64,8 +64,8 @@ fn sd_alg(claims: &serde_json::Value) -> Result { fn translate_to_in_progress_disclosures( disclosures: &[&str], sd_alg: SdAlg, -) -> Result, Error> { - let disclosure_vec: Result, Error> = disclosures +) -> Result, DecodeError> { + let disclosure_vec: Result, DecodeError> = disclosures .iter() .map(|disclosure| InProgressDisclosure::new(disclosure, sd_alg)) .collect(); @@ -77,7 +77,7 @@ fn translate_to_in_progress_disclosures( let prev = disclosure_map.insert(disclosure.hash.clone(), disclosure); if prev.is_some() { - return Err(Error::MultipleDisclosuresWithSameHash); + return Err(DecodeError::MultipleDisclosuresWithSameHash); } } @@ -92,7 +92,7 @@ struct InProgressDisclosure { } impl InProgressDisclosure { - fn new(disclosure: &str, sd_alg: SdAlg) -> Result { + fn new(disclosure: &str, sd_alg: SdAlg) -> Result { Ok(InProgressDisclosure { decoded: DecodedDisclosure::new(disclosure)?, hash: hash_encoded_disclosure(sd_alg, disclosure), @@ -104,7 +104,7 @@ impl InProgressDisclosure { fn visit_claims( payload_claims: &mut serde_json::Value, disclosures: &mut BTreeMap, -) -> Result<(), Error> { +) -> Result<(), DecodeError> { let payload_claims = match payload_claims.as_object_mut() { Some(obj) => obj, None => return Ok(()), @@ -132,7 +132,7 @@ fn visit_claims( let prev = payload_claims.insert(new_claim_name, new_claim_value); if prev.is_some() { - return Err(Error::DisclosureClaimCollidesWithJwtClaim); + return Err(DecodeError::DisclosureClaimCollidesWithJwtClaim); } } @@ -155,20 +155,24 @@ fn visit_claims( fn decode_sd_claims( sd_claims: &serde_json::Value, disclosures: &mut BTreeMap, -) -> Result, Error> { - let sd_claims = sd_claims.as_array().ok_or(Error::SdPropertyNotArray)?; +) -> Result, DecodeError> { + let sd_claims = sd_claims + .as_array() + .ok_or(DecodeError::SdPropertyNotArray)?; let mut found_disclosures = vec![]; for disclosure_hash in sd_claims { - let disclosure_hash = disclosure_hash.as_str().ok_or(Error::SdClaimNotString)?; + let disclosure_hash = disclosure_hash + .as_str() + .ok_or(DecodeError::SdClaimNotString)?; if let Some(in_progress_disclosure) = disclosures.get_mut(disclosure_hash) { if in_progress_disclosure.found { - return Err(Error::DisclosureUsedMultipleTimes); + return Err(DecodeError::DisclosureUsedMultipleTimes); } in_progress_disclosure.found = true; match in_progress_disclosure.decoded.kind { DisclosureKind::ArrayItem(_) => { - return Err(Error::ArrayDisclosureWhenExpectingProperty) + return Err(DecodeError::ArrayDisclosureWhenExpectingProperty) } DisclosureKind::Property { ref name, @@ -184,13 +188,13 @@ fn decode_sd_claims( fn decode_array_claims( array: &[serde_json::Value], disclosures: &mut BTreeMap, -) -> Result, Error> { +) -> Result, DecodeError> { let mut new_items = vec![]; for item in array.iter() { if let Some(hash) = array_item_is_disclosure(item) { if let Some(in_progress_disclosure) = disclosures.get_mut(hash) { if in_progress_disclosure.found { - return Err(Error::DisclosureUsedMultipleTimes); + return Err(DecodeError::DisclosureUsedMultipleTimes); } in_progress_disclosure.found = true; match in_progress_disclosure.decoded.kind { @@ -198,7 +202,7 @@ fn decode_array_claims( new_items.push(value.clone()); } DisclosureKind::Property { .. } => { - return Err(Error::PropertyDisclosureWhenExpectingArray) + return Err(DecodeError::PropertyDisclosureWhenExpectingArray) } } } diff --git a/ssi-sd-jwt/src/digest.rs b/ssi-sd-jwt/src/digest.rs index 23aff5fb6..39b9093a0 100644 --- a/ssi-sd-jwt/src/digest.rs +++ b/ssi-sd-jwt/src/digest.rs @@ -1,7 +1,7 @@ use jose_b64::base64ct::{Base64UrlUnpadded, Encoding}; use sha2::Digest; -use crate::Error; +use crate::DecodeError; #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum SdAlg { @@ -21,12 +21,12 @@ impl SdAlg { } impl TryFrom<&str> for SdAlg { - type Error = Error; + type Error = DecodeError; fn try_from(value: &str) -> Result { Ok(match value { Self::SHA256_STR => SdAlg::Sha256, - other => return Err(Error::UnknownSdAlg(other.to_owned())), + other => return Err(DecodeError::UnknownSdAlg(other.to_owned())), }) } } diff --git a/ssi-sd-jwt/src/encode.rs b/ssi-sd-jwt/src/encode.rs index 15c38964f..73f2f0887 100644 --- a/ssi-sd-jwt/src/encode.rs +++ b/ssi-sd-jwt/src/encode.rs @@ -72,10 +72,10 @@ pub fn encode_sign( key: &JWK, sd_alg: SdAlg, disclosures: Vec, -) -> Result<(String, Vec), Error> { +) -> Result<(String, Vec), EncodeError> { let mut base_claims_json = serde_json::to_value(base_claims)?; - let post_encoded_disclosures: Result, Error> = disclosures + let post_encoded_disclosures: Result, EncodeError> = disclosures .iter() .map(|disclosure| { let encoded = disclosure.encode()?; @@ -93,7 +93,7 @@ pub fn encode_sign( { let base_claims_obj = base_claims_json .as_object_mut() - .ok_or(Error::EncodedAsNonObject)?; + .ok_or(EncodeError::EncodedAsNonObject)?; let prev_sd_alg = base_claims_obj.insert( SD_ALG_CLAIM_NAME.to_owned(), @@ -101,7 +101,7 @@ pub fn encode_sign( ); if prev_sd_alg.is_some() { - return Err(Error::EncodedClaimsContainsReservedProperty); + return Err(EncodeError::EncodedClaimsContainsReservedProperty); } let mut sd_claim = vec![]; @@ -118,7 +118,7 @@ pub fn encode_sign( } let array = base_claims_obj.get_mut(claim_name).unwrap(); - let array = array.as_array_mut().ok_or(Error::ExpectedArray)?; + let array = array.as_array_mut().ok_or(EncodeError::ExpectedArray)?; array.push(serde_json::json!({ARRAY_CLAIM_ITEM_PROPERTY_NAME: disclosure.hash.clone()})); } @@ -129,7 +129,7 @@ pub fn encode_sign( base_claims_obj.insert(SD_CLAIM_NAME.to_owned(), serde_json::Value::Array(sd_claim)); if prev_sd.is_some() { - return Err(Error::EncodedClaimsContainsReservedProperty); + return Err(EncodeError::EncodedClaimsContainsReservedProperty); } } diff --git a/ssi-sd-jwt/src/error.rs b/ssi-sd-jwt/src/error.rs new file mode 100644 index 000000000..ca4105bad --- /dev/null +++ b/ssi-sd-jwt/src/error.rs @@ -0,0 +1,47 @@ +#[derive(thiserror::Error, Debug)] +pub enum DecodeError { + #[error("Unable to deserialize string format of concatenated tildes")] + UnableToDeserializeStringFormat, + #[error("JWT is missing _sd_alg property")] + MissingSdAlg, + #[error("Unknown value of _sd_alg {0}")] + UnknownSdAlg(String), + #[error("Multiple disclosures given with the same hash")] + MultipleDisclosuresWithSameHash, + #[error("An _sd claim wasn't a string")] + SdClaimNotString, + #[error("And _sd property was not an array type")] + SdPropertyNotArray, + #[error("A disclosure claim would collid with an existing JWT claim")] + DisclosureClaimCollidesWithJwtClaim, + #[error("A disclosure is malformed")] + DisclosureMalformed, + #[error("A single disclosure was used multiple times")] + DisclosureUsedMultipleTimes, + #[error("Found an array item disclosure when expecting a property type")] + ArrayDisclosureWhenExpectingProperty, + #[error("Found a property type disclosure when expecting an array item")] + PropertyDisclosureWhenExpectingArray, + #[error("A disclosure was not used during decoding")] + UnusedDisclosure, + #[error(transparent)] + JWS(#[from] ssi_jws::Error), + #[error(transparent)] + JsonDeserialization(#[from] serde_json::Error), +} + +#[derive(thiserror::Error, Debug)] +pub enum EncodeError { + #[error("The base claims to encode did not become a JSON object")] + EncodedAsNonObject, + #[error("The base claims to encode contained a property reserved by SD-JWT")] + EncodedClaimsContainsReservedProperty, + #[error("A property for an array sd claim was not an array")] + ExpectedArray, + #[error("A disclosure was not used during decoding")] + UnusedDisclosure, + #[error(transparent)] + JWS(#[from] ssi_jws::Error), + #[error(transparent)] + JsonSerialization(#[from] serde_json::Error), +} diff --git a/ssi-sd-jwt/src/lib.rs b/ssi-sd-jwt/src/lib.rs index 3005c0ac8..929fb7c98 100644 --- a/ssi-sd-jwt/src/lib.rs +++ b/ssi-sd-jwt/src/lib.rs @@ -1,6 +1,7 @@ mod decode; pub(crate) mod digest; pub(crate) mod encode; +mod error; pub(crate) mod serialized; pub(crate) mod verify; @@ -9,47 +10,10 @@ pub use digest::{hash_encoded_disclosure, SdAlg}; pub use encode::{ encode_array_disclosure, encode_property_disclosure, encode_sign, UnencodedDisclosure, }; +pub use error::{DecodeError, EncodeError}; pub use serialized::{deserialize_string_format, serialize_string_format}; pub use verify::verify_sd_disclosures_array; -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("Unable to deserialize string format of concatenated tildes")] - UnableToDeserializeStringFormat, - #[error("JWT is missing _sd_alg property")] - MissingSdAlg, - #[error("Unknown value of _sd_alg {0}")] - UnknownSdAlg(String), - #[error("Multiple disclosures given with the same hash")] - MultipleDisclosuresWithSameHash, - #[error("An _sd claim wasn't a string")] - SdClaimNotString, - #[error("And _sd property was not an array type")] - SdPropertyNotArray, - #[error("A disclosure claim would collid with an existing JWT claim")] - DisclosureClaimCollidesWithJwtClaim, - #[error("A disclosure is malformed")] - DisclosureMalformed, - #[error("A single disclosure was used multiple times")] - DisclosureUsedMultipleTimes, - #[error("Found an array item disclosure when expecting a property type")] - ArrayDisclosureWhenExpectingProperty, - #[error("Found a property type disclosure when expecting an array item")] - PropertyDisclosureWhenExpectingArray, - #[error("The base claims to encode did not become a JSON object")] - EncodedAsNonObject, - #[error("The base claims to encode contained a property reserved by SD-JWT")] - EncodedClaimsContainsReservedProperty, - #[error("A property for an array sd claim was not an array")] - ExpectedArray, - #[error("A disclosure was not used during decoding")] - UnusedDisclosure, - #[error(transparent)] - JWS(#[from] ssi_jws::Error), - #[error(transparent)] - JsonSerialization(#[from] serde_json::Error), -} - const SD_CLAIM_NAME: &str = "_sd"; const SD_ALG_CLAIM_NAME: &str = "_sd_alg"; const ARRAY_CLAIM_ITEM_PROPERTY_NAME: &str = "..."; diff --git a/ssi-sd-jwt/src/verify.rs b/ssi-sd-jwt/src/verify.rs index 5397e4e0a..a642c8a51 100644 --- a/ssi-sd-jwt/src/verify.rs +++ b/ssi-sd-jwt/src/verify.rs @@ -1,7 +1,7 @@ use jose_b64::base64ct::{Base64UrlUnpadded, Encoding}; use crate::digest::{hash_encoded_disclosure, SdAlg}; -use crate::Error; +use crate::DecodeError; #[derive(Debug, PartialEq)] pub struct DecodedDisclosure { @@ -19,7 +19,7 @@ pub enum DisclosureKind { } impl DecodedDisclosure { - pub fn new(encoded: &str) -> Result { + pub fn new(encoded: &str) -> Result { let bytes = Base64UrlUnpadded::decode_vec(encoded).unwrap(); let json: serde_json::Value = serde_json::from_slice(&bytes).unwrap(); @@ -27,17 +27,19 @@ impl DecodedDisclosure { serde_json::Value::Array(values) => match values.len() { 3 => validate_property_disclosure(&values), 2 => validate_array_item_disclosure(&values), - _ => Err(Error::DisclosureMalformed), + _ => Err(DecodeError::DisclosureMalformed), }, - _ => Err(Error::DisclosureMalformed), + _ => Err(DecodeError::DisclosureMalformed), } } } -fn validate_property_disclosure(values: &[serde_json::Value]) -> Result { - let salt = values[0].as_str().ok_or(Error::DisclosureMalformed)?; +fn validate_property_disclosure( + values: &[serde_json::Value], +) -> Result { + let salt = values[0].as_str().ok_or(DecodeError::DisclosureMalformed)?; - let name = values[1].as_str().ok_or(Error::DisclosureMalformed)?; + let name = values[1].as_str().ok_or(DecodeError::DisclosureMalformed)?; Ok(DecodedDisclosure { salt: salt.to_owned(), @@ -50,8 +52,8 @@ fn validate_property_disclosure(values: &[serde_json::Value]) -> Result Result { - let salt = values[0].as_str().ok_or(Error::DisclosureMalformed)?; +) -> Result { + let salt = values[0].as_str().ok_or(DecodeError::DisclosureMalformed)?; Ok(DecodedDisclosure { salt: salt.to_owned(), @@ -63,7 +65,7 @@ pub fn verify_sd_disclosures_array( digest_algo: SdAlg, disclosures: &[&str], sd_claim: &[&str], -) -> Result { +) -> Result { let mut verfied_claims = serde_json::Map::new(); for disclosure in disclosures { @@ -80,11 +82,11 @@ pub fn verify_sd_disclosures_array( let orig = verfied_claims.insert(name, value); if orig.is_some() { - return Err(Error::DisclosureUsedMultipleTimes); + return Err(DecodeError::DisclosureUsedMultipleTimes); } } DisclosureKind::ArrayItem(_) => { - return Err(Error::ArrayDisclosureWhenExpectingProperty); + return Err(DecodeError::ArrayDisclosureWhenExpectingProperty); } } }