iota_tls/
certgen.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use fastcrypto::ed25519::{Ed25519PrivateKey, Ed25519PublicKey};
6use pkcs8::EncodePrivateKey;
7use rcgen::{CertificateParams, KeyPair};
8use rustls::pki_types::{CertificateDer, PrivateKeyDer};
9
10pub struct SelfSignedCertificate {
11    inner: rcgen::Certificate,
12    key: KeyPair,
13}
14
15impl SelfSignedCertificate {
16    pub fn new(private_key: Ed25519PrivateKey, server_name: &str) -> Self {
17        let (cert, key) = generate_self_signed_tls_certificate(private_key, server_name);
18        Self { inner: cert, key }
19    }
20
21    pub fn rustls_certificate(&self) -> CertificateDer<'static> {
22        self.inner.der().to_owned()
23    }
24
25    pub fn rustls_private_key(&self) -> PrivateKeyDer<'static> {
26        PrivateKeyDer::Pkcs8(self.key.serialize_der().into())
27    }
28
29    pub fn reqwest_identity(&self) -> reqwest::tls::Identity {
30        let pem = self.inner.pem() + &self.key.serialize_pem();
31        reqwest::tls::Identity::from_pem(pem.as_ref()).unwrap()
32    }
33
34    pub fn reqwest_certificate(&self) -> reqwest::tls::Certificate {
35        reqwest::tls::Certificate::from_der(self.inner.der()).unwrap()
36    }
37}
38
39fn generate_self_signed_tls_certificate(
40    private_key: Ed25519PrivateKey,
41    server_name: &str,
42) -> (rcgen::Certificate, KeyPair) {
43    let keypair = ed25519::KeypairBytes {
44        secret_key: private_key.0.to_bytes(),
45        // ring cannot handle the optional public key that would be legal der here
46        // that is, ring expects PKCS#8 v.1
47        public_key: None,
48    };
49
50    let pkcs8 = keypair.to_pkcs8_der().unwrap();
51    let key_der = PrivateKeyDer::Pkcs8(pkcs8.as_bytes().to_vec().into());
52    let keypair = KeyPair::from_der_and_sign_algo(&key_der, &rcgen::PKCS_ED25519).unwrap();
53
54    (generate_cert(&keypair, server_name), keypair)
55}
56
57fn generate_cert(keypair: &KeyPair, server_name: &str) -> rcgen::Certificate {
58    CertificateParams::new(vec![server_name.to_owned()])
59        .unwrap()
60        .self_signed(keypair)
61        .expect(
62            "unreachable! from_params should only fail if the key is incompatible with params.algo",
63        )
64}
65
66pub(crate) fn public_key_from_certificate(
67    certificate: &CertificateDer,
68) -> Result<Ed25519PublicKey, anyhow::Error> {
69    use fastcrypto::traits::ToFromBytes;
70    use x509_parser::{certificate::X509Certificate, prelude::FromDer};
71
72    let cert = X509Certificate::from_der(certificate.as_ref())
73        .map_err(|e| rustls::Error::General(e.to_string()))?;
74    let spki = cert.1.public_key();
75    let public_key_bytes =
76        <ed25519::pkcs8::PublicKeyBytes as pkcs8::DecodePublicKey>::from_public_key_der(spki.raw)
77            .map_err(|e| rustls::Error::General(format!("invalid ed25519 public key: {e}")))?;
78
79    let public_key = Ed25519PublicKey::from_bytes(public_key_bytes.as_ref())?;
80
81    Ok(public_key)
82}