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