Skip to content

Commit

Permalink
feat: Add generate binary crate
Browse files Browse the repository at this point in the history
Signed-off-by: Abdulbois <[email protected]>
Signed-off-by: Abdulbois <[email protected]>
  • Loading branch information
Abdulbois committed Dec 13, 2023
1 parent 89c05e9 commit 721af5e
Show file tree
Hide file tree
Showing 8 changed files with 302 additions and 1 deletion.
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/
11 changes: 11 additions & 0 deletions generate/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "sd-jwt-generate"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = { version = "4.4.10", features = ["derive"] }
serde = { version = "1.0.193", features = ["derive"] }
serde_yaml = "0.9.27"
20 changes: 20 additions & 0 deletions generate/src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use clap::Parser;
use serde::Serialize;

#[derive(Parser)]
pub struct Cli {
/// The type to generate
#[arg(short, value_enum, default_value_t = GenerateType::Example)]
pub type_: GenerateType,
/// The paths to the directories where specifications.yaml file is located
#[arg(short, value_delimiter = ' ', num_args = 0.., require_equals = false)]
pub paths: Vec<std::path::PathBuf>,
}


#[derive(clap::ValueEnum, Clone, Debug, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum GenerateType {
Example,
TestCase,
}
71 changes: 71 additions & 0 deletions generate/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
mod cli;
mod types;

use std::path::PathBuf;
use clap::Parser;
use crate::cli::{Cli, GenerateType};
use crate::types::settings::Settings;

const SPECIFICATION_FILE_NAME: &str = "specification.yml";
const SETTINGS_FILE_NAME: &str = "settings.yml";

fn main() {
let args = Cli::parse();

println!("type_: {:?}, paths: {:?}", args.type_.clone(), args.paths);

let basedir = std::env::current_dir().expect("Unable to get current directory");
let settings_file = basedir.join(SETTINGS_FILE_NAME);

if !settings_file.exists() {
eprintln!("Settings file '{:?}' does not exist.", settings_file);
std::process::exit(1);
}

let glob: Vec<PathBuf>;
if args.paths.is_empty() {
glob = basedir
.read_dir()
.expect("Unable to read directory")
.filter_map(|entry| {
if let Ok(entry) = entry {
let path = entry.path();
if path.is_dir() && path.join(SPECIFICATION_FILE_NAME).exists() {
return Some(path.join(SPECIFICATION_FILE_NAME));
}
}
None
})
.collect();
} else {
glob = args.paths.iter()
.map(|d| basedir.join(d).join(SPECIFICATION_FILE_NAME))
.collect();
}
println!("settings.yaml - {:?}", settings_file);
println!("specification.yaml files - {:?}", glob);


let settings = load_yaml_settings(&settings_file);
println!("{:#?}", settings);


for case_path in glob {
println!("Generating data for '{:?}'", case_path);
generate_test_case_data(&settings, &case_path, args.type_.clone());
}
}

fn generate_test_case_data(settings: &Settings, test_case: &PathBuf, type_: GenerateType) {
todo!()
}

fn load_yaml_settings(file: &PathBuf) -> Settings {
let contents = std::fs::read_to_string(file)
.expect("Failed to read settings file");

let settings: Settings = serde_yaml::from_str(&contents)
.expect("Failed to parse YAML");

settings
}
2 changes: 2 additions & 0 deletions generate/src/types/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod settings;
pub mod specification;
77 changes: 77 additions & 0 deletions generate/src/types/settings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct KeySettings {
pub key_size: i32,
pub kty: String,
pub issuer_key: Key,
pub holder_key: Key,
}

#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct Key {
pub kty: String,
pub d: String,
pub crv: String,
pub x: String,
pub y: String,
}

#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct Identifiers {
pub issuer: String,
pub verifier: String,
}

#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct Settings {
pub identifiers: Identifiers,
pub key_settings: KeySettings,
pub key_binding_nonce: String,
pub expiry_seconds: Option<u64>,
pub random_seed: Option<u64>,
pub iat: Option<u64>,
pub exp: Option<u64>,
}

