fn length(len: usize) -> Vec<u8> {
if len < 0x80 {
return vec![u8::try_from(len).unwrap_or(0x7f)];
}
let body: Vec<u8> = len
.to_be_bytes()
.into_iter()
.skip_while(|byte| *byte == 0)
.collect();
let mut out = Vec::with_capacity(body.len().saturating_add(1));
out.push(0x80 | u8::try_from(body.len()).unwrap_or(0x7f));
out.extend_from_slice(&body);
out
}
#[must_use]
pub fn tlv(tag: u8, body: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(body.len().saturating_add(6));
out.push(tag);
out.extend_from_slice(&length(body.len()));
out.extend_from_slice(body);
out
}
#[must_use]
pub fn sequence(elements: &[Vec<u8>]) -> Vec<u8> {
tlv(0x30, &elements.concat())
}
#[must_use]
pub fn set_of(elements: &[Vec<u8>]) -> Vec<u8> {
tlv(0x31, &elements.concat())
}
#[must_use]
pub fn octet_string(bytes: &[u8]) -> Vec<u8> {
tlv(0x04, bytes)
}
#[must_use]
pub fn small_integer(value: u8) -> Vec<u8> {
if value < 0x80 {
tlv(0x02, &[value])
} else {
tlv(0x02, &[0x00, value])
}
}
#[must_use]
pub fn object_identifier(dotted: &str) -> Option<Vec<u8>> {
let arcs: Vec<u64> = dotted
.split('.')
.map(|arc| arc.parse().ok())
.collect::<Option<_>>()?;
let (&first, rest) = arcs.split_first()?;
let (&second, rest) = rest.split_first()?;
if first > 2 || (first < 2 && second >= 40) {
return None;
}
let mut body = base128(first.saturating_mul(40).saturating_add(second));
for &arc in rest {
body.extend_from_slice(&base128(arc));
}
Some(tlv(0x06, &body))
}
fn base128(mut value: u64) -> Vec<u8> {
let mut out = vec![u8::try_from(value & 0x7f).unwrap_or(0)];
value >>= 7;
while value > 0 {
out.push(u8::try_from(value & 0x7f).unwrap_or(0) | 0x80);
value >>= 7;
}
out.reverse();
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn writes_short_and_long_lengths() {
assert_eq!(tlv(0x04, &[]), vec![0x04, 0x00]);
assert_eq!(tlv(0x04, &[0xaa]), vec![0x04, 0x01, 0xaa]);
assert!(tlv(0x04, &[0; 127]).starts_with(&[0x04, 0x7f]));
assert!(tlv(0x04, &[0; 128]).starts_with(&[0x04, 0x81, 0x80]));
assert!(tlv(0x04, &[0; 256]).starts_with(&[0x04, 0x82, 0x01, 0x00]));
assert_eq!(tlv(0x04, &[0; 300]).len(), 300 + 4);
}
#[test]
fn writes_the_known_object_identifiers() {
assert_eq!(
object_identifier("2.100.3").unwrap(),
vec![0x06, 0x03, 0x81, 0x34, 0x03]
);
assert_eq!(
object_identifier(crate::types::oid::AES_256_WRAP).unwrap(),
vec![
0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2d
]
);
assert_eq!(
object_identifier(crate::types::oid::AES_256_GCM).unwrap(),
vec![
0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2e
]
);
assert_eq!(
object_identifier("1.2.840.113549.1.7.1").unwrap(),
vec![
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01
]
);
}
#[test]
fn refuses_what_is_not_an_object_identifier() {
for bad in ["", "1", "1.x", "3.0.1", "1.40.1"] {
assert!(object_identifier(bad).is_none(), "{bad} was accepted");
}
}
#[test]
fn writes_integers_that_stay_positive() {
assert_eq!(small_integer(0), vec![0x02, 0x01, 0x00]);
assert_eq!(small_integer(4), vec![0x02, 0x01, 0x04]);
assert_eq!(small_integer(12), vec![0x02, 0x01, 0x0c]);
assert_eq!(small_integer(0x80), vec![0x02, 0x02, 0x00, 0x80]);
}
#[test]
fn nests_constructed_types() {
assert_eq!(
sequence(&[small_integer(1), octet_string(&[0xff])]),
vec![0x30, 0x06, 0x02, 0x01, 0x01, 0x04, 0x01, 0xff]
);
assert_eq!(
set_of(&[small_integer(1)]),
vec![0x31, 0x03, 0x02, 0x01, 0x01]
);
}
}