use crate::{ACCESS_FORMAT_VERSION, AccessError};
pub(crate) const MAGIC: &[u8; 4] = b"BPAC";
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct AccessValidationPolicy {
pub max_identifier_bytes: usize,
pub max_rules: usize,
pub max_wrapped_grant_bytes: usize,
pub max_event_body_bytes: usize,
}
impl Default for AccessValidationPolicy {
fn default() -> Self {
Self {
max_identifier_bytes: 255,
max_rules: 512,
max_wrapped_grant_bytes: 4 * 1024,
max_event_body_bytes: 8 * 1024 * 1024,
}
}
}
pub(crate) fn push_header(out: &mut Vec<u8>, tag: u8) {
out.extend_from_slice(MAGIC);
out.extend_from_slice(&ACCESS_FORMAT_VERSION.to_be_bytes());
out.push(tag);
}
pub(crate) fn push_bytes(out: &mut Vec<u8>, bytes: &[u8]) {
let length = u32::try_from(bytes.len()).expect("access values are bounded below u32::MAX");
out.extend_from_slice(&length.to_be_bytes());
out.extend_from_slice(bytes);
}
pub(crate) fn push_string(out: &mut Vec<u8>, value: &str) {
push_bytes(out, value.as_bytes());
}
pub(crate) struct Cursor<'a> {
bytes: &'a [u8],
offset: usize,
}
impl<'a> Cursor<'a> {
pub(crate) const fn new(bytes: &'a [u8]) -> Self {
Self { bytes, offset: 0 }
}
pub(crate) fn take_header(&mut self, expected_tag: u8) -> Result<(), AccessError> {
if self.take_exact(MAGIC.len())? != MAGIC {
return Err(AccessError::WrongObjectType);
}
let version = self.take_u16()?;
if version != ACCESS_FORMAT_VERSION {
return Err(AccessError::UnsupportedVersion(version));
}
if self.take_u8()? != expected_tag {
return Err(AccessError::WrongObjectType);
}
Ok(())
}
pub(crate) const fn is_empty(&self) -> bool {
self.offset == self.bytes.len()
}
pub(crate) fn take_exact(&mut self, length: usize) -> Result<&'a [u8], AccessError> {
let end = self
.offset
.checked_add(length)
.ok_or(AccessError::Truncated)?;
if end > self.bytes.len() {
return Err(AccessError::Truncated);
}
let value = &self.bytes[self.offset..end];
self.offset = end;
Ok(value)
}
pub(crate) fn take_u8(&mut self) -> Result<u8, AccessError> {
Ok(self.take_exact(1)?[0])
}
pub(crate) fn take_u16(&mut self) -> Result<u16, AccessError> {
Ok(u16::from_be_bytes(
self.take_exact(2)?.try_into().expect("two bytes"),
))
}
pub(crate) fn take_u32(&mut self) -> Result<u32, AccessError> {
Ok(u32::from_be_bytes(
self.take_exact(4)?.try_into().expect("four bytes"),
))
}
pub(crate) fn take_u64(&mut self) -> Result<u64, AccessError> {
Ok(u64::from_be_bytes(
self.take_exact(8)?.try_into().expect("eight bytes"),
))
}
pub(crate) fn take_array32(&mut self) -> Result<[u8; 32], AccessError> {
Ok(self.take_exact(32)?.try_into().expect("32 bytes"))
}
pub(crate) fn take_array64(&mut self) -> Result<[u8; 64], AccessError> {
Ok(self.take_exact(64)?.try_into().expect("64 bytes"))
}
pub(crate) fn take_len(&mut self, limit: usize) -> Result<usize, AccessError> {
let length = self.take_u32()?;
let length = usize::try_from(length).expect("u32 fits into usize on supported targets");
if length > limit {
return Err(AccessError::LengthLimit(length));
}
Ok(length)
}
pub(crate) fn take_bytes(&mut self, limit: usize) -> Result<&'a [u8], AccessError> {
let length = self.take_len(limit)?;
self.take_exact(length)
}
pub(crate) fn take_string(&mut self, limit: usize) -> Result<String, AccessError> {
let bytes = self.take_bytes(limit)?;
core::str::from_utf8(bytes)
.map(str::to_owned)
.map_err(|_| AccessError::InvalidUtf8)
}
}
pub(crate) fn validate_identifier(value: &str, limit: usize) -> Result<(), AccessError> {
if value.is_empty() || value.len() > limit {
Err(AccessError::InvalidIdentifier)
} else {
Ok(())
}
}