Skip to content

Commit

Permalink
fix 0 disclosures bug
Browse files Browse the repository at this point in the history
  • Loading branch information
UMR1352 committed Sep 26, 2024
1 parent 38013b0 commit f730806
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 14 deletions.
15 changes: 11 additions & 4 deletions src/key_binding_jwt_claims.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use anyhow::Context as _;
use serde::Deserialize;
use serde::Serialize;
use serde_json::Value;
use std::borrow::Cow;
use std::fmt::Display;
use std::ops::Deref;
use std::str::FromStr;
Expand Down Expand Up @@ -72,12 +73,18 @@ impl KeyBindingJwtBuilder {
self.0.insert("iat".to_string(), iat.into());
self
}
pub fn aud(mut self, aud: impl ToOwned<Owned = String>) -> Self {
self.0.insert("aud".to_string(), aud.to_owned().into());
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
}
pub fn nonce(mut self, nonce: impl ToOwned<Owned = String>) -> Self {
self.0.insert("nonce".to_string(), nonce.to_owned().into());
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
}
pub fn insert_property(mut self, name: &str, value: Value) -> Self {
Expand Down
19 changes: 9 additions & 10 deletions src/sd_jwt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ impl SdJwt {
.as_ref()
.map(ToString::to_string)
.unwrap_or_default();
format!("{}~{}~{}", self.jwt, disclosures, key_bindings)
if disclosures.is_empty() {
format!("{}~{}", self.jwt, key_bindings)
} else {
format!("{}~{}~{}", self.jwt, disclosures, key_bindings)
}
}

/// Parses an SD-JWT into its components as [`SdJwt`].
Expand All @@ -128,22 +132,17 @@ impl SdJwt {
));
}

let includes_key_binding = sd_jwt.chars().next_back().is_some_and(|char| char != '~');
if includes_key_binding && num_of_segments < 3 {
return Err(Error::DeserializationError(
"SD-JWT format is invalid, less than 3 segments with key binding jwt".to_string(),
));
}

let jwt = sd_segments.first().unwrap().parse()?;

let disclosures = sd_segments[1..num_of_segments - 1]
.iter()
.map(|s| Disclosure::parse(s))
.try_collect()?;

let key_binding_jwt = includes_key_binding
.then(|| sd_segments[num_of_segments - 1].parse())
let key_binding_jwt = sd_segments
.last()
.filter(|segment| !segment.is_empty())
.map(|segment| segment.parse())
.transpose()?;

Ok(Self {
Expand Down
44 changes: 44 additions & 0 deletions tests/api_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ use josekit::jws::JwsHeader;
use josekit::jws::HS256;
use josekit::jwt;
use josekit::jwt::JwtPayload;
use sd_jwt_payload::Hasher;
use sd_jwt_payload::JsonObject;
use sd_jwt_payload::JwsSigner;
use sd_jwt_payload::KeyBindingJwt;
use sd_jwt_payload::Sha256Hasher;
use serde_json::json;
use serde_json::Value;
Expand Down Expand Up @@ -43,6 +45,17 @@ async fn make_sd_jwt(object: Value, disclosable_values: impl IntoIterator<Item =
.unwrap()
}

async fn make_kb_jwt(sd_jwt: &SdJwt, hasher: &dyn Hasher) -> KeyBindingJwt {
let signer = HmacSignerAdapter(HS256.signer_from_bytes(HMAC_SECRET).unwrap());
KeyBindingJwt::builder()
.nonce("abcdefghi")
.aud("https://example.com")
.iat(1458304832)
.finish(sd_jwt, hasher, "HS256", &signer)
.await
.unwrap()
}

#[test]
fn simple_sd_jwt() {
// Values taken from https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-06.html#name-example-2-handling-structur
Expand Down Expand Up @@ -103,3 +116,34 @@ async fn sd_jwt_is_verifiable() -> anyhow::Result<()> {
josekit::jwt::decode_with_verifier(&jwt, &verifier)?;
Ok(())
}

#[tokio::test]
async fn sd_jwt_without_disclosures_works() -> anyhow::Result<()> {
let hasher = Sha256Hasher::new();
let sd_jwt = make_sd_jwt(json!({"parent": {"property1": "value1", "property2": [1, 2, 3]}}), []).await;
// Try to serialize & deserialize `sd_jwt`.
let sd_jwt = {
let s = sd_jwt.to_string();
s.parse::<SdJwt>()?
};

assert!(sd_jwt.disclosures().is_empty());
assert!(sd_jwt.key_binding_jwt().is_none());

let with_kb = sd_jwt
.clone()
.into_presentation(&hasher)?
.attach_key_binding_jwt(make_kb_jwt(&sd_jwt, &hasher).await)
.finish()
.0;
// Try to serialize & deserialize `with_kb`.
let with_kb = {
let s = with_kb.to_string();
s.parse::<SdJwt>()?
};

assert!(with_kb.disclosures().is_empty());
assert!(with_kb.key_binding_jwt().is_some());

Ok(())
}

0 comments on commit f730806

Please sign in to comment.