Skip to content

Commit

Permalink
fail to create presentation if missing requied KB-JWT
Browse files Browse the repository at this point in the history
  • Loading branch information
UMR1352 committed Sep 26, 2024
1 parent 1c387e7 commit 11d0c85
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 21 deletions.
2 changes: 1 addition & 1 deletion examples/sd_jwt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
.into_presentation(&hasher)?
.conceal("/email")?
.conceal("/nationalities/0")?
.finish();
.finish()?;

// The holder send its token to a verifier.
let received_sd_jwt = presented_sd_jwt.presentation();
Expand Down
2 changes: 1 addition & 1 deletion src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ impl<H: Hasher> SdJwtBuilder<H> {

/// Sets the JWT header.
/// ## Notes
/// - if [`SdJwtBuilder::header`] is not called, a default below header is used: ```json { "typ": "sd-jwt", "alg":
/// - if [`SdJwtBuilder::header`] is not called, the default header is used: ```json { "typ": "sd-jwt", "alg":
/// "<algorithm used in SdJwtBulider::finish>" } ```
/// - `alg` is always replaced with the value passed to [`SdJwtBuilder::finish`].
pub fn header(mut self, header: JsonObject) -> Self {
Expand Down
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,7 @@ pub enum Error {

#[error("JWS creation failure: {0}")]
JwsSignerFailure(String),

#[error("Missing required KB-JWT")]
MissingKeyBindingJwt,
}
37 changes: 24 additions & 13 deletions src/key_binding_jwt_claims.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,35 +60,47 @@ impl KeyBindingJwt {
}

#[derive(Debug, Default, Clone)]
pub struct KeyBindingJwtBuilder(JsonObject);
pub struct KeyBindingJwtBuilder {
header: JsonObject,
payload: JsonObject,
}

impl KeyBindingJwtBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn from_object(object: JsonObject) -> Self {
Self(object)
Self {
header: JsonObject::default(),
payload: object,
}
}
pub fn header(mut self, header: JsonObject) -> Self {
self.header = header;
self
}
pub fn iat(mut self, iat: i64) -> Self {
self.0.insert("iat".to_string(), iat.into());
self.payload.insert("iat".to_string(), iat.into());
self
}
pub fn aud<'a, S>(mut self, aud: S) -> Self
where
S: Into<Cow<'a, str>>,
{
self.0.insert("aud".to_string(), aud.into().into_owned().into());
self.payload.insert("aud".to_string(), aud.into().into_owned().into());
self
}
pub fn nonce<'a, S>(mut self, nonce: S) -> Self
where
S: Into<Cow<'a, str>>,
{
self.0.insert("nonce".to_string(), nonce.into().into_owned().into());
self
.payload
.insert("nonce".to_string(), nonce.into().into_owned().into());
self
}
pub fn insert_property(mut self, name: &str, value: Value) -> Self {
self.0.insert(name.to_string(), value);
self.payload.insert(name.to_string(), value);
self
}
pub async fn finish<S>(
Expand All @@ -101,7 +113,7 @@ impl KeyBindingJwtBuilder {
where
S: JwsSigner,
{
let mut claims = self.0;
let mut claims = self.payload;
if alg == "none" {
return Err(Error::DataTypeMismatch(
"A KeyBindingJwt cannot use algorithm \"none\"".to_string(),
Expand All @@ -121,12 +133,11 @@ impl KeyBindingJwtBuilder {
let sd_hash = hasher.encoded_digest(&sd_jwt.to_string());
claims.insert("sd_hash".to_string(), sd_hash.into());

let Value::Object(header) = serde_json::json!({
"alg": alg,
"typ": KB_JWT_HEADER_TYP,
}) else {
unreachable!();
};
let mut header = self.header;
header.insert("alg".to_string(), alg.to_owned().into());
header
.entry("typ")
.or_insert_with(|| KB_JWT_HEADER_TYP.to_owned().into());

// Validate claims
let parsed_claims = serde_json::from_value::<KeyBindingJwtClaims>(claims.clone().into())
Expand Down
12 changes: 9 additions & 3 deletions src/sd_jwt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,13 @@ impl SdJwtPresentationBuilder {
}

/// Returns the resulting [`SdJwt`] together with all removed disclosures.
pub fn finish(self) -> (SdJwt, Vec<Disclosure>) {
/// ## Errors
/// - Fails with [`Error::MissingKeyBindingJwt`] if this [`SdJwt`] requires a key binding but none was provided.
pub fn finish(self) -> Result<(SdJwt, Vec<Disclosure>)> {
if self.sd_jwt.required_key_bind().is_some() && self.key_binding_jwt.is_none() {
return Err(Error::MissingKeyBindingJwt);
}

// Put everything back in its place.
let SdJwtPresentationBuilder {
mut sd_jwt,
Expand All @@ -281,7 +287,7 @@ impl SdJwtPresentationBuilder {
let Value::Object(mut obj) = object else {
unreachable!();
};
let Value::Array(sd) = obj.remove(DIGESTS_KEY).unwrap() else {
let Value::Array(sd) = obj.remove(DIGESTS_KEY).unwrap_or(Value::Array(vec![])) else {
unreachable!()
};
sd_jwt.jwt.claims._sd = sd
Expand All @@ -296,7 +302,7 @@ impl SdJwtPresentationBuilder {
.collect();
sd_jwt.jwt.claims.properties = obj;

(sd_jwt, removed_disclosures)
Ok((sd_jwt, removed_disclosures))
}
}

Expand Down
6 changes: 3 additions & 3 deletions tests/api_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ async fn concealing_parent_also_removes_all_sub_disclosures() -> anyhow::Result<
)
.await;

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

Ok(())
Expand All @@ -102,7 +102,7 @@ async fn concealing_property_of_concealable_value_works() -> anyhow::Result<()>
sd_jwt
.into_presentation(&hasher)?
.conceal("/parent/property2/0")?
.finish();
.finish()?;

Ok(())
}
Expand Down Expand Up @@ -134,7 +134,7 @@ async fn sd_jwt_without_disclosures_works() -> anyhow::Result<()> {
.clone()
.into_presentation(&hasher)?
.attach_key_binding_jwt(make_kb_jwt(&sd_jwt, &hasher).await)
.finish()
.finish()?
.0;
// Try to serialize & deserialize `with_kb`.
let with_kb = {
Expand Down

0 comments on commit 11d0c85

Please sign in to comment.