Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes and e2e tests #6

Merged
merged 2 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ Cargo.lock

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
.idea/
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ log = "0.4"
rand = "0.8"
serde_json = { version = "1.0", features = ["preserve_order"] }
sha2 = "0.10"

[dev-dependencies]
rstest = "0.18.2"
62 changes: 44 additions & 18 deletions src/holder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,31 +93,40 @@ impl SDJWTHolder {
claims_to_disclose: Map<String, Value>,
) -> Vec<String> {
let mut hash_to_disclosure = Vec::new();

let default_list = Vec::new();
let sd_map: HashMap<&str, (&Value, &str)> = sd_jwt_claims[SD_DIGESTS_KEY].as_array().unwrap_or(&default_list).iter().map(|digest| {
let sd_map: HashMap<&str, (&Value, &str)> = sd_jwt_claims.get(SD_DIGESTS_KEY).and_then(Value::as_array).unwrap_or(&default_list).iter().filter_map(|digest| {
let digest = digest.as_str().unwrap();
let disclosure = self.sd_jwt_engine.hash_to_decoded_disclosure[digest].as_array().unwrap();
(disclosure[1].as_str().unwrap(), (&disclosure[2], digest))
if let Some(Value::Array(disclosure)) = self.sd_jwt_engine.hash_to_decoded_disclosure.get(digest) {
return Some((disclosure[1].as_str().unwrap(), (&disclosure[2], digest)));
}
None
}).collect(); //TODO split to 2 maps
for (key_to_disclose, value_to_disclose) in claims_to_disclose {
match value_to_disclose {
Value::Null | Value::Bool(true) | Value::Number(_) | Value::String(_) => { /* disclose without children */ }
Value::Array(arr_to_disclose) => {
if let Some(arr) = sd_jwt_claims.get(&key_to_disclose).and_then(Value::as_array) {
hash_to_disclosure.append(&mut self.select_disclosures_from_disclosed_list(&arr, &arr_to_disclose))
Value::Bool(true) | Value::Number(_) | Value::String(_) => { /* disclose without children */ }
Value::Array(claims_to_disclose) => {
if let Some(sd_jwt_claims) = sd_jwt_claims
.get(&key_to_disclose)
.and_then(Value::as_array)
{
hash_to_disclosure.append(&mut self.select_disclosures_from_disclosed_list(&sd_jwt_claims, &claims_to_disclose))
} else if let Some(sd_jwt_claims) = sd_map
.get(key_to_disclose.as_str())
.and_then(|(sd, _)| sd.as_array())
{
hash_to_disclosure.append(&mut self.select_disclosures_from_disclosed_list(&sd_jwt_claims, &claims_to_disclose))
}
}
Value::Object(next_disclosure) if (!next_disclosure.is_empty()) => {
let next_sd_jwt_claims = if let Some(next) = sd_jwt_claims.get(&key_to_disclose).and_then(Value::as_object) {
Value::Object(claims_to_disclose) if (!claims_to_disclose.is_empty()) => {
let sd_jwt_claims = if let Some(next) = sd_jwt_claims.get(&key_to_disclose).and_then(Value::as_object) {
next
} else {
sd_map[key_to_disclose.as_str()].0.as_object().unwrap()
};
hash_to_disclosure.append(&mut self.select_disclosures(next_sd_jwt_claims, next_disclosure));
hash_to_disclosure.append(&mut self.select_disclosures(sd_jwt_claims, claims_to_disclose));
}
Value::Object(_) => { /* disclose without children */ }
Value::Bool(false) => {
Value::Bool(false) | Value::Null => {
// skip unrevealed
continue
}
Expand All @@ -134,15 +143,32 @@ impl SDJWTHolder {

fn select_disclosures_from_disclosed_list(&self, sd_jwt_claims: &Vec<Value>, claims_to_disclose: &Vec<Value>) -> Vec<String> {
let mut hash_to_disclosure: Vec<String> = Vec::new();
for (claim_to_disclose, claim) in claims_to_disclose.iter().zip(sd_jwt_claims) {
match (claim_to_disclose, claim) {
(Value::Bool(true), Value::Object(claim)) => {
if let Some(Value::String(digest)) = claim.get(SD_LIST_PREFIX) {
for (claim_to_disclose, sd_jwt_claims) in claims_to_disclose.iter().zip(sd_jwt_claims) {
match (claim_to_disclose, sd_jwt_claims) {
(Value::Bool(true), Value::Object(sd_jwt_claims)) => {
if let Some(Value::String(digest)) = sd_jwt_claims.get(SD_LIST_PREFIX) {
hash_to_disclosure.push(self.sd_jwt_engine.hash_to_disclosure[digest].to_owned());
}
}
(Value::Array(new_claims_to_disclose), Value::Array(claim)) => {
self.select_disclosures_from_disclosed_list(claim, new_claims_to_disclose);
(claim_to_disclose, Value::Object(sd_jwt_claims)) => {
if let Some(Value::String(digest)) = sd_jwt_claims.get(SD_LIST_PREFIX) {
let disclosure = self.sd_jwt_engine.hash_to_decoded_disclosure[digest].as_array().unwrap();
match (claim_to_disclose, disclosure.get(1)) {
(Value::Array(claim_to_disclose), Some(Value::Array(sd_jwt_claims))) => {
hash_to_disclosure.append(&mut self.select_disclosures_from_disclosed_list(&sd_jwt_claims, claim_to_disclose));
}
(Value::Object(claim_to_disclose), Some(Value::Object(sd_jwt_claims))) => {
hash_to_disclosure.push(self.sd_jwt_engine.hash_to_disclosure[digest].to_owned());
hash_to_disclosure.append(&mut self.select_disclosures(&sd_jwt_claims, claim_to_disclose.to_owned()));
}
_ => {}
}
} else if let Some(claim_to_disclose) = claim_to_disclose.as_object() {
hash_to_disclosure.append(&mut self.select_disclosures(sd_jwt_claims, claim_to_disclose.to_owned()));
}
}
(Value::Array(claim_to_disclose), Value::Array(sd_jwt_claims)) => {
hash_to_disclosure.append(&mut self.select_disclosures_from_disclosed_list(sd_jwt_claims, claim_to_disclose));
}
_ => {}
}
Expand Down
21 changes: 6 additions & 15 deletions src/issuer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ pub struct SDJWTIssuer {
// internal fields
inner: SDJWTCommon,
all_disclosures: Vec<SDJWTDisclosure>,
sd_jwt_payload: SJMap<String, Value>,
signed_sd_jwt: String,
pub(crate) serialized_sd_jwt: String,
pub sd_jwt_payload: SJMap<String, Value>,
pub signed_sd_jwt: String,
pub serialized_sd_jwt: String,
}

pub enum SDJWTClaimsStrategy<'a> {
Expand Down Expand Up @@ -72,21 +72,12 @@ impl<'a> SDJWTClaimsStrategy<'a> {
str.strip_prefix(key).as_mut().and_then(|claim| {
if let Some(next_claim) = claim.strip_prefix('.') {
Some(next_claim)
} else if let Some(next_claim) = claim.strip_prefix('[').and_then(|str| str.strip_suffix(']')) {
Some(next_claim)
} else {
// FIXME Replace to non-leackable impl
// Removes "[", "]" symbols form "index" and returns "next_claim" as "index.remained_claims.."
// For example: [0].street -> 0.street
if let Some(remainder) = claim.strip_prefix('[') {
*claim = remainder;
let remainder: Vec<&str> = claim.splitn(2, ']').collect();
//FIXME Change to safe impl
*claim = remainder.join("").leak();

Some(claim)
} else {
None
}
*claim = claim.replace("[", "").replace("]","").leak();
Some(claim)
}
})
}).collect();
Expand Down
19 changes: 8 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
use std::collections::HashMap;
use std::sync::Mutex;

use base64::{Engine, engine::general_purpose};
use base64::{engine::general_purpose, Engine};
use lazy_static::lazy_static;
use rand::prelude::ThreadRng;
use rand::RngCore;
use serde_json::{Map, Value};
use sha2::Digest;

use std::sync::Mutex;
pub use {holder::SDJWTHolder, issuer::SDJWTIssuer, verifier::SDJWTVerifier};

mod issuer;
mod verifier;
mod holder;
mod disclosure;
pub mod holder;
pub mod issuer;
pub mod verifier;

const DEFAULT_SIGNING_ALG: &str = "ES256";
pub const DEFAULT_SIGNING_ALG: &str = "ES256";
const SD_DIGESTS_KEY: &str = "_sd";
const DIGEST_ALG_KEY: &str = "_sd_alg";
const DEFAULT_DIGEST_ALG: &str = "sha-256";
pub const DEFAULT_DIGEST_ALG: &str = "sha-256";
const SD_LIST_PREFIX: &str = "...";
const _SD_JWT_TYP_HEADER: &str = "sd+jwt";
const KB_JWT_TYP_HEADER: &str = "kb+jwt";
const JWS_KEY_DISCLOSURES: &str = "disclosures";
const JWS_KEY_KB_JWT: &str = "kb_jwt";
const COMBINED_SERIALIZATION_FORMAT_SEPARATOR: &str = "~";
pub const COMBINED_SERIALIZATION_FORMAT_SEPARATOR: &str = "~";
const JWT_SEPARATOR: &str = ".";

#[derive(Debug)]
Expand Down
2 changes: 1 addition & 1 deletion src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub struct SDJWTVerifier {
sd_jwt_payload: Map<String, Value>,
_holder_public_key_payload: Option<HashMap<String, Value>>,
duplicate_hash_check: Vec<String>,
verified_claims: Value,
pub verified_claims: Value,

cb_get_issuer_key: Box<KeyResolver>,
}
Expand Down
Loading