20260316迭代 增加delta replay以及multi-rir
replay 对比,五个RIR 输出vrp与routinator一致
This commit is contained in:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user