From fcb4f1171ce8d94b064ee19c950be647a21846f1 Mon Sep 17 00:00:00 2001 From: l4rot <120567783+l4rot@users.noreply.github.com> Date: Wed, 29 Nov 2023 16:46:17 +0100 Subject: [PATCH 1/6] Update README.md (#1266) added the memstore feature to the cargo.toml, since the example won't work without that activated. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 83254d2152..564817faa8 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ version = "1.0.0" edition = "2021" [dependencies] -identity_iota = { version = "1.0.0" } +identity_iota = {version = "1.0.0", features = ["memstore"]} iota-sdk = { version = "1.0.2", default-features = true, features = ["tls", "client", "stronghold"] } tokio = { version = "1", features = ["full"] } ``` From 38498835989d1610e0224f5a5c8473433b9123df Mon Sep 17 00:00:00 2001 From: Enrico Marconi Date: Wed, 29 Nov 2023 11:17:00 +0100 Subject: [PATCH 2/6] Validate domain-linkage URL making sure they only include an origin --- .../domain_linkage_configuration.rs | 3 +++ .../domain_linkage_credential_builder.rs | 17 +++++++++++++++++ .../domain_linkage/domain_linkage_validator.rs | 6 ++++++ 3 files changed, 26 insertions(+) diff --git a/identity_credential/src/domain_linkage/domain_linkage_configuration.rs b/identity_credential/src/domain_linkage/domain_linkage_configuration.rs index 2e4eaf3493..15637df441 100644 --- a/identity_credential/src/domain_linkage/domain_linkage_configuration.rs +++ b/identity_credential/src/domain_linkage/domain_linkage_configuration.rs @@ -128,6 +128,9 @@ mod __fetch_configuration { if domain.scheme() != "https" { return Err(DomainLinkageError("domain` does not use `https` protocol".into())); } + if domain.path() != "/" || domain.query().is_some() || domain.fragment().is_some() { + return Err(DomainLinkageError("domain mut not include any path, query of fragment".into())); + } domain.set_path(".well-known/did-configuration.json"); let client: Client = reqwest::ClientBuilder::new() diff --git a/identity_credential/src/domain_linkage/domain_linkage_credential_builder.rs b/identity_credential/src/domain_linkage/domain_linkage_credential_builder.rs index 96e3323718..2ae7db01af 100644 --- a/identity_credential/src/domain_linkage/domain_linkage_credential_builder.rs +++ b/identity_credential/src/domain_linkage/domain_linkage_credential_builder.rs @@ -74,6 +74,11 @@ impl DomainLinkageCredentialBuilder { "origin must be a domain with http(s) scheme".into(), )); } + if origin.path() != "/" || origin.query().is_some() || origin.fragment().is_some() { + return Err(Error::DomainLinkageError( + "origin must not contain any path, query or fragment".into() + )) + } let mut properties: Object = Object::new(); properties.insert("origin".into(), origin.into_string().into()); @@ -139,6 +144,18 @@ mod tests { .unwrap_err(); assert!(matches!(err, Error::DomainLinkageError(_))); } + #[test] + fn test_builder_origin_is_a_url() { + let issuer: CoreDID = "did:example:issuer".parse().unwrap(); + let err: Error = DomainLinkageCredentialBuilder::new() + .issuance_date(Timestamp::now_utc()) + .expiration_date(Timestamp::now_utc()) + .issuer(issuer) + .origin(Url::parse("https://example.com/foo?bar=420#baz").unwrap()) + .build() + .unwrap_err(); + assert!(matches!(err, Error::DomainLinkageError(_))); + } #[test] fn test_builder_no_issuer() { diff --git a/identity_credential/src/domain_linkage/domain_linkage_validator.rs b/identity_credential/src/domain_linkage/domain_linkage_validator.rs index aa2fd8e656..bacde4ef75 100644 --- a/identity_credential/src/domain_linkage/domain_linkage_validator.rs +++ b/identity_credential/src/domain_linkage/domain_linkage_validator.rs @@ -190,6 +190,12 @@ impl JwtDomainLinkageValidator { source: Some(Box::new(other_error)), }), }?; + if origin_url.path() != "/" || origin_url.query().is_some() || origin_url.fragment().is_some() { + return Err(DomainLinkageValidationError { + cause: DomainLinkageValidationErrorCause::InvalidSubjectOrigin, + source: None, + }); + } if origin_url.origin() != domain.origin() { return Err(DomainLinkageValidationError { cause: DomainLinkageValidationErrorCause::OriginMismatch, From a85f1bf5a0987b1a529480d6ee35f7659bd59b9a Mon Sep 17 00:00:00 2001 From: Enrico Marconi Date: Wed, 29 Nov 2023 14:49:43 +0100 Subject: [PATCH 3/6] review, format, clippy --- .../domain_linkage_configuration.rs | 7 ++-- .../domain_linkage_credential_builder.rs | 32 ++++++++++++------- .../domain_linkage_validator.rs | 3 +- identity_credential/src/domain_linkage/mod.rs | 1 + .../src/domain_linkage/utils.rs | 19 +++++++++++ identity_storage/src/key_storage/memstore.rs | 2 +- 6 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 identity_credential/src/domain_linkage/utils.rs diff --git a/identity_credential/src/domain_linkage/domain_linkage_configuration.rs b/identity_credential/src/domain_linkage/domain_linkage_configuration.rs index 15637df441..51d54756e2 100644 --- a/identity_credential/src/domain_linkage/domain_linkage_configuration.rs +++ b/identity_credential/src/domain_linkage/domain_linkage_configuration.rs @@ -109,6 +109,7 @@ impl DomainLinkageConfiguration { #[cfg(feature = "domain-linkage-fetch")] mod __fetch_configuration { + use crate::domain_linkage::utils::url_only_includes_origin; use crate::domain_linkage::DomainLinkageConfiguration; use crate::error::Result; use crate::Error::DomainLinkageError; @@ -128,8 +129,10 @@ mod __fetch_configuration { if domain.scheme() != "https" { return Err(DomainLinkageError("domain` does not use `https` protocol".into())); } - if domain.path() != "/" || domain.query().is_some() || domain.fragment().is_some() { - return Err(DomainLinkageError("domain mut not include any path, query of fragment".into())); + if !url_only_includes_origin(&domain) { + return Err(DomainLinkageError( + "domain must not include any path, query or fragment".into(), + )); } domain.set_path(".well-known/did-configuration.json"); diff --git a/identity_credential/src/domain_linkage/domain_linkage_credential_builder.rs b/identity_credential/src/domain_linkage/domain_linkage_credential_builder.rs index 2ae7db01af..518d79956a 100644 --- a/identity_credential/src/domain_linkage/domain_linkage_credential_builder.rs +++ b/identity_credential/src/domain_linkage/domain_linkage_credential_builder.rs @@ -14,6 +14,8 @@ use identity_core::common::Url; use identity_did::CoreDID; use identity_did::DID; +use super::utils::url_only_includes_origin; + /// Convenient builder to create a spec compliant Domain Linkage Credential. /// /// See: @@ -74,10 +76,10 @@ impl DomainLinkageCredentialBuilder { "origin must be a domain with http(s) scheme".into(), )); } - if origin.path() != "/" || origin.query().is_some() || origin.fragment().is_some() { + if !url_only_includes_origin(&origin) { return Err(Error::DomainLinkageError( - "origin must not contain any path, query or fragment".into() - )) + "origin must not contain any path, query or fragment".into(), + )); } let mut properties: Object = Object::new(); @@ -146,15 +148,23 @@ mod tests { } #[test] fn test_builder_origin_is_a_url() { + let urls = [ + "https://example.com/foo?bar=420#baz", + "https://example.com/?bar=420", + "https://example.com/#baz", + "https://example.com/?bar=420#baz", + ]; let issuer: CoreDID = "did:example:issuer".parse().unwrap(); - let err: Error = DomainLinkageCredentialBuilder::new() - .issuance_date(Timestamp::now_utc()) - .expiration_date(Timestamp::now_utc()) - .issuer(issuer) - .origin(Url::parse("https://example.com/foo?bar=420#baz").unwrap()) - .build() - .unwrap_err(); - assert!(matches!(err, Error::DomainLinkageError(_))); + for url in urls { + let err: Error = DomainLinkageCredentialBuilder::new() + .issuance_date(Timestamp::now_utc()) + .expiration_date(Timestamp::now_utc()) + .issuer(issuer.clone()) + .origin(Url::parse(url).unwrap()) + .build() + .unwrap_err(); + assert!(matches!(err, Error::DomainLinkageError(_))); + } } #[test] diff --git a/identity_credential/src/domain_linkage/domain_linkage_validator.rs b/identity_credential/src/domain_linkage/domain_linkage_validator.rs index bacde4ef75..169701b9fe 100644 --- a/identity_credential/src/domain_linkage/domain_linkage_validator.rs +++ b/identity_credential/src/domain_linkage/domain_linkage_validator.rs @@ -17,6 +17,7 @@ use identity_verification::jws::JwsVerifier; use crate::validator::DecodedJwtCredential; +use super::utils::url_only_includes_origin; use super::DomainLinkageValidationResult; /// A validator for a Domain Linkage Configuration and Credentials. @@ -190,7 +191,7 @@ impl JwtDomainLinkageValidator { source: Some(Box::new(other_error)), }), }?; - if origin_url.path() != "/" || origin_url.query().is_some() || origin_url.fragment().is_some() { + if !url_only_includes_origin(&origin_url) { return Err(DomainLinkageValidationError { cause: DomainLinkageValidationErrorCause::InvalidSubjectOrigin, source: None, diff --git a/identity_credential/src/domain_linkage/mod.rs b/identity_credential/src/domain_linkage/mod.rs index 7595641990..8f31ddfa7f 100644 --- a/identity_credential/src/domain_linkage/mod.rs +++ b/identity_credential/src/domain_linkage/mod.rs @@ -7,6 +7,7 @@ mod domain_linkage_configuration; mod domain_linkage_credential_builder; mod domain_linkage_validator; mod error; +mod utils; pub use self::domain_linkage_configuration::*; pub use self::domain_linkage_credential_builder::*; diff --git a/identity_credential/src/domain_linkage/utils.rs b/identity_credential/src/domain_linkage/utils.rs new file mode 100644 index 0000000000..d8fd8944e7 --- /dev/null +++ b/identity_credential/src/domain_linkage/utils.rs @@ -0,0 +1,19 @@ +use identity_core::common::Url; + +pub(crate) fn url_only_includes_origin(url: &Url) -> bool { + url.path() == "/" && url.query().is_none() && url.fragment().is_none() +} + +#[cfg(test)] +mod tests { + use super::url_only_includes_origin; + use identity_core::common::Url; + + #[test] + fn empty_path_and_root_are_valid_origins() { + let urls = ["https://example.com", "https://example.com/"]; + for url in urls { + assert!(url_only_includes_origin(&Url::parse(url).unwrap())); + } + } +} diff --git a/identity_storage/src/key_storage/memstore.rs b/identity_storage/src/key_storage/memstore.rs index 7dd92fc3c2..f101af4759 100644 --- a/identity_storage/src/key_storage/memstore.rs +++ b/identity_storage/src/key_storage/memstore.rs @@ -186,7 +186,7 @@ enum MemStoreKeyType { } impl JwkMemStore { - const ED25519_KEY_TYPE_STR: &str = "Ed25519"; + const ED25519_KEY_TYPE_STR: &'static str = "Ed25519"; /// The Ed25519 key type. pub const ED25519_KEY_TYPE: KeyType = KeyType::from_static_str(Self::ED25519_KEY_TYPE_STR); } From 0951252ff61b38d313942b2684dc30526ffe4c6a Mon Sep 17 00:00:00 2001 From: Enrico Marconi <31142849+UMR1352@users.noreply.github.com> Date: Thu, 30 Nov 2023 08:51:03 +0100 Subject: [PATCH 4/6] Add license to identity_credential/src/domain_linkage/utils.rs Co-authored-by: Abdulrahim Al Methiab <31316147+abdulmth@users.noreply.github.com> --- identity_credential/src/domain_linkage/utils.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/identity_credential/src/domain_linkage/utils.rs b/identity_credential/src/domain_linkage/utils.rs index d8fd8944e7..4f6c4d4830 100644 --- a/identity_credential/src/domain_linkage/utils.rs +++ b/identity_credential/src/domain_linkage/utils.rs @@ -1,3 +1,6 @@ +// Copyright 2020-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use identity_core::common::Url; pub(crate) fn url_only_includes_origin(url: &Url) -> bool { From 1d970c9a50e7262738065f670cc831c7d8006cfe Mon Sep 17 00:00:00 2001 From: Enrico Marconi Date: Thu, 30 Nov 2023 09:00:38 +0100 Subject: [PATCH 5/6] Add origin validation to credential's service --- .../src/credential/linked_domain_service.rs | 11 +++++++++++ identity_credential/src/domain_linkage/mod.rs | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/identity_credential/src/credential/linked_domain_service.rs b/identity_credential/src/credential/linked_domain_service.rs index cf78f9b333..b976825512 100644 --- a/identity_credential/src/credential/linked_domain_service.rs +++ b/identity_credential/src/credential/linked_domain_service.rs @@ -10,6 +10,7 @@ use identity_document::service::ServiceBuilder; use identity_document::service::ServiceEndpoint; use indexmap::map::IndexMap; +use crate::domain_linkage::utils::url_only_includes_origin; use crate::error::Result; use crate::Error; use crate::Error::DomainLinkageError; @@ -97,6 +98,11 @@ impl LinkedDomainService { if endpoint.scheme() != "https" { Err(DomainLinkageError("domain does not include `https` scheme".into()))?; } + if !url_only_includes_origin(endpoint) { + Err(DomainLinkageError( + "domain must not contain any path, query or fragment".into(), + ))?; + } Ok(()) } ServiceEndpoint::Set(_) => Err(DomainLinkageError( @@ -114,6 +120,11 @@ impl LinkedDomainService { if origin.scheme() != "https" { return Err(DomainLinkageError("domain does not include `https` scheme".into())); } + if !url_only_includes_origin(origin) { + Err(DomainLinkageError( + "domain must not contain any path, query or fragment".into(), + ))?; + } } Ok(()) } diff --git a/identity_credential/src/domain_linkage/mod.rs b/identity_credential/src/domain_linkage/mod.rs index 8f31ddfa7f..6451b938fe 100644 --- a/identity_credential/src/domain_linkage/mod.rs +++ b/identity_credential/src/domain_linkage/mod.rs @@ -7,7 +7,7 @@ mod domain_linkage_configuration; mod domain_linkage_credential_builder; mod domain_linkage_validator; mod error; -mod utils; +pub(crate) mod utils; pub use self::domain_linkage_configuration::*; pub use self::domain_linkage_credential_builder::*; From e2953ba1a5cbd8c7c79987ca03fc2450aa3cf96a Mon Sep 17 00:00:00 2001 From: Enrico Marconi Date: Thu, 30 Nov 2023 12:34:40 +0100 Subject: [PATCH 6/6] Fix credential crate's features shenanigans --- identity_credential/src/credential/linked_domain_service.rs | 2 +- .../src/domain_linkage/domain_linkage_configuration.rs | 2 +- .../src/domain_linkage/domain_linkage_credential_builder.rs | 2 +- .../src/domain_linkage/domain_linkage_validator.rs | 2 +- identity_credential/src/domain_linkage/mod.rs | 1 - identity_credential/src/lib.rs | 1 + identity_credential/src/{domain_linkage => }/utils.rs | 0 7 files changed, 5 insertions(+), 5 deletions(-) rename identity_credential/src/{domain_linkage => }/utils.rs (100%) diff --git a/identity_credential/src/credential/linked_domain_service.rs b/identity_credential/src/credential/linked_domain_service.rs index b976825512..c6efbae255 100644 --- a/identity_credential/src/credential/linked_domain_service.rs +++ b/identity_credential/src/credential/linked_domain_service.rs @@ -10,8 +10,8 @@ use identity_document::service::ServiceBuilder; use identity_document::service::ServiceEndpoint; use indexmap::map::IndexMap; -use crate::domain_linkage::utils::url_only_includes_origin; use crate::error::Result; +use crate::utils::url_only_includes_origin; use crate::Error; use crate::Error::DomainLinkageError; diff --git a/identity_credential/src/domain_linkage/domain_linkage_configuration.rs b/identity_credential/src/domain_linkage/domain_linkage_configuration.rs index 51d54756e2..667c70276d 100644 --- a/identity_credential/src/domain_linkage/domain_linkage_configuration.rs +++ b/identity_credential/src/domain_linkage/domain_linkage_configuration.rs @@ -109,9 +109,9 @@ impl DomainLinkageConfiguration { #[cfg(feature = "domain-linkage-fetch")] mod __fetch_configuration { - use crate::domain_linkage::utils::url_only_includes_origin; use crate::domain_linkage::DomainLinkageConfiguration; use crate::error::Result; + use crate::utils::url_only_includes_origin; use crate::Error::DomainLinkageError; use futures::StreamExt; use identity_core::common::Url; diff --git a/identity_credential/src/domain_linkage/domain_linkage_credential_builder.rs b/identity_credential/src/domain_linkage/domain_linkage_credential_builder.rs index 518d79956a..726e9b0cb6 100644 --- a/identity_credential/src/domain_linkage/domain_linkage_credential_builder.rs +++ b/identity_credential/src/domain_linkage/domain_linkage_credential_builder.rs @@ -14,7 +14,7 @@ use identity_core::common::Url; use identity_did::CoreDID; use identity_did::DID; -use super::utils::url_only_includes_origin; +use crate::utils::url_only_includes_origin; /// Convenient builder to create a spec compliant Domain Linkage Credential. /// diff --git a/identity_credential/src/domain_linkage/domain_linkage_validator.rs b/identity_credential/src/domain_linkage/domain_linkage_validator.rs index 169701b9fe..24969c1c65 100644 --- a/identity_credential/src/domain_linkage/domain_linkage_validator.rs +++ b/identity_credential/src/domain_linkage/domain_linkage_validator.rs @@ -17,8 +17,8 @@ use identity_verification::jws::JwsVerifier; use crate::validator::DecodedJwtCredential; -use super::utils::url_only_includes_origin; use super::DomainLinkageValidationResult; +use crate::utils::url_only_includes_origin; /// A validator for a Domain Linkage Configuration and Credentials. diff --git a/identity_credential/src/domain_linkage/mod.rs b/identity_credential/src/domain_linkage/mod.rs index 6451b938fe..7595641990 100644 --- a/identity_credential/src/domain_linkage/mod.rs +++ b/identity_credential/src/domain_linkage/mod.rs @@ -7,7 +7,6 @@ mod domain_linkage_configuration; mod domain_linkage_credential_builder; mod domain_linkage_validator; mod error; -pub(crate) mod utils; pub use self::domain_linkage_configuration::*; pub use self::domain_linkage_credential_builder::*; diff --git a/identity_credential/src/lib.rs b/identity_credential/src/lib.rs index 79f85044a8..1901dd0ca5 100644 --- a/identity_credential/src/lib.rs +++ b/identity_credential/src/lib.rs @@ -23,6 +23,7 @@ pub mod error; pub mod presentation; #[cfg(feature = "revocation-bitmap")] pub mod revocation; +mod utils; #[cfg(feature = "validator")] pub mod validator; diff --git a/identity_credential/src/domain_linkage/utils.rs b/identity_credential/src/utils.rs similarity index 100% rename from identity_credential/src/domain_linkage/utils.rs rename to identity_credential/src/utils.rs