rpki/src/cir/encode.rs

148 lines
3.7 KiB
Rust

use crate::cir::model::{
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
};
use crate::data_model::oid::OID_SHA256_RAW;
#[derive(Debug, thiserror::Error)]
pub enum CirEncodeError {
#[error("CIR model validation failed: {0}")]
Validate(String),
}
pub fn encode_cir(cir: &CanonicalInputRepresentation) -> Result<Vec<u8>, CirEncodeError> {
cir.validate().map_err(CirEncodeError::Validate)?;
Ok(encode_sequence(&[
encode_integer_u32(cir.version),
encode_oid(match cir.hash_alg {
CirHashAlgorithm::Sha256 => OID_SHA256_RAW,
}),
encode_generalized_time(cir.validation_time),
encode_sequence(
&cir.objects
.iter()
.map(encode_object)
.collect::<Result<Vec<_>, _>>()?,
),
encode_sequence(
&cir.tals
.iter()
.map(encode_tal)
.collect::<Result<Vec<_>, _>>()?,
),
]))
}
fn encode_object(object: &CirObject) -> Result<Vec<u8>, CirEncodeError> {
object.validate().map_err(CirEncodeError::Validate)?;
Ok(encode_sequence(&[
encode_ia5_string(object.rsync_uri.as_bytes()),
encode_octet_string(&object.sha256),
]))
}
fn encode_tal(tal: &CirTal) -> Result<Vec<u8>, CirEncodeError> {
tal.validate().map_err(CirEncodeError::Validate)?;
Ok(encode_sequence(&[
encode_ia5_string(tal.tal_uri.as_bytes()),
encode_octet_string(&tal.tal_bytes),
]))
}
fn encode_generalized_time(t: time::OffsetDateTime) -> Vec<u8> {
let t = t.to_offset(time::UtcOffset::UTC);
let s = format!(
"{:04}{:02}{:02}{:02}{:02}{:02}Z",
t.year(),
u8::from(t.month()),
t.day(),
t.hour(),
t.minute(),
t.second()
);
encode_tlv(0x18, s.into_bytes())
}
fn encode_integer_u32(v: u32) -> Vec<u8> {
encode_integer_bytes(unsigned_integer_bytes(v as u64))
}
fn encode_integer_bytes(mut bytes: Vec<u8>) -> Vec<u8> {
if bytes.is_empty() {
bytes.push(0);
}
if bytes[0] & 0x80 != 0 {
bytes.insert(0, 0);
}
encode_tlv(0x02, bytes)
}
fn unsigned_integer_bytes(v: u64) -> Vec<u8> {
if v == 0 {
return vec![0];
}
let mut out = Vec::new();
let mut n = v;
while n > 0 {
out.push((n & 0xFF) as u8);
n >>= 8;
}
out.reverse();
out
}
fn encode_oid(raw_body: &[u8]) -> Vec<u8> {
encode_tlv(0x06, raw_body.to_vec())
}
fn encode_ia5_string(bytes: &[u8]) -> Vec<u8> {
encode_tlv(0x16, bytes.to_vec())
}
fn encode_octet_string(bytes: &[u8]) -> Vec<u8> {
encode_tlv(0x04, bytes.to_vec())
}
fn encode_sequence(elements: &[Vec<u8>]) -> Vec<u8> {
let mut body = Vec::new();
for element in elements {
body.extend_from_slice(element);
}
encode_tlv(0x30, body)
}
fn encode_tlv(tag: u8, value: Vec<u8>) -> Vec<u8> {
let mut out = Vec::with_capacity(1 + encoded_len_len(value.len()) + value.len());
out.push(tag);
encode_len_into(value.len(), &mut out);
out.extend_from_slice(&value);
out
}
fn encoded_len_len(len: usize) -> usize {
if len < 0x80 {
1
} else {
1 + len.to_be_bytes().iter().skip_while(|&&b| b == 0).count()
}
}
fn encode_len_into(len: usize, out: &mut Vec<u8>) {
if len < 0x80 {
out.push(len as u8);
return;
}
let bytes = len.to_be_bytes();
let first_non_zero = bytes
.iter()
.position(|&b| b != 0)
.unwrap_or(bytes.len() - 1);
let len_bytes = &bytes[first_non_zero..];
out.push(0x80 | (len_bytes.len() as u8));
out.extend_from_slice(len_bytes);
}
#[allow(dead_code)]
const _: () = {
let _ = CIR_VERSION_V1;
};