// Signed attributes, signing-time, and algorithm parsing helpers. fn parse_signed_attrs_implicit( input: &[u8], ) -> Result { let mut content_type: Option = None; let mut message_digest: Option> = None; let mut signing_time: Option = None; fn count_elements(mut r: DerReader<'_>) -> Result { 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 { 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, 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 { 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 "".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("."); 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 { 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 { 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..] } }