diff --git a/assets/csr-challenge-password.pem b/assets/csr-challenge-password.pem new file mode 100644 index 0000000..a48e99d --- /dev/null +++ b/assets/csr-challenge-password.pem @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIFuDCCA6ACAQAwgb4xCzAJBgNVBAYTAkdCMR8wHQYDVQQIDBZUZXN0IFN0YXRl +IG9yIFByb3ZpbmNlMRYwFAYDVQQHDA1UZXN0IExvY2FsaXR5MRowGAYDVQQKDBFP +cmdhbml6YXRpb24gTmFtZTEhMB8GA1UECwwYT3JnYW5pemF0aW9uYWwgVW5pdCBO +YW1lMRQwEgYDVQQDDAtDb21tb24gTmFtZTEhMB8GCSqGSIb3DQEJARYSdGVzdEBl +bWFpbC5hZGRyZXNzMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2pTx +B0wQkImcsMaHCO1jYsFRii4JhWJQT326yzI/1FM7pWA/s/kecMOkWVn/KtGDroqE +9Jw6UQOEsmFpLJEOTzAaGbNMM2BkJBNdhO2Vmmhyjks1sJljTftA+aKu4zpxs0i6 +r83oMc6iJ08kMMyDYrKCt4oJjln7dpJXc8OoV9INoRpyU2KhTiqJdw0+4V8lB3BI +QGP6o4mN1Jx/ElJogWyS4CB3wt9I7n1RH08+f/StCs0iAkASKFxAoCLrdIKSk4CM +yUFilaZz6SvHXiLLi+NoLkc+nDaDu4Nu7Pj/e2fYtq8dLRB8A25v5wqQplF7s/ZJ +wiFHVfFJDY8wtdBJ12bgLOPQbqmpBiWVrnBZHGMQTsc5YSMHy6EPv33w16cUWneu +Ho/ryqBwTWww+fCnjOapkSajckmVpm8e4fijhNmq6N6VTpfgOZ3loCjVOp4/EW3M +L+qsYQyM5Y4trY4h0zZh9hZZmQig6vLMIl9n1r+580rLeGsdeyHwHAc6jfwsy4Zz +/jOfbWF3rB1xNgSBHL0po02k8PWFr+uBfKPmfTK8Yvlz3fsTJfzLr6u6y3XAejw/ +ZZjjPydwLn3hL/q+J9SAhA3wxnCu3puwiyjhQQHgUHLkQsYpivdF1a7Pbz+l1D89 +cTOwwkYonkReosLI5QiKbQAX2NbSfVqv8ALn09sCAwEAAaCBszAjBgkqhkiG9w0B +CQcxFgwUQSBjaGFsbGVuZ2UgcGFzc3dvcmQwgYsGCSqGSIb3DQEJDjF+MHwwDgYD +VR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNV +HQ4EFgQUkDknz2bwybBXuXEG1RmUpP84GxMwLAYDVR0RBCUwI4cEfwAAAYcQAAAA +AAAAAAAAAAAAAAAAAYIJbG9jYWxob3N0MA0GCSqGSIb3DQEBCwUAA4ICAQAll1fo +6YIKQHINhdj6aKRHKZ/CluFSTAvctga5+JQG8clbWWxjI+RDUZhnw/r5Jr+8OaBW +rm7c/aDePP9AOQ93tBfIEv4OMwP4jQVOrZJuAOuyrUoSTSehMPZoR7IZv2QcyRk+ +RUi2aqZbYLPFptsJ6+8FlX/ieSM+GEBmj2W9U5LsvI2y6B34N0uy/if9IbQO8OHj +afrjZc2jZ1QYj69OUJekEjPZzrb3kSIVELyWfZEocq+e0hLgQEdI0ZCIdzKFeH8i +yQfCtJ7g7k/hVkrQcyd3JwFvhIJRlyhw8ORxOHqFsyZ2XWvg0lGVWk9ksRDGFcf3 +g38Erl1sU3jPzEfb/B2aio2DO+42ZY65P7nuLNB5Tatdyy+yYmd3OlxL0XtUb/VW +DS9zKjNrjBLvgN9w5QUE0hc4HOHfcceEcPljALoeZWF/XmPjEUjbiKSIP7vIKDi2 +bkx1LiLnuDzjvapG/C6YmuFrTxoqjc9qZs/e2NjrxJ5tI3s/d9R9UET+jh/1swLH +wz35SohLLDLc2ShaNlNuMOwWOySFV93RwjxcP6OsEFyCZrVsQ6UXz6odXk509A5e +w3xhpMdQFlsNRpH0f7CjcxsTUx3qFEXMwq0iYt9w0wrTezOTL25oGouHH11JQsZh +g7iXkOHZIfzZVIF3sagZhdp7stnJTWnUIhJvXw== +-----END CERTIFICATE REQUEST----- diff --git a/src/cri_attributes.rs b/src/cri_attributes.rs index e2ceaae..0bfd015 100644 --- a/src/cri_attributes.rs +++ b/src/cri_attributes.rs @@ -7,6 +7,8 @@ use asn1_rs::{Error, FromDer, Header, Oid, Sequence, Tag}; use nom::Err; use oid_registry::*; use std::collections::HashMap; +use nom::combinator::{all_consuming, complete}; +use nom::multi::many0; /// Attributes for Certification Request #[derive(Clone, Debug, PartialEq)] @@ -107,15 +109,9 @@ pub(crate) mod parser { pub(crate) fn parse_cri_attributes(i: &[u8]) -> X509Result> { let (i, hdr) = Header::from_der(i).map_err(|_| Err::Error(X509Error::InvalidAttributes))?; - if i.is_empty() { - return Ok((i, Vec::new())); + if hdr.is_contextspecific() && hdr.tag().0 == 0 { + all_consuming(many0(complete(X509CriAttribute::from_der)))(i) + } else { + Err(Err::Error(X509Error::InvalidAttributes)) } - let constructed = if hdr.constructed() { 1 } else { 0 }; - (0..constructed) - .into_iter() - .try_fold((i, Vec::new()), |(i, mut attrs), _| { - let (rem, attr) = X509CriAttribute::from_der(i)?; - attrs.push(attr); - Ok((rem, attrs)) - }) } diff --git a/src/lib.rs b/src/lib.rs index 57b1bbe..a25d728 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -128,7 +128,7 @@ unreachable_pub )] #![forbid(unsafe_code)] -#![deny(broken_intra_doc_links)] +#![deny(rustdoc::broken_intra_doc_links)] #![doc(test( no_crate_inject, attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) diff --git a/tests/readcsr.rs b/tests/readcsr.rs index 77dafec..5edb87d 100644 --- a/tests/readcsr.rs +++ b/tests/readcsr.rs @@ -3,6 +3,7 @@ use x509_parser::prelude::*; const CSR_DATA_EMPTY_ATTRIB: &[u8] = include_bytes!("../assets/csr-empty-attributes.csr"); const CSR_DATA: &[u8] = include_bytes!("../assets/test.csr"); +const CSR_CHALLENGE_PASSWORD: &[u8] = include_bytes!("../assets/csr-challenge-password.pem"); #[test] fn read_csr_empty_attrib() { @@ -52,6 +53,34 @@ fn read_csr_with_san() { } } +#[test] +fn read_csr_with_challenge_password() { + let der = pem::parse_x509_pem(CSR_CHALLENGE_PASSWORD).unwrap().1; + let (rem, csr) = X509CertificationRequest::from_der(&der.contents) + .expect("Could not parse CSR with challenge password"); + + assert!(rem.is_empty()); + let cri = &csr.certification_request_info; + assert_eq!(cri.version, X509Version(0)); + // CSR contains 2 attributes: challenge password and extensions + assert_eq!(cri.attributes().len(), 2); + + // Make sure we can read requested extensions + let extensions = csr.requested_extensions().expect("Didn't find requested extensions in CSR"); + let mut found_san = false; + for extension in extensions { + match extension { + ParsedExtension::SubjectAlternativeName(san) => { + let name = san.general_names.get(2).unwrap(); + assert!(matches!(name, GeneralName::DNSName("localhost"))); + found_san = true; + } + _ => {}, + } + } + assert!(found_san); +} + #[cfg(feature = "verify")] #[test] fn read_csr_verify() {