fn len(n: usize) -> Vec<u8> {
if n < 0x80 {
vec![n as u8]
} else {
let mut b = n.to_be_bytes().to_vec();
while b.first() == Some(&0) {
b.remove(0);
}
let mut out = vec![0x80 | b.len() as u8];
out.extend_from_slice(&b);
out
}
}
pub fn tlv(tag: u8, content: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(content.len() + 4);
out.push(tag);
out.extend_from_slice(&len(content.len()));
out.extend_from_slice(content);
out
}
fn concat(parts: &[&[u8]]) -> Vec<u8> {
let mut v = Vec::new();
for p in parts {
v.extend_from_slice(p);
}
v
}
pub fn seq(parts: &[&[u8]]) -> Vec<u8> {
tlv(0x30, &concat(parts))
}
pub fn set(parts: &[&[u8]]) -> Vec<u8> {
tlv(0x31, &concat(parts))
}
pub fn octet_string(v: &[u8]) -> Vec<u8> {
tlv(0x04, v)
}
pub fn null() -> Vec<u8> {
vec![0x05, 0x00]
}
pub fn int_one() -> Vec<u8> {
vec![0x02, 0x01, 0x01]
}
pub fn ctx(n: u8, content: &[u8]) -> Vec<u8> {
tlv(0xa0 | n, content)
}
pub fn ctx_prim(n: u8, content: &[u8]) -> Vec<u8> {
tlv(0x80 | n, content)
}
pub fn utc_time(s: &str) -> Vec<u8> {
tlv(0x17, s.as_bytes())
}
pub fn oid(dotted: &str) -> Vec<u8> {
let parts: Vec<u64> = dotted.split('.').map(|p| p.parse().unwrap()).collect();
let mut body = vec![(parts[0] * 40 + parts[1]) as u8];
for &n in &parts[2..] {
let mut stack = vec![(n & 0x7f) as u8];
let mut n = n >> 7;
while n > 0 {
stack.push((n & 0x7f) as u8 | 0x80);
n >>= 7;
}
stack.reverse();
body.extend_from_slice(&stack);
}
tlv(0x06, &body)
}
pub fn raw(bytes: &[u8]) -> Vec<u8> {
bytes.to_vec()
}
pub fn bool_true() -> Vec<u8> {
vec![0x01, 0x01, 0xff]
}
pub fn children(tlv: &[u8]) -> Result<Vec<&[u8]>, &'static str> {
let (hdr, len) = read_len(tlv, 1)?;
let content = tlv.get(hdr..hdr + len).ok_or("truncated TLV")?;
let mut out = Vec::new();
let mut i = 0;
while i < content.len() {
let (h, l) = read_len(content, i + 1)?;
let end = h + l;
out.push(content.get(i..end).ok_or("truncated element")?);
i = end;
}
Ok(out)
}
fn read_len(b: &[u8], at: usize) -> Result<(usize, usize), &'static str> {
let first = *b.get(at).ok_or("truncated length")?;
if first < 0x80 {
Ok((at + 1, first as usize))
} else {
let n = (first & 0x7f) as usize;
let bytes = b.get(at + 1..at + 1 + n).ok_or("truncated long length")?;
let mut len = 0usize;
for &byte in bytes {
len = (len << 8) | byte as usize;
}
Ok((at + 1 + n, len))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encodes_oids() {
assert_eq!(
hex::encode(oid("2.16.840.1.101.3.4.2.1")),
"0609608648016503040201"
);
assert_eq!(
hex::encode(oid("1.3.6.1.4.1.311.2.1.4")),
"060a2b060104018237020104"
);
}
#[test]
fn long_length() {
let content = vec![0u8; 300];
let e = tlv(0x04, &content);
assert_eq!(&e[..4], &[0x04, 0x82, 0x01, 0x2c]);
}
}