Something went wrong. Try again.
we (web engine): Experimental web browser project to understand the limits of Claude
Something went wrong. Try again.
Rust
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286//! ASN.1 DER (Distinguished Encoding Rules) parser.//!//! Parses TLV (Tag-Length-Value) structures from DER-encoded byte slices.//! Supports all types needed for X.509 certificate and PKCS#1 key parsing.
use core::fmt;
// ---------------------------------------------------------------------------// Error type// ---------------------------------------------------------------------------
#[derive(Debug, Clone, PartialEq, Eq)]pub enum Asn1Error { /// Not enough data to read the tag/length/value. Truncated, /// Indefinite-length encoding is forbidden in DER. IndefiniteLength, /// Length encoding is not minimal (DER violation). NonMinimalLength, /// Integer encoding is not minimal (leading zero or negative without sign byte). NonMinimalInteger, /// Unexpected tag encountered. UnexpectedTag { expected: u8, got: u8 }, /// Trailing data after a complete parse. TrailingData, /// Invalid OID encoding. InvalidOid, /// Invalid boolean value in DER (must be 0x00 or 0xFF). InvalidBoolean, /// Invalid UTF-8 in string value. InvalidUtf8, /// Invalid time string format. InvalidTime, /// Bit string unused bits > 7 or non-zero unused bits with empty content. InvalidBitString, /// Content length exceeds available data. LengthOverflow,}
impl fmt::Display for Asn1Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Truncated => write!(f, "truncated data"), Self::IndefiniteLength => write!(f, "indefinite length not allowed in DER"), Self::NonMinimalLength => write!(f, "non-minimal length encoding"), Self::NonMinimalInteger => write!(f, "non-minimal integer encoding"), Self::UnexpectedTag { expected, got } => { write!( f, "unexpected tag: expected 0x{expected:02x}, got 0x{got:02x}" ) } Self::TrailingData => write!(f, "trailing data after value"), Self::InvalidOid => write!(f, "invalid OID encoding"), Self::InvalidBoolean => write!(f, "invalid DER boolean"), Self::InvalidUtf8 => write!(f, "invalid UTF-8"), Self::InvalidTime => write!(f, "invalid time string"), Self::InvalidBitString => write!(f, "invalid bit string"), Self::LengthOverflow => write!(f, "length overflow"), } }}
pub type Result<T> = core::result::Result<T, Asn1Error>;
// ---------------------------------------------------------------------------// Tag constants// ---------------------------------------------------------------------------
// Tag class bits (bits 7-6)pub const TAG_CLASS_UNIVERSAL: u8 = 0x00;pub const TAG_CLASS_APPLICATION: u8 = 0x40;pub const TAG_CLASS_CONTEXT: u8 = 0x80;pub const TAG_CLASS_PRIVATE: u8 = 0xC0;
// Constructed bit (bit 5)pub const TAG_CONSTRUCTED: u8 = 0x20;
// Universal type tagspub const TAG_BOOLEAN: u8 = 0x01;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_UTF8_STRING: u8 = 0x0C;pub const TAG_SEQUENCE: u8 = 0x30; // constructedpub const TAG_SET: u8 = 0x31; // constructedpub const TAG_PRINTABLE_STRING: u8 = 0x13;pub const TAG_IA5_STRING: u8 = 0x16;pub const TAG_UTC_TIME: u8 = 0x17;pub const TAG_GENERALIZED_TIME: u8 = 0x18;
// ---------------------------------------------------------------------------// OID — Object Identifier// ---------------------------------------------------------------------------
/// An ASN.1 Object Identifier, stored as raw DER-encoded bytes.#[derive(Clone, PartialEq, Eq)]pub struct Oid<'a> { /// The raw DER-encoded OID value bytes (without tag and length). bytes: &'a [u8],}
impl<'a> Oid<'a> { /// Create an OID from raw DER-encoded bytes. pub fn from_der_bytes(bytes: &'a [u8]) -> Self { Self { bytes } }
/// Return the raw DER-encoded bytes. pub fn as_bytes(&self) -> &[u8] { self.bytes }
/// Decode to a vector of arc components (e.g., [1, 2, 840, 113549, ...]). pub fn components(&self) -> Result<Vec<u32>> { if self.bytes.is_empty() { return Err(Asn1Error::InvalidOid); }
let mut out = Vec::new(); let first = self.bytes[0]; out.push((first / 40) as u32); out.push((first % 40) as u32);
let mut i = 1; while i < self.bytes.len() { let mut value: u32 = 0; loop { if i >= self.bytes.len() { return Err(Asn1Error::InvalidOid); } let byte = self.bytes[i]; i += 1; value = value .checked_shl(7) .and_then(|v| v.checked_add((byte & 0x7F) as u32)) .ok_or(Asn1Error::InvalidOid)?; if byte & 0x80 == 0 { break; } } out.push(value); }
Ok(out) }
/// Encode from dotted-notation components. pub fn encode_components(components: &[u32]) -> Result<Vec<u8>> { if components.len() < 2 { return Err(Asn1Error::InvalidOid); } if components[0] > 2 || (components[0] < 2 && components[1] >= 40) { return Err(Asn1Error::InvalidOid); }
let mut out = Vec::new(); let first = (components[0] * 40 + components[1]) as u8; out.push(first);
for &c in &components[2..] { encode_base128(&mut out, c); }
Ok(out) }
/// Compare against a known OID given as a byte slice of DER-encoded value. pub fn matches(&self, other: &[u8]) -> bool { self.bytes == other }}
impl fmt::Debug for Oid<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.components() { Ok(comps) => { let s: Vec<String> = comps.iter().map(|c| c.to_string()).collect(); write!(f, "OID({})", s.join(".")) } Err(_) => write!(f, "OID(<invalid>)"), } }}
impl fmt::Display for Oid<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.components() { Ok(comps) => { let s: Vec<String> = comps.iter().map(|c| c.to_string()).collect(); write!(f, "{}", s.join(".")) } Err(_) => write!(f, "<invalid OID>"), } }}
fn encode_base128(out: &mut Vec<u8>, mut value: u32) { if value == 0 { out.push(0); return; } // Find out how many bytes we need let mut tmp = Vec::new(); while value > 0 { tmp.push((value & 0x7F) as u8); value >>= 7; } tmp.reverse(); for (i, byte) in tmp.iter().enumerate() { if i < tmp.len() - 1 { out.push(byte | 0x80); } else { out.push(*byte); } }}
// ---------------------------------------------------------------------------// Well-known OIDs (DER-encoded value bytes)// ---------------------------------------------------------------------------
/// sha256WithRSAEncryption (1.2.840.113549.1.1.11)pub const OID_SHA256_WITH_RSA: &[u8] = &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B];
/// sha384WithRSAEncryption (1.2.840.113549.1.1.12)pub const OID_SHA384_WITH_RSA: &[u8] = &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C];
/// sha512WithRSAEncryption (1.2.840.113549.1.1.13)pub const OID_SHA512_WITH_RSA: &[u8] = &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D];
/// rsaEncryption (1.2.840.113549.1.1.1)pub const OID_RSA_ENCRYPTION: &[u8] = &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01];
/// ecPublicKey (1.2.840.10045.2.1)pub const OID_EC_PUBLIC_KEY: &[u8] = &[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01];
/// prime256v1 / secp256r1 (1.2.840.10045.3.1.7)pub const OID_PRIME256V1: &[u8] = &[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07];
/// secp384r1 (1.3.132.0.34)pub const OID_SECP384R1: &[u8] = &[0x2B, 0x81, 0x04, 0x00, 0x22];
/// ecdsaWithSHA256 (1.2.840.10045.4.3.2)pub const OID_ECDSA_WITH_SHA256: &[u8] = &[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02];
/// ecdsaWithSHA384 (1.2.840.10045.4.3.3)pub const OID_ECDSA_WITH_SHA384: &[u8] = &[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03];
/// sha256 (2.16.840.1.101.3.4.2.1)pub const OID_SHA256: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01];
/// sha384 (2.16.840.1.101.3.4.2.2)pub const OID_SHA384: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02];
/// sha512 (2.16.840.1.101.3.4.2.3)pub const OID_SHA512: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03];
/// id-ce-subjectKeyIdentifier (2.5.29.14)pub const OID_SUBJECT_KEY_IDENTIFIER: &[u8] = &[0x55, 0x1D, 0x0E];
/// id-ce-keyUsage (2.5.29.15)pub const OID_KEY_USAGE: &[u8] = &[0x55, 0x1D, 0x0F];
/// id-ce-subjectAltName (2.5.29.17)pub const OID_SUBJECT_ALT_NAME: &[u8] = &[0x55, 0x1D, 0x11];
/// id-ce-basicConstraints (2.5.29.19)pub const OID_BASIC_CONSTRAINTS: &[u8] = &[0x55, 0x1D, 0x13];
/// id-ce-authorityKeyIdentifier (2.5.29.35)pub const OID_AUTHORITY_KEY_IDENTIFIER: &[u8] = &[0x55, 0x1D, 0x23];
/// id-at-commonName (2.5.4.3)pub const OID_COMMON_NAME: &[u8] = &[0x55, 0x04, 0x03];
/// id-at-countryName (2.5.4.6)pub const OID_COUNTRY_NAME: &[u8] = &[0x55, 0x04, 0x06];
/// id-at-organizationName (2.5.4.10)pub const OID_ORGANIZATION_NAME: &[u8] = &[0x55, 0x04, 0x0A];
/// id-at-organizationalUnitName (2.5.4.11)pub const OID_ORGANIZATIONAL_UNIT_NAME: &[u8] = &[0x55, 0x04, 0x0B];
// ---------------------------------------------------------------------------// TLV item — a parsed tag-length-value triple// ---------------------------------------------------------------------------
/// A single parsed TLV (Tag-Length-Value) item.#[derive(Debug, Clone)]pub struct Tlv<'a> { /// The raw tag byte. pub tag: u8, /// The value bytes (not including tag and length encoding). pub value: &'a [u8], /// The full TLV encoding (tag + length + value). Useful for signature verification. pub raw: &'a [u8],}
impl<'a> Tlv<'a> { /// True if this is a constructed (SEQUENCE, SET, context-specific constructed) type. pub fn is_constructed(&self) -> bool { self.tag & TAG_CONSTRUCTED != 0 }
/// Return the tag class (UNIVERSAL, APPLICATION, CONTEXT, PRIVATE). pub fn tag_class(&self) -> u8 { self.tag & 0xC0 }
/// Return the tag number (bits 4-0). pub fn tag_number(&self) -> u8 { self.tag & 0x1F }
/// True if this is a context-specific tag (class bits = 10). pub fn is_context_specific(&self) -> bool { self.tag_class() == TAG_CLASS_CONTEXT }
/// Return context-specific tag number, or None if not context-specific. pub fn context_tag(&self) -> Option<u8> { if self.is_context_specific() { Some(self.tag_number()) } else { None } }
/// Parse the value as a SEQUENCE of TLV items. pub fn sequence_items(&self) -> Result<Vec<Tlv<'a>>> { let mut parser = DerParser::new(self.value); let mut items = Vec::new(); while parser.has_more() { items.push(parser.read_tlv()?); } Ok(items) }
/// Parse the value as a DER INTEGER and return the bytes (may have leading zero for sign). pub fn as_integer(&self) -> Result<&'a [u8]> { if self.tag != TAG_INTEGER { return Err(Asn1Error::UnexpectedTag { expected: TAG_INTEGER, got: self.tag, }); } validate_der_integer(self.value)?; Ok(self.value) }
/// Parse the value as a positive DER INTEGER, stripping any leading zero byte. pub fn as_positive_integer(&self) -> Result<&'a [u8]> { let bytes = self.as_integer()?; if bytes.first() == Some(&0) && bytes.len() > 1 { Ok(&bytes[1..]) } else { Ok(bytes) } }
/// Parse the value as a BOOLEAN. pub fn as_boolean(&self) -> Result<bool> { if self.tag != TAG_BOOLEAN { return Err(Asn1Error::UnexpectedTag { expected: TAG_BOOLEAN, got: self.tag, }); } if self.value.len() != 1 { return Err(Asn1Error::InvalidBoolean); } match self.value[0] { 0x00 => Ok(false), 0xFF => Ok(true), _ => Err(Asn1Error::InvalidBoolean), } }
/// Parse the value as a NULL (must be empty). pub fn as_null(&self) -> Result<()> { if self.tag != TAG_NULL { return Err(Asn1Error::UnexpectedTag { expected: TAG_NULL, got: self.tag, }); } if !self.value.is_empty() { return Err(Asn1Error::TrailingData); } Ok(()) }
/// Parse the value as an OID. pub fn as_oid(&self) -> Result<Oid<'a>> { if self.tag != TAG_OID { return Err(Asn1Error::UnexpectedTag { expected: TAG_OID, got: self.tag, }); } if self.value.is_empty() { return Err(Asn1Error::InvalidOid); } Ok(Oid::from_der_bytes(self.value)) }
/// Parse the value as a BIT STRING. Returns (unused_bits, data). pub fn as_bit_string(&self) -> Result<(u8, &'a [u8])> { if self.tag != TAG_BIT_STRING { return Err(Asn1Error::UnexpectedTag { expected: TAG_BIT_STRING, got: self.tag, }); } if self.value.is_empty() { return Err(Asn1Error::InvalidBitString); } let unused = self.value[0]; if unused > 7 { return Err(Asn1Error::InvalidBitString); } let data = &self.value[1..]; if data.is_empty() && unused != 0 { return Err(Asn1Error::InvalidBitString); } // DER: unused bits in the last byte must be zero if unused > 0 && !data.is_empty() { let mask = (1u8 << unused) - 1; if data[data.len() - 1] & mask != 0 { return Err(Asn1Error::InvalidBitString); } } Ok((unused, data)) }
/// Parse the value as an OCTET STRING. pub fn as_octet_string(&self) -> Result<&'a [u8]> { if self.tag != TAG_OCTET_STRING { return Err(Asn1Error::UnexpectedTag { expected: TAG_OCTET_STRING, got: self.tag, }); } Ok(self.value) }
/// Parse the value as a UTF8String. pub fn as_utf8_string(&self) -> Result<&'a str> { if self.tag != TAG_UTF8_STRING { return Err(Asn1Error::UnexpectedTag { expected: TAG_UTF8_STRING, got: self.tag, }); } core::str::from_utf8(self.value).map_err(|_| Asn1Error::InvalidUtf8) }
/// Parse the value as a PrintableString (ASCII subset). pub fn as_printable_string(&self) -> Result<&'a str> { if self.tag != TAG_PRINTABLE_STRING { return Err(Asn1Error::UnexpectedTag { expected: TAG_PRINTABLE_STRING, got: self.tag, }); } core::str::from_utf8(self.value).map_err(|_| Asn1Error::InvalidUtf8) }
/// Parse the value as an IA5String (ASCII). pub fn as_ia5_string(&self) -> Result<&'a str> { if self.tag != TAG_IA5_STRING { return Err(Asn1Error::UnexpectedTag { expected: TAG_IA5_STRING, got: self.tag, }); } core::str::from_utf8(self.value).map_err(|_| Asn1Error::InvalidUtf8) }
/// Parse as any string type (UTF8String, PrintableString, IA5String). pub fn as_string(&self) -> Result<&'a str> { match self.tag { TAG_UTF8_STRING | TAG_PRINTABLE_STRING | TAG_IA5_STRING => { core::str::from_utf8(self.value).map_err(|_| Asn1Error::InvalidUtf8) } _ => Err(Asn1Error::UnexpectedTag { expected: TAG_UTF8_STRING, got: self.tag, }), } }
/// Parse the value as UTCTime (YYMMDDHHMMSSZ). pub fn as_utc_time(&self) -> Result<&'a str> { if self.tag != TAG_UTC_TIME { return Err(Asn1Error::UnexpectedTag { expected: TAG_UTC_TIME, got: self.tag, }); } let s = core::str::from_utf8(self.value).map_err(|_| Asn1Error::InvalidTime)?; // Basic validation: should be 13 chars ending in 'Z' (YYMMDDHHMMSSZ) if s.len() != 13 || !s.ends_with('Z') { return Err(Asn1Error::InvalidTime); } Ok(s) }
/// Parse the value as GeneralizedTime (YYYYMMDDHHMMSSZ). pub fn as_generalized_time(&self) -> Result<&'a str> { if self.tag != TAG_GENERALIZED_TIME { return Err(Asn1Error::UnexpectedTag { expected: TAG_GENERALIZED_TIME, got: self.tag, }); } let s = core::str::from_utf8(self.value).map_err(|_| Asn1Error::InvalidTime)?; // Basic validation: should be 15 chars ending in 'Z' (YYYYMMDDHHMMSSZ) if s.len() != 15 || !s.ends_with('Z') { return Err(Asn1Error::InvalidTime); } Ok(s) }}
// ---------------------------------------------------------------------------// DER integer validation// ---------------------------------------------------------------------------
fn validate_der_integer(bytes: &[u8]) -> Result<()> { if bytes.is_empty() { return Err(Asn1Error::NonMinimalInteger); } // Check for non-minimal positive: leading 0x00 followed by a byte < 0x80 if bytes.len() > 1 && bytes[0] == 0x00 && bytes[1] & 0x80 == 0 { return Err(Asn1Error::NonMinimalInteger); } // Check for non-minimal negative: leading 0xFF followed by a byte >= 0x80 if bytes.len() > 1 && bytes[0] == 0xFF && bytes[1] & 0x80 != 0 { return Err(Asn1Error::NonMinimalInteger); } Ok(())}
// ---------------------------------------------------------------------------// DER parser// ---------------------------------------------------------------------------
/// A streaming DER parser over a byte slice.pub struct DerParser<'a> { data: &'a [u8], pos: usize,}
impl<'a> DerParser<'a> { /// Create a new parser over the given data. pub fn new(data: &'a [u8]) -> Self { Self { data, pos: 0 } }
/// True if there is more data to parse. pub fn has_more(&self) -> bool { self.pos < self.data.len() }
/// Remaining unparsed bytes. pub fn remaining(&self) -> &'a [u8] { &self.data[self.pos..] }
/// Current position in the input. pub fn position(&self) -> usize { self.pos }
/// Read the next TLV item. pub fn read_tlv(&mut self) -> Result<Tlv<'a>> { let start = self.pos;
// Read tag let tag = self.read_byte()?;
// We only support single-byte tags (tag number < 31). // Multi-byte tags (tag number == 0x1F) are rare in X.509. if tag & 0x1F == 0x1F { // Long-form tag — skip continuation bytes // For simplicity, we don't decode these but we need to consume them. while self.pos < self.data.len() { let b = self.read_byte()?; if b & 0x80 == 0 { break; } } // This is a simplification; we store the first tag byte only. }
// Read length let length = self.read_length()?;
// Read value if self.pos + length > self.data.len() { return Err(Asn1Error::Truncated); } let value = &self.data[self.pos..self.pos + length]; self.pos += length; let raw = &self.data[start..self.pos];
Ok(Tlv { tag, value, raw }) }
/// Read a TLV and expect a specific tag. pub fn read_expect(&mut self, expected_tag: u8) -> Result<Tlv<'a>> { let tlv = self.read_tlv()?; if tlv.tag != expected_tag { return Err(Asn1Error::UnexpectedTag { expected: expected_tag, got: tlv.tag, }); } Ok(tlv) }
/// Read an optional context-specific tagged value. Returns None if the next /// tag doesn't match. pub fn read_optional_context( &mut self, tag_number: u8, constructed: bool, ) -> Result<Option<Tlv<'a>>> { if !self.has_more() { return Ok(None); } let expected = TAG_CLASS_CONTEXT | if constructed { TAG_CONSTRUCTED } else { 0 } | tag_number; if self.peek_tag()? != expected { return Ok(None); } Ok(Some(self.read_tlv()?)) }
/// Peek at the next tag byte without consuming it. pub fn peek_tag(&self) -> Result<u8> { if self.pos >= self.data.len() { return Err(Asn1Error::Truncated); } Ok(self.data[self.pos]) }
/// Skip the next TLV item. pub fn skip(&mut self) -> Result<()> { self.read_tlv()?; Ok(()) }
/// Read all remaining TLV items. pub fn read_all(&mut self) -> Result<Vec<Tlv<'a>>> { let mut items = Vec::new(); while self.has_more() { items.push(self.read_tlv()?); } Ok(items) }
// --- Internal helpers ---
fn read_byte(&mut self) -> Result<u8> { if self.pos >= self.data.len() { return Err(Asn1Error::Truncated); } let b = self.data[self.pos]; self.pos += 1; Ok(b) }
fn read_length(&mut self) -> Result<usize> { let first = self.read_byte()?; if first == 0x80 { // Indefinite length — forbidden in DER return Err(Asn1Error::IndefiniteLength); } if first & 0x80 == 0 { // Short form: length is the byte itself return Ok(first as usize); }
// Long form: first byte tells how many length bytes follow let num_bytes = (first & 0x7F) as usize; if num_bytes == 0 { return Err(Asn1Error::IndefiniteLength); } if num_bytes > 4 { // We don't support lengths > 4 bytes (4 GB should be more than enough) return Err(Asn1Error::LengthOverflow); }
let mut length: usize = 0; for i in 0..num_bytes { let b = self.read_byte()? as usize; // DER minimality: first byte must not be 0 in multi-byte encoding if i == 0 && b == 0 { return Err(Asn1Error::NonMinimalLength); } length = length .checked_shl(8) .and_then(|v| v.checked_add(b)) .ok_or(Asn1Error::LengthOverflow)?; }
// DER minimality: if the length fits in short form (< 128), must use short form if length < 128 { return Err(Asn1Error::NonMinimalLength); } // DER minimality: if the length fits in fewer bytes, must use fewer bytes if num_bytes > 1 { let min_bytes = if length < 0x100 { 1 } else if length < 0x10000 { 2 } else if length < 0x1000000 { 3 } else { 4 }; if num_bytes > min_bytes { return Err(Asn1Error::NonMinimalLength); } }
Ok(length) }}
// ---------------------------------------------------------------------------// Convenience: parse a single top-level TLV from a byte slice// ---------------------------------------------------------------------------
/// Parse a single DER-encoded TLV from the data, returning an error if there/// is trailing data.pub fn parse_one(data: &[u8]) -> Result<Tlv<'_>> { let mut parser = DerParser::new(data); let tlv = parser.read_tlv()?; if parser.has_more() { return Err(Asn1Error::TrailingData); } Ok(tlv)}
/// Parse a DER-encoded SEQUENCE and return its inner TLV items.pub fn parse_sequence(data: &[u8]) -> Result<Vec<Tlv<'_>>> { let outer = parse_one(data)?; if outer.tag != TAG_SEQUENCE { return Err(Asn1Error::UnexpectedTag { expected: TAG_SEQUENCE, got: outer.tag, }); } outer.sequence_items()}
// ---------------------------------------------------------------------------// Tests// ---------------------------------------------------------------------------
#[cfg(test)]mod tests { use super::*;
// --- Basic TLV parsing ---
#[test] fn parse_null() { let data = [0x05, 0x00]; // NULL let tlv = parse_one(&data).unwrap(); assert_eq!(tlv.tag, TAG_NULL); assert!(tlv.value.is_empty()); tlv.as_null().unwrap(); }
#[test] fn parse_boolean_true() { let data = [0x01, 0x01, 0xFF]; // BOOLEAN TRUE let tlv = parse_one(&data).unwrap(); assert!(tlv.as_boolean().unwrap()); }
#[test] fn parse_boolean_false() { let data = [0x01, 0x01, 0x00]; // BOOLEAN FALSE let tlv = parse_one(&data).unwrap(); assert!(!tlv.as_boolean().unwrap()); }
#[test] fn reject_invalid_boolean() { let data = [0x01, 0x01, 0x01]; // Not 0x00 or 0xFF let tlv = parse_one(&data).unwrap(); assert_eq!(tlv.as_boolean().unwrap_err(), Asn1Error::InvalidBoolean); }
#[test] fn parse_integer_positive() { // INTEGER 127 let data = [0x02, 0x01, 0x7F]; let tlv = parse_one(&data).unwrap(); assert_eq!(tlv.as_integer().unwrap(), &[0x7F]); }
#[test] fn parse_integer_with_leading_zero() { // INTEGER 128 (needs leading 0x00 to stay positive) let data = [0x02, 0x02, 0x00, 0x80]; let tlv = parse_one(&data).unwrap(); assert_eq!(tlv.as_integer().unwrap(), &[0x00, 0x80]); assert_eq!(tlv.as_positive_integer().unwrap(), &[0x80]); }
#[test] fn reject_non_minimal_integer() { // Non-minimal: leading 0x00 before a byte < 0x80 let data = [0x02, 0x02, 0x00, 0x7F]; let tlv = parse_one(&data).unwrap(); assert_eq!(tlv.as_integer().unwrap_err(), Asn1Error::NonMinimalInteger); }
#[test] fn parse_octet_string() { let data = [0x04, 0x03, 0x01, 0x02, 0x03]; let tlv = parse_one(&data).unwrap(); assert_eq!(tlv.as_octet_string().unwrap(), &[0x01, 0x02, 0x03]); }
#[test] fn parse_bit_string() { // BIT STRING: 0 unused bits, data = [0xAB, 0xCD] let data = [0x03, 0x03, 0x00, 0xAB, 0xCD]; let tlv = parse_one(&data).unwrap(); let (unused, bits) = tlv.as_bit_string().unwrap(); assert_eq!(unused, 0); assert_eq!(bits, &[0xAB, 0xCD]); }
#[test] fn parse_bit_string_with_unused_bits() { // BIT STRING: 4 unused bits, last byte has low 4 bits = 0 let data = [0x03, 0x02, 0x04, 0xF0]; let tlv = parse_one(&data).unwrap(); let (unused, bits) = tlv.as_bit_string().unwrap(); assert_eq!(unused, 4); assert_eq!(bits, &[0xF0]); }
#[test] fn reject_invalid_bit_string_nonzero_unused() { // BIT STRING: 4 unused bits but low 4 bits are non-zero let data = [0x03, 0x02, 0x04, 0xF1]; let tlv = parse_one(&data).unwrap(); assert_eq!( tlv.as_bit_string().unwrap_err(), Asn1Error::InvalidBitString ); }
// --- OID tests ---
#[test] fn parse_oid_rsa_encryption() { // OID 1.2.840.113549.1.1.1 (rsaEncryption) let data = [ 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, ]; let tlv = parse_one(&data).unwrap(); let oid = tlv.as_oid().unwrap(); assert!(oid.matches(OID_RSA_ENCRYPTION)); let comps = oid.components().unwrap(); assert_eq!(comps, vec![1, 2, 840, 113549, 1, 1, 1]); }
#[test] fn parse_oid_sha256_with_rsa() { let data = [ 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, ]; let tlv = parse_one(&data).unwrap(); let oid = tlv.as_oid().unwrap(); assert!(oid.matches(OID_SHA256_WITH_RSA)); assert_eq!(oid.components().unwrap(), vec![1, 2, 840, 113549, 1, 1, 11]); }
#[test] fn parse_oid_prime256v1() { let data = [0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]; let tlv = parse_one(&data).unwrap(); let oid = tlv.as_oid().unwrap(); assert!(oid.matches(OID_PRIME256V1)); assert_eq!(oid.components().unwrap(), vec![1, 2, 840, 10045, 3, 1, 7]); }
#[test] fn oid_display() { let oid = Oid::from_der_bytes(OID_RSA_ENCRYPTION); assert_eq!(format!("{}", oid), "1.2.840.113549.1.1.1"); }
#[test] fn oid_encode_decode_roundtrip() { let components = [1u32, 2, 840, 113549, 1, 1, 1]; let encoded = Oid::encode_components(&components).unwrap(); assert_eq!(encoded.as_slice(), OID_RSA_ENCRYPTION); let oid = Oid::from_der_bytes(&encoded); assert_eq!(oid.components().unwrap(), components); }
#[test] fn oid_encode_simple() { // 2.5.4.3 (id-at-commonName) let components = [2u32, 5, 4, 3]; let encoded = Oid::encode_components(&components).unwrap(); assert_eq!(encoded.as_slice(), OID_COMMON_NAME); }
// --- String tests ---
#[test] fn parse_utf8_string() { let data = [0x0C, 0x05, b'H', b'e', b'l', b'l', b'o']; let tlv = parse_one(&data).unwrap(); assert_eq!(tlv.as_utf8_string().unwrap(), "Hello"); }
#[test] fn parse_printable_string() { let data = [0x13, 0x02, b'U', b'S']; let tlv = parse_one(&data).unwrap(); assert_eq!(tlv.as_printable_string().unwrap(), "US"); }
#[test] fn parse_ia5_string() { let data = [0x16, 0x03, b'c', b'o', b'm']; let tlv = parse_one(&data).unwrap(); assert_eq!(tlv.as_ia5_string().unwrap(), "com"); }
// --- Time tests ---
#[test] fn parse_utc_time() { // "230101120000Z" let time_str = b"230101120000Z"; let mut data = vec![TAG_UTC_TIME, time_str.len() as u8]; data.extend_from_slice(time_str); let tlv = parse_one(&data).unwrap(); assert_eq!(tlv.as_utc_time().unwrap(), "230101120000Z"); }
#[test] fn parse_generalized_time() { // "20230101120000Z" let time_str = b"20230101120000Z"; let mut data = vec![TAG_GENERALIZED_TIME, time_str.len() as u8]; data.extend_from_slice(time_str); let tlv = parse_one(&data).unwrap(); assert_eq!(tlv.as_generalized_time().unwrap(), "20230101120000Z"); }
// --- SEQUENCE tests ---
#[test] fn parse_sequence_of_integers() { // SEQUENCE { INTEGER 1, INTEGER 2 } #[rustfmt::skip] let data = [ 0x30, 0x06, // SEQUENCE, length 6 0x02, 0x01, 0x01, // INTEGER 1 0x02, 0x01, 0x02, // INTEGER 2 ]; let items = parse_sequence(&data).unwrap(); assert_eq!(items.len(), 2); assert_eq!(items[0].as_integer().unwrap(), &[0x01]); assert_eq!(items[1].as_integer().unwrap(), &[0x02]); }
#[test] fn parse_nested_sequence() { // SEQUENCE { SEQUENCE { INTEGER 42 } } #[rustfmt::skip] let data = [ 0x30, 0x05, // outer SEQUENCE 0x30, 0x03, // inner SEQUENCE 0x02, 0x01, 0x2A, // INTEGER 42 ]; let outer = parse_sequence(&data).unwrap(); assert_eq!(outer.len(), 1); let inner = outer[0].sequence_items().unwrap(); assert_eq!(inner.len(), 1); assert_eq!(inner[0].as_integer().unwrap(), &[0x2A]); }
// --- Context-specific tags ---
#[test] fn parse_context_specific_explicit() { // [0] EXPLICIT { INTEGER 3 } #[rustfmt::skip] let data = [ 0xA0, 0x03, // context-specific [0] constructed 0x02, 0x01, 0x03, // INTEGER 3 ]; let tlv = parse_one(&data).unwrap(); assert!(tlv.is_context_specific()); assert_eq!(tlv.context_tag(), Some(0)); assert!(tlv.is_constructed());
// Parse inner content let mut inner = DerParser::new(tlv.value); let int_tlv = inner.read_tlv().unwrap(); assert_eq!(int_tlv.as_integer().unwrap(), &[0x03]); }
#[test] fn parse_context_specific_implicit() { // [1] IMPLICIT OCTET STRING (primitive) let data = [0x81, 0x02, 0xAB, 0xCD]; let tlv = parse_one(&data).unwrap(); assert!(tlv.is_context_specific()); assert_eq!(tlv.context_tag(), Some(1)); assert!(!tlv.is_constructed()); assert_eq!(tlv.value, &[0xAB, 0xCD]); }
#[test] fn read_optional_context_present() { #[rustfmt::skip] let data = [ 0xA0, 0x03, // [0] constructed 0x02, 0x01, 0x03, // INTEGER 3 ]; let mut parser = DerParser::new(&data); let opt = parser.read_optional_context(0, true).unwrap(); assert!(opt.is_some()); assert_eq!(opt.unwrap().context_tag(), Some(0)); }
#[test] fn read_optional_context_absent() { let data = [0x02, 0x01, 0x42]; // INTEGER, not [0] let mut parser = DerParser::new(&data); let opt = parser.read_optional_context(0, true).unwrap(); assert!(opt.is_none()); // Parser should not have advanced assert_eq!(parser.position(), 0); }
// --- Long-form length ---
#[test] fn parse_long_form_length() { // OCTET STRING with 200 bytes (needs 2-byte length encoding: 0x81, 0xC8) let mut data = vec![0x04, 0x81, 0xC8]; data.extend_from_slice(&[0xAA; 200]); let tlv = parse_one(&data).unwrap(); assert_eq!(tlv.value.len(), 200); }
#[test] fn parse_two_byte_long_form_length() { // OCTET STRING with 256 bytes (needs: 0x82, 0x01, 0x00) let mut data = vec![0x04, 0x82, 0x01, 0x00]; data.extend_from_slice(&[0xBB; 256]); let tlv = parse_one(&data).unwrap(); assert_eq!(tlv.value.len(), 256); }
// --- DER violation rejection ---
#[test] fn reject_indefinite_length() { let data = [0x30, 0x80, 0x00, 0x00]; // SEQUENCE with indefinite length assert_eq!(parse_one(&data).unwrap_err(), Asn1Error::IndefiniteLength); }
#[test] fn reject_non_minimal_length() { // OCTET STRING, length 5 encoded as 0x81, 0x05 (should be just 0x05) let data = [0x04, 0x81, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05]; assert_eq!(parse_one(&data).unwrap_err(), Asn1Error::NonMinimalLength); }
#[test] fn reject_trailing_data() { let data = [0x05, 0x00, 0xFF]; // NULL + trailing byte assert_eq!(parse_one(&data).unwrap_err(), Asn1Error::TrailingData); }
#[test] fn reject_truncated() { let data = [0x04, 0x05, 0x01, 0x02]; // OCTET STRING claims 5 bytes but only 2 given assert_eq!(parse_one(&data).unwrap_err(), Asn1Error::Truncated); }
// --- Real certificate fragment ---
#[test] fn parse_algorithm_identifier() { // AlgorithmIdentifier { algorithm: sha256WithRSA, parameters: NULL } #[rustfmt::skip] let data = [ 0x30, 0x0D, // SEQUENCE 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, // OID 0x05, 0x00, // NULL ]; let items = parse_sequence(&data).unwrap(); assert_eq!(items.len(), 2); let oid = items[0].as_oid().unwrap(); assert!(oid.matches(OID_SHA256_WITH_RSA)); items[1].as_null().unwrap(); }
#[test] fn parse_rdn_sequence_fragment() { // A single RelativeDistinguishedName: CN = "Test" #[rustfmt::skip] let data = [ 0x30, 0x13, // SEQUENCE (Name) 0x31, 0x11, // SET (RDN) 0x30, 0x0F, // SEQUENCE (AttributeTypeAndValue) 0x06, 0x03, 0x55, 0x04, 0x03, // OID: id-at-commonName 0x0C, 0x08, b'T', b'e', b's', b't', b' ', b'C', b'A', b'0', // UTF8String "Test CA0" ]; let name_items = parse_sequence(&data).unwrap(); assert_eq!(name_items.len(), 1); // One RDN
let rdn = name_items[0].sequence_items().unwrap(); assert_eq!(rdn.len(), 1); // One attribute
let atv = rdn[0].sequence_items().unwrap(); assert_eq!(atv.len(), 2);
let oid = atv[0].as_oid().unwrap(); assert!(oid.matches(OID_COMMON_NAME)); assert_eq!(atv[1].as_utf8_string().unwrap(), "Test CA0"); }
// --- Raw field access ---
#[test] fn raw_includes_full_encoding() { let data = [0x02, 0x01, 0x42]; // INTEGER 66 let tlv = parse_one(&data).unwrap(); assert_eq!(tlv.raw, &data); assert_eq!(tlv.value, &[0x42]); }
// --- DerParser sequential reads ---
#[test] fn parser_sequential_reads() { #[rustfmt::skip] let data = [ 0x02, 0x01, 0x01, // INTEGER 1 0x02, 0x01, 0x02, // INTEGER 2 0x05, 0x00, // NULL ]; let mut parser = DerParser::new(&data); let a = parser.read_expect(TAG_INTEGER).unwrap(); assert_eq!(a.as_integer().unwrap(), &[0x01]); let b = parser.read_expect(TAG_INTEGER).unwrap(); assert_eq!(b.as_integer().unwrap(), &[0x02]); let c = parser.read_expect(TAG_NULL).unwrap(); c.as_null().unwrap(); assert!(!parser.has_more()); }
#[test] fn parser_read_expect_wrong_tag() { let data = [0x05, 0x00]; // NULL let mut parser = DerParser::new(&data); let err = parser.read_expect(TAG_INTEGER).unwrap_err(); assert_eq!( err, Asn1Error::UnexpectedTag { expected: TAG_INTEGER, got: TAG_NULL, } ); }
// --- X.509 version field (context-specific [0] EXPLICIT INTEGER) ---
#[test] fn parse_x509_version_field() { // [0] EXPLICIT { INTEGER 2 } — X.509 v3 #[rustfmt::skip] let data = [ 0xA0, 0x03, // [0] constructed 0x02, 0x01, 0x02, // INTEGER 2 ]; let tlv = parse_one(&data).unwrap(); assert!(tlv.is_context_specific()); assert!(tlv.is_constructed()); assert_eq!(tlv.context_tag(), Some(0));
let mut inner = DerParser::new(tlv.value); let version = inner.read_expect(TAG_INTEGER).unwrap(); assert_eq!(version.as_integer().unwrap(), &[0x02]); }
// --- BasicConstraints extension value ---
#[test] fn parse_basic_constraints() { // SEQUENCE { BOOLEAN TRUE, INTEGER 0 } #[rustfmt::skip] let data = [ 0x30, 0x06, // SEQUENCE 0x01, 0x01, 0xFF, // BOOLEAN TRUE (cA) 0x02, 0x01, 0x00, // INTEGER 0 (pathLenConstraint) ]; let items = parse_sequence(&data).unwrap(); assert_eq!(items.len(), 2); assert!(items[0].as_boolean().unwrap()); assert_eq!(items[1].as_integer().unwrap(), &[0x00]); }
// --- Large OID component values ---
#[test] fn parse_oid_large_components() { // 2.16.840.1.101.3.4.2.1 (sha256) let data = [ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, ]; let tlv = parse_one(&data).unwrap(); let oid = tlv.as_oid().unwrap(); assert!(oid.matches(OID_SHA256)); assert_eq!( oid.components().unwrap(), vec![2, 16, 840, 1, 101, 3, 4, 2, 1] ); }
// --- SET tag ---
#[test] fn parse_set() { // SET { INTEGER 1, INTEGER 2 } #[rustfmt::skip] let data = [ 0x31, 0x06, // SET 0x02, 0x01, 0x01, // INTEGER 1 0x02, 0x01, 0x02, // INTEGER 2 ]; let tlv = parse_one(&data).unwrap(); assert_eq!(tlv.tag, TAG_SET); assert!(tlv.is_constructed()); let items = tlv.sequence_items().unwrap(); assert_eq!(items.len(), 2); }
// --- read_all ---
#[test] fn read_all_items() { #[rustfmt::skip] let data = [ 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03, ]; let mut parser = DerParser::new(&data); let items = parser.read_all().unwrap(); assert_eq!(items.len(), 3); }
// --- Empty data ---
#[test] fn empty_data_returns_truncated() { assert_eq!(parse_one(&[]).unwrap_err(), Asn1Error::Truncated); }}