///|
/// Encode a BER BOOLEAN (X.690 8.2). `false` encodes as `0x01 0x01 0x00`.
pub fn encode_boolean(b : Bool) -> Bytes {
let content = if b {
Bytes::from_array([0xFF])
} else {
Bytes::from_array([0x00])
}
encode_tlv(universal_tag(tag_boolean, false), content)
}
///|
/// Encode a BER INTEGER with the minimal two's-complement representation
/// (X.690 8.3). Negative values get a leading `0xFF` padding byte when the
/// high bit would otherwise be clear.
pub fn encode_integer(v : Int) -> Bytes {
encode_tlv(universal_tag(tag_integer, false), integer_content(v))
}
///|
/// Encode a BER NULL (X.690 8.8): `0x05 0x00`.
pub fn encode_null() -> Bytes {
encode_tlv(universal_tag(tag_null, false), Bytes::new(0))
}
///|
/// Encode a BER OCTET STRING (X.690 8.7).
pub fn encode_octet_string(content : Bytes) -> Bytes {
encode_tlv(universal_tag(tag_octet_string, false), content)
}
///|
/// Encode a UTF8String (X.690 8.21).
pub fn encode_utf8_string(s : String) -> Bytes {
encode_tlv(universal_tag(tag_utf8_string, false), @utf8.encode(s))
}
///|
/// Encode an IA5String (X.690 8.22).
pub fn encode_ia5_string(s : String) -> Bytes {
encode_tlv(universal_tag(tag_ia5_string, false), @utf8.encode(s))
}
///|
/// Encode a BER ENUMERATED (X.690 8.4), reusing the INTEGER encoding rules.
pub fn encode_enumerated(v : Int) -> Bytes {
encode_tlv(universal_tag(tag_enumerated, false), integer_content(v))
}
///|
/// Encode a constructed SEQUENCE (X.690 8.9).
pub fn encode_sequence(items : Array[Bytes]) -> Bytes {
let mut content = Bytes::new(0)
for item in items {
content = content + item
}
encode_tlv(universal_tag(tag_sequence, true), content)
}
///|
/// Encode a constructed SET (X.690 8.11).
pub fn encode_set(items : Array[Bytes]) -> Bytes {
let mut content = Bytes::new(0)
for item in items {
content = content + item
}
encode_tlv(universal_tag(tag_set, true), content)
}
///|
/// Encode a BER OID from its dotted-decimal string form (X.690 8.19).
pub fn encode_oid(oid : String) -> Result[Bytes, BerError] {
result_of_ber(fn() raise BerError {
let parts = split_oid(oid)
if parts.length() < 2 {
raise InvalidOid
}
let acc : Array[Byte] = []
// First arc: first * 40 + second.
let first = parse_oid_arc(parts[0])
let second = parse_oid_arc(parts[1])
let first_value = first * 40 + second
push_base128(acc, first_value)
for i in 2.. Bytes {
let content = Bytes::from_array([unused_bits.to_byte()]) + data
encode_tlv(universal_tag(tag_bit_string, false), content)
}
// Compute the minimal two's-complement content octets of an integer.
///|
pub fn integer_content(v : Int) -> Bytes {
let acc : Array[Byte] = []
let mut remaining = v
while remaining != 0 && remaining != -1 {
acc.push((remaining & 0xFF).to_byte())
remaining = remaining >> 8
}
if acc.is_empty() {
// v is 0 or -1.
let b = if v == 0 { (0).to_byte() } else { (0xFF).to_byte() }
return Bytes::from_array([b])
}
let out = reverse_array(acc)
let first = out[0].to_int()
if v >= 0 && (first & 0x80) != 0 {
out.insert(0, (0).to_byte())
} else if v < 0 && (first & 0x80) == 0 {
out.insert(0, (0xFF).to_byte())
}
Bytes::from_array(out)
}
///|
fn split_oid(oid : String) -> Array[String] {
let out : Array[String] = []
let mut current = StringBuilder()
for c in oid {
if c == '.' {
out.push(current.to_string())
current = StringBuilder()
} else {
current.write_char(c)
}
}
out.push(current.to_string())
out
}
///|
fn parse_oid_arc(s : String) -> Int raise BerError {
let mut value = 0
let mut seen = false
for c in s {
if c.is_ascii_digit() {
seen = true
value = value * 10 + (c.to_int() - '0'.to_int())
if value > 1000000000 {
raise InvalidOid
}
} else {
raise InvalidOid
}
}
if !seen {
raise InvalidOid
}
value
}
///|
fn push_base128(acc : Array[Byte], v : Int) -> Unit {
let tmp : Array[Byte] = []
let mut remaining = v
while remaining > 0 {
tmp.push((remaining & 0x7F).to_byte())
remaining = remaining >> 7
}
if tmp.is_empty() {
tmp.push((0).to_byte())
}
let out = reverse_array(tmp)
for i in 0..