Skip to content

Commit

Permalink
add missing typed parameters for client metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryanmtate committed Dec 1, 2024
1 parent bd56069 commit 7318755
Show file tree
Hide file tree
Showing 13 changed files with 408 additions and 71 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ http = "1.1.0"
# 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.6.7"
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"] }
Expand All @@ -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"] }
Expand Down
17 changes: 17 additions & 0 deletions src/core/authorization_request/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -16,6 +17,7 @@ use self::{
};

use super::{
metadata::parameters::verifier::VpFormats,
object::{ParsingErrorContext, UntypedObject},
util::{base_request, AsyncHttpClient},
};
Expand Down Expand Up @@ -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<ClientMetadata> {
self.0
.get()
.ok_or(anyhow!("missing `client_metadata` object"))?
}

/// Return the `VpFormats` from the `client_metadata` field.
pub fn vp_formats(&self) -> Result<VpFormats> {
self.client_metadata()?
.0
.get()
.ok_or(anyhow!("missing vp_formats"))?
}
}

impl From<AuthorizationRequestObject> for UntypedObject {
Expand Down
138 changes: 137 additions & 1 deletion src/core/authorization_request/parameters.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
use std::{fmt, ops::Deref};

use crate::core::{
metadata::parameters::{
verifier::{JWKs, VpFormats},
wallet::{
AuthorizationEncryptedResponseAlg, AuthorizationEncryptedResponseEnc,
AuthorizationSignedResponseAlg,
},
},
object::{ParsingErrorContext, TypedParameter, UntypedObject},
presentation_definition::PresentationDefinition as PresentationDefinitionParsed,
util::{base_request, AsyncHttpClient},
};
use anyhow::{bail, Context, Error, Ok};
use anyhow::{anyhow, bail, Context, Error, Ok};
use serde::{Deserialize, Serialize};
use serde_json::Value as Json;
use ssi::JWK;
use url::Url;

use super::AuthorizationRequestObject;
Expand Down Expand Up @@ -105,6 +113,23 @@ impl fmt::Display for ClientIdScheme {
}

/// `client_metadata` field in the Authorization Request.
///
/// client_metadata: OPTIONAL. A JSON object containing the Verifier metadata values.
/// It MUST be UTF-8 encoded. The following metadata parameters MAY be used:
///
/// jwks: OPTIONAL. A JWKS as defined in [RFC7591]. It MAY contain one or more public keys, such as those used by the Wallet as an input to a key agreement that may be used for encryption of the Authorization Response (see Section 7.3), or where the Wallet will require the public key of the Verifier to generate the Verifiable Presentation. This allows the Verifier to pass ephemeral keys specific to this Authorization Request. Public keys included in this parameter MUST NOT be used to verify the signature of signed Authorization Requests.
/// vp_formats: REQUIRED when not available to the Wallet via another mechanism. As defined in Section 10.1.
/// authorization_signed_response_alg: OPTIONAL. As defined in [JARM].
/// authorization_encrypted_response_alg: OPTIONAL. As defined in [JARM].
/// authorization_encrypted_response_enc: OPTIONAL. As defined in [JARM].
/// Authoritative data the Wallet is able to obtain about the Client from other sources,
/// for example those from an OpenID Federation Entity Statement, take precedence over the
/// values passed in client_metadata. Other metadata parameters MUST be ignored unless a
/// profile of this specification explicitly defines them as usable in the client_metadata parameter.
///
///
/// See reference: https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-5.1-4.2.4
///
#[derive(Debug, Clone)]
pub struct ClientMetadata(pub UntypedObject);

Expand Down Expand Up @@ -169,6 +194,117 @@ impl ClientMetadata {
tracing::warn!("the client metadata was not passed by reference or value");
Ok(ClientMetadata(UntypedObject::default()))
}

/// OPTIONAL. A JWKS as defined in
/// [RFC7591](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#RFC7591).
///
/// It MAY contain one or more public keys, such as those used by the Wallet as an input to a
/// key agreement that may be used for encryption of the Authorization Response
/// (see [Section 7.3](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#jarm)),
/// or where the Wallet will require the public key of the Verifier to generate the Verifiable Presentation.
///
/// This allows the Verifier to pass ephemeral keys specific to this Authorization Request.
/// Public keys included in this parameter MUST NOT be used to verify the signature of signed Authorization Requests.
///
///
/// See reference: https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-5.1-4.2.2.1
///
/// The jwks_uri or jwks metadata parameters can be used by clients to register their public encryption keys.
///
/// See: https://openid.net/specs/oauth-v2-jarm-final.html#section-3-4
///
pub fn jwks(&self) -> Option<Result<JWKs, Error>> {
self.0.get()
}

/// Return the `VpFormats` from the `client_metadata` field.
///
/// vp_formats: REQUIRED when not available to the Wallet via another mechanism.
///
/// As defined in [Section 10.1](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#client_metadata_parameters).
///
/// See reference: https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-5.1-4.2.2.2
pub fn vp_formats(&self) -> Result<VpFormats, Error> {
self.0.get().ok_or(anyhow!("missing vp_formats"))?
}

/// OPTIONAL. As defined in [JARM](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#JARM).
///
/// JARM -> JWT Secured Authorization Response Mode for OAuth 2.0
///
/// The JWS [RFC7515](https://openid.net/specs/oauth-v2-jarm-final.html#RFC7515)
/// `alg` algorithm REQUIRED for signing authorization responses.
///
/// If this is specified, the response will be signed using JWS and the configured algorithm.
///
/// If unspecified, the default algorithm to use for signing authorization responses is RS256.
///
/// The algorithm none is not allowed.
///
/// A list of defined ["alg" values](https://datatracker.ietf.org/doc/html/rfc7518#section-3.1)
/// for this use can be found in the IANA "JSON Web Signature and Encryption Algorithms" registry established
/// by [JWA](https://www.rfc-editor.org/rfc/rfc7515.html#ref-JWA); the initial contents of this registry are the values
/// defined in Section 3.1 of [JWA](https://www.rfc-editor.org/rfc/rfc7515.html#ref-JWA).
///
/// See: https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-5.1-4.2.2.3
/// See: https://openid.net/specs/oauth-v2-jarm-final.html#section-3-3.2.1
/// See: https://datatracker.ietf.org/doc/html/rfc7518#section-3.1
///
pub fn authorization_signed_response_alg(
&self,
) -> Result<AuthorizationSignedResponseAlg, Error> {
self.0.get().unwrap_or(Ok(AuthorizationSignedResponseAlg(
ssi::crypto::Algorithm::RS256,
)))
}

/// OPTIONAL. As defined in [JARM](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#JARM).
///
/// JARM -> JWT Secured Authorization Response Mode for OAuth 2.0
///
/// The JWE [RFC7516](https://openid.net/specs/oauth-v2-jarm-final.html#RFC7516)
/// `alg` algorithm REQUIRED for encrypting authorization responses.
///
/// If both signing and encryption are requested, the response will be signed then encrypted,
/// with the result being a Nested JWT, as defined in JWT
/// [RFC7519](https://openid.net/specs/oauth-v2-jarm-final.html#RFC7519).
///
/// The default, if omitted, is that no encryption is performed.
///
///
/// See: https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-5.1-4.2.2.4
/// See: https://openid.net/specs/oauth-v2-jarm-final.html#section-3-3.4.1
///
pub fn authorization_encrypted_response_alg(
&self,
) -> Option<Result<AuthorizationEncryptedResponseAlg, Error>> {
self.0.get()
}

/// OPTIONAL. As defined in [JARM](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#JARM).
///
/// JARM -> JWT Secured Authorization Response Mode for OAuth 2.0
///
/// The JWE [RFC7516](https://openid.net/specs/oauth-v2-jarm-final.html#RFC7516) `enc` algorithm
/// REQUIRED for encrypting authorization responses.
///
/// If `authorization_encrypted_response_alg` is specified, the default for this value is `A128CBC-HS256`.
///
/// When `authorization_encrypted_response_enc` is included, authorization_encrypted_response_alg MUST also be provided.
///
/// See: https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-5.1-4.2.2.5
/// See: https://openid.net/specs/oauth-v2-jarm-final.html#section-3-3.6.1
///
pub fn authorization_encrypted_response_enc(
&self,
) -> Option<Result<AuthorizationEncryptedResponseEnc, Error>> {
match self.0.get() {
Some(enc) => Some(enc),
None => self
.authorization_encrypted_response_alg()
.map(|_| Ok(AuthorizationEncryptedResponseEnc("A128CBC-HS256".into()))),
}
}
}

