ot_certs/asn1/
x509.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::Result;
6use num_bigint_dig::BigUint;
7use num_traits::FromPrimitive;
8
9use crate::asn1::builder::{Builder, concat_suffix};
10use crate::asn1::{Oid, Tag};
11use crate::template::{
12    AttributeType, BasicConstraints, Certificate, CertificateExtension, EcCurve, EcPublicKeyInfo,
13    EcdsaSignature, HashAlgorithm, KeyUsage, Name, Signature, SubjectPublicKeyInfo, Value,
14};
15
16impl HashAlgorithm {
17    // Return the object identifier of this algorithm.
18    pub fn oid(&self) -> Oid {
19        match self {
20            HashAlgorithm::Sha256 => Oid::Sha256,
21        }
22    }
23}
24
25impl EcCurve {
26    pub fn oid(&self) -> Oid {
27        match self {
28            EcCurve::Prime256v1 => Oid::Prime256v1,
29        }
30    }
31}
32
33// Those attribute types are described in X.520. For UnboundedDirectoryString,
34// we have chosen the UTF-8 type.
35impl AttributeType {
36    // Return the object identifier of this attribute type.
37    pub fn oid(&self) -> Oid {
38        match self {
39            Self::Country => Oid::Country,
40            Self::Organization => Oid::Organization,
41            Self::OrganizationalUnit => Oid::OrganizationalUnit,
42            Self::State => Oid::State,
43            Self::CommonName => Oid::CommonName,
44            Self::SerialNumber => Oid::SerialNumber,
45            Self::TpmModel => Oid::SalTpmModel,
46            Self::TpmVendor => Oid::SalTpmVendor,
47            Self::TpmVersion => Oid::SalTpmVersion,
48        }
49    }
50
51    // Return the tag of the value associated with this attribute type.
52    pub fn data_type(&self) -> Tag {
53        match self {
54            Self::Country => Tag::PrintableString,
55            Self::Organization => Tag::Utf8String,
56            Self::OrganizationalUnit => Tag::Utf8String,
57            Self::State => Tag::Utf8String,
58            Self::CommonName => Tag::Utf8String,
59            Self::SerialNumber => Tag::PrintableString,
60            Self::TpmModel => Tag::Utf8String,
61            Self::TpmVendor => Tag::Utf8String,
62            Self::TpmVersion => Tag::Utf8String,
63        }
64    }
65}
66
67pub struct X509;
68
69impl X509 {
70    /// Push an X509 certificate into the builder. The TBS certificate is represented by
71    /// a byte array (see push_x509_tbs_certificate).
72    pub fn push_certificate<B: Builder>(
73        builder: &mut B,
74        tbs_cert_var: &Value<Vec<u8>>,
75        sig: &Signature,
76    ) -> Result<()> {
77        // From https://datatracker.ietf.org/doc/html/rfc5280#section-4.1:
78        // Certificate  ::=  SEQUENCE  {
79        //   tbsCertificate       TBSCertificate,
80        //   signatureAlgorithm   AlgorithmIdentifier,
81        //   signatureValue       BIT STRING }
82        builder.push_seq(Some("cert".into()), |builder| {
83            builder.push_byte_array(Some("tbs".into()), tbs_cert_var)?;
84            Self::push_sig_alg_id(builder, sig)?;
85            builder.push_as_bit_string(Some("cert_sig".into()), &Tag::BitString, 0, |builder| {
86                Self::push_signature(builder, sig)
87            })
88        })
89    }
90
91    /// Push an X509 TBS certificate into the builder.
92    pub fn push_tbs_certificate<B: Builder>(builder: &mut B, cert: &Certificate) -> Result<()> {
93        // From https://datatracker.ietf.org/doc/html/rfc5280#section-4.1:
94        // TBSCertificate  ::=  SEQUENCE  {
95        //   version         [0]  EXPLICIT Version DEFAULT v1,
96        //   serialNumber         CertificateSerialNumber,
97        //   signature            AlgorithmIdentifier,
98        //   issuer               Name,
99        //   validity             Validity,
100        //   subject              Name,
101        //   subjectPublicKeyInfo SubjectPublicKeyInfo,
102        //   issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
103        //                        -- If present, version MUST be v2 or v3
104        //   subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
105        //                        -- If present, version MUST be v2 or v3
106        //   extensions      [3]  EXPLICIT Extensions OPTIONAL
107        //                        -- If present, version MUST be v3
108        // }
109        //
110        // // Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
111        //
112        // Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
113        builder.push_seq(Some("cert".into()), |builder| {
114            builder.push_tag(
115                Some("tbs_version_tag".into()),
116                &Tag::Context {
117                    constructed: true,
118                    value: 0,
119                },
120                |builder| {
121                    builder.push_integer(
122                        Some("tbs_version".into()),
123                        &Tag::Integer,
124                        &Value::Literal(
125                            BigUint::from_u32(2).expect("cannot make biguint from u32"),
126                        ),
127                    )
128                },
129            )?;
130            // CertificateSerialNumber  ::=  INTEGER
131            builder.push_integer(
132                Some("tbs_serial_number".into()),
133                &Tag::Integer,
134                &cert.serial_number,
135            )?;
136            Self::push_sig_alg_id(builder, &cert.signature)?;
137            Self::push_name(builder, Some("issuer".into()), &cert.issuer)?;
138            Self::push_validity(builder, &cert.not_before, &cert.not_after)?;
139            Self::push_name(builder, Some("subject".into()), &cert.subject)?;
140            Self::push_public_key_info(builder, &cert.subject_public_key_info)?;
141            builder.push_tag(
142                Some("tbs_extensions_tag".into()),
143                &Tag::Context {
144                    constructed: true,
145                    value: 3,
146                },
147                |builder| {
148                    builder.push_seq(Some("tbs_extensions".into()), |builder| {
149                        if let Some(constraints) = &cert.basic_constraints {
150                            Self::push_basic_constraints_ext(builder, constraints)?;
151                        }
152                        Self::push_subject_alt_name_ext(builder, &cert.subject_alt_name)?;
153                        if let Some(key_usage) = &cert.key_usage {
154                            Self::push_key_usage_ext(builder, key_usage)?;
155                        }
156                        if let Some(auth_key_id) = &cert.authority_key_identifier {
157                            Self::push_auth_key_id_ext(builder, auth_key_id)?;
158                        }
159                        if let Some(subj_key_id) = &cert.subject_key_identifier {
160                            Self::push_subject_key_id_ext(builder, subj_key_id)?;
161                        }
162                        for ext in &cert.private_extensions {
163                            Self::push_cert_extension(builder, ext)?
164                        }
165                        Ok(())
166                    })
167                },
168            )
169        })
170    }
171
172    pub fn push_cert_extension<B: Builder>(
173        builder: &mut B,
174        ext: &CertificateExtension,
175    ) -> Result<()> {
176        match ext {
177            CertificateExtension::DiceTcbInfo(dice_ext) => dice_ext.push_extension(builder),
178        }
179    }
180
181    pub fn push_name<B: Builder>(
182        builder: &mut B,
183        name_hint: Option<String>,
184        name: &Name,
185    ) -> Result<()> {
186        // https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.4
187        // Name ::= CHOICE { -- only one possibility for now --
188        // rdnSequence  RDNSequence }
189        //
190        // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
191        //
192        // RelativeDistinguishedName ::=
193        //     SET SIZE (1..MAX) OF AttributeTypeAndValue
194        //
195        // AttributeTypeAndValue ::= SEQUENCE {
196        //     type     AttributeType,
197        //     value    AttributeValue }
198        //
199        // AttributeType ::= OBJECT IDENTIFIER
200        //
201        // AttributeValue ::= ANY -- DEFINED BY AttributeType
202        builder.push_seq(name_hint.clone(), |builder| {
203            for attributes in name {
204                builder.push_set(concat_suffix(&name_hint, "set"), |builder| {
205                    for (attr_type, val) in attributes {
206                        builder.push_seq(
207                            concat_suffix(&name_hint, &attr_type.to_string()),
208                            |builder| {
209                                builder.push_oid(&attr_type.oid())?;
210                                builder.push_string(
211                                    concat_suffix(&name_hint, &attr_type.to_string()),
212                                    &attr_type.data_type(),
213                                    val,
214                                )
215                            },
216                        )?;
217                    }
218                    Ok(())
219                })?;
220            }
221            Ok(())
222        })
223    }
224
225    pub fn push_basic_constraints_ext<B: Builder>(
226        builder: &mut B,
227        constraints: &BasicConstraints,
228    ) -> Result<()> {
229        // https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.9
230        // BasicConstraints ::= SEQUENCE {
231        //   cA                      BOOLEAN DEFAULT FALSE,
232        //   pathLenConstraint       INTEGER (0..MAX) OPTIONAL }
233
234        // Retrieve the configured value of the extension.
235        Self::push_extension(builder, &Oid::BasicConstraints, true, |builder| {
236            builder.push_seq(Some("basic_constraints".into()), |builder| {
237                builder.push_boolean(&Tag::Boolean, &constraints.ca)
238            })
239        })
240    }
241
242    pub fn push_subject_alt_name_ext<B: Builder>(
243        builder: &mut B,
244        subject_alt_name: &Name,
245    ) -> Result<()> {
246        // SubjectAltName ::= GeneralNames
247        //
248        // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
249        //
250        // GeneralName ::= CHOICE {
251        //     otherName                       [0]     OtherName,
252        //     rfc822Name                      [1]     IA5String,
253        //     dNSName                         [2]     IA5String,
254        //     x400Address                     [3]     ORAddress,
255        //     directoryName                   [4]     Name,
256        //     ediPartyName                    [5]     EDIPartyName,
257        //     uniformResourceIdentifier       [6]     IA5String,
258        //     iPAddress                       [7]     OCTET STRING,
259        //     registeredID                    [8]     OBJECT IDENTIFIER }
260        //
261        // We only support `directoryName` at the moment.
262        if subject_alt_name.is_empty() {
263            // Subject Alternative Name is an optional extension, it's ok not to be present.
264            return Ok(());
265        }
266        Self::push_extension(builder, &Oid::SubjectAltName, false, |builder| {
267            builder.push_seq(Some("subject_alt_name".into()), |builder| -> Result<()> {
268                builder.push_tag(
269                    None,
270                    &Tag::Context {
271                        constructed: true,
272                        value: 4,
273                    },
274                    |builder| -> Result<()> { Self::push_name(builder, None, subject_alt_name) },
275                )
276            })
277        })
278    }
279
280    pub fn push_key_usage_ext<B: Builder>(builder: &mut B, key_usage: &KeyUsage) -> Result<()> {
281        // From https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.3
282        // KeyUsage ::= BIT STRING {
283        //    digitalSignature        (0),
284        //    nonRepudiation          (1), -- recent editions of X.509 have
285        //                         -- renamed this bit to contentCommitment
286        //    keyEncipherment         (2),
287        //    dataEncipherment        (3),
288        //    keyAgreement            (4),
289        //    keyCertSign             (5),
290        //    cRLSign                 (6),
291        //    encipherOnly            (7),
292        //    decipherOnly            (8) }
293        Self::push_extension(builder, &Oid::KeyUsage, true, |builder| {
294            builder.push_bitstring(
295                Some("key_usage".into()),
296                &Tag::BitString,
297                &[
298                    key_usage
299                        .digital_signature
300                        .clone()
301                        .unwrap_or(Value::literal(false)),
302                    /* nonRepudiation */ Value::Literal(false),
303                    /* keyEncipherment */ Value::Literal(false),
304                    /* dataEncipherment */ Value::Literal(false),
305                    key_usage
306                        .key_agreement
307                        .clone()
308                        .unwrap_or(Value::literal(false)),
309                    key_usage.cert_sign.clone().unwrap_or(Value::literal(false)),
310                    /* cRLSign */ Value::Literal(false),
311                    /* encipherOnly */ Value::Literal(false),
312                    /* decipherOnly */ Value::Literal(false),
313                ],
314            )
315        })
316    }
317
318    pub fn push_auth_key_id_ext<B: Builder>(
319        builder: &mut B,
320        auth_key_id: &Value<Vec<u8>>,
321    ) -> Result<()> {
322        // From https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.1
323        // AuthorityKeyIdentifier ::= SEQUENCE {
324        //   keyIdentifier             [0] KeyIdentifier           OPTIONAL,
325        //   authorityCertIssuer       [1] GeneralNames            OPTIONAL,
326        //   authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL  }
327        //
328        // KeyIdentifier ::= OCTET STRING
329        //
330        // Note: this is part of the implicit tagged modules:
331        // https://datatracker.ietf.org/doc/html/rfc5280#appendix-A.2
332        Self::push_extension(builder, &Oid::AuthorityKeyIdentifier, false, |builder| {
333            builder.push_seq(Some("auth_key_id".into()), |builder| {
334                builder.push_tag(
335                    Some("key_id".into()),
336                    &Tag::Context {
337                        constructed: false,
338                        value: 0,
339                    },
340                    |builder| builder.push_byte_array(Some("authority_key_id".into()), auth_key_id),
341                )
342            })
343        })
344    }
345
346    pub fn push_subject_key_id_ext<B: Builder>(
347        builder: &mut B,
348        auth_key_id: &Value<Vec<u8>>,
349    ) -> Result<()> {
350        // From https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.2
351        // KeyIdentifier ::= OCTET STRING
352        Self::push_extension(builder, &Oid::SubjectKeyIdentifier, false, |builder| {
353            builder.push_octet_string(Some("subject_key_id".into()), |builder| {
354                builder.push_byte_array(Some("subject_key_id".into()), auth_key_id)
355            })
356        })
357    }
358
359    pub fn push_extension<B: Builder>(
360        builder: &mut B,
361        oid: &Oid,
362        critical: bool,
363        build: impl FnOnce(&mut B) -> Result<()>,
364    ) -> Result<()> {
365        // From https://datatracker.ietf.org/doc/html/rfc5280#section-4.1:
366        // Extension  ::=  SEQUENCE  {
367        //         extnID      OBJECT IDENTIFIER,
368        //         critical    BOOLEAN DEFAULT FALSE,
369        //         extnValue   OCTET STRING
370        //                     -- contains the DER encoding of an ASN.1 value
371        //                     -- corresponding to the extension type identified
372        //                     -- by extnID
373        //         }
374        builder.push_seq(concat_suffix(&Some(oid.to_string()), "ext"), |builder| {
375            builder.push_oid(oid)?;
376            builder.push_boolean(&Tag::Boolean, &Value::Literal(critical))?;
377            builder.push_octet_string(concat_suffix(&Some(oid.to_string()), "ext_value"), build)
378        })
379    }
380
381    pub fn push_public_key_info<B: Builder>(
382        builder: &mut B,
383        pubkey_info: &SubjectPublicKeyInfo,
384    ) -> Result<()> {
385        // From https://datatracker.ietf.org/doc/html/rfc5280#section-4.1:
386        // SubjectPublicKeyInfo  ::=  SEQUENCE  {
387        // algorithm            AlgorithmIdentifier,
388        // subjectPublicKey     BIT STRING  }
389        builder.push_seq(Some("subject_public_key_info".into()), |builder| {
390            Self::push_pubkey_alg_id(builder, pubkey_info)?;
391            builder.push_as_bit_string(
392                Some("subject_public_key".into()),
393                &Tag::BitString,
394                0,
395                |builder| Self::push_public_key(builder, pubkey_info),
396            )
397        })
398    }
399
400    pub fn push_pubkey_alg_id<B: Builder>(
401        builder: &mut B,
402        pubkey_info: &SubjectPublicKeyInfo,
403    ) -> Result<()> {
404        // From https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.1.2
405        // AlgorithmIdentifier  ::=  SEQUENCE  {
406        // algorithm               OBJECT IDENTIFIER,
407        // parameters              ANY DEFINED BY algorithm OPTIONAL  }
408        builder.push_seq(
409            Some("subject_public_key_alg".into()),
410            |builder| match pubkey_info {
411                SubjectPublicKeyInfo::EcPublicKey(ec_pubkey) => {
412                    builder.push_oid(&Oid::EcPublicKey)?;
413                    Self::push_ec_public_key_params(builder, ec_pubkey)
414                }
415            },
416        )
417    }
418
419    pub fn push_public_key<B: Builder>(
420        builder: &mut B,
421        pubkey_info: &SubjectPublicKeyInfo,
422    ) -> Result<()> {
423        match pubkey_info {
424            SubjectPublicKeyInfo::EcPublicKey(ec_pubkey) => {
425                Self::push_ec_public_key(builder, ec_pubkey)
426            }
427        }
428    }
429
430    pub fn push_ec_public_key_params<B: Builder>(
431        builder: &mut B,
432        pubkey: &EcPublicKeyInfo,
433    ) -> Result<()> {
434        // From the X9.62 spec: section 6.4
435        // Parameters ::= CHOICE {
436        //   ecParameters ECParameters,
437        //   namedCurve CURVES.&id({CurveNames}),
438        //   implicitlyCA NULL
439        // }
440        //
441        // CURVES ::= CLASS {
442        //   &id OBJECT IDENTIFIER UNIQUE
443        // }
444        // WITH SYNTAX { ID &id }
445        //
446        // CurveNames CURVES ::= {
447        //   { ID prime256v1 } | ...
448        // }
449        //
450        builder.push_oid(&pubkey.curve.oid())?;
451        Ok(())
452    }
453
454    pub fn push_ec_public_key<B: Builder>(builder: &mut B, pubkey: &EcPublicKeyInfo) -> Result<()> {
455        // From the X9.62 spec: section 6.2
456        // ECPoint ::= OCTET STRING
457        //
458        // From the X9.62 spec: 4.3.6
459        // If P = (x,y), If the uncompressed form is used, then do the following:
460        // - Convert the field element x p to an octet string X1.
461        // - Convert the field element y to an octet string Y1.
462        // - Assign the value 04 to the single octet PC.
463        // - The result is the octet string PO = PC || X1 ||Y1.
464        // Section 4.3.3 explains how to convert a field element: this is a big-endian
465        // integer padded to ceil(log q) bytes.
466        //
467        // From the X9.62 spec: section 6.4
468        // The elliptic curve public key (a value of type ECPoint which is an OCTET STRING) is mapped to a
469        // subjectPublicKey (a value of type BIT STRING) as follows: the most significant bit of the OCTET STRING
470        // value becomes the most significant bit of the BIT STRING value, etc.; the least significant bit of the OCTET
471        // STRING becomes the least significant bit of the BIT STRING.
472        let size = match pubkey.curve {
473            EcCurve::Prime256v1 => 32,
474        };
475
476        builder.push_byte(4)?;
477        builder.push_integer_pad(Some("pubkey_ec_x".into()), &pubkey.public_key.x, size)?;
478        builder.push_integer_pad(Some("pubkey_ec_y".into()), &pubkey.public_key.y, size)?;
479        Ok(())
480    }
481
482    pub fn push_validity<B: Builder>(
483        builder: &mut B,
484        not_before: &Value<String>,
485        not_after: &Value<String>,
486    ) -> Result<()> {
487        // From https://datatracker.ietf.org/doc/html/rfc5280#section-4.1:
488        // Validity ::= SEQUENCE {
489        //   notBefore      Time,
490        //   notAfter       Time }
491        //
492        // Time ::= CHOICE {
493        //   utcTime        UTCTime,
494        //   generalTime    GeneralizedTime }
495        //
496        // From the X680 spec: section 46
497        // GeneralizedTime ::= [UNIVERSAL 24] IMPLICIT VisibleString
498        builder.push_seq(Some("validity".into()), |builder| {
499            builder.push_string(Some("not_before".into()), &Tag::GeneralizedTime, not_before)?;
500            builder.push_string(Some("not_after".into()), &Tag::GeneralizedTime, not_after)
501        })
502    }
503
504    pub fn push_sig_alg_id<B: Builder>(builder: &mut B, sig: &Signature) -> Result<()> {
505        // From https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.1.2
506        // AlgorithmIdentifier  ::=  SEQUENCE  {
507        // algorithm               OBJECT IDENTIFIER,
508        // parameters              ANY DEFINED BY algorithm OPTIONAL  }
509        //
510        // Per the X509 specification, the signature parameters must not contain any parameters
511        builder.push_seq(Some("algorithm_identifier".into()), |builder| match sig {
512            Signature::EcdsaWithSha256 { .. } => builder.push_oid(&Oid::EcdsaWithSha256),
513        })
514    }
515
516    pub fn push_signature<B: Builder>(builder: &mut B, sig: &Signature) -> Result<()> {
517        match sig {
518            Signature::EcdsaWithSha256 { value } => {
519                let zero = BigUint::from_u32(0).expect("cannot build BigUint from u32");
520                let empty_ecdsa = EcdsaSignature {
521                    r: Value::Literal(zero.clone()),
522                    s: Value::Literal(zero.clone()),
523                };
524                Self::push_ecdsa_sig(builder, value.as_ref().unwrap_or(&empty_ecdsa))
525            }
526        }
527    }
528
529    pub fn push_ecdsa_sig<B: Builder>(builder: &mut B, sig: &EcdsaSignature) -> Result<()> {
530        // From https://datatracker.ietf.org/doc/html/rfc3279#section-2.2.3:
531        //
532        // Ecdsa-Sig-Value  ::=  SEQUENCE  {
533        //  r     INTEGER,
534        //  s     INTEGER  }
535        builder.push_seq(Some("sig_ecdsa_value".into()), |builder| {
536            builder.push_integer(Some("sig_ecdsa_r".into()), &Tag::Integer, &sig.r)?;
537            builder.push_integer(Some("sig_ecdsa_s".into()), &Tag::Integer, &sig.s)
538        })
539    }
540}