pub(crate) mod tag {
pub(crate) const BOOLEAN: u8 = 0x01;
pub(crate) const INTEGER: u8 = 0x02;
pub(crate) const BIT_STRING: u8 = 0x03;
pub(crate) const OCTET_STRING: u8 = 0x04;
pub(crate) const OID: u8 = 0x06;
pub(crate) const UTC_TIME: u8 = 0x17;
pub(crate) const GENERALIZED_TIME: u8 = 0x18;
pub(crate) const SEQUENCE: u8 = 0x30;
pub(crate) const SET: u8 = 0x31;
#[must_use]
pub(crate) const fn context(n: u8) -> u8 {
0xA0 | n
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum DerError {
Truncated,
UnexpectedTag {
expected: u8,
found: u8,
},
NonCanonicalLength,
LengthTooLarge,
TrailingData,
Malformed,
}
#[derive(Debug, Clone)]
pub(crate) struct Der<'a> {
rest: &'a [u8],
}
impl<'a> Der<'a> {
pub(crate) const fn new(input: &'a [u8]) -> Self {
Self { rest: input }
}
pub(crate) const fn is_empty(&self) -> bool {
self.rest.is_empty()
}
pub(crate) const fn finish(self) -> Result<(), DerError> {
if self.rest.is_empty() { Ok(()) } else { Err(DerError::TrailingData) }
}
pub(crate) fn peek(&self) -> Option<u8> {
self.rest.first().copied()
}
pub(crate) fn any(&mut self) -> Result<(u8, &'a [u8]), DerError> {
let (&tag, after_tag) = self.rest.split_first().ok_or(DerError::Truncated)?;
let (len, after_len) = read_length(after_tag)?;
let contents = after_len.get(..len).ok_or(DerError::Truncated)?;
self.rest = &after_len[len..];
Ok((tag, contents))
}
pub(crate) fn any_raw(&mut self) -> Result<(u8, &'a [u8], &'a [u8]), DerError> {
let before = self.rest;
let (tag, contents) = self.any()?;
let consumed = before.len() - self.rest.len();
Ok((tag, &before[..consumed], contents))
}
pub(crate) fn expect(&mut self, expected: u8) -> Result<&'a [u8], DerError> {
let (tag, contents) = self.any()?;
if tag == expected {
Ok(contents)
} else {
Err(DerError::UnexpectedTag { expected, found: tag })
}
}
pub(crate) fn nested(&mut self, expected: u8) -> Result<Self, DerError> {
Ok(Self::new(self.expect(expected)?))
}
pub(crate) fn optional(&mut self, expected: u8) -> Result<Option<&'a [u8]>, DerError> {
if self.peek() == Some(expected) { self.expect(expected).map(Some) } else { Ok(None) }
}
pub(crate) fn boolean(&mut self) -> Result<bool, DerError> {
match self.expect(tag::BOOLEAN)? {
[0x00] => Ok(false),
[0xFF] => Ok(true),
_ => Err(DerError::Malformed),
}
}
pub(crate) fn small_uint(&mut self) -> Result<u32, DerError> {
let bytes = self.expect(tag::INTEGER)?;
let (&first, rest) = bytes.split_first().ok_or(DerError::Malformed)?;
if first == 0x00 && rest.first().is_some_and(|&b| b & 0x80 == 0) {
return Err(DerError::Malformed);
}
if first & 0x80 != 0 {
return Err(DerError::Malformed);
}
let bytes = if first == 0x00 { rest } else { bytes };
if bytes.len() > 4 {
return Err(DerError::LengthTooLarge);
}
Ok(bytes.iter().fold(0u32, |acc, &b| (acc << 8) | u32::from(b)))
}
pub(crate) fn bit_string(&mut self) -> Result<(u8, &'a [u8]), DerError> {
let contents = self.expect(tag::BIT_STRING)?;
let (&unused, bytes) = contents.split_first().ok_or(DerError::Malformed)?;
if unused > 7 || (unused > 0 && bytes.is_empty()) {
return Err(DerError::Malformed);
}
Ok((unused, bytes))
}
}
fn read_length(input: &[u8]) -> Result<(usize, &[u8]), DerError> {
let (&first, rest) = input.split_first().ok_or(DerError::Truncated)?;
if first < 0x80 {
return Ok((usize::from(first), rest));
}
if first == 0x80 {
return Err(DerError::NonCanonicalLength);
}
if first == 0xFF {
return Err(DerError::Malformed);
}
let count = usize::from(first & 0x7F);
let (octets, rest) = rest.split_at_checked(count).ok_or(DerError::Truncated)?;
if octets.first() == Some(&0x00) {
return Err(DerError::NonCanonicalLength);
}
if count > core::mem::size_of::<usize>() {
return Err(DerError::LengthTooLarge);
}
let len = octets.iter().fold(0usize, |acc, &b| (acc << 8) | usize::from(b));
if len < 0x80 {
return Err(DerError::NonCanonicalLength);
}
Ok((len, rest))
}
impl core::fmt::Display for DerError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Truncated => f.write_str("the DER encoding ends inside a value"),
Self::UnexpectedTag { expected, found } => {
write!(f, "expected DER tag {expected:#04x}, found {found:#04x}")
}
Self::NonCanonicalLength => f.write_str("the length is not in DER's canonical form"),
Self::LengthTooLarge => f.write_str("the length field is wider than this platform"),
Self::TrailingData => f.write_str("bytes follow a value the grammar says is complete"),
Self::Malformed => f.write_str("a value is structurally impossible"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for DerError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_short_form_length_reads() {
let mut d = Der::new(&[0x02, 0x01, 0x07]);
assert_eq!(d.small_uint().unwrap(), 7);
assert!(d.finish().is_ok());
}
#[test]
fn the_indefinite_length_form_is_refused() {
let mut d = Der::new(&[0x30, 0x80, 0x00, 0x00]);
assert_eq!(d.any(), Err(DerError::NonCanonicalLength));
}
#[test]
fn a_length_written_wider_than_it_needs_is_refused() {
let mut d = Der::new(&[0x04, 0x81, 0x05, 1, 2, 3, 4, 5]);
assert_eq!(d.any(), Err(DerError::NonCanonicalLength));
let mut d = Der::new(&[0x04, 0x82, 0x00, 0x05, 1, 2, 3, 4, 5]);
assert_eq!(d.any(), Err(DerError::NonCanonicalLength));
}
#[test]
fn a_boolean_must_be_der_not_ber() {
assert!(Der::new(&[0x01, 0x01, 0xFF]).boolean().unwrap());
assert!(!Der::new(&[0x01, 0x01, 0x00]).boolean().unwrap());
assert_eq!(Der::new(&[0x01, 0x01, 0x01]).boolean(), Err(DerError::Malformed));
}
#[test]
fn a_non_minimal_integer_is_refused() {
assert_eq!(Der::new(&[0x02, 0x02, 0x00, 0x07]).small_uint(), Err(DerError::Malformed));
assert_eq!(Der::new(&[0x02, 0x02, 0x00, 0x80]).small_uint().unwrap(), 128);
assert_eq!(Der::new(&[0x02, 0x01, 0x00]).small_uint().unwrap(), 0);
}
#[test]
fn a_truncated_value_is_refused() {
assert_eq!(Der::new(&[0x04, 0x05, 1, 2]).any(), Err(DerError::Truncated));
}
#[test]
fn raw_bytes_include_the_tag_and_length() {
let mut d = Der::new(&[0x04, 0x02, 0xAA, 0xBB]);
let (tag, raw, contents) = d.any_raw().unwrap();
assert_eq!(tag, tag::OCTET_STRING);
assert_eq!(raw, &[0x04, 0x02, 0xAA, 0xBB]);
assert_eq!(contents, &[0xAA, 0xBB]);
}
}