Merge pull request #2800 from optout21/channel-close-add-funding
[rust-lightning] / lightning / src / util / chacha20poly1305rfc.rs
1 // ring has a garbage API so its use is avoided, but rust-crypto doesn't have RFC-variant poly1305
2 // Instead, we steal rust-crypto's implementation and tweak it to match the RFC.
3 //
4 // This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5 // or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7 // You may not use this file except in accordance with one or both of these
8 // licenses.
9 //
10 // This is a port of Andrew Moons poly1305-donna
11 // https://github.com/floodyberry/poly1305-donna
12
13 use crate::ln::msgs::DecodeError;
14 use crate::util::ser::{FixedLengthReader, LengthRead, LengthReadableArgs, Readable, Writeable, Writer};
15 use crate::io::{self, Read, Write};
16
17 #[cfg(not(fuzzing))]
18 mod real_chachapoly {
19         use crate::util::chacha20::ChaCha20;
20         use crate::util::poly1305::Poly1305;
21         use bitcoin::hashes::cmp::fixed_time_eq;
22
23         #[derive(Clone, Copy)]
24         pub struct ChaCha20Poly1305RFC {
25                 cipher: ChaCha20,
26                 mac: Poly1305,
27                 finished: bool,
28                 data_len: usize,
29                 aad_len: u64,
30         }
31
32         impl ChaCha20Poly1305RFC {
33                 #[inline]
34                 fn pad_mac_16(mac: &mut Poly1305, len: usize) {
35                         if len % 16 != 0 {
36                                 mac.input(&[0; 16][0..16 - (len % 16)]);
37                         }
38                 }
39                 pub fn new(key: &[u8], nonce: &[u8], aad: &[u8]) -> ChaCha20Poly1305RFC {
40                         assert!(key.len() == 16 || key.len() == 32);
41                         assert!(nonce.len() == 12);
42
43                         // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
44                         assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
45
46                         let mut cipher = ChaCha20::new(key, &nonce[4..]);
47                         let mut mac_key = [0u8; 64];
48                         let zero_key = [0u8; 64];
49                         cipher.process(&zero_key, &mut mac_key);
50
51                         let mut mac = Poly1305::new(&mac_key[..32]);
52                         mac.input(aad);
53                         ChaCha20Poly1305RFC::pad_mac_16(&mut mac, aad.len());
54
55                         ChaCha20Poly1305RFC {
56                                 cipher,
57                                 mac,
58                                 finished: false,
59                                 data_len: 0,
60                                 aad_len: aad.len() as u64,
61                         }
62                 }
63
64                 pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
65                         assert!(input.len() == output.len());
66                         assert!(self.finished == false);
67                         self.cipher.process(input, output);
68                         self.data_len += input.len();
69                         self.mac.input(output);
70                         ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
71                         self.finished = true;
72                         self.mac.input(&self.aad_len.to_le_bytes());
73                         self.mac.input(&(self.data_len as u64).to_le_bytes());
74                         self.mac.raw_result(out_tag);
75                 }
76
77                 pub fn encrypt_full_message_in_place(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) {
78                         self.encrypt_in_place(input_output);
79                         self.finish_and_get_tag(out_tag);
80                 }
81
82                 // Encrypt `input_output` in-place. To finish and calculate the tag, use `finish_and_get_tag`
83                 // below.
84                 pub(super) fn encrypt_in_place(&mut self, input_output: &mut [u8]) {
85                         debug_assert!(self.finished == false);
86                         self.cipher.process_in_place(input_output);
87                         self.data_len += input_output.len();
88                         self.mac.input(input_output);
89                 }
90
91                 // If we were previously encrypting with `encrypt_in_place`, this method can be used to finish
92                 // encrypting and calculate the tag.
93                 pub(super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) {
94                         debug_assert!(self.finished == false);
95                         ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
96                         self.finished = true;
97                         self.mac.input(&self.aad_len.to_le_bytes());
98                         self.mac.input(&(self.data_len as u64).to_le_bytes());
99                         self.mac.raw_result(out_tag);
100                 }
101
102                 pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
103                         assert!(input.len() == output.len());
104                         assert!(self.finished == false);
105
106                         self.finished = true;
107
108                         self.mac.input(input);
109
110                         self.data_len += input.len();
111                         ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
112                         self.mac.input(&self.aad_len.to_le_bytes());
113                         self.mac.input(&(self.data_len as u64).to_le_bytes());
114
115                         let mut calc_tag =  [0u8; 16];
116                         self.mac.raw_result(&mut calc_tag);
117                         if fixed_time_eq(&calc_tag, tag) {
118                                 self.cipher.process(input, output);
119                                 true
120                         } else {
121                                 false
122                         }
123                 }
124
125                 pub fn check_decrypt_in_place(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
126                         self.decrypt_in_place(input_output);
127                         if self.finish_and_check_tag(tag) { Ok(()) } else { Err(()) }
128                 }
129
130                 /// Decrypt in place, without checking the tag. Use `finish_and_check_tag` to check it
131                 /// later when decryption finishes.
132                 ///
133                 /// Should never be `pub` because the public API should always enforce tag checking.
134                 pub(super) fn decrypt_in_place(&mut self, input_output: &mut [u8]) {
135                         debug_assert!(self.finished == false);
136                         self.mac.input(input_output);
137                         self.data_len += input_output.len();
138                         self.cipher.process_in_place(input_output);
139                 }
140
141                 /// If we were previously decrypting with `just_decrypt_in_place`, this method must be used
142                 /// to check the tag. Returns whether or not the tag is valid.
143                 pub(super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool {
144                         debug_assert!(self.finished == false);
145                         self.finished = true;
146                         ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
147                         self.mac.input(&self.aad_len.to_le_bytes());
148                         self.mac.input(&(self.data_len as u64).to_le_bytes());
149
150                         let mut calc_tag =  [0u8; 16];
151                         self.mac.raw_result(&mut calc_tag);
152                         if fixed_time_eq(&calc_tag, tag) {
153                                 true
154                         } else {
155                                 false
156                         }
157                 }
158         }
159 }
160 #[cfg(not(fuzzing))]
161 pub use self::real_chachapoly::ChaCha20Poly1305RFC;
162
163 /// Enables simultaneously reading and decrypting a ChaCha20Poly1305RFC stream from a std::io::Read.
164 struct ChaChaPolyReader<'a, R: Read> {
165         pub chacha: &'a mut ChaCha20Poly1305RFC,
166         pub read: R,
167 }
168
169 impl<'a, R: Read> Read for ChaChaPolyReader<'a, R> {
170         // Decrypt bytes from Self::read into `dest`.
171         // `ChaCha20Poly1305RFC::finish_and_check_tag` must be called to check the tag after all reads
172         // complete.
173         fn read(&mut self, dest: &mut [u8]) -> Result<usize, io::Error> {
174                 let res = self.read.read(dest)?;
175                 if res > 0 {
176                         self.chacha.decrypt_in_place(&mut dest[0..res]);
177                 }
178                 Ok(res)
179         }
180 }
181
182 /// Enables simultaneously writing and encrypting a byte stream into a Writer.
183 struct ChaChaPolyWriter<'a, W: Writer> {
184         pub chacha: &'a mut ChaCha20Poly1305RFC,
185         pub write: &'a mut W,
186 }
187
188 impl<'a, W: Writer> Writer for ChaChaPolyWriter<'a, W> {
189         // Encrypt then write bytes from `src` into Self::write.
190         // `ChaCha20Poly1305RFC::finish_and_get_tag` can be called to retrieve the tag after all writes
191         // complete.
192         fn write_all(&mut self, src: &[u8]) -> Result<(), io::Error> {
193                 let mut src_idx = 0;
194                 while src_idx < src.len() {
195                         let mut write_buffer = [0; 8192];
196                         let bytes_written = (&mut write_buffer[..]).write(&src[src_idx..]).expect("In-memory writes can't fail");
197                         self.chacha.encrypt_in_place(&mut write_buffer[..bytes_written]);
198                         self.write.write_all(&write_buffer[..bytes_written])?;
199                         src_idx += bytes_written;
200                 }
201                 Ok(())
202         }
203 }
204
205 /// Enables the use of the serialization macros for objects that need to be simultaneously encrypted and
206 /// serialized. This allows us to avoid an intermediate Vec allocation.
207 pub(crate) struct ChaChaPolyWriteAdapter<'a, W: Writeable> {
208         pub rho: [u8; 32],
209         pub writeable: &'a W,
210 }
211
212 impl<'a, W: Writeable> ChaChaPolyWriteAdapter<'a, W> {
213         #[allow(unused)] // This will be used for onion messages soon
214         pub fn new(rho: [u8; 32], writeable: &'a W) -> ChaChaPolyWriteAdapter<'a, W> {
215                 Self { rho, writeable }
216         }
217 }
218
219 impl<'a, T: Writeable> Writeable for ChaChaPolyWriteAdapter<'a, T> {
220         // Simultaneously write and encrypt Self::writeable.
221         fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
222                 let mut chacha = ChaCha20Poly1305RFC::new(&self.rho, &[0; 12], &[]);
223                 let mut chacha_stream = ChaChaPolyWriter { chacha: &mut chacha, write: w };
224                 self.writeable.write(&mut chacha_stream)?;
225                 let mut tag = [0 as u8; 16];
226                 chacha.finish_and_get_tag(&mut tag);
227                 tag.write(w)?;
228
229                 Ok(())
230         }
231 }
232
233 /// Enables the use of the serialization macros for objects that need to be simultaneously decrypted and
234 /// deserialized. This allows us to avoid an intermediate Vec allocation.
235 pub(crate) struct ChaChaPolyReadAdapter<R: Readable> {
236         pub readable: R,
237 }
238
239 impl<T: Readable> LengthReadableArgs<[u8; 32]> for ChaChaPolyReadAdapter<T> {
240         // Simultaneously read and decrypt an object from a LengthRead, storing it in Self::readable.
241         // LengthRead must be used instead of std::io::Read because we need the total length to separate
242         // out the tag at the end.
243         fn read<R: LengthRead>(mut r: &mut R, secret: [u8; 32]) -> Result<Self, DecodeError> {
244                 if r.total_bytes() < 16 { return Err(DecodeError::InvalidValue) }
245
246                 let mut chacha = ChaCha20Poly1305RFC::new(&secret, &[0; 12], &[]);
247                 let decrypted_len = r.total_bytes() - 16;
248                 let s = FixedLengthReader::new(&mut r, decrypted_len);
249                 let mut chacha_stream = ChaChaPolyReader { chacha: &mut chacha, read: s };
250                 let readable: T = Readable::read(&mut chacha_stream)?;
251                 chacha_stream.read.eat_remaining()?;
252
253                 let mut tag = [0 as u8; 16];
254                 r.read_exact(&mut tag)?;
255                 if !chacha.finish_and_check_tag(&tag) {
256                         return Err(DecodeError::InvalidValue)
257                 }
258
259                 Ok(Self { readable })
260         }
261 }
262
263 #[cfg(fuzzing)]
264 mod fuzzy_chachapoly {
265         #[derive(Clone, Copy)]
266         pub struct ChaCha20Poly1305RFC {
267                 tag: [u8; 16],
268                 finished: bool,
269         }
270         impl ChaCha20Poly1305RFC {
271                 pub fn new(key: &[u8], nonce: &[u8], _aad: &[u8]) -> ChaCha20Poly1305RFC {
272                         assert!(key.len() == 16 || key.len() == 32);
273                         assert!(nonce.len() == 12);
274
275                         // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
276                         assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
277
278                         let mut tag = [0; 16];
279                         tag.copy_from_slice(&key[0..16]);
280
281                         ChaCha20Poly1305RFC {
282                                 tag,
283                                 finished: false,
284                         }
285                 }
286
287                 pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
288                         assert!(input.len() == output.len());
289                         assert!(self.finished == false);
290
291                         output.copy_from_slice(&input);
292                         out_tag.copy_from_slice(&self.tag);
293                         self.finished = true;
294                 }
295
296                 pub fn encrypt_full_message_in_place(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) {
297                         self.encrypt_in_place(input_output);
298                         self.finish_and_get_tag(out_tag);
299                 }
300
301                 pub(super) fn encrypt_in_place(&mut self, _input_output: &mut [u8]) {
302                         assert!(self.finished == false);
303                 }
304
305                 pub(super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) {
306                         assert!(self.finished == false);
307                         out_tag.copy_from_slice(&self.tag);
308                         self.finished = true;
309                 }
310
311                 pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
312                         assert!(input.len() == output.len());
313                         assert!(self.finished == false);
314
315                         if tag[..] != self.tag[..] { return false; }
316                         output.copy_from_slice(input);
317                         self.finished = true;
318                         true
319                 }
320
321                 pub fn check_decrypt_in_place(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> {
322                         self.decrypt_in_place(input_output);
323                         if self.finish_and_check_tag(tag) { Ok(()) } else { Err(()) }
324                 }
325
326                 pub(super) fn decrypt_in_place(&mut self, _input: &mut [u8]) {
327                         assert!(self.finished == false);
328                 }
329
330                 pub(super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool {
331                         if tag[..] != self.tag[..] { return false; }
332                         self.finished = true;
333                         true
334                 }
335         }
336 }
337 #[cfg(fuzzing)]
338 pub use self::fuzzy_chachapoly::ChaCha20Poly1305RFC;
339
340 #[cfg(test)]
341 mod tests {
342         use crate::ln::msgs::DecodeError;
343         use super::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter};
344         use crate::util::ser::{self, FixedLengthReader, LengthReadableArgs, Writeable};
345
346         // Used for for testing various lengths of serialization.
347         #[derive(Debug, PartialEq, Eq)]
348         struct TestWriteable {
349                 field1: Vec<u8>,
350                 field2: Vec<u8>,
351                 field3: Vec<u8>,
352         }
353         impl_writeable_tlv_based!(TestWriteable, {
354                 (1, field1, required_vec),
355                 (2, field2, required_vec),
356                 (3, field3, required_vec),
357         });
358
359         #[test]
360         fn test_chacha_stream_adapters() {
361                 // Check that ChaChaPolyReadAdapter and ChaChaPolyWriteAdapter correctly encode and decode an
362                 // encrypted object.
363                 macro_rules! check_object_read_write {
364                         ($obj: expr) => {
365                                 // First, serialize the object, encrypted with ChaCha20Poly1305.
366                                 let rho = [42; 32];
367                                 let writeable_len = $obj.serialized_length() as u64 + 16;
368                                 let write_adapter = ChaChaPolyWriteAdapter::new(rho, &$obj);
369                                 let encrypted_writeable_bytes = write_adapter.encode();
370                                 let encrypted_writeable = &encrypted_writeable_bytes[..];
371
372                                 // Now deserialize the object back and make sure it matches the original.
373                                 let mut rd = FixedLengthReader::new(encrypted_writeable, writeable_len);
374                                 let read_adapter = <ChaChaPolyReadAdapter<TestWriteable>>::read(&mut rd, rho).unwrap();
375                                 assert_eq!($obj, read_adapter.readable);
376                         };
377                 }
378
379                 // Try a big object that will require multiple write buffers.
380                 let big_writeable = TestWriteable {
381                         field1: vec![43],
382                         field2: vec![44; 4192],
383                         field3: vec![45; 4192 + 1],
384                 };
385                 check_object_read_write!(big_writeable);
386
387                 // Try a small object that fits into one write buffer.
388                 let small_writeable = TestWriteable {
389                         field1: vec![43],
390                         field2: vec![44],
391                         field3: vec![45],
392                 };
393                 check_object_read_write!(small_writeable);
394         }
395
396         fn do_chacha_stream_adapters_ser_macros() -> Result<(), DecodeError> {
397                 let writeable = TestWriteable {
398                         field1: vec![43],
399                         field2: vec![44; 4192],
400                         field3: vec![45; 4192 + 1],
401                 };
402
403                 // First, serialize the object into a TLV stream, encrypted with ChaCha20Poly1305.
404                 let rho = [42; 32];
405                 let write_adapter = ChaChaPolyWriteAdapter::new(rho, &writeable);
406                 let mut writer = ser::VecWriter(Vec::new());
407                 encode_tlv_stream!(&mut writer, {
408                         (1, write_adapter, required),
409                 });
410
411                 // Now deserialize the object back and make sure it matches the original.
412                 let mut read_adapter: Option<ChaChaPolyReadAdapter<TestWriteable>> = None;
413                 decode_tlv_stream!(&writer.0[..], {
414                         (1, read_adapter, (option: LengthReadableArgs, rho)),
415                 });
416                 assert_eq!(writeable, read_adapter.unwrap().readable);
417
418                 Ok(())
419         }
420
421         #[test]
422         fn chacha_stream_adapters_ser_macros() {
423                 // Test that our stream adapters work as expected with the TLV macros.
424                 // This also serves to test the `option: $trait` variant of the `_decode_tlv` ser macro.
425                 do_chacha_stream_adapters_ser_macros().unwrap()
426         }
427 }