Skip to content

Commit

Permalink
Split Error into DecodeError and EncodeError
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanmiller-spruceid committed Oct 4, 2023
1 parent 81d4022 commit b720817
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 80 deletions.
46 changes: 25 additions & 21 deletions ssi-sd-jwt/src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ pub struct ValidityClaims {
pub fn decode_verify<Claims: DeserializeOwned>(
serialized: &str,
key: &JWK,
) -> Result<(ValidityClaims, Claims), Error> {
let deserialized =
deserialize_string_format(serialized).ok_or(Error::UnableToDeserializeStringFormat)?;
) -> Result<(ValidityClaims, Claims), DecodeError> {
let deserialized = deserialize_string_format(serialized)
.ok_or(DecodeError::UnableToDeserializeStringFormat)?;

decode_verify_disclosure_array(deserialized.jwt, key, &deserialized.disclosures)
}
Expand All @@ -29,7 +29,7 @@ pub fn decode_verify_disclosure_array<Claims: DeserializeOwned>(
jwt: &str,
key: &JWK,
disclosures: &[&str],
) -> Result<(ValidityClaims, Claims), Error> {
) -> Result<(ValidityClaims, Claims), DecodeError> {
let mut payload_claims: serde_json::Value = ssi_jwt::decode_verify(jwt, key)?;

let validity_claims: ValidityClaims = serde_json::from_value(payload_claims.clone())?;
Expand All @@ -46,26 +46,26 @@ pub fn decode_verify_disclosure_array<Claims: DeserializeOwned>(

for (_, disclosure) in disclosures {
if !disclosure.found {
return Err(Error::UnusedDisclosure);
return Err(DecodeError::UnusedDisclosure);
}
}

Ok((validity_claims, serde_json::from_value(payload_claims)?))
}

fn sd_alg(claims: &serde_json::Value) -> Result<SdAlg, Error> {
fn sd_alg(claims: &serde_json::Value) -> Result<SdAlg, DecodeError> {
let alg_name = claims[SD_ALG_CLAIM_NAME]
.as_str()
.ok_or(Error::MissingSdAlg)?;
.ok_or(DecodeError::MissingSdAlg)?;

SdAlg::try_from(alg_name)
}

fn translate_to_in_progress_disclosures(
disclosures: &[&str],
sd_alg: SdAlg,
) -> Result<BTreeMap<String, InProgressDisclosure>, Error> {
let disclosure_vec: Result<Vec<_>, Error> = disclosures
) -> Result<BTreeMap<String, InProgressDisclosure>, DecodeError> {
let disclosure_vec: Result<Vec<_>, DecodeError> = disclosures
.iter()
.map(|disclosure| InProgressDisclosure::new(disclosure, sd_alg))
.collect();
Expand All @@ -77,7 +77,7 @@ fn translate_to_in_progress_disclosures(
let prev = disclosure_map.insert(disclosure.hash.clone(), disclosure);

if prev.is_some() {
return Err(Error::MultipleDisclosuresWithSameHash);
return Err(DecodeError::MultipleDisclosuresWithSameHash);
}
}

Expand All @@ -92,7 +92,7 @@ struct InProgressDisclosure {
}

impl InProgressDisclosure {
fn new(disclosure: &str, sd_alg: SdAlg) -> Result<Self, Error> {
fn new(disclosure: &str, sd_alg: SdAlg) -> Result<Self, DecodeError> {
Ok(InProgressDisclosure {
decoded: DecodedDisclosure::new(disclosure)?,
hash: hash_encoded_disclosure(sd_alg, disclosure),
Expand All @@ -104,7 +104,7 @@ impl InProgressDisclosure {
fn visit_claims(
payload_claims: &mut serde_json::Value,
disclosures: &mut BTreeMap<String, InProgressDisclosure>,
) -> Result<(), Error> {
) -> Result<(), DecodeError> {
let payload_claims = match payload_claims.as_object_mut() {
Some(obj) => obj,
None => return Ok(()),
Expand Down Expand Up @@ -132,7 +132,7 @@ fn visit_claims(
let prev = payload_claims.insert(new_claim_name, new_claim_value);

if prev.is_some() {
return Err(Error::DisclosureClaimCollidesWithJwtClaim);
return Err(DecodeError::DisclosureClaimCollidesWithJwtClaim);
}
}

Expand All @@ -155,20 +155,24 @@ fn visit_claims(
fn decode_sd_claims(
sd_claims: &serde_json::Value,
disclosures: &mut BTreeMap<String, InProgressDisclosure>,
) -> Result<Vec<(String, serde_json::Value)>, Error> {
let sd_claims = sd_claims.as_array().ok_or(Error::SdPropertyNotArray)?;
) -> Result<Vec<(String, serde_json::Value)>, DecodeError> {
let sd_claims = sd_claims
.as_array()
.ok_or(DecodeError::SdPropertyNotArray)?;
let mut found_disclosures = vec![];
for disclosure_hash in sd_claims {
let disclosure_hash = disclosure_hash.as_str().ok_or(Error::SdClaimNotString)?;
let disclosure_hash = disclosure_hash
.as_str()
.ok_or(DecodeError::SdClaimNotString)?;

if let Some(in_progress_disclosure) = disclosures.get_mut(disclosure_hash) {
if in_progress_disclosure.found {
return Err(Error::DisclosureUsedMultipleTimes);
return Err(DecodeError::DisclosureUsedMultipleTimes);
}
in_progress_disclosure.found = true;
match in_progress_disclosure.decoded.kind {
DisclosureKind::ArrayItem(_) => {
return Err(Error::ArrayDisclosureWhenExpectingProperty)
return Err(DecodeError::ArrayDisclosureWhenExpectingProperty)
}
DisclosureKind::Property {
ref name,
Expand All @@ -184,21 +188,21 @@ fn decode_sd_claims(
fn decode_array_claims(
array: &[serde_json::Value],
disclosures: &mut BTreeMap<String, InProgressDisclosure>,
) -> Result<Vec<serde_json::Value>, Error> {
) -> Result<Vec<serde_json::Value>, DecodeError> {
let mut new_items = vec![];
for item in array.iter() {
if let Some(hash) = array_item_is_disclosure(item) {
if let Some(in_progress_disclosure) = disclosures.get_mut(hash) {
if in_progress_disclosure.found {
return Err(Error::DisclosureUsedMultipleTimes);
return Err(DecodeError::DisclosureUsedMultipleTimes);
}
in_progress_disclosure.found = true;
match in_progress_disclosure.decoded.kind {
DisclosureKind::ArrayItem(ref value) => {
new_items.push(value.clone());
}
DisclosureKind::Property { .. } => {
return Err(Error::PropertyDisclosureWhenExpectingArray)
return Err(DecodeError::PropertyDisclosureWhenExpectingArray)
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions ssi-sd-jwt/src/digest.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use jose_b64::base64ct::{Base64UrlUnpadded, Encoding};
use sha2::Digest;

use crate::Error;
use crate::DecodeError;

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum SdAlg {
Expand All @@ -21,12 +21,12 @@ impl SdAlg {
}

impl TryFrom<&str> for SdAlg {
type Error = Error;
type Error = DecodeError;

fn try_from(value: &str) -> Result<Self, Self::Error> {
Ok(match value {
Self::SHA256_STR => SdAlg::Sha256,
other => return Err(Error::UnknownSdAlg(other.to_owned())),
other => return Err(DecodeError::UnknownSdAlg(other.to_owned())),
})
}
}
Expand Down
12 changes: 6 additions & 6 deletions ssi-sd-jwt/src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@ pub fn encode_sign<Claims: Serialize>(
key: &JWK,
sd_alg: SdAlg,
disclosures: Vec<UnencodedDisclosure>,
) -> Result<(String, Vec<FullDisclosure>), Error> {
) -> Result<(String, Vec<FullDisclosure>), EncodeError> {
let mut base_claims_json = serde_json::to_value(base_claims)?;

let post_encoded_disclosures: Result<Vec<_>, Error> = disclosures
let post_encoded_disclosures: Result<Vec<_>, EncodeError> = disclosures
.iter()
.map(|disclosure| {
let encoded = disclosure.encode()?;
Expand All @@ -93,15 +93,15 @@ pub fn encode_sign<Claims: Serialize>(
{
let base_claims_obj = base_claims_json
.as_object_mut()
.ok_or(Error::EncodedAsNonObject)?;
.ok_or(EncodeError::EncodedAsNonObject)?;

let prev_sd_alg = base_claims_obj.insert(
SD_ALG_CLAIM_NAME.to_owned(),
serde_json::json!(sd_alg.to_str()),
);

if prev_sd_alg.is_some() {
return Err(Error::EncodedClaimsContainsReservedProperty);
return Err(EncodeError::EncodedClaimsContainsReservedProperty);
}

let mut sd_claim = vec![];
Expand All @@ -118,7 +118,7 @@ pub fn encode_sign<Claims: Serialize>(
}

let array = base_claims_obj.get_mut(claim_name).unwrap();
let array = array.as_array_mut().ok_or(Error::ExpectedArray)?;
let array = array.as_array_mut().ok_or(EncodeError::ExpectedArray)?;

array.push(serde_json::json!({ARRAY_CLAIM_ITEM_PROPERTY_NAME: disclosure.hash.clone()}));
}
Expand All @@ -129,7 +129,7 @@ pub fn encode_sign<Claims: Serialize>(
base_claims_obj.insert(SD_CLAIM_NAME.to_owned(), serde_json::Value::Array(sd_claim));

if prev_sd.is_some() {
return Err(Error::EncodedClaimsContainsReservedProperty);
return Err(EncodeError::EncodedClaimsContainsReservedProperty);
}
}

Expand Down
47 changes: 47 additions & 0 deletions ssi-sd-jwt/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#[derive(thiserror::Error, Debug)]
pub enum DecodeError {
#[error("Unable to deserialize string format of concatenated tildes")]
UnableToDeserializeStringFormat,
#[error("JWT is missing _sd_alg property")]
MissingSdAlg,
#[error("Unknown value of _sd_alg {0}")]
UnknownSdAlg(String),
#[error("Multiple disclosures given with the same hash")]
MultipleDisclosuresWithSameHash,
#[error("An _sd claim wasn't a string")]
SdClaimNotString,
#[error("And _sd property was not an array type")]
SdPropertyNotArray,
#[error("A disclosure claim would collid with an existing JWT claim")]
DisclosureClaimCollidesWithJwtClaim,
#[error("A disclosure is malformed")]
DisclosureMalformed,
#[error("A single disclosure was used multiple times")]
DisclosureUsedMultipleTimes,
#[error("Found an array item disclosure when expecting a property type")]
ArrayDisclosureWhenExpectingProperty,
#[error("Found a property type disclosure when expecting an array item")]
PropertyDisclosureWhenExpectingArray,
#[error("A disclosure was not used during decoding")]
UnusedDisclosure,
#[error(transparent)]
JWS(#[from] ssi_jws::Error),
#[error(transparent)]
JsonDeserialization(#[from] serde_json::Error),
}

#[derive(thiserror::Error, Debug)]
pub enum EncodeError {
#[error("The base claims to encode did not become a JSON object")]
EncodedAsNonObject,
#[error("The base claims to encode contained a property reserved by SD-JWT")]
EncodedClaimsContainsReservedProperty,
#[error("A property for an array sd claim was not an array")]
ExpectedArray,
#[error("A disclosure was not used during decoding")]
UnusedDisclosure,
#[error(transparent)]
JWS(#[from] ssi_jws::Error),
#[error(transparent)]
JsonSerialization(#[from] serde_json::Error),
}
40 changes: 2 additions & 38 deletions ssi-sd-jwt/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod decode;
pub(crate) mod digest;
pub(crate) mod encode;
mod error;
pub(crate) mod serialized;
pub(crate) mod verify;

Expand All @@ -9,47 +10,10 @@ pub use digest::{hash_encoded_disclosure, SdAlg};
pub use encode::{
encode_array_disclosure, encode_property_disclosure, encode_sign, UnencodedDisclosure,
};
pub use error::{DecodeError, EncodeError};
pub use serialized::{deserialize_string_format, serialize_string_format};
pub use verify::verify_sd_disclosures_array;

#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Unable to deserialize string format of concatenated tildes")]
UnableToDeserializeStringFormat,
#[error("JWT is missing _sd_alg property")]
MissingSdAlg,
#[error("Unknown value of _sd_alg {0}")]
UnknownSdAlg(String),
#[error("Multiple disclosures given with the same hash")]
MultipleDisclosuresWithSameHash,
#[error("An _sd claim wasn't a string")]
SdClaimNotString,
#[error("And _sd property was not an array type")]
SdPropertyNotArray,
#[error("A disclosure claim would collid with an existing JWT claim")]
DisclosureClaimCollidesWithJwtClaim,
#[error("A disclosure is malformed")]
DisclosureMalformed,
#[error("A single disclosure was used multiple times")]
DisclosureUsedMultipleTimes,
#[error("Found an array item disclosure when expecting a property type")]
ArrayDisclosureWhenExpectingProperty,
#[error("Found a property type disclosure when expecting an array item")]
PropertyDisclosureWhenExpectingArray,
#[error("The base claims to encode did not become a JSON object")]
EncodedAsNonObject,
#[error("The base claims to encode contained a property reserved by SD-JWT")]
EncodedClaimsContainsReservedProperty,
#[error("A property for an array sd claim was not an array")]
ExpectedArray,
#[error("A disclosure was not used during decoding")]
UnusedDisclosure,
#[error(transparent)]
JWS(#[from] ssi_jws::Error),
#[error(transparent)]
JsonSerialization(#[from] serde_json::Error),
}

const SD_CLAIM_NAME: &str = "_sd";
const SD_ALG_CLAIM_NAME: &str = "_sd_alg";
const ARRAY_CLAIM_ITEM_PROPERTY_NAME: &str = "...";
Expand Down
26 changes: 14 additions & 12 deletions ssi-sd-jwt/src/verify.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use jose_b64::base64ct::{Base64UrlUnpadded, Encoding};

use crate::digest::{hash_encoded_disclosure, SdAlg};
use crate::Error;
use crate::DecodeError;

#[derive(Debug, PartialEq)]
pub struct DecodedDisclosure {
Expand All @@ -19,25 +19,27 @@ pub enum DisclosureKind {
}

impl DecodedDisclosure {
pub fn new(encoded: &str) -> Result<Self, Error> {
pub fn new(encoded: &str) -> Result<Self, DecodeError> {
let bytes = Base64UrlUnpadded::decode_vec(encoded).unwrap();
let json: serde_json::Value = serde_json::from_slice(&bytes).unwrap();

match json {
serde_json::Value::Array(values) => match values.len() {
3 => validate_property_disclosure(&values),
2 => validate_array_item_disclosure(&values),
_ => Err(Error::DisclosureMalformed),
_ => Err(DecodeError::DisclosureMalformed),
},
_ => Err(Error::DisclosureMalformed),
_ => Err(DecodeError::DisclosureMalformed),
}
}
}

fn validate_property_disclosure(values: &[serde_json::Value]) -> Result<DecodedDisclosure, Error> {
let salt = values[0].as_str().ok_or(Error::DisclosureMalformed)?;
fn validate_property_disclosure(
values: &[serde_json::Value],
) -> Result<DecodedDisclosure, DecodeError> {
let salt = values[0].as_str().ok_or(DecodeError::DisclosureMalformed)?;

let name = values[1].as_str().ok_or(Error::DisclosureMalformed)?;
let name = values[1].as_str().ok_or(DecodeError::DisclosureMalformed)?;

Ok(DecodedDisclosure {
salt: salt.to_owned(),
Expand All @@ -50,8 +52,8 @@ fn validate_property_disclosure(values: &[serde_json::Value]) -> Result<DecodedD

fn validate_array_item_disclosure(
values: &[serde_json::Value],
) -> Result<DecodedDisclosure, Error> {
let salt = values[0].as_str().ok_or(Error::DisclosureMalformed)?;
) -> Result<DecodedDisclosure, DecodeError> {
let salt = values[0].as_str().ok_or(DecodeError::DisclosureMalformed)?;

Ok(DecodedDisclosure {
salt: salt.to_owned(),
Expand All @@ -63,7 +65,7 @@ pub fn verify_sd_disclosures_array(
digest_algo: SdAlg,
disclosures: &[&str],
sd_claim: &[&str],
) -> Result<serde_json::Value, Error> {
) -> Result<serde_json::Value, DecodeError> {
let mut verfied_claims = serde_json::Map::new();

for disclosure in disclosures {
Expand All @@ -80,11 +82,11 @@ pub fn verify_sd_disclosures_array(
let orig = verfied_claims.insert(name, value);

if orig.is_some() {
return Err(Error::DisclosureUsedMultipleTimes);
return Err(DecodeError::DisclosureUsedMultipleTimes);
}
}
DisclosureKind::ArrayItem(_) => {
return Err(Error::ArrayDisclosureWhenExpectingProperty);
return Err(DecodeError::ArrayDisclosureWhenExpectingProperty);
}
}
}
Expand Down

0 comments on commit b720817

Please sign in to comment.