Skip to main content

mysql_async/opts/
rustls_opts.rs

1#![cfg(feature = "rustls-tls")]
2
3use rustls::pki_types::{pem, pem::PemObject, CertificateDer, PrivateKeyDer, PrivatePkcs1KeyDer};
4
5use std::{borrow::Cow, path::Path};
6
7use super::PathOrBuf;
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
10pub struct ClientIdentity {
11    cert_chain: PathOrBuf<'static>,
12    priv_key: PathOrBuf<'static>,
13}
14
15impl ClientIdentity {
16    /// Creates new identity.
17    ///
18    /// `cert_chain` - certificate chain (in PEM or DER)
19    /// `priv_key` - private key (in DER or PEM) (it'll take the first one)
20    pub fn new(cert_chain: PathOrBuf<'static>, priv_key: PathOrBuf<'static>) -> Self {
21        Self {
22            cert_chain,
23            priv_key,
24        }
25    }
26
27    /// Sets the certificate chain path (in DER or PEM).
28    pub fn with_cert_chain(mut self, cert_chain: PathOrBuf<'static>) -> Self {
29        self.cert_chain = cert_chain;
30        self
31    }
32
33    /// Sets the private key path (in DER or PEM) (it'll take the first one).
34    pub fn with_priv_key<T>(mut self, priv_key: PathOrBuf<'static>) -> Self
35    where
36        T: Into<Cow<'static, Path>>,
37    {
38        self.priv_key = priv_key;
39        self
40    }
41
42    /// Returns the certificate chain.
43    pub fn cert_chain(&self) -> PathOrBuf<'_> {
44        self.cert_chain.borrow()
45    }
46
47    /// Returns the private key.
48    pub fn priv_key(&self) -> PathOrBuf<'_> {
49        self.priv_key.borrow()
50    }
51
52    pub(crate) async fn load(
53        &self,
54    ) -> crate::Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)> {
55        let cert_data = self.cert_chain.read().await?;
56        let key_data = self.priv_key.read().await?;
57
58        let mut cert_chain = Vec::new();
59        if std::str::from_utf8(&cert_data).is_err() {
60            cert_chain.push(CertificateDer::from(cert_data.into_owned()));
61        } else {
62            for cert in pem::SliceIter::<CertificateDer<'_>>::new(&cert_data) {
63                cert_chain.push(cert?);
64            }
65        }
66
67        let priv_key = if std::str::from_utf8(&key_data).is_err() {
68            PrivateKeyDer::Pkcs1(PrivatePkcs1KeyDer::from(key_data.into_owned()))
69        } else {
70            // Accept PKCS#1 (`RSA PRIVATE KEY`), PKCS#8 (`PRIVATE KEY`),
71            // or SEC1 (`EC PRIVATE KEY`) PEM blocks. Preserve
72            // `DriverError::NoKeyFound` for the "no PEM section present"
73            // case; surface real parse errors via `TlsError::Pem`.
74            match PrivateKeyDer::from_pem_slice(&key_data) {
75                Ok(k) => k,
76                Err(pem::Error::NoItemsFound) => {
77                    return Err(crate::DriverError::NoKeyFound.into());
78                }
79                Err(e) => return Err(e.into()),
80            }
81        };
82
83        Ok((cert_chain, priv_key))
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    // 2048-bit RSA key, PKCS#1 PEM format (-----BEGIN RSA PRIVATE KEY-----)
92    const PKCS1_KEY: &[u8] = b"-----BEGIN RSA PRIVATE KEY-----\n\
93MIIEowIBAAKCAQEAxE2SbfMeeA4m6r3T+oZPTMTKCuwhhKV0qZgxoU/aC3STgkTR\n\
94lIDbx4ukRx0pZdNvum2LlgmkKBuMQvRE5ivLkS/pKgWkKAa+zylkEr4X5tAj6o3e\n\
95juvcg4ogy0CIfvyDl/l/2gLCrgc1VVsRIqM2cs1oZZVUl3f0NuixgPaoVotVjkTq\n\
96Oqv+KkPWpE+mUmGlrik0jzwPcYyF3ZqlecMyqADLMXW7JByasMYd9imQHNcuojtE\n\
971Z41JaKO60sNpT1ibEZHsve1ZF1spSRD5pRgStwV8GZ+a8mmIYIahEfgyJTLrHwM\n\
98Dhc00sivdsXXnynFgoTyyvOq+6KbjfDi0Fj0AQIDAQABAoIBAALWWTG4JB5ZnAPk\n\
99EwKJuu8x3/a484ISjyVdYwhBGnZ0bKZjHsFd/G89rDXv7LeBTxnbd/tG7+W5gjU8\n\
100iRtnkiVq0xytoLIIaO0fHMhtkXRfWUmMW+VmcNVA45j0eZSWS0Og0lGBHTW9Om7d\n\
101plmBEOonYGUpe6PF0tSRV/F0fzni+rDMkmLpX14BQdzzA/cBAF7O+kTkxjOZtufe\n\
102oNeEa/GwoWdlTKYtUaVx9XriEc4BDPHUPgbMQuT8Gepp/FQ1+L9TltFIyO8B4ytX\n\
103KRvo3tF+E/YI7Vz4GnuYU7IL5O/fb8BZnTb0jplhbcPddF26qQTGbbPzgV8dY6wA\n\
104Yy6jszECgYEA6ikNucjg0L7uQxwQz/8OCA1oFKYYPj+gmhzsW0BEZ5VzN+hDEw9J\n\
105sErPp0ZNpG50+YaZPNAPxGfk6culonOAPyg3G9hnRj71S/pqSe35gnUYvc7mttcG\n\
106LobMy/QerVmQRzoiR51VujrpH1wf4cT/KV7a2C/Pk1vjBuFffi3AkWkCgYEA1pyd\n\
107XQqNr7c+GYX7KUnxv93fkzWtnztEoXoigqszrCpiCevWSNKNX1kHk9mIXFuoJmlZ\n\
108PIIRDqOpibBCgcZu+j3R611F2if0elSPj1YNMNc1vh87brHkhNxdQpIp4YqeMSFg\n\
109tX1zsrAwVIA0hoZP+TFCVODyJJBXQITTBALn4tkCgYAx6gJlAe76UFjVsVvcGpBR\n\
110Ixp2nFk6m7GOaG/xm6d5NSBUYIw7udyJWckd7RyL2ofQ0OJFVkymH0dqluB92oUR\n\
1118W6d3ulUzgLX6U9S5wlyx6c4fqwreXZ14IIzT5xic18P79Jy1ZT6l6gt6SNaqvWB\n\
112Shj4UGi9Dq88Pjpu2S3dUQKBgQCGaJ3xyItGUphU+eF8UXBTvwyoMMUlZcQs8cYt\n\
113WjXJjN3L4uVYxG2AGs0xHttVJJ5iODaIO9mc9olWz4pHptSYayFOrCL0Z3OpLc6f\n\
114ccBfJ1nkUcEyKb26LB1IdSw/skYy9PmRkRll/wy1z3mWCwaJRf2KFTvyBGhw4v8Z\n\
115kwxRuQKBgFwRC0AXlHArWqf3EBZmYd7OoyyspRVkzzSoWEJXNPH+G2WSmQt/lSeE\n\
116nAF3k8yHWco7+bSbV3xLWsI+THNn0BNWrbfX7EAo1kWfliSy0qrevQWeaaJkd83V\n\
117zmF/Qc7Ilu47gQKZWz9GASWJkOkbvJKKIDIwlIQvvXX1eDoAevmY\n\
118-----END RSA PRIVATE KEY-----\n";
119
120    // Same key, PKCS#8 PEM format (-----BEGIN PRIVATE KEY-----)
121    const PKCS8_KEY: &[u8] = b"-----BEGIN PRIVATE KEY-----\n\
122MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDETZJt8x54Dibq\n\
123vdP6hk9MxMoK7CGEpXSpmDGhT9oLdJOCRNGUgNvHi6RHHSll02+6bYuWCaQoG4xC\n\
1249ETmK8uRL+kqBaQoBr7PKWQSvhfm0CPqjd6O69yDiiDLQIh+/IOX+X/aAsKuBzVV\n\
125WxEiozZyzWhllVSXd/Q26LGA9qhWi1WOROo6q/4qQ9akT6ZSYaWuKTSPPA9xjIXd\n\
126mqV5wzKoAMsxdbskHJqwxh32KZAc1y6iO0TVnjUloo7rSw2lPWJsRkey97VkXWyl\n\
127JEPmlGBK3BXwZn5ryaYhghqER+DIlMusfAwOFzTSyK92xdefKcWChPLK86r7opuN\n\
1288OLQWPQBAgMBAAECggEAAtZZMbgkHlmcA+QTAom67zHf9rjzghKPJV1jCEEadnRs\n\
129pmMewV38bz2sNe/st4FPGdt3+0bv5bmCNTyJG2eSJWrTHK2gsgho7R8cyG2RdF9Z\n\
130SYxb5WZw1UDjmPR5lJZLQ6DSUYEdNb06bt2mWYEQ6idgZSl7o8XS1JFX8XR/OeL6\n\
131sMySYulfXgFB3PMD9wEAXs76ROTGM5m2596g14Rr8bChZ2VMpi1RpXH1euIRzgEM\n\
1328dQ+BsxC5PwZ6mn8VDX4v1OW0UjI7wHjK1cpG+je0X4T9gjtXPgae5hTsgvk799v\n\
133wFmdNvSOmWFtw910XbqpBMZts/OBXx1jrABjLqOzMQKBgQDqKQ25yODQvu5DHBDP\n\
134/w4IDWgUphg+P6CaHOxbQERnlXM36EMTD0mwSs+nRk2kbnT5hpk80A/EZ+Tpy6Wi\n\
135c4A/KDcb2GdGPvVL+mpJ7fmCdRi9zua21wYuhszL9B6tWZBHOiJHnVW6OukfXB/h\n\
136xP8pXtrYL8+TW+MG4V9+LcCRaQKBgQDWnJ1dCo2vtz4ZhfspSfG/3d+TNa2fO0Sh\n\
137eiKCqzOsKmIJ69ZI0o1fWQeT2YhcW6gmaVk8ghEOo6mJsEKBxm76PdHrXUXaJ/R6\n\
138VI+PVg0w1zW+HztuseSE3F1Ckinhip4xIWC1fXOysDBUgDSGhk/5MUJU4PIkkFdA\n\
139hNMEAufi2QKBgDHqAmUB7vpQWNWxW9wakFEjGnacWTqbsY5ob/Gbp3k1IFRgjDu5\n\
1403IlZyR3tHIvah9DQ4kVWTKYfR2qW4H3ahRHxbp3e6VTOAtfpT1LnCXLHpzh+rCt5\n\
141dnXggjNPnGJzXw/v0nLVlPqXqC3pI1qq9YFKGPhQaL0Orzw+Om7ZLd1RAoGBAIZo\n\
142nfHIi0ZSmFT54XxRcFO/DKgwxSVlxCzxxi1aNcmM3cvi5VjEbYAazTEe21UknmI4\n\
143Nog72Zz2iVbPikem1JhrIU6sIvRnc6ktzp9xwF8nWeRRwTIpvbosHUh1LD+yRjL0\n\
144+ZGRGWX/DLXPeZYLBolF/YoVO/IEaHDi/xmTDFG5AoGAXBELQBeUcCtap/cQFmZh\n\
1453s6jLKylFWTPNKhYQlc08f4bZZKZC3+VJ4ScAXeTzIdZyjv5tJtXfEtawj5Mc2fQ\n\
146E1att9fsQCjWRZ+WJLLSqt69BZ5pomR3zdXOYX9BzsiW7juBAplbP0YBJYmQ6Ru8\n\
147koogMjCUhC+9dfV4OgB6+Zg=\n\
148-----END PRIVATE KEY-----\n";
149
150    // EC P-256 private key, SEC1 PEM format (-----BEGIN EC PRIVATE KEY-----)
151    const SEC1_KEY: &[u8] = b"-----BEGIN EC PRIVATE KEY-----\n\
152MHcCAQEEIIfgf9+zR4IQO67je8L7RpFN7ILlUCHg8HHf2VDbIT4XoAoGCCqGSM49\n\
153AwEHoUQDQgAEyhKJO00+LzRLH50nrmX8KAJK6RtqqsWnW+rfCvOBs4nnUS1yruzM\n\
1545WB9UK1mVxsLVAPtNLXX0n0QvNBU6UkW7w==\n\
155-----END EC PRIVATE KEY-----\n";
156
157    // Self-signed EC certificate for the SEC1 key above
158    const EC_CERT: &[u8] = b"-----BEGIN CERTIFICATE-----\n\
159MIIBlDCCATmgAwIBAgIUYB9LFUfast9GdpYQRTH8LqWphD4wCgYIKoZIzj0EAwIw\n\
160HjEcMBoGA1UEAwwTbXlzcWxfYXN5bmMtdGVzdC1lYzAgFw0yNjA1MjMwNjQ5MzNa\n\
161GA8yMTI2MDQyOTA2NDkzM1owHjEcMBoGA1UEAwwTbXlzcWxfYXN5bmMtdGVzdC1l\n\
162YzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABMoSiTtNPi80Sx+dJ65l/CgCSukb\n\
163aqrFp1vq3wrzgbOJ51Etcq7szOVgfVCtZlcbC1QD7TS119J9ELzQVOlJFu+jUzBR\n\
164MB0GA1UdDgQWBBSEYBKB4Y2cpNeLJCL4a444cXfQxzAfBgNVHSMEGDAWgBSEYBKB\n\
1654Y2cpNeLJCL4a444cXfQxzAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kA\n\
166MEYCIQCEKz/dG8PRLqE5asy+Xcja5H8EOzOdQId9rl9+UmfD2wIhAMIwy9puBewL\n\
1670fndnN33G44NOnqH6QPMstJSEDX+XmyZ\n\
168-----END CERTIFICATE-----\n";
169
170    // Self-signed RSA certificate for the PKCS#1 / PKCS#8 keys above
171    const CERT: &[u8] = b"-----BEGIN CERTIFICATE-----\n\
172MIIC/zCCAeegAwIBAgIULM9bG/oB2Sts7i0XtMwbBjnhosswDQYJKoZIhvcNAQEL\n\
173BQAwDzENMAsGA1UEAwwEdGVzdDAeFw0yNjA1MjIxMzMwMjRaFw0yNjA1MjMxMzMw\n\
174MjRaMA8xDTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n\
175AoIBAQDETZJt8x54DibqvdP6hk9MxMoK7CGEpXSpmDGhT9oLdJOCRNGUgNvHi6RH\n\
176HSll02+6bYuWCaQoG4xC9ETmK8uRL+kqBaQoBr7PKWQSvhfm0CPqjd6O69yDiiDL\n\
177QIh+/IOX+X/aAsKuBzVVWxEiozZyzWhllVSXd/Q26LGA9qhWi1WOROo6q/4qQ9ak\n\
178T6ZSYaWuKTSPPA9xjIXdmqV5wzKoAMsxdbskHJqwxh32KZAc1y6iO0TVnjUloo7r\n\
179Sw2lPWJsRkey97VkXWylJEPmlGBK3BXwZn5ryaYhghqER+DIlMusfAwOFzTSyK92\n\
180xdefKcWChPLK86r7opuN8OLQWPQBAgMBAAGjUzBRMB0GA1UdDgQWBBQ6IWIObXB0\n\
181XSoICqsjND3CIeNy8DAfBgNVHSMEGDAWgBQ6IWIObXB0XSoICqsjND3CIeNy8DAP\n\
182BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB2jfKJEUntMcyAi28T\n\
183bDc/jORi/WMIWhGjfYnxJPjzTtt6Lst3tvGhmCcAd99PKS4xXbrvwbY48FMNWcZq\n\
184p1FwDzRucgafLxLJFQW26YUMltXX/P1Fh93BGjBqPomUBbXyLVeLs4+QtyLaGNSj\n\
185Ijx10780osF0noZ0G3BUiXD6v3qAPOcD+29d83oiXhBeLo2OyzqUUFTJ0KR4cNjg\n\
186wWBGf/Anl7ptxEVmEQvKSI8jNln6E0og3kCz4D+cIqoHUZain4IRYTwpK+kapDyQ\n\
187Lf3esme5EHGvjY9cZq5jMJUYZNx67uvxdyj05dtLAg26MoXaUvojn3Kf7bOE7IKD\n\
188t2kx\n\
189-----END CERTIFICATE-----\n";
190
191    fn identity(cert: &'static [u8], key: &'static [u8]) -> ClientIdentity {
192        ClientIdentity::new(cert.into(), key.into())
193    }
194
195    #[tokio::test]
196    async fn loads_pkcs1_key() {
197        let (certs, key) = identity(CERT, PKCS1_KEY).load().await.unwrap();
198        assert_eq!(certs.len(), 1);
199        assert!(matches!(key, PrivateKeyDer::Pkcs1(_)));
200    }
201
202    #[tokio::test]
203    async fn loads_pkcs8_key() {
204        let (certs, key) = identity(CERT, PKCS8_KEY).load().await.unwrap();
205        assert_eq!(certs.len(), 1);
206        assert!(matches!(key, PrivateKeyDer::Pkcs8(_)));
207    }
208
209    #[tokio::test]
210    async fn loads_sec1_key() {
211        let (certs, key) = identity(EC_CERT, SEC1_KEY).load().await.unwrap();
212        assert_eq!(certs.len(), 1);
213        assert!(matches!(key, PrivateKeyDer::Sec1(_)));
214    }
215
216    #[tokio::test]
217    async fn no_key_marker_returns_error() {
218        let err = identity(CERT, b"not a pem key\n").load().await.unwrap_err();
219        assert!(matches!(
220            err,
221            crate::Error::Driver(crate::DriverError::NoKeyFound)
222        ));
223    }
224}