Panda RPKI OSS Local 277cbca878
Some checks failed
ci / rust (push) Has been cancelled
ci / docker (push) Has been cancelled
初始化 Panda RPKI v0.1.0 开源候选版本
2026-09-09 18:01:15 +08:00

345 lines
12 KiB
Rust

// Signed attributes, signing-time, and algorithm parsing helpers.
fn parse_signed_attrs_implicit(
input: &[u8],
) -> Result<SignedAttrsProfiled, SignedObjectValidateError> {
let mut content_type: Option<String> = None;
let mut message_digest: Option<Vec<u8>> = None;
let mut signing_time: Option<Asn1TimeUtc> = None;
fn count_elements(mut r: DerReader<'_>) -> Result<usize, String> {
let mut n = 0usize;
while !r.is_empty() {
r.skip_any()?;
n += 1;
}
Ok(n)
}
let mut remaining = DerReader::new(input);
while !remaining.is_empty() {
let mut attr = remaining
.take_sequence()
.map_err(|e| SignedObjectValidateError::SignedAttrsParse(e.to_string()))?;
let oid_bytes = attr
.take_tag(0x06)
.map_err(|e| SignedObjectValidateError::SignedAttrsParse(e.to_string()))?;
let oid = oid_value_bytes_to_string(oid_bytes);
let values_bytes = attr
.take_tag(0x31)
.map_err(|e| SignedObjectValidateError::SignedAttrsParse(e.to_string()))?;
if !attr.is_empty() {
return Err(SignedObjectValidateError::SignedAttrsParse(
"Attribute must be SEQUENCE of 2".into(),
));
}
let mut values = DerReader::new(values_bytes);
let count = if values.is_empty() {
0
} else {
values
.skip_any()
.map_err(|e| SignedObjectValidateError::SignedAttrsParse(e.to_string()))?;
if values.is_empty() {
1
} else {
1 + count_elements(values)
.map_err(|e| SignedObjectValidateError::SignedAttrsParse(e.to_string()))?
}
};
if count != 1 {
return Err(
SignedObjectValidateError::InvalidSignedAttributeValuesCount { oid, count },
);
}
// Re-parse the sole value.
let mut values = DerReader::new(values_bytes);
let (val_tag, val_bytes) = values
.take_any()
.map_err(|e| SignedObjectValidateError::SignedAttrsParse(e.to_string()))?;
match oid.as_str() {
OID_CMS_ATTR_CONTENT_TYPE => {
if content_type.is_some() {
return Err(SignedObjectValidateError::DuplicateSignedAttribute(oid));
}
if val_tag != 0x06 {
return Err(SignedObjectValidateError::SignedAttrsParse(
"content-type attr value must be OBJECT IDENTIFIER".into(),
));
}
content_type = Some(oid_value_bytes_to_string(val_bytes));
}
OID_CMS_ATTR_MESSAGE_DIGEST => {
if message_digest.is_some() {
return Err(SignedObjectValidateError::DuplicateSignedAttribute(oid));
}
if val_tag != 0x04 {
return Err(SignedObjectValidateError::SignedAttrsParse(
"message-digest attr value must be OCTET STRING".into(),
));
}
message_digest = Some(val_bytes.to_vec());
}
OID_CMS_ATTR_SIGNING_TIME => {
if signing_time.is_some() {
return Err(SignedObjectValidateError::DuplicateSignedAttribute(oid));
}
signing_time = Some(parse_signing_time_value_tlv(val_tag, val_bytes)?);
}
_ => {
return Err(SignedObjectValidateError::UnsupportedSignedAttribute(oid));
}
}
}
Ok(SignedAttrsProfiled {
content_type: content_type
.ok_or(SignedObjectValidateError::SignedAttrsContentTypeMissing)?,
message_digest: message_digest
.ok_or(SignedObjectValidateError::SignedAttrsMessageDigestMissing)?,
signing_time: signing_time
.ok_or(SignedObjectValidateError::SignedAttrsSigningTimeMissing)?,
other_attrs_present: false,
})
}
fn parse_signing_time_value_tlv(
tag: u8,
value: &[u8],
) -> Result<Asn1TimeUtc, SignedObjectValidateError> {
match tag {
0x17 => Ok(Asn1TimeUtc {
utc: parse_utctime(value)?,
encoding: Asn1TimeEncoding::UtcTime,
}),
0x18 => Ok(Asn1TimeUtc {
utc: parse_generalized_time(value)?,
encoding: Asn1TimeEncoding::GeneralizedTime,
}),
_ => Err(SignedObjectValidateError::InvalidSigningTimeValue),
}
}
fn make_signed_attrs_der_for_signature(full_tlv: &[u8]) -> Result<Vec<u8>, SignedObjectParseError> {
// We need the DER encoding of SignedAttributes (SET OF Attribute) as signature input.
// The SignedAttributes field in SignerInfo is `[0] IMPLICIT`, so the on-wire bytes start with
// a context-specific constructed tag (0xA0 for tag 0). For signature verification, this tag
// is replaced with the universal SET tag (0x31), leaving length+content unchanged.
//
let mut cs_der = full_tlv.to_vec();
if cs_der.is_empty() {
return Err(SignedObjectParseError::Parse(
"signedAttrs encoding is empty".into(),
));
}
// The first byte should be the context-specific tag (0xA0) for [0] constructed.
// Replace it with universal SET (0x31) for signature input.
cs_der[0] = 0x31;
Ok(cs_der)
}
fn take_oid_string(seq: &mut CmsReader<'_>) -> Result<String, SignedObjectParseError> {
let oid = seq
.take_tag(0x06)
.map_err(|e| SignedObjectParseError::Parse(e.to_string()))?;
Ok(oid_value_bytes_to_string(oid))
}
fn oid_value_bytes_to_string(oid_value: &[u8]) -> String {
if oid_value == OID_SHA256_RAW {
return OID_SHA256.to_string();
}
if oid_value == OID_SIGNED_DATA_RAW {
return OID_SIGNED_DATA.to_string();
}
if oid_value == OID_CMS_ATTR_CONTENT_TYPE_RAW {
return OID_CMS_ATTR_CONTENT_TYPE.to_string();
}
if oid_value == OID_CMS_ATTR_MESSAGE_DIGEST_RAW {
return OID_CMS_ATTR_MESSAGE_DIGEST.to_string();
}
if oid_value == OID_CMS_ATTR_SIGNING_TIME_RAW {
return OID_CMS_ATTR_SIGNING_TIME.to_string();
}
if oid_value == OID_RSA_ENCRYPTION_RAW {
return OID_RSA_ENCRYPTION.to_string();
}
if oid_value == OID_SHA256_WITH_RSA_ENCRYPTION_RAW {
return OID_SHA256_WITH_RSA_ENCRYPTION.to_string();
}
if oid_value == OID_CT_RPKI_MANIFEST_RAW {
return OID_CT_RPKI_MANIFEST.to_string();
}
if oid_value == OID_CT_ROUTE_ORIGIN_AUTHZ_RAW {
return OID_CT_ROUTE_ORIGIN_AUTHZ.to_string();
}
if oid_value == OID_CT_ASPA_RAW {
return OID_CT_ASPA.to_string();
}
decode_oid_to_dotted_string(oid_value)
}
fn decode_oid_to_dotted_string(value: &[u8]) -> String {
if value.is_empty() {
return "<empty-oid>".into();
}
let first = value[0];
let a = (first / 40) as u32;
let b = (first % 40) as u32;
let mut out = String::new();
out.push_str(&a.to_string());
out.push('.');
out.push_str(&b.to_string());
let mut idx = 1usize;
while idx < value.len() {
let mut v: u32 = 0;
loop {
if idx >= value.len() {
out.push_str(".<truncated>");
return out;
}
let byte = value[idx];
idx += 1;
v = (v << 7) | (byte as u32 & 0x7F);
if (byte & 0x80) == 0 {
break;
}
}
out.push('.');
out.push_str(&v.to_string());
}
out
}
fn parse_algorithm_identifier_cursor(
mut seq: CmsReader<'_>,
) -> Result<(String, bool), SignedObjectParseError> {
if seq.is_empty() {
return Err(SignedObjectParseError::Parse(
"AlgorithmIdentifier must be SEQUENCE of 1..2".into(),
));
}
let oid = take_oid_string(&mut seq)?;
let params_ok = if seq.is_empty() {
true
} else {
let (tag, value) = seq
.take_any()
.map_err(|e| SignedObjectParseError::Parse(e.to_string()))?;
tag == 0x05 && value.is_empty()
};
if !seq.is_empty() {
return Err(SignedObjectParseError::Parse(
"AlgorithmIdentifier must be SEQUENCE of 1..2".into(),
));
}
Ok((oid, params_ok))
}
fn parse_utctime(value: &[u8]) -> Result<time::OffsetDateTime, SignedObjectValidateError> {
let s = std::str::from_utf8(value)
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?;
if !s.ends_with('Z') {
return Err(SignedObjectValidateError::InvalidSigningTimeValue);
}
let digits = &s[..s.len() - 1];
if digits.len() != 10 && digits.len() != 12 {
return Err(SignedObjectValidateError::InvalidSigningTimeValue);
}
if !digits.as_bytes().iter().all(|b| b.is_ascii_digit()) {
return Err(SignedObjectValidateError::InvalidSigningTimeValue);
}
let yy: i32 = digits[0..2]
.parse()
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?;
let year = if yy <= 49 { 2000 + yy } else { 1900 + yy };
let mon: u8 = digits[2..4]
.parse()
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?;
let day: u8 = digits[4..6]
.parse()
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?;
let hour: u8 = digits[6..8]
.parse()
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?;
let min: u8 = digits[8..10]
.parse()
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?;
let sec: u8 = if digits.len() == 12 {
digits[10..12]
.parse()
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?
} else {
0
};
let month = time::Month::try_from(mon)
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?;
let date = time::Date::from_calendar_date(year, month, day)
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?;
let time = time::Time::from_hms(hour, min, sec)
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?;
Ok(time::OffsetDateTime::new_utc(date, time))
}
fn parse_generalized_time(value: &[u8]) -> Result<time::OffsetDateTime, SignedObjectValidateError> {
let s = std::str::from_utf8(value)
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?;
if !s.ends_with('Z') {
return Err(SignedObjectValidateError::InvalidSigningTimeValue);
}
let digits = &s[..s.len() - 1];
if digits.len() != 12 && digits.len() != 14 {
return Err(SignedObjectValidateError::InvalidSigningTimeValue);
}
if !digits.as_bytes().iter().all(|b| b.is_ascii_digit()) {
return Err(SignedObjectValidateError::InvalidSigningTimeValue);
}
let year: i32 = digits[0..4]
.parse()
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?;
let mon: u8 = digits[4..6]
.parse()
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?;
let day: u8 = digits[6..8]
.parse()
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?;
let hour: u8 = digits[8..10]
.parse()
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?;
let min: u8 = digits[10..12]
.parse()
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?;
let sec: u8 = if digits.len() == 14 {
digits[12..14]
.parse()
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?
} else {
0
};
let month = time::Month::try_from(mon)
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?;
let date = time::Date::from_calendar_date(year, month, day)
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?;
let time = time::Time::from_hms(hour, min, sec)
.map_err(|_| SignedObjectValidateError::InvalidSigningTimeValue)?;
Ok(time::OffsetDateTime::new_utc(date, time))
}
fn strip_leading_zeros(bytes: &[u8]) -> &[u8] {
let mut idx = 0;
while idx < bytes.len() && bytes[idx] == 0 {
idx += 1;
}
if idx == bytes.len() {
&bytes[bytes.len() - 1..]
} else {
&bytes[idx..]
}
}