Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/cri_attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,21 @@ impl<'a> X509CriAttribute<'a> {
}

/// Iterate over the unparsed values of 'SET OF AttributeValue'
///
/// Each item yields the raw DER content of an individual value within the SET,
/// returned as an [`Any`] object containing the DER-encoded tag, length, and value.
///
/// If the SET header cannot be parsed, an empty iterator is returned.
pub fn iter_raw_values(
&self,
) -> impl Iterator<Item = Result<(Input<'a>, Any<'a>), BerError<Input<'a>>>> {
AnyIterator::<DerMode>::new(self.value.clone())
// `self.value` contains the full SET TLV; parse past the SET header
// to iterate over the individual values inside the SET
let content = match <Set>::parse_der_as_input(self.value.clone()) {
Ok((_, (_, content))) => content,
Err(_) => Input::default(),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems reasonable for this context, but might be worth mentioning in the rustdoc.

};
AnyIterator::<DerMode>::new(content)
}
}

Expand Down
52 changes: 52 additions & 0 deletions tests/readcsr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,58 @@ fn read_csr_with_challenge_password() {
assert!(found_san);
}

#[test]
fn test_iter_raw_values() {
let der = pem::parse_x509_pem(CSR_CHALLENGE_PASSWORD).unwrap().1;
let (_, csr) = X509CertificationRequest::from_der(&der.contents)
.expect("Could not parse CSR with challenge password");

// Test iter_raw_values on the challenge password attribute (single value)
let challenge_attr = csr
.certification_request_info
.find_attribute(&OID_PKCS9_CHALLENGE_PASSWORD)
.expect("Challenge password attribute not found");

let raw_values: Vec<_> = challenge_attr
.iter_raw_values()
.collect::<Result<Vec<_>, _>>()
.expect("iter_raw_values should yield parseable items");
// The challenge password SET has exactly one value
assert_eq!(raw_values.len(), 1);

// The parsed Any should be a UTF8String containing "A challenge password"
let (_, any_val) = &raw_values[0];
// Verify raw DER: tag should be UTF8String (0x0C), not SET (0x31)
assert_eq!(
any_val.header.tag(),
x509_parser::asn1_rs::Tag::Utf8String,
"iter_raw_values should yield individual values inside the SET, not the SET itself"
);
let s = std::str::from_utf8(any_val.data.as_bytes2())
.expect("challenge password value should be valid UTF-8");
assert_eq!(s, "A challenge password");

// Test iter_raw_values on the extension request attribute (single value: a SEQUENCE)
let ext_attr = csr
.certification_request_info
.find_attribute(&OID_PKCS9_EXTENSION_REQUEST)
.expect("Extension request attribute not found");

let raw_values: Vec<_> = ext_attr
.iter_raw_values()
.collect::<Result<Vec<_>, _>>()
.expect("iter_raw_values should yield parseable items");
// The extension request SET has exactly one value (the SEQUENCE of extensions)
assert_eq!(raw_values.len(), 1);
let (_, any_val) = &raw_values[0];
// Verify raw DER: tag should be SEQUENCE (0x30), not SET (0x31)
assert_eq!(
any_val.header.tag(),
x509_parser::asn1_rs::Tag::Sequence,
"iter_raw_values should yield SET contents, not the SET envelope"
);
}

#[cfg(any(feature = "verify", feature = "verify-aws"))]
#[test]
fn read_csr_verify() {
Expand Down
Loading