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");
}
}
}