diff --git a/Cargo.toml b/Cargo.toml index 0758c6a..6588f4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ rand = { version = "0.8.5", default-features = false, features = ["std", "std_rn thiserror = { version = "1.0", default-features = false } strum = { version = "0.25", default-features = false, features = ["std", "derive"] } itertools = { version = "0.12", default-features = false, features = ["use_std"] } -iota-crypto = { version = "0.23", default-features = false, features = ["std", "sha"] } +iota-crypto = { version = "0.23", default-features = false, features = ["sha"], optional = true } serde = { version = "1.0", default-features = false, features = ["derive"] } [dev-dependencies] @@ -27,3 +27,6 @@ josekit = "0.8.4" [[example]] name = "sd_jwt" +[features] +default = ["sha"] +sha = ["iota-crypto"] diff --git a/examples/sd_jwt.rs b/examples/sd_jwt.rs index 70ff61c..be0017d 100644 --- a/examples/sd_jwt.rs +++ b/examples/sd_jwt.rs @@ -78,7 +78,7 @@ fn main() { let (payload, _header) = jwt::decode_with_verifier(&sd_jwt.jwt, &verifier).unwrap(); // Decode the payload by providing the disclosures that were parsed from the SD-JWT. - let decoder = SdObjectDecoder::new(); + let decoder = SdObjectDecoder::new_with_sha256(); let decoded = decoder.decode(payload.claims_set(), &sd_jwt.disclosures).unwrap(); println!("decoded object: {}", serde_json::to_string_pretty(&decoded).unwrap()); } diff --git a/src/api_test.rs b/src/api_test.rs index 69e8e8d..908cd67 100644 --- a/src/api_test.rs +++ b/src/api_test.rs @@ -115,7 +115,7 @@ fn test_complex_structure() { let (payload, _header) = jwt::decode_with_verifier(&sd_jwt.jwt, &verifier).unwrap(); // Decode the payload by providing the disclosures that were parsed from the SD-JWT. - let decoder = SdObjectDecoder::new(); + let decoder = SdObjectDecoder::new_with_sha256(); let decoded = decoder.decode(payload.claims_set(), &sd_jwt.disclosures).unwrap(); println!("decoded object: {}", serde_json::to_string_pretty(&decoded).unwrap()); assert_eq!(Value::Object(decoded), object); @@ -156,7 +156,7 @@ fn concealed_object_in_array() { .into_iter() .map(|disclosure| disclosure.to_string()) .collect(); - let decoder = SdObjectDecoder::new(); + let decoder = SdObjectDecoder::new_with_sha256(); let decoded = decoder.decode(encoder.object(), &disclosures).unwrap(); assert_eq!(Value::Object(decoded), expected); } diff --git a/src/decoder.rs b/src/decoder.rs index 8183114..e535125 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -4,9 +4,11 @@ use crate::ARRAY_DIGEST_KEY; use crate::DIGESTS_KEY; use crate::SD_ALG; +use crate::SHA_ALG_NAME; use super::Disclosure; use super::Hasher; +#[cfg(feature = "sha")] use super::Sha256Hasher; use crate::Error; use serde_json::Map; @@ -20,13 +22,20 @@ pub struct SdObjectDecoder { impl SdObjectDecoder { /// Creates a new [`SdObjectDecoder`] with `sha-256` hasher. - pub fn new() -> Self { + #[cfg(feature = "sha")] + pub fn new_with_sha256() -> Self { let hashers: BTreeMap> = BTreeMap::new(); let mut hasher = Self { hashers }; hasher.add_hasher(Box::new(Sha256Hasher::new())); hasher } + /// Creates a new [`SdObjectDecoder`] without any hashers. + pub fn new() -> Self { + let hashers: BTreeMap> = BTreeMap::new(); + Self { hashers } + } + /// Adds a hasher. /// /// If a hasher for the same algorithm [`Hasher::alg_name`] already exists, it will be replaced and @@ -87,7 +96,7 @@ impl SdObjectDecoder { "the value of `_sd_alg` is not a string".to_string(), ))? } else { - Sha256Hasher::ALG_NAME + SHA_ALG_NAME }; self .hashers @@ -227,9 +236,10 @@ impl SdObjectDecoder { } } +#[cfg(feature = "sha")] impl Default for SdObjectDecoder { fn default() -> Self { - Self::new() + Self::new_with_sha256() } } @@ -251,7 +261,7 @@ mod test { encoder .object_mut() .insert("id".to_string(), Value::String("id-value".to_string())); - let decoder = SdObjectDecoder::new(); + let decoder = SdObjectDecoder::new_with_sha256(); let decoded = decoder.decode(encoder.object(), &vec![dis.to_string()]).unwrap_err(); assert!(matches!(decoded, Error::ClaimCollisionError(_))); } @@ -267,7 +277,7 @@ mod test { let mut encoder = SdObjectEncoder::try_from(object).unwrap(); encoder.add_sd_alg_property(); assert_eq!(encoder.object().get("_sd_alg").unwrap(), "sha-256"); - let decoder = SdObjectDecoder::new(); + let decoder = SdObjectDecoder::new_with_sha256(); let decoded = decoder.decode(encoder.object(), &vec![]).unwrap(); assert!(decoded.get("_sd_alg").is_none()); } diff --git a/src/encoder.rs b/src/encoder.rs index 13a9b3e..ff95fae 100644 --- a/src/encoder.rs +++ b/src/encoder.rs @@ -3,6 +3,7 @@ use super::Disclosure; use super::Hasher; +#[cfg(feature = "sha")] use super::Sha256Hasher; use crate::Error; use crate::Result; @@ -18,6 +19,20 @@ pub(crate) const SD_ALG: &str = "_sd_alg"; /// Transforms a JSON object into an SD-JWT object by substituting selected values /// with their corresponding disclosure digests. +#[cfg(not(feature = "sha"))] +pub struct SdObjectEncoder { + /// The object in JSON format. + object: Map, + /// Size of random data used to generate the salts for disclosures in bytes. + /// Constant length for readability considerations. + salt_size: usize, + /// The hash function used to create digests. + hasher: H, +} + +/// Transforms a JSON object into an SD-JWT object by substituting selected values +/// with their corresponding disclosure digests. +#[cfg(feature = "sha")] pub struct SdObjectEncoder { /// The object in JSON format. object: Map, @@ -28,6 +43,7 @@ pub struct SdObjectEncoder { hasher: H, } +#[cfg(feature = "sha")] impl SdObjectEncoder { /// Creates a new [`SdObjectEncoder`] with `sha-256` hash function. /// @@ -51,6 +67,7 @@ impl SdObjectEncoder { } } +#[cfg(feature = "sha")] impl TryFrom for SdObjectEncoder { type Error = crate::Error; diff --git a/src/hasher.rs b/src/hasher.rs index f9b588f..2e8ace4 100644 --- a/src/hasher.rs +++ b/src/hasher.rs @@ -1,9 +1,14 @@ // Copyright 2020-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +#[cfg(feature = "sha")] use crypto::hashes::sha::SHA256; + +#[cfg(feature = "sha")] use crypto::hashes::sha::SHA256_LEN; +pub const SHA_ALG_NAME: &'static str = "sha-256"; + /// Used to implement hash functions to be used for encoding/decoding. /// /// ## Note @@ -32,16 +37,17 @@ pub trait Hasher: Sync + Send { /// An implementation of [`Hasher`] that uses the `sha-256` hash function. #[derive(Default)] +#[cfg(feature = "sha")] pub struct Sha256Hasher; +#[cfg(feature = "sha")] impl Sha256Hasher { - pub const ALG_NAME: &'static str = "sha-256"; /// Creates a new [`ShaHasher`] pub fn new() -> Self { Sha256Hasher {} } } - +#[cfg(feature = "sha")] impl Hasher for Sha256Hasher { fn digest(&self, input: &[u8]) -> Vec { let mut digest: [u8; SHA256_LEN] = Default::default(); @@ -50,7 +56,7 @@ impl Hasher for Sha256Hasher { } fn alg_name(&self) -> &'static str { - Sha256Hasher::ALG_NAME + SHA_ALG_NAME } }