Skip to content

Commit 89d82ee

Browse files
authored
Add x509_hash client (#72)
1 parent 6127287 commit 89d82ee

File tree

1 file changed

+64
-0
lines changed

1 file changed

+64
-0
lines changed

src/verifier/client.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use anyhow::{bail, Context as _, Result};
44
use async_trait::async_trait;
55
use base64::prelude::*;
66
use serde_json::{json, Value as Json};
7+
use sha2::{Digest, Sha256};
78
use ssi::jwk::JWKResolver;
89

910
use 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]
127158
impl 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+
186250
async fn make_jwt<S: RequestSigner + ?Sized>(
187251
header: Json,
188252
body: &AuthorizationRequestObject,

0 commit comments

Comments
 (0)