Skip to content

Commit 8d99360

Browse files
authored
x509-cert: make (Tbs)CertificateInner fields private (#1505)
This allows them to maintain invariants. Adds read-only accessor methods in place of public fields. Closes #1486.
1 parent 474971e commit 8d99360

File tree

10 files changed

+223
-109
lines changed

10 files changed

+223
-109
lines changed

cmpv2/tests/cert_req.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,9 @@ fn cr_rsp_message_test() {
127127
let server_cert = Certificate::from_der(enc_server_cert).unwrap();
128128
let header = &message.header;
129129
match &header.sender {
130-
GeneralName::DirectoryName(name) => assert_eq!(server_cert.tbs_certificate.subject, *name),
130+
GeneralName::DirectoryName(name) => {
131+
assert_eq!(server_cert.tbs_certificate().subject(), name)
132+
}
131133
_ => panic!(),
132134
}
133135
match &header.recipient {

cmpv2/tests/init_req.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,9 @@ fn ir_rsp_message_test() {
234234
let server_cert = Certificate::from_der(enc_server_cert).unwrap();
235235
let header = &message.header;
236236
match &header.sender {
237-
GeneralName::DirectoryName(name) => assert_eq!(server_cert.tbs_certificate.subject, *name),
237+
GeneralName::DirectoryName(name) => {
238+
assert_eq!(server_cert.tbs_certificate().subject(), name)
239+
}
238240
_ => panic!(),
239241
}
240242
match &header.recipient {

cmpv2/tests/p10cr_req.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ fn p10cr_req_message_test() {
5858
match &message.body {
5959
PkiBody::P10cr(p10crs) => {
6060
assert_eq!(
61-
ee_cert.tbs_certificate.subject.to_string(),
61+
ee_cert.tbs_certificate().subject().to_string(),
6262
p10crs.info.subject.to_string()
6363
);
6464
}

cms/tests/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,7 @@ fn test_degenerate_certificates_only_cms() {
522522
};
523523

524524
let original_cert = x509_cert::Certificate::from_pem(cert_buf).unwrap();
525-
assert_eq!(original_cert.signature, extracted_cert.signature)
525+
assert_eq!(original_cert.signature(), extracted_cert.signature())
526526
}
527527

528528
#[test]

cms/tests/enveloped_data.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ fn reencode_enveloped_data_ktri_test() {
4949
);
5050
match &ktri.rid {
5151
RecipientIdentifier::IssuerAndSerialNumber(iasn) => {
52-
assert_eq!(cert.tbs_certificate.issuer, iasn.issuer);
53-
assert_eq!(cert.tbs_certificate.serial_number, iasn.serial_number);
52+
assert_eq!(cert.tbs_certificate().issuer(), &iasn.issuer);
53+
assert_eq!(cert.tbs_certificate().serial_number(), &iasn.serial_number);
5454
}
5555
_ => panic!(),
5656
}
@@ -146,8 +146,8 @@ fn reencode_enveloped_data_kari_test() {
146146
for rk in &kari.recipient_enc_keys {
147147
match &rk.rid {
148148
KeyAgreeRecipientIdentifier::IssuerAndSerialNumber(iasn) => {
149-
assert_eq!(cert.tbs_certificate.issuer, iasn.issuer);
150-
assert_eq!(cert.tbs_certificate.serial_number, iasn.serial_number);
149+
assert_eq!(cert.tbs_certificate().issuer(), &iasn.issuer);
150+
assert_eq!(cert.tbs_certificate().serial_number(), &iasn.serial_number);
151151
}
152152
_ => panic!(),
153153
}
@@ -375,8 +375,11 @@ fn reencode_enveloped_data_multi_test() {
375375
);
376376
match &ktri.rid {
377377
RecipientIdentifier::IssuerAndSerialNumber(iasn) => {
378-
assert_eq!(rsa_cert.tbs_certificate.issuer, iasn.issuer);
379-
assert_eq!(rsa_cert.tbs_certificate.serial_number, iasn.serial_number);
378+
assert_eq!(rsa_cert.tbs_certificate().issuer(), &iasn.issuer);
379+
assert_eq!(
380+
rsa_cert.tbs_certificate().serial_number(),
381+
&iasn.serial_number
382+
);
380383
}
381384
_ => panic!(),
382385
}
@@ -415,8 +418,11 @@ fn reencode_enveloped_data_multi_test() {
415418
for rk in &kari.recipient_enc_keys {
416419
match &rk.rid {
417420
KeyAgreeRecipientIdentifier::IssuerAndSerialNumber(iasn) => {
418-
assert_eq!(ec_cert.tbs_certificate.issuer, iasn.issuer);
419-
assert_eq!(ec_cert.tbs_certificate.serial_number, iasn.serial_number);
421+
assert_eq!(ec_cert.tbs_certificate().issuer(), &iasn.issuer);
422+
assert_eq!(
423+
ec_cert.tbs_certificate().serial_number(),
424+
&iasn.serial_number
425+
);
420426
}
421427
_ => panic!(),
422428
}

x509-cert/src/certificate.rs

Lines changed: 108 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Certificate types
22
3-
use crate::{name::Name, serial_number::SerialNumber, time::Validity};
3+
use crate::{ext, name::Name, serial_number::SerialNumber, time::Validity};
44
use alloc::vec::Vec;
55
use const_oid::AssociatedOid;
66
use core::{cmp::Ordering, fmt::Debug};
@@ -136,38 +136,109 @@ pub type TbsCertificate = TbsCertificateInner<Rfc5280>;
136136
#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
137137
#[allow(missing_docs)]
138138
pub struct TbsCertificateInner<P: Profile = Rfc5280> {
139-
/// The certificate version
139+
/// The certificate version.
140140
///
141141
/// Note that this value defaults to Version 1 per the RFC. However,
142142
/// fields such as `issuer_unique_id`, `subject_unique_id` and `extensions`
143143
/// require later versions. Care should be taken in order to ensure
144144
/// standards compliance.
145145
#[asn1(context_specific = "0", default = "Default::default")]
146-
pub version: Version,
146+
pub(crate) version: Version,
147147

148-
pub serial_number: SerialNumber<P>,
149-
pub signature: AlgorithmIdentifierOwned,
150-
pub issuer: Name,
151-
pub validity: Validity<P>,
152-
pub subject: Name,
153-
pub subject_public_key_info: SubjectPublicKeyInfoOwned,
148+
pub(crate) serial_number: SerialNumber<P>,
149+
pub(crate) signature: AlgorithmIdentifierOwned,
150+
pub(crate) issuer: Name,
151+
pub(crate) validity: Validity<P>,
152+
pub(crate) subject: Name,
153+
pub(crate) subject_public_key_info: SubjectPublicKeyInfoOwned,
154154

155155
#[asn1(context_specific = "1", tag_mode = "IMPLICIT", optional = "true")]
156-
pub issuer_unique_id: Option<BitString>,
156+
pub(crate) issuer_unique_id: Option<BitString>,
157157

158158
#[asn1(context_specific = "2", tag_mode = "IMPLICIT", optional = "true")]
159-
pub subject_unique_id: Option<BitString>,
159+
pub(crate) subject_unique_id: Option<BitString>,
160160

161161
#[asn1(context_specific = "3", tag_mode = "EXPLICIT", optional = "true")]
162-
pub extensions: Option<crate::ext::Extensions>,
162+
pub(crate) extensions: Option<ext::Extensions>,
163163
}
164164

165165
impl<P: Profile> TbsCertificateInner<P> {
166-
/// Decodes a single extension
166+
/// [`Version`] of this certificate (v1/v2/v3).
167+
pub fn version(&self) -> Version {
168+
self.version
169+
}
170+
171+
/// Serial number of this certificate.
172+
///
173+
/// X.509 serial numbers are used to uniquely identify certificates issued by a given
174+
/// Certificate Authority (CA) identified in the `issuer` field.
175+
pub fn serial_number(&self) -> &SerialNumber<P> {
176+
&self.serial_number
177+
}
178+
179+
/// Identifies the signature algorithm that this `TBSCertificate` should be signed with.
180+
///
181+
/// In a signed certificate, matches [`CertificateInner::signature_algorithm`].
182+
pub fn signature(&self) -> &AlgorithmIdentifierOwned {
183+
&self.signature
184+
}
185+
186+
/// Certificate issuer: [`Name`] of the Certificate Authority (CA) which issued this
187+
/// certificate.
188+
pub fn issuer(&self) -> &Name {
189+
&self.issuer
190+
}
191+
192+
/// Validity period for this certificate: time range in which a certificate is considered valid,
193+
/// after which it expires.
194+
pub fn validity(&self) -> &Validity<P> {
195+
&self.validity
196+
}
197+
198+
/// Subject of this certificate: entity that the certificate is intended to represent or
199+
/// authenticate, e.g. an individual, a device, or an organization.
200+
pub fn subject(&self) -> &Name {
201+
&self.subject
202+
}
203+
204+
/// Subject Public Key Info (SPKI): public key information about this certificate including
205+
/// algorithm identifier and key data.
206+
pub fn subject_public_key_info(&self) -> &SubjectPublicKeyInfoOwned {
207+
&self.subject_public_key_info
208+
}
209+
210+
/// Issuer unique ID: unique identifier representing the issuing CA, as defined by the
211+
/// issuing CA.
212+
///
213+
/// (NOTE: added in X.509 v2)
214+
pub fn issuer_unique_id(&self) -> &Option<BitString> {
215+
&self.issuer_unique_id
216+
}
217+
218+
/// Subject unique ID: unique identifier representing the certificate subject, as defined by the
219+
/// issuing CA.
220+
///
221+
/// (NOTE: added in X.509 v2)
222+
pub fn subject_unique_id(&self) -> &Option<BitString> {
223+
&self.subject_unique_id
224+
}
225+
226+
/// Certificate extensions.
227+
///
228+
/// Additional fields in a digital certificate that provide extra information beyond the
229+
/// standard fields. These extensions enhance the functionality and flexibility of certificates,
230+
/// allowing them to convey more specific details about the certificate's usage and constraints.
231+
///
232+
/// (NOTE: added in X.509 v3)
233+
pub fn extensions(&self) -> Option<&ext::Extensions> {
234+
self.extensions.as_ref()
235+
}
236+
237+
/// Decodes a single extension.
167238
///
168239
/// Returns `Ok(None)` if the extension is not present.
169240
///
170-
/// Otherwise returns the extension, and indicates if the extension was marked critical in the
241+
/// Otherwise, returns the extension, and indicates if the extension was marked critical in the
171242
/// boolean.
172243
///
173244
/// ```
@@ -177,7 +248,7 @@ impl<P: Profile> TbsCertificateInner<P> {
177248
/// use x509_cert::{der::DecodePem, ext::pkix::BasicConstraints, Certificate};
178249
/// let certificate = Certificate::from_pem(CERT_PEM.as_bytes()).expect("parse certificate");
179250
///
180-
/// let (critical, constraints) = certificate.tbs_certificate.get_extension::<BasicConstraints>()
251+
/// let (critical, constraints) = certificate.tbs_certificate().get_extension::<BasicConstraints>()
181252
/// .expect("Failed to parse extension")
182253
/// .expect("Basic constraints expected");
183254
/// # let _ = constraints;
@@ -213,7 +284,7 @@ impl<P: Profile> TbsCertificateInner<P> {
213284
/// use x509_cert::{der::DecodePem, ext::pkix::BasicConstraints, Certificate};
214285
/// let certificate = Certificate::from_pem(CERT_PEM.as_bytes()).expect("parse certificate");
215286
///
216-
/// let mut extensions_found = certificate.tbs_certificate.filter_extensions::<BasicConstraints>();
287+
/// let mut extensions_found = certificate.tbs_certificate().filter_extensions::<BasicConstraints>();
217288
/// while let Some(Ok((critical, extension))) = extensions_found.next() {
218289
/// println!("Found (critical={critical}): {extension:?}");
219290
/// }
@@ -258,9 +329,27 @@ pub type Certificate = CertificateInner<Rfc5280>;
258329
#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
259330
#[allow(missing_docs)]
260331
pub struct CertificateInner<P: Profile = Rfc5280> {
261-
pub tbs_certificate: TbsCertificateInner<P>,
262-
pub signature_algorithm: AlgorithmIdentifierOwned,
263-
pub signature: BitString,
332+
pub(crate) tbs_certificate: TbsCertificateInner<P>,
333+
pub(crate) signature_algorithm: AlgorithmIdentifierOwned,
334+
pub(crate) signature: BitString,
335+
}
336+
337+
impl<P: Profile> CertificateInner<P> {
338+
/// Get the [`TbsCertificateInner`] (i.e. the part the signature is computed over).
339+
pub fn tbs_certificate(&self) -> &TbsCertificateInner<P> {
340+
&self.tbs_certificate
341+
}
342+
343+
/// Signature algorithm used to sign the serialization of [`CertificateInner::tbs_certificate`].
344+
pub fn signature_algorithm(&self) -> &AlgorithmIdentifierOwned {
345+
&self.signature_algorithm
346+
}
347+
348+
/// Signature over the DER serialization of [`CertificateInner::tbs_certificate`] using the
349+
/// algorithm identified in [`CertificateInner::signature_algorithm`].
350+
pub fn signature(&self) -> &BitString {
351+
&self.signature
352+
}
264353
}
265354

266355
#[cfg(feature = "pem")]

0 commit comments

Comments
 (0)