Move the two-AtomicUsize counter in peer_handler to a util struct
[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 #[cfg(not(feature = "fuzztarget"))]
14 mod real_chachapoly {
15         use util::chacha20::ChaCha20;
16         use util::poly1305::Poly1305;
17         use bitcoin::hashes::cmp::fixed_time_eq;
18
19         #[derive(Clone, Copy)]
20         pub struct ChaCha20Poly1305RFC {
21                 cipher: ChaCha20,
22                 mac: Poly1305,
23                 finished: bool,
24                 data_len: usize,
25                 aad_len: u64,
26         }
27
28         impl ChaCha20Poly1305RFC {
29                 #[inline]
30                 fn pad_mac_16(mac: &mut Poly1305, len: usize) {
31                         if len % 16 != 0 {
32                                 mac.input(&[0; 16][0..16 - (len % 16)]);
33                         }
34                 }
35                 pub fn new(key: &[u8], nonce: &[u8], aad: &[u8]) -> ChaCha20Poly1305RFC {
36                         assert!(key.len() == 16 || key.len() == 32);
37                         assert!(nonce.len() == 12);
38
39                         // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
40                         assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
41
42                         let mut cipher = ChaCha20::new(key, &nonce[4..]);
43                         let mut mac_key = [0u8; 64];
44                         let zero_key = [0u8; 64];
45                         cipher.process(&zero_key, &mut mac_key);
46
47                         let mut mac = Poly1305::new(&mac_key[..32]);
48                         mac.input(aad);
49                         ChaCha20Poly1305RFC::pad_mac_16(&mut mac, aad.len());
50
51                         ChaCha20Poly1305RFC {
52                                 cipher,
53                                 mac,
54                                 finished: false,
55                                 data_len: 0,
56                                 aad_len: aad.len() as u64,
57                         }
58                 }
59
60                 pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
61                         assert!(input.len() == output.len());
62                         assert!(self.finished == false);
63                         self.cipher.process(input, output);
64                         self.data_len += input.len();
65                         self.mac.input(output);
66                         ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
67                         self.finished = true;
68                         self.mac.input(&self.aad_len.to_le_bytes());
69                         self.mac.input(&(self.data_len as u64).to_le_bytes());
70                         self.mac.raw_result(out_tag);
71                 }
72
73                 pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
74                         assert!(input.len() == output.len());
75                         assert!(self.finished == false);
76
77                         self.finished = true;
78
79                         self.mac.input(input);
80
81                         self.data_len += input.len();
82                         ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
83                         self.mac.input(&self.aad_len.to_le_bytes());
84                         self.mac.input(&(self.data_len as u64).to_le_bytes());
85
86                         let mut calc_tag =  [0u8; 16];
87                         self.mac.raw_result(&mut calc_tag);
88                         if fixed_time_eq(&calc_tag, tag) {
89                                 self.cipher.process(input, output);
90                                 true
91                         } else {
92                                 false
93                         }
94                 }
95         }
96 }
97 #[cfg(not(feature = "fuzztarget"))]
98 pub use self::real_chachapoly::ChaCha20Poly1305RFC;
99
100 #[cfg(feature = "fuzztarget")]
101 mod fuzzy_chachapoly {
102         #[derive(Clone, Copy)]
103         pub struct ChaCha20Poly1305RFC {
104                 tag: [u8; 16],
105                 finished: bool,
106         }
107         impl ChaCha20Poly1305RFC {
108                 pub fn new(key: &[u8], nonce: &[u8], _aad: &[u8]) -> ChaCha20Poly1305RFC {
109                         assert!(key.len() == 16 || key.len() == 32);
110                         assert!(nonce.len() == 12);
111
112                         // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
113                         assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
114
115                         let mut tag = [0; 16];
116                         tag.copy_from_slice(&key[0..16]);
117
118                         ChaCha20Poly1305RFC {
119                                 tag,
120                                 finished: false,
121                         }
122                 }
123
124                 pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
125                         assert!(input.len() == output.len());
126                         assert!(self.finished == false);
127
128                         output.copy_from_slice(&input);
129                         out_tag.copy_from_slice(&self.tag);
130                         self.finished = true;
131                 }
132
133                 pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
134                         assert!(input.len() == output.len());
135                         assert!(self.finished == false);
136
137                         if tag[..] != self.tag[..] { return false; }
138                         output.copy_from_slice(input);
139                         self.finished = true;
140                         true
141                 }
142         }
143 }
144 #[cfg(feature = "fuzztarget")]
145 pub use self::fuzzy_chachapoly::ChaCha20Poly1305RFC;