Skip to content

Commit 11d0c85

Browse files
committed
fail to create presentation if missing requied KB-JWT
1 parent 1c387e7 commit 11d0c85

File tree

6 files changed

+41
-21
lines changed

6 files changed

+41
-21
lines changed

examples/sd_jwt.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
7979
.into_presentation(&hasher)?
8080
.conceal("/email")?
8181
.conceal("/nationalities/0")?
82-
.finish();
82+
.finish()?;
8383

8484
// The holder send its token to a verifier.
8585
let received_sd_jwt = presented_sd_jwt.presentation();

src/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ impl<H: Hasher> SdJwtBuilder<H> {
9797

9898
/// Sets the JWT header.
9999
/// ## Notes
100-
/// - if [`SdJwtBuilder::header`] is not called, a default below header is used: ```json { "typ": "sd-jwt", "alg":
100+
/// - if [`SdJwtBuilder::header`] is not called, the default header is used: ```json { "typ": "sd-jwt", "alg":
101101
/// "<algorithm used in SdJwtBulider::finish>" } ```
102102
/// - `alg` is always replaced with the value passed to [`SdJwtBuilder::finish`].
103103
pub fn header(mut self, header: JsonObject) -> Self {

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,7 @@ pub enum Error {
4242

4343
#[error("JWS creation failure: {0}")]
4444
JwsSignerFailure(String),
45+
46+
#[error("Missing required KB-JWT")]
47+
MissingKeyBindingJwt,
4548
}

src/key_binding_jwt_claims.rs

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,35 +60,47 @@ impl KeyBindingJwt {
6060
}
6161

6262
#[derive(Debug, Default, Clone)]
63-
pub struct KeyBindingJwtBuilder(JsonObject);
63+
pub struct KeyBindingJwtBuilder {
64+
header: JsonObject,
65+
payload: JsonObject,
66+
}
6467

6568
impl KeyBindingJwtBuilder {
6669
pub fn new() -> Self {
6770
Self::default()
6871
}
6972
pub fn from_object(object: JsonObject) -> Self {
70-
Self(object)
73+
Self {
74+
header: JsonObject::default(),
75+
payload: object,
76+
}
77+
}
78+
pub fn header(mut self, header: JsonObject) -> Self {
79+
self.header = header;
80+
self
7181
}
7282
pub fn iat(mut self, iat: i64) -> Self {
73-
self.0.insert("iat".to_string(), iat.into());
83+
self.payload.insert("iat".to_string(), iat.into());
7484
self
7585
}
7686
pub fn aud<'a, S>(mut self, aud: S) -> Self
7787
where
7888
S: Into<Cow<'a, str>>,
7989
{
80-
self.0.insert("aud".to_string(), aud.into().into_owned().into());
90+
self.payload.insert("aud".to_string(), aud.into().into_owned().into());
8191
self
8292
}
8393
pub fn nonce<'a, S>(mut self, nonce: S) -> Self
8494
where
8595
S: Into<Cow<'a, str>>,
8696
{
87-
self.0.insert("nonce".to_string(), nonce.into().into_owned().into());
97+
self
98+
.payload
99+
.insert("nonce".to_string(), nonce.into().into_owned().into());
88100
self
89101
}
90102
pub fn insert_property(mut self, name: &str, value: Value) -> Self {
91-
self.0.insert(name.to_string(), value);
103+
self.payload.insert(name.to_string(), value);
92104
self
93105
}
94106
pub async fn finish<S>(
@@ -101,7 +113,7 @@ impl KeyBindingJwtBuilder {
101113
where
102114
S: JwsSigner,
103115
{
104-
let mut claims = self.0;
116+
let mut claims = self.payload;
105117
if alg == "none" {
106118
return Err(Error::DataTypeMismatch(
107119
"A KeyBindingJwt cannot use algorithm \"none\"".to_string(),
@@ -121,12 +133,11 @@ impl KeyBindingJwtBuilder {
121133
let sd_hash = hasher.encoded_digest(&sd_jwt.to_string());
122134
claims.insert("sd_hash".to_string(), sd_hash.into());
123135

124-
let Value::Object(header) = serde_json::json!({
125-
"alg": alg,
126-
"typ": KB_JWT_HEADER_TYP,
127-
}) else {
128-
unreachable!();
129-
};
136+
let mut header = self.header;
137+
header.insert("alg".to_string(), alg.to_owned().into());
138+
header
139+
.entry("typ")
140+
.or_insert_with(|| KB_JWT_HEADER_TYP.to_owned().into());
130141

131142
// Validate claims
132143
let parsed_claims = serde_json::from_value::<KeyBindingJwtClaims>(claims.clone().into())

src/sd_jwt.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,13 @@ impl SdJwtPresentationBuilder {
267267
}
268268

269269
/// Returns the resulting [`SdJwt`] together with all removed disclosures.
270-
pub fn finish(self) -> (SdJwt, Vec<Disclosure>) {
270+
/// ## Errors
271+
/// - Fails with [`Error::MissingKeyBindingJwt`] if this [`SdJwt`] requires a key binding but none was provided.
272+
pub fn finish(self) -> Result<(SdJwt, Vec<Disclosure>)> {
273+
if self.sd_jwt.required_key_bind().is_some() && self.key_binding_jwt.is_none() {
274+
return Err(Error::MissingKeyBindingJwt);
275+
}
276+
271277
// Put everything back in its place.
272278
let SdJwtPresentationBuilder {
273279
mut sd_jwt,
@@ -281,7 +287,7 @@ impl SdJwtPresentationBuilder {
281287
let Value::Object(mut obj) = object else {
282288
unreachable!();
283289
};
284-
let Value::Array(sd) = obj.remove(DIGESTS_KEY).unwrap() else {
290+
let Value::Array(sd) = obj.remove(DIGESTS_KEY).unwrap_or(Value::Array(vec![])) else {
285291
unreachable!()
286292
};
287293
sd_jwt.jwt.claims._sd = sd
@@ -296,7 +302,7 @@ impl SdJwtPresentationBuilder {
296302
.collect();
297303
sd_jwt.jwt.claims.properties = obj;
298304

299-
(sd_jwt, removed_disclosures)
305+
Ok((sd_jwt, removed_disclosures))
300306
}
301307
}
302308

tests/api_test.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ async fn concealing_parent_also_removes_all_sub_disclosures() -> anyhow::Result<
8484
)
8585
.await;
8686

87-
let removed_disclosures = sd_jwt.into_presentation(&hasher)?.conceal("/parent")?.finish().1;
87+
let removed_disclosures = sd_jwt.into_presentation(&hasher)?.conceal("/parent")?.finish()?.1;
8888
assert_eq!(removed_disclosures.len(), 3);
8989

9090
Ok(())
@@ -102,7 +102,7 @@ async fn concealing_property_of_concealable_value_works() -> anyhow::Result<()>
102102
sd_jwt
103103
.into_presentation(&hasher)?
104104
.conceal("/parent/property2/0")?
105-
.finish();
105+
.finish()?;
106106

107107
Ok(())
108108
}
@@ -134,7 +134,7 @@ async fn sd_jwt_without_disclosures_works() -> anyhow::Result<()> {
134134
.clone()
135135
.into_presentation(&hasher)?
136136
.attach_key_binding_jwt(make_kb_jwt(&sd_jwt, &hasher).await)
137-
.finish()
137+
.finish()?
138138
.0;
139139
// Try to serialize & deserialize `with_kb`.
140140
let with_kb = {

0 commit comments

Comments
 (0)