Skip to content

Commit 1c27899

Browse files
committed
wip: extension
1 parent 9e5f117 commit 1c27899

File tree

5 files changed

+101
-22
lines changed

5 files changed

+101
-22
lines changed

src/lib.rs

Lines changed: 86 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ const OID_CRL_REASONS :&[u64] = &[2, 5, 29, 21];
163163
// https://www.rfc-editor.org/rfc/rfc5280#section-5.3.2
164164
const OID_CRL_INVALIDITY_DATE :&[u64] = &[2, 5, 29, 24];
165165

166+
// id-ce-issuingDistributionPoint
167+
// https://datatracker.ietf.org/doc/html/rfc5280#section-5.2.5
168+
const OID_CRL_ISSUING_DISTRIBUTION_POINT :&[u64] = &[2, 5, 29, 28];
169+
166170
#[cfg(feature = "pem")]
167171
const ENCODE_CONFIG: pem::EncodeConfig = match cfg!(target_family = "windows") {
168172
true => pem::EncodeConfig { line_ending: pem::LineEnding::CRLF },
@@ -680,6 +684,7 @@ impl CertificateSigningRequest {
680684
/// this_update: date_time_ymd(2023, 06, 17),
681685
/// next_update: date_time_ymd(2024, 06, 17),
682686
/// crl_number: SerialNumber::from(1234),
687+
/// issuing_distribution_point: None,
683688
/// revoked_certs: vec![revoked_cert],
684689
/// alg: &PKCS_ECDSA_P256_SHA256,
685690
/// key_identifier_method: KeyIdMethod::Sha256,
@@ -1247,7 +1252,10 @@ impl CertificateParams {
12471252
write_x509_extension(writer.next(), OID_CRL_DISTRIBUTION_POINTS, false, |writer| {
12481253
writer.write_sequence(|writer| {
12491254
for distribution_point in &self.crl_distribution_points {
1250-
write_crl_distribution_point(writer.next(), distribution_point);
1255+
// DistributionPoint SEQUENCE
1256+
writer.next().write_sequence(|writer| {
1257+
write_crl_distribution_point_name(writer.next(), &distribution_point.uris);
1258+
});
12511259
}
12521260
})
12531261
});
@@ -1404,7 +1412,8 @@ impl NameConstraints {
14041412
}
14051413

14061414
/// A certificate revocation list (CRL) distribution point, to be included in a certificate's
1407-
/// [distribution points extension](https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.13).
1415+
/// [distribution points extension](https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.13),
1416+
/// or a CRL's [issuing distribution point extension](https://datatracker.ietf.org/doc/html/rfc5280#section-5.2.5).
14081417
#[derive(Debug, PartialEq, Eq, Clone)]
14091418
pub struct CrlDistributionPoint {
14101419
/// One or more URI distribution point names, indicating a place the current CRL can
@@ -1567,6 +1576,11 @@ pub struct CertificateRevocationListParams {
15671576
pub next_update :OffsetDateTime,
15681577
/// A monotonically increasing sequence number for a given CRL scope and issuer.
15691578
pub crl_number :SerialNumber,
1579+
/// An optional CRL extension identifying the CRL distribution point and scope for a
1580+
/// particular CRL as described in RFC 5280 Section 5.2.5[^1].
1581+
///
1582+
/// [^1]: <https://datatracker.ietf.org/doc/html/rfc5280#section-5.2.5>
1583+
pub issuing_distribution_point :Option<CrlIssuingDistributionPoint>,
15701584
/// A list of zero or more parameters describing revoked certificates included in the CRL.
15711585
pub revoked_certs :Vec<RevokedCertParams>,
15721586
/// Signature algorithm to use when signing the serialized CRL.
@@ -1664,6 +1678,13 @@ impl CertificateRevocationListParams {
16641678
write_x509_extension(writer.next(), OID_CRL_NUMBER, false, |writer| {
16651679
writer.write_bigint_bytes(self.crl_number.as_ref(), true);
16661680
});
1681+
1682+
// Write issuing distribution point.
1683+
if let Some(issuing_distribution_point) = &self.issuing_distribution_point {
1684+
write_x509_extension(writer.next(), OID_CRL_ISSUING_DISTRIBUTION_POINT, true, |writer| {
1685+
issuing_distribution_point.write_der(writer);
1686+
});
1687+
}
16671688
});
16681689
});
16691690

@@ -1672,6 +1693,54 @@ impl CertificateRevocationListParams {
16721693
}
16731694
}
16741695

1696+
/// A certificate revocation list (CRL) issuing distribution point, to be included in a CRL's
1697+
/// [issuing distribution point extension](https://datatracker.ietf.org/doc/html/rfc5280#section-5.2.5).
1698+
pub struct CrlIssuingDistributionPoint {
1699+
/// The CRL's distribution point, containing a sequence of URIs the CRL can be retrieved from.
1700+
pub distribution_point :CrlDistributionPoint,
1701+
/// An optional description of the CRL's scope. If omitted, the CRL may contain
1702+
/// both user certs and CA certs.
1703+
pub scope :Option<CrlScope>,
1704+
}
1705+
1706+
impl CrlIssuingDistributionPoint {
1707+
fn write_der(&self, writer :DERWriter) {
1708+
// IssuingDistributionPoint SEQUENCE
1709+
writer.write_sequence(|writer| {
1710+
// distributionPoint [0] DistributionPointName OPTIONAL
1711+
write_crl_distribution_point_name(writer.next(), &self.distribution_point.uris);
1712+
1713+
// -- at most one of onlyContainsUserCerts, onlyContainsCACerts,
1714+
// -- and onlyContainsAttributeCerts may be set to TRUE.
1715+
if let Some(scope) = self.scope {
1716+
match scope {
1717+
// onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE,
1718+
CrlScope::UserCertsOnly => {
1719+
writer.next().write_tagged_implicit(Tag::context(1), |writer| {
1720+
writer.write_bool(true);
1721+
});
1722+
}
1723+
// onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE,
1724+
CrlScope::CaCertsOnly => {
1725+
writer.next().write_tagged_implicit(Tag::context(2), |writer| {
1726+
writer.write_bool(true);
1727+
});
1728+
}
1729+
}
1730+
}
1731+
});
1732+
}
1733+
}
1734+
1735+
/// Describes the scope of a CRL for an issuing distribution point extension.
1736+
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
1737+
pub enum CrlScope {
1738+
/// The CRL contains only end-entity user certificates.
1739+
UserCertsOnly,
1740+
/// The CRL contains only CA certificates.
1741+
CaCertsOnly,
1742+
}
1743+
16751744
/// Parameters used for describing a revoked certificate included in a [`CertificateRevocationList`].
16761745
pub struct RevokedCertParams {
16771746
/// Serial number identifying the revoked certificate.
@@ -1828,24 +1897,21 @@ fn write_general_subtrees(writer :DERWriter, tag :u64, general_subtrees :&[Gener
18281897
});
18291898
}
18301899

1831-
fn write_crl_distribution_point(writer :DERWriter, dp :&CrlDistributionPoint) {
1832-
// DistributionPoint SEQUENCE
1833-
writer.write_sequence(|writer| {
1834-
// distributionPoint DistributionPointName
1835-
writer.next().write_tagged_implicit(Tag::context(0), |writer| {
1836-
writer.write_sequence(|writer| {
1837-
// fullName GeneralNames
1838-
writer.next().write_tagged_implicit(Tag::context(0), | writer| {
1839-
// GeneralNames
1840-
writer.write_sequence(|writer| {
1841-
for uri in dp.uris.iter() {
1842-
// uniformResourceIdentifier [6] IA5String,
1843-
writer.next().write_tagged_implicit(Tag::context(6), |writer| {
1844-
writer.write_ia5_string(uri)
1845-
});
1846-
}
1847-
})
1848-
});
1900+
fn write_crl_distribution_point_name<'a>(writer :DERWriter, uris :impl IntoIterator<Item = &'a String>) {
1901+
// distributionPoint DistributionPointName
1902+
writer.write_tagged_implicit(Tag::context(0), |writer| {
1903+
writer.write_sequence(|writer| {
1904+
// fullName GeneralNames
1905+
writer.next().write_tagged_implicit(Tag::context(0), | writer| {
1906+
// GeneralNames
1907+
writer.write_sequence(|writer| {
1908+
for uri in uris.into_iter() {
1909+
// uniformResourceIdentifier [6] IA5String,
1910+
writer.next().write_tagged_implicit(Tag::context(6), |writer| {
1911+
writer.write_ia5_string(uri)
1912+
});
1913+
}
1914+
})
18491915
});
18501916
});
18511917
});

tests/botan.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ fn test_botan_crl_parse() {
232232
this_update: now,
233233
next_update: now + Duration::weeks(1),
234234
crl_number: rcgen::SerialNumber::from(1234),
235+
issuing_distribution_point: None,
235236
revoked_certs: vec![RevokedCertParams{
236237
serial_number: ee.get_params().serial_number.clone().unwrap(),
237238
revocation_time: now,

tests/generic.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,17 @@ mod test_x509_parser_crl {
109109
crl.get_params().this_update.unix_timestamp());
110110
assert_eq!(x509_crl.next_update().unwrap().to_datetime().unix_timestamp(),
111111
crl.get_params().next_update.unix_timestamp());
112-
// TODO(XXX): Waiting on https://github.com/rusticata/x509-parser/pull/144
112+
// TODO(XXX): Waiting on x509-parser 0.15.1 to be released.
113113
// let crl_number = BigUint::from_bytes_be(crl.get_params().crl_number.as_ref());
114114
// assert_eq!(x509_crl.crl_number().unwrap(), &crl_number);
115115

116+
// The issuing distribution point extension should be present and marked critical.
117+
let issuing_dp_ext = x509_crl.extensions().iter()
118+
.find(|ext| ext.oid == x509_parser::oid_registry::OID_X509_EXT_ISSUER_DISTRIBUTION_POINT)
119+
.expect("failed to find issuing distribution point extension");
120+
assert!(issuing_dp_ext.critical);
121+
// TODO(XXX): x509-parser does not yet parse the CRL issuing DP extension for further examination.
122+
116123
// We should find the expected revoked certificate serial with the correct reason code.
117124
let x509_revoked_cert = x509_crl.iter_revoked_certificates().next()
118125
.expect("failed to find revoked cert in CRL");

tests/util.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use time::{Duration, OffsetDateTime};
2-
use rcgen::{BasicConstraints, Certificate, CertificateParams, CertificateRevocationList, CrlDistributionPoint};
2+
use rcgen::{BasicConstraints, Certificate, CertificateParams, CertificateRevocationList, CrlDistributionPoint, CrlIssuingDistributionPoint, CrlScope};
33
use rcgen::{CertificateRevocationListParams, DnType, IsCa, KeyIdMethod};
44
use rcgen::{KeyUsagePurpose, PKCS_ECDSA_P256_SHA256, RevocationReason, RevokedCertParams, SerialNumber};
55

@@ -91,6 +91,10 @@ pub fn test_crl() -> (CertificateRevocationList, Certificate) {
9191
this_update: now,
9292
next_update: next_week,
9393
crl_number: SerialNumber::from(1234),
94+
issuing_distribution_point: Some(CrlIssuingDistributionPoint{
95+
distribution_point: CrlDistributionPoint { uris: vec!["http://example.com/crl".to_string()] },
96+
scope: Some(CrlScope::UserCertsOnly),
97+
}),
9498
revoked_certs: vec![revoked_cert],
9599
alg: &PKCS_ECDSA_P256_SHA256,
96100
key_identifier_method: KeyIdMethod::Sha256,

tests/webpki.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@ fn test_webpki_crl_revoke() {
503503
this_update: now,
504504
next_update: now + Duration::weeks(1),
505505
crl_number: rcgen::SerialNumber::from(1234),
506+
issuing_distribution_point: None,
506507
revoked_certs: vec![RevokedCertParams{
507508
serial_number: ee.get_params().serial_number.clone().unwrap(),
508509
revocation_time: now,

0 commit comments

Comments
 (0)