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
}
}
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;