Skip to main content
This is unreleased documentation for the main (development) branch of crypto-glue.

x509_cert/builder/profile/
devid.rs

1//! Profile for 802.1AR // Secure Device Identity certificates
2//!
3//! Specification can be found here:
4//! <https://ieeexplore.ieee.org/document/8423794>
5
6// NOTE(baloo): due to copyright issues, I am not going to
7// copy paste parts of spec relevant to the implementation.
8// Unlike other organizations, IEEE does not appear to grant a license for
9// reproduction in implementations.
10// There is a fair use exclusion to copyright, but I am not willing to
11// go to court to test waters.
12//
13// You, as a reader/reviewer, are expected to download a copy of the spec
14// yourself.
15
16use alloc::vec;
17
18use crate::{
19    builder::{BuilderProfile, Result},
20    certificate::TbsCertificate,
21    ext::{
22        Extension, ToExtension,
23        pkix::{
24            AuthorityKeyIdentifier, KeyUsage, KeyUsages, SubjectAltName,
25            name::{GeneralName, GeneralNames, HardwareModuleName, OtherName},
26        },
27    },
28    name::Name,
29};
30use der::{ErrorKind, asn1::OctetString};
31use spki::{ObjectIdentifier, SubjectPublicKeyInfoRef};
32
33// TODO(tarcieri): use this when `const-oid` has been bumped to v0.10.0-rc.0
34//use const_oid::db::tcgtpm;
35#[allow(missing_docs)]
36pub mod tcgtpm {
37    use const_oid::ObjectIdentifier;
38    pub const TCG_SV_TPM_12: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.23.133.1.0");
39    pub const TCG_SV_TPM_20: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.23.133.1.2");
40}
41
42/// DevID Certificate
43///
44/// See: section 8 DevID certificate fields and extensions
45pub struct DevId {
46    /// issuer   Name,
47    /// represents the name signing the certificate
48    pub issuer: Name,
49
50    subject: Name,
51
52    subject_alt_name: Option<GeneralNames>,
53}
54
55impl DevId {
56    /// Create a new DevID
57    ///
58    /// Spec: 802.1AR Section 8.10.4 subjectAltName
59    /// Also documented in
60    /// <https://trustedcomputinggroup.org/wp-content/uploads/TPM-2p0-Keys-for-Device-Identity-and-Attestation_v1_r12_pub10082021.pdf#page=58>
61    pub fn new(issuer: Name, subject: Name, alt_names: Option<GeneralNames>) -> Result<Self> {
62        // If alt_name is present it is required to include `HardwareModuleName`
63        // HardwareModuleName is der-encoded in an OtherName field of GeneralNames.
64        if let Some(ref alt_names) = alt_names {
65            // TODO: do we need to validate the SAN more than that? check for duplicates?
66            let mut found = false;
67            for gn in alt_names {
68                match gn {
69                    GeneralName::OtherName(on)
70                        if HardwareModuleName::from_other_name(on)?.is_some() =>
71                    {
72                        found = true;
73                        break;
74                    }
75                    _ => {}
76                }
77            }
78
79            if !found {
80                return Err(der::Error::from(ErrorKind::Failed).into());
81            }
82        }
83
84        Ok(Self {
85            issuer,
86            subject,
87            subject_alt_name: alt_names,
88        })
89    }
90
91    /// Create a new IDevID for a TPM-based key.
92    pub fn idevid_tpm(
93        issuer: Name,
94        subject: Name,
95        hw_type: TpmVersion,
96        serial_number: OctetString,
97    ) -> Result<Self> {
98        let hardware_module_name = HardwareModuleName {
99            hw_type: hw_type.to_oid(),
100            hw_serial_num: serial_number,
101        };
102
103        let alt_names = vec![GeneralName::OtherName(OtherName::try_from(
104            &hardware_module_name,
105        )?)];
106
107        Ok(Self {
108            issuer,
109            subject,
110            subject_alt_name: Some(alt_names),
111        })
112    }
113}
114
115impl BuilderProfile for DevId {
116    fn get_issuer(&self, _subject: &Name) -> Name {
117        self.issuer.clone()
118    }
119
120    fn get_subject(&self) -> Name {
121        self.subject.clone()
122    }
123
124    fn build_extensions(
125        &self,
126        _spk: SubjectPublicKeyInfoRef<'_>,
127        issuer_spk: SubjectPublicKeyInfoRef<'_>,
128        tbs: &TbsCertificate,
129    ) -> Result<vec::Vec<Extension>> {
130        let mut extensions: vec::Vec<Extension> = vec::Vec::new();
131
132        // # Table 8-2 - DevID certificate and intermediate certificate extensions
133
134        // ## authorityKeyIdentifier MUST
135        // Section 8.10.1
136        extensions.push(
137            AuthorityKeyIdentifier::try_from(issuer_spk.clone())?
138                .to_extension(&tbs.subject, &extensions)?,
139        );
140
141        // ## subjectKeyIdentifier NOT RECOMMENDED
142
143        // ## keyUsage SHOULD
144        // Section 8.10.3
145        //
146        // NOTE(baloo):
147        //   IEEE spec allows for keyEncipherment but that would be used for TLS1.2 RSA and RSA_PSK
148        //   (IE: non-DH) session scheme.
149        //   In the mean time, when used with TPMs, the [TCG] will only allow for `digitalSignature`:
150        //   Use of digitalSignature (only) is RECOMMENDED. Refer to section 3.8.
151        //
152        // [TCG]: https://trustedcomputinggroup.org/wp-content/uploads/TPM-2p0-Keys-for-Device-Identity-and-Attestation_v1_r12_pub10082021.pdf#page=57
153        let key_usage = KeyUsages::DigitalSignature.into();
154        extensions.push(KeyUsage(key_usage).to_extension(&tbs.subject, &extensions)?);
155
156        // ## subjectAltName SHOULD
157        // 8.10.4
158        if let Some(san) = &self.subject_alt_name {
159            extensions.push(SubjectAltName(san.clone()).to_extension(&tbs.subject, &extensions)?);
160        }
161
162        Ok(extensions)
163    }
164}
165
166/// Version of the TPM used for DevID
167#[derive(Debug, Clone, PartialEq)]
168pub enum TpmVersion {
169    /// TPM version 1.2
170    Tpm12,
171    /// TPM version 2.0
172    Tpm20,
173    /// Other TPM version
174    #[cfg(feature = "hazmat")]
175    Other(ObjectIdentifier),
176}
177
178impl TpmVersion {
179    fn to_oid(&self) -> ObjectIdentifier {
180        match self {
181            Self::Tpm12 => tcgtpm::TCG_SV_TPM_12,
182            Self::Tpm20 => tcgtpm::TCG_SV_TPM_20,
183            #[cfg(feature = "hazmat")]
184            Self::Other(o) => *o,
185        }
186    }
187}
188
189// Notes:
190// Example of a certificate can be found in A.2
191// https://trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf#page=37
192//
193// OID 2.23.133.1.0 for TPM version 1.2
194// OID 2.23.133.1.2 for TPM version 2.0
195//
196//
197// https://trustedcomputinggroup.org/wp-content/uploads/TPM-2p0-Keys-for-Device-Identity-and-Attestation_v1_r12_pub10082021.pdf#page=60
198// An IDevID/IAK complying with this specification SHOULD include tcg-cap-verifiedTPMResidency to indicate
199// compliance with section 4 and also one of tcg-cap-verifiedTPMFixed (IDevID) or tcg-cap-verifiedTPMRestricted
200// (IAK).
201//
202// tcg-cap-verifiedTPMResidency 2.23.133.11.1.1
203// tcg-cap-verifiedTPMFixed 2.23.133.11.1.2
204// tcg-cap-verifiedTPMRestricted 2.23.133.11.1.3