Skip to content

Commit

Permalink
Passing tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
timothee-haudebourg committed Sep 16, 2024
1 parent 72667a8 commit 4f4e50f
Show file tree
Hide file tree
Showing 10 changed files with 261 additions and 130 deletions.
2 changes: 1 addition & 1 deletion crates/claims/crates/jws/src/compact/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ impl CompactJWSBuf {
) -> Result<Self, InvalidCompactJWS<Vec<u8>>> {
let mut bytes = signing_bytes;
bytes.push(b'.');
bytes.extend(signature.iter().copied());
bytes.extend_from_slice(signature);
Self::new(bytes)
}

Expand Down
31 changes: 11 additions & 20 deletions crates/claims/crates/jws/src/compact/url_safe.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use base64::Engine;
use core::fmt;
use ssi_core::BytesBuf;
use std::{ops::Deref, str::FromStr};

use crate::{
Expand Down Expand Up @@ -201,24 +202,14 @@ impl<'a> PartialEq<&'a UrlSafeJws> for String {
pub struct UrlSafeJwsBuf(String);

impl UrlSafeJwsBuf {
pub fn new(bytes: Vec<u8>) -> Result<Self, InvalidCompactJWS<Vec<u8>>> {
match String::from_utf8(bytes) {
Ok(string) => {
if CompactJWS::validate(string.as_bytes()) {
Ok(Self(string))
} else {
Err(InvalidCompactJWS(string.into_bytes()))
}
}
Err(e) => Err(InvalidCompactJWS(e.into_bytes())),
}
}

pub fn from_string(string: String) -> Result<Self, InvalidCompactJWS<String>> {
if CompactJWS::validate(string.as_bytes()) {
Ok(Self(string))
pub fn new<B: BytesBuf>(bytes: B) -> Result<Self, InvalidCompactJWS<B>> {
if UrlSafeJws::validate(bytes.as_ref()) {
Ok(unsafe {
// SAFETY: we just validated the bytes.
Self::new_unchecked(bytes.into())
})
} else {
Err(InvalidCompactJWS(string))
Err(InvalidCompactJWS(bytes))
}
}

Expand Down Expand Up @@ -331,15 +322,15 @@ impl FromStr for UrlSafeJwsBuf {
type Err = InvalidCompactJWS;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::from_string(s.to_owned())
Self::new(s.to_owned())
}
}

impl TryFrom<String> for UrlSafeJwsBuf {
type Error = InvalidCompactJWS<String>;

fn try_from(value: String) -> Result<Self, Self::Error> {
Self::from_string(value)
Self::new(value)
}
}

Expand Down Expand Up @@ -368,7 +359,7 @@ impl<'de> serde::Deserialize<'de> for UrlSafeJwsBuf {
where
E: serde::de::Error,
{
UrlSafeJwsBuf::from_string(v).map_err(|e| E::custom(e))
UrlSafeJwsBuf::new(v).map_err(|e| E::custom(e))
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/claims/crates/jws/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ impl<'a, T> DecodedJWS<'a, T> {
pub fn into_encoded(self) -> CompactJWSBuf {
CompactJWSBuf::from_signing_bytes_and_signature(
self.signing_bytes.bytes.into_owned(),
&self.signature,
self.signature.encode().as_bytes(),
)
.unwrap()
}
Expand Down
5 changes: 5 additions & 0 deletions crates/claims/crates/jws/src/verification.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{verify_bytes, DecodedJWS, DecodedSigningBytes, Error, Header};
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use ssi_claims_core::{
ClaimsValidity, InvalidProof, ProofValidationError, ProofValidity, ResolverProvider,
ValidateClaims, ValidateProof, VerifiableClaims,
Expand Down Expand Up @@ -47,6 +48,10 @@ impl JWSSignature {
pub fn into_bytes(self) -> Vec<u8> {
self.0
}

pub fn encode(&self) -> String {
URL_SAFE_NO_PAD.encode(&self.0)
}
}

impl From<Vec<u8>> for JWSSignature {
Expand Down
5 changes: 4 additions & 1 deletion crates/claims/crates/sd-jwt/src/conceal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,10 @@ impl SdJwtPayload {
) -> Result<(Self, Vec<DecodedDisclosure<'static>>), ConcealError> {
let mut disclosures = Vec::new();

for pointer in pointers {
let mut sorted_pointers: Vec<_> = pointers.iter().map(Borrow::borrow).collect();
sorted_pointers.sort_unstable();

for pointer in pointers.into_iter().rev() {
disclosures.push(conceal_object_at(
&mut claims,
&mut rng,
Expand Down
20 changes: 14 additions & 6 deletions crates/claims/crates/sd-jwt/src/disclosure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,10 @@ pub struct DisclosureBuf(Vec<u8>);
impl DisclosureBuf {
/// Creates a disclosure from its defining parts.
pub fn encode_from_parts(salt: &str, kind: &DisclosureDescription) -> Self {
Self(
BASE64_URL_SAFE_NO_PAD
.encode(kind.to_value(salt).to_string())
.into_bytes(),
)
let string = kind.to_value(salt).to_string();
// eprintln!("value = {string}");

Self(BASE64_URL_SAFE_NO_PAD.encode(string).into_bytes())
}

/// Borrows the disclosure.
Expand Down Expand Up @@ -232,6 +231,15 @@ impl<'a> DecodedDisclosure<'a> {
desc: kind,
}
}

/// Clones the encoded disclosure to fully owned the decoded disclosure.
pub fn into_owned(self) -> DecodedDisclosure<'static> {
DecodedDisclosure {
encoded: Cow::Owned(self.encoded.into_owned()),
salt: self.salt,
desc: self.desc,
}
}
}

/// Disclosure description.
Expand Down Expand Up @@ -383,7 +391,7 @@ mod tests {
"nPuoQnkRFq3BIeAm7AnXFA".to_owned(),
DisclosureDescription::ArrayItem(serde_json::json!("DE"))
),
DecodedDisclosure::new("WyJuUHVvUW5rUkZxM0JJZUFtN0FuWEZBIiwgIkRFIl0").unwrap()
DecodedDisclosure::new("WyJuUHVvUW5rUkZxM0JJZUFtN0FuWEZBIiwiREUiXQ").unwrap()
)
}
}
2 changes: 1 addition & 1 deletion crates/claims/crates/sd-jwt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ impl<'a> Iterator for Disclosures<'a> {
fn next(&mut self) -> Option<Self::Item> {
let mut i = self.offset;

while i < self.offset {
while i < self.bytes.len() {
if self.bytes[i] == b'~' {
let disclosure = unsafe {
// SAFETY: we already validated the SD-JWT and know
Expand Down
95 changes: 55 additions & 40 deletions crates/claims/crates/sd-jwt/src/reveal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ pub enum RevealError {
Decode(#[from] DecodeError),

/// Unused disclosure.
#[error("unused disclosure")]
UnusedDisclosure,
#[error("unused disclosure `{0:?}`")]
UnusedDisclosure(DecodedDisclosure<'static>),

/// Claim collision.
#[error("claim collision")]
Expand Down Expand Up @@ -79,6 +79,9 @@ impl SdJwtPayload {
disclosures: &[DecodedDisclosure],
pointers: &mut Vec<JsonPointerBuf>,
) -> Result<JWTClaims<T>, RevealError> {
eprintln!("payload: {}", serde_json::to_string_pretty(self).unwrap());
eprintln!("disclosures: {disclosures:#?}");

let mut disclosures: IndexMap<_, _> = disclosures
.iter()
.map(|disclosure| {
Expand All @@ -87,17 +90,17 @@ impl SdJwtPayload {
})
.collect();

let mut disclosed_claims = serde_json::Map::new();
for (key, value) in &self.claims {
let mut pointer = JsonPointerBuf::default();
pointer.push(key);
let mut value = value.clone();
reveal_value(&pointer, &mut value, &mut disclosures)?;
disclosed_claims.insert(key.clone(), value);
}
let mut disclosed_claims = self.claims.clone();
reveal_object(
&JsonPointerBuf::default(),
&mut disclosed_claims,
&mut disclosures,
)?;

for (_, disclosure) in disclosures {
pointers.push(disclosure.pointer.ok_or(RevealError::UnusedDisclosure)?);
pointers.push(disclosure.pointer.ok_or_else(|| {
RevealError::UnusedDisclosure(disclosure.disclosure.clone().into_owned())
})?);
}

serde_json::from_value(Value::Object(disclosed_claims)).map_err(Into::into)
Expand Down Expand Up @@ -127,25 +130,7 @@ fn reveal_value(
disclosures: &mut IndexMap<String, InProgressDisclosure>,
) -> Result<(), RevealError> {
match value {
Value::Object(object) => {
// Process `_sd` claim.
if let Some(sd_claims) = object.remove(SD_CLAIM_NAME) {
for (key, value) in reveal_sd_claim(pointer, &sd_claims, disclosures)? {
if object.insert(key, value).is_some() {
return Err(RevealError::Collision);
}
}
}

// Visit sub-values.
for (key, sub_value) in object {
let mut pointer = pointer.to_owned();
pointer.push(key);
reveal_value(&pointer, sub_value, disclosures)?
}

Ok(())
}
Value::Object(object) => reveal_object(pointer, object, disclosures),
Value::Array(array) => array.try_retain_mut(|i, item| {
let mut pointer = pointer.to_owned();
pointer.push_index(i);
Expand All @@ -154,11 +139,16 @@ fn reveal_value(
Some(hash) => match disclosures.get_mut(hash) {
Some(in_progress_disclosure) => match &in_progress_disclosure.disclosure.desc {
DisclosureDescription::ArrayItem(value) => {
if in_progress_disclosure.pointer.replace(pointer).is_some() {
if in_progress_disclosure
.pointer
.replace(pointer.clone())
.is_some()
{
return Err(RevealError::DisclosureUsedMultipleTimes);
}

*item = value.clone();
reveal_value(&pointer, item, disclosures)?;
Ok(true)
}
DisclosureDescription::ObjectEntry { .. } => {
Expand All @@ -177,6 +167,30 @@ fn reveal_value(
}
}

fn reveal_object(
pointer: &JsonPointer,
object: &mut serde_json::Map<String, Value>,
disclosures: &mut IndexMap<String, InProgressDisclosure>,
) -> Result<(), RevealError> {
// Process `_sd` claim.
if let Some(sd_claims) = object.remove(SD_CLAIM_NAME) {
for (key, value) in reveal_sd_claim(pointer, &sd_claims, disclosures)? {
if object.insert(key, value).is_some() {
return Err(RevealError::Collision);
}
}
}

// Visit sub-values.
for (key, sub_value) in object {
let mut pointer = pointer.to_owned();
pointer.push(key);
reveal_value(&pointer, sub_value, disclosures)?
}

Ok(())
}

fn reveal_sd_claim(
pointer: &JsonPointer,
sd_claim: &serde_json::Value,
Expand Down Expand Up @@ -261,25 +275,26 @@ mod tests {
"iss": "https://example.com/issuer",
"iat": 1683000000,
"exp": 1883000000,
"sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c",
"given_name": "太郎",
"family_name": "山田",
"email": "\"unusual email address\"@example.jp",
"phone_number": "+81-80-1234-5678",
// "sub": "6c5c0a49-b589-431d-bae7-219122a9ec2c",
// "given_name": "太郎",
// "family_name": "山田",
// "email": "\"unusual email address\"@example.jp",
// "phone_number": "+81-80-1234-5678",
"address": {
"street_address": "東京都港区芝公園4丁目2−8",
"locality": "東京都",
// "street_address": "東京都港区芝公園4丁目2−8",
// "locality": "東京都",
"region": "港区",
"country": "JP"
},
"birthdate": "1940-01-01"
// "birthdate": "1940-01-01"
})
});

#[test]
fn disclose() {
let sd_jwt = SdJwt::new(SD_JWT).unwrap();
let disclosed = sd_jwt.decode().unwrap().reveal_any().unwrap();
let decoded = sd_jwt.decode().unwrap();
let disclosed = decoded.reveal_any().unwrap();
let output = serde_json::to_value(disclosed.claims()).unwrap();
assert_eq!(output, *DISCLOSED_CLAIMS)
}
Expand Down
Loading

0 comments on commit 4f4e50f

Please sign in to comment.