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