diff --git a/Cargo.toml b/Cargo.toml index f84ec68..f3369dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,9 @@ documentation = "https://docs.rs/openid4vp/" [features] default = [] +# Enables code paths that increase the chance of interoperability with other wallets and verifiers. +# This should be used with caution in production environments. +maximize_interoperability = [] [dependencies] anyhow = "1.0.75" @@ -20,8 +23,6 @@ http = "1.1.0" # This is currently used in the jwt_vp test to go from a `VeriableCredential` to an `AnyJsonCredential` type. # There may be a better way to handle this that doesn't require the `json-syntax` crate directly. json-syntax = { version = "0.12.5", features = ["serde_json"] } -# jsonpath_lib = "0.3.0" -serde_json_path = "0.7.1" jsonschema = "0.18.0" openid4vp-frontend = { version = "0.1.0", path = "openid4vp-frontend" } p256 = { version = "0.13.2", features = ["jwk"] } @@ -29,6 +30,7 @@ rand = { version = "0.8.5" } reqwest = { version = "0.12.5", features = ["rustls-tls"] } serde = "1.0.188" serde_json = "1.0.107" +serde_json_path = "0.7.1" serde_urlencoded = "0.7.1" ssi = { version = "0.10.1", features = ["secp256r1"] } tokio = "1.32.0" diff --git a/src/core/authorization_request/verification/x509_san.rs b/src/core/authorization_request/verification/x509_san.rs index 52fe3ec..8e2e124 100644 --- a/src/core/authorization_request/verification/x509_san.rs +++ b/src/core/authorization_request/verification/x509_san.rs @@ -69,6 +69,8 @@ pub fn validate( let leaf_cert = Certificate::from_der(&leaf_cert_der) .context("leaf certificate in 'x5c' was not valid DER")?; + debug!("Leaf certificate: {leaf_cert:?}"); + if !leaf_cert .tbs_certificate .filter::() @@ -82,19 +84,30 @@ pub fn validate( .flatten() .filter_map(|gn| match (gn, x509_san_variant) { (GeneralName::DnsName(uri), X509SanVariant::Dns) => Some(uri.to_string()), + (GeneralName::UniformResourceIdentifier(uri), X509SanVariant::Uri) => { + Some(uri.to_string()) + } + #[cfg(feature = "maximize_interoperability")] + (GeneralName::DnsName(uri), X509SanVariant::Uri) => Some(uri.to_string()), + #[cfg(feature = "maximize_interoperability")] + (GeneralName::UniformResourceIdentifier(uri), X509SanVariant::Dns) => Some( + Url::parse(uri.as_str()) + .map(|u| u.authority().to_string()) + .unwrap_or(uri.to_string()), + ), (gn, X509SanVariant::Dns) => { debug!("found non-DNS SAN: {gn:?}"); None } - (GeneralName::UniformResourceIdentifier(uri), X509SanVariant::Uri) => { - Some(uri.to_string()) - } (gn, X509SanVariant::Uri) => { debug!("found non-URI SAN: {gn:?}"); None } }) - .any(|uri| uri == client_id) + .any(|uri| { + debug!("comparing SAN '{uri}' to client_id '{client_id}'"); + uri == client_id + }) { bail!("client_id does not match any Subject Alternative Name") } diff --git a/src/core/presentation_definition.rs b/src/core/presentation_definition.rs index 2b9402b..df25ea4 100644 --- a/src/core/presentation_definition.rs +++ b/src/core/presentation_definition.rs @@ -7,7 +7,7 @@ use std::collections::HashSet; use anyhow::Result; use serde::{Deserialize, Serialize}; -use serde_json::Map; +use serde_json::{Map, Value as Json}; /// A presentation definition is a JSON object that describes the information a [Verifier](https://identity.foundation/presentation-exchange/spec/v2.0.0/#term:verifier) requires of a [Holder](https://identity.foundation/presentation-exchange/spec/v2.0.0/#term:holder). /// @@ -189,10 +189,7 @@ impl PresentationDefinition { /// Returns the requested fields of a given JSON-encoded credential /// that match the constraint fields of the input descriptors of the /// presentation definition. - pub fn requested_fields<'a>( - &self, - credential: &'a serde_json::Value, - ) -> Vec> { + pub fn requested_fields<'a>(&self, credential: &'a Json) -> Vec> { self.input_descriptors .iter() .flat_map(|descriptor| descriptor.requested_fields(credential)) @@ -215,7 +212,7 @@ impl PresentationDefinition { /// NOTE: this method accepts a generic serde_json::Value argument and checks whether /// the JSON value conforms to the presentation definition's input descriptor constraint /// fields. - pub fn is_credential_match(&self, credential: &serde_json::Value) -> bool { + pub fn is_credential_match(&self, credential: &Json) -> bool { self.input_descriptors() .iter() .flat_map(|descriptor| descriptor.constraints.fields()) @@ -228,7 +225,7 @@ impl PresentationDefinition { .iter() .flat_map(|path| path.query(credential)) .any(|value| { - if let serde_json::Value::Array(vals) = value { + if let Json::Array(vals) = value { if vals.iter().any(|val| validator.validate(val).is_ok()) { return true; } @@ -248,7 +245,7 @@ pub struct SubmissionRequirementObject { pub name: Option, pub purpose: Option, #[serde(flatten)] - pub property_set: Option>, + pub property_set: Option>, } #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]