use crate::arithmetic::scalars::ScalarNonZero;
use crate::data::padding::external::{create_external_padding_block, is_external_padding_block};
use crate::data::simple::{
Attribute, ElGamalEncryptable, ElGamalEncrypted, EncryptedAttribute, EncryptedPseudonym,
Pseudonym,
};
use crate::data::traits::{
BatchEncryptable, Encryptable, Encrypted, Pseudonymizable, Rekeyable, Transcryptable,
};
use crate::factors::TranscryptionInfo;
use crate::factors::{
AttributeRekeyInfo, PseudonymRekeyInfo, PseudonymizationInfo, RerandomizeFactor,
};
#[cfg(feature = "offline")]
use crate::keys::{AttributeGlobalPublicKey, PseudonymGlobalPublicKey};
use crate::keys::{
AttributeSessionPublicKey, AttributeSessionSecretKey, PseudonymSessionPublicKey,
PseudonymSessionSecretKey,
};
use derive_more::{Deref, From};
use rand_core::{CryptoRng, Rng};
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::io::{Error, ErrorKind};
#[cfg(all(feature = "offline", feature = "insecure"))]
use crate::keys::{AttributeGlobalSecretKey, PseudonymGlobalSecretKey};
use crate::transcryptor::BatchError;
#[derive(Clone, Eq, PartialEq, Hash, Debug, Deref, From)]
pub struct LongPseudonym(pub Vec<Pseudonym>);
#[derive(Clone, Eq, PartialEq, Hash, Debug, Deref, From)]
pub struct LongAttribute(pub Vec<Attribute>);
#[derive(Clone, Eq, PartialEq, Hash, Debug, Deref, From)]
pub struct LongEncryptedPseudonym(pub Vec<EncryptedPseudonym>);
#[derive(Clone, Eq, PartialEq, Hash, Debug, Deref, From)]
pub struct LongEncryptedAttribute(pub Vec<EncryptedAttribute>);
impl LongPseudonym {
pub fn from_bytes_padded(data: &[u8]) -> Self {
LongPseudonym(from_bytes_padded_impl::<Pseudonym>(data))
}
pub fn from_string_padded(text: &str) -> Self {
Self::from_bytes_padded(text.as_bytes())
}
pub fn to_string_padded(&self) -> Result<String, Error> {
let bytes = self.to_bytes_padded()?;
String::from_utf8(bytes).map_err(|e| Error::new(ErrorKind::InvalidData, e.to_string()))
}
pub fn to_bytes_padded(&self) -> Result<Vec<u8>, Error> {
to_bytes_padded_impl(&self.0)
}
pub fn to_hex(&self) -> String {
self.0
.iter()
.map(|pseudonym| pseudonym.to_hex())
.collect::<Vec<_>>()
.join("")
}
pub fn pad_to(&self, target_blocks: usize) -> Result<Self, Error> {
let current_blocks = self.0.len();
if current_blocks > target_blocks {
return Err(Error::new(
ErrorKind::InvalidInput,
format!(
"Cannot pad: current blocks ({}) exceeds target ({})",
current_blocks, target_blocks
),
));
}
if current_blocks == target_blocks {
return Ok(self.clone());
}
let padding_pattern = create_external_padding_block();
let padding_block = Pseudonym::from_lizard(&padding_pattern);
let mut blocks = self.0.clone();
blocks.resize(target_blocks, padding_block);
Ok(LongPseudonym(blocks))
}
}
impl LongAttribute {
pub fn from_bytes_padded(data: &[u8]) -> Self {
LongAttribute(from_bytes_padded_impl::<Attribute>(data))
}
pub fn from_string_padded(text: &str) -> Self {
Self::from_bytes_padded(text.as_bytes())
}
pub fn to_string_padded(&self) -> Result<String, Error> {
let bytes = self.to_bytes_padded()?;
String::from_utf8(bytes).map_err(|e| Error::new(ErrorKind::InvalidData, e.to_string()))
}
pub fn to_bytes_padded(&self) -> Result<Vec<u8>, Error> {
to_bytes_padded_impl(&self.0)
}
pub fn to_hex(&self) -> String {
self.0
.iter()
.map(|attribute| attribute.to_hex())
.collect::<Vec<_>>()
.join("")
}
pub fn pad_to(&self, target_blocks: usize) -> Result<Self, Error> {
let current_blocks = self.0.len();
if current_blocks > target_blocks {
return Err(Error::new(
ErrorKind::InvalidInput,
format!(
"Cannot pad: current blocks ({}) exceeds target ({})",
current_blocks, target_blocks
),
));
}
if current_blocks == target_blocks {
return Ok(self.clone());
}
let padding_pattern = create_external_padding_block();
let padding_block = Attribute::from_lizard(&padding_pattern);
let mut blocks = self.0.clone();
blocks.resize(target_blocks, padding_block);
Ok(LongAttribute(blocks))
}
}
impl LongEncryptedPseudonym {
pub fn serialize(&self) -> String {
self.0
.iter()
.map(|item| item.to_base64())
.collect::<Vec<_>>()
.join("|")
}
pub fn deserialize(s: &str) -> Result<Self, Error> {
if s.is_empty() {
return Ok(LongEncryptedPseudonym(vec![]));
}
let items: Result<Vec<EncryptedPseudonym>, Error> = s
.split('|')
.map(|part| {
EncryptedPseudonym::from_base64(part).ok_or_else(|| {
Error::new(
ErrorKind::InvalidData,
format!("Invalid base64 encoding: {}", part),
)
})
})
.collect();
items.map(LongEncryptedPseudonym)
}
}
impl LongEncryptedAttribute {
pub fn serialize(&self) -> String {
self.0
.iter()
.map(|item| item.to_base64())
.collect::<Vec<_>>()
.join("|")
}
pub fn deserialize(s: &str) -> Result<Self, Error> {
if s.is_empty() {
return Ok(LongEncryptedAttribute(vec![]));
}
let items: Result<Vec<EncryptedAttribute>, Error> = s
.split('|')
.map(|part| {
EncryptedAttribute::from_base64(part).ok_or_else(|| {
Error::new(
ErrorKind::InvalidData,
format!("Invalid base64 encoding: {}", part),
)
})
})
.collect();
items.map(LongEncryptedAttribute)
}
}
#[cfg(feature = "serde")]
impl Serialize for LongEncryptedPseudonym {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.serialize())
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for LongEncryptedPseudonym {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Self::deserialize(&s).map_err(serde::de::Error::custom)
}
}
#[cfg(feature = "serde")]
impl Serialize for LongEncryptedAttribute {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.serialize())
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for LongEncryptedAttribute {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Self::deserialize(&s).map_err(serde::de::Error::custom)
}
}
pub trait LongEncryptable {
type EncryptedType: LongEncrypted;
type Block: ElGamalEncryptable;
fn blocks(&self) -> &[Self::Block];
fn from_encrypted_blocks(
blocks: Vec<<Self::Block as Encryptable>::EncryptedType>,
) -> Self::EncryptedType;
}
impl LongEncryptable for LongPseudonym {
type EncryptedType = LongEncryptedPseudonym;
type Block = Pseudonym;
fn blocks(&self) -> &[Self::Block] {
&self.0
}
fn from_encrypted_blocks(
blocks: Vec<<Self::Block as Encryptable>::EncryptedType>,
) -> Self::EncryptedType {
LongEncryptedPseudonym(blocks)
}
}
impl Encryptable for LongPseudonym {
type EncryptedType = LongEncryptedPseudonym;
type PublicKeyType = PseudonymSessionPublicKey;
#[cfg(feature = "offline")]
type GlobalPublicKeyType = PseudonymGlobalPublicKey;
fn encrypt<R>(&self, public_key: &Self::PublicKeyType, rng: &mut R) -> Self::EncryptedType
where
R: Rng + CryptoRng,
{
let encrypted_blocks = self
.blocks()
.iter()
.map(|block| block.encrypt(public_key, rng))
.collect();
LongEncryptedPseudonym(encrypted_blocks)
}
#[cfg(feature = "offline")]
fn encrypt_global<R>(
&self,
public_key: &Self::GlobalPublicKeyType,
rng: &mut R,
) -> Self::EncryptedType
where
R: Rng + CryptoRng,
{
let encrypted_blocks = self
.blocks()
.iter()
.map(|block| block.encrypt_global(public_key, rng))
.collect();
LongEncryptedPseudonym(encrypted_blocks)
}
}
impl LongEncryptable for LongAttribute {
type EncryptedType = LongEncryptedAttribute;
type Block = Attribute;
fn blocks(&self) -> &[Self::Block] {
&self.0
}
fn from_encrypted_blocks(
blocks: Vec<<Self::Block as Encryptable>::EncryptedType>,
) -> Self::EncryptedType {
LongEncryptedAttribute(blocks)
}
}
impl Encryptable for LongAttribute {
type EncryptedType = LongEncryptedAttribute;
type PublicKeyType = AttributeSessionPublicKey;
#[cfg(feature = "offline")]
type GlobalPublicKeyType = AttributeGlobalPublicKey;
fn encrypt<R>(&self, public_key: &Self::PublicKeyType, rng: &mut R) -> Self::EncryptedType
where
R: Rng + CryptoRng,
{
let encrypted_blocks = self
.blocks()
.iter()
.map(|block| block.encrypt(public_key, rng))
.collect();
LongEncryptedAttribute(encrypted_blocks)
}
#[cfg(feature = "offline")]
fn encrypt_global<R>(
&self,
public_key: &Self::GlobalPublicKeyType,
rng: &mut R,
) -> Self::EncryptedType
where
R: Rng + CryptoRng,
{
let encrypted_blocks = self
.blocks()
.iter()
.map(|block| block.encrypt_global(public_key, rng))
.collect();
LongEncryptedAttribute(encrypted_blocks)
}
}
pub trait LongEncrypted {
type UnencryptedType: LongEncryptable;
type EncryptedBlock: ElGamalEncrypted;
fn encrypted_blocks(&self) -> &[Self::EncryptedBlock];
fn from_decrypted_blocks(
blocks: Vec<<Self::EncryptedBlock as Encrypted>::UnencryptedType>,
) -> Self::UnencryptedType;
fn from_encrypted_blocks(blocks: Vec<Self::EncryptedBlock>) -> Self;
}
impl LongEncrypted for LongEncryptedPseudonym {
type UnencryptedType = LongPseudonym;
type EncryptedBlock = EncryptedPseudonym;
fn encrypted_blocks(&self) -> &[Self::EncryptedBlock] {
&self.0
}
fn from_decrypted_blocks(
blocks: Vec<<Self::EncryptedBlock as Encrypted>::UnencryptedType>,
) -> Self::UnencryptedType {
LongPseudonym(blocks)
}
fn from_encrypted_blocks(blocks: Vec<Self::EncryptedBlock>) -> Self {
LongEncryptedPseudonym(blocks)
}
}
impl LongEncrypted for LongEncryptedAttribute {
type UnencryptedType = LongAttribute;
type EncryptedBlock = EncryptedAttribute;
fn encrypted_blocks(&self) -> &[Self::EncryptedBlock] {
&self.0
}
fn from_decrypted_blocks(
blocks: Vec<<Self::EncryptedBlock as Encrypted>::UnencryptedType>,
) -> Self::UnencryptedType {
LongAttribute(blocks)
}
fn from_encrypted_blocks(blocks: Vec<Self::EncryptedBlock>) -> Self {
LongEncryptedAttribute(blocks)
}
}
impl Encrypted for LongEncryptedPseudonym {
type UnencryptedType = LongPseudonym;
type SecretKeyType = PseudonymSessionSecretKey;
#[cfg(all(feature = "offline", feature = "insecure"))]
type GlobalSecretKeyType = PseudonymGlobalSecretKey;
#[cfg(feature = "elgamal3")]
fn decrypt(&self, secret_key: &Self::SecretKeyType) -> Option<Self::UnencryptedType> {
let decrypted_blocks: Option<Vec<_>> = self
.encrypted_blocks()
.iter()
.map(|block| block.decrypt(secret_key))
.collect();
decrypted_blocks.map(LongPseudonym)
}
#[cfg(not(feature = "elgamal3"))]
fn decrypt(&self, secret_key: &Self::SecretKeyType) -> Self::UnencryptedType {
let decrypted_blocks: Vec<_> = self
.encrypted_blocks()
.iter()
.map(|block| block.decrypt(secret_key))
.collect();
LongPseudonym(decrypted_blocks)
}
#[cfg(all(feature = "offline", feature = "insecure", feature = "elgamal3"))]
fn decrypt_global(
&self,
secret_key: &Self::GlobalSecretKeyType,
) -> Option<Self::UnencryptedType> {
let decrypted_blocks: Option<Vec<_>> = self
.encrypted_blocks()
.iter()
.map(|block| block.decrypt_global(secret_key))
.collect();
decrypted_blocks.map(LongPseudonym)
}
#[cfg(all(feature = "offline", feature = "insecure", not(feature = "elgamal3")))]
fn decrypt_global(&self, secret_key: &Self::GlobalSecretKeyType) -> Self::UnencryptedType {
let decrypted_blocks: Vec<_> = self
.encrypted_blocks()
.iter()
.map(|block| block.decrypt_global(secret_key))
.collect();
LongPseudonym(decrypted_blocks)
}
#[cfg(feature = "elgamal3")]
fn rerandomize<R>(&self, rng: &mut R) -> Self
where
R: Rng + CryptoRng,
{
let r = ScalarNonZero::random(rng);
self.rerandomize_known(&RerandomizeFactor(r))
}
#[cfg(not(feature = "elgamal3"))]
fn rerandomize<R>(
&self,
public_key: &<Self::UnencryptedType as Encryptable>::PublicKeyType,
rng: &mut R,
) -> Self
where
R: Rng + CryptoRng,
{
let r = ScalarNonZero::random(rng);
self.rerandomize_known(public_key, &RerandomizeFactor(r))
}
#[cfg(feature = "elgamal3")]
fn rerandomize_known(&self, factor: &RerandomizeFactor) -> Self {
let rerandomized_blocks: Vec<_> = self
.encrypted_blocks()
.iter()
.map(|block| block.rerandomize_known(factor))
.collect();
LongEncryptedPseudonym(rerandomized_blocks)
}
#[cfg(not(feature = "elgamal3"))]
fn rerandomize_known(
&self,
public_key: &<Self::UnencryptedType as Encryptable>::PublicKeyType,
factor: &RerandomizeFactor,
) -> Self {
let rerandomized_blocks: Vec<_> = self
.encrypted_blocks()
.iter()
.map(|block| block.rerandomize_known(public_key, factor))
.collect();
LongEncryptedPseudonym(rerandomized_blocks)
}
}
impl Encrypted for LongEncryptedAttribute {
type UnencryptedType = LongAttribute;
type SecretKeyType = AttributeSessionSecretKey;
#[cfg(all(feature = "offline", feature = "insecure"))]
type GlobalSecretKeyType = AttributeGlobalSecretKey;
#[cfg(feature = "elgamal3")]
fn decrypt(&self, secret_key: &Self::SecretKeyType) -> Option<Self::UnencryptedType> {
let decrypted_blocks: Option<Vec<_>> = self
.encrypted_blocks()
.iter()
.map(|block| block.decrypt(secret_key))
.collect();
decrypted_blocks.map(LongAttribute)
}
#[cfg(not(feature = "elgamal3"))]
fn decrypt(&self, secret_key: &Self::SecretKeyType) -> Self::UnencryptedType {
let decrypted_blocks: Vec<_> = self
.encrypted_blocks()
.iter()
.map(|block| block.decrypt(secret_key))
.collect();
LongAttribute(decrypted_blocks)
}
#[cfg(all(feature = "offline", feature = "insecure", feature = "elgamal3"))]
fn decrypt_global(
&self,
secret_key: &Self::GlobalSecretKeyType,
) -> Option<Self::UnencryptedType> {
let decrypted_blocks: Option<Vec<_>> = self
.encrypted_blocks()
.iter()
.map(|block| block.decrypt_global(secret_key))
.collect();
decrypted_blocks.map(LongAttribute)
}
#[cfg(all(feature = "offline", feature = "insecure", not(feature = "elgamal3")))]
fn decrypt_global(&self, secret_key: &Self::GlobalSecretKeyType) -> Self::UnencryptedType {
let decrypted_blocks: Vec<_> = self
.encrypted_blocks()
.iter()
.map(|block| block.decrypt_global(secret_key))
.collect();
LongAttribute(decrypted_blocks)
}
#[cfg(feature = "elgamal3")]
fn rerandomize<R>(&self, rng: &mut R) -> Self
where
R: Rng + CryptoRng,
{
let r = ScalarNonZero::random(rng);
self.rerandomize_known(&RerandomizeFactor(r))
}
#[cfg(not(feature = "elgamal3"))]
fn rerandomize<R>(
&self,
public_key: &<Self::UnencryptedType as Encryptable>::PublicKeyType,
rng: &mut R,
) -> Self
where
R: Rng + CryptoRng,
{
let r = ScalarNonZero::random(rng);
self.rerandomize_known(public_key, &RerandomizeFactor(r))
}
#[cfg(feature = "elgamal3")]
fn rerandomize_known(&self, factor: &RerandomizeFactor) -> Self {
let rerandomized_blocks: Vec<_> = self
.encrypted_blocks()
.iter()
.map(|block| block.rerandomize_known(factor))
.collect();
LongEncryptedAttribute(rerandomized_blocks)
}
#[cfg(not(feature = "elgamal3"))]
fn rerandomize_known(
&self,
public_key: &<Self::UnencryptedType as Encryptable>::PublicKeyType,
factor: &RerandomizeFactor,
) -> Self {
let rerandomized_blocks: Vec<_> = self
.encrypted_blocks()
.iter()
.map(|block| block.rerandomize_known(public_key, factor))
.collect();
LongEncryptedAttribute(rerandomized_blocks)
}
}
impl Pseudonymizable for LongEncryptedPseudonym {
fn pseudonymize(&self, info: &PseudonymizationInfo) -> Self {
let pseudonymized_blocks: Vec<_> = self
.encrypted_blocks()
.iter()
.map(|block| block.pseudonymize(info))
.collect();
LongEncryptedPseudonym(pseudonymized_blocks)
}
}
impl Rekeyable for LongEncryptedPseudonym {
type RekeyInfo = PseudonymRekeyInfo;
fn rekey(&self, info: &Self::RekeyInfo) -> Self {
let rekeyed_blocks: Vec<_> = self
.encrypted_blocks()
.iter()
.map(|block| block.rekey(info))
.collect();
LongEncryptedPseudonym(rekeyed_blocks)
}
}
impl Rekeyable for LongEncryptedAttribute {
type RekeyInfo = AttributeRekeyInfo;
fn rekey(&self, info: &Self::RekeyInfo) -> Self {
let rekeyed_blocks: Vec<_> = self
.encrypted_blocks()
.iter()
.map(|block| block.rekey(info))
.collect();
LongEncryptedAttribute(rekeyed_blocks)
}
}
impl Transcryptable for LongEncryptedPseudonym {
fn transcrypt(&self, info: &TranscryptionInfo) -> Self {
self.pseudonymize(&info.pseudonym)
}
}
impl Transcryptable for LongEncryptedAttribute {
fn transcrypt(&self, info: &TranscryptionInfo) -> Self {
self.rekey(&info.attribute)
}
}
#[cfg(feature = "batch")]
impl crate::data::traits::HasStructure for LongEncryptedPseudonym {
type Structure = usize;
fn structure(&self) -> Self::Structure {
self.0.len()
}
}
#[cfg(feature = "batch")]
impl crate::data::traits::HasStructure for LongEncryptedAttribute {
type Structure = usize;
fn structure(&self) -> Self::Structure {
self.0.len()
}
}
#[cfg(feature = "batch")]
impl BatchEncryptable for LongPseudonym {
fn preprocess_batch(items: &[Self]) -> Result<Vec<Self>, BatchError> {
Ok(items.to_vec())
}
}
#[cfg(feature = "batch")]
impl BatchEncryptable for LongAttribute {
fn preprocess_batch(items: &[Self]) -> Result<Vec<Self>, BatchError> {
Ok(items.to_vec())
}
}
fn from_bytes_padded_impl<T: ElGamalEncryptable>(data: &[u8]) -> Vec<T> {
let full_blocks = data.len() / 16;
let remaining = data.len() % 16;
let total_blocks = if data.is_empty() { 1 } else { full_blocks + 1 };
let mut result = Vec::with_capacity(total_blocks);
for i in 0..full_blocks {
let start = i * 16;
#[allow(clippy::unwrap_used)]
result.push(T::from_lizard(&data[start..start + 16].try_into().unwrap()));
}
let padding_byte = (16 - remaining) as u8;
let mut last_block = [padding_byte; 16];
if remaining > 0 {
last_block[..remaining].copy_from_slice(&data[data.len() - remaining..]);
}
result.push(T::from_lizard(&last_block));
result
}
fn to_bytes_padded_impl<T: ElGamalEncryptable>(items: &[T]) -> Result<Vec<u8>, Error> {
if items.is_empty() {
return Err(Error::new(
ErrorKind::InvalidInput,
"No encryptables provided",
));
}
let mut last_data_block_idx = items.len() - 1;
while last_data_block_idx > 0 {
let block = items[last_data_block_idx].to_lizard().ok_or(Error::new(
ErrorKind::InvalidData,
"Encryptable conversion to bytes failed",
))?;
if is_external_padding_block(&block) {
last_data_block_idx -= 1;
} else {
break;
}
}
let mut result = Vec::with_capacity((last_data_block_idx + 1) * 16);
for item in items.iter().take(last_data_block_idx) {
let block = item.to_lizard().ok_or(Error::new(
ErrorKind::InvalidData,
"Encryptable conversion to bytes failed",
))?;
result.extend_from_slice(&block);
}
let last_block = items[last_data_block_idx].to_lizard().ok_or(Error::new(
ErrorKind::InvalidData,
"Last encryptable conversion to bytes failed",
))?;
let padding_byte = last_block[15];
if padding_byte == 0 || padding_byte > 16 {
return Err(Error::new(ErrorKind::InvalidData, "Invalid padding"));
}
if last_block[16 - padding_byte as usize..]
.iter()
.any(|&b| b != padding_byte)
{
return Err(Error::new(ErrorKind::InvalidData, "Inconsistent padding"));
}
let data_bytes = 16 - padding_byte as usize;
result.extend_from_slice(&last_block[..data_bytes]);
Ok(result)
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
use crate::client::encrypt;
use crate::factors::contexts::EncryptionContext;
use crate::factors::EncryptionSecret;
use crate::keys::{make_attribute_session_keys, make_pseudonym_session_keys};
use std::io::ErrorKind;
#[test]
fn long_attribute_from_bytes_padded_empty() {
let data: &[u8] = &[];
let result = LongAttribute::from_bytes_padded(data);
assert_eq!(result.len(), 1);
let block_bytes = result[0].to_lizard().unwrap();
assert_eq!([16u8; 16], block_bytes);
}
#[test]
fn long_attribute_from_bytes_padded_single_block() {
let data = b"Hello, world!";
let result = LongAttribute::from_bytes_padded(data);
assert_eq!(1, result.len());
let bytes = result[0].to_lizard().unwrap();
assert_eq!(b"Hello, world!\x03\x03\x03", &bytes);
}
#[test]
fn long_attribute_from_bytes_padded_exact_block() {
let data = b"0123456789ABCDEF";
let result = LongAttribute::from_bytes_padded(data);
assert_eq!(2, result.len());
assert_eq!(b"0123456789ABCDEF", &result[0].to_lizard().unwrap());
let expected_padding = [16u8; 16];
assert_eq!(expected_padding, result[1].to_lizard().unwrap());
}
#[test]
fn long_attribute_from_bytes_padded_multiple_blocks() {
let data = b"This is a longer string that spans multiple blocks";
let result = LongAttribute::from_bytes_padded(data);
let expected_blocks = (data.len() / 16) + 1;
assert_eq!(expected_blocks, result.len());
for (i, block) in result.iter().enumerate().take(data.len() / 16) {
let start = i * 16;
let expected = data[start..start + 16].to_vec();
assert_eq!(expected, block.to_lizard().unwrap()[..16]);
}
let last_block = result.last().unwrap().to_lizard().unwrap();
let remaining = data.len() % 16;
let padding_byte = (16 - remaining) as u8;
assert_eq!(&data[data.len() - remaining..], &last_block[..remaining]);
for byte in last_block.iter().skip(remaining) {
assert_eq!(&padding_byte, byte);
}
}
#[test]
fn long_attribute_to_bytes_padded() {
let original = b"This is some test data for padding";
let attributes = LongAttribute::from_bytes_padded(original);
let decoded = attributes.to_bytes_padded().unwrap();
assert_eq!(original, decoded.as_slice());
}
#[test]
fn long_attribute_to_bytes_padded_empty() {
let attributes = LongAttribute::from(vec![]);
let result = attributes.to_bytes_padded();
assert!(result.is_err());
assert_eq!(ErrorKind::InvalidInput, result.unwrap_err().kind());
}
#[test]
fn long_attribute_to_bytes_padded_invalid_padding() {
let invalid_block = [0u8; 16];
let attribute = Attribute::from_lizard(&invalid_block);
let long_attr = LongAttribute::from(vec![attribute]);
let result = long_attr.to_bytes_padded();
assert!(result.is_err());
assert_eq!(ErrorKind::InvalidData, result.unwrap_err().kind());
let mut inconsistent_block = [5u8; 16];
inconsistent_block[15] = 6;
let attribute = Attribute::from_lizard(&inconsistent_block);
let long_attr = LongAttribute::from(vec![attribute]);
let result = long_attr.to_bytes_padded();
assert!(result.is_err());
}
#[test]
fn long_attribute_to_string_padded() {
let original = "This is a UTF-8 string with special chars: ñáéÃóú ä½ å¥½";
let attributes = LongAttribute::from_string_padded(original);
let decoded = attributes.to_string_padded().unwrap();
assert_eq!(original, decoded);
}
#[test]
fn long_attribute_to_string_padded_invalid_utf8() {
let invalid_utf8 = vec![0xFF, 0xFE, 0xFD];
let mut block = [0u8; 16];
block[..3].copy_from_slice(&invalid_utf8);
block[3..].fill(13);
let attribute = Attribute::from_lizard(&block);
let long_attr = LongAttribute::from(vec![attribute]);
let result = long_attr.to_string_padded();
assert!(result.is_err());
}
#[test]
fn long_attribute_roundtrip_all_padding_sizes() {
for padding_size in 1..=16 {
let size = 32 - padding_size;
let data = vec![b'X'; size];
let attributes = LongAttribute::from_bytes_padded(&data);
let decoded = attributes.to_bytes_padded().unwrap();
assert_eq!(data, decoded);
}
}
#[test]
fn long_pseudonym_from_bytes_padded() {
let data = b"Hello, world!";
let result = LongPseudonym::from_bytes_padded(data);
assert_eq!(1, result.len());
let bytes = result[0].to_lizard().unwrap();
assert_eq!(b"Hello, world!\x03\x03\x03", &bytes);
}
#[test]
fn long_pseudonym_to_bytes_padded() {
let original = b"This is some test data for padding";
let pseudonyms = LongPseudonym::from_bytes_padded(original);
let decoded = pseudonyms.to_bytes_padded().unwrap();
assert_eq!(original, decoded.as_slice());
}
#[test]
fn long_pseudonym_string_roundtrip() {
let original = "Testing pseudonym string conversion";
let pseudonyms = LongPseudonym::from_string_padded(original);
let decoded = pseudonyms.to_string_padded().unwrap();
assert_eq!(original, decoded);
}
#[test]
fn long_encrypted_pseudonym_serialize_deserialize() {
let mut rng = rand::rng();
let (session_public, _session_secret) = make_pseudonym_session_keys(
&crate::keys::make_pseudonym_global_keys(&mut rng).1,
&EncryptionContext::from("session-1"),
&EncryptionSecret::from("enc-secret".as_bytes().to_vec()),
);
let pseudonyms = LongPseudonym::from_string_padded("test-data-for-serialization");
let encrypted: Vec<EncryptedPseudonym> = pseudonyms
.iter()
.map(|p| encrypt(p, &session_public, &mut rng))
.collect();
let long_encrypted = LongEncryptedPseudonym::from(encrypted);
let serialized = long_encrypted.serialize();
assert!(serialized.contains('|'));
let deserialized = LongEncryptedPseudonym::deserialize(&serialized).unwrap();
assert_eq!(long_encrypted.len(), deserialized.len());
for (original, restored) in long_encrypted.iter().zip(deserialized.iter()) {
assert_eq!(original, restored);
}
}
#[test]
fn long_encrypted_attribute_serialize_deserialize() {
let mut rng = rand::rng();
let (session_public, _session_secret) = make_attribute_session_keys(
&crate::keys::make_attribute_global_keys(&mut rng).1,
&EncryptionContext::from("session-1"),
&EncryptionSecret::from("enc-secret".as_bytes().to_vec()),
);
let attributes = LongAttribute::from_string_padded("attribute-test-data");
let encrypted: Vec<_> = attributes
.iter()
.map(|a| encrypt(a, &session_public, &mut rng))
.collect();
let long_encrypted = LongEncryptedAttribute::from(encrypted);
let serialized = long_encrypted.serialize();
assert!(serialized.contains('|'));
let deserialized = LongEncryptedAttribute::deserialize(&serialized).unwrap();
assert_eq!(long_encrypted.len(), deserialized.len());
for (original, restored) in long_encrypted.iter().zip(deserialized.iter()) {
assert_eq!(original, restored);
}
}
#[test]
fn long_encrypted_empty_roundtrip() {
let empty_pseudo = LongEncryptedPseudonym(vec![]);
let serialized = empty_pseudo.serialize();
assert_eq!(serialized, "");
let deserialized = LongEncryptedPseudonym::deserialize(&serialized).unwrap();
assert_eq!(deserialized.len(), 0);
let empty_attr = LongEncryptedAttribute(vec![]);
let serialized = empty_attr.serialize();
assert_eq!(serialized, "");
let deserialized = LongEncryptedAttribute::deserialize(&serialized).unwrap();
assert_eq!(deserialized.len(), 0);
}
#[test]
fn long_encrypted_deserialize_invalid_base64() {
let result = LongEncryptedPseudonym::deserialize("invalid!!!|also-invalid!!!");
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidData);
let result = LongEncryptedAttribute::deserialize("invalid!!!|also-invalid!!!");
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidData);
}
#[test]
fn long_encrypted_serde_json() {
let mut rng = rand::rng();
let (session_public, _session_secret) = make_pseudonym_session_keys(
&crate::keys::make_pseudonym_global_keys(&mut rng).1,
&EncryptionContext::from("session-1"),
&EncryptionSecret::from("enc-secret".as_bytes().to_vec()),
);
let pseudonyms = LongPseudonym::from_string_padded("serde-test-data");
let encrypted: Vec<EncryptedPseudonym> = pseudonyms
.iter()
.map(|p| encrypt(p, &session_public, &mut rng))
.collect();
let long_encrypted = LongEncryptedPseudonym::from(encrypted);
let json = serde_json::to_string(&long_encrypted).expect("Failed to serialize to JSON");
let deserialized: LongEncryptedPseudonym =
serde_json::from_str(&json).expect("Failed to deserialize from JSON");
assert_eq!(long_encrypted.len(), deserialized.len());
for (original, restored) in long_encrypted.iter().zip(deserialized.iter()) {
assert_eq!(original, restored);
}
}
#[test]
fn long_encrypted_pseudonym_single_item() {
use crate::data::padding::Padded;
let mut rng = rand::rng();
let (session_public, _session_secret) = make_pseudonym_session_keys(
&crate::keys::make_pseudonym_global_keys(&mut rng).1,
&EncryptionContext::from("session-1"),
&EncryptionSecret::from("enc-secret".as_bytes().to_vec()),
);
let pseudonym = Pseudonym::from_bytes_padded(b"single").unwrap();
let encrypted = encrypt(&pseudonym, &session_public, &mut rng);
let long_encrypted = LongEncryptedPseudonym::from(vec![encrypted]);
let serialized = long_encrypted.serialize();
assert!(!serialized.contains('|'));
let deserialized = LongEncryptedPseudonym::deserialize(&serialized).unwrap();
assert_eq!(1, deserialized.len());
assert_eq!(long_encrypted[0], deserialized[0]);
}
#[test]
fn long_attribute_null_bytes_in_middle() {
let str_with_nulls = "hello\0world";
let attr = LongAttribute::from_string_padded(str_with_nulls);
let decoded = attr.to_string_padded().unwrap();
assert_eq!(str_with_nulls, decoded);
}
#[test]
fn long_attribute_null_bytes_at_end() {
let str_ending_nulls = "test\0\0";
let attr = LongAttribute::from_string_padded(str_ending_nulls);
let decoded = attr.to_string_padded().unwrap();
assert_eq!(str_ending_nulls, decoded);
}
#[test]
fn long_attribute_empty_string() {
let empty = "";
let attr = LongAttribute::from_string_padded(empty);
let decoded = attr.to_string_padded().unwrap();
assert_eq!(empty, decoded);
}
#[test]
fn long_attribute_strings_ending_with_many_null_bytes() {
for null_count in 1..=20 {
let mut test_str = String::from("test");
test_str.push_str(&"\0".repeat(null_count));
let attr = LongAttribute::from_string_padded(&test_str);
let decoded = attr.to_string_padded().unwrap();
assert_eq!(test_str, decoded, "Failed for {} null bytes", null_count);
}
}
#[test]
fn long_attribute_only_null_bytes() {
for null_count in 1..=20 {
let test_str = "\0".repeat(null_count);
let attr = LongAttribute::from_string_padded(&test_str);
let decoded = attr.to_string_padded().unwrap();
assert_eq!(
test_str, decoded,
"Failed for string of {} null bytes",
null_count
);
}
}
#[test]
fn long_attribute_edge_case_15_and_16_null_bytes() {
let str_15 = "\0".repeat(15);
let attr_15 = LongAttribute::from_string_padded(&str_15);
let decoded_15 = attr_15.to_string_padded().unwrap();
assert_eq!(str_15, decoded_15);
let str_16 = "\0".repeat(16);
let attr_16 = LongAttribute::from_string_padded(&str_16);
let decoded_16 = attr_16.to_string_padded().unwrap();
assert_eq!(str_16, decoded_16);
let str_17 = "\0".repeat(17);
let attr_17 = LongAttribute::from_string_padded(&str_17);
let decoded_17 = attr_17.to_string_padded().unwrap();
assert_eq!(str_17, decoded_17);
}
#[test]
fn long_attribute_pad_to_with_null_bytes() {
let str_with_nulls = "data\0\0end";
let attr = LongAttribute::from_string_padded(str_with_nulls);
let padded = attr.pad_to(3).unwrap();
let decoded = padded.to_string_padded().unwrap();
assert_eq!(str_with_nulls, decoded);
}
#[test]
fn long_attribute_pad_to_only_null_bytes() {
for null_count in 1..=10 {
let test_str = "\0".repeat(null_count);
let attr = LongAttribute::from_string_padded(&test_str);
let padded = attr.pad_to(5).unwrap();
let decoded = padded.to_string_padded().unwrap();
assert_eq!(
test_str, decoded,
"Failed for padded string of {} null bytes",
null_count
);
}
}
#[test]
fn long_attribute_pad_to_empty_string() {
let empty = "";
let attr = LongAttribute::from_string_padded(empty);
let padded = attr.pad_to(2).unwrap();
let decoded = padded.to_string_padded().unwrap();
assert_eq!(empty, decoded);
}
#[test]
fn long_pseudonym_null_bytes_roundtrip() {
let str_with_nulls = "user\0\0id";
let pseudo = LongPseudonym::from_string_padded(str_with_nulls);
let decoded = pseudo.to_string_padded().unwrap();
assert_eq!(str_with_nulls, decoded);
}
#[test]
fn long_pseudonym_pad_to_with_null_bytes() {
let str_with_nulls = "id\0\0x";
let pseudo = LongPseudonym::from_string_padded(str_with_nulls);
let padded = pseudo.pad_to(3).unwrap();
let decoded = padded.to_string_padded().unwrap();
assert_eq!(str_with_nulls, decoded);
}
#[test]
fn long_attribute_data_ending_with_full_0x10_block() {
let data = vec![0x10u8; 16];
let attr = LongAttribute::from_bytes_padded(&data);
let decoded = attr.to_bytes_padded().unwrap();
assert_eq!(
data, decoded,
"Data ending with full 0x10 block should roundtrip correctly"
);
}
#[test]
fn long_attribute_ascending_sequence_data() {
let data: Vec<u8> = (0..16).collect();
let attr = LongAttribute::from_bytes_padded(&data);
let decoded = attr.to_bytes_padded().unwrap();
assert_eq!(
data, decoded,
"Ascending sequence data should roundtrip correctly"
);
}
#[test]
fn long_attribute_pad_to_preserves_data() {
let attr = LongAttribute::from_string_padded("hello");
let original_len = attr.len();
let padded = attr.pad_to(original_len + 2).unwrap();
assert_eq!(
padded.len(),
original_len + 2,
"Padded length should match target"
);
let decoded = padded.to_string_padded().unwrap();
assert_eq!(decoded, "hello", "pad_to should preserve original data");
}
#[test]
fn long_pseudonym_pad_to_preserves_data() {
let pseudo = LongPseudonym::from_string_padded("test-user-id");
let original_len = pseudo.len();
let padded = pseudo.pad_to(original_len + 3).unwrap();
assert_eq!(
padded.len(),
original_len + 3,
"Padded length should match target"
);
let decoded = padded.to_string_padded().unwrap();
assert_eq!(
decoded, "test-user-id",
"pad_to should preserve original data"
);
}
#[test]
fn long_attribute_data_containing_magic_marker_multiblock() {
let data = vec![
0xFF, 0xEE, 0xDD, 0xCC, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00,
0xAA, 0xBB, 0xCC,
];
let attr = LongAttribute::from_bytes_padded(&data);
let decoded = attr.to_bytes_padded().unwrap();
assert_eq!(
data, decoded,
"Multi-block data with any bytes should roundtrip correctly"
);
}
#[test]
fn long_attribute_single_block_with_magic_marker() {
let data = vec![0xFF, 0xEE, 0xDD, 0xCC, 0x99, 0x88, 0x77, 0x66];
let attr = LongAttribute::from_bytes_padded(&data);
let decoded = attr.to_bytes_padded().unwrap();
assert_eq!(
data, decoded,
"Single-block data with any bytes should roundtrip correctly"
);
}
#[test]
fn long_attribute_data_exactly_matching_external_padding_pattern() {
let data = vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
];
let attr = LongAttribute::from_bytes_padded(&data);
assert_eq!(attr.len(), 1);
let decoded = attr.to_bytes_padded().unwrap();
assert_eq!(data, decoded, "All-zero data should roundtrip correctly");
}
#[test]
fn long_attribute_double_pad_to_works() {
let attr = LongAttribute::from_string_padded("test");
let padded_once = attr.pad_to(2).unwrap();
let padded_twice = padded_once.pad_to(3).unwrap();
let result = padded_twice.to_string_padded();
assert!(
result.is_ok(),
"Double pad_to should succeed with all-zero padding"
);
assert_eq!(result.unwrap(), "test");
}
#[test]
fn verify_no_ambiguous_edge_cases() {
let data1 = vec![0xFF, 0xEE, 0xDD, 0xCC, 0x99, 0x88, 0x77, 0x66];
let attr1 = LongAttribute::from_bytes_padded(&data1);
let decoded1 = attr1.to_bytes_padded().unwrap();
assert_eq!(data1, decoded1, "Arbitrary data should work");
let data2 = vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
];
let attr2 = LongAttribute::from_bytes_padded(&data2);
let decoded2 = attr2.to_bytes_padded().unwrap();
assert_eq!(data2, decoded2, "All-zero data should work");
let data3 = vec![
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x11, 0x12,
0x13, 0x14, 0x15, 0x16,
];
let attr3 = LongAttribute::from_bytes_padded(&data3);
let decoded3 = attr3.to_bytes_padded().unwrap();
assert_eq!(data3, decoded3, "Mixed data should work");
let attr4 = LongAttribute::from_string_padded("hello");
let padded = attr4.pad_to(3).unwrap();
assert_eq!(
padded.len(),
3,
"pad_to should create correct number of blocks"
);
let decoded4 = padded.to_string_padded().unwrap();
assert_eq!("hello", decoded4, "pad_to should preserve original data");
for len in 1..=32 {
let mut data = vec![0x00; len]; data[0] = 0xFF;
let attr = LongAttribute::from_bytes_padded(&data);
let decoded = attr.to_bytes_padded().unwrap();
assert_eq!(data, decoded, "Data of length {} should work", len);
}
}
}