1use alloc::collections::BTreeMap;
4use alloc::string::String;
5use alloc::string::ToString;
6use alloc::vec::Vec;
7use ibs::gg::Identity;
8
9use crate::error::Error;
10use ibe::kem::IBKEM;
11use ibe::Derive;
12use serde::{Deserialize, Serialize};
13use tiny_keccak::{Hasher, Sha3};
14
15const IDENTITY_UNSET: u64 = u64::MAX;
16const MAX_CON: usize = (IDENTITY_UNSET as usize - 1) >> 1;
17const AMOUNT_CHARS_TO_HIDE: usize = 4;
18const HINT_TYPES: &[&str] = &[
19 "pbdf.sidn-pbdf.mobilenumber.mobilenumber",
20 "pbdf.pbdf.surfnet-2.id",
21 "pbdf.nuts.agb.agbcode",
22 "irma-demo.sidn-pbdf.mobilenumber.mobilenumber",
23 "irma-demo.nuts.agb.agbcode",
24];
25
26pub type EncryptionPolicy = BTreeMap<String, Policy>;
28
29#[derive(Serialize, Deserialize, Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Default)]
31pub struct Attribute {
32 #[serde(rename = "t")]
33 pub atype: String,
35
36 #[serde(rename = "v")]
38 pub value: Option<String>,
39}
40
41#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
43pub struct Policy {
44 #[serde(rename = "ts")]
46 pub timestamp: u64,
47
48 pub con: Vec<Attribute>,
50}
51
52#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
57pub struct HiddenPolicy {
58 #[serde(rename = "ts")]
60 pub timestamp: u64,
61
62 pub con: Vec<Attribute>,
64}
65
66impl Attribute {
67 fn hintify_value(&self) -> Attribute {
68 let hidden_value = self.value.as_ref().map(|v| {
69 if HINT_TYPES.contains(&&self.atype[..]) {
70 let (begin, end) = v.split_at(v.len().saturating_sub(AMOUNT_CHARS_TO_HIDE));
71 format!("{begin}{}", "*".repeat(end.len()))
72 } else {
73 "".to_string()
74 }
75 });
76
77 Attribute {
78 atype: self.atype.clone(),
79 value: hidden_value,
80 }
81 }
82}
83
84impl Policy {
85 pub fn to_hidden(&self) -> HiddenPolicy {
87 HiddenPolicy {
88 timestamp: self.timestamp,
89 con: self.con.iter().map(Attribute::hintify_value).collect(),
90 }
91 }
92
93 pub fn derive(&self) -> Result<[u8; 64], Error> {
95 if self.con.len() > MAX_CON {
108 return Err(Error::ConstraintViolation);
109 }
110
111 let mut tmp = [0u8; 64];
112 let mut pre_h = Sha3::v512();
113
114 pre_h.update(&[0x00]);
116
117 let mut copy = self.con.clone();
118 copy.sort();
119
120 for (i, ar) in copy.iter().enumerate() {
121 let mut f = Sha3::v512();
122
123 f.update(&((2 * i + 1) as u64).to_be_bytes());
124 let at_bytes = ar.atype.as_bytes();
125 f.update(&(at_bytes.len() as u64).to_be_bytes());
126 f.update(at_bytes);
127 f.finalize(&mut tmp);
128
129 pre_h.update(&tmp);
130
131 f = Sha3::v512();
133 f.update(&((2 * i + 2) as u64).to_be_bytes());
134
135 match &ar.value {
136 None => f.update(&IDENTITY_UNSET.to_be_bytes()),
137 Some(val) => {
138 let val_bytes = val.as_bytes();
139 f.update(&(val_bytes.len() as u64).to_be_bytes());
140 f.update(val_bytes);
141 }
142 }
143
144 f.finalize(&mut tmp);
145 pre_h.update(&tmp);
146 }
147
148 pre_h.update(&self.timestamp.to_be_bytes());
149 let mut res = [0u8; 64];
150 pre_h.finalize(&mut res);
151
152 Ok(res)
153 }
154
155 pub fn derive_kem<K: IBKEM>(&self) -> Result<<K as IBKEM>::Id, Error> {
157 Ok(<K as IBKEM>::Id::derive(&self.derive()?))
158 }
159
160 pub fn derive_ibs(&self) -> Result<ibs::gg::Identity, Error> {
162 Ok(Identity::from(&self.derive()?))
163 }
164}
165
166impl Attribute {
167 pub fn new(atype: &str, value: Option<&str>) -> Self {
169 let atype = atype.to_string();
170 let value = value.map(|s| s.to_string());
171
172 Attribute { atype, value }
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use crate::identity::{Attribute, Policy};
179 use crate::test::TestSetup;
180 use alloc::string::ToString;
181 use alloc::vec::Vec;
182 use ibe::kem::cgw_kv::CGWKV;
183
184 #[test]
185 fn test_ordering() {
186 let mut rng = rand::thread_rng();
187 let setup = TestSetup::new(&mut rng);
189
190 let policies: Vec<Policy> = setup.policy.into_values().collect();
191 let p1_derived = policies[1].derive_kem::<CGWKV>().unwrap();
192
193 let mut reversed = policies[1].clone();
194 reversed.con.reverse();
195 assert_eq!(&p1_derived, &reversed.derive_kem::<CGWKV>().unwrap());
196
197 reversed.timestamp += 1;
199 assert_ne!(&p1_derived, &reversed.derive_kem::<CGWKV>().unwrap());
200 }
201
202 #[test]
203 fn test_hints() {
204 let attr = Attribute {
205 atype: "pbdf.sidn-pbdf.mobilenumber.mobilenumber".to_string(),
206 value: Some("123456789".to_string()),
207 };
208 let hinted = attr.hintify_value();
209 assert_eq!(hinted.value, Some("12345****".to_string()));
210
211 let attr_short = Attribute {
212 atype: "pbdf.sidn-pbdf.mobilenumber.mobilenumber".to_string(),
213 value: Some("123".to_string()),
214 };
215 let hinted_short = attr_short.hintify_value();
216 assert_eq!(hinted_short.value, Some("***".to_string()));
217
218 let attr_not_whitelisted = Attribute {
219 atype: "pbdf.sidn-pbdf.mobilenumber.test".to_string(),
220 value: Some("123456789".to_string()),
221 };
222 let hinted_empty = attr_not_whitelisted.hintify_value();
223 assert_eq!(hinted_empty.value, Some("".to_string()));
224 }
225
226 #[test]
227 fn test_regression() {
228 let mut rng = rand::thread_rng();
229 let setup = TestSetup::new(&mut rng);
230
231 let kem_ids: [[u8; 64]; 5] = [
233 [
234 243, 215, 91, 185, 176, 144, 186, 190, 101, 135, 237, 186, 47, 183, 76, 243, 182,
235 195, 213, 35, 18, 38, 203, 7, 53, 157, 78, 193, 99, 141, 169, 0, 13, 112, 111, 32,
236 172, 75, 5, 106, 165, 47, 53, 111, 177, 2, 8, 107, 242, 252, 49, 241, 67, 229, 5,
237 191, 13, 17, 246, 216, 119, 186, 227, 119,
238 ],
239 [
240 245, 162, 197, 104, 15, 166, 248, 109, 79, 173, 252, 30, 92, 165, 193, 237, 255,
241 228, 162, 5, 42, 227, 151, 207, 97, 134, 20, 41, 20, 142, 220, 5, 234, 222, 45,
242 199, 163, 191, 112, 167, 52, 193, 120, 143, 245, 8, 24, 46, 8, 77, 183, 255, 32,
243 196, 251, 247, 233, 114, 16, 114, 69, 19, 88, 105,
244 ],
245 [
246 55, 240, 138, 50, 172, 20, 36, 194, 154, 137, 247, 125, 112, 215, 118, 219, 172,
247 226, 21, 87, 116, 226, 44, 228, 62, 148, 86, 82, 119, 154, 209, 89, 219, 49, 115,
248 130, 187, 57, 252, 108, 239, 118, 210, 165, 13, 53, 96, 200, 55, 211, 229, 32, 59,
249 140, 234, 87, 124, 64, 128, 223, 6, 248, 172, 238,
250 ],
251 [
252 224, 26, 15, 201, 109, 47, 252, 119, 219, 216, 15, 186, 65, 123, 47, 131, 130, 196,
253 248, 145, 241, 235, 13, 216, 182, 74, 236, 81, 198, 67, 28, 7, 114, 158, 252, 90,
254 123, 131, 138, 155, 56, 93, 46, 93, 160, 8, 72, 122, 193, 229, 123, 36, 69, 50,
255 189, 38, 183, 208, 7, 102, 249, 33, 219, 46,
256 ],
257 [
258 199, 241, 225, 34, 158, 92, 56, 128, 249, 122, 93, 192, 132, 106, 3, 247, 209, 109,
259 66, 92, 203, 108, 184, 198, 208, 254, 255, 150, 116, 17, 225, 112, 114, 121, 189,
260 231, 19, 215, 46, 246, 250, 211, 61, 254, 172, 44, 242, 18, 170, 49, 37, 56, 140,
261 217, 127, 97, 247, 210, 224, 181, 220, 246, 126, 140,
262 ],
263 ];
264
265 let ibs_ids: [[u8; 32]; 5] = [
266 [
267 180, 14, 93, 181, 36, 29, 110, 232, 226, 36, 52, 230, 202, 168, 128, 63, 18, 200,
268 133, 234, 142, 171, 42, 130, 204, 102, 83, 232, 69, 19, 188, 40,
269 ],
270 [
271 28, 98, 33, 83, 107, 211, 195, 182, 119, 220, 223, 113, 224, 225, 193, 22, 200,
272 249, 124, 48, 182, 122, 0, 65, 241, 201, 164, 104, 236, 175, 50, 108,
273 ],
274 [
275 254, 181, 235, 14, 113, 97, 93, 200, 45, 48, 184, 245, 237, 118, 89, 250, 199, 105,
276 213, 208, 27, 41, 189, 166, 246, 1, 105, 163, 244, 239, 78, 122,
277 ],
278 [
279 165, 205, 240, 238, 241, 135, 30, 175, 42, 99, 93, 112, 171, 40, 249, 246, 133,
280 162, 228, 144, 133, 77, 246, 199, 134, 77, 78, 182, 224, 66, 111, 239,
281 ],
282 [
283 22, 61, 147, 117, 0, 147, 225, 164, 134, 216, 244, 108, 165, 173, 205, 236, 24,
284 185, 73, 128, 9, 95, 91, 162, 155, 120, 67, 252, 138, 112, 249, 217,
285 ],
286 ];
287
288 for (p, (kem, ibs)) in setup
289 .policies
290 .iter()
291 .zip(kem_ids.iter().zip(ibs_ids.iter()))
292 {
293 let kem2 = p.derive_kem::<CGWKV>().unwrap();
294 let ibs2 = p.derive_ibs().unwrap();
295
296 assert_eq!(&kem[..], &kem2.0);
297 assert_eq!(&ibs::gg::Identity::from(&ibs), &ibs2);
298 }
299 }
300}