use crate::ccr::model::{ AspaPayloadSet, AspaPayloadState, CCR_VERSION_V0, CcrContentInfo, CcrDigestAlgorithm, ManifestInstance, ManifestState, RoaPayloadSet, RoaPayloadState, RouterKey, RouterKeySet, RouterKeyState, RpkiCanonicalCacheRepresentation, TrustAnchorState, }; use crate::data_model::common::BigUnsigned; use crate::data_model::oid::{OID_CT_RPKI_CCR_RAW, OID_SHA256_RAW}; #[derive(Debug, thiserror::Error)] pub enum CcrEncodeError { #[error("CCR model validation failed: {0}")] Validate(String), #[error("GeneralizedTime formatting failed: {0}")] ProducedAtFormat(String), } pub fn encode_content_info(content_info: &CcrContentInfo) -> Result, CcrEncodeError> { content_info.validate().map_err(CcrEncodeError::Validate)?; let content_der = encode_ccr(&content_info.content)?; Ok(encode_sequence(&[ encode_oid(OID_CT_RPKI_CCR_RAW), encode_explicit(0, &content_der), ])) } pub fn encode_ccr(ccr: &RpkiCanonicalCacheRepresentation) -> Result, CcrEncodeError> { ccr.validate().map_err(CcrEncodeError::Validate)?; let mut fields = Vec::new(); if ccr.version != CCR_VERSION_V0 { fields.push(encode_explicit(0, &encode_integer_u32(ccr.version))); } fields.push(encode_digest_algorithm(&ccr.hash_alg)); fields.push(encode_generalized_time(ccr.produced_at)?); if let Some(mfts) = &ccr.mfts { fields.push(encode_explicit(1, &encode_manifest_state(mfts)?)); } if let Some(vrps) = &ccr.vrps { fields.push(encode_explicit(2, &encode_roa_payload_state(vrps)?)); } if let Some(vaps) = &ccr.vaps { fields.push(encode_explicit(3, &encode_aspa_payload_state(vaps)?)); } if let Some(tas) = &ccr.tas { fields.push(encode_explicit(4, &encode_trust_anchor_state(tas)?)); } if let Some(rks) = &ccr.rks { fields.push(encode_explicit(5, &encode_router_key_state(rks)?)); } Ok(encode_sequence(&fields)) } pub fn encode_manifest_state(state: &ManifestState) -> Result, CcrEncodeError> { state.validate().map_err(CcrEncodeError::Validate)?; let mis = encode_manifest_state_payload_der(&state.mis)?; Ok(encode_sequence(&[ mis, encode_generalized_time(state.most_recent_update)?, encode_octet_string(&state.hash), ])) } pub fn encode_manifest_state_payload_der( instances: &[ManifestInstance], ) -> Result, CcrEncodeError> { Ok(encode_sequence( &instances .iter() .map(encode_manifest_instance) .collect::, _>>()?, )) } fn encode_manifest_instance(instance: &ManifestInstance) -> Result, CcrEncodeError> { instance.validate().map_err(CcrEncodeError::Validate)?; let mut fields = vec![ encode_octet_string(&instance.hash), encode_integer_u64(instance.size), encode_octet_string(&instance.aki), encode_integer_bigunsigned(&instance.manifest_number), encode_generalized_time(instance.this_update)?, encode_sequence(&instance.locations), ]; if !instance.subordinates.is_empty() { fields.push(encode_sequence( &instance .subordinates .iter() .map(|ski| encode_octet_string(ski)) .collect::>(), )); } Ok(encode_sequence(&fields)) } pub fn encode_roa_payload_state(state: &RoaPayloadState) -> Result, CcrEncodeError> { state.validate().map_err(CcrEncodeError::Validate)?; let rps = encode_roa_payload_state_payload_der(&state.rps)?; Ok(encode_sequence(&[rps, encode_octet_string(&state.hash)])) } pub fn encode_roa_payload_state_payload_der( sets: &[RoaPayloadSet], ) -> Result, CcrEncodeError> { Ok(encode_sequence( &sets .iter() .map(encode_roa_payload_set) .collect::, _>>()?, )) } fn encode_roa_payload_set(set: &RoaPayloadSet) -> Result, CcrEncodeError> { set.validate().map_err(CcrEncodeError::Validate)?; Ok(encode_sequence(&[ encode_integer_u32(set.as_id), encode_sequence(&set.ip_addr_blocks), ])) } pub fn encode_aspa_payload_state(state: &AspaPayloadState) -> Result, CcrEncodeError> { state.validate().map_err(CcrEncodeError::Validate)?; let aps = encode_aspa_payload_state_payload_der(&state.aps)?; Ok(encode_sequence(&[aps, encode_octet_string(&state.hash)])) } pub fn encode_aspa_payload_state_payload_der( sets: &[AspaPayloadSet], ) -> Result, CcrEncodeError> { Ok(encode_sequence( &sets .iter() .map(encode_aspa_payload_set) .collect::, _>>()?, )) } fn encode_aspa_payload_set(set: &AspaPayloadSet) -> Result, CcrEncodeError> { set.validate().map_err(CcrEncodeError::Validate)?; Ok(encode_sequence(&[ encode_integer_u32(set.customer_as_id), encode_sequence( &set.providers .iter() .map(|provider| encode_integer_u32(*provider)) .collect::>(), ), ])) } pub fn encode_trust_anchor_state(state: &TrustAnchorState) -> Result, CcrEncodeError> { state.validate().map_err(CcrEncodeError::Validate)?; let skis = encode_trust_anchor_state_payload_der(&state.skis)?; Ok(encode_sequence(&[skis, encode_octet_string(&state.hash)])) } pub fn encode_trust_anchor_state_payload_der(skis: &[Vec]) -> Result, CcrEncodeError> { Ok(encode_sequence( &skis .iter() .map(|ski| encode_octet_string(ski)) .collect::>(), )) } pub fn encode_router_key_state(state: &RouterKeyState) -> Result, CcrEncodeError> { state.validate().map_err(CcrEncodeError::Validate)?; let rksets = encode_router_key_state_payload_der(&state.rksets)?; Ok(encode_sequence(&[rksets, encode_octet_string(&state.hash)])) } pub fn encode_router_key_state_payload_der( sets: &[RouterKeySet], ) -> Result, CcrEncodeError> { Ok(encode_sequence( &sets .iter() .map(encode_router_key_set) .collect::, _>>()?, )) } fn encode_router_key_set(set: &RouterKeySet) -> Result, CcrEncodeError> { set.validate().map_err(CcrEncodeError::Validate)?; Ok(encode_sequence(&[ encode_integer_u32(set.as_id), encode_sequence( &set.router_keys .iter() .map(encode_router_key) .collect::, _>>()?, ), ])) } fn encode_router_key(key: &RouterKey) -> Result, CcrEncodeError> { key.validate().map_err(CcrEncodeError::Validate)?; Ok(encode_sequence(&[ encode_octet_string(&key.ski), key.spki_der.clone(), ])) } fn encode_digest_algorithm(alg: &CcrDigestAlgorithm) -> Vec { match alg { CcrDigestAlgorithm::Sha256 => encode_sequence(&[encode_oid(OID_SHA256_RAW)]), } } fn encode_generalized_time(t: time::OffsetDateTime) -> Result, CcrEncodeError> { 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() ); Ok(encode_tlv(0x18, s.into_bytes())) } fn encode_integer_u32(v: u32) -> Vec { encode_integer_bytes(unsigned_integer_bytes(v as u64)) } fn encode_integer_u64(v: u64) -> Vec { encode_integer_bytes(unsigned_integer_bytes(v)) } fn encode_integer_bigunsigned(v: &BigUnsigned) -> Vec { encode_integer_bytes(v.bytes_be.clone()) } fn encode_integer_bytes(mut bytes: Vec) -> Vec { 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 { 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 { encode_tlv(0x06, raw_body.to_vec()) } fn encode_octet_string(bytes: &[u8]) -> Vec { encode_tlv(0x04, bytes.to_vec()) } fn encode_explicit(tag_number: u8, inner_der: &[u8]) -> Vec { encode_tlv(0xA0 + tag_number, inner_der.to_vec()) } fn encode_sequence(elements: &[Vec]) -> Vec { let total_len: usize = elements.iter().map(Vec::len).sum(); let mut buf = Vec::with_capacity(total_len); for element in elements { buf.extend_from_slice(element); } encode_tlv(0x30, buf) } fn encode_tlv(tag: u8, value: Vec) -> Vec { let mut out = Vec::with_capacity(1 + 9 + value.len()); out.push(tag); encode_length(value.len(), &mut out); out.extend_from_slice(&value); out } fn encode_length(len: usize, out: &mut Vec) { if len < 0x80 { out.push(len as u8); return; } let mut bytes = Vec::new(); let mut value = len; while value > 0 { bytes.push((value & 0xFF) as u8); value >>= 8; } bytes.reverse(); out.push(0x80 | (bytes.len() as u8)); out.extend_from_slice(&bytes); }