Skip to main content

nsm_nitro_enclave_utils_keygen/
lib.rs

1//! A small collection of utilities used to generated [`NsmCertChain`], which is used by [nsm-nitro-enclave-utils](https://crates.io/crates/nsm-nitro-enclave-utils) to self-sign attestation documents in local development environments.
2
3pub mod encode;
4
5use p384::{
6    ecdsa::{DerSignature, SigningKey, VerifyingKey},
7    SecretKey,
8};
9use std::time::Duration;
10use x509_cert::{
11    builder::{Builder, CertificateBuilder, Profile},
12    name::Name,
13    serial_number::SerialNumber,
14    spki::SubjectPublicKeyInfo,
15    time::Validity,
16};
17
18pub use x509_cert::{
19    der::{Decode as DerDecodeExt, Encode as DerEncodeExt, EncodePem as PemEncodeExt},
20    Certificate,
21};
22
23/// A bundle that comprises every certificate (and an end signing key) that is used by [nsm-nitro-enclave-utils](https://crates.io/crates/nsm-nitro-enclave-utils) to self-sign attestation documents in local development environments.
24#[derive(Clone)]
25pub struct NsmCertChain {
26    pub root: Certificate,
27    pub int: Certificate,
28    pub end_signer: EndCertificateSigner,
29}
30
31/// Contains the end certificate and its associated signing key
32#[derive(Clone)]
33pub struct EndCertificateSigner {
34    pub cert: Certificate,
35    pub signing_key: SigningKey,
36}
37
38impl NsmCertChain {
39    /// Generates an [`NsmCertChain`] that is valid until the specified [`Duration`].
40    ///
41    /// These functions are not designed to be called inside a server and can theoretically panic, though that isn't expected behavior.
42    pub fn generate(valid_until: Duration) -> Self {
43        let (root_signing_key, root_public_key) = generate_key();
44        let root_cert = build_cert(
45            Profile::Root,
46            root_signing_key.clone(),
47            root_public_key,
48            valid_until,
49        );
50
51        let (int_key, int_public_key) = generate_key();
52        let int_cert = build_cert(
53            Profile::SubCA {
54                issuer: Default::default(),
55                path_len_constraint: None,
56            },
57            root_signing_key,
58            int_public_key,
59            valid_until,
60        );
61
62        let (end_signing_key, end_public_key) = generate_key();
63        let end_cert = build_cert(
64            Profile::Leaf {
65                issuer: Default::default(),
66                enable_key_agreement: false,
67                enable_key_encipherment: false,
68            },
69            int_key,
70            end_public_key,
71            valid_until,
72        );
73
74        Self {
75            root: root_cert,
76            int: int_cert,
77            end_signer: EndCertificateSigner {
78                cert: end_cert,
79                signing_key: end_signing_key,
80            },
81        }
82    }
83}
84
85fn build_cert(
86    profile: Profile,
87    signing_key: SigningKey,
88    public_key: VerifyingKey,
89    valid_until: Duration,
90) -> Certificate {
91    let cert = CertificateBuilder::new(
92        profile.clone(),
93        SerialNumber::new(&[1]).expect("SerialNumber"),
94        Validity::from_now(valid_until).expect("Validity"),
95        Name::default(),
96        SubjectPublicKeyInfo::from_key(public_key).expect("SubjectPublicKeyInfo"),
97        &signing_key,
98    )
99    .unwrap()
100    .build::<DerSignature>()
101    .unwrap();
102
103    cert
104}
105
106fn generate_key() -> (SigningKey, VerifyingKey) {
107    let signing_key = SigningKey::from(SecretKey::random(&mut rand_core::OsRng));
108    let verifying_key = VerifyingKey::from(signing_key.clone());
109    (signing_key, verifying_key)
110}
111
112#[cfg(test)]
113mod test {
114    use crate::NsmCertChain;
115    use std::time::Duration;
116
117    #[test]
118    fn generate_chain() {
119        let until = Duration::from_secs(0);
120        NsmCertChain::generate(until);
121    }
122}