use crate::{
core::PubNubError,
lib::alloc::{vec, vec::Vec},
};
const IDENTIFIER_LENGTH: usize = 4;
const NULL_IDENTIFIER: &[u8; 4] = &[0x00u8; 4];
const SENTINEL: &[u8; 4] = b"PNED";
const MAX_VERSION: u8 = 1;
#[derive(Debug)]
pub(crate) enum CryptorHeader {
V1 {
identifier: [u8; 4],
data_size: usize,
},
None,
}
impl CryptorHeader {
pub fn new(identifier: [u8; 4], cryptor_metadata: &Option<Vec<u8>>) -> Self {
Self::V1 {
data_size: cryptor_metadata
.as_ref()
.map(|metadata| metadata.len())
.unwrap_or(0),
identifier,
}
}
pub fn len(&self) -> usize {
match self.data_size() {
Some(size) => {
SENTINEL.len() + 1 + IDENTIFIER_LENGTH + if size < 255 { 1 } else { 3 } + size
}
None => 0,
}
}
pub fn version(&self) -> u8 {
match self {
CryptorHeader::V1 { .. } => 1,
_ => 0,
}
}
pub fn data_size(&self) -> Option<usize> {
match self {
CryptorHeader::V1 { data_size, .. } => Some(*data_size),
_ => None,
}
}
pub fn identifier(&self) -> Option<[u8; 4]> {
match self {
CryptorHeader::V1 { identifier, .. } => {
identifier.ne(NULL_IDENTIFIER).then_some(*identifier)
}
_ => None,
}
}
}
impl From<CryptorHeader> for Vec<u8> {
fn from(value: CryptorHeader) -> Self {
let Some(identifier) = value.identifier() else {
return vec![];
};
let mut data = vec![0; value.len()];
data.splice(0..SENTINEL.len(), SENTINEL.to_vec());
let mut pos = SENTINEL.len();
data[pos] = value.version();
pos += 1;
identifier.ne(NULL_IDENTIFIER).then(|| {
data.splice(pos..(pos + identifier.len()), identifier);
pos += identifier.len();
});
let header_size = value.data_size().unwrap_or(0);
if header_size < 255 {
data[pos] = header_size as u8;
} else {
data.splice(
pos..(pos + 3),
vec![255, (header_size >> 8) as u8, (header_size & 0xFF) as u8],
);
}
data
}
}
impl TryFrom<&Vec<u8>> for CryptorHeader {
type Error = PubNubError;
fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
let Some(sentinel) = value.len().ge(&4).then(|| &value[0..4]) else {
return Ok(Self::None);
};
if sentinel.ne(SENTINEL) {
return Ok(Self::None);
}
let Some(version) = value.len().ge(&5).then(|| &value[4]) else {
return Err(PubNubError::Decryption {
details: "Decrypted data header is malformed.".into(),
});
};
if *version == 0 || *version > MAX_VERSION {
return Err(PubNubError::UnknownCryptor {
details: "Decrypting data created by unknown cryptor.".into(),
});
}
let mut pos = 5 + IDENTIFIER_LENGTH;
let Some(identifier) = value.len().ge(&pos).then(|| {
let mut identifier: [u8; 4] = [0; 4];
identifier.copy_from_slice(&value[5..pos]);
identifier
}) else {
return Err(PubNubError::Decryption {
details: "Decrypted data header is malformed.".into(),
});
};
let Some(mut header_size) = value.len().ge(&(pos + 1)).then(|| value[pos] as usize) else {
return Ok(Self::None);
};
pos += 1;
if header_size == 255 {
let Some(size_bytes) = value.len().ge(&(pos + 2)).then(|| {
let mut size_bytes: [u8; 2] = [0; 2];
size_bytes.clone_from_slice(&value[pos..(pos + 2)]);
size_bytes
}) else {
return Ok(Self::None);
};
header_size = u16::from_be_bytes(size_bytes) as usize;
}
Ok(match version {
&1 => Self::V1 {
data_size: header_size,
identifier,
},
_ => Self::None,
})
}
}