20260316迭代 增加delta replay以及multi-rir

replay 对比,五个RIR 输出vrp与routinator一致
This commit is contained in:
2026-03-16 22:54:48 +08:00
parent 73d8ebb5c1
commit 557a69cbd2
24 changed files with 4678 additions and 55 deletions

View File

@@ -1,4 +1,4 @@
use crate::data_model::common::{Asn1TimeEncoding, Asn1TimeUtc, DerReader, der_take_tlv};
use crate::data_model::common::{Asn1TimeEncoding, Asn1TimeUtc, DerReader, der_uint_from_bytes};
use crate::data_model::oid::{
OID_AD_SIGNED_OBJECT, OID_CMS_ATTR_CONTENT_TYPE, OID_CMS_ATTR_CONTENT_TYPE_RAW,
OID_CMS_ATTR_MESSAGE_DIGEST, OID_CMS_ATTR_MESSAGE_DIGEST_RAW, OID_CMS_ATTR_SIGNING_TIME,
@@ -9,6 +9,7 @@ use crate::data_model::oid::{
OID_SIGNED_DATA_RAW, OID_SUBJECT_INFO_ACCESS,
};
use crate::data_model::rc::{ResourceCertificate, SubjectInfoAccess};
use asn1_rs::{Any, Class, FromBer, Header, Tag};
use ring::digest;
use x509_parser::prelude::FromDer;
use x509_parser::public_key::PublicKey;
@@ -305,27 +306,7 @@ impl RpkiSignedObject {
/// This performs encoding/structure parsing only. Profile constraints are enforced by
/// `RpkiSignedObjectParsed::validate_profile`.
pub fn parse_der(der: &[u8]) -> Result<RpkiSignedObjectParsed, SignedObjectParseError> {
let mut r = DerReader::new(der);
let mut content_info_seq = r
.take_sequence()
.map_err(|e| SignedObjectParseError::Parse(e.to_string()))?;
if !r.is_empty() {
return Err(SignedObjectParseError::TrailingBytes(r.remaining_len()));
}
let content_type = take_oid_string(&mut content_info_seq)?;
let signed_data = parse_signed_data_from_contentinfo_cursor(&mut content_info_seq)?;
if !content_info_seq.is_empty() {
return Err(SignedObjectParseError::Parse(
"ContentInfo must be a SEQUENCE of 2 elements".into(),
));
}
Ok(RpkiSignedObjectParsed {
raw_der: der.to_vec(),
content_info_content_type: content_type,
signed_data,
})
parse_signed_object_content_info(der, der)
}
/// Decode a DER-encoded RPKI Signed Object (CMS ContentInfo wrapping SignedData) and enforce
@@ -380,7 +361,6 @@ impl RpkiSignedObject {
};
let signer = &self.signed_data.signer_infos[0];
// The message to be verified is the DER encoding of SignedAttributes (SET OF Attribute).
let msg = &signer.signed_attrs_der_for_signature;
let pk = ring::signature::RsaPublicKeyComponents { n, e };
@@ -393,6 +373,159 @@ impl RpkiSignedObject {
}
}
struct CmsReader<'a> {
buf: &'a [u8],
}
impl<'a> CmsReader<'a> {
fn new(buf: &'a [u8]) -> Self {
Self { buf }
}
fn is_empty(&self) -> bool {
self.buf.is_empty()
}
fn remaining_len(&self) -> usize {
self.buf.len()
}
fn peek_tag(&self) -> Result<u8, String> {
let (_rem, any) = Any::from_ber(self.buf).map_err(|e| format!("BER parse error: {e}"))?;
header_to_single_byte_tag(&any.header)
}
fn take_any(&mut self) -> Result<(u8, &'a [u8]), String> {
let (rem, any) = Any::from_ber(self.buf).map_err(|e| format!("BER parse error: {e}"))?;
let tag = header_to_single_byte_tag(&any.header)?;
self.buf = rem;
Ok((tag, any.data))
}
fn take_any_full(&mut self) -> Result<(u8, &'a [u8], &'a [u8]), String> {
let (rem, any) = Any::from_ber(self.buf).map_err(|e| format!("BER parse error: {e}"))?;
let consumed = self.buf.len() - rem.len();
let full = &self.buf[..consumed];
let tag = header_to_single_byte_tag(&any.header)?;
self.buf = rem;
Ok((tag, full, any.data))
}
fn skip_any(&mut self) -> Result<(), String> {
let _ = self.take_any()?;
Ok(())
}
fn take_tag(&mut self, expected_tag: u8) -> Result<&'a [u8], String> {
let (tag, value) = self.take_any()?;
if tag != expected_tag {
return Err(format!(
"unexpected tag: got 0x{tag:02X}, expected 0x{expected_tag:02X}"
));
}
Ok(value)
}
fn take_sequence(&mut self) -> Result<CmsReader<'a>, String> {
let value = self.take_tag(0x30)?;
Ok(CmsReader::new(value))
}
fn take_octet_string(&mut self) -> Result<Vec<u8>, String> {
let (rem, any) = Any::from_ber(self.buf).map_err(|e| format!("BER parse error: {e}"))?;
let tag = header_to_single_byte_tag(&any.header)?;
if tag != 0x04 && tag != 0x24 {
return Err(format!(
"unexpected tag: got 0x{tag:02X}, expected 0x04"
));
}
let octets = flatten_octet_string(any)?;
self.buf = rem;
Ok(octets)
}
fn take_uint_u64(&mut self) -> Result<u64, String> {
let value = self.take_tag(0x02)?;
der_uint_from_bytes(value)
}
fn take_explicit(&mut self, expected_outer_tag: u8) -> Result<(u8, &'a [u8]), String> {
let inner_der = self.take_tag(expected_outer_tag)?;
let (tag, value, rem) = cms_take_tlv(inner_der)?;
if !rem.is_empty() {
return Err("trailing bytes inside EXPLICIT value".into());
}
Ok((tag, value))
}
fn take_explicit_der(&mut self, expected_outer_tag: u8) -> Result<&'a [u8], String> {
let inner_der = self.take_tag(expected_outer_tag)?;
let (_tag, _value, rem) = cms_take_tlv(inner_der)?;
if !rem.is_empty() {
return Err("trailing bytes inside EXPLICIT value".into());
}
Ok(inner_der)
}
}
fn parse_signed_object_content_info(
raw_der: &[u8],
parse_der: &[u8],
) -> Result<RpkiSignedObjectParsed, SignedObjectParseError> {
let mut r = CmsReader::new(parse_der);
let mut content_info_seq = r
.take_sequence()
.map_err(|e| SignedObjectParseError::Parse(e.to_string()))?;
if !r.is_empty() {
return Err(SignedObjectParseError::TrailingBytes(r.remaining_len()));
}
let content_type = take_oid_string(&mut content_info_seq)?;
let signed_data = parse_signed_data_from_contentinfo_cursor(&mut content_info_seq)?;
if !content_info_seq.is_empty() {
return Err(SignedObjectParseError::Parse(
"ContentInfo must be a SEQUENCE of 2 elements".into(),
));
}
Ok(RpkiSignedObjectParsed {
raw_der: raw_der.to_vec(),
content_info_content_type: content_type,
signed_data,
})
}
fn header_to_single_byte_tag(header: &Header<'_>) -> Result<u8, String> {
let tag_no = header.tag().0;
if tag_no > 30 {
return Err(format!("high-tag-number form not supported: {tag_no}"));
}
Ok(((header.class() as u8) << 6) | if header.constructed() { 0x20 } else { 0x00 } | tag_no as u8)
}
fn cms_take_tlv(input: &[u8]) -> Result<(u8, &[u8], &[u8]), String> {
let (rem, any) = Any::from_ber(input).map_err(|e| format!("BER parse error: {e}"))?;
let tag = header_to_single_byte_tag(&any.header)?;
Ok((tag, any.data, rem))
}
fn flatten_octet_string(any: Any<'_>) -> Result<Vec<u8>, String> {
if any.class() != Class::Universal || any.tag() != Tag::OctetString {
return Err("expected OCTET STRING".into());
}
if !any.header.constructed() {
return Ok(any.data.to_vec());
}
let mut out = Vec::new();
let mut input = any.data;
while !input.is_empty() {
let (rem, child) = Any::from_ber(input).map_err(|e| format!("BER parse error: {e}"))?;
out.extend(flatten_octet_string(child)?);
input = rem;
}
Ok(out)
}
impl RpkiSignedObjectParsed {
pub fn validate_profile(self) -> Result<RpkiSignedObject, SignedObjectValidateError> {
if self.content_info_content_type != OID_SIGNED_DATA {
@@ -412,12 +545,12 @@ impl RpkiSignedObjectParsed {
}
fn parse_signed_data_from_contentinfo_cursor(
seq: &mut DerReader<'_>,
seq: &mut CmsReader<'_>,
) -> Result<SignedDataParsed, SignedObjectParseError> {
let inner_der = seq.take_explicit_der(0xA0).map_err(|_e| {
SignedObjectParseError::Parse("ContentInfo.content must be [0] EXPLICIT".into())
})?;
let mut r = DerReader::new(inner_der);
let mut r = CmsReader::new(inner_der);
let signed_data_seq = r
.take_sequence()
.map_err(|e| SignedObjectParseError::Parse(e.to_string()))?;
@@ -430,7 +563,7 @@ fn parse_signed_data_from_contentinfo_cursor(
}
fn parse_signed_data_cursor(
mut seq: DerReader<'_>,
mut seq: CmsReader<'_>,
) -> Result<SignedDataParsed, SignedObjectParseError> {
let version = seq
.take_uint_u64()
@@ -439,7 +572,7 @@ fn parse_signed_data_cursor(
let digest_set_bytes = seq
.take_tag(0x31)
.map_err(|e| SignedObjectParseError::Parse(e.to_string()))?;
let mut digest_set = DerReader::new(digest_set_bytes);
let mut digest_set = CmsReader::new(digest_set_bytes);
let mut digest_algorithms: Vec<AlgorithmIdentifierParsed> = Vec::new();
while !digest_set.is_empty() {
let alg = digest_set
@@ -512,7 +645,7 @@ fn parse_signed_data_cursor(
}
fn parse_encapsulated_content_info_cursor(
mut seq: DerReader<'_>,
mut seq: CmsReader<'_>,
) -> Result<EncapsulatedContentInfoParsed, SignedObjectParseError> {
if seq.is_empty() {
return Err(SignedObjectParseError::Parse(
@@ -530,7 +663,7 @@ fn parse_encapsulated_content_info_cursor(
"EncapsulatedContentInfo.eContent must be [0] EXPLICIT".into(),
)
})?;
let mut inner = DerReader::new(inner_der);
let mut inner = CmsReader::new(inner_der);
let octets = inner
.take_octet_string()
.map_err(|e| SignedObjectParseError::Parse(e.to_string()))?;
@@ -539,7 +672,7 @@ fn parse_encapsulated_content_info_cursor(
"trailing bytes inside EncapsulatedContentInfo.eContent".into(),
));
}
Some(octets.to_vec())
Some(octets)
};
if !seq.is_empty() {
return Err(SignedObjectParseError::Parse(
@@ -557,7 +690,7 @@ fn split_der_objects(mut input: &[u8]) -> Result<Vec<Vec<u8>>, SignedObjectParse
let mut out: Vec<Vec<u8>> = Vec::new();
while !input.is_empty() {
let (_tag, _value, rem) =
der_take_tlv(input).map_err(|e| SignedObjectParseError::Parse(e.to_string()))?;
cms_take_tlv(input).map_err(|e| SignedObjectParseError::Parse(e.to_string()))?;
let consumed = input.len() - rem.len();
out.push(input[..consumed].to_vec());
input = rem;
@@ -568,7 +701,7 @@ fn split_der_objects(mut input: &[u8]) -> Result<Vec<Vec<u8>>, SignedObjectParse
fn parse_signer_infos_set_cursor(
set_bytes: &[u8],
) -> Result<Vec<SignerInfoParsed>, SignedObjectParseError> {
let mut set = DerReader::new(set_bytes);
let mut set = CmsReader::new(set_bytes);
let mut out: Vec<SignerInfoParsed> = Vec::new();
while !set.is_empty() {
let si = set
@@ -631,7 +764,7 @@ fn validate_ee_certificate(der: &[u8]) -> Result<ResourceEeCertificate, SignedOb
}
fn parse_signer_info_cursor(
mut seq: DerReader<'_>,
mut seq: CmsReader<'_>,
) -> Result<SignerInfoParsed, SignedObjectParseError> {
let version = seq
.take_uint_u64()
@@ -980,7 +1113,7 @@ fn make_signed_attrs_der_for_signature(full_tlv: &[u8]) -> Result<Vec<u8>, Signe
Ok(cs_der)
}
fn take_oid_string(seq: &mut DerReader<'_>) -> Result<String, SignedObjectParseError> {
fn take_oid_string(seq: &mut CmsReader<'_>) -> Result<String, SignedObjectParseError> {
let oid = seq
.take_tag(0x06)
.map_err(|e| SignedObjectParseError::Parse(e.to_string()))?;
@@ -1055,7 +1188,7 @@ fn decode_oid_to_dotted_string(value: &[u8]) -> String {
}
fn parse_algorithm_identifier_cursor(
mut seq: DerReader<'_>,
mut seq: CmsReader<'_>,
) -> Result<(String, bool), SignedObjectParseError> {
if seq.is_empty() {
return Err(SignedObjectParseError::Parse(