use crate::data::json::{JsonError, UnifyError};
use crate::data::traits::{HasStructure, Pseudonymizable, Rekeyable, Transcryptable};
use crate::factors::TranscryptionInfo;
use rand_core::{CryptoRng, Rng};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum BatchError {
#[error("Inconsistent structure in batch. Entry at index {index} has structure {actual_structure}, expected {expected_structure}.")]
InconsistentStructure {
index: usize,
expected_structure: String,
actual_structure: String,
},
#[error(transparent)]
UnifyError(#[from] UnifyError),
#[error(transparent)]
JsonError(#[from] JsonError),
}
fn shuffle<T, R: Rng + CryptoRng>(slice: &mut [T], rng: &mut R) {
for i in (1..slice.len()).rev() {
let j = (rng.next_u64() as usize) % (i + 1);
slice.swap(i, j);
}
}
fn validate_structure<E: HasStructure>(encrypted: &[E]) -> Result<(), BatchError> {
if let Some(first) = encrypted.first() {
let expected_structure = first.structure();
for (index, item) in encrypted.iter().enumerate().skip(1) {
let item_structure = item.structure();
if item_structure != expected_structure {
return Err(BatchError::InconsistentStructure {
index,
expected_structure: format!("{:?}", expected_structure),
actual_structure: format!("{:?}", item_structure),
});
}
}
}
Ok(())
}
pub fn pseudonymize_batch<E, R>(
encrypted: &mut [E],
info: &crate::factors::PseudonymizationInfo,
rng: &mut R,
) -> Result<Box<[E]>, BatchError>
where
E: Pseudonymizable + HasStructure + Clone,
R: Rng + CryptoRng,
{
validate_structure(encrypted)?;
shuffle(encrypted, rng);
Ok(encrypted.iter().map(|x| x.pseudonymize(info)).collect())
}
pub fn rekey_batch<E, R>(
encrypted: &mut [E],
info: &E::RekeyInfo,
rng: &mut R,
) -> Result<Box<[E]>, BatchError>
where
E: Rekeyable + HasStructure + Clone,
E::RekeyInfo: Copy,
R: Rng + CryptoRng,
{
validate_structure(encrypted)?;
shuffle(encrypted, rng);
Ok(encrypted.iter().map(|x| x.rekey(info)).collect())
}
pub fn transcrypt_batch<E, R>(
encrypted: &mut [E],
info: &TranscryptionInfo,
rng: &mut R,
) -> Result<Box<[E]>, BatchError>
where
E: Transcryptable + HasStructure + Clone,
R: Rng + CryptoRng,
{
validate_structure(encrypted)?;
shuffle(encrypted, rng);
Ok(encrypted.iter().map(|x| x.transcrypt(info)).collect())
}