mls_rs_crypto_rustcrypto/x509/
reader.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// Copyright by contributors to this project.
3// SPDX-License-Identifier: (Apache-2.0 OR MIT)
4
5use mls_rs_identity_x509::{
6    DerCertificate, SubjectAltName as MlsSubjectAltName, SubjectComponent, X509CertificateReader,
7};
8use spki::der::{oid::AssociatedOid, Decode, Encode};
9use x509_cert::{
10    ext::pkix::{name::GeneralNames, SubjectAltName},
11    Certificate,
12};
13
14use crate::{ec::pub_key_to_uncompressed, ec_for_x509::pub_key_from_spki};
15
16use super::{
17    util::{general_names_to_alt_names, parse_x509_name},
18    X509Error,
19};
20
21#[derive(Debug, Clone, Default)]
22#[non_exhaustive]
23pub struct X509Reader {}
24
25impl X509Reader {
26    pub fn new() -> X509Reader {
27        Self {}
28    }
29}
30
31impl X509CertificateReader for X509Reader {
32    type Error = X509Error;
33
34    fn subject_bytes(&self, certificate: &DerCertificate) -> Result<Vec<u8>, Self::Error> {
35        Certificate::from_der(certificate)?
36            .tbs_certificate
37            .subject
38            .to_der()
39            .map_err(Into::into)
40    }
41
42    fn subject_components(
43        &self,
44        certificate: &DerCertificate,
45    ) -> Result<Vec<SubjectComponent>, Self::Error> {
46        parse_x509_name(&Certificate::from_der(certificate)?.tbs_certificate.subject)
47    }
48
49    fn subject_alt_names(
50        &self,
51        certificate: &DerCertificate,
52    ) -> Result<Vec<MlsSubjectAltName>, Self::Error> {
53        Ok(Certificate::from_der(certificate)?
54            .tbs_certificate
55            .extensions
56            .unwrap_or_default()
57            .iter()
58            .filter(|ext| ext.extn_id == SubjectAltName::OID)
59            .map(|ext| {
60                general_names_to_alt_names(&GeneralNames::from_der(ext.extn_value.as_bytes())?)
61            })
62            .collect::<Result<Vec<_>, _>>()?
63            .into_iter()
64            .flatten()
65            .collect::<Vec<_>>())
66    }
67
68    fn public_key(
69        &self,
70        certificate: &DerCertificate,
71    ) -> Result<mls_rs_core::crypto::SignaturePublicKey, Self::Error> {
72        let spki = Certificate::from_der(certificate)?
73            .tbs_certificate
74            .subject_public_key_info;
75
76        let pub_key = pub_key_from_spki(&spki)?;
77
78        pub_key_to_uncompressed(&pub_key)
79            .map_err(Into::into)
80            .map(Into::into)
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use mls_rs_identity_x509::{SubjectAltName, SubjectComponent, X509CertificateReader};
87    use spki::der::{
88        asn1::{SetOfVec, Utf8StringRef},
89        oid::db::rfc4519,
90        Any, Encode,
91    };
92    use x509_cert::{attr::AttributeTypeAndValue, name::RelativeDistinguishedName};
93
94    use crate::x509::{
95        util::test_utils::{load_github_leaf, load_ip_cert, load_test_ca},
96        X509Reader,
97    };
98
99    #[test]
100    fn subject_parser_bytes() {
101        let test_cert = load_test_ca();
102
103        let expected_type_and_value = AttributeTypeAndValue {
104            oid: rfc4519::CN,
105            value: Any::from(Utf8StringRef::new("CA").unwrap()),
106        };
107
108        let expected_rdn = RelativeDistinguishedName::from(
109            SetOfVec::try_from(vec![expected_type_and_value]).unwrap(),
110        );
111
112        let expected_name = vec![expected_rdn].to_der().unwrap();
113
114        assert_eq!(
115            X509Reader::new().subject_bytes(&test_cert).unwrap(),
116            expected_name
117        );
118    }
119
120    #[test]
121    fn subject_parser_components() {
122        let test_cert = load_github_leaf();
123
124        let expected = vec![
125            SubjectComponent::CountryName(String::from("US")),
126            SubjectComponent::State(String::from("California")),
127            SubjectComponent::Locality(String::from("San Francisco")),
128            SubjectComponent::OrganizationName(String::from("GitHub, Inc.")),
129            SubjectComponent::CommonName(String::from("github.com")),
130        ];
131
132        assert_eq!(
133            X509Reader::new().subject_components(&test_cert).unwrap(),
134            expected
135        )
136    }
137
138    #[test]
139    fn subject_alt_names() {
140        let test_cert = load_github_leaf();
141
142        let expected = vec![
143            SubjectAltName::Dns(String::from("github.com")),
144            SubjectAltName::Dns(String::from("www.github.com")),
145        ];
146
147        assert_eq!(
148            X509Reader::new().subject_alt_names(&test_cert).unwrap(),
149            expected
150        )
151    }
152
153    #[test]
154    fn subject_alt_names_ip() {
155        let test_cert = load_ip_cert();
156
157        let expected = vec![
158            SubjectAltName::Ip(String::from("97.97.97.254")),
159            SubjectAltName::Ip(String::from("97.97.97.253")),
160        ];
161
162        assert_eq!(
163            X509Reader::new().subject_alt_names(&test_cert).unwrap(),
164            expected
165        )
166    }
167}