///|
/// A decoded ASN.1 value.
pub(all) enum Der {
/// A primitive value: its tag and a view of its content octets.
Primitive(Tag, BytesView)
/// A constructed value: its tag and its decoded children, in order.
Constructed(Tag, Array[Der])
}
///|
/// The tag of this value.
pub fn Der::tag(self : Der) -> Tag {
match self {
Primitive(tag, _) | Constructed(tag, _) => tag
}
}
///|
/// Whether this value is constructed.
pub fn Der::is_constructed(self : Der) -> Bool {
self.tag().constructed
}
///|
/// Returns the raw content octets of a primitive value.
pub fn Der::content(self : Der) -> BytesView raise DerError {
match self {
Primitive(_, content) => content
Constructed(tag, _) =>
raise InvalidValue("expected a primitive value, got " + tag.to_string())
}
}
///|
/// Returns the children of a constructed value.
pub fn Der::children(self : Der) -> Array[Der] raise DerError {
match self {
Constructed(_, children) => children
Primitive(tag, _) =>
raise InvalidValue("expected a constructed value, got " + tag.to_string())
}
}
///|
/// Returns the `index`-th child of a constructed value.
pub fn Der::child(self : Der, index : Int) -> Der raise DerError {
let children = self.children()
match children.get(index) {
Some(child) => child
None =>
raise InvalidValue("child index " + index.to_string() + " out of range")
}
}
///|
/// Renders the value as an indented tree, handy for debugging and for the
/// command line tools built on top of this package.
pub fn Der::to_string(self : Der) -> String {
let builder = StringBuilder()
render_der(self, 0, builder)
builder.to_string()
}
///|
fn render_der(value : Der, depth : Int, builder : StringBuilder) -> Unit {
for _ in 0.. {
builder.write_string(tag.to_string())
builder.write_string(" ")
builder.write_string(content.length().to_string())
builder.write_string("B ")
builder.write_string(hex_preview(content))
}
Constructed(tag, children) => {
builder.write_string(tag.to_string())
builder.write_string(" {")
builder.write_string(children.length().to_string())
builder.write_string("}")
for child in children {
builder.write_string("\n")
render_der(child, depth + 1, builder)
}
}
}
}
///|
/// Formats up to eight content octets as space separated hex for `to_string`.
fn hex_preview(content : BytesView) -> String {
let limit = if content.length() < 8 { content.length() } else { 8 }
let encoded = @hex.encode(content[:limit])
let builder = StringBuilder()
let mut index = 0
while index < encoded.length() {
if index > 0 {
builder.write_string(" ")
}
builder.write_substring(encoded, index, 2)
index = index + 2
}
if content.length() > limit {
builder.write_string(" ..")
}
builder.to_string()
}