1use cbor2::Value;
2use cose2::{iana, Error, Label, Sign1Message, Signer, Verifier};
3use sd_cwt::{
4 issue_from_preissuance, restore_payload_from_message, set_disclosures, set_sd_alg,
5 set_sd_cwt_typ, RedactionHasher, RestoreMode, Sha256RedactionHasher, TO_BE_DECOY_TAG,
6 TO_BE_REDACTED_TAG,
7};
8
9fn toy_tag(secret: &[u8], data: &[u8]) -> Vec<u8> {
10 let mut out = [0u8; 16];
11 for (i, byte) in secret.iter().chain(data).enumerate() {
12 out[i % out.len()] ^= byte.wrapping_add(i as u8);
13 }
14 out.to_vec()
15}
16
17struct Issuer;
18
19impl Signer for Issuer {
20 fn alg(&self) -> Option<Label> {
21 Some(iana::AlgorithmEdDSA.into())
22 }
23
24 fn kid(&self) -> Option<&[u8]> {
25 Some(b"issuer-key-1")
26 }
27
28 fn sign(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
29 Ok(toy_tag(b"issuer signing secret", data))
30 }
31}
32
33struct IssuerVerifier;
34
35impl Verifier for IssuerVerifier {
36 fn alg(&self) -> Option<Label> {
37 Some(iana::AlgorithmEdDSA.into())
38 }
39
40 fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), Error> {
41 if toy_tag(b"issuer signing secret", data) == signature {
42 Ok(())
43 } else {
44 Err(Error::verify("SD-CWT issuer signature mismatch"))
45 }
46 }
47}
48
49fn deterministic_demo_salts() -> impl FnMut() -> [u8; 16] {
50 let mut next = 1u8;
51 move || {
52 let salt = [next; 16];
53 next = next.wrapping_add(1);
54 salt
55 }
56}
57
58fn main() -> Result<(), Error> {
59 let preissued_claims = Value::Map(vec![
62 (Value::from(1), Value::from("https://issuer.example")),
63 (Value::from(8), Value::Map(vec![])), (
65 Value::Tag(TO_BE_REDACTED_TAG, Box::new(Value::from("name"))),
66 Value::from("Alice Example"),
67 ),
68 (
69 Value::from("roles"),
70 Value::Array(vec![
71 Value::Tag(TO_BE_REDACTED_TAG, Box::new(Value::from("admin"))),
72 Value::from("user"),
73 ]),
74 ),
75 (
76 Value::Tag(TO_BE_DECOY_TAG, Box::new(Value::from(1))),
77 Value::Null,
78 ),
79 ]);
80
81 let mut salts = deterministic_demo_salts();
84 let issued = issue_from_preissuance(preissued_claims, &mut salts, &Sha256RedactionHasher)?;
85
86 let mut sd_cwt = Sign1Message::new(Some(cbor2::to_vec(&issued.value)?));
89 set_sd_cwt_typ(&mut sd_cwt.protected);
90 set_sd_alg(&mut sd_cwt.protected, Sha256RedactionHasher.algorithm());
91 set_disclosures(&mut sd_cwt.unprotected, issued.disclosures.as_slice());
92 let encoded = sd_cwt.sign_and_encode(&Issuer, None)?;
93
94 let holder_sd_cwt = Sign1Message::verify_and_decode(&IssuerVerifier, &encoded, None)?;
97 let holder_claims = restore_payload_from_message(&holder_sd_cwt, RestoreMode::Holder)?;
98 println!("holder disclosed claims: {}", holder_claims.disclosed);
99
100 let mut presented = holder_sd_cwt.clone();
104 let selected = issued.disclosures.as_slice()[0].clone();
105 set_disclosures(&mut presented.unprotected, std::slice::from_ref(&selected));
106
107 let verifier_claims = restore_payload_from_message(&presented, RestoreMode::Verifier)?;
108 println!(
109 "verifier disclosed claims: {}, removed redactions: {}",
110 verifier_claims.disclosed, verifier_claims.removed_redactions
111 );
112
113 Ok(())
114}