Bitcoin deps refactoring (BDR): Linearizing bitcoin_hash deps
[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 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
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         use util::byte_utils;
20
21         #[derive(Clone, Copy)]
22         pub struct ChaCha20Poly1305RFC {
23                 cipher: ChaCha20,
24                 mac: Poly1305,
25                 finished: bool,
26                 data_len: usize,
27                 aad_len: u64,
28         }
29
30         impl ChaCha20Poly1305RFC {
31                 #[inline]
32                 fn pad_mac_16(mac: &mut Poly1305, len: usize) {
33                         if len % 16 != 0 {
34                                 mac.input(&[0; 16][0..16 - (len % 16)]);
35                         }
36                 }
37                 pub fn new(key: &[u8], nonce: &[u8], aad: &[u8]) -> ChaCha20Poly1305RFC {
38                         assert!(key.len() == 16 || key.len() == 32);
39                         assert!(nonce.len() == 12);
40
41                         // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
42                         assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
43
44                         let mut cipher = ChaCha20::new(key, &nonce[4..]);
45                         let mut mac_key = [0u8; 64];
46                         let zero_key = [0u8; 64];
47                         cipher.process(&zero_key, &mut mac_key);
48
49                         let mut mac = Poly1305::new(&mac_key[..32]);
50                         mac.input(aad);
51                         ChaCha20Poly1305RFC::pad_mac_16(&mut mac, aad.len());
52
53                         ChaCha20Poly1305RFC {
54                                 cipher: cipher,
55                                 mac: mac,
56                                 finished: false,
57                                 data_len: 0,
58                                 aad_len: aad.len() as u64,
59                         }
60                 }
61
62                 pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
63                         assert!(input.len() == output.len());
64                         assert!(self.finished == false);
65                         self.cipher.process(input, output);
66                         self.data_len += input.len();
67                         self.mac.input(output);
68                         ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
69                         self.finished = true;
70                         self.mac.input(&byte_utils::le64_to_array(self.aad_len));
71                         self.mac.input(&byte_utils::le64_to_array(self.data_len as u64));
72                         self.mac.raw_result(out_tag);
73                 }
74
75                 pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
76                         assert!(input.len() == output.len());
77                         assert!(self.finished == false);
78
79                         self.finished = true;
80
81                         self.mac.input(input);
82
83                         self.data_len += input.len();
84                         ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
85                         self.mac.input(&byte_utils::le64_to_array(self.aad_len));
86                         self.mac.input(&byte_utils::le64_to_array(self.data_len as u64));
87
88                         let mut calc_tag =  [0u8; 16];
89                         self.mac.raw_result(&mut calc_tag);
90                         if fixed_time_eq(&calc_tag, tag) {
91                                 self.cipher.process(input, output);
92                                 true
93                         } else {
94                                 false
95                         }
96                 }
97         }
98 }
99 #[cfg(not(feature = "fuzztarget"))]
100 pub use self::real_chachapoly::ChaCha20Poly1305RFC;
101
102 #[cfg(feature = "fuzztarget")]
103 mod fuzzy_chachapoly {
104         #[derive(Clone, Copy)]
105         pub struct ChaCha20Poly1305RFC {
106                 tag: [u8; 16],
107                 finished: bool,
108         }
109         impl ChaCha20Poly1305RFC {
110                 pub fn new(key: &[u8], nonce: &[u8], _aad: &[u8]) -> ChaCha20Poly1305RFC {
111                         assert!(key.len() == 16 || key.len() == 32);
112                         assert!(nonce.len() == 12);
113
114                         // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
115                         assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
116
117                         let mut tag = [0; 16];
118                         tag.copy_from_slice(&key[0..16]);
119
120                         ChaCha20Poly1305RFC {
121                                 tag,
122                                 finished: false,
123                         }
124                 }
125
126                 pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
127                         assert!(input.len() == output.len());
128                         assert!(self.finished == false);
129
130                         output.copy_from_slice(&input);
131                         out_tag.copy_from_slice(&self.tag);
132                         self.finished = true;
133                 }
134
135                 pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
136                         assert!(input.len() == output.len());
137                         assert!(self.finished == false);
138
139                         if tag[..] != self.tag[..] { return false; }
140                         output.copy_from_slice(input);
141                         self.finished = true;
142                         true
143                 }
144         }
145 }
146 #[cfg(feature = "fuzztarget")]
147 pub use self::fuzzy_chachapoly::ChaCha20Poly1305RFC;