From 26afa2c5a7e14cfc7ea6bf9b3085624011227ef7 Mon Sep 17 00:00:00 2001 From: Frederik Rothenberger Date: Tue, 17 Sep 2024 15:07:14 +0200 Subject: [PATCH 1/6] Add support for custom JWS algorithms (#1410) * Add support for custom JWS algorithms This PR introduces a feature `custom_alg` to `identity_jose` (disabled by default) that allows it to process JWS with custom `alg` values. Switching on `custom_alg` makes quite a few changes to `JwsAlgorithm`: - The type is no longer `Copy` - `name()` takes only a reference and returns a `String` rather than `&'static str` - The constant `ALL` is removed as it is no longer possible to enumerate all variants * fmt * Add comment * Nightly fmt * chore: add template for custom_alg file * Split implementation of Display --------- Co-authored-by: Yasir --- identity_jose/Cargo.toml | 7 ++ identity_jose/src/jwk/key.rs | 4 +- identity_jose/src/jws/algorithm.rs | 51 +++++++- identity_jose/src/jws/header.rs | 2 +- identity_jose/tests/custom_alg.rs | 110 ++++++++++++++++++ identity_storage/src/key_storage/memstore.rs | 6 +- .../src/storage/stronghold_jwk_storage.rs | 4 +- identity_stronghold/src/utils.rs | 2 +- 8 files changed, 173 insertions(+), 13 deletions(-) create mode 100644 identity_jose/tests/custom_alg.rs diff --git a/identity_jose/Cargo.toml b/identity_jose/Cargo.toml index 1dbcb2781f..622ac4f347 100644 --- a/identity_jose/Cargo.toml +++ b/identity_jose/Cargo.toml @@ -34,3 +34,10 @@ test = true [lints] workspace = true + +[features] +custom_alg = [] + +[[test]] +name = "custom_alg" +required-features = ["custom_alg"] diff --git a/identity_jose/src/jwk/key.rs b/identity_jose/src/jwk/key.rs index be1db84c35..e2cb05d62d 100644 --- a/identity_jose/src/jwk/key.rs +++ b/identity_jose/src/jwk/key.rs @@ -395,9 +395,9 @@ impl Jwk { // =========================================================================== /// Checks if the `alg` claim of the JWK is equal to `expected`. - pub fn check_alg(&self, expected: &str) -> Result<()> { + pub fn check_alg(&self, expected: impl AsRef) -> Result<()> { match self.alg() { - Some(value) if value == expected => Ok(()), + Some(value) if value == expected.as_ref() => Ok(()), Some(_) => Err(Error::InvalidClaim("alg")), None => Ok(()), } diff --git a/identity_jose/src/jws/algorithm.rs b/identity_jose/src/jws/algorithm.rs index a60dc84050..1d6b1c319c 100644 --- a/identity_jose/src/jws/algorithm.rs +++ b/identity_jose/src/jws/algorithm.rs @@ -6,12 +6,11 @@ use core::fmt::Formatter; use core::fmt::Result; use std::str::FromStr; -use crate::error::Error; - /// Supported algorithms for the JSON Web Signatures `alg` claim. /// /// [More Info](https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms) -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, serde::Deserialize, serde::Serialize)] +#[cfg_attr(not(feature = "custom_alg"), derive(Copy))] #[allow(non_camel_case_types)] pub enum JwsAlgorithm { /// HMAC using SHA-256 @@ -45,10 +44,19 @@ pub enum JwsAlgorithm { NONE, /// EdDSA signature algorithms EdDSA, + /// Custom algorithm + #[cfg(feature = "custom_alg")] + #[serde(untagged)] + Custom(String), } impl JwsAlgorithm { /// A slice of all supported [`JwsAlgorithm`]s. + /// + /// Not available when feature `custom_alg` is enabled + /// as it is not possible to enumerate all variants when + /// supporting arbitrary `alg` values. + #[cfg(not(feature = "custom_alg"))] pub const ALL: &'static [Self] = &[ Self::HS256, Self::HS384, @@ -68,6 +76,7 @@ impl JwsAlgorithm { ]; /// Returns the JWS algorithm as a `str` slice. + #[cfg(not(feature = "custom_alg"))] pub const fn name(self) -> &'static str { match self { Self::HS256 => "HS256", @@ -87,6 +96,29 @@ impl JwsAlgorithm { Self::EdDSA => "EdDSA", } } + + /// Returns the JWS algorithm as a `str` slice. + #[cfg(feature = "custom_alg")] + pub fn name(&self) -> String { + match self { + Self::HS256 => "HS256".to_string(), + Self::HS384 => "HS384".to_string(), + Self::HS512 => "HS512".to_string(), + Self::RS256 => "RS256".to_string(), + Self::RS384 => "RS384".to_string(), + Self::RS512 => "RS512".to_string(), + Self::PS256 => "PS256".to_string(), + Self::PS384 => "PS384".to_string(), + Self::PS512 => "PS512".to_string(), + Self::ES256 => "ES256".to_string(), + Self::ES384 => "ES384".to_string(), + Self::ES512 => "ES512".to_string(), + Self::ES256K => "ES256K".to_string(), + Self::NONE => "none".to_string(), + Self::EdDSA => "EdDSA".to_string(), + Self::Custom(name) => name.clone(), + } + } } impl FromStr for JwsAlgorithm { @@ -109,13 +141,24 @@ impl FromStr for JwsAlgorithm { "ES256K" => Ok(Self::ES256K), "none" => Ok(Self::NONE), "EdDSA" => Ok(Self::EdDSA), - _ => Err(Error::JwsAlgorithmParsingError), + #[cfg(feature = "custom_alg")] + value => Ok(Self::Custom(value.to_string())), + #[cfg(not(feature = "custom_alg"))] + _ => Err(crate::error::Error::JwsAlgorithmParsingError), } } } +#[cfg(not(feature = "custom_alg"))] impl Display for JwsAlgorithm { fn fmt(&self, f: &mut Formatter<'_>) -> Result { f.write_str(self.name()) } } + +#[cfg(feature = "custom_alg")] +impl Display for JwsAlgorithm { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.write_str(&(*self).name()) + } +} diff --git a/identity_jose/src/jws/header.rs b/identity_jose/src/jws/header.rs index b5749608a1..30b4f5fe82 100644 --- a/identity_jose/src/jws/header.rs +++ b/identity_jose/src/jws/header.rs @@ -67,7 +67,7 @@ impl JwsHeader { /// Returns the value for the algorithm claim (alg). pub fn alg(&self) -> Option { - self.alg.as_ref().copied() + self.alg.as_ref().cloned() } /// Sets a value for the algorithm claim (alg). diff --git a/identity_jose/tests/custom_alg.rs b/identity_jose/tests/custom_alg.rs new file mode 100644 index 0000000000..3297d6258b --- /dev/null +++ b/identity_jose/tests/custom_alg.rs @@ -0,0 +1,110 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::ops::Deref; +use std::time::SystemTime; + +use crypto::signatures::ed25519::PublicKey; +use crypto::signatures::ed25519::SecretKey; +use crypto::signatures::ed25519::Signature; +use identity_jose::jwk::EdCurve; +use identity_jose::jwk::Jwk; +use identity_jose::jwk::JwkParamsOkp; +use identity_jose::jwk::JwkType; +use identity_jose::jws::CompactJwsEncoder; +use identity_jose::jws::Decoder; +use identity_jose::jws::JwsAlgorithm; +use identity_jose::jws::JwsHeader; +use identity_jose::jws::JwsVerifierFn; +use identity_jose::jws::SignatureVerificationError; +use identity_jose::jws::SignatureVerificationErrorKind; +use identity_jose::jws::VerificationInput; +use identity_jose::jwt::JwtClaims; +use identity_jose::jwu; +use jsonprooftoken::encoding::base64url_decode; + +#[test] +fn custom_alg_roundtrip() { + let secret_key = SecretKey::generate().unwrap(); + let public_key = secret_key.public_key(); + + let mut header: JwsHeader = JwsHeader::new(); + header.set_alg(JwsAlgorithm::Custom("test".to_string())); + let kid = "did:iota:0x123#signing-key"; + header.set_kid(kid); + + let mut claims: JwtClaims = JwtClaims::new(); + claims.set_iss("issuer"); + claims.set_iat( + SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs() as i64, + ); + claims.set_custom(serde_json::json!({"num": 42u64})); + + let claims_bytes: Vec = serde_json::to_vec(&claims).unwrap(); + + let encoder: CompactJwsEncoder<'_> = CompactJwsEncoder::new(&claims_bytes, &header).unwrap(); + let signing_input: &[u8] = encoder.signing_input(); + let signature = secret_key.sign(signing_input).to_bytes(); + let jws = encoder.into_jws(&signature); + + let header = jws.split(".").next().unwrap(); + let header_json = String::from_utf8(base64url_decode(header.as_bytes())).expect("failed to decode header"); + assert_eq!(header_json, r#"{"kid":"did:iota:0x123#signing-key","alg":"test"}"#); + + let verifier = JwsVerifierFn::from(|input: VerificationInput, key: &Jwk| { + if input.alg != JwsAlgorithm::Custom("test".to_string()) { + panic!("invalid algorithm"); + } + verify(input, key) + }); + let decoder = Decoder::new(); + let mut public_key_jwk = Jwk::new(JwkType::Okp); + public_key_jwk.set_kid(kid); + public_key_jwk + .set_params(JwkParamsOkp { + crv: "Ed25519".into(), + x: jwu::encode_b64(public_key.as_slice()), + d: None, + }) + .unwrap(); + + let token = decoder + .decode_compact_serialization(jws.as_bytes(), None) + .and_then(|decoded| decoded.verify(&verifier, &public_key_jwk)) + .unwrap(); + + let recovered_claims: JwtClaims = serde_json::from_slice(&token.claims).unwrap(); + + assert_eq!(token.protected.alg(), Some(JwsAlgorithm::Custom("test".to_string()))); + assert_eq!(claims, recovered_claims); +} + +fn verify(verification_input: VerificationInput, jwk: &Jwk) -> Result<(), SignatureVerificationError> { + let public_key = expand_public_jwk(jwk); + + let signature_arr = <[u8; Signature::LENGTH]>::try_from(verification_input.decoded_signature.deref()) + .map_err(|err| err.to_string()) + .unwrap(); + + let signature = Signature::from_bytes(signature_arr); + if public_key.verify(&signature, &verification_input.signing_input) { + Ok(()) + } else { + Err(SignatureVerificationErrorKind::InvalidSignature.into()) + } +} + +fn expand_public_jwk(jwk: &Jwk) -> PublicKey { + let params: &JwkParamsOkp = jwk.try_okp_params().unwrap(); + + if params.try_ed_curve().unwrap() != EdCurve::Ed25519 { + panic!("expected an ed25519 jwk"); + } + + let pk: [u8; PublicKey::LENGTH] = jwu::decode_b64(params.x.as_str()).unwrap().try_into().unwrap(); + + PublicKey::try_from(pk).unwrap() +} diff --git a/identity_storage/src/key_storage/memstore.rs b/identity_storage/src/key_storage/memstore.rs index 9bf4e6ea9a..2f51be97ce 100644 --- a/identity_storage/src/key_storage/memstore.rs +++ b/identity_storage/src/key_storage/memstore.rs @@ -58,7 +58,7 @@ impl JwkStorage for JwkMemStore { async fn generate(&self, key_type: KeyType, alg: JwsAlgorithm) -> KeyStorageResult { let key_type: MemStoreKeyType = MemStoreKeyType::try_from(&key_type)?; - check_key_alg_compatibility(key_type, alg)?; + check_key_alg_compatibility(key_type, &alg)?; let (private_key, public_key) = match key_type { MemStoreKeyType::Ed25519 => { @@ -102,7 +102,7 @@ impl JwkStorage for JwkMemStore { Some(alg) => { let alg: JwsAlgorithm = JwsAlgorithm::from_str(alg) .map_err(|err| KeyStorageError::new(KeyStorageErrorKind::UnsupportedSignatureAlgorithm).with_source(err))?; - check_key_alg_compatibility(key_type, alg)?; + check_key_alg_compatibility(key_type, &alg)?; } None => { return Err( @@ -291,7 +291,7 @@ fn random_key_id() -> KeyId { } /// Check that the key type can be used with the algorithm. -fn check_key_alg_compatibility(key_type: MemStoreKeyType, alg: JwsAlgorithm) -> KeyStorageResult<()> { +fn check_key_alg_compatibility(key_type: MemStoreKeyType, alg: &JwsAlgorithm) -> KeyStorageResult<()> { match (key_type, alg) { (MemStoreKeyType::Ed25519, JwsAlgorithm::EdDSA) => Ok(()), (key_type, alg) => Err( diff --git a/identity_stronghold/src/storage/stronghold_jwk_storage.rs b/identity_stronghold/src/storage/stronghold_jwk_storage.rs index b0400c8f65..efe0f6531b 100644 --- a/identity_stronghold/src/storage/stronghold_jwk_storage.rs +++ b/identity_stronghold/src/storage/stronghold_jwk_storage.rs @@ -36,7 +36,7 @@ impl JwkStorage for StrongholdStorage { let client = get_client(&stronghold)?; let key_type = StrongholdKeyType::try_from(&key_type)?; - check_key_alg_compatibility(key_type, alg)?; + check_key_alg_compatibility(key_type, &alg)?; let keytype: ProceduresKeyType = match key_type { StrongholdKeyType::Ed25519 => ProceduresKeyType::Ed25519, @@ -106,7 +106,7 @@ impl JwkStorage for StrongholdStorage { Some(alg) => { let alg: JwsAlgorithm = JwsAlgorithm::from_str(alg) .map_err(|err| KeyStorageError::new(KeyStorageErrorKind::UnsupportedSignatureAlgorithm).with_source(err))?; - check_key_alg_compatibility(key_type, alg)?; + check_key_alg_compatibility(key_type, &alg)?; } None => { return Err( diff --git a/identity_stronghold/src/utils.rs b/identity_stronghold/src/utils.rs index 3a9ae72842..0bf83e1f18 100644 --- a/identity_stronghold/src/utils.rs +++ b/identity_stronghold/src/utils.rs @@ -24,7 +24,7 @@ pub fn random_key_id() -> KeyId { } /// Check that the key type can be used with the algorithm. -pub fn check_key_alg_compatibility(key_type: StrongholdKeyType, alg: JwsAlgorithm) -> KeyStorageResult<()> { +pub fn check_key_alg_compatibility(key_type: StrongholdKeyType, alg: &JwsAlgorithm) -> KeyStorageResult<()> { match (key_type, alg) { (StrongholdKeyType::Ed25519, JwsAlgorithm::EdDSA) => Ok(()), (key_type, alg) => Err( From 95884a634366eedd9372fdd14b40b344cd7e9b05 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:33:47 +0200 Subject: [PATCH 2/6] Release v1.4.0 (#1411) --- CHANGELOG.md | 19 +++++++++++++++++-- examples/Cargo.toml | 2 +- identity_core/Cargo.toml | 2 +- identity_credential/Cargo.toml | 10 +++++----- identity_did/Cargo.toml | 6 +++--- identity_document/Cargo.toml | 8 ++++---- identity_ecdsa_verifier/Cargo.toml | 4 ++-- identity_eddsa_verifier/Cargo.toml | 4 ++-- identity_iota/Cargo.toml | 18 +++++++++--------- identity_iota_core/Cargo.toml | 12 ++++++------ identity_jose/Cargo.toml | 4 ++-- identity_resolver/Cargo.toml | 12 ++++++------ identity_storage/Cargo.toml | 18 +++++++++--------- identity_stronghold/Cargo.toml | 10 +++++----- identity_verification/Cargo.toml | 8 ++++---- 15 files changed, 76 insertions(+), 61 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45d3fa510e..ab1b36f03b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## [v1.4.0](https://github.com/iotaledger/identity.rs/tree/v1.4.0) (2024-09-23) + +[Full Changelog](https://github.com/iotaledger/identity.rs/compare/v1.3.1...v1.4.0) + +### Added + +- Add feature to support custom `now_utc` implementations [\#1397](https://github.com/iotaledger/identity.rs/pull/1397) +- Add support for `did:jwk` resolution [\#1404](https://github.com/iotaledger/identity.rs/pull/1404) +- Linked Verifiable Presentations [\#1398](https://github.com/iotaledger/identity.rs/pull/1398) +- Add support for custom JWS algorithms [\#1410](https://github.com/iotaledger/identity.rs/pull/1410) + +### Patch + +- Make `bls12_381_plus` dependency more flexible again [\#1393](https://github.com/iotaledger/identity.rs/pull/1393) +- Mark `js-sys` as optional for identity_core [\#1405](https://github.com/iotaledger/identity.rs/pull/1405) +- Remove dependency on `identity_core` default features [\#1408](https://github.com/iotaledger/identity.rs/pull/1408) + ## [v1.3.1](https://github.com/iotaledger/identity.rs/tree/v1.3.1) (2024-06-12) [Full Changelog](https://github.com/iotaledger/identity.rs/compare/v1.3.0...v1.3.1) @@ -8,8 +25,6 @@ - Pin and bump `bls12_381_plus` dependency [\#1378](https://github.com/iotaledger/identity.rs/pull/1378) -# Changelog - ## [v1.3.0](https://github.com/iotaledger/identity.rs/tree/v1.3.0) (2024-05-28) [Full Changelog](https://github.com/iotaledger/identity.rs/compare/v1.2.0...v1.3.0) diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 9866115ad3..1a3d313705 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "examples" -version = "1.3.1" +version = "1.4.0" authors = ["IOTA Stiftung"] edition = "2021" publish = false diff --git a/identity_core/Cargo.toml b/identity_core/Cargo.toml index 239383c2f6..fcdd263cc7 100644 --- a/identity_core/Cargo.toml +++ b/identity_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_core" -version = "1.3.1" +version = "1.4.0" authors.workspace = true edition.workspace = true homepage.workspace = true diff --git a/identity_credential/Cargo.toml b/identity_credential/Cargo.toml index aaba6c974e..62cb6d0a41 100644 --- a/identity_credential/Cargo.toml +++ b/identity_credential/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_credential" -version = "1.3.1" +version = "1.4.0" authors = ["IOTA Stiftung"] edition = "2021" homepage.workspace = true @@ -16,10 +16,10 @@ async-trait = { version = "0.1.64", default-features = false } bls12_381_plus = { workspace = true, optional = true } flate2 = { version = "1.0.28", default-features = false, features = ["rust_backend"], optional = true } futures = { version = "0.3", default-features = false, optional = true } -identity_core = { version = "=1.3.1", path = "../identity_core", default-features = false } -identity_did = { version = "=1.3.1", path = "../identity_did", default-features = false } -identity_document = { version = "=1.3.1", path = "../identity_document", default-features = false } -identity_verification = { version = "=1.3.1", path = "../identity_verification", default-features = false } +identity_core = { version = "=1.4.0", path = "../identity_core", default-features = false } +identity_did = { version = "=1.4.0", path = "../identity_did", default-features = false } +identity_document = { version = "=1.4.0", path = "../identity_document", default-features = false } +identity_verification = { version = "=1.4.0", path = "../identity_verification", default-features = false } indexmap = { version = "2.0", default-features = false, features = ["std", "serde"] } itertools = { version = "0.11", default-features = false, features = ["use_std"], optional = true } json-proof-token = { workspace = true, optional = true } diff --git a/identity_did/Cargo.toml b/identity_did/Cargo.toml index e1025a9fcd..473ffc8860 100644 --- a/identity_did/Cargo.toml +++ b/identity_did/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_did" -version = "1.3.1" +version = "1.4.0" authors.workspace = true edition = "2021" homepage.workspace = true @@ -13,8 +13,8 @@ description = "Agnostic implementation of the Decentralized Identifiers (DID) st [dependencies] did_url_parser = { version = "0.2.0", features = ["std", "serde"] } form_urlencoded = { version = "1.2.0", default-features = false, features = ["alloc"] } -identity_core = { version = "=1.3.1", path = "../identity_core", default-features = false } -identity_jose = { version = "=1.3.1", path = "../identity_jose" } +identity_core = { version = "=1.4.0", path = "../identity_core", default-features = false } +identity_jose = { version = "=1.4.0", path = "../identity_jose" } serde.workspace = true strum.workspace = true thiserror.workspace = true diff --git a/identity_document/Cargo.toml b/identity_document/Cargo.toml index f87fc86c33..4bb50dd09d 100644 --- a/identity_document/Cargo.toml +++ b/identity_document/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_document" -version = "1.3.1" +version = "1.4.0" authors.workspace = true edition.workspace = true homepage.workspace = true @@ -13,9 +13,9 @@ description = "Method-agnostic implementation of the Decentralized Identifiers ( [dependencies] did_url_parser = { version = "0.2.0", features = ["std", "serde"] } -identity_core = { version = "=1.3.1", path = "../identity_core", default-features = false } -identity_did = { version = "=1.3.1", path = "../identity_did" } -identity_verification = { version = "=1.3.1", path = "../identity_verification", default-features = false } +identity_core = { version = "=1.4.0", path = "../identity_core", default-features = false } +identity_did = { version = "=1.4.0", path = "../identity_did" } +identity_verification = { version = "=1.4.0", path = "../identity_verification", default-features = false } indexmap = { version = "2.0", default-features = false, features = ["std", "serde"] } serde.workspace = true strum.workspace = true diff --git a/identity_ecdsa_verifier/Cargo.toml b/identity_ecdsa_verifier/Cargo.toml index 654b8aebe3..6829d41ae0 100644 --- a/identity_ecdsa_verifier/Cargo.toml +++ b/identity_ecdsa_verifier/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_ecdsa_verifier" -version = "1.3.1" +version = "1.4.0" authors = ["IOTA Stiftung", "Filancore GmbH"] edition.workspace = true homepage.workspace = true @@ -15,7 +15,7 @@ description = "JWS ECDSA signature verification for IOTA Identity" workspace = true [dependencies] -identity_verification = { version = "=1.3.1", path = "../identity_verification", default-features = false } +identity_verification = { version = "=1.4.0", path = "../identity_verification", default-features = false } k256 = { version = "0.13.3", default-features = false, features = ["std", "ecdsa", "ecdsa-core"], optional = true } p256 = { version = "0.13.2", default-features = false, features = ["std", "ecdsa", "ecdsa-core"], optional = true } signature = { version = "2", default-features = false } diff --git a/identity_eddsa_verifier/Cargo.toml b/identity_eddsa_verifier/Cargo.toml index 97308beebf..b7da49295a 100644 --- a/identity_eddsa_verifier/Cargo.toml +++ b/identity_eddsa_verifier/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_eddsa_verifier" -version = "1.3.1" +version = "1.4.0" authors.workspace = true edition.workspace = true homepage.workspace = true @@ -12,7 +12,7 @@ rust-version.workspace = true description = "JWS EdDSA signature verification for IOTA Identity" [dependencies] -identity_jose = { version = "=1.3.1", path = "../identity_jose", default-features = false } +identity_jose = { version = "=1.4.0", path = "../identity_jose", default-features = false } iota-crypto = { version = "0.23.2", default-features = false, features = ["std"] } [features] diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index 4e32790288..67933e0634 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_iota" -version = "1.3.1" +version = "1.4.0" authors.workspace = true edition.workspace = true homepage.workspace = true @@ -12,14 +12,14 @@ rust-version.workspace = true description = "Framework for Self-Sovereign Identity with IOTA DID." [dependencies] -identity_core = { version = "=1.3.1", path = "../identity_core", default-features = false } -identity_credential = { version = "=1.3.1", path = "../identity_credential", features = ["validator"], default-features = false } -identity_did = { version = "=1.3.1", path = "../identity_did", default-features = false } -identity_document = { version = "=1.3.1", path = "../identity_document", default-features = false } -identity_iota_core = { version = "=1.3.1", path = "../identity_iota_core", default-features = false } -identity_resolver = { version = "=1.3.1", path = "../identity_resolver", default-features = false, optional = true } -identity_storage = { version = "=1.3.1", path = "../identity_storage", default-features = false, features = ["iota-document"] } -identity_verification = { version = "=1.3.1", path = "../identity_verification", default-features = false } +identity_core = { version = "=1.4.0", path = "../identity_core", default-features = false } +identity_credential = { version = "=1.4.0", path = "../identity_credential", features = ["validator"], default-features = false } +identity_did = { version = "=1.4.0", path = "../identity_did", default-features = false } +identity_document = { version = "=1.4.0", path = "../identity_document", default-features = false } +identity_iota_core = { version = "=1.4.0", path = "../identity_iota_core", default-features = false } +identity_resolver = { version = "=1.4.0", path = "../identity_resolver", default-features = false, optional = true } +identity_storage = { version = "=1.4.0", path = "../identity_storage", default-features = false, features = ["iota-document"] } +identity_verification = { version = "=1.4.0", path = "../identity_verification", default-features = false } [dev-dependencies] anyhow = "1.0.64" diff --git a/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index f44a3ca27c..73dcc4190e 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_iota_core" -version = "1.3.1" +version = "1.4.0" authors.workspace = true edition.workspace = true homepage.workspace = true @@ -14,11 +14,11 @@ description = "An IOTA Ledger integration for the IOTA DID Method." [dependencies] async-trait = { version = "0.1.56", default-features = false, optional = true } futures = { version = "0.3", default-features = false } -identity_core = { version = "=1.3.1", path = "../identity_core", default-features = false } -identity_credential = { version = "=1.3.1", path = "../identity_credential", default-features = false, features = ["validator"] } -identity_did = { version = "=1.3.1", path = "../identity_did", default-features = false } -identity_document = { version = "=1.3.1", path = "../identity_document", default-features = false } -identity_verification = { version = "=1.3.1", path = "../identity_verification", default-features = false } +identity_core = { version = "=1.4.0", path = "../identity_core", default-features = false } +identity_credential = { version = "=1.4.0", path = "../identity_credential", default-features = false, features = ["validator"] } +identity_did = { version = "=1.4.0", path = "../identity_did", default-features = false } +identity_document = { version = "=1.4.0", path = "../identity_document", default-features = false } +identity_verification = { version = "=1.4.0", path = "../identity_verification", default-features = false } iota-sdk = { version = "1.1.5", default-features = false, features = ["serde", "std"], optional = true } num-derive = { version = "0.4", default-features = false } num-traits = { version = "0.2", default-features = false, features = ["std"] } diff --git a/identity_jose/Cargo.toml b/identity_jose/Cargo.toml index 622ac4f347..73a7fa3cdb 100644 --- a/identity_jose/Cargo.toml +++ b/identity_jose/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_jose" -version = "1.3.1" +version = "1.4.0" authors.workspace = true edition.workspace = true homepage.workspace = true @@ -13,7 +13,7 @@ description = "A library for JOSE (JSON Object Signing and Encryption)" [dependencies] bls12_381_plus.workspace = true -identity_core = { version = "=1.3.1", path = "../identity_core" } +identity_core = { version = "=1.4.0", path = "../identity_core" } iota-crypto = { version = "0.23.2", default-features = false, features = ["std", "sha"] } json-proof-token.workspace = true serde.workspace = true diff --git a/identity_resolver/Cargo.toml b/identity_resolver/Cargo.toml index a85969c286..d99158835d 100644 --- a/identity_resolver/Cargo.toml +++ b/identity_resolver/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_resolver" -version = "1.3.1" +version = "1.4.0" authors.workspace = true edition.workspace = true homepage.workspace = true @@ -15,16 +15,16 @@ description = "DID Resolution utilities for the identity.rs library." # This is currently necessary for the ResolutionHandler trait. This can be made an optional dependency if alternative ways of attaching handlers are introduced. async-trait = { version = "0.1", default-features = false } futures = { version = "0.3" } -identity_core = { version = "=1.3.1", path = "../identity_core", default-features = false } -identity_credential = { version = "=1.3.1", path = "../identity_credential", default-features = false, features = ["validator"] } -identity_did = { version = "=1.3.1", path = "../identity_did", default-features = false } -identity_document = { version = "=1.3.1", path = "../identity_document", default-features = false } +identity_core = { version = "=1.4.0", path = "../identity_core", default-features = false } +identity_credential = { version = "=1.4.0", path = "../identity_credential", default-features = false, features = ["validator"] } +identity_did = { version = "=1.4.0", path = "../identity_did", default-features = false } +identity_document = { version = "=1.4.0", path = "../identity_document", default-features = false } serde = { version = "1.0", default-features = false, features = ["std", "derive"] } strum.workspace = true thiserror = { version = "1.0", default-features = false } [dependencies.identity_iota_core] -version = "=1.3.1" +version = "=1.4.0" path = "../identity_iota_core" default-features = false features = ["send-sync-client-ext", "iota-client"] diff --git a/identity_storage/Cargo.toml b/identity_storage/Cargo.toml index fbbe93b346..5331dc725f 100644 --- a/identity_storage/Cargo.toml +++ b/identity_storage/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_storage" -version = "1.3.1" +version = "1.4.0" authors.workspace = true edition.workspace = true homepage.workspace = true @@ -16,12 +16,12 @@ anyhow = "1.0.82" async-trait = { version = "0.1.64", default-features = false } bls12_381_plus = { workspace = true, optional = true } futures = { version = "0.3.27", default-features = false, features = ["async-await"] } -identity_core = { version = "=1.3.1", path = "../identity_core", default-features = false } -identity_credential = { version = "=1.3.1", path = "../identity_credential", default-features = false, features = ["credential", "presentation", "revocation-bitmap"] } -identity_did = { version = "=1.3.1", path = "../identity_did", default-features = false } -identity_document = { version = "=1.3.1", path = "../identity_document", default-features = false } -identity_iota_core = { version = "=1.3.1", path = "../identity_iota_core", default-features = false, optional = true } -identity_verification = { version = "=1.3.1", path = "../identity_verification", default-features = false } +identity_core = { version = "=1.4.0", path = "../identity_core", default-features = false } +identity_credential = { version = "=1.4.0", path = "../identity_credential", default-features = false, features = ["credential", "presentation", "revocation-bitmap"] } +identity_did = { version = "=1.4.0", path = "../identity_did", default-features = false } +identity_document = { version = "=1.4.0", path = "../identity_document", default-features = false } +identity_iota_core = { version = "=1.4.0", path = "../identity_iota_core", default-features = false, optional = true } +identity_verification = { version = "=1.4.0", path = "../identity_verification", default-features = false } iota-crypto = { version = "0.23.2", default-features = false, features = ["ed25519", "random"], optional = true } json-proof-token = { workspace = true, optional = true } rand = { version = "0.8.5", default-features = false, features = ["std", "std_rng"], optional = true } @@ -33,8 +33,8 @@ tokio = { version = "1.29.0", default-features = false, features = ["macros", "s zkryptium = { workspace = true, optional = true } [dev-dependencies] -identity_credential = { version = "=1.3.1", path = "../identity_credential", features = ["revocation-bitmap"] } -identity_eddsa_verifier = { version = "=1.3.1", path = "../identity_eddsa_verifier", default-features = false, features = ["ed25519"] } +identity_credential = { version = "=1.4.0", path = "../identity_credential", features = ["revocation-bitmap"] } +identity_eddsa_verifier = { version = "=1.4.0", path = "../identity_eddsa_verifier", default-features = false, features = ["ed25519"] } once_cell = { version = "1.18", default-features = false } tokio = { version = "1.29.0", default-features = false, features = ["macros", "sync", "rt"] } diff --git a/identity_stronghold/Cargo.toml b/identity_stronghold/Cargo.toml index d6b0825cba..b7c61a998f 100644 --- a/identity_stronghold/Cargo.toml +++ b/identity_stronghold/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_stronghold" -version = "1.3.1" +version = "1.4.0" authors.workspace = true edition.workspace = true homepage.workspace = true @@ -14,8 +14,8 @@ description = "Secure JWK storage with Stronghold for IOTA Identity" [dependencies] async-trait = { version = "0.1.64", default-features = false } bls12_381_plus = { workspace = true, optional = true } -identity_storage = { version = "=1.3.1", path = "../identity_storage", default-features = false } -identity_verification = { version = "=1.3.1", path = "../identity_verification", default-features = false } +identity_storage = { version = "=1.4.0", path = "../identity_storage", default-features = false } +identity_verification = { version = "=1.4.0", path = "../identity_verification", default-features = false } iota-crypto = { version = "0.23.2", default-features = false, features = ["ed25519"] } iota-sdk = { version = "1.1.5", default-features = false, features = ["client", "stronghold"] } iota_stronghold = { version = "2.1.0", default-features = false } @@ -28,8 +28,8 @@ zkryptium = { workspace = true, optional = true } [dev-dependencies] anyhow = "1.0.82" bls12_381_plus = { workspace = true } -identity_did = { version = "=1.3.1", path = "../identity_did", default-features = false } -identity_storage = { version = "=1.3.1", path = "../identity_storage", default-features = false, features = ["jpt-bbs-plus"] } +identity_did = { version = "=1.4.0", path = "../identity_did", default-features = false } +identity_storage = { version = "=1.4.0", path = "../identity_storage", default-features = false, features = ["jpt-bbs-plus"] } json-proof-token = { workspace = true } tokio = { version = "1.29.0", default-features = false, features = ["macros", "sync", "rt"] } zkryptium = { workspace = true } diff --git a/identity_verification/Cargo.toml b/identity_verification/Cargo.toml index 8990dd96e0..46fcc5ac24 100644 --- a/identity_verification/Cargo.toml +++ b/identity_verification/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_verification" -version = "1.3.1" +version = "1.4.0" authors.workspace = true edition.workspace = true homepage.workspace = true @@ -10,9 +10,9 @@ rust-version.workspace = true description = "Verification data types and functionality for identity.rs" [dependencies] -identity_core = { version = "=1.3.1", path = "./../identity_core" } -identity_did = { version = "=1.3.1", path = "./../identity_did", default-features = false } -identity_jose = { version = "=1.3.1", path = "./../identity_jose", default-features = false } +identity_core = { version = "=1.4.0", path = "./../identity_core" } +identity_did = { version = "=1.4.0", path = "./../identity_did", default-features = false } +identity_jose = { version = "=1.4.0", path = "./../identity_jose", default-features = false } serde.workspace = true serde_json.workspace = true strum.workspace = true From 2b20378af2dda33f315f39421296c48752077cca Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:34:34 +0200 Subject: [PATCH 3/6] Release wasm-v1.4.0 (#1412) --- bindings/wasm/CHANGELOG.md | 12 +++++++++++- bindings/wasm/Cargo.toml | 2 +- bindings/wasm/package-lock.json | 4 ++-- bindings/wasm/package.json | 2 +- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/bindings/wasm/CHANGELOG.md b/bindings/wasm/CHANGELOG.md index cd49c3874f..55ab6c5e4e 100644 --- a/bindings/wasm/CHANGELOG.md +++ b/bindings/wasm/CHANGELOG.md @@ -1,6 +1,16 @@ # Changelog -## [wasm-v1.3.1](https://github.com/iotaledger/identity.rs/tree/wasm-v1.3.1) (2024-06-27) +## [wasm-v1.4.0](https://github.com/iotaledger/identity.rs/tree/wasm-v1.4.0) (2024-09-23) + +[Full Changelog](https://github.com/iotaledger/identity.rs/compare/wasm-v1.3.1...wasm-v1.4.0) + +### Added + +- Add support for `did:jwk` resolution [\#1404](https://github.com/iotaledger/identity.rs/pull/1404) +- Linked Verifiable Presentations [\#1398](https://github.com/iotaledger/identity.rs/pull/1398) +- Add WASM bindings for EcDSA JWS Verifier [\#1396](https://github.com/iotaledger/identity.rs/pull/1396) + +## [wasm-v1.3.1](https://github.com/iotaledger/identity.rs/tree/wasm-v1.3.1) (2024-06-28) [Full Changelog](https://github.com/iotaledger/identity.rs/compare/wasm-v1.3.0...wasm-v1.3.1) diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index b3017d8dc8..8406b386b2 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identity_wasm" -version = "1.3.1" +version = "1.4.0" authors = ["IOTA Stiftung"] edition = "2021" homepage = "https://www.iota.org" diff --git a/bindings/wasm/package-lock.json b/bindings/wasm/package-lock.json index f70949c608..c6afb8ec91 100644 --- a/bindings/wasm/package-lock.json +++ b/bindings/wasm/package-lock.json @@ -1,12 +1,12 @@ { "name": "@iota/identity-wasm", - "version": "1.3.1", + "version": "1.4.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@iota/identity-wasm", - "version": "1.3.1", + "version": "1.4.0", "license": "Apache-2.0", "dependencies": { "@noble/ed25519": "^1.7.3", diff --git a/bindings/wasm/package.json b/bindings/wasm/package.json index 085abe22a7..1673750e23 100644 --- a/bindings/wasm/package.json +++ b/bindings/wasm/package.json @@ -1,6 +1,6 @@ { "name": "@iota/identity-wasm", - "version": "1.3.1", + "version": "1.4.0", "description": "WASM bindings for IOTA Identity - A Self Sovereign Identity Framework implementing the DID and VC standards from W3C. To be used in Javascript/Typescript", "repository": { "type": "git", From 6f83696140ef2418dcf3c1db4b03e2428cc5952c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Tue, 24 Sep 2024 17:58:42 +0200 Subject: [PATCH 4/6] fix crate version in readme (#1414) --- README.md | 4 ++-- identity_iota/README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e8001e6788..ef2d8cb15e 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ If you want to include IOTA Identity in your project, simply add it as a depende ```toml [dependencies] -identity_iota = { version = "1.3.1" } +identity_iota = { version = "1.4.0" } ``` To try out the [examples](https://github.com/iotaledger/identity.rs/blob/HEAD/examples), you can also do this: @@ -88,7 +88,7 @@ version = "1.0.0" edition = "2021" [dependencies] -identity_iota = { version = "1.3.1", features = ["memstore"] } +identity_iota = { version = "1.4.0", features = ["memstore"] } iota-sdk = { version = "1.0.2", default-features = true, features = ["tls", "client", "stronghold"] } tokio = { version = "1", features = ["full"] } anyhow = "1.0.62" diff --git a/identity_iota/README.md b/identity_iota/README.md index e8001e6788..ef2d8cb15e 100644 --- a/identity_iota/README.md +++ b/identity_iota/README.md @@ -54,7 +54,7 @@ If you want to include IOTA Identity in your project, simply add it as a depende ```toml [dependencies] -identity_iota = { version = "1.3.1" } +identity_iota = { version = "1.4.0" } ``` To try out the [examples](https://github.com/iotaledger/identity.rs/blob/HEAD/examples), you can also do this: @@ -88,7 +88,7 @@ version = "1.0.0" edition = "2021" [dependencies] -identity_iota = { version = "1.3.1", features = ["memstore"] } +identity_iota = { version = "1.4.0", features = ["memstore"] } iota-sdk = { version = "1.0.2", default-features = true, features = ["tls", "client", "stronghold"] } tokio = { version = "1", features = ["full"] } anyhow = "1.0.62" From e8df2994a7e225e139d48e1398fc554f9d484d32 Mon Sep 17 00:00:00 2001 From: Yasir Date: Wed, 13 Nov 2024 16:07:34 +0300 Subject: [PATCH 5/6] fix: serialization of status list (#1423) * Add serialization for usize as string in StatusList2021Entry * Add test for statusListIndex serialization in StatusList2021Entry * chore: downgrade rust version for wasm crate * chore: fmt --- bindings/wasm/rust-toolchain.toml | 3 ++- .../src/revocation/status_list_2021/entry.rs | 22 ++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/bindings/wasm/rust-toolchain.toml b/bindings/wasm/rust-toolchain.toml index 825d39b571..eb46cc977d 100644 --- a/bindings/wasm/rust-toolchain.toml +++ b/bindings/wasm/rust-toolchain.toml @@ -1,5 +1,6 @@ [toolchain] -channel = "stable" +# @itsyaasir - Update to latest stable version when wasm-bindgen is updated +channel = "1.81" components = ["rustfmt"] targets = ["wasm32-unknown-unknown"] profile = "minimal" diff --git a/identity_credential/src/revocation/status_list_2021/entry.rs b/identity_credential/src/revocation/status_list_2021/entry.rs index 7eecf2f28e..92415d06b7 100644 --- a/identity_credential/src/revocation/status_list_2021/entry.rs +++ b/identity_credential/src/revocation/status_list_2021/entry.rs @@ -37,6 +37,14 @@ where .map(ToOwned::to_owned) } +/// Serialize usize as string. +fn serialize_number_as_string(value: &usize, serializer: S) -> Result +where + S: serde::Serializer, +{ + serializer.serialize_str(&value.to_string()) +} + /// [StatusList2021Entry](https://www.w3.org/TR/2023/WD-vc-status-list-20230427/#statuslist2021entry) implementation. #[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)] #[serde(rename_all = "camelCase")] @@ -45,7 +53,10 @@ pub struct StatusList2021Entry { #[serde(rename = "type", deserialize_with = "deserialize_status_entry_type")] type_: String, status_purpose: StatusPurpose, - #[serde(deserialize_with = "serde_aux::prelude::deserialize_number_from_string")] + #[serde( + deserialize_with = "serde_aux::prelude::deserialize_number_from_string", + serialize_with = "serialize_number_as_string" + )] status_list_index: usize, status_list_credential: Url, } @@ -142,4 +153,13 @@ mod tests { }); serde_json::from_value::(status).expect("wrong type"); } + + #[test] + fn test_status_list_index_serialization() { + let base_url = Url::parse("https://example.com/credentials/status/3").unwrap(); + + let entry1 = StatusList2021Entry::new(base_url.clone(), StatusPurpose::Revocation, 94567, None); + let json1 = serde_json::to_value(&entry1).unwrap(); + assert_eq!(json1["statusListIndex"], "94567"); + } } From 9db4fa62bed23300b75b283d0e1b4f12643511d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eike=20Ha=C3=9F?= Date: Fri, 6 Dec 2024 13:03:26 +0100 Subject: [PATCH 6/6] Add Rebased banner for Stardust (#1486) --- README.md | 3 +++ identity_iota/README.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/README.md b/README.md index ef2d8cb15e..773cae64f9 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ --- +> [!NOTE] +> This version of the library is compatible with IOTA Stardust networks, for a version of the library compatible with IOTA Rebased networks check [here](https://github.com/iotaledger/identity.rs/tree/feat/identity-rebased-alpha/) + ## Introduction IOTA Identity is a [Rust](https://www.rust-lang.org/) implementation of decentralized digital identity, also known as Self-Sovereign Identity (SSI). It implements the W3C [Decentralized Identifiers (DID)](https://www.w3.org/TR/did-core/) and [Verifiable Credentials](https://www.w3.org/TR/vc-data-model/) specifications. This library can be used to create, resolve and authenticate digital identities and to create verifiable credentials and presentations in order to share information in a verifiable manner and establish trust in the digital world. It does so while supporting secure storage of cryptographic keys, which can be implemented for your preferred key management system. Many of the individual libraries (Rust crates) are agnostic over the concrete DID method, with the exception of some libraries dedicated to implement the [IOTA DID method](https://wiki.iota.org/identity.rs/specs/did/iota_did_method_spec/), which is an implementation of decentralized digital identity on the IOTA and Shimmer networks. Written in stable Rust, IOTA Identity has strong guarantees of memory safety and process integrity while maintaining exceptional performance. diff --git a/identity_iota/README.md b/identity_iota/README.md index ef2d8cb15e..773cae64f9 100644 --- a/identity_iota/README.md +++ b/identity_iota/README.md @@ -22,6 +22,9 @@ --- +> [!NOTE] +> This version of the library is compatible with IOTA Stardust networks, for a version of the library compatible with IOTA Rebased networks check [here](https://github.com/iotaledger/identity.rs/tree/feat/identity-rebased-alpha/) + ## Introduction IOTA Identity is a [Rust](https://www.rust-lang.org/) implementation of decentralized digital identity, also known as Self-Sovereign Identity (SSI). It implements the W3C [Decentralized Identifiers (DID)](https://www.w3.org/TR/did-core/) and [Verifiable Credentials](https://www.w3.org/TR/vc-data-model/) specifications. This library can be used to create, resolve and authenticate digital identities and to create verifiable credentials and presentations in order to share information in a verifiable manner and establish trust in the digital world. It does so while supporting secure storage of cryptographic keys, which can be implemented for your preferred key management system. Many of the individual libraries (Rust crates) are agnostic over the concrete DID method, with the exception of some libraries dedicated to implement the [IOTA DID method](https://wiki.iota.org/identity.rs/specs/did/iota_did_method_spec/), which is an implementation of decentralized digital identity on the IOTA and Shimmer networks. Written in stable Rust, IOTA Identity has strong guarantees of memory safety and process integrity while maintaining exceptional performance.