use crate::{
core::{CryptoProvider, Cryptor, EncryptedData, PubNubError},
lib::alloc::{boxed::Box, format, string::String, vec, vec::Vec},
providers::crypto::CryptorHeader,
};
#[derive(Debug)]
pub struct CryptoModule {
default: Box<dyn Cryptor>,
cryptors: Option<Vec<Box<dyn Cryptor>>>,
}
impl CryptoModule {
pub fn new(default: Box<dyn Cryptor>, cryptors: Option<Vec<Box<dyn Cryptor>>>) -> Self {
Self { default, cryptors }
}
fn cryptor_with_identifier(&self, header: &CryptorHeader) -> Option<&dyn Cryptor> {
let identifier = header.identifier().unwrap_or([0x00u8; 4]);
if self.default.identifier().eq(&identifier) {
return Some(self.default.as_ref());
}
self.cryptors.as_ref().and_then(|cryptors| {
cryptors
.iter()
.position(|cryptor| cryptor.identifier().eq(&identifier))
.map(|position| cryptors[position].as_ref())
})
}
}
impl CryptoProvider for CryptoModule {
fn encrypt(&self, data: Vec<u8>) -> Result<Vec<u8>, PubNubError> {
let encrypted = self.default.encrypt(data)?;
let header = CryptorHeader::new(self.default.identifier(), &encrypted.metadata);
let mut payload = vec![0; header.len()];
let mut pos = header.len();
let header_data: Vec<u8> = header.into();
payload.splice(0..pos, header_data);
if let Some(metadata) = encrypted.metadata {
pos -= metadata.len();
payload.splice(pos..(pos + metadata.len()), metadata.clone());
}
payload.extend(encrypted.data);
Ok(payload)
}
fn decrypt(&self, data: Vec<u8>) -> Result<Vec<u8>, PubNubError> {
if data.is_empty() {
return Err(PubNubError::Decryption {
details: "Decrypted data is empty".into(),
});
}
let header = CryptorHeader::try_from(&data)?;
let Some(cryptor) = self.cryptor_with_identifier(&header) else {
let identifier = header.identifier().unwrap_or(*b"UNKN");
Err(PubNubError::UnknownCryptor {
details: format!(
"Decrypting data created by unknown cryptor. Please make sure to register {} \
or update SDK",
String::from_utf8(identifier.to_vec()).unwrap_or("non-utf8 identifier".into())
),
})?
};
let metadata = match header.data_size() {
Some(size) => {
let offset = header.len() - size;
Some(data[offset..(offset + size)].to_vec())
}
None => None,
};
cryptor.decrypt(EncryptedData {
metadata,
data: data[header.len()..].to_vec(),
})
}
}
#[cfg(test)]
mod it_should {
use super::*;
const IDENTIFIER: [u8; 4] = *b"ABCD";
const METADATA: [u8; 17] = *b"this-is-meta-data";
const ENCRYPTED_DATA: [u8; 22] = *b"this-is-encrypted-data";
const DECRYPTED_DATA: [u8; 22] = *b"this-is-decrypted-data";
#[derive(Debug)]
struct MyCryptor;
impl Cryptor for MyCryptor {
fn identifier(&self) -> [u8; 4] {
IDENTIFIER
}
fn encrypt(&self, data: Vec<u8>) -> Result<EncryptedData, PubNubError> {
assert_eq!(data, DECRYPTED_DATA.to_vec());
Ok(EncryptedData {
metadata: Some(METADATA.to_vec()),
data: ENCRYPTED_DATA.to_vec(),
})
}
fn decrypt(&self, data: EncryptedData) -> Result<Vec<u8>, PubNubError> {
assert_eq!(data.metadata.unwrap(), METADATA.to_vec());
assert_eq!(data.data, ENCRYPTED_DATA.to_vec());
Ok(DECRYPTED_DATA.to_vec())
}
}
#[test]
fn add_crypto_header_v1_data() {
let cryptor_module = CryptoModule::new(Box::new(MyCryptor), None);
let encrypt_result = cryptor_module.encrypt(DECRYPTED_DATA.to_vec());
let Ok(data) = encrypt_result else {
panic!("Encryption should be successful")
};
assert_eq!(data[0..4], *b"PNED");
assert_eq!(data[4], 1);
assert_eq!(data[5..9], IDENTIFIER);
assert_eq!(data[9] as usize, METADATA.len());
assert_eq!(data[10..(10 + data[9] as usize)], METADATA)
}
#[test]
fn encrypt_data() {
let cryptor_module = CryptoModule::new(Box::new(MyCryptor), None);
let encrypt_result = cryptor_module.encrypt(DECRYPTED_DATA.to_vec());
let Ok(data) = encrypt_result else {
panic!("Encryption should be successful")
};
assert_eq!(data[(10 + data[9] as usize)..], ENCRYPTED_DATA)
}
#[test]
fn decrypt_data() {
let mut encrypted_data: Vec<u8> = vec![];
encrypted_data.extend(b"PNED");
encrypted_data.push(1);
encrypted_data.extend(IDENTIFIER);
encrypted_data.push(METADATA.len() as u8);
encrypted_data.extend(METADATA);
encrypted_data.extend(ENCRYPTED_DATA);
let cryptor_module = CryptoModule::new(Box::new(MyCryptor), None);
let decrypt_result = cryptor_module.decrypt(encrypted_data);
let Ok(data) = decrypt_result else {
panic!("Decryption should be successful")
};
assert_eq!(data, DECRYPTED_DATA)
}
#[test]
fn not_decrypt_data_with_unknown_cryptor() {
let mut encrypted_data: Vec<u8> = vec![];
encrypted_data.extend(b"PNED");
encrypted_data.push(1);
encrypted_data.extend(b"PNDC");
encrypted_data.push(METADATA.len() as u8);
encrypted_data.extend(METADATA);
encrypted_data.extend(ENCRYPTED_DATA);
let cryptor_module = CryptoModule::new(Box::new(MyCryptor), None);
let decrypt_result = cryptor_module.decrypt(encrypted_data);
let Err(err) = decrypt_result else {
panic!("Decryption should not be successful")
};
assert!(matches!(err, PubNubError::UnknownCryptor { .. }))
}
}