use alloc::vec::Vec;
pub const TAG_INTEGER: u8 = 0x02;
pub const TAG_BIT_STRING: u8 = 0x03;
pub const TAG_OCTET_STRING: u8 = 0x04;
pub const TAG_NULL: u8 = 0x05;
pub const TAG_OID: u8 = 0x06;
pub const TAG_SEQUENCE: u8 = 0x30;
pub const TAG_SET: u8 = 0x31;
pub const MAX_DER_LEN: usize = 16_777_216;
#[must_use]
pub fn read_tag(input: &[u8], expected: u8) -> Option<&[u8]> {
let (tag, rest) = input.split_first()?;
if *tag == expected { Some(rest) } else { None }
}
#[must_use]
pub fn read_length(input: &[u8]) -> Option<(usize, &[u8])> {
let (first, rest) = input.split_first()?;
if *first < 0x80 {
Some((*first as usize, rest))
} else if *first == 0x81 {
let (b, rest) = rest.split_first()?;
if *b < 0x80 {
return None; }
Some((*b as usize, rest))
} else if *first == 0x82 {
let (hi, rest) = rest.split_first()?;
let (lo, rest) = rest.split_first()?;
let len = (usize::from(*hi) << 8) | usize::from(*lo);
if len < 256 {
return None; }
Some((len, rest))
} else if *first == 0x83 {
let (b2, rest) = rest.split_first()?;
let (b1, rest) = rest.split_first()?;
let (b0, rest) = rest.split_first()?;
let len = (usize::from(*b2) << 16) | (usize::from(*b1) << 8) | usize::from(*b0);
if len < 65_536 {
return None; }
Some((len, rest))
} else {
None
}
}
#[must_use]
pub fn read_tlv(input: &[u8], expected: u8) -> Option<(&[u8], &[u8])> {
let rest = read_tag(input, expected)?;
let (len, rest) = read_length(rest)?;
if rest.len() < len {
return None;
}
Some(rest.split_at(len))
}
#[must_use]
pub fn read_integer(input: &[u8]) -> Option<(&[u8], &[u8])> {
let (bytes, rest) = read_tlv(input, TAG_INTEGER)?;
if bytes.is_empty() {
return None;
}
if bytes[0] & 0x80 != 0 {
return None; }
let unsigned = if bytes[0] == 0x00 {
if bytes.len() == 1 {
bytes
} else if bytes[1] & 0x80 == 0 {
return None;
} else {
&bytes[1..]
}
} else {
bytes
};
Some((unsigned, rest))
}
#[must_use]
pub fn read_octet_string(input: &[u8]) -> Option<(&[u8], &[u8])> {
read_tlv(input, TAG_OCTET_STRING)
}
#[must_use]
pub fn read_null(input: &[u8]) -> Option<&[u8]> {
let (value, rest) = read_tlv(input, TAG_NULL)?;
if !value.is_empty() {
return None;
}
Some(rest)
}
#[must_use]
pub fn read_oid(input: &[u8]) -> Option<(&[u8], &[u8])> {
let (value, rest) = read_tlv(input, TAG_OID)?;
if value.is_empty() {
return None;
}
if value[value.len() - 1] & 0x80 != 0 {
return None;
}
Some((value, rest))
}
#[must_use]
pub fn read_bit_string(input: &[u8]) -> Option<(u8, &[u8], &[u8])> {
let (value, rest) = read_tlv(input, TAG_BIT_STRING)?;
let (unused, bit_bytes) = value.split_first()?;
if *unused > 7 {
return None;
}
Some((*unused, bit_bytes, rest))
}
#[must_use]
pub fn read_sequence(input: &[u8]) -> Option<(&[u8], &[u8])> {
read_tlv(input, TAG_SEQUENCE)
}
#[must_use]
pub fn read_context_tagged_explicit(input: &[u8], n: u8) -> Option<(&[u8], &[u8])> {
if n > 30 {
return None;
}
let tag = 0xA0 | n;
read_tlv(input, tag)
}
#[must_use]
pub fn read_context_tagged_implicit(input: &[u8], n: u8) -> Option<(&[u8], &[u8])> {
if n > 30 {
return None;
}
let tag = 0x80 | n;
read_tlv(input, tag)
}
#[must_use]
pub fn collect_sequence_of<'a, T, F>(body: &'a [u8], mut read_item: F) -> Option<Vec<T>>
where
F: FnMut(&'a [u8]) -> Option<(T, &'a [u8])>,
{
let mut items = Vec::new();
let mut cursor = body;
while !cursor.is_empty() {
let (item, next) = read_item(cursor)?;
items.push(item);
cursor = next;
}
Some(items)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn length_one_byte_forms() {
assert_eq!(read_length(&[0x00]), Some((0, &[][..])));
assert_eq!(read_length(&[0x7F]), Some((127, &[][..])));
assert_eq!(
read_length(&[0x05, 0xAA, 0xBB]),
Some((5, &[0xAA, 0xBB][..]))
);
}
#[test]
fn length_two_byte_form() {
assert_eq!(read_length(&[0x81, 0x80]), Some((128, &[][..])));
assert_eq!(read_length(&[0x81, 0xFF]), Some((255, &[][..])));
}
#[test]
fn length_two_byte_non_minimal_rejected() {
assert_eq!(read_length(&[0x81, 0x05]), None);
assert_eq!(read_length(&[0x81, 0x7F]), None);
}
#[test]
fn length_three_byte_form() {
assert_eq!(read_length(&[0x82, 0x01, 0x00]), Some((256, &[][..])));
assert_eq!(read_length(&[0x82, 0xFF, 0xFF]), Some((65_535, &[][..])));
}
#[test]
fn length_three_byte_non_minimal_rejected() {
assert_eq!(read_length(&[0x82, 0x00, 0xFF]), None);
assert_eq!(read_length(&[0x82, 0x00, 0xFF]), None);
}
#[test]
fn length_four_byte_form() {
assert_eq!(
read_length(&[0x83, 0x01, 0x00, 0x00]),
Some((65_536, &[][..]))
);
assert_eq!(
read_length(&[0x83, 0xFF, 0xFF, 0xFF]),
Some((16_777_215, &[][..]))
);
}
#[test]
fn length_four_byte_non_minimal_rejected() {
assert_eq!(read_length(&[0x83, 0x00, 0xFF, 0xFF]), None);
}
#[test]
fn length_above_max_rejected() {
assert_eq!(read_length(&[0x84, 0x01, 0x00, 0x00, 0x00]), None);
}
#[test]
fn length_truncated_rejected() {
assert_eq!(read_length(&[]), None);
assert_eq!(read_length(&[0x81]), None);
assert_eq!(read_length(&[0x82, 0x01]), None);
assert_eq!(read_length(&[0x83, 0x01, 0x00]), None);
}
#[test]
fn integer_canonical_zero() {
let (bytes, rest) = read_integer(&[0x02, 0x01, 0x00]).expect("zero");
assert_eq!(bytes, &[0x00]);
assert!(rest.is_empty());
}
#[test]
fn integer_small_positive() {
let (bytes, _) = read_integer(&[0x02, 0x01, 0x01]).unwrap();
assert_eq!(bytes, &[0x01]);
let (bytes, _) = read_integer(&[0x02, 0x01, 0x7F]).unwrap();
assert_eq!(bytes, &[0x7F]);
}
#[test]
fn integer_strips_disambiguating_pad() {
let (bytes, _) = read_integer(&[0x02, 0x02, 0x00, 0x80]).unwrap();
assert_eq!(bytes, &[0x80]);
}
#[test]
fn integer_rejects_redundant_pad() {
assert!(read_integer(&[0x02, 0x02, 0x00, 0x01]).is_none());
}
#[test]
fn integer_rejects_negative() {
assert!(read_integer(&[0x02, 0x01, 0x80]).is_none());
assert!(read_integer(&[0x02, 0x01, 0xFF]).is_none());
}
#[test]
fn integer_rejects_empty_content() {
assert!(read_integer(&[0x02, 0x00]).is_none());
}
#[test]
fn integer_rejects_wrong_tag() {
assert!(read_integer(&[0x03, 0x01, 0x01]).is_none());
}
#[test]
fn integer_preserves_remainder() {
let (bytes, rest) = read_integer(&[0x02, 0x01, 0x05, 0xDE, 0xAD]).unwrap();
assert_eq!(bytes, &[0x05]);
assert_eq!(rest, &[0xDE, 0xAD]);
}
#[test]
fn octet_string_round_trip() {
let (value, rest) = read_octet_string(&[0x04, 0x03, 0x01, 0x02, 0x03]).unwrap();
assert_eq!(value, &[0x01, 0x02, 0x03]);
assert!(rest.is_empty());
}
#[test]
fn octet_string_empty() {
let (value, rest) = read_octet_string(&[0x04, 0x00]).unwrap();
assert!(value.is_empty());
assert!(rest.is_empty());
}
#[test]
fn null_canonical() {
assert_eq!(read_null(&[0x05, 0x00]), Some(&[][..]));
assert_eq!(read_null(&[0x05, 0x00, 0xFF]), Some(&[0xFF][..]));
}
#[test]
fn null_with_content_rejected() {
assert!(read_null(&[0x05, 0x01, 0x00]).is_none());
}
#[test]
fn oid_id_pbkdf2() {
let der = [
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x0C,
];
let (value, rest) = read_oid(&der).unwrap();
assert_eq!(value, &der[2..]);
assert!(rest.is_empty());
}
#[test]
fn oid_empty_rejected() {
assert!(read_oid(&[0x06, 0x00]).is_none());
}
#[test]
fn oid_unterminated_continuation_rejected() {
assert!(read_oid(&[0x06, 0x01, 0x80]).is_none());
assert!(read_oid(&[0x06, 0x02, 0x2A, 0x80]).is_none());
}
#[test]
fn bit_string_zero_unused() {
let (unused, bytes, rest) = read_bit_string(&[0x03, 0x03, 0x00, 0xAB, 0xCD]).unwrap();
assert_eq!(unused, 0);
assert_eq!(bytes, &[0xAB, 0xCD]);
assert!(rest.is_empty());
}
#[test]
fn bit_string_unused_above_7_rejected() {
assert!(read_bit_string(&[0x03, 0x02, 0x08, 0xFF]).is_none());
}
#[test]
fn bit_string_empty_value_rejected() {
assert!(read_bit_string(&[0x03, 0x00]).is_none());
}
#[test]
fn sequence_round_trip() {
let der = [0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02];
let (body, rest) = read_sequence(&der).unwrap();
assert_eq!(body, &der[2..]);
assert!(rest.is_empty());
let (a, body) = read_integer(body).unwrap();
let (b, body) = read_integer(body).unwrap();
assert_eq!(a, &[0x01]);
assert_eq!(b, &[0x02]);
assert!(body.is_empty());
}
#[test]
fn context_explicit_round_trip() {
let der = [0xA0, 0x03, 0x02, 0x01, 0x01];
let (inner, rest) = read_context_tagged_explicit(&der, 0).unwrap();
assert!(rest.is_empty());
let (val, _) = read_integer(inner).unwrap();
assert_eq!(val, &[0x01]);
}
#[test]
fn context_implicit_round_trip() {
let der = [0x81, 0x02, 0x61, 0x62];
let (value, rest) = read_context_tagged_implicit(&der, 1).unwrap();
assert_eq!(value, b"ab");
assert!(rest.is_empty());
}
#[test]
fn context_explicit_wrong_number_rejected() {
let der = [0xA0, 0x03, 0x02, 0x01, 0x01];
assert!(read_context_tagged_explicit(&der, 1).is_none());
}
#[test]
fn context_explicit_above_30_rejected() {
assert!(read_context_tagged_explicit(&[0xBF, 0x1F, 0x00], 31).is_none());
}
#[test]
fn collect_three_integers() {
let body = [0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03];
let items = collect_sequence_of(&body, |b| {
let (v, rest) = read_integer(b)?;
Some((v[0], rest))
})
.unwrap();
assert_eq!(items, alloc::vec![1u8, 2, 3]);
}
#[test]
fn collect_stops_on_short_input() {
let body = [0x02, 0x01, 0x01, 0x02, 0x01]; let result: Option<Vec<u8>> = collect_sequence_of(&body, |b| {
let (v, rest) = read_integer(b)?;
Some((v[0], rest))
});
assert!(result.is_none());
}
}