use crate::error::ErrorCode;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Length {
Definite(u64),
Indefinite,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Encoding {
Der,
Ber,
}
impl Encoding {
#[must_use]
pub fn is_der(self) -> bool {
self == Self::Der
}
}
#[derive(Debug, Clone, Copy)]
pub struct DerLength {
bytes: [u8; 9],
length: u8,
}
impl DerLength {
#[must_use]
pub fn new(length: u64) -> Self {
let mut bytes = [0; 9];
if length < 128 {
bytes[0] = length.to_be_bytes()[7];
return Self { bytes, length: 1 };
}
let count = (64 - length.leading_zeros()).div_ceil(8).to_le_bytes()[0];
bytes[0] = 0x80 | count;
let count_usize = usize::from(count);
bytes[1..=count_usize].copy_from_slice(&length.to_be_bytes()[8 - count_usize..]);
Self {
bytes,
length: 1 + count,
}
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..usize::from(self.length)]
}
}
impl AsRef<[u8]> for DerLength {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
pub(crate) fn read_accounted(
input: &[u8],
encoding: Encoding,
offset: u64,
mut charge: impl FnMut(u64) -> Result<(), ErrorCode>,
) -> Result<(Length, usize), (ErrorCode, u64)> {
let fail = |code| (code, offset);
charge(1).map_err(fail)?;
let first = *input
.first()
.ok_or_else(|| fail(ErrorCode::UnexpectedEnd))?;
if first & 0x80 == 0 {
return Ok((Length::Definite(u64::from(first)), 1));
}
let count = usize::from(first & 0x7f);
if count == 0 {
return match encoding {
Encoding::Ber => Ok((Length::Indefinite, 1)),
Encoding::Der => Err(fail(ErrorCode::IndefiniteLengthNotAllowed)),
};
}
if count == 0x7f {
return Err(fail(ErrorCode::LengthTooLarge));
}
charge(count as u64).map_err(fail)?;
let bytes = input
.get(1..=count)
.ok_or_else(|| fail(ErrorCode::UnexpectedEnd))?;
if encoding.is_der() {
if bytes[0] == 0 {
return Err(fail(ErrorCode::LengthNotMinimal));
}
if count == 1 && bytes[0] < 0x80 {
return Err(fail(ErrorCode::LengthNotMinimal));
}
}
let significant = bytes.iter().skip_while(|byte| **byte == 0).count();
if significant > 8 {
return Err(fail(ErrorCode::LengthTooLarge));
}
let mut value: u64 = 0;
for byte in bytes {
value = value
.checked_mul(256)
.and_then(|shifted| shifted.checked_add(u64::from(*byte)))
.ok_or_else(|| fail(ErrorCode::LengthTooLarge))?;
}
Ok((Length::Definite(value), 1 + count))
}
#[cfg(test)]
fn read(
input: &[u8],
encoding: Encoding,
offset: u64,
) -> Result<(Length, usize), (ErrorCode, u64)> {
read_accounted(input, encoding, offset, |_| Ok(()))
}
#[cfg(test)]
mod tests;