diff --git a/matcher-rs/src/openid4vp.rs b/matcher-rs/src/openid4vp.rs index 06405f0..b3ec3e8 100644 --- a/matcher-rs/src/openid4vp.rs +++ b/matcher-rs/src/openid4vp.rs @@ -1,30 +1,69 @@ use crate::base64url::decode_base64url; use crate::credman::CredmanApi; +use crate::json_value::JsonValue; pub use crate::openid4vp_models::*; use crate::reporter::report_match_result; use nanoserde::DeJson; use std::borrow::Cow; +fn extract_request_str<'a>( + pr: &'a ProtocolRequest, +) -> Result<&'a str, Box> { + if let Some(data) = &pr.data { + match data { + ProtocolRequestData::String(s) => Ok(s.as_str()), + ProtocolRequestData::Object(obj) => { + if obj.request.is_empty() { + return Err("Missing 'request' field in data object".into()); + } + Ok(obj.request.as_str()) + } + } + } else if !pr.request.is_empty() { + Ok(pr.request.as_str()) + } else { + Err("Missing request data".into()) + } +} + +fn extract_multisigned_payload( + pr: &ProtocolRequest, +) -> Result> { + let json_str = extract_request_str(pr)?; + let parsed: JsonValue = DeJson::deserialize_json(json_str)?; + + let JsonValue::Object(mut map) = parsed else { + return Err("Multisigned request must be a JSON object".into()); + }; + + let payload = if let Some(req_val) = map.shift_remove("request") { + let JsonValue::Object(mut req_map) = req_val else { + return Err("Missing 'payload' in 'request' object".into()); + }; + let Some(JsonValue::String(p)) = req_map.shift_remove("payload") else { + return Err("Missing 'payload' in 'request' object".into()); + }; + p + } else { + let Some(JsonValue::String(p)) = map.shift_remove("payload") else { + return Err("Missing 'payload' field in multisigned request".into()); + }; + p + }; + + if payload.is_empty() { + return Err("Empty payload in multisigned request".into()); + } + + Ok(payload) +} + fn parse_protocol_request_data<'a>( pr: &'a ProtocolRequest, ) -> Result, Box> { if pr.protocol == "openid4vp-v1-signed" { log::debug!("Handling signed OpenID4VP request"); - let jws: &'a str = if let Some(data) = &pr.data { - match data { - ProtocolRequestData::String(s) => s, - ProtocolRequestData::Object(obj) => { - if obj.request.is_empty() { - return Err("Missing 'request' field in signed data object".into()); - } - &obj.request - } - } - } else if !pr.request.is_empty() { - &pr.request - } else { - return Err("Missing signed request data".into()); - }; + let jws = extract_request_str(pr)?; let parts: Vec<&str> = jws.split('.').collect(); if parts.len() < 2 { @@ -36,7 +75,14 @@ fn parse_protocol_request_data<'a>( return Ok(Cow::Owned(DeJson::deserialize_json(std::str::from_utf8( &decoded, )?)?)); - } + } else if pr.protocol == "openid4vp-v1-multisigned" { + log::debug!("Handling multisigned OpenID4VP request"); + let payload_str = extract_multisigned_payload(pr)?; + let decoded = decode_base64url(&payload_str)?; + return Ok(Cow::Owned(DeJson::deserialize_json(std::str::from_utf8( + &decoded, + )?)?)); + } log::debug!("Handling unsigned OpenID4VP request"); if let Some(data) = &pr.data { @@ -110,7 +156,10 @@ pub fn openid4vp_main(credman: &mut impl CredmanApi) -> Result<(), Box