///|
/// A decoded OBJECT IDENTIFIER, stored as its arc values.
///
/// The first arc is always 0, 1, or 2 and the second arc is at most 39 when the
/// first arc is 0 or 1 (X.690, 8.19).
pub struct ObjectIdentifier {
arcs : Array[Int64]
}
///|
/// Creates an object identifier from its arcs, validating the two leading arcs.
pub fn ObjectIdentifier::from_arcs(
arcs : Array[Int64],
) -> ObjectIdentifier raise DerError {
if arcs.length() < 2 {
raise InvalidValue("OBJECT IDENTIFIER needs at least two arcs")
}
let first = arcs[0]
let second = arcs[1]
if first < 0L || first > 2L {
raise InvalidValue("first OBJECT IDENTIFIER arc must be 0, 1, or 2")
}
if first < 2L && (second < 0L || second > 39L) {
raise InvalidValue(
"second arc must be at most 39 when the first arc is 0 or 1",
)
}
{ arcs, }
}
///|
/// The arcs of the identifier.
pub fn ObjectIdentifier::arcs(self : ObjectIdentifier) -> Array[Int64] {
self.arcs
}
///|
/// Renders the identifier in dotted decimal notation, for example
/// `1.2.840.113549.1.1.1`.
pub fn ObjectIdentifier::to_string(self : ObjectIdentifier) -> String {
let builder = StringBuilder()
for i, arc in self.arcs {
if i > 0 {
builder.write_string(".")
}
builder.write_string(arc.to_string())
}
builder.to_string()
}
///|
/// Parses a dotted-decimal object identifier such as `2.5.29.17`.
pub fn ObjectIdentifier::from_string(
text : StringView,
) -> ObjectIdentifier raise DerError {
let arcs : Array[Int64] = []
for part in text.split(".") {
let arc_text = part.trim()
if arc_text.is_empty() {
raise InvalidValue("empty arc in object identifier")
}
let mut value = 0L
for i in 0.. 0x39 {
raise InvalidValue("object identifier arcs must be decimal digits")
}
if value > 0x0FFF_FFFF_FFFF_FFFFL {
raise InvalidValue("object identifier arc is too large")
}
value = value * 10L + (code - 0x30).to_int64()
}
arcs.push(value)
}
ObjectIdentifier::from_arcs(arcs)
}
///|
/// Decodes the content octets of an OBJECT IDENTIFIER.
pub fn decode_object_identifier(
content : BytesView,
) -> ObjectIdentifier raise DerError {
if content.length() == 0 {
raise InvalidValue("OBJECT IDENTIFIER has no content octets")
}
let (combined, next) = read_subidentifier(content, 0)
let first = if combined < 40L { 0L } else if combined < 80L { 1L } else { 2L }
let arcs : Array[Int64] = [first, combined - first * 40L]
let mut index = next
while index < content.length() {
let (arc, after) = read_subidentifier(content, index)
arcs.push(arc)
index = after
}
{ arcs, }
}
///|
/// Decodes one base-128 subidentifier starting at `at`, rejecting the
/// non-minimal leading `0x80` group that DER forbids.
fn read_subidentifier(
content : BytesView,
at : Int,
) -> (Int64, Int) raise DerError {
let mut index = at
let mut value = 0L
let mut first = true
while true {
let byte = read_byte(content, index)
if first && byte == 0x80 {
raise InvalidValue("OBJECT IDENTIFIER subidentifier is not minimal")
}
if value > 0x00FF_FFFF_FFFF_FFFFL {
raise InvalidValue("OBJECT IDENTIFIER subidentifier is too large")
}
value = (value << 7) | (byte & 0x7F).to_int64()
index = index + 1
first = false
if (byte & 0x80) == 0 {
break
}
}
(value, index)
}