Skip to content

Commit

Permalink
Merge pull request #1267 from iotaledger/task/1265
Browse files Browse the repository at this point in the history
Validate domain-linkage URL making sure they only include an origin
  • Loading branch information
UMR1352 authored Dec 4, 2023
2 parents fcb4f11 + e2953ba commit 8227410
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 1 deletion.
11 changes: 11 additions & 0 deletions identity_credential/src/credential/linked_domain_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use identity_document::service::ServiceEndpoint;
use indexmap::map::IndexMap;

use crate::error::Result;
use crate::utils::url_only_includes_origin;
use crate::Error;
use crate::Error::DomainLinkageError;

Expand Down Expand Up @@ -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(
Expand All @@ -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(())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ impl DomainLinkageConfiguration {
mod __fetch_configuration {
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;
Expand All @@ -128,6 +129,11 @@ mod __fetch_configuration {
if domain.scheme() != "https" {
return Err(DomainLinkageError("domain` does not use `https` protocol".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");

let client: Client = reqwest::ClientBuilder::new()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use identity_core::common::Url;
use identity_did::CoreDID;
use identity_did::DID;

use crate::utils::url_only_includes_origin;

/// Convenient builder to create a spec compliant Domain Linkage Credential.
///
/// See: <https://identity.foundation/.well-known/resources/did-configuration/#linked-data-proof-format>
Expand Down Expand Up @@ -74,6 +76,11 @@ impl DomainLinkageCredentialBuilder {
"origin must be a domain with http(s) scheme".into(),
));
}
if !url_only_includes_origin(&origin) {
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());
Expand Down Expand Up @@ -139,6 +146,26 @@ mod tests {
.unwrap_err();
assert!(matches!(err, Error::DomainLinkageError(_)));
}
#[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();
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]
fn test_builder_no_issuer() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use identity_verification::jws::JwsVerifier;
use crate::validator::DecodedJwtCredential;

use super::DomainLinkageValidationResult;
use crate::utils::url_only_includes_origin;

/// A validator for a Domain Linkage Configuration and Credentials.

Expand Down Expand Up @@ -190,6 +191,12 @@ impl<V: JwsVerifier> JwtDomainLinkageValidator<V> {
source: Some(Box::new(other_error)),
}),
}?;
if !url_only_includes_origin(&origin_url) {
return Err(DomainLinkageValidationError {
cause: DomainLinkageValidationErrorCause::InvalidSubjectOrigin,
source: None,
});
}
if origin_url.origin() != domain.origin() {
return Err(DomainLinkageValidationError {
cause: DomainLinkageValidationErrorCause::OriginMismatch,
Expand Down
1 change: 1 addition & 0 deletions identity_credential/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
22 changes: 22 additions & 0 deletions identity_credential/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// 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 {
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()));
}
}
}
2 changes: 1 addition & 1 deletion identity_storage/src/key_storage/memstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down

0 comments on commit 8227410

Please sign in to comment.