1use std::collections::BTreeMap;
4
5use crate::credential::AnonCredsCredential;
6use crate::proof::{ProofRequest, ProofResponse, RevealedAttribute};
7
8#[derive(Debug, Clone, Default)]
10pub struct CredentialHolder {
11 credentials: BTreeMap<String, AnonCredsCredential>,
13 next_id: u64,
15}
16
17impl CredentialHolder {
18 pub fn new() -> Self {
20 Self {
21 credentials: BTreeMap::new(),
22 next_id: 0,
23 }
24 }
25
26 pub fn store_credential(&mut self, credential: AnonCredsCredential) -> String {
28 let id = format!("anoncred-{}", self.next_id);
29 self.next_id += 1;
30 self.credentials.insert(id.clone(), credential);
31 id
32 }
33
34 pub fn find_credentials_for_request(
38 &self,
39 request: &ProofRequest,
40 ) -> Vec<(&str, &AnonCredsCredential)> {
41 self.credentials
42 .iter()
43 .filter(|(_, cred)| request.matches_credential(cred))
44 .map(|(id, cred)| (id.as_str(), cred))
45 .collect()
46 }
47
48 pub fn create_proof_response(
53 &self,
54 credential_id: &str,
55 request: &ProofRequest,
56 ) -> Option<ProofResponse> {
57 let credential = self.credentials.get(credential_id)?;
58
59 if !request.matches_credential(credential) {
60 return None;
61 }
62
63 let mut revealed_attrs = BTreeMap::new();
64
65 for (referent, attr) in &request.requested_attributes {
66 if let Some(ref name) = attr.name {
67 if let Some(value) = credential.values.get(name) {
68 revealed_attrs.insert(
69 referent.clone(),
70 RevealedAttribute {
71 sub_proof_index: 0,
72 raw: value.raw.clone(),
73 encoded: value.encoded.clone(),
74 },
75 );
76 }
77 }
78 if let Some(ref names) = attr.names {
79 for (i, name) in names.iter().enumerate() {
81 if let Some(value) = credential.values.get(name) {
82 let key = if names.len() == 1 {
83 referent.clone()
84 } else {
85 format!("{referent}_{i}")
86 };
87 revealed_attrs.insert(
88 key,
89 RevealedAttribute {
90 sub_proof_index: 0,
91 raw: value.raw.clone(),
92 encoded: value.encoded.clone(),
93 },
94 );
95 }
96 }
97 }
98 }
99
100 Some(ProofResponse {
101 revealed_attrs,
102 self_attested_attrs: BTreeMap::new(),
103 })
104 }
105
106 pub fn len(&self) -> usize {
108 self.credentials.len()
109 }
110
111 pub fn is_empty(&self) -> bool {
113 self.credentials.is_empty()
114 }
115
116 pub fn get(&self, id: &str) -> Option<&AnonCredsCredential> {
118 self.credentials.get(id)
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use crate::credential::AttributeValue;
126 use crate::proof::{RequestedAttribute, RequestedPredicate};
127
128 fn sample_credential() -> AnonCredsCredential {
129 let mut values = BTreeMap::new();
130 values.insert(
131 "name".to_string(),
132 AttributeValue {
133 raw: "Alice".to_string(),
134 encoded: "123".to_string(),
135 },
136 );
137 values.insert(
138 "degree".to_string(),
139 AttributeValue {
140 raw: "Computer Science".to_string(),
141 encoded: "456".to_string(),
142 },
143 );
144 values.insert(
145 "university".to_string(),
146 AttributeValue {
147 raw: "University of Toronto".to_string(),
148 encoded: "789".to_string(),
149 },
150 );
151 AnonCredsCredential {
152 schema_id: "did:sov:NcYx:2:degree:1.0".to_string(),
153 cred_def_id: "did:sov:NcYx:3:CL:12:default".to_string(),
154 rev_reg_id: None,
155 issuer_did: "did:sov:NcYx".to_string(),
156 values,
157 }
158 }
159
160 fn sample_proof_request() -> ProofRequest {
161 let mut requested_attributes = BTreeMap::new();
162 requested_attributes.insert(
163 "attr1_referent".to_string(),
164 RequestedAttribute {
165 name: Some("name".to_string()),
166 names: None,
167 restrictions: None,
168 },
169 );
170 requested_attributes.insert(
171 "attr2_referent".to_string(),
172 RequestedAttribute {
173 name: Some("degree".to_string()),
174 names: None,
175 restrictions: None,
176 },
177 );
178
179 ProofRequest {
180 name: "degree-proof".to_string(),
181 version: "1.0".to_string(),
182 nonce: "12345".to_string(),
183 requested_attributes,
184 requested_predicates: BTreeMap::new(),
185 }
186 }
187
188 #[test]
189 fn store_and_retrieve() {
190 let mut holder = CredentialHolder::new();
191 assert!(holder.is_empty());
192
193 let id = holder.store_credential(sample_credential());
194 assert_eq!(holder.len(), 1);
195
196 let cred = holder.get(&id).unwrap();
197 assert_eq!(cred.get_raw_value("name"), Some("Alice"));
198 }
199
200 #[test]
201 fn find_credentials_for_request() {
202 let mut holder = CredentialHolder::new();
203 holder.store_credential(sample_credential());
204
205 let request = sample_proof_request();
206 let matches = holder.find_credentials_for_request(&request);
207 assert_eq!(matches.len(), 1);
208 }
209
210 #[test]
211 fn find_credentials_no_match() {
212 let mut holder = CredentialHolder::new();
213 holder.store_credential(sample_credential());
214
215 let mut requested_attributes = BTreeMap::new();
216 requested_attributes.insert(
217 "attr1_referent".to_string(),
218 RequestedAttribute {
219 name: Some("nonexistent".to_string()),
220 names: None,
221 restrictions: None,
222 },
223 );
224
225 let request = ProofRequest {
226 name: "bad-req".to_string(),
227 version: "1.0".to_string(),
228 nonce: "99999".to_string(),
229 requested_attributes,
230 requested_predicates: BTreeMap::new(),
231 };
232
233 let matches = holder.find_credentials_for_request(&request);
234 assert!(matches.is_empty());
235 }
236
237 #[test]
238 fn create_proof_response() {
239 let mut holder = CredentialHolder::new();
240 let id = holder.store_credential(sample_credential());
241
242 let request = sample_proof_request();
243 let response = holder.create_proof_response(&id, &request).unwrap();
244
245 assert_eq!(response.revealed_attrs.len(), 2);
246 assert_eq!(response.revealed_attrs["attr1_referent"].raw, "Alice");
247 assert_eq!(
248 response.revealed_attrs["attr2_referent"].raw,
249 "Computer Science"
250 );
251 }
252
253 #[test]
254 fn create_proof_response_missing_credential() {
255 let holder = CredentialHolder::new();
256 let request = sample_proof_request();
257 assert!(holder
258 .create_proof_response("nonexistent", &request)
259 .is_none());
260 }
261
262 #[test]
263 fn predicate_matching() {
264 let mut holder = CredentialHolder::new();
265 holder.store_credential(sample_credential());
266
267 let mut requested_predicates = BTreeMap::new();
268 requested_predicates.insert(
269 "pred1_referent".to_string(),
270 RequestedPredicate {
271 name: "name".to_string(),
272 p_type: ">=".to_string(),
273 p_value: 18,
274 },
275 );
276
277 let request = ProofRequest {
278 name: "pred-req".to_string(),
279 version: "1.0".to_string(),
280 nonce: "77777".to_string(),
281 requested_attributes: BTreeMap::new(),
282 requested_predicates,
283 };
284
285 let matches = holder.find_credentials_for_request(&request);
286 assert_eq!(matches.len(), 1);
288 }
289}