@@ -4,6 +4,7 @@ use anyhow::{bail, Context as _, Result};
44use async_trait:: async_trait;
55use base64:: prelude:: * ;
66use serde_json:: { json, Value as Json } ;
7+ use sha2:: { Digest , Sha256 } ;
78use ssi:: jwk:: JWKResolver ;
89
910use tracing:: debug;
@@ -123,6 +124,36 @@ impl X509SanDnsClient {
123124 }
124125}
125126
127+ /// A [Client] with the `x509_hash` Client Identifier.
128+ /// See: Section 5.9.3
129+ #[ derive( Debug , Clone ) ]
130+ pub struct X509HashClient {
131+ id : ClientId ,
132+ x5c : Vec < Certificate > ,
133+ signer : Arc < dyn RequestSigner < Error = anyhow:: Error > + Send + Sync > ,
134+ }
135+
136+ impl X509HashClient {
137+ pub fn new (
138+ x5c : Vec < Certificate > ,
139+ signer : Arc < dyn RequestSigner < Error = anyhow:: Error > + Send + Sync > ,
140+ ) -> Result < Self > {
141+ let leaf = & x5c[ 0 ] ;
142+ let leaf_der = leaf
143+ . to_der ( )
144+ . context ( "failed to DER-encode leaf certificate" ) ?;
145+ let hash = BASE64_URL_SAFE_NO_PAD . encode ( Sha256 :: digest ( & leaf_der) ) ;
146+ // Per OID4VP v1.0 Section 5.9.3, client_id for x509_hash prefix
147+ // must be "x509_hash:<base64url SHA-256 hash of DER-encoded leaf cert>"
148+ let prefixed_id = format ! ( "{}:{}" , ClientIdScheme :: X509_HASH , hash) ;
149+ Ok ( Self {
150+ id : ClientId ( prefixed_id) ,
151+ x5c,
152+ signer,
153+ } )
154+ }
155+ }
156+
126157#[ async_trait]
127158impl Client for DIDClient {
128159 fn id ( & self ) -> & ClientId {
@@ -183,6 +214,39 @@ impl Client for X509SanDnsClient {
183214 }
184215}
185216
217+ #[ async_trait]
218+ impl Client for X509HashClient {
219+ fn id ( & self ) -> & ClientId {
220+ & self . id
221+ }
222+
223+ fn prefix ( & self ) -> ClientIdScheme {
224+ ClientIdScheme ( ClientIdScheme :: X509_HASH . to_string ( ) )
225+ }
226+
227+ async fn generate_request_object_jwt (
228+ & self ,
229+ body : & AuthorizationRequestObject ,
230+ ) -> Result < String > {
231+ let algorithm = self
232+ . signer
233+ . alg ( )
234+ . context ( "failed to retrieve signing algorithm" ) ?;
235+ let x5c: Vec < String > = self
236+ . x5c
237+ . iter ( )
238+ . map ( |x509| x509. to_der ( ) )
239+ . map ( |der| Ok ( BASE64_STANDARD . encode ( der?) ) )
240+ . collect :: < Result < _ > > ( ) ?;
241+ let header = json ! ( {
242+ "alg" : algorithm,
243+ "x5c" : x5c,
244+ "typ" : "oauth-authz-req+jwt"
245+ } ) ;
246+ make_jwt ( header, body, self . signer . as_ref ( ) ) . await
247+ }
248+ }
249+
186250async fn make_jwt < S : RequestSigner + ?Sized > (
187251 header : Json ,
188252 body : & AuthorizationRequestObject ,
0 commit comments