solana_tls_utils/
tls_certificates.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use {
    solana_keypair::Keypair,
    solana_pubkey::Pubkey,
    solana_signer::Signer,
    x509_parser::{prelude::*, public_key::PublicKey},
};

pub fn new_dummy_x509_certificate(
    keypair: &Keypair,
) -> (
    rustls::pki_types::CertificateDer<'static>,
    rustls::pki_types::PrivateKeyDer<'static>,
) {
    // Unfortunately, rustls does not accept a "raw" Ed25519 key.
    // We have to convert it to DER and pass it to the library.

    // Convert private key into PKCS#8 v1 object.
    // RFC 8410, Section 7: Private Key Format
    // https://www.rfc-editor.org/rfc/rfc8410#section-7
    //
    // The hardcoded prefix decodes to the following ASN.1 structure:
    //
    //   PrivateKeyInfo SEQUENCE (3 elem)
    //     version Version INTEGER 0
    //     privateKeyAlgorithm AlgorithmIdentifier SEQUENCE (1 elem)
    //       algorithm OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm)
    //     privateKey PrivateKey OCTET STRING (34 byte)
    const PKCS8_PREFIX: [u8; 16] = [
        0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04,
        0x20,
    ];

    let key_pkcs8_der = {
        let keypair_secret_bytes = keypair.secret().as_bytes();
        let keypair_secret_len = keypair_secret_bytes.len();
        if keypair_secret_len != 32 {
            panic!("Unexpected secret key length!");
        }
        let buffer_size = PKCS8_PREFIX
            .len()
            .checked_add(keypair_secret_len) //clippy being overly guarded here but optimizer will elide checked_add
            .expect("Unexpected secret key length!");
        let mut key_pkcs8_der = Vec::<u8>::with_capacity(buffer_size);
        key_pkcs8_der.extend_from_slice(&PKCS8_PREFIX);
        key_pkcs8_der.extend_from_slice(keypair_secret_bytes);
        key_pkcs8_der
    };

    // Create a dummy certificate. Only the SubjectPublicKeyInfo field
    // is relevant to the peer-to-peer protocols. The signature of the
    // X.509 certificate is deliberately invalid. (Peer authenticity is
    // checked in the TLS 1.3 CertificateVerify)
    // See https://www.itu.int/rec/T-REC-X.509-201910-I/en for detailed definitions.

    let mut cert_der = Vec::<u8>::with_capacity(0xf4);
    //    Certificate SEQUENCE (3 elem)
    //      tbsCertificate TBSCertificate SEQUENCE (8 elem)
    //        version [0] (1 elem)
    //          INTEGER  2
    //        serialNumber CertificateSerialNumber INTEGER (62 bit)
    //        signature AlgorithmIdentifier SEQUENCE (1 elem)
    //          algorithm OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm)
    //        issuer Name SEQUENCE (1 elem)
    //          RelativeDistinguishedName SET (1 elem)
    //            AttributeTypeAndValue SEQUENCE (2 elem)
    //              type AttributeType OBJECT IDENTIFIER 2.5.4.3 commonName (X.520 DN component)
    //              value AttributeValue [?] UTF8String Solana
    //        validity Validity SEQUENCE (2 elem)
    //          notBefore Time UTCTime 1970-01-01 00:00:00 UTC
    //          notAfter Time GeneralizedTime 4096-01-01 00:00:00 UTC
    //        subject Name SEQUENCE (0 elem)
    //        subjectPublicKeyInfo SubjectPublicKeyInfo SEQUENCE (2 elem)
    //          algorithm AlgorithmIdentifier SEQUENCE (1 elem)
    //            algorithm OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm)
    //          subjectPublicKey BIT STRING (256 bit)
    cert_der.extend_from_slice(&[
        0x30, 0x81, 0xf6, 0x30, 0x81, 0xa9, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08, 0x01, 0x01,
        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x30, 0x16,
        0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0b, 0x53, 0x6f, 0x6c, 0x61,
        0x6e, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x30, 0x20, 0x17, 0x0d, 0x37, 0x30, 0x30, 0x31,
        0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x34, 0x30, 0x39, 0x36,
        0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x00, 0x30, 0x2a,
        0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00,
    ]);
    cert_der.extend_from_slice(&keypair.pubkey().to_bytes());
    //        extensions [3] (1 elem)
    //          Extensions SEQUENCE (2 elem)
    //            Extension SEQUENCE (3 elem)
    //              extnID OBJECT IDENTIFIER 2.5.29.17 subjectAltName (X.509 extension)
    //              critical BOOLEAN true
    //              extnValue OCTET STRING (13 byte) encapsulating
    //                SEQUENCE (1 elem)
    //                [2] (9 byte) localhost
    //            Extension SEQUENCE (3 elem)
    //              extnID OBJECT IDENTIFIER 2.5.29.19 basicConstraints (X.509 extension)
    //              critical BOOLEAN true
    //              extnValue OCTET STRING (2 byte) encapsulating
    //                SEQUENCE (0 elem)
    //      signatureAlgorithm AlgorithmIdentifier SEQUENCE (1 elem)
    //        algorithm OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm)
    //        signature BIT STRING (512 bit)
    cert_der.extend_from_slice(&[
        0xa3, 0x29, 0x30, 0x27, 0x30, 0x17, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x01, 0x01, 0xff, 0x04,
        0x0d, 0x30, 0x0b, 0x82, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x30,
        0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x05,
        0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x41, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    ]);

    (
        rustls::pki_types::CertificateDer::from(cert_der),
        rustls::pki_types::PrivateKeyDer::try_from(key_pkcs8_der).unwrap(),
    )
}

pub fn get_pubkey_from_tls_certificate(
    der_cert: &rustls::pki_types::CertificateDer,
) -> Option<Pubkey> {
    let (_, cert) = X509Certificate::from_der(der_cert.as_ref()).ok()?;
    match cert.public_key().parsed().ok()? {
        PublicKey::Unknown(key) => Pubkey::try_from(key).ok(),
        _ => None,
    }
}

#[cfg(test)]
mod tests {
    use {super::*, solana_signer::Signer};

    #[test]
    fn test_generate_tls_certificate() {
        let keypair = Keypair::new();
        let (cert, _) = new_dummy_x509_certificate(&keypair);
        if let Some(pubkey) = get_pubkey_from_tls_certificate(&cert) {
            assert_eq!(pubkey, keypair.pubkey());
        } else {
            panic!("Failed to get certificate pubkey");
        }
    }
}