/// `client_metadata_uri` field in the Authorization Request.
Expand Down
8 changes: 8 additions & 0 deletions src/core/authorization_request/verification/did.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")?
Expand Down
1 change: 1 addition & 0 deletions src/core/credential_format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ pub enum ClaimFormatPayload {
/// claim presentation algorithm types supported by a wallet.
#[serde(rename = "alg_values_supported")]
AlgValuesSupported(Vec<String>),
/// This variant is primarily used for `ldp`, `ldp_vc`, `ldp_vp`, `ac_vc`, and `ac_vp`
#[serde(rename = "proof_type")]
ProofType(Vec<String>),
#[serde(untagged)]
Expand Down
5 changes: 5 additions & 0 deletions src/core/input_descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ConstraintsField> {
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`
Expand Down
31 changes: 25 additions & 6 deletions src/core/metadata/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,38 @@ impl WalletMetadata {
&mut self.2
}

/// Add a client ID scheme to the list of the client ID schemes supported.
/// Add a request object signing algorithm to the list of the request object
/// signing algorithms supported.
pub fn add_request_object_signing_alg_values_supported(
&mut self,
alg: Algorithm,
) -> Result<()> {
let mut supported = self
.0
.get_or_default::<RequestObjectSigningAlgValuesSupported>()?;

// Insert the algorithm.
supported.0.push(alg.to_string());

// Insert the updated request object signing algorithms supported.
self.0.insert(supported);

Ok(())
}

/// Appends the client ID schemes to the list of the client ID schemes supported.
///
/// This method will construct a `client_id_schemes_supported` proprety in the
/// wallet metadata if none exists previously, otherwise, this method will add
/// the client ID scheme to the existing list of the client ID schemes supported.
/// This method will construct a `client_id_schemes_supported` property in the
/// wallet metadata if none exists previously, otherwise, this method will append
/// the client ID schemes to the existing list of the client ID schemes supported.
pub fn add_client_id_schemes_supported(
&mut self,
client_id_scheme: ClientIdScheme,
client_id_schemes: &[ClientIdScheme],
) -> Result<()> {
let mut supported = self.0.get_or_default::<ClientIdSchemesSupported>()?;

// Insert the scheme.
supported.0.push(client_id_scheme);
supported.0.extend_from_slice(client_id_schemes);

// Insert the updated client IDs schemes supported.
self.0.insert(supported);
Expand Down
31 changes: 30 additions & 1 deletion src/core/metadata/parameters/verifier.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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";
}
Expand Down
Loading

0 comments on commit 7318755

Please sign in to comment.