148 lines
3.7 KiB
Rust
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;
|
|
};
|