#[cfg(feature = "alloc")]
mod oid;
#[cfg(feature = "alloc")]
mod pem;
#[cfg(feature = "alloc")]
mod writer;
#[cfg(feature = "alloc")]
pub use oid::{encode_oid_arcs, oid_tlv, oid_to_string, parse_oid};
#[cfg(feature = "alloc")]
pub use pem::{base64_decode, base64_encode, pem_decode, pem_encode};
#[cfg(feature = "alloc")]
pub use writer::{
encode_bit_string, encode_boolean, encode_context, encode_integer, encode_null,
encode_octet_string, encode_oid, encode_sequence, encode_string, encode_tlv,
};
pub mod tag {
pub const BOOLEAN: u8 = 0x01;
pub const INTEGER: u8 = 0x02;
pub const BIT_STRING: u8 = 0x03;
pub const OCTET_STRING: u8 = 0x04;
pub const NULL: u8 = 0x05;
pub const OID: u8 = 0x06;
pub const UTF8_STRING: u8 = 0x0c;
pub const PRINTABLE_STRING: u8 = 0x13;
pub const IA5_STRING: u8 = 0x16;
pub const UTC_TIME: u8 = 0x17;
pub const GENERALIZED_TIME: u8 = 0x18;
pub const SEQUENCE: u8 = 0x30;
pub const SET: u8 = 0x31;
pub const fn context(n: u8) -> u8 {
0xA0 | n
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
Truncated,
UnexpectedTag {
expected: u8,
found: u8,
},
InvalidLength,
Malformed,
TrailingData,
Pem,
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Error::Truncated => f.write_str("unexpected end of DER input"),
Error::UnexpectedTag { expected, found } => {
write!(
f,
"unexpected DER tag {found:#04x} (expected {expected:#04x})"
)
}
Error::InvalidLength => f.write_str("invalid DER length encoding"),
Error::Malformed => f.write_str("malformed DER value"),
Error::TrailingData => f.write_str("trailing data after DER value"),
Error::Pem => f.write_str("malformed PEM document"),
}
}
}
impl core::error::Error for Error {}
#[derive(Clone, Debug)]
pub struct Reader<'a> {
data: &'a [u8],
}
impl<'a> Reader<'a> {
#[inline]
pub fn new(data: &'a [u8]) -> Self {
Reader { data }
}
#[inline]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
fn take(&mut self, n: usize) -> Result<&'a [u8], Error> {
if self.data.len() < n {
return Err(Error::Truncated);
}
let (head, tail) = self.data.split_at(n);
self.data = tail;
Ok(head)
}
fn read_u8(&mut self) -> Result<u8, Error> {
Ok(self.take(1)?[0])
}
fn read_length(&mut self) -> Result<usize, Error> {
let first = self.read_u8()?;
if first < 0x80 {
return Ok(first as usize);
}
let count = (first & 0x7f) as usize;
if count == 0 || count > core::mem::size_of::<usize>() {
return Err(Error::InvalidLength);
}
let mut len = 0usize;
for _ in 0..count {
len = (len << 8) | self.read_u8()? as usize;
}
if len < 0x80 {
return Err(Error::InvalidLength);
}
Ok(len)
}
pub fn read_tlv(&mut self, expected_tag: u8) -> Result<&'a [u8], Error> {
let tag = self.read_u8()?;
if tag != expected_tag {
return Err(Error::UnexpectedTag {
expected: expected_tag,
found: tag,
});
}
let len = self.read_length()?;
self.take(len)
}
pub fn read_sequence(&mut self) -> Result<Reader<'a>, Error> {
Ok(Reader::new(self.read_tlv(tag::SEQUENCE)?))
}
pub fn read_integer_bytes(&mut self) -> Result<&'a [u8], Error> {
self.read_tlv(tag::INTEGER)
}
pub fn read_unsigned_integer_bytes(&mut self) -> Result<&'a [u8], Error> {
let body = self.read_tlv(tag::INTEGER)?;
if body.is_empty() {
return Err(Error::Malformed);
}
match body {
[0x00] => Ok(body), [0x00, second, ..] => {
if second & 0x80 == 0 {
return Err(Error::Malformed);
}
Ok(body)
}
[first, ..] => {
if first & 0x80 != 0 {
return Err(Error::Malformed);
}
Ok(body)
}
[] => Err(Error::Malformed), }
}
pub fn read_octet_string(&mut self) -> Result<&'a [u8], Error> {
self.read_tlv(tag::OCTET_STRING)
}
pub fn read_oid(&mut self) -> Result<&'a [u8], Error> {
self.read_tlv(tag::OID)
}
pub fn read_null(&mut self) -> Result<(), Error> {
if self.read_tlv(tag::NULL)?.is_empty() {
Ok(())
} else {
Err(Error::Malformed)
}
}
pub fn read_bit_string(&mut self) -> Result<&'a [u8], Error> {
match self.read_tlv(tag::BIT_STRING)?.split_first() {
Some((0, rest)) => Ok(rest),
_ => Err(Error::Malformed),
}
}
pub fn read_boolean(&mut self) -> Result<bool, Error> {
match self.read_tlv(tag::BOOLEAN)? {
[0x00] => Ok(false),
[0xff] => Ok(true),
_ => Err(Error::Malformed),
}
}
#[inline]
pub fn peek_tag(&self) -> Option<u8> {
self.data.first().copied()
}
pub fn read_any(&mut self) -> Result<(u8, &'a [u8]), Error> {
let tag = self.read_u8()?;
let len = self.read_length()?;
Ok((tag, self.take(len)?))
}
pub fn read_element(&mut self) -> Result<&'a [u8], Error> {
let start = self.data;
let _tag = self.read_u8()?;
let len = self.read_length()?;
let header = start.len() - self.data.len();
self.take(len)?;
Ok(&start[..header + len])
}
pub fn finish(self) -> Result<(), Error> {
if self.data.is_empty() {
Ok(())
} else {
Err(Error::TrailingData)
}
}
}
#[cfg(all(test, feature = "alloc"))]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn roundtrip_sequence_of_integers() {
let seq =
encode_sequence(&[encode_integer(&[1]), encode_integer(&[0x01, 0x00, 0x01])].concat());
assert_eq!(
seq,
vec![0x30, 0x08, 0x02, 0x01, 0x01, 0x02, 0x03, 0x01, 0x00, 0x01]
);
let mut r = Reader::new(&seq);
let mut inner = r.read_sequence().unwrap();
assert_eq!(inner.read_integer_bytes().unwrap(), &[0x01]);
assert_eq!(inner.read_integer_bytes().unwrap(), &[0x01, 0x00, 0x01]);
inner.finish().unwrap();
r.finish().unwrap();
}
#[test]
fn integer_high_bit_gets_zero_prefix() {
assert_eq!(encode_integer(&[0x80]), vec![0x02, 0x02, 0x00, 0x80]);
assert_eq!(encode_integer(&[0x00, 0x00, 0x2a]), vec![0x02, 0x01, 0x2a]);
}
#[test]
fn long_form_length() {
let content = vec![0xabu8; 200];
let tlv = encode_octet_string(&content);
assert_eq!(&tlv[..2], &[0x04, 0x81]);
assert_eq!(tlv[2], 200);
let mut r = Reader::new(&tlv);
assert_eq!(r.read_octet_string().unwrap(), &content[..]);
}
#[test]
fn rejects_malformed() {
assert_eq!(
Reader::new(&[0x04, 0x05, 0x01, 0x02]).read_octet_string(),
Err(Error::Truncated)
);
assert_eq!(
Reader::new(&[0x02, 0x01, 0x01]).read_octet_string(),
Err(Error::UnexpectedTag {
expected: tag::OCTET_STRING,
found: tag::INTEGER
})
);
let mut r = Reader::new(&[0x02, 0x01, 0x01, 0xff]);
r.read_integer_bytes().unwrap();
assert_eq!(r.finish(), Err(Error::TrailingData));
}
#[test]
fn bit_string_and_null() {
let bs = encode_bit_string(&[0xde, 0xad]);
assert_eq!(bs, vec![0x03, 0x03, 0x00, 0xde, 0xad]);
assert_eq!(Reader::new(&bs).read_bit_string().unwrap(), &[0xde, 0xad]);
let n = encode_null();
assert_eq!(n, vec![0x05, 0x00]);
Reader::new(&n).read_null().unwrap();
}
}