#[cfg(test)]
mod tests {
use crate::types::settings::Settings;

#[test]
fn test_test_settings() {
let yaml_str = r#"
identifiers:
issuer: "https://example.com/issuer"
verifier: "https://example.com/verifier"
key_settings:
key_size: 256
kty: "EC"
issuer_key:
kty: "EC"
d: "Ur2bNKuBPOrAaxsRnbSH6hIhmNTxSGXshDSUD1a1y7g"
crv: "P-256"
x: "b28d4MwZMjw8-00CG4xfnn9SLMVMM19SlqZpVb_uNtQ"
y: "Xv5zWwuoaTgdS6hV43yI6gBwTnjukmFQQnJ_kCxzqk8"
holder_key:
kty: "EC"
d: "5K5SCos8zf9zRemGGUl6yfok-_NiiryNZsvANWMhF-I"
crv: "P-256"
x: "TCAER19Zvu3OHF4j4W4vfSVoHIP1ILilDls7vCeGemc"
y: "ZxjiWWbZMQGHVWKVQ4hbSIirsVfuecCE6t4jT9F2HZQ"
key_binding_nonce: "1234567890"
expiry_seconds: 86400000
random_seed: 0
iat: 1683000000
exp: 1883000000
"#;

let settings: Settings = serde_yaml::from_str(yaml_str).unwrap();
println!("{:#?}", settings);
assert_eq!(settings.identifiers.issuer, "https://example.com/issuer");
}
}

119 changes: 119 additions & 0 deletions generate/src/types/specification.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use serde::{Deserialize, Deserializer, Serialize};
use serde_yaml::value::TaggedValue;
use serde_yaml::Value;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::io;
use std::io::Read;
use serde::de::Error;

const SD_TAG: &str = "!sd";

#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct Claims(HashMap<ClaimName, Value>);

#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
pub enum ClaimName {
Str(String),
Tag(TaggedValue),
}

impl PartialEq for ClaimName {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(ClaimName::Str(a), ClaimName::Str(b)) => a == b,
(ClaimName::Tag(a), ClaimName::Tag(b)) => b.eq(a),
_ => false,
}
}
}

impl Eq for ClaimName {}

impl Hash for ClaimName {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
ClaimName::Str(string) => string.hash(state),
ClaimName::Tag(tag) => tag.hash(state),
}
}
}

#[derive(Debug, Deserialize)]
pub struct Specification {
pub user_claims: UserClaims,
pub holder_disclosed_claims: Claims,
}

impl From<&str> for Specification {
fn from(value: &str) -> Self {
serde_yaml::from_str(value).expect("Failed to parse YAML")
}
}

#[derive(Serialize, PartialEq, Debug)]
pub struct UserClaims {
pub sub: String,
#[serde(flatten)]
pub claims: Claims,
}

impl<'de> Deserialize<'de> for UserClaims {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let mut value: HashMap<Value, Value> = Deserialize::deserialize(deserializer)?;
let sub = &Value::from("sub".to_string());

if let Some(Value::String(sub)) = value.remove(sub) {
let mut claims: Claims = Claims(HashMap::new());

for (k, v) in value {
match k {
Value::String(s) => {
claims.0.insert(ClaimName::Str(s), v);
}
Value::Tagged(tag) => {
if tag.tag.to_string() == SD_TAG {
claims.0.insert(ClaimName::Tag(*tag), v);
} else {
return Err(Error::custom("Unsupported tag in claim-name, only !sd tag is supported"));
}
}
_ => {
return Err(Error::custom("Unsupported type for claim-name, it can be only string or tagged"));
}
}
}

return Ok(UserClaims { sub, claims });
}

Err(Error::custom("Unsupported structure for user-claims"))
}
}

#[cfg(test)]
mod tests {
use crate::types::specification::Specification;

#[test]
fn test_specification() {
let yaml_str = r#"
user_claims:
sub: 6c5c0a49-b589-431d-bae7-219122a9ec2c
name: Bois
!sd address:
street_address: Schulstr. 12
!sd street_address1: Schulstr. 12
holder_disclosed_claims: {}
"#;

let example = Specification::from(yaml_str);
println!("{:#?}", example);
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use sha2::Digest;

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

mod issuer;
pub mod issuer;
mod verifier;
mod holder;
mod disclosure;
Expand Down

0 comments on commit 721af5e

Please sign in to comment.