diff --git a/Cargo.toml b/Cargo.toml index 0963749..f84ec68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ reqwest = { version = "0.12.5", features = ["rustls-tls"] } serde = "1.0.188" serde_json = "1.0.107" serde_urlencoded = "0.7.1" -ssi = { version = "0.10", features = ["secp256r1"] } +ssi = { version = "0.10.1", features = ["secp256r1"] } tokio = "1.32.0" tracing = "0.1.37" url = { version = "2.4.1", features = ["serde"] } diff --git a/src/core/authorization_request/mod.rs b/src/core/authorization_request/mod.rs index fa77ae9..68f4cf8 100644 --- a/src/core/authorization_request/mod.rs +++ b/src/core/authorization_request/mod.rs @@ -1,6 +1,7 @@ use std::ops::{Deref, DerefMut}; use anyhow::{anyhow, bail, Context, Error, Result}; +use parameters::ClientMetadata; use serde::{Deserialize, Serialize}; use serde_json::Value as Json; use url::Url; @@ -16,6 +17,7 @@ use self::{ }; use super::{ + metadata::parameters::verifier::VpFormats, object::{ParsingErrorContext, UntypedObject}, util::{base_request, AsyncHttpClient}, }; @@ -263,6 +265,21 @@ impl AuthorizationRequestObject { pub fn nonce(&self) -> &Nonce { &self.7 } + + /// Return the `client_metadata` field from the authorization request. + pub fn client_metadata(&self) -> Result { + self.0 + .get() + .ok_or(anyhow!("missing `client_metadata` object"))? + } + + /// Return the `VpFormats` from the `client_metadata` field. + pub fn vp_formats(&self) -> Result { + self.client_metadata()? + .0 + .get() + .ok_or(anyhow!("missing vp_formats"))? + } } impl From for UntypedObject { diff --git a/src/core/authorization_request/verification/did.rs b/src/core/authorization_request/verification/did.rs index cd69956..42fba26 100644 --- a/src/core/authorization_request/verification/did.rs +++ b/src/core/authorization_request/verification/did.rs @@ -40,6 +40,14 @@ pub async fn verify_with_resolver( bail!("request was signed with unsupported algorithm: {alg}") } + // This bypass is for unencoded JWT requests, but we will need to change this later + // so that trust is preserved when receiving unencoded requests + // NOTE: This requires that `Algorithm::None` is permitted in the wallet metadata + // Otherwise, this function will error in the previous assertion. + if alg.contains("none") { + return Ok(()); + } + let Json::String(kid) = headers .remove("kid") .context("'kid' was missing from jwt headers")? diff --git a/src/core/credential_format/mod.rs b/src/core/credential_format/mod.rs index f206ae5..c9d25f1 100644 --- a/src/core/credential_format/mod.rs +++ b/src/core/credential_format/mod.rs @@ -137,6 +137,7 @@ pub enum ClaimFormatPayload { /// claim presentation algorithm types supported by a wallet. #[serde(rename = "alg_values_supported")] AlgValuesSupported(Vec), + /// This variant is primarily used for `ldp`, `ldp_vc`, `ldp_vp`, `ac_vc`, and `ac_vp` #[serde(rename = "proof_type")] ProofType(Vec), #[serde(untagged)] diff --git a/src/core/input_descriptor.rs b/src/core/input_descriptor.rs index b9c26d8..0d7c91a 100644 --- a/src/core/input_descriptor.rs +++ b/src/core/input_descriptor.rs @@ -209,6 +209,11 @@ impl Constraints { self.fields.as_ref() } + /// Returns the fields of the constraints object as a mutable reference. + pub fn fields_mut(&mut self) -> &mut Vec { + self.fields.as_mut() + } + /// Set the limit disclosure value. /// /// For all [Claims](https://identity.foundation/presentation-exchange/spec/v2.0.0/#term:claims) submitted in relation to [InputDescriptor] Objects that include a `constraints` diff --git a/src/core/metadata/parameters/verifier.rs b/src/core/metadata/parameters/verifier.rs index e66b9e0..99dc127 100644 --- a/src/core/metadata/parameters/verifier.rs +++ b/src/core/metadata/parameters/verifier.rs @@ -1,5 +1,6 @@ -use crate::core::credential_format::ClaimFormatMap; +use crate::core::metadata::ClaimFormatPayload; use crate::core::object::TypedParameter; +use crate::core::{credential_format::ClaimFormatMap, metadata::ClaimFormatDesignation}; use anyhow::{Context, Error}; use serde::{Deserialize, Serialize}; @@ -8,6 +9,34 @@ use serde_json::{Map, Value as Json}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct VpFormats(pub ClaimFormatMap); +impl VpFormats { + /// Returns a boolean to denote whether a particular pair of format and security method + /// are supported in the VP formats. A security method could be a JOSE algorithm, a COSE + /// algorithm, a Cryptosuite, etc. + /// + /// NOTE: This method is interested in the security method of the claim format + /// payload and not the claim format designation. + /// + /// For example, the security method would need to match one of the `alg` + /// values in the claim format payload. + pub fn supports_security_method( + &self, + format: &ClaimFormatDesignation, + security_method: &String, + ) -> bool { + match self.0.get(format) { + Some(ClaimFormatPayload::Alg(alg_values)) + | Some(ClaimFormatPayload::AlgValuesSupported(alg_values)) => { + alg_values.contains(security_method) + } + Some(ClaimFormatPayload::ProofType(proof_types)) => { + proof_types.contains(security_method) + } + _ => false, + } + } +} + impl TypedParameter for VpFormats { const KEY: &'static str = "vp_formats"; } diff --git a/src/core/response/parameters.rs b/src/core/response/parameters.rs index c997283..f2522d1 100644 --- a/src/core/response/parameters.rs +++ b/src/core/response/parameters.rs @@ -4,7 +4,13 @@ use crate::core::object::TypedParameter; use anyhow::Error; use serde::{Deserialize, Serialize}; use serde_json::Value as Json; -use ssi::{claims::vc, one_or_many::OneOrManyRef, prelude::AnyJsonPresentation, OneOrMany}; +use ssi::{ + claims::vc::{self, v2::SpecializedJsonCredential}, + json_ld::syntax::Object, + one_or_many::OneOrManyRef, + prelude::{AnyDataIntegrity, AnyJsonPresentation, AnySuite, DataIntegrity}, + OneOrMany, +}; #[derive(Debug, Clone)] pub struct IdToken(pub String); @@ -106,6 +112,12 @@ impl From for VpToken { } } +impl From>> for VpToken { + fn from(value: vc::v2::syntax::JsonPresentation>) -> Self { + Self(vec![value.into()]) + } +} + impl From for VpToken { fn from(value: AnyJsonPresentation) -> Self { Self(vec![value.into()]) @@ -159,6 +171,20 @@ impl From for VpTokenItem { } } +impl From for VpTokenItem { + fn from(value: AnyDataIntegrity) -> Self { + let serde_json::Value::Object(obj) = serde_json::to_value(&value) + // SAFETY: by definition a Data Integrity Object is a Json LD Node and is a JSON object. + .unwrap() + else { + // SAFETY: by definition a Data Integrity Object is a Json LD Node and is a JSON object. + unreachable!() + }; + + Self::JsonObject(obj) + } +} + impl From for VpTokenItem { fn from(value: vc::v1::syntax::JsonPresentation) -> Self { let serde_json::Value::Object(obj) = serde_json::to_value(value) @@ -187,6 +213,20 @@ impl From for VpTokenItem { } } +impl From>> for VpTokenItem { + fn from(value: vc::v2::syntax::JsonPresentation>) -> Self { + let serde_json::Value::Object(obj) = serde_json::to_value(value) + // SAFETY: by definition a VCDM2.0 presentation is a JSON object. + .unwrap() + else { + // SAFETY: by definition a VCDM2.0 presentation is a JSON object. + unreachable!() + }; + + Self::JsonObject(obj) + } +} + impl From for VpTokenItem { fn from(value: AnyJsonPresentation) -> Self { let serde_json::Value::Object(obj) = serde_json::to_value(value) @@ -200,3 +240,17 @@ impl From for VpTokenItem { Self::JsonObject(obj) } } + +impl From> for VpTokenItem { + fn from(value: DataIntegrity) -> Self { + let serde_json::Value::Object(obj) = serde_json::to_value(value) + // SAFETY: by definition a VCDM2.0 presentation is a JSON object. + .unwrap() + else { + // SAFETY: by definition a VCDM2.0 presentation is a JSON object. + unreachable!() + }; + + Self::JsonObject(obj) + } +}