rustls/tls12/
cipher.rs

1use crate::cipher::{make_nonce, Iv, MessageDecrypter, MessageEncrypter};
2use crate::enums::ContentType;
3use crate::enums::ProtocolVersion;
4use crate::error::Error;
5use crate::msgs::base::Payload;
6use crate::msgs::codec;
7use crate::msgs::fragmenter::MAX_FRAGMENT_LEN;
8use crate::msgs::message::{BorrowedPlainMessage, OpaqueMessage, PlainMessage};
9
10use ring::aead;
11
12const TLS12_AAD_SIZE: usize = 8 + 1 + 2 + 2;
13
14fn make_tls12_aad(
15    seq: u64,
16    typ: ContentType,
17    vers: ProtocolVersion,
18    len: usize,
19) -> aead::Aad<[u8; TLS12_AAD_SIZE]> {
20    let mut out = [0; TLS12_AAD_SIZE];
21    codec::put_u64(seq, &mut out[0..]);
22    out[8] = typ.get_u8();
23    codec::put_u16(vers.get_u16(), &mut out[9..]);
24    codec::put_u16(len as u16, &mut out[11..]);
25    aead::Aad::from(out)
26}
27
28pub(crate) struct AesGcm;
29
30impl Tls12AeadAlgorithm for AesGcm {
31    fn decrypter(&self, dec_key: aead::LessSafeKey, dec_iv: &[u8]) -> Box<dyn MessageDecrypter> {
32        let mut ret = GcmMessageDecrypter {
33            dec_key,
34            dec_salt: [0u8; 4],
35        };
36
37        debug_assert_eq!(dec_iv.len(), 4);
38        ret.dec_salt.copy_from_slice(dec_iv);
39        Box::new(ret)
40    }
41
42    fn encrypter(
43        &self,
44        enc_key: aead::LessSafeKey,
45        write_iv: &[u8],
46        explicit: &[u8],
47    ) -> Box<dyn MessageEncrypter> {
48        debug_assert_eq!(write_iv.len(), 4);
49        debug_assert_eq!(explicit.len(), 8);
50
51        // The GCM nonce is constructed from a 32-bit 'salt' derived
52        // from the master-secret, and a 64-bit explicit part,
53        // with no specified construction.  Thanks for that.
54        //
55        // We use the same construction as TLS1.3/ChaCha20Poly1305:
56        // a starting point extracted from the key block, xored with
57        // the sequence number.
58        let mut iv = Iv(Default::default());
59        iv.0[..4].copy_from_slice(write_iv);
60        iv.0[4..].copy_from_slice(explicit);
61
62        Box::new(GcmMessageEncrypter { enc_key, iv })
63    }
64}
65
66pub(crate) struct ChaCha20Poly1305;
67
68impl Tls12AeadAlgorithm for ChaCha20Poly1305 {
69    fn decrypter(&self, dec_key: aead::LessSafeKey, iv: &[u8]) -> Box<dyn MessageDecrypter> {
70        Box::new(ChaCha20Poly1305MessageDecrypter {
71            dec_key,
72            dec_offset: Iv::copy(iv),
73        })
74    }
75
76    fn encrypter(
77        &self,
78        enc_key: aead::LessSafeKey,
79        enc_iv: &[u8],
80        _: &[u8],
81    ) -> Box<dyn MessageEncrypter> {
82        Box::new(ChaCha20Poly1305MessageEncrypter {
83            enc_key,
84            enc_offset: Iv::copy(enc_iv),
85        })
86    }
87}
88
89pub(crate) trait Tls12AeadAlgorithm: Send + Sync + 'static {
90    fn decrypter(&self, key: aead::LessSafeKey, iv: &[u8]) -> Box<dyn MessageDecrypter>;
91    fn encrypter(
92        &self,
93        key: aead::LessSafeKey,
94        iv: &[u8],
95        extra: &[u8],
96    ) -> Box<dyn MessageEncrypter>;
97}
98
99/// A `MessageEncrypter` for AES-GCM AEAD ciphersuites. TLS 1.2 only.
100struct GcmMessageEncrypter {
101    enc_key: aead::LessSafeKey,
102    iv: Iv,
103}
104
105/// A `MessageDecrypter` for AES-GCM AEAD ciphersuites.  TLS1.2 only.
106struct GcmMessageDecrypter {
107    dec_key: aead::LessSafeKey,
108    dec_salt: [u8; 4],
109}
110
111const GCM_EXPLICIT_NONCE_LEN: usize = 8;
112const GCM_OVERHEAD: usize = GCM_EXPLICIT_NONCE_LEN + 16;
113
114impl MessageDecrypter for GcmMessageDecrypter {
115    fn decrypt(&self, mut msg: OpaqueMessage, seq: u64) -> Result<PlainMessage, Error> {
116        let payload = &mut msg.payload.0;
117        if payload.len() < GCM_OVERHEAD {
118            return Err(Error::DecryptError);
119        }
120
121        let nonce = {
122            let mut nonce = [0u8; 12];
123            nonce[..4].copy_from_slice(&self.dec_salt);
124            nonce[4..].copy_from_slice(&payload[..8]);
125            aead::Nonce::assume_unique_for_key(nonce)
126        };
127
128        let aad = make_tls12_aad(seq, msg.typ, msg.version, payload.len() - GCM_OVERHEAD);
129
130        let plain_len = self
131            .dec_key
132            .open_within(nonce, aad, payload, GCM_EXPLICIT_NONCE_LEN..)
133            .map_err(|_| Error::DecryptError)?
134            .len();
135
136        if plain_len > MAX_FRAGMENT_LEN {
137            return Err(Error::PeerSentOversizedRecord);
138        }
139
140        payload.truncate(plain_len);
141        Ok(msg.into_plain_message())
142    }
143}
144
145impl MessageEncrypter for GcmMessageEncrypter {
146    fn encrypt(&self, msg: BorrowedPlainMessage, seq: u64) -> Result<OpaqueMessage, Error> {
147        let nonce = make_nonce(&self.iv, seq);
148        let aad = make_tls12_aad(seq, msg.typ, msg.version, msg.payload.len());
149
150        let total_len = msg.payload.len() + self.enc_key.algorithm().tag_len();
151        let mut payload = Vec::with_capacity(GCM_EXPLICIT_NONCE_LEN + total_len);
152        payload.extend_from_slice(&nonce.as_ref()[4..]);
153        payload.extend_from_slice(msg.payload);
154
155        self.enc_key
156            .seal_in_place_separate_tag(nonce, aad, &mut payload[GCM_EXPLICIT_NONCE_LEN..])
157            .map(|tag| payload.extend(tag.as_ref()))
158            .map_err(|_| Error::EncryptError)?;
159
160        Ok(OpaqueMessage {
161            typ: msg.typ,
162            version: msg.version,
163            payload: Payload::new(payload),
164        })
165    }
166}
167
168/// The RFC7905/RFC7539 ChaCha20Poly1305 construction.
169/// This implementation does the AAD construction required in TLS1.2.
170/// TLS1.3 uses `TLS13MessageEncrypter`.
171struct ChaCha20Poly1305MessageEncrypter {
172    enc_key: aead::LessSafeKey,
173    enc_offset: Iv,
174}
175
176/// The RFC7905/RFC7539 ChaCha20Poly1305 construction.
177/// This implementation does the AAD construction required in TLS1.2.
178/// TLS1.3 uses `TLS13MessageDecrypter`.
179struct ChaCha20Poly1305MessageDecrypter {
180    dec_key: aead::LessSafeKey,
181    dec_offset: Iv,
182}
183
184const CHACHAPOLY1305_OVERHEAD: usize = 16;
185
186impl MessageDecrypter for ChaCha20Poly1305MessageDecrypter {
187    fn decrypt(&self, mut msg: OpaqueMessage, seq: u64) -> Result<PlainMessage, Error> {
188        let payload = &mut msg.payload.0;
189
190        if payload.len() < CHACHAPOLY1305_OVERHEAD {
191            return Err(Error::DecryptError);
192        }
193
194        let nonce = make_nonce(&self.dec_offset, seq);
195        let aad = make_tls12_aad(
196            seq,
197            msg.typ,
198            msg.version,
199            payload.len() - CHACHAPOLY1305_OVERHEAD,
200        );
201
202        let plain_len = self
203            .dec_key
204            .open_in_place(nonce, aad, payload)
205            .map_err(|_| Error::DecryptError)?
206            .len();
207
208        if plain_len > MAX_FRAGMENT_LEN {
209            return Err(Error::PeerSentOversizedRecord);
210        }
211
212        payload.truncate(plain_len);
213        Ok(msg.into_plain_message())
214    }
215}
216
217impl MessageEncrypter for ChaCha20Poly1305MessageEncrypter {
218    fn encrypt(&self, msg: BorrowedPlainMessage, seq: u64) -> Result<OpaqueMessage, Error> {
219        let nonce = make_nonce(&self.enc_offset, seq);
220        let aad = make_tls12_aad(seq, msg.typ, msg.version, msg.payload.len());
221
222        let total_len = msg.payload.len() + self.enc_key.algorithm().tag_len();
223        let mut buf = Vec::with_capacity(total_len);
224        buf.extend_from_slice(msg.payload);
225
226        self.enc_key
227            .seal_in_place_append_tag(nonce, aad, &mut buf)
228            .map_err(|_| Error::EncryptError)?;
229
230        Ok(OpaqueMessage {
231            typ: msg.typ,
232            version: msg.version,
233            payload: Payload::new(buf),
234        })
235    }
236}