///|
/// Reads one byte from `view` at `at`, raising `UnexpectedEof` past the end.
fn read_byte(view : BytesView, at : Int) -> Int raise DerError {
match view.get(at) {
Some(byte) => byte.to_int()
None => raise UnexpectedEof
}
}
///|
/// Decodes the identifier octets starting at `at`, returning the tag and the
/// index immediately after it.
///
/// Handles both the low-tag-number form and the multi-octet high-tag-number
/// form, rejecting a leading `0x80` group as required by DER (X.690, 8.1.2.4).
fn parse_tag(view : BytesView, at : Int) -> (Tag, Int) raise DerError {
let first = read_byte(view, at)
let cls = match first >> 6 {
0 => Universal
1 => Application
2 => ContextSpecific
_ => Private
}
let constructed = (first & 0x20) != 0
let number = first & 0x1F
if number != 0x1F {
return (Tag::new(cls, number, constructed~), at + 1)
}
let mut index = at + 1
let mut value = 0
let mut first_group = true
while true {
let byte = read_byte(view, index)
if first_group && (byte & 0x7F) == 0 {
raise InvalidTag(byte)
}
// Guard before shifting so the 32-bit accumulator cannot overflow.
if value > 0x00FF_FFFF {
raise InvalidTag(byte)
}
value = (value << 7) | (byte & 0x7F)
index = index + 1
first_group = false
if (byte & 0x80) == 0 {
break
}
}
(Tag::new(cls, value, constructed~), index)
}
///|
/// Decodes the length octets starting at `at`, returning the content length and
/// the index immediately after it.
///
/// DER requires the definite form with the minimal number of octets; the
/// indefinite form (`0x80`) and non-minimal encodings are rejected.
fn parse_length(view : BytesView, at : Int) -> (Int, Int) raise DerError {
let first = read_byte(view, at)
if first < 0x80 {
return (first, at + 1)
}
if first == 0x80 {
raise IndefiniteLength
}
let count = first & 0x7F
if count > 4 {
raise LengthTooLong(count)
}
let mut value = 0L
for i in 0.. 0x7FFF_FFFFL {
raise LengthTooLong(count)
}
let length = value.to_int()
let leading = read_byte(view, at + 1)
if count > 1 && leading == 0 {
raise NonMinimalLength(length)
}
if count == 1 && length < 0x80 {
raise NonMinimalLength(length)
}
(length, at + 1 + count)
}
///|
/// A cursor that decodes tag-length-value elements from a `BytesView`.
///
/// The reader never copies: content views point into the original buffer.
pub struct DerReader {
input : BytesView
mut pos : Int
}
///|
/// Creates a reader over `input`.
pub fn DerReader::new(input : BytesView) -> DerReader {
{ input, pos: 0, }
}
///|
/// Creates a reader over a byte buffer.
pub fn DerReader::from_bytes(input : Bytes) -> DerReader {
DerReader::new(input[:])
}
///|
/// Absolute offset of the cursor inside the underlying byte buffer.
pub fn DerReader::offset(self : DerReader) -> Int {
self.input.start_offset() + self.pos
}
///|
/// Number of bytes left to read.
pub fn DerReader::remaining(self : DerReader) -> Int {
self.input.length() - self.pos
}
///|
/// Whether the cursor has reached the end of the input.
pub fn DerReader::is_empty(self : DerReader) -> Bool {
self.remaining() == 0
}
///|
/// Returns the tag of the next element without consuming it.
pub fn DerReader::peek_tag(self : DerReader) -> Tag raise DerError {
parse_tag(self.input, self.pos).0
}
///|
/// Reads one element and returns its tag together with a view of its content
/// octets. The cursor advances past the element.
pub fn DerReader::read_tlv(self : DerReader) -> (Tag, BytesView) raise DerError {
let (tag, after_tag) = parse_tag(self.input, self.pos)
let (length, after_length) = parse_length(self.input, after_tag)
if length > self.input.length() - after_length {
raise Truncated(length)
}
let end = after_length + length
let content = self.input[after_length:end]
self.pos = end
(tag, content)
}
///|
/// Reads one element, asserting that it is a primitive universal value with the
/// given tag number, and returns its content octets.
pub fn DerReader::read_expected(
self : DerReader,
number : Int,
) -> BytesView raise DerError {
let (tag, content) = self.read_tlv()
if tag.cls != Universal || tag.number != number {
raise ExpectedTag(Tag::new(Universal, number).to_string(), tag.to_string())
}
content
}
///|
/// Decodes one element, recursing into constructed values.
pub fn DerReader::read_element(self : DerReader) -> Der raise DerError {
let (tag, content) = self.read_tlv()
if tag.constructed {
Constructed(tag, DerReader::new(content).read_all())
} else {
Primitive(tag, content)
}
}
///|
/// Decodes every remaining element in the input.
pub fn DerReader::read_all(self : DerReader) -> Array[Der] raise DerError {
let elements : Array[Der] = []
while self.remaining() > 0 {
elements.push(self.read_element())
}
elements
}
///|
/// Decodes exactly one element, raising `TrailingData` if anything follows it.
pub fn DerReader::read_single(self : DerReader) -> Der raise DerError {
let value = self.read_element()
if self.remaining() > 0 {
raise TrailingData(self.remaining())
}
value
}