1use baseid_core::claims::{ClaimDisclosure, ClaimPath, ClaimSet, DisclosureSelection};
2use baseid_core::lifecycle::*;
3use baseid_core::types::CredentialFormat;
4
5use crate::credential::{BbsClaim, BbsCredential, BbsDerivedProof};
6use crate::keys::BbsKeyPair;
7use crate::proof::{bbs_proof_gen, bbs_proof_verify};
8use crate::signing::{bbs_sign, bbs_verify};
9
10pub struct BbsLifecycle {
12 key_pair: BbsKeyPair,
13}
14
15impl BbsLifecycle {
16 pub fn new(key_pair: BbsKeyPair) -> Self {
17 Self { key_pair }
18 }
19
20 pub fn public_key_bytes(&self) -> &[u8] {
21 &self.key_pair.public_key
22 }
23}
24
25impl CredentialIssuer for BbsLifecycle {
26 fn format(&self) -> CredentialFormat {
27 CredentialFormat::Bbs
28 }
29
30 fn issue(
31 &self,
32 issuer_did: &str,
33 subject_did: Option<&str>,
34 claims: &ClaimSet,
35 options: &IssuanceOptions,
36 ) -> baseid_core::Result<IssuedCredential> {
37 let mut bbs_claims: Vec<BbsClaim> = Vec::new();
39 for (ns, claims_map) in claims.namespaces() {
40 for (name, value) in claims_map {
41 bbs_claims.push(BbsClaim {
42 namespace: ns.to_string(),
43 name: name.clone(),
44 value: value.clone(),
45 });
46 }
47 }
48
49 let cred = BbsCredential {
50 issuer: issuer_did.to_string(),
51 subject: subject_did.map(|s| s.to_string()),
52 types: if options.types.is_empty() {
53 vec!["VerifiableCredential".to_string()]
54 } else {
55 options.types.clone()
56 },
57 claims: bbs_claims,
58 signature: vec![], issuer_public_key: self.key_pair.public_key.clone(),
60 };
61
62 let messages = cred.encode_messages();
64 let signature = bbs_sign(&self.key_pair, &messages, None)
65 .map_err(|e| -> baseid_core::Error { e.into() })?;
66
67 let mut signed_cred = cred;
68 signed_cred.signature = signature;
69
70 let data = signed_cred.to_bytes()?;
71
72 Ok(IssuedCredential {
73 data,
74 format: CredentialFormat::Bbs,
75 id: options.credential_id.clone(),
76 issuer: issuer_did.to_string(),
77 subject: subject_did.map(|s| s.to_string()),
78 })
79 }
80}
81
82impl CredentialVerifier for BbsLifecycle {
83 fn format(&self) -> CredentialFormat {
84 CredentialFormat::Bbs
85 }
86
87 fn verify(&self, credential_data: &[u8]) -> baseid_core::Result<VerificationOutcome> {
88 let cred = BbsCredential::from_bytes(credential_data)?;
89 let messages = cred.encode_messages();
90
91 let valid = bbs_verify(&cred.issuer_public_key, &cred.signature, &messages, None)
92 .map_err(|e| -> baseid_core::Error { e.into() })?;
93
94 let mut claim_set = ClaimSet::new();
96 for claim in &cred.claims {
97 claim_set.insert(&claim.namespace, &claim.name, claim.value.clone());
98 }
99
100 Ok(VerificationOutcome {
101 valid,
102 format: CredentialFormat::Bbs,
103 issuer: cred.issuer,
104 subject: cred.subject,
105 claims: claim_set,
106 unlinkable: false, predicates_verified: vec![],
108 valid_until: None,
109 revocation_status: RevocationStatus::NotChecked,
110 })
111 }
112}
113
114impl CredentialPresenter for BbsLifecycle {
115 fn format(&self) -> CredentialFormat {
116 CredentialFormat::Bbs
117 }
118
119 fn present(
120 &self,
121 credential_data: &[u8],
122 disclosure: &DisclosureSelection,
123 options: &PresentationOptions,
124 ) -> baseid_core::Result<PresentedCredential> {
125 let cred = BbsCredential::from_bytes(credential_data)?;
126 let messages = cred.encode_messages();
127
128 let mut disclosed_indices = Vec::new();
130 let mut disclosed_claims = Vec::new();
131 let mut predicate_results = Vec::new();
132
133 for (i, claim) in cred.claims.iter().enumerate() {
134 match disclosure.get(&claim.namespace, &claim.name) {
135 Some(ClaimDisclosure::Reveal) | None => {
136 disclosed_indices.push(i);
138 disclosed_claims.push((i, claim.clone()));
139 }
140 Some(ClaimDisclosure::Hide) => {
141 }
143 Some(ClaimDisclosure::Predicate(predicate)) => {
144 let satisfied = crate::predicates::evaluate_predicate(&claim.value, predicate);
146 predicate_results.push((claim.name.clone(), satisfied));
147 }
149 }
150 }
151
152 let nonce = options.nonce.as_deref().map(|n| n.as_bytes());
154 let proof = bbs_proof_gen(
155 &cred.issuer_public_key,
156 &cred.signature,
157 &messages,
158 &disclosed_indices,
159 None,
160 nonce,
161 )
162 .map_err(|e| -> baseid_core::Error { e.into() })?;
163
164 let derived = BbsDerivedProof {
165 issuer: cred.issuer,
166 subject: cred.subject,
167 types: cred.types,
168 proof,
169 disclosed_claims,
170 total_claims: cred.claims.len(),
171 issuer_public_key: cred.issuer_public_key,
172 predicate_results,
173 nonce: nonce.map(|n| n.to_vec()),
174 };
175
176 let data = derived.to_bytes()?;
177
178 Ok(PresentedCredential {
179 data,
180 format: CredentialFormat::Bbs,
181 unlinkable: true,
182 })
183 }
184}
185
186pub fn verify_derived_proof(proof_data: &[u8]) -> baseid_core::Result<VerificationOutcome> {
188 let derived = BbsDerivedProof::from_bytes(proof_data)?;
189
190 let disclosed_msgs: Vec<(usize, Vec<u8>)> = derived
191 .disclosed_claims
192 .iter()
193 .map(|(i, claim)| {
194 let canonical = serde_json::json!({
195 "ns": claim.namespace,
196 "name": claim.name,
197 "value": claim.value,
198 });
199 (*i, serde_json::to_vec(&canonical).unwrap_or_default())
200 })
201 .collect();
202
203 let nonce_ref = derived.nonce.as_deref();
204 let valid = bbs_proof_verify(
205 &derived.issuer_public_key,
206 &derived.proof,
207 &disclosed_msgs,
208 None,
209 nonce_ref,
210 )
211 .map_err(|e| -> baseid_core::Error { e.into() })?;
212
213 let mut claim_set = ClaimSet::new();
215 for (_, claim) in &derived.disclosed_claims {
216 claim_set.insert(&claim.namespace, &claim.name, claim.value.clone());
217 }
218
219 let predicates_verified: Vec<(ClaimPath, baseid_core::claims::PredicateType)> = vec![];
220
221 Ok(VerificationOutcome {
222 valid,
223 format: CredentialFormat::Bbs,
224 issuer: derived.issuer,
225 subject: derived.subject,
226 claims: claim_set,
227 unlinkable: true,
228 predicates_verified,
229 valid_until: None,
230 revocation_status: RevocationStatus::NotChecked,
231 })
232}