diff --git a/src/cri_attributes.rs b/src/cri_attributes.rs index e87a02b..56c762c 100644 --- a/src/cri_attributes.rs +++ b/src/cri_attributes.rs @@ -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, Any<'a>), BerError>>> { - AnyIterator::::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 ::parse_der_as_input(self.value.clone()) { + Ok((_, (_, content))) => content, + Err(_) => Input::default(), + }; + AnyIterator::::new(content) } } diff --git a/tests/readcsr.rs b/tests/readcsr.rs index 13705b8..b8013e0 100644 --- a/tests/readcsr.rs +++ b/tests/readcsr.rs @@ -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::, _>>() + .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::, _>>() + .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() {