use crate::{
common::{
self,
types::{KeyIndex, KeyPair, TransactionTime},
*,
},
curve_arithmetic::Curve,
dodis_yampolskiy_prf as prf,
elgamal::{PublicKey, SecretKey},
id::{
account_holder::*,
anonymity_revoker::*,
chain::*,
constants::{ArCurve, BaseField, IpPairing, *},
identity_provider::*,
secret_sharing::Threshold,
types::*,
},
};
use ed25519::SigningKey;
use ed25519_dalek as ed25519;
use either::Either::Left;
use rand::*;
use std::{collections::BTreeMap, convert::TryFrom};
type ExampleAttribute = AttributeKind;
pub type ExampleAttributeList = AttributeList<BaseField, ExampleAttribute>;
pub const EXPIRY: TransactionTime = TransactionTime {
seconds: 111111111111111111,
};
pub fn test_create_ars<T: Rng>(
ar_base: &ArCurve,
num_ars: u8,
csprng: &mut T,
) -> (
BTreeMap<ArIdentity, ArInfo<ArCurve>>,
BTreeMap<ArIdentity, SecretKey<ArCurve>>,
) {
let mut ar_infos = BTreeMap::new();
let mut ar_keys = BTreeMap::new();
for i in 1..=num_ars {
let ar_id = ArIdentity::new(i as u32);
let ar_secret_key = SecretKey::generate(ar_base, csprng);
let ar_public_key = PublicKey::from(&ar_secret_key);
let ar_info = ArInfo::<ArCurve> {
ar_identity: ar_id,
ar_description: Description {
name: format!("AnonymityRevoker{}", i),
url: format!("AnonymityRevoker{}.com", i),
description: format!("AnonymityRevoker{}", i),
},
ar_public_key,
};
let _ = ar_infos.insert(ar_id, ar_info);
let _ = ar_keys.insert(ar_id, ar_secret_key);
}
(ar_infos, ar_keys)
}
pub fn test_create_ip_info<T: Rng + rand::CryptoRng>(
csprng: &mut T,
num_ars: u8,
max_attrs: u8,
) -> IpData<IpPairing> {
let ps_len = (5 + num_ars + max_attrs) as usize;
let ip_secret_key = crate::ps_sig::SecretKey::<IpPairing>::generate(ps_len, csprng);
let ip_verify_key = crate::ps_sig::PublicKey::from(&ip_secret_key);
let signing = SigningKey::generate(csprng);
let secret = signing.to_bytes();
let ip_cdi_verify_key = signing.verifying_key();
let ip_cdi_secret_key = secret;
IpData {
public_ip_info: IpInfo {
ip_identity: IpIdentity(0),
ip_description: Description {
name: "IP0".to_owned(),
url: "IP0.com".to_owned(),
description: "IP0".to_owned(),
},
ip_verify_key,
ip_cdi_verify_key,
},
ip_secret_key,
ip_cdi_secret_key,
}
}
pub fn test_create_aci<T: Rng>(csprng: &mut T) -> AccCredentialInfo<ArCurve> {
let ah_info = CredentialHolderInfo::<ArCurve> {
id_cred: IdCredentials::generate(csprng),
};
let prf_key = prf::SecretKey::generate(csprng);
AccCredentialInfo {
cred_holder_info: ah_info,
prf_key,
}
}
pub fn test_create_id_use_data<T: Rng>(csprng: &mut T) -> IdObjectUseData<IpPairing, ArCurve> {
let aci = test_create_aci(csprng);
let randomness = crate::ps_sig::SigRetrievalRandomness::generate_non_zero(csprng);
IdObjectUseData { aci, randomness }
}
pub fn test_create_pio<'a>(
id_use_data: &IdObjectUseData<IpPairing, ArCurve>,
ip_info: &'a IpInfo<IpPairing>,
ars_infos: &'a BTreeMap<ArIdentity, ArInfo<ArCurve>>,
global_ctx: &'a GlobalContext<ArCurve>,
num_ars: u8, initial_account_data: &InitialAccountData,
) -> (
IpContext<'a, IpPairing, ArCurve>,
PreIdentityObject<IpPairing, ArCurve>,
crate::ps_sig::SigRetrievalRandomness<IpPairing>,
) {
let context = IpContext::new(ip_info, ars_infos, global_ctx);
let threshold =
Threshold::try_from(num_ars - 1).unwrap_or(Threshold::try_new(1).expect("threshold"));
let (pio, randomness) = generate_pio(&context, threshold, id_use_data, initial_account_data)
.expect("Generating the pre-identity object should succeed.");
(context, pio, randomness)
}
pub fn test_create_pio_v1<'a>(
id_use_data: &IdObjectUseData<IpPairing, ArCurve>,
ip_info: &'a IpInfo<IpPairing>,
ars_infos: &'a BTreeMap<ArIdentity, ArInfo<ArCurve>>,
global_ctx: &'a GlobalContext<ArCurve>,
num_ars: u8, csprng: &mut (impl Rng + CryptoRng),
) -> (
IpContext<'a, IpPairing, ArCurve>,
PreIdentityObjectV1<IpPairing, ArCurve>,
crate::ps_sig::SigRetrievalRandomness<IpPairing>,
) {
let context = IpContext::new(ip_info, ars_infos, global_ctx);
let threshold =
Threshold::try_from(num_ars - 1).unwrap_or(Threshold::try_new(1).expect("threshold"));
let (pio, randomness) = generate_pio_v1_with_rng(&context, threshold, id_use_data, csprng)
.expect("Generating the pre-identity object should succeed.");
(context, pio, randomness)
}
pub fn test_create_attributes() -> ExampleAttributeList {
let mut alist = BTreeMap::new();
alist.insert(AttributeTag::from(0u8), AttributeKind::from(55));
alist.insert(AttributeTag::from(8u8), AttributeKind::from(31));
let valid_to = YearMonth::try_from(2022 << 8 | 5).unwrap(); let created_at = YearMonth::try_from(2020 << 8 | 5).unwrap(); ExampleAttributeList {
valid_to,
created_at,
max_accounts: 237,
alist,
_phantom: Default::default(),
}
}
#[test]
pub fn test_pipeline() {
let mut csprng = thread_rng();
let max_attrs = 10;
let num_ars = 5;
let IpData {
public_ip_info: ip_info,
ip_secret_key,
ip_cdi_secret_key,
} = test_create_ip_info(&mut csprng, num_ars, max_attrs);
let global_ctx = GlobalContext::generate(String::from("genesis_string"));
let (ars_infos, ars_secret) =
test_create_ars(&global_ctx.on_chain_commitment_key.g, num_ars, &mut csprng);
let id_use_data = test_create_id_use_data(&mut csprng);
let acc_data = InitialAccountData {
keys: {
let mut keys = BTreeMap::new();
keys.insert(KeyIndex(0), KeyPair::generate(&mut csprng));
keys.insert(KeyIndex(1), KeyPair::generate(&mut csprng));
keys.insert(KeyIndex(2), KeyPair::generate(&mut csprng));
keys
},
threshold: SignatureThreshold::TWO,
};
let (context, pio, _) = test_create_pio(
&id_use_data,
&ip_info,
&ars_infos,
&global_ctx,
num_ars,
&acc_data,
);
let alist = test_create_attributes();
let ver_ok = verify_credentials(
&pio,
context,
&alist,
EXPIRY,
&ip_secret_key,
&ip_cdi_secret_key,
);
assert!(ver_ok.is_ok(), "Signature on the credential is invalid.");
let (ip_sig, initial_cdi) = ver_ok.unwrap();
let cdi_check = verify_initial_cdi(&ip_info, &initial_cdi, EXPIRY);
assert_eq!(cdi_check, Ok(()));
let initial_cdi_values = serialize_deserialize(&initial_cdi.values);
assert!(
initial_cdi_values.is_ok(),
"INITIAL VALUES Deserialization must be successful."
);
let initial_cdi_sig = serialize_deserialize(&initial_cdi.sig);
assert!(
initial_cdi_sig.is_ok(),
"Signature deserialization must be successful."
);
let des_initial = serialize_deserialize(&initial_cdi);
assert!(des_initial.is_ok(), "Deserialization must be successful.");
let id_object = IdentityObject {
pre_identity_object: pio,
alist,
signature: ip_sig,
};
let valid_to = YearMonth::try_from(2022 << 8 | 5).unwrap(); let created_at = YearMonth::try_from(2020 << 8 | 5).unwrap(); let policy = Policy {
valid_to,
created_at,
policy_vec: {
let mut tree = BTreeMap::new();
tree.insert(AttributeTag::from(8u8), AttributeKind::from(31));
tree
},
_phantom: Default::default(),
};
let acc_data = CredentialData {
keys: {
let mut keys = BTreeMap::new();
keys.insert(KeyIndex(0), KeyPair::generate(&mut csprng));
keys.insert(KeyIndex(1), KeyPair::generate(&mut csprng));
keys.insert(KeyIndex(2), KeyPair::generate(&mut csprng));
keys
},
threshold: SignatureThreshold::TWO,
};
let (cdi, _) = create_credential(
context,
&id_object,
&id_use_data,
0,
policy.clone(),
&acc_data,
&SystemAttributeRandomness {},
&Left(EXPIRY),
)
.expect("Should generate the credential successfully.");
let cdi_check = verify_cdi(&global_ctx, &ip_info, &ars_infos, &cdi, &Left(EXPIRY));
assert_eq!(cdi_check, Ok(()));
let cdi_values = serialize_deserialize(&cdi.values);
assert!(
cdi_values.is_ok(),
"VALUES Deserialization must be successful."
);
let cdi_commitments = serialize_deserialize(&cdi.proofs.id_proofs.commitments);
assert!(
cdi_commitments.is_ok(),
"commitments Deserialization must be successful."
);
let cdi_proofs = serialize_deserialize(&cdi.proofs);
assert!(
cdi_proofs.is_ok(),
"Proof deserialization must be successful."
);
let des = serialize_deserialize(&cdi);
assert!(des.is_ok(), "Deserialization must be successful.");
assert_eq!(
des.unwrap().proofs.id_proofs.proof_reg_id,
cdi.proofs.id_proofs.proof_reg_id,
"It should deserialize back to what we started with."
);
let mut shares = Vec::new();
for (ar_id, key) in ars_secret.iter().skip(1) {
let ar = cdi
.values
.ar_data
.get(ar_id)
.unwrap_or_else(|| panic!("Anonymity revoker {} is not present.", ar_id));
let decrypted_share = (*ar_id, key.decrypt(&ar.enc_id_cred_pub_share));
shares.push(decrypted_share);
}
let revealed_id_cred_pub = reveal_id_cred_pub(&shares);
assert_eq!(
revealed_id_cred_pub,
global_ctx
.on_chain_commitment_key
.g
.mul_by_scalar(&id_use_data.aci.cred_holder_info.id_cred.id_cred_sec)
);
let (mut cdi, _) = create_credential(
context,
&id_object,
&id_use_data,
0,
policy,
&acc_data,
&SystemAttributeRandomness {},
&Left(EXPIRY),
)
.expect("Should generate the credential successfully.");
let x_2 = cdi
.values
.ar_data
.get(&ArIdentity::new(2))
.expect("AR 2 exists")
.clone();
let x_3 = cdi
.values
.ar_data
.get(&ArIdentity::new(3))
.expect("AR 3 exists")
.clone();
*cdi.values
.ar_data
.get_mut(&ArIdentity::new(2))
.expect("AR 2 exists") = x_3;
*cdi.values
.ar_data
.get_mut(&ArIdentity::new(3))
.expect("AR 2 exists") = x_2;
let cdi_check = verify_cdi(&global_ctx, &ip_info, &ars_infos, &cdi, &Left(EXPIRY));
assert_ne!(cdi_check, Ok(()));
}
#[test]
pub fn test_pipeline_v1() {
let mut csprng = thread_rng();
let max_attrs = 10;
let num_ars = 5;
let IpData {
public_ip_info: ip_info,
ip_secret_key,
..
} = test_create_ip_info(&mut csprng, num_ars, max_attrs);
let global_ctx = GlobalContext::generate(String::from("genesis_string"));
let (ars_infos, ars_secret) =
test_create_ars(&global_ctx.on_chain_commitment_key.g, num_ars, &mut csprng);
let id_use_data = test_create_id_use_data(&mut csprng);
let (context, pio, randomness) = test_create_pio_v1(
&id_use_data,
&ip_info,
&ars_infos,
&global_ctx,
num_ars,
&mut csprng,
);
assert!(
*randomness == *id_use_data.randomness,
"Returned randomness is not equal to used randomness."
);
let alist = test_create_attributes();
let ver_ok = verify_credentials_v1(&pio, context, &alist, &ip_secret_key);
assert!(ver_ok.is_ok(), "Signature on the credential is invalid.");
let ip_sig = ver_ok.unwrap();
let id_object = IdentityObjectV1 {
pre_identity_object: pio,
alist,
signature: ip_sig,
};
let valid_to = YearMonth::try_from(2022 << 8 | 5).unwrap(); let created_at = YearMonth::try_from(2020 << 8 | 5).unwrap(); let policy = Policy {
valid_to,
created_at,
policy_vec: {
let mut tree = BTreeMap::new();
tree.insert(AttributeTag::from(8u8), AttributeKind::from(31));
tree
},
_phantom: Default::default(),
};
let acc_data = CredentialData {
keys: {
let mut keys = BTreeMap::new();
keys.insert(KeyIndex(0), KeyPair::generate(&mut csprng));
keys.insert(KeyIndex(1), KeyPair::generate(&mut csprng));
keys.insert(KeyIndex(2), KeyPair::generate(&mut csprng));
keys
},
threshold: SignatureThreshold::TWO,
};
let (cdi, _) = create_credential(
context,
&id_object,
&id_use_data,
0,
policy.clone(),
&acc_data,
&SystemAttributeRandomness {},
&Left(EXPIRY),
)
.expect("Should generate the credential successfully.");
let cdi_check = verify_cdi(&global_ctx, &ip_info, &ars_infos, &cdi, &Left(EXPIRY));
assert_eq!(cdi_check, Ok(()));
let cdi_values = serialize_deserialize(&cdi.values);
assert!(
cdi_values.is_ok(),
"VALUES Deserialization must be successful."
);
let cdi_commitments = serialize_deserialize(&cdi.proofs.id_proofs.commitments);
assert!(
cdi_commitments.is_ok(),
"commitments Deserialization must be successful."
);
let cdi_proofs = serialize_deserialize(&cdi.proofs);
assert!(
cdi_proofs.is_ok(),
"Proof deserialization must be successful."
);
let des = serialize_deserialize(&cdi);
assert!(des.is_ok(), "Deserialization must be successful.");
assert_eq!(
des.unwrap().proofs.id_proofs.proof_reg_id,
cdi.proofs.id_proofs.proof_reg_id,
"It should deserialize back to what we started with."
);
let mut shares = Vec::new();
for (ar_id, key) in ars_secret.iter().skip(1) {
let ar = cdi
.values
.ar_data
.get(ar_id)
.unwrap_or_else(|| panic!("Anonymity revoker {} is not present.", ar_id));
let decrypted_share = (*ar_id, key.decrypt(&ar.enc_id_cred_pub_share));
shares.push(decrypted_share);
}
let revealed_id_cred_pub = reveal_id_cred_pub(&shares);
assert_eq!(
revealed_id_cred_pub,
global_ctx
.on_chain_commitment_key
.g
.mul_by_scalar(&id_use_data.aci.cred_holder_info.id_cred.id_cred_sec)
);
let (mut cdi, _) = create_credential(
context,
&id_object,
&id_use_data,
0,
policy,
&acc_data,
&SystemAttributeRandomness {},
&Left(EXPIRY),
)
.expect("Should generate the credential successfully.");
let x_2 = cdi
.values
.ar_data
.get(&ArIdentity::new(2))
.expect("AR 2 exists")
.clone();
let x_3 = cdi
.values
.ar_data
.get(&ArIdentity::new(3))
.expect("AR 3 exists")
.clone();
*cdi.values
.ar_data
.get_mut(&ArIdentity::new(2))
.expect("AR 2 exists") = x_3;
*cdi.values
.ar_data
.get_mut(&ArIdentity::new(3))
.expect("AR 2 exists") = x_2;
let cdi_check = verify_cdi(&global_ctx, &ip_info, &ars_infos, &cdi, &Left(EXPIRY));
assert_ne!(cdi_check, Ok(()));
}
fn seed0() -> rand::rngs::StdRng {
rand::rngs::StdRng::seed_from_u64(0)
}
#[test]
pub fn test_pipline_stable() {
let max_attrs = 10;
let num_ars = 5;
let IpData {
public_ip_info: ip_info,
ip_secret_key,
ip_cdi_secret_key,
} = test_create_ip_info(&mut seed0(), num_ars, max_attrs);
let global_ctx = GlobalContext::generate(String::from("genesis_string"));
let alist = test_create_attributes();
let (ars_infos, _) =
test_create_ars(&global_ctx.on_chain_commitment_key.g, num_ars, &mut seed0());
let context = IpContext::<'_, _, _> {
ip_info: &ip_info,
ars_infos: &ars_infos,
global_context: &global_ctx,
};
let pio_stable_bytes_hex = include_str!("test/pio_stable_bytes.hex");
let pio_stable_bytes = hex::decode(&pio_stable_bytes_hex).unwrap();
let pio_stable: PreIdentityObject<IpPairing, ArCurve> =
common::from_bytes(&mut pio_stable_bytes.as_slice())
.expect("Could not deserialize stable pio");
let ver_ok = verify_credentials(
&pio_stable,
context,
&alist,
EXPIRY,
&ip_secret_key,
&ip_cdi_secret_key,
);
assert!(ver_ok.is_ok(), "Signature on the credential is invalid.");
let init_cdi_stable_bytes_hex = include_str!("test/init_cdi_stable_bytes.hex");
let init_cdi_stable_bytes = hex::decode(&init_cdi_stable_bytes_hex).unwrap();
let init_cdi_stable: InitialCredentialDeploymentInfo<ArCurve, AttributeKind> =
common::from_bytes(&mut init_cdi_stable_bytes.as_slice())
.expect("Could not deserialize stable initial cdi");
let cdi_check = verify_initial_cdi(&ip_info, &init_cdi_stable, EXPIRY);
assert_eq!(cdi_check, Ok(()));
let cdi_stable_bytes_hex = include_str!("test/cdi_stable_bytes.hex");
let cdi_stable_bytes = hex::decode(&cdi_stable_bytes_hex).unwrap();
let cdi_stable: CredentialDeploymentInfo<IpPairing, ArCurve, AttributeKind> =
common::from_bytes(&mut cdi_stable_bytes.as_slice())
.expect("Could not deserialize stable pio");
let cdi_check = verify_cdi(
&global_ctx,
&ip_info,
&ars_infos,
&cdi_stable,
&Left(EXPIRY),
);
assert_eq!(cdi_check, Ok(()));
}
#[test]
pub fn test_pipline_v1_stable() {
let max_attrs = 10;
let num_ars = 5;
let IpData {
public_ip_info: ip_info,
ip_secret_key,
..
} = test_create_ip_info(&mut seed0(), num_ars, max_attrs);
let global_ctx = GlobalContext::generate(String::from("genesis_string"));
let alist = test_create_attributes();
let (ars_infos, _) =
test_create_ars(&global_ctx.on_chain_commitment_key.g, num_ars, &mut seed0());
let context = IpContext::<'_, _, _> {
ip_info: &ip_info,
ars_infos: &ars_infos,
global_context: &global_ctx,
};
let pio_stable_bytes_hex = include_str!("test/pio_v1_stable_bytes.hex");
let pio_stable_bytes = hex::decode(&pio_stable_bytes_hex).unwrap();
let pio_stable: PreIdentityObjectV1<IpPairing, ArCurve> =
common::from_bytes(&mut pio_stable_bytes.as_slice())
.expect("Could not deserialize stable pio");
let ver_ok = verify_credentials_v1(&pio_stable, context, &alist, &ip_secret_key);
assert!(ver_ok.is_ok(), "Signature on the credential is invalid.");
let cdi_stable_bytes_hex = include_str!("test/cdi_v1_stable_bytes.hex");
let cdi_stable_bytes = hex::decode(&cdi_stable_bytes_hex).unwrap();
let cdi_stable: CredentialDeploymentInfo<IpPairing, ArCurve, AttributeKind> =
common::from_bytes(&mut cdi_stable_bytes.as_slice())
.expect("Could not deserialize stable pio");
let cdi_check = verify_cdi(
&global_ctx,
&ip_info,
&ars_infos,
&cdi_stable,
&Left(EXPIRY),
);
assert_eq!(cdi_check, Ok(()));
}
#[test]
fn test_validate_id_recovery_request_stable() {
let max_attrs = 10;
let num_ars = 4;
let IpData {
public_ip_info: ip_info,
..
} = test_create_ip_info(&mut seed0(), num_ars, max_attrs);
let global_ctx = GlobalContext::<ArCurve>::generate(String::from("genesis_string"));
let req_bytes_hex_stable = "856793e4ba5d058cea0b5c3a1c8affb272efcf53bbab77ee28d3e2270d5041d220c1e1a9c6c8619c84e40ebd70fb583e00000000000003e8195ee98f613d9c804f37de1af16fa6c5961542eebd55f90abbef2b6f540e14d01bc2c96f80e35f2e2491e871d296c76caded01d62603a9e73980d9268f045547";
let req_bytes_stable = hex::decode(&req_bytes_hex_stable).unwrap();
let req_stable: IdRecoveryRequest<ArCurve> =
common::from_bytes(&mut req_bytes_stable.as_slice())
.expect("Could not deserialize stable request");
let result = validate_id_recovery_request(&ip_info, &global_ctx, &req_stable);
assert!(result);
}