rustls_mbedcrypto_provider/
tls12.rs

1/* Copyright (c) Fortanix, Inc.
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 */
7
8use crate::error::mbedtls_err_to_rustls_error;
9use alloc::boxed::Box;
10use alloc::vec::Vec;
11use mbedtls::cipher::raw::{CipherId, CipherMode, CipherType};
12use mbedtls::cipher::{Authenticated, Cipher, Decryption, Encryption, Fresh};
13use rustls::crypto::cipher::{
14    make_tls12_aad, AeadKey, InboundOpaqueMessage, InboundPlainMessage, Iv, KeyBlockShape, MessageDecrypter, MessageEncrypter,
15    Nonce, OutboundOpaqueMessage, OutboundPlainMessage, PlainMessage, PrefixedPayload, Tls12AeadAlgorithm,
16    UnsupportedOperationError, NONCE_LEN,
17};
18use rustls::crypto::tls12::PrfUsingHmac;
19use rustls::crypto::{CipherSuiteCommon, KeyExchangeAlgorithm};
20
21use super::aead::{self, Algorithm, AES128_GCM, AES256_GCM};
22use alloc::string::String;
23use rustls::{CipherSuite, ConnectionTrafficSecrets, Error, SignatureScheme, SupportedCipherSuite, Tls12CipherSuite};
24
25pub(crate) const GCM_FIXED_IV_LEN: usize = 4;
26pub(crate) const GCM_EXPLICIT_NONCE_LEN: usize = 8;
27pub(crate) const GCM_OVERHEAD: usize = GCM_EXPLICIT_NONCE_LEN + 16;
28pub(crate) const MAX_FRAGMENT_LEN: usize = 16384;
29
30/// The TLS1.2 ciphersuite TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256.
31pub static TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite =
32    SupportedCipherSuite::Tls12(&Tls12CipherSuite {
33        common: CipherSuiteCommon {
34            suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
35            hash_provider: &super::hash::SHA256,
36            confidentiality_limit: u64::MAX,
37        },
38        kx: KeyExchangeAlgorithm::ECDHE,
39        sign: TLS12_ECDSA_SCHEMES,
40        aead_alg: &ChaCha20Poly1305,
41        prf_provider: &PrfUsingHmac(&super::hmac::HMAC_SHA256),
42    });
43
44/// The TLS1.2 ciphersuite TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
45pub static TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = SupportedCipherSuite::Tls12(&Tls12CipherSuite {
46    common: CipherSuiteCommon {
47        suite: CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
48        hash_provider: &super::hash::SHA256,
49        confidentiality_limit: u64::MAX,
50    },
51    kx: KeyExchangeAlgorithm::ECDHE,
52    sign: TLS12_RSA_SCHEMES,
53    aead_alg: &ChaCha20Poly1305,
54    prf_provider: &PrfUsingHmac(&super::hmac::HMAC_SHA256),
55});
56
57/// The TLS1.2 ciphersuite TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
58pub static TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: SupportedCipherSuite = SupportedCipherSuite::Tls12(&Tls12CipherSuite {
59    common: CipherSuiteCommon {
60        suite: CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
61        hash_provider: &super::hash::SHA256,
62        confidentiality_limit: 1 << 23,
63    },
64    kx: KeyExchangeAlgorithm::ECDHE,
65    sign: TLS12_RSA_SCHEMES,
66    aead_alg: &AES128_GCM,
67    prf_provider: &PrfUsingHmac(&super::hmac::HMAC_SHA256),
68});
69
70/// The TLS1.2 ciphersuite TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
71pub static TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: SupportedCipherSuite = SupportedCipherSuite::Tls12(&Tls12CipherSuite {
72    common: CipherSuiteCommon {
73        suite: CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
74        hash_provider: &super::hash::SHA384,
75        confidentiality_limit: 1 << 23,
76    },
77    kx: KeyExchangeAlgorithm::ECDHE,
78    sign: TLS12_RSA_SCHEMES,
79    aead_alg: &AES256_GCM,
80    prf_provider: &PrfUsingHmac(&super::hmac::HMAC_SHA384),
81});
82
83/// The TLS1.2 ciphersuite TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
84pub static TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: SupportedCipherSuite = SupportedCipherSuite::Tls12(&Tls12CipherSuite {
85    common: CipherSuiteCommon {
86        suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
87        hash_provider: &super::hash::SHA256,
88        confidentiality_limit: 1 << 23,
89    },
90    kx: KeyExchangeAlgorithm::ECDHE,
91    sign: TLS12_ECDSA_SCHEMES,
92    aead_alg: &AES128_GCM,
93    prf_provider: &PrfUsingHmac(&super::hmac::HMAC_SHA256),
94});
95
96/// The TLS1.2 ciphersuite TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
97pub static TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: SupportedCipherSuite = SupportedCipherSuite::Tls12(&Tls12CipherSuite {
98    common: CipherSuiteCommon {
99        suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
100        hash_provider: &super::hash::SHA384,
101        confidentiality_limit: 1 << 23,
102    },
103    kx: KeyExchangeAlgorithm::ECDHE,
104    sign: TLS12_ECDSA_SCHEMES,
105    aead_alg: &AES256_GCM,
106    prf_provider: &PrfUsingHmac(&super::hmac::HMAC_SHA384),
107});
108
109/// The TLS1.2 ciphersuite TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
110pub static TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: SupportedCipherSuite = SupportedCipherSuite::Tls12(&Tls12CipherSuite {
111    common: CipherSuiteCommon {
112        suite: CipherSuite::TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
113        hash_provider: &super::hash::SHA256,
114        confidentiality_limit: 1 << 23,
115    },
116    kx: KeyExchangeAlgorithm::DHE,
117    sign: TLS12_RSA_SCHEMES,
118    aead_alg: &AES128_GCM,
119    prf_provider: &PrfUsingHmac(&super::hmac::HMAC_SHA256),
120});
121
122/// The TLS1.2 ciphersuite TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
123pub static TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: SupportedCipherSuite = SupportedCipherSuite::Tls12(&Tls12CipherSuite {
124    common: CipherSuiteCommon {
125        suite: CipherSuite::TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
126        hash_provider: &super::hash::SHA384,
127        confidentiality_limit: 1 << 23,
128    },
129    kx: KeyExchangeAlgorithm::DHE,
130    sign: TLS12_RSA_SCHEMES,
131    aead_alg: &AES256_GCM,
132    prf_provider: &PrfUsingHmac(&super::hmac::HMAC_SHA384),
133});
134
135/// The TLS1.2 ciphersuite TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
136pub static TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = SupportedCipherSuite::Tls12(&Tls12CipherSuite {
137    common: CipherSuiteCommon {
138        suite: CipherSuite::TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
139        hash_provider: &super::hash::SHA256,
140        confidentiality_limit: u64::MAX,
141    },
142    kx: KeyExchangeAlgorithm::DHE,
143    sign: TLS12_RSA_SCHEMES,
144    aead_alg: &ChaCha20Poly1305,
145    prf_provider: &PrfUsingHmac(&super::hmac::HMAC_SHA256),
146});
147
148static TLS12_ECDSA_SCHEMES: &[SignatureScheme] = &[
149    SignatureScheme::ED25519,
150    SignatureScheme::ECDSA_NISTP521_SHA512,
151    SignatureScheme::ECDSA_NISTP384_SHA384,
152    SignatureScheme::ECDSA_NISTP256_SHA256,
153];
154
155static TLS12_RSA_SCHEMES: &[SignatureScheme] = &[
156    SignatureScheme::RSA_PSS_SHA512,
157    SignatureScheme::RSA_PSS_SHA384,
158    SignatureScheme::RSA_PSS_SHA256,
159    SignatureScheme::RSA_PKCS1_SHA512,
160    SignatureScheme::RSA_PKCS1_SHA384,
161    SignatureScheme::RSA_PKCS1_SHA256,
162];
163
164impl Tls12AeadAlgorithm for Algorithm {
165    fn encrypter(&self, enc_key: AeadKey, iv: &[u8], extra: &[u8]) -> Box<dyn MessageEncrypter> {
166        let iv = gcm_iv(iv, extra);
167        Box::new(GcmMessageEncrypter { enc_key, iv })
168    }
169
170    fn decrypter(&self, dec_key: AeadKey, iv: &[u8]) -> Box<dyn MessageDecrypter> {
171        let mut ret = GcmMessageDecrypter { dec_key, dec_salt: [0u8; GCM_FIXED_IV_LEN] };
172        debug_assert_eq!(iv.len(), GCM_FIXED_IV_LEN);
173        ret.dec_salt.copy_from_slice(iv);
174        Box::new(ret)
175    }
176
177    fn key_block_shape(&self) -> KeyBlockShape {
178        KeyBlockShape {
179            enc_key_len: self.key_length,
180            fixed_iv_len: GCM_FIXED_IV_LEN,
181            explicit_nonce_len: GCM_EXPLICIT_NONCE_LEN,
182        }
183    }
184
185    fn extract_keys(
186        &self,
187        key: AeadKey,
188        iv: &[u8],
189        explicit: &[u8],
190    ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> {
191        match self.cipher_type {
192            CipherType::Aes128Gcm => Ok(ConnectionTrafficSecrets::Aes128Gcm { key, iv: gcm_iv(iv, explicit) }),
193            CipherType::Aes256Gcm => Ok(ConnectionTrafficSecrets::Aes256Gcm { key, iv: gcm_iv(iv, explicit) }),
194            _ => Err(UnsupportedOperationError),
195        }
196    }
197}
198
199pub(crate) struct ChaCha20Poly1305;
200
201impl Tls12AeadAlgorithm for ChaCha20Poly1305 {
202    fn decrypter(&self, dec_key: AeadKey, iv: &[u8]) -> Box<dyn MessageDecrypter> {
203        Box::new(ChaCha20Poly1305MessageDecrypter { dec_key, dec_offset: Iv::copy(iv) })
204    }
205
206    fn encrypter(&self, enc_key: AeadKey, enc_iv: &[u8], _: &[u8]) -> Box<dyn MessageEncrypter> {
207        Box::new(ChaCha20Poly1305MessageEncrypter { enc_key, enc_offset: Iv::copy(enc_iv) })
208    }
209
210    fn key_block_shape(&self) -> KeyBlockShape {
211        KeyBlockShape { enc_key_len: 32, fixed_iv_len: 12, explicit_nonce_len: 0 }
212    }
213
214    fn extract_keys(
215        &self,
216        key: AeadKey,
217        iv: &[u8],
218        _explicit: &[u8],
219    ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> {
220        // This should always be true because KeyBlockShape and the Iv nonce len are in agreement.
221        debug_assert_eq!(NONCE_LEN, iv.len());
222        Ok(ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv: Iv::new(iv[..].try_into().unwrap()) })
223    }
224}
225
226/// A `MessageEncrypter` for AES-GCM AEAD ciphersuites. TLS 1.2 only.
227struct GcmMessageEncrypter {
228    enc_key: AeadKey,
229    iv: Iv,
230}
231
232/// A `MessageDecrypter` for AES-GCM AEAD ciphersuites.  TLS1.2 only.
233struct GcmMessageDecrypter {
234    dec_key: AeadKey,
235    dec_salt: [u8; GCM_FIXED_IV_LEN],
236}
237
238impl MessageDecrypter for GcmMessageDecrypter {
239    fn decrypt<'a>(&mut self, mut msg: InboundOpaqueMessage<'a>, seq: u64) -> Result<InboundPlainMessage<'a>, Error> {
240        let payload = &mut msg.payload;
241        if payload.len() < GCM_OVERHEAD {
242            return Err(Error::DecryptError);
243        }
244        let nonce = {
245            let mut nonce = [0u8; NONCE_LEN];
246            nonce[..GCM_FIXED_IV_LEN].copy_from_slice(&self.dec_salt);
247            nonce[GCM_FIXED_IV_LEN..].copy_from_slice(&payload[..GCM_EXPLICIT_NONCE_LEN]);
248            nonce
249        };
250        let aad = make_tls12_aad(seq, msg.typ, msg.version, payload.len() - GCM_OVERHEAD);
251
252        let dec_key = self.dec_key.as_ref();
253        let cipher = Cipher::<Decryption, Authenticated, Fresh>::new(CipherId::Aes, CipherMode::GCM, (dec_key.len() * 8) as _)
254            .map_err(mbedtls_err_to_rustls_error)?;
255
256        let cipher = cipher
257            .set_key_iv(dec_key, &nonce)
258            .map_err(mbedtls_err_to_rustls_error)?;
259
260        let tag_offset = payload
261            .len()
262            .checked_sub(aead::TAG_LEN)
263            .ok_or(Error::General(String::from("Tag length overflow")))?;
264        let (remaining, tag) = payload.split_at_mut(tag_offset);
265        let (_, ciphertext) = remaining.split_at_mut(GCM_EXPLICIT_NONCE_LEN);
266        let (plain_len, _) = cipher
267            .decrypt_auth_inplace(&aad, ciphertext, tag)
268            .map_err(|err| match err {
269                mbedtls::Error::CcmAuthFailed
270                | mbedtls::Error::ChachapolyAuthFailed
271                | mbedtls::Error::CipherAuthFailed
272                | mbedtls::Error::GcmAuthFailed => Error::DecryptError,
273                _ => mbedtls_err_to_rustls_error(err),
274            })?;
275        if plain_len > MAX_FRAGMENT_LEN {
276            return Err(Error::PeerSentOversizedRecord);
277        }
278        payload.copy_within(GCM_EXPLICIT_NONCE_LEN..tag_offset, 0);
279        payload.truncate(plain_len);
280        Ok(msg.into_plain_message())
281    }
282}
283
284impl MessageEncrypter for GcmMessageEncrypter {
285    fn encrypt(&mut self, msg: OutboundPlainMessage, seq: u64) -> Result<OutboundOpaqueMessage, Error> {
286        let payload_len = msg.payload.len();
287        let total_len = self.encrypted_payload_len(payload_len);
288        let mut payload = PrefixedPayload::with_capacity(total_len);
289
290        let nonce = Nonce::new(&self.iv, seq).0;
291        payload.extend_from_slice(&nonce.as_ref()[GCM_FIXED_IV_LEN..]);
292        payload.extend_from_chunks(&msg.payload);
293
294        let aad = make_tls12_aad(seq, msg.typ, msg.version, payload_len);
295        let mut tag = [0u8; aead::TAG_LEN];
296
297        let enc_key = self.enc_key.as_ref();
298        let cipher = Cipher::<Encryption, Authenticated, Fresh>::new(CipherId::Aes, CipherMode::GCM, (enc_key.len() * 8) as _)
299            .map_err(mbedtls_err_to_rustls_error)?;
300        let cipher = cipher
301            .set_key_iv(enc_key, &nonce)
302            .map_err(mbedtls_err_to_rustls_error)?;
303
304        cipher
305            .encrypt_auth_inplace(&aad, &mut payload.as_mut()[GCM_EXPLICIT_NONCE_LEN..], &mut tag)
306            .map_err(|err| match err {
307                mbedtls::Error::CcmAuthFailed
308                | mbedtls::Error::ChachapolyAuthFailed
309                | mbedtls::Error::CipherAuthFailed
310                | mbedtls::Error::GcmAuthFailed => Error::EncryptError,
311                _ => mbedtls_err_to_rustls_error(err),
312            })?;
313        payload.extend_from_slice(&tag);
314
315        Ok(OutboundOpaqueMessage::new(msg.typ, msg.version, payload))
316    }
317
318    fn encrypted_payload_len(&self, payload_len: usize) -> usize {
319        payload_len + GCM_EXPLICIT_NONCE_LEN + aead::TAG_LEN
320    }
321}
322
323/// The RFC7905/RFC7539 ChaCha20Poly1305 construction.
324/// This implementation does the AAD construction required in TLS1.2.
325/// TLS1.3 uses `TLS13MessageEncrypter`.
326struct ChaCha20Poly1305MessageEncrypter {
327    enc_key: AeadKey,
328    enc_offset: Iv,
329}
330
331/// The RFC7905/RFC7539 ChaCha20Poly1305 construction.
332/// This implementation does the AAD construction required in TLS1.2.
333/// TLS1.3 uses `TLS13MessageDecrypter`.
334struct ChaCha20Poly1305MessageDecrypter {
335    dec_key: AeadKey,
336    dec_offset: Iv,
337}
338
339const CHACHAPOLY1305_OVERHEAD: usize = 16;
340
341impl MessageDecrypter for ChaCha20Poly1305MessageDecrypter {
342    fn decrypt<'a>(&mut self, mut msg: InboundOpaqueMessage<'a>, seq: u64) -> Result<InboundPlainMessage<'a>, Error> {
343        let payload = &mut msg.payload;
344
345        if payload.len() < CHACHAPOLY1305_OVERHEAD {
346            return Err(Error::DecryptError);
347        }
348
349        let nonce = Nonce::new(&self.dec_offset, seq).0;
350        let aad = make_tls12_aad(seq, msg.typ, msg.version, payload.len() - CHACHAPOLY1305_OVERHEAD);
351
352        let dec_key = self.dec_key.as_ref();
353        let cipher = Cipher::<Decryption, Authenticated, Fresh>::new(
354            CipherId::Chacha20,
355            CipherMode::CHACHAPOLY,
356            (dec_key.len() * 8) as _,
357        )
358        .map_err(mbedtls_err_to_rustls_error)?;
359
360        let cipher = cipher
361            .set_key_iv(dec_key, &nonce)
362            .map_err(mbedtls_err_to_rustls_error)?;
363
364        let tag_offset = payload
365            .len()
366            .checked_sub(aead::TAG_LEN)
367            .ok_or(Error::DecryptError)?;
368
369        let (ciphertext, tag) = payload.split_at_mut(tag_offset);
370
371        let (plain_len, _) = cipher
372            .decrypt_auth_inplace(&aad, ciphertext, tag)
373            .map_err(|err| match err {
374                mbedtls::Error::CcmAuthFailed
375                | mbedtls::Error::ChachapolyAuthFailed
376                | mbedtls::Error::CipherAuthFailed
377                | mbedtls::Error::GcmAuthFailed => Error::DecryptError,
378                _ => mbedtls_err_to_rustls_error(err),
379            })?;
380
381        if plain_len > MAX_FRAGMENT_LEN {
382            return Err(Error::PeerSentOversizedRecord);
383        }
384
385        payload.truncate(plain_len);
386        Ok(msg.into_plain_message())
387    }
388}
389
390impl MessageEncrypter for ChaCha20Poly1305MessageEncrypter {
391    fn encrypt(&mut self, msg: OutboundPlainMessage, seq: u64) -> Result<OutboundOpaqueMessage, Error> {
392        let payload_len = msg.payload.len();
393        let total_len = self.encrypted_payload_len(payload_len);
394        let mut payload = PrefixedPayload::with_capacity(total_len);
395        payload.extend_from_chunks(&msg.payload);
396
397        let nonce = Nonce::new(&self.enc_offset, seq).0;
398        let aad = make_tls12_aad(seq, msg.typ, msg.version, payload_len);
399        let mut tag = [0u8; aead::TAG_LEN];
400
401        let enc_key = self.enc_key.as_ref();
402        let cipher = Cipher::<Encryption, Authenticated, Fresh>::new(
403            CipherId::Chacha20,
404            CipherMode::CHACHAPOLY,
405            (enc_key.len() * 8) as _,
406        )
407        .map_err(mbedtls_err_to_rustls_error)?;
408
409        let cipher = cipher
410            .set_key_iv(enc_key, &nonce)
411            .map_err(mbedtls_err_to_rustls_error)?;
412
413        cipher
414            .encrypt_auth_inplace(&aad, payload.as_mut(), &mut tag)
415            .map_err(|err| match err {
416                mbedtls::Error::CcmAuthFailed
417                | mbedtls::Error::ChachapolyAuthFailed
418                | mbedtls::Error::CipherAuthFailed
419                | mbedtls::Error::GcmAuthFailed => Error::EncryptError,
420                _ => mbedtls_err_to_rustls_error(err),
421            })?;
422        payload.extend_from_slice(&tag);
423
424        Ok(OutboundOpaqueMessage::new(msg.typ, msg.version, payload))
425    }
426
427    fn encrypted_payload_len(&self, payload_len: usize) -> usize {
428        payload_len + aead::TAG_LEN
429    }
430}
431
432/// Generate GCM IV based on given IV and explicit nonce.
433fn gcm_iv(write_iv: &[u8], explicit_nonce: &[u8]) -> Iv {
434    debug_assert_eq!(write_iv.len(), GCM_FIXED_IV_LEN);
435    debug_assert_eq!(explicit_nonce.len(), GCM_EXPLICIT_NONCE_LEN);
436
437    // The GCM nonce is constructed from a 32-bit 'salt' derived
438    // from the master-secret, and a 64-bit explicit part,
439    // with no specified construction.
440    //
441    // We use the same construction as TLS1.3/ChaCha20Poly1305:
442    // a starting point extracted from the key block, XOR-ed with
443    // the sequence number.
444    let mut iv = [0; NONCE_LEN];
445    iv[..GCM_FIXED_IV_LEN].copy_from_slice(write_iv);
446    iv[GCM_FIXED_IV_LEN..].copy_from_slice(explicit_nonce);
447
448    Iv::new(iv)
449}