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