use cbor2::Value;
use cose2::{iana, Error, Label, Sign1Message, Signer, Verifier};
use sd_cwt::{
issue_from_preissuance, restore_payload_from_message, set_disclosures, set_sd_alg,
set_sd_cwt_typ, RedactionHasher, RestoreMode, Sha256RedactionHasher, TO_BE_DECOY_TAG,
TO_BE_REDACTED_TAG,
};
fn toy_tag(secret: &[u8], data: &[u8]) -> Vec<u8> {
let mut out = [0u8; 16];
for (i, byte) in secret.iter().chain(data).enumerate() {
out[i % out.len()] ^= byte.wrapping_add(i as u8);
}
out.to_vec()
}
struct Issuer;
impl Signer for Issuer {
fn alg(&self) -> Option<Label> {
Some(iana::AlgorithmEdDSA.into())
}
fn kid(&self) -> Option<&[u8]> {
Some(b"issuer-key-1")
}
fn sign(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
Ok(toy_tag(b"issuer signing secret", data))
}
}
struct IssuerVerifier;
impl Verifier for IssuerVerifier {
fn alg(&self) -> Option<Label> {
Some(iana::AlgorithmEdDSA.into())
}
fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), Error> {
if toy_tag(b"issuer signing secret", data) == signature {
Ok(())
} else {
Err(Error::verify("SD-CWT issuer signature mismatch"))
}
}
}
fn deterministic_demo_salts() -> impl FnMut() -> [u8; 16] {
let mut next = 1u8;
move || {
let salt = [next; 16];
next = next.wrapping_add(1);
salt
}
}
fn main() -> Result<(), Error> {
let preissued_claims = Value::Map(vec![
(Value::from(1), Value::from("https://issuer.example")),
(Value::from(8), Value::Map(vec![])), (
Value::Tag(TO_BE_REDACTED_TAG, Box::new(Value::from("name"))),
Value::from("Alice Example"),
),
(
Value::from("roles"),
Value::Array(vec![
Value::Tag(TO_BE_REDACTED_TAG, Box::new(Value::from("admin"))),
Value::from("user"),
]),
),
(
Value::Tag(TO_BE_DECOY_TAG, Box::new(Value::from(1))),
Value::Null,
),
]);
let mut salts = deterministic_demo_salts();
let issued = issue_from_preissuance(preissued_claims, &mut salts, &Sha256RedactionHasher)?;
let mut sd_cwt = Sign1Message::new(Some(cbor2::to_vec(&issued.value)?));
set_sd_cwt_typ(&mut sd_cwt.protected);
set_sd_alg(&mut sd_cwt.protected, Sha256RedactionHasher.algorithm());
set_disclosures(&mut sd_cwt.unprotected, issued.disclosures.as_slice());
let encoded = sd_cwt.sign_and_encode(&Issuer, None)?;
let holder_sd_cwt = Sign1Message::verify_and_decode(&IssuerVerifier, &encoded, None)?;
let holder_claims = restore_payload_from_message(&holder_sd_cwt, RestoreMode::Holder)?;
println!("holder disclosed claims: {}", holder_claims.disclosed);
let mut presented = holder_sd_cwt.clone();
let selected = issued.disclosures.as_slice()[0].clone();
set_disclosures(&mut presented.unprotected, std::slice::from_ref(&selected));
let verifier_claims = restore_payload_from_message(&presented, RestoreMode::Verifier)?;
println!(
"verifier disclosed claims: {}, removed redactions: {}",
verifier_claims.disclosed, verifier_claims.removed_redactions
);
Ok(())
}