#![allow(clippy::expect_used, clippy::unwrap_used)]
use libpep::client::{decrypt, encrypt};
#[cfg(feature = "long")]
use libpep::data::long::{LongAttribute, LongPseudonym};
#[cfg(feature = "long")]
use libpep::data::records::LongEncryptedRecord;
use libpep::data::simple::*;
use libpep::factors::contexts::*;
use libpep::factors::{
AttributeRekeyInfo, EncryptionSecret, PseudonymRekeyInfo, PseudonymizationInfo,
PseudonymizationSecret, TranscryptionInfo,
};
use libpep::keys::*;
#[cfg(feature = "elgamal3")]
use libpep::transcryptor::rerandomize;
use libpep::transcryptor::{pseudonymize, rekey, transcrypt};
#[cfg(feature = "batch")]
use libpep::transcryptor::{pseudonymize_batch, rekey_batch, transcrypt_batch};
#[test]
fn test_core_flow() {
let rng = &mut rand::rng();
let (_pseudonym_global_public, pseudonym_global_secret) = make_pseudonym_global_keys(rng);
let (_attribute_global_public, attribute_global_secret) = make_attribute_global_keys(rng);
let pseudo_secret = PseudonymizationSecret::from("secret".into());
let enc_secret = EncryptionSecret::from("secret".into());
let domain1 = PseudonymizationDomain::from("domain1");
let session1 = EncryptionContext::from("session1");
let domain2 = PseudonymizationDomain::from("context2");
let session2 = EncryptionContext::from("session2");
let (pseudonym_session1_public, pseudonym_session1_secret) =
make_pseudonym_session_keys(&pseudonym_global_secret, &session1, &enc_secret);
let (_pseudonym_session2_public, pseudonym_session2_secret) =
make_pseudonym_session_keys(&pseudonym_global_secret, &session2, &enc_secret);
let (attribute_session1_public, attribute_session1_secret) =
make_attribute_session_keys(&attribute_global_secret, &session1, &enc_secret);
let (_attribute_session2_public, attribute_session2_secret) =
make_attribute_session_keys(&attribute_global_secret, &session2, &enc_secret);
let pseudo = Pseudonym::random(rng);
let enc_pseudo = encrypt(&pseudo, &pseudonym_session1_public, rng);
let data = Attribute::random(rng);
let enc_data = encrypt(&data, &attribute_session1_public, rng);
#[cfg(feature = "elgamal3")]
let dec_pseudo =
decrypt(&enc_pseudo, &pseudonym_session1_secret).expect("decryption should succeed");
#[cfg(not(feature = "elgamal3"))]
let dec_pseudo = decrypt(&enc_pseudo, &pseudonym_session1_secret);
#[cfg(feature = "elgamal3")]
let dec_data =
decrypt(&enc_data, &attribute_session1_secret).expect("decryption should succeed");
#[cfg(not(feature = "elgamal3"))]
let dec_data = decrypt(&enc_data, &attribute_session1_secret);
assert_eq!(pseudo, dec_pseudo);
assert_eq!(data, dec_data);
#[cfg(feature = "elgamal3")]
{
let rr_pseudo = rerandomize(&enc_pseudo, rng);
let rr_data = rerandomize(&enc_data, rng);
assert_ne!(enc_pseudo, rr_pseudo);
assert_ne!(enc_data, rr_data);
let rr_dec_pseudo =
decrypt(&rr_pseudo, &pseudonym_session1_secret).expect("decryption should succeed");
let rr_dec_data =
decrypt(&rr_data, &attribute_session1_secret).expect("decryption should succeed");
assert_eq!(pseudo, rr_dec_pseudo);
assert_eq!(data, rr_dec_data);
}
let transcryption_info = TranscryptionInfo::new(
&domain1,
&domain2,
&session1,
&session2,
&pseudo_secret,
&enc_secret,
);
let attribute_rekey_info = transcryption_info.attribute;
let rekeyed = rekey(&enc_data, &attribute_rekey_info);
#[cfg(feature = "elgamal3")]
let rekeyed_dec =
decrypt(&rekeyed, &attribute_session2_secret).expect("decryption should succeed");
#[cfg(not(feature = "elgamal3"))]
let rekeyed_dec = decrypt(&rekeyed, &attribute_session2_secret);
assert_eq!(data, rekeyed_dec);
let pseudonymized = transcrypt(&enc_pseudo, &transcryption_info);
#[cfg(feature = "elgamal3")]
let pseudonymized_dec =
decrypt(&pseudonymized, &pseudonym_session2_secret).expect("decryption should succeed");
#[cfg(not(feature = "elgamal3"))]
let pseudonymized_dec = decrypt(&pseudonymized, &pseudonym_session2_secret);
assert_ne!(pseudo, pseudonymized_dec);
let rev_pseudonymized = transcrypt(&pseudonymized, &transcryption_info.reverse());
#[cfg(feature = "elgamal3")]
let rev_pseudonymized_dec =
decrypt(&rev_pseudonymized, &pseudonym_session1_secret).expect("decryption should succeed");
#[cfg(not(feature = "elgamal3"))]
let rev_pseudonymized_dec = decrypt(&rev_pseudonymized, &pseudonym_session1_secret);
assert_eq!(pseudo, rev_pseudonymized_dec);
}
#[test]
#[cfg(feature = "batch")]
fn test_batch() {
let rng = &mut rand::rng();
let (_pseudonym_global_public, pseudonym_global_secret) = make_pseudonym_global_keys(rng);
let (_attribute_global_public, attribute_global_secret) = make_attribute_global_keys(rng);
let pseudo_secret = PseudonymizationSecret::from("secret".into());
let enc_secret = EncryptionSecret::from("secret".into());
let domain1 = PseudonymizationDomain::from("domain1");
let session1 = EncryptionContext::from("session1");
let domain2 = PseudonymizationDomain::from("domain2");
let session2 = EncryptionContext::from("session2");
let (pseudonym_session1_public, _pseudonym_session1_secret) =
make_pseudonym_session_keys(&pseudonym_global_secret, &session1, &enc_secret);
let (_pseudonym_session2_public, _pseudonym_session2_secret) =
make_pseudonym_session_keys(&pseudonym_global_secret, &session2, &enc_secret);
let (attribute_session1_public, _attribute_session1_secret) =
make_attribute_session_keys(&attribute_global_secret, &session1, &enc_secret);
let (_attribute_session2_public, _attribute_session2_secret) =
make_attribute_session_keys(&attribute_global_secret, &session2, &enc_secret);
let mut attributes = vec![];
let mut pseudonyms = vec![];
for _ in 0..10 {
attributes.push(encrypt(
&Attribute::random(rng),
&attribute_session1_public,
rng,
));
pseudonyms.push(encrypt(
&Pseudonym::random(rng),
&pseudonym_session1_public,
rng,
));
}
let transcryption_info = TranscryptionInfo::new(
&domain1,
&domain2,
&session1,
&session2,
&pseudo_secret,
&enc_secret,
);
let attribute_rekey_info = transcryption_info.attribute;
let _rekeyed = rekey_batch(&mut attributes, &attribute_rekey_info, rng);
let _pseudonymized = pseudonymize_batch(&mut pseudonyms, &transcryption_info.pseudonym, rng);
let mut data: Vec<(Vec<EncryptedPseudonym>, Vec<EncryptedAttribute>)> = vec![];
for _ in 0..10 {
let pseudonyms: Vec<EncryptedPseudonym> = (0..10)
.map(|_| encrypt(&Pseudonym::random(rng), &pseudonym_session1_public, rng))
.collect();
let attributes: Vec<EncryptedAttribute> = (0..10)
.map(|_| encrypt(&Attribute::random(rng), &attribute_session1_public, rng))
.collect();
data.push((pseudonyms, attributes));
}
let _ = data; }
#[test]
#[cfg(all(feature = "batch", feature = "long"))]
fn test_batch_long() {
let rng = &mut rand::rng();
let (_pseudonym_global_public, pseudonym_global_secret) = make_pseudonym_global_keys(rng);
let (_attribute_global_public, attribute_global_secret) = make_attribute_global_keys(rng);
let pseudo_secret = PseudonymizationSecret::from("secret".into());
let enc_secret = EncryptionSecret::from("secret".into());
let domain1 = PseudonymizationDomain::from("domain1");
let session1 = EncryptionContext::from("session1");
let domain2 = PseudonymizationDomain::from("domain2");
let session2 = EncryptionContext::from("session2");
let (pseudonym_session1_public, _pseudonym_session1_secret) =
make_pseudonym_session_keys(&pseudonym_global_secret, &session1, &enc_secret);
let (_pseudonym_session2_public, pseudonym_session2_secret) =
make_pseudonym_session_keys(&pseudonym_global_secret, &session2, &enc_secret);
let (attribute_session1_public, _attribute_session1_secret) =
make_attribute_session_keys(&attribute_global_secret, &session1, &enc_secret);
let (_attribute_session2_public, attribute_session2_secret) =
make_attribute_session_keys(&attribute_global_secret, &session2, &enc_secret);
let test_strings = [
"User 1 identifier string that spans multiple blocks",
"User 2 identifier string that spans multiple blocks",
"User 3 identifier string that spans multiple blocks",
];
let long_pseudonyms: Vec<_> = test_strings
.iter()
.map(|s| {
let long_pseudo = LongPseudonym::from_string_padded(s);
encrypt(&long_pseudo, &pseudonym_session1_public, rng)
})
.collect();
let long_attributes: Vec<_> = test_strings
.iter()
.map(|s| {
let long_attr = LongAttribute::from_string_padded(s);
encrypt(&long_attr, &attribute_session1_public, rng)
})
.collect();
let transcryption_info = TranscryptionInfo::new(
&domain1,
&domain2,
&session1,
&session2,
&pseudo_secret,
&enc_secret,
);
let rekeyed_pseudonyms = rekey_batch(
&mut long_pseudonyms.clone(),
&transcryption_info.pseudonym.k,
rng,
)
.unwrap();
assert_eq!(rekeyed_pseudonyms.len(), 3);
let rekeyed_attributes = rekey_batch(
&mut long_attributes.clone(),
&transcryption_info.attribute,
rng,
)
.unwrap();
assert_eq!(rekeyed_attributes.len(), 3);
for rekeyed_attr in rekeyed_attributes.iter() {
#[cfg(feature = "elgamal3")]
let decrypted =
decrypt(rekeyed_attr, &attribute_session2_secret).expect("decryption should succeed");
#[cfg(not(feature = "elgamal3"))]
let decrypted = decrypt(rekeyed_attr, &attribute_session2_secret);
let decrypted_string = decrypted.to_string_padded().unwrap();
assert!(test_strings.contains(&decrypted_string.as_str()));
}
let pseudonymized = pseudonymize_batch(
&mut long_pseudonyms.clone(),
&transcryption_info.pseudonym,
rng,
)
.unwrap();
assert_eq!(pseudonymized.len(), 3);
for pseudonymized_pseudo in pseudonymized.iter() {
#[cfg(feature = "elgamal3")]
let decrypted = decrypt(pseudonymized_pseudo, &pseudonym_session2_secret)
.expect("decryption should succeed");
#[cfg(not(feature = "elgamal3"))]
let decrypted = decrypt(pseudonymized_pseudo, &pseudonym_session2_secret);
assert_eq!(decrypted.0.len(), 4); }
let data: Vec<_> = (0..3)
.map(|i| {
let pseudo_str = format!("Entity {} pseudonym data", i);
let attr_str = format!("Entity {} attribute data", i);
let long_pseudonyms = vec![{
let long_pseudo = LongPseudonym::from_string_padded(&pseudo_str);
encrypt(&long_pseudo, &pseudonym_session1_public, rng)
}];
let long_attributes = vec![{
let long_attr = LongAttribute::from_string_padded(&attr_str);
encrypt(&long_attr, &attribute_session1_public, rng)
}];
LongEncryptedRecord::new(long_pseudonyms, long_attributes)
})
.collect();
let mut data_slice: Vec<_> = data.into_iter().collect();
let transcrypted = transcrypt_batch(&mut data_slice, &transcryption_info, rng)
.expect("Batch transcryption should succeed");
assert_eq!(transcrypted.len(), 3);
for record in transcrypted.iter() {
assert_eq!(record.pseudonyms.len(), 1);
assert_eq!(record.attributes.len(), 1);
#[cfg(feature = "elgamal3")]
let decrypted_attr = decrypt(&record.attributes[0], &attribute_session2_secret)
.expect("decryption should succeed");
#[cfg(not(feature = "elgamal3"))]
let decrypted_attr = decrypt(&record.attributes[0], &attribute_session2_secret);
let attr_str = decrypted_attr.to_string_padded().unwrap();
assert!(attr_str.starts_with("Entity ") && attr_str.ends_with(" attribute data"));
}
}
#[test]
fn test_pseudonymize_changes_encryption_context() {
let mut rng = rand::rng();
let (_, global_sk) = make_global_keys(&mut rng);
let from_ctx = EncryptionContext::from("from");
let to_ctx = EncryptionContext::from("to");
let enc_secret = EncryptionSecret::from(b"enc".to_vec());
let pseudo_secret = PseudonymizationSecret::from(b"pseudo".to_vec());
let from_domain = PseudonymizationDomain::from("domain-from");
let to_domain = PseudonymizationDomain::from("domain-to");
let from_session = make_session_keys(&global_sk, &from_ctx, &enc_secret);
let to_session = make_session_keys(&global_sk, &to_ctx, &enc_secret);
let pseudonym = Pseudonym::random(&mut rng);
let encrypted = encrypt(&pseudonym, &from_session.pseudonym.public, &mut rng);
let info = PseudonymizationInfo::new(
&from_domain,
&to_domain,
&from_ctx,
&to_ctx,
&pseudo_secret,
&enc_secret,
);
let pseudonymized = pseudonymize(&encrypted, &info);
#[cfg(feature = "elgamal3")]
let decrypted = decrypt(&pseudonymized, &to_session.pseudonym.secret).expect("decrypt failed");
#[cfg(not(feature = "elgamal3"))]
let decrypted = decrypt(&pseudonymized, &to_session.pseudonym.secret);
assert_ne!(pseudonym, decrypted);
}
#[test]
fn test_rekey_pseudonym_preserves_plaintext() {
let mut rng = rand::rng();
let (_, global_sk) = make_global_keys(&mut rng);
let from_ctx = EncryptionContext::from("from");
let to_ctx = EncryptionContext::from("to");
let enc_secret = EncryptionSecret::from(b"enc".to_vec());
let from_session = make_session_keys(&global_sk, &from_ctx, &enc_secret);
let to_session = make_session_keys(&global_sk, &to_ctx, &enc_secret);
let pseudonym = Pseudonym::random(&mut rng);
let encrypted = encrypt(&pseudonym, &from_session.pseudonym.public, &mut rng);
let rekey_info = PseudonymRekeyInfo::new(&from_ctx, &to_ctx, &enc_secret);
let rekeyed = rekey(&encrypted, &rekey_info);
#[cfg(feature = "elgamal3")]
let decrypted = decrypt(&rekeyed, &to_session.pseudonym.secret).expect("decrypt failed");
#[cfg(not(feature = "elgamal3"))]
let decrypted = decrypt(&rekeyed, &to_session.pseudonym.secret);
assert_eq!(pseudonym, decrypted);
}
#[test]
fn test_rekey_attribute_preserves_plaintext() {
let mut rng = rand::rng();
let (_, global_sk) = make_global_keys(&mut rng);
let from_ctx = EncryptionContext::from("from");
let to_ctx = EncryptionContext::from("to");
let enc_secret = EncryptionSecret::from(b"enc".to_vec());
let from_session = make_session_keys(&global_sk, &from_ctx, &enc_secret);
let to_session = make_session_keys(&global_sk, &to_ctx, &enc_secret);
let attribute = Attribute::random(&mut rng);
let encrypted = encrypt(&attribute, &from_session.attribute.public, &mut rng);
let rekey_info = AttributeRekeyInfo::new(&from_ctx, &to_ctx, &enc_secret);
let rekeyed = rekey(&encrypted, &rekey_info);
#[cfg(feature = "elgamal3")]
let decrypted = decrypt(&rekeyed, &to_session.attribute.secret).expect("decrypt failed");
#[cfg(not(feature = "elgamal3"))]
let decrypted = decrypt(&rekeyed, &to_session.attribute.secret);
assert_eq!(attribute, decrypted);
}
#[test]
fn test_transcrypt_pseudonym_applies_pseudonymization() {
let mut rng = rand::rng();
let (_, global_sk) = make_global_keys(&mut rng);
let from_ctx = EncryptionContext::from("from");
let to_ctx = EncryptionContext::from("to");
let enc_secret = EncryptionSecret::from(b"enc".to_vec());
let pseudo_secret = PseudonymizationSecret::from(b"pseudo".to_vec());
let from_domain = PseudonymizationDomain::from("domain-from");
let to_domain = PseudonymizationDomain::from("domain-to");
let from_session = make_session_keys(&global_sk, &from_ctx, &enc_secret);
let to_session = make_session_keys(&global_sk, &to_ctx, &enc_secret);
let pseudonym = Pseudonym::random(&mut rng);
let encrypted = encrypt(&pseudonym, &from_session.pseudonym.public, &mut rng);
let info = TranscryptionInfo::new(
&from_domain,
&to_domain,
&from_ctx,
&to_ctx,
&pseudo_secret,
&enc_secret,
);
let transcrypted = transcrypt(&encrypted, &info);
#[cfg(feature = "elgamal3")]
let decrypted = decrypt(&transcrypted, &to_session.pseudonym.secret).expect("decrypt failed");
#[cfg(not(feature = "elgamal3"))]
let decrypted = decrypt(&transcrypted, &to_session.pseudonym.secret);
assert_ne!(pseudonym, decrypted);
}
#[test]
fn test_transcrypt_attribute_rekeys_only() {
let mut rng = rand::rng();
let (_, global_sk) = make_global_keys(&mut rng);
let from_ctx = EncryptionContext::from("from");
let to_ctx = EncryptionContext::from("to");
let enc_secret = EncryptionSecret::from(b"enc".to_vec());
let pseudo_secret = PseudonymizationSecret::from(b"pseudo".to_vec());
let from_domain = PseudonymizationDomain::from("domain-from");
let to_domain = PseudonymizationDomain::from("domain-to");
let from_session = make_session_keys(&global_sk, &from_ctx, &enc_secret);
let to_session = make_session_keys(&global_sk, &to_ctx, &enc_secret);
let attribute = Attribute::random(&mut rng);
let encrypted = encrypt(&attribute, &from_session.attribute.public, &mut rng);
let info = TranscryptionInfo::new(
&from_domain,
&to_domain,
&from_ctx,
&to_ctx,
&pseudo_secret,
&enc_secret,
);
let transcrypted = transcrypt(&encrypted, &info);
#[cfg(feature = "elgamal3")]
let decrypted = decrypt(&transcrypted, &to_session.attribute.secret).expect("decrypt failed");
#[cfg(not(feature = "elgamal3"))]
let decrypted = decrypt(&transcrypted, &to_session.attribute.secret);
assert_eq!(attribute, decrypted);
}
#[test]
fn test_polymorphic_rekey_works_for_both_types() {
let mut rng = rand::rng();
let (_, global_sk) = make_global_keys(&mut rng);
let from_ctx = EncryptionContext::from("from");
let to_ctx = EncryptionContext::from("to");
let enc_secret = EncryptionSecret::from(b"enc".to_vec());
let from_session = make_session_keys(&global_sk, &from_ctx, &enc_secret);
let to_session = make_session_keys(&global_sk, &to_ctx, &enc_secret);
let pseudonym = Pseudonym::random(&mut rng);
let enc_p = encrypt(&pseudonym, &from_session.pseudonym.public, &mut rng);
let rekey_p = PseudonymRekeyInfo::new(&from_ctx, &to_ctx, &enc_secret);
let rekeyed_p = rekey(&enc_p, &rekey_p);
#[cfg(feature = "elgamal3")]
let decrypted_p = decrypt(&rekeyed_p, &to_session.pseudonym.secret).expect("decrypt failed");
#[cfg(not(feature = "elgamal3"))]
let decrypted_p = decrypt(&rekeyed_p, &to_session.pseudonym.secret);
assert_eq!(pseudonym, decrypted_p);
let attribute = Attribute::random(&mut rng);
let enc_a = encrypt(&attribute, &from_session.attribute.public, &mut rng);
let rekey_a = AttributeRekeyInfo::new(&from_ctx, &to_ctx, &enc_secret);
let rekeyed_a = rekey(&enc_a, &rekey_a);
#[cfg(feature = "elgamal3")]
let decrypted_a = decrypt(&rekeyed_a, &to_session.attribute.secret).expect("decrypt failed");
#[cfg(not(feature = "elgamal3"))]
let decrypted_a = decrypt(&rekeyed_a, &to_session.attribute.secret);
assert_eq!(attribute, decrypted_a);
}