ot_certs/asn1/mod.rs
1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use anyhow::{Context, Result};
6
7pub mod builder;
8pub mod codegen;
9pub mod der;
10pub mod dice_tcb;
11pub mod x509;
12
13/// An ASN1 tag.
14#[derive(Debug, Copy, Clone, PartialEq, Eq)]
15pub enum Tag {
16 // Universal tags.
17 Boolean,
18 BitString,
19 GeneralizedTime,
20 Integer,
21 OctetString,
22 Oid,
23 PrintableString,
24 Sequence,
25 Set,
26 Utf8String,
27 /// Context tags.
28 Context {
29 constructed: bool,
30 value: usize,
31 },
32}
33
34/// An ASN1 object identifier.
35#[derive(Debug, Clone, PartialEq, Eq, strum::Display)]
36pub enum Oid {
37 // X509 extensions.
38 AuthorityKeyIdentifier,
39 BasicConstraints,
40 DiceTcbInfo,
41 KeyUsage,
42 SubjectKeyIdentifier,
43 // Name attributes.
44 CommonName,
45 Country,
46 Organization,
47 OrganizationalUnit,
48 SerialNumber,
49 State,
50 // Signature algorithms.
51 EcdsaWithSha256,
52 // Public key type.
53 EcPublicKey,
54 // Elliptic curve names.
55 Prime256v1,
56 // Hash algorithms.
57 Sha256,
58 // Subject alternative name and its components.
59 SubjectAltName,
60 // Vendor aka manufacturer.
61 SalTpmVendor,
62 // Tpm Model (i.e. Ti50).
63 SalTpmModel,
64 // Tpm firmware version at the time of manufacturing.
65 SalTpmVersion,
66 // Custom oid.
67 Custom(String),
68}
69
70impl Oid {
71 /// Return the standard notation of the OID as string.
72 pub fn oid(&self) -> &str {
73 match self {
74 // From https://datatracker.ietf.org/doc/html/rfc5280
75 // id-ce OBJECT IDENTIFIER ::= { joint-iso-ccitt(2) ds(5) 29 }
76 //
77 // id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 }
78 Oid::BasicConstraints => "2.5.29.19",
79 // id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 }
80 Oid::KeyUsage => "2.5.29.15",
81 // id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 }
82 Oid::AuthorityKeyIdentifier => "2.5.29.35",
83 // id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 14 }
84 Oid::SubjectKeyIdentifier => "2.5.29.14",
85
86 // https://trustedcomputinggroup.org/wp-content/uploads/TCG_DICE_Attestation_Architecture_r22_02dec2020.pdf
87 // tcg OBJECT IDENTIFIER ::= {2 23 133}
88 // tcg-dice OBJECT IDENTIFIER ::= { tcg platformClass(5) 4 }
89 // tcg-dice-TcbInfo OBJECT IDENTIFIER ::= {tcg-dice 1}
90 Oid::DiceTcbInfo => "2.23.133.5.4.1",
91
92 // From https://www.itu.int/rec/T-REC-X.501/en
93 // ID ::= OBJECT IDENTIFIER
94 // ds ID ::= {joint-iso-itu-t ds(5)}
95 // attributeType ID ::= {ds 4}
96 // id-at ID ::= attributeType
97 //
98 // From https://www.itu.int/rec/T-REC-X.520
99 // id-at-commonName OBJECT IDENTIFIER ::= {id-at 3}
100 Oid::CommonName => "2.5.4.3",
101 // id-at-serialNumber OBJECT IDENTIFIER ::= {id-at 5}
102 Oid::SerialNumber => "2.5.4.5",
103 // id-at-countryName OBJECT IDENTIFIER ::= {id-at 6}
104 Oid::Country => "2.5.4.6",
105 // id-at-stateOrProvinceName OBJECT IDENTIFIER ::= {id-at 8}
106 Oid::State => "2.5.4.8",
107 // id-at-organizationName OBJECT IDENTIFIER ::= {id-at 10}
108 Oid::Organization => "2.5.4.10",
109 // id-at-organizationalUnitName OBJECT IDENTIFIER ::= {id-at 11}
110 Oid::OrganizationalUnit => "2.5.4.11",
111
112 // From https://datatracker.ietf.org/doc/html/rfc5758#section-3.2
113 // ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840)
114 // ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
115 Oid::EcdsaWithSha256 => "1.2.840.10045.4.3.2",
116
117 // From https://datatracker.ietf.org/doc/html/rfc3279
118 // ansi-X9-62 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) 10045 }
119 // id-publicKeyType OBJECT IDENTIFIER ::= { ansi-X9-62 keyType(2) }
120 // id-ecPublicKey OBJECT IDENTIFIER ::= { id-publicKeyType 1 }
121 Oid::EcPublicKey => "1.2.840.10045.2.1",
122 // ellipticCurve OBJECT IDENTIFIER ::= { ansi-X9-62 curves(3) }
123 // primeCurve OBJECT IDENTIFIER ::= { ellipticCurve prime(1) }
124 // prime256v1 OBJECT IDENTIFIER ::= { primeCurve 7 }
125 Oid::Prime256v1 => "1.2.840.10045.3.1.7",
126
127 // From https://datatracker.ietf.org/doc/html/rfc5758#section-2
128 // id-sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840)
129 // organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 1 }
130 Oid::Sha256 => "2.16.840.1.101.3.4.2.1",
131
132 // From https://www.rfc-editor.org/rfc/rfc5280.html#section-4.2.1.6
133 // subject-alt-name OBJECT_IDENTIFIER ::= { joint-iso-itu-t(2) ds(5)
134 // certificateExtension(29) subjectAltName(17)}
135 Oid::SubjectAltName => "2.5.29.17",
136
137 // The following three object IDs come from
138 // TCG EK Credential Profile
139 // For TPM Family 2.0; Level 0
140 // Version 2.5
141 // Revision 1
142 // January 26, 2022,
143 // Section 4 X.509 ASN.1 Structures and OIDs
144 // tcg OBJECT IDENTIFIER ::= {
145 // joint-iso-itu-t(2) international-organizations(23) tcg(133)
146 // tcg-attribute OBJECT IDENTIFIER ::= {tcg 2}
147 //
148 // tcg-at-tpmManufacturer OBJECT IDENTIFIER ::= {tcg-attribute 1}
149 Oid::SalTpmVendor => "2.23.133.2.1",
150
151 // tcg-at-tpmModel OBJECT IDENTIFIER ::= {tcg-attribute 2}
152 Oid::SalTpmModel => "2.23.133.2.2",
153
154 // tcg-at-tpmVersion OBJECT IDENTIFIER ::= {tcg-attribute 3}
155 Oid::SalTpmVersion => "2.23.133.2.3",
156
157 Oid::Custom(oid) => oid,
158 }
159 }
160
161 /// Return the DER encoding of the OID as a list of bytes.
162 pub fn to_der(&self) -> Result<Vec<u8>> {
163 let mut components = self
164 .oid()
165 .split('.')
166 .map(|s| {
167 s.parse::<u32>()
168 .with_context(|| format!("invalid OID component '{s}'"))
169 })
170 .collect::<Result<Vec<_>>>()?;
171 // From X.690 spec, section 8.19:
172 // The number of subidentifiers (N) shall be one less than the number of object identifier components
173 // in the object identifier value being encoded. The numerical value of the first subidentifier is derived
174 // from the values of the first two object identifier components in the object identifier value being encoded,
175 // using the formula: (X*40) + Y where X is the value of the first object identifier component and Y is the value
176 // of the second object identifier component.
177 components.reverse();
178 let first = components
179 .pop()
180 .context("cannot call push_oid with an empty OID")?;
181 let second = components
182 .pop()
183 .context("cannot call push_oid with a single-component OID")?;
184 components.push(40 * first + second);
185 components.reverse();
186 let mut bytes = Vec::<u8>::new();
187 for comp in components {
188 // The contents octets shall be an (ordered) list of encodings of subidentifiers (see 8.19.3 and 8.19.4) concatenated
189 // together. Each subidentifier is represented as a series of (one or more) octets. Bit 8 of each octet indicates whether it is the last in the
190 // series: bit 8 of the last octet is zero; bit 8 of each preceding octet is one. Bits 7 to 1 of the octets in the series collectively
191 // encode the subidentifier. Conceptually, these groups of bits are concatenated to form an unsigned binary number whose most
192 // significant bit is bit 7 of the first octet and whose least significant bit is bit 1 of the last octet. The subidentifier shall be
193 // encoded in the fewest possible octets, that is, the leading octet of the subidentifier shall not have the value 8016
194
195 // Compute the length that we need: each byte stores 7 bits so this is the
196 // the log in base 128 of the number.
197 let mut copy_comp = comp;
198 let mut length = 0;
199 while copy_comp > 0 {
200 length += 1;
201 copy_comp >>= 7;
202 }
203 if length == 0 {
204 length = 1;
205 }
206 // Create the bytes
207 for i in (0..length).rev() {
208 // Extract 7-bit chunk of the number.
209 let mut byte = ((comp >> (7 * i)) & 0x7f) as u8;
210 // Add continuation marker.
211 if i != 0 {
212 byte |= 0x80;
213 }
214 bytes.push(byte);
215 }
216 }
217 Ok(bytes)
218 }
219}