#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Asn1Error {
#[error("unexpected end of data")]
UnexpectedEnd,
#[error("expected tag 0x{expected:02x}, found 0x{found:02x}")]
UnexpectedTag {
expected: u8,
found: u8,
},
#[error("data truncated")]
Truncated,
#[error("trailing data after structure")]
TrailingData,
#[error("non-minimal length encoding (long-form or indefinite length)")]
NonMinimalLength,
#[error("non-minimal integer encoding (superfluous leading 0x00/0xff)")]
NonMinimalInteger,
}
#[cfg(test)]
#[derive(Debug, Clone, Default)]
pub struct ASN1Writer(Vec<u8>);
#[cfg(any(target_os = "macos", target_os = "ios", test))]
#[derive(Debug, Clone, Copy)]
pub struct ASN1Reader<'a>(&'a [u8]);
#[cfg(test)]
impl ASN1Writer {
pub fn new() -> Self {
Self::default()
}
pub fn sequence(&mut self, len: u8) {
self.0.push(0x30u8);
self.0.push(len);
}
pub fn integer(&mut self, integer: &[u8]) {
assert!(
integer.len() < (u8::MAX as usize),
"integer too large: {} bytes",
integer.len()
);
self.0.push(0x02u8);
self.0.push(integer.len() as u8);
self.0.extend_from_slice(integer);
}
pub fn finalize(self) -> Vec<u8> {
self.0
}
}
#[cfg(any(target_os = "macos", target_os = "ios", test))]
impl<'a> ASN1Reader<'a> {
#[inline]
pub const fn new(bytes: &'a [u8]) -> Self {
Self(bytes)
}
fn not_empty(self) -> Result<Self, Asn1Error> {
if self.0.is_empty() {
return Err(Asn1Error::UnexpectedEnd);
}
Ok(self)
}
#[inline(always)]
pub fn is_empty(self) -> bool {
self.0.is_empty()
}
pub fn sequence(self) -> Result<Self, Asn1Error> {
let this = self.not_empty()?;
if this.0[0] != 0x30 {
return Err(Asn1Error::UnexpectedTag {
expected: 0x30,
found: this.0[0],
});
}
if this.0.len() < 2 {
return Err(Asn1Error::Truncated);
}
if this.0[1] >= 0x80 {
return Err(Asn1Error::NonMinimalLength);
}
let length = this.0[1] as usize;
let body = &this.0[2..];
if body.len() < length {
return Err(Asn1Error::Truncated);
}
if body.len() > length {
return Err(Asn1Error::TrailingData);
}
Ok(Self(body))
}
pub fn integer(self) -> Result<(Self, &'a [u8]), Asn1Error> {
let this = self.not_empty()?;
if this.0[0] != 0x02 {
return Err(Asn1Error::UnexpectedTag {
expected: 0x02,
found: this.0[0],
});
}
if this.0.len() < 2 {
return Err(Asn1Error::Truncated);
}
if this.0[1] >= 0x80 {
return Err(Asn1Error::NonMinimalLength);
}
let length = this.0[1] as usize;
if length == 0 {
return Err(Asn1Error::Truncated);
}
if this.0.len() < 2 + length {
return Err(Asn1Error::Truncated);
}
let integer = &this.0[2..(2 + length)];
if length >= 2 {
let superfluous_zero = integer[0] == 0x00 && (integer[1] & 0x80) == 0;
let superfluous_ones = integer[0] == 0xff && (integer[1] & 0x80) != 0;
if superfluous_zero || superfluous_ones {
return Err(Asn1Error::NonMinimalInteger);
}
}
let remaining = &this.0[(2 + length)..];
Ok((Self(remaining), integer))
}
}
#[cfg(test)]
mod tests {
use super::*;
const ENCODED: &[u8] = &[
48, 69, 2, 32, 17, 228, 99, 34, 120, 37, 69, 116, 229, 142, 174, 166, 66, 182, 115, 165,
236, 153, 178, 59, 233, 223, 255, 125, 25, 93, 206, 45, 220, 10, 53, 97, 2, 33, 0, 241, 1,
246, 203, 223, 101, 49, 70, 12, 167, 176, 90, 118, 217, 115, 61, 23, 200, 214, 81, 204, 74,
219, 44, 58, 232, 125, 187, 1, 202, 203, 185,
];
const R: &[u8] = &[
17, 228, 99, 34, 120, 37, 69, 116, 229, 142, 174, 166, 66, 182, 115, 165, 236, 153, 178,
59, 233, 223, 255, 125, 25, 93, 206, 45, 220, 10, 53, 97,
];
const S: &[u8] = &[
0, 241, 1, 246, 203, 223, 101, 49, 70, 12, 167, 176, 90, 118, 217, 115, 61, 23, 200, 214,
81, 204, 74, 219, 44, 58, 232, 125, 187, 1, 202, 203, 185,
];
#[test]
fn encode_signature() {
let mut writer = ASN1Writer::new();
writer.sequence(69);
writer.integer(R);
writer.integer(S);
assert_eq!(ENCODED, writer.0.as_slice());
}
#[test]
fn decode_signature() {
let reader = ASN1Reader(ENCODED);
let reader = reader.sequence().unwrap();
let (reader, r) = reader.integer().unwrap();
let (reader, s) = reader.integer().unwrap();
assert!(reader.is_empty());
assert_eq!(r, R);
assert_eq!(s, S);
}
#[test]
fn sequence_rejects_long_form_length() {
let der = &[0x30, 0x81, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02];
assert!(matches!(
ASN1Reader::new(der).sequence(),
Err(Asn1Error::NonMinimalLength)
));
}
#[test]
fn sequence_rejects_trailing_data() {
let der = &[0x30, 0x03, 0x02, 0x01, 0x01, 0xff];
assert!(matches!(
ASN1Reader::new(der).sequence(),
Err(Asn1Error::TrailingData)
));
}
#[test]
fn sequence_rejects_truncated_body() {
let der = &[0x30, 0x05, 0x02, 0x01, 0x01];
assert!(matches!(
ASN1Reader::new(der).sequence(),
Err(Asn1Error::Truncated)
));
}
#[test]
fn integer_rejects_long_form_length() {
assert!(matches!(
ASN1Reader::new(&[0x02, 0x81, 0x01, 0x01]).integer(),
Err(Asn1Error::NonMinimalLength)
));
}
#[test]
fn integer_rejects_empty_content() {
assert!(matches!(
ASN1Reader::new(&[0x02, 0x00]).integer(),
Err(Asn1Error::Truncated)
));
}
#[test]
fn integer_rejects_superfluous_leading_zero() {
assert!(matches!(
ASN1Reader::new(&[0x02, 0x02, 0x00, 0x01]).integer(),
Err(Asn1Error::NonMinimalInteger)
));
}
#[test]
fn integer_rejects_superfluous_leading_ones() {
assert!(matches!(
ASN1Reader::new(&[0x02, 0x02, 0xff, 0x80]).integer(),
Err(Asn1Error::NonMinimalInteger)
));
}
#[test]
fn integer_accepts_required_sign_byte() {
let (reader, content) = ASN1Reader::new(&[0x02, 0x02, 0x00, 0x80])
.integer()
.unwrap();
assert!(reader.is_empty());
assert_eq!(content, &[0x00, 0x80]);
}
}