diff --git a/src/ca/idcert.rs b/src/ca/idcert.rs index 297da88..8ad924d 100644 --- a/src/ca/idcert.rs +++ b/src/ca/idcert.rs @@ -479,7 +479,7 @@ impl TbsIdCert { let value = OctetString::take_from(cons)?; Mode::Der.decode(value.into_source(), |content| { if id == oid::CE_BASIC_CONSTRAINTS { - TbsCert::take_basic_constraints( + Self::take_basic_constraints( content, &mut basic_ca ) } else if id == oid::CE_SUBJECT_KEY_IDENTIFIER { @@ -487,7 +487,7 @@ impl TbsIdCert { content, &mut subject_key_id ) } else if id == oid::CE_AUTHORITY_KEY_IDENTIFIER { - TbsIdCert::take_authority_key_identifier( + Self::take_authority_key_identifier( content, &mut authority_key_id, ) @@ -527,6 +527,32 @@ impl TbsIdCert { }) } + /// Parses the Basic Constraints extension. + /// + /// ```text + /// BasicConstraints ::= SEQUENCE { + /// cA BOOLEAN DEFAULT FALSE, + /// pathLenConstraint INTEGER (0..MAX) OPTIONAL + /// } + /// ``` + /// Contrary to RFC 6487 the pathLenConstraint is not forbidden + /// in identity certificates. + fn take_basic_constraints( + cons: &mut decode::Constructed, + basic_ca: &mut Option, + ) -> Result<(), DecodeError> { + if basic_ca.is_some() { + Err(cons.content_err("duplicate Basic Constraints extension")) + } + else { + cons.take_sequence(|cons| { + *basic_ca = Some(cons.take_opt_bool()?.unwrap_or(false)); + let _path_len_constraint = cons.take_opt_u64()?; + Ok(()) + }) + } + } + /// Parses the Authority Key Identifier extension. /// /// ```text @@ -683,6 +709,14 @@ pub mod tests { let idcert_moment = Time::utc(2012, 1, 1, 0, 0, 0); idcert.validate_ta_at(idcert_moment).unwrap(); } + + #[test] + fn parse_afrinic_ta_id_cert() { + let data = include_bytes!("../../test-data/ca/id_afrinic.cer"); + let idcert = IdCert::decode(Bytes::from_static(data)).unwrap(); + let idcert_moment = Time::utc(2022, 10, 25, 15, 0, 0); + idcert.validate_ta_at(idcert_moment).unwrap(); + } } #[cfg(all(test, feature = "softkeys"))] diff --git a/src/ca/idexchange.rs b/src/ca/idexchange.rs index ec56156..6cd762c 100644 --- a/src/ca/idexchange.rs +++ b/src/ca/idexchange.rs @@ -702,12 +702,13 @@ impl ParentResponse { writer.done() } - /// Validates and return the IdCert if it is correct and valid. + /// Validates the IdCert and returns it if it is valid. pub fn validate(&self) -> Result { self.validate_at(Time::now()) } - fn validate_at(&self, when: Time) -> Result { + /// Validates the IdCert at the given date, and returns it if it is valid. + pub fn validate_at(&self, when: Time) -> Result { validate_idcert_at(&self.id_cert, when) } @@ -1314,6 +1315,20 @@ mod tests { assert_eq!(req, re_decoded); } + #[test] + fn afrinic_parent_response_codec() { + let xml = include_str!("../../test-data/ca/rfc8183/afrinic-parent-response.xml"); + let req = ParentResponse::parse(xml.as_bytes()).unwrap(); + + let re_encoded_xml = req.to_xml_string(); + let re_decoded = + ParentResponse::parse(re_encoded_xml.as_bytes()).unwrap(); + + assert_eq!(req, re_decoded); + + let _ta_cert = req.validate().unwrap(); + } + #[test] fn parent_response_krill_0_9() { let xml = include_str!("../../test-data/ca/rfc8183/krill-0-9-parent-response.xml"); diff --git a/src/repository/cert.rs b/src/repository/cert.rs index c88d5b4..08de625 100644 --- a/src/repository/cert.rs +++ b/src/repository/cert.rs @@ -1737,15 +1737,19 @@ impl TbsCert { basic_ca: &mut Option, ) -> Result<(), DecodeError> { if basic_ca.is_some() { - Err(cons.content_err("duplicate Basic Contraints extension")) + Err(cons.content_err("duplicate Basic Constraints extension")) } else { - *basic_ca = Some( - cons.take_sequence(|cons| { - cons.take_opt_bool() - })?.unwrap_or(false) - ); - Ok(()) + cons.take_sequence(|cons| { + *basic_ca = Some(cons.take_opt_bool()?.unwrap_or(false)); + if cons.take_opt_u64()?.is_some() { + Err(cons.content_err( + "pathLenConstraint in Basic Constraints extension" + )) + } else { + Ok(()) + } + }) } } diff --git a/test-data/ca/id_afrinic.cer b/test-data/ca/id_afrinic.cer new file mode 100644 index 0000000..78c9a24 Binary files /dev/null and b/test-data/ca/id_afrinic.cer differ diff --git a/test-data/ca/rfc8183/afrinic-parent-response.xml b/test-data/ca/rfc8183/afrinic-parent-response.xml new file mode 100644 index 0000000..2d711b9 --- /dev/null +++ b/test-data/ca/rfc8183/afrinic-parent-response.xml @@ -0,0 +1,5 @@ + + +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 + +