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.
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
10 // This is a port of Andrew Moons poly1305-donna
11 // https://github.com/floodyberry/poly1305-donna
13 use crate::ln::msgs::DecodeError;
14 use crate::util::ser::{FixedLengthReader, LengthRead, LengthReadableArgs, Readable, Writeable, Writer};
15 use crate::io::{self, Read, Write};
19 use crate::util::chacha20::ChaCha20;
20 use crate::util::poly1305::Poly1305;
21 use bitcoin::hashes::cmp::fixed_time_eq;
23 #[derive(Clone, Copy)]
24 pub struct ChaCha20Poly1305RFC {
32 impl ChaCha20Poly1305RFC {
34 fn pad_mac_16(mac: &mut Poly1305, len: usize) {
36 mac.input(&[0; 16][0..16 - (len % 16)]);
39 pub fn new(key: &[u8], nonce: &[u8], aad: &[u8]) -> ChaCha20Poly1305RFC {
40 assert!(key.len() == 16 || key.len() == 32);
41 assert!(nonce.len() == 12);
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);
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);
51 let mut mac = Poly1305::new(&mac_key[..32]);
53 ChaCha20Poly1305RFC::pad_mac_16(&mut mac, aad.len());
60 aad_len: aad.len() as u64,
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);
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);
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);
82 // Encrypt `input_output` in-place. To finish and calculate the tag, use `finish_and_get_tag`
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);
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);
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);
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);
106 self.finished = true;
108 self.mac.input(input);
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());
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);
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(()) }
130 /// Decrypt in place, without checking the tag. Use `finish_and_check_tag` to check it
131 /// later when decryption finishes.
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);
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());
150 let mut calc_tag = [0u8; 16];
151 self.mac.raw_result(&mut calc_tag);
152 if fixed_time_eq(&calc_tag, tag) {
161 pub use self::real_chachapoly::ChaCha20Poly1305RFC;
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,
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
173 fn read(&mut self, dest: &mut [u8]) -> Result<usize, io::Error> {
174 let res = self.read.read(dest)?;
176 self.chacha.decrypt_in_place(&mut dest[0..res]);
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,
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
192 fn write_all(&mut self, src: &[u8]) -> Result<(), io::Error> {
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;
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> {
209 pub writeable: &'a W,
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 }
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);
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> {
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) }
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()?;
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)
259 Ok(Self { readable })
264 mod fuzzy_chachapoly {
265 #[derive(Clone, Copy)]
266 pub struct ChaCha20Poly1305RFC {
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);
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);
278 let mut tag = [0; 16];
279 tag.copy_from_slice(&key[0..16]);
281 ChaCha20Poly1305RFC {
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);
291 output.copy_from_slice(&input);
292 out_tag.copy_from_slice(&self.tag);
293 self.finished = true;
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);
301 pub(super) fn encrypt_in_place(&mut self, _input_output: &mut [u8]) {
302 assert!(self.finished == false);
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;
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);
315 if tag[..] != self.tag[..] { return false; }
316 output.copy_from_slice(input);
317 self.finished = true;
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(()) }
326 pub(super) fn decrypt_in_place(&mut self, _input: &mut [u8]) {
327 assert!(self.finished == false);
330 pub(super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool {
331 if tag[..] != self.tag[..] { return false; }
332 self.finished = true;
338 pub use self::fuzzy_chachapoly::ChaCha20Poly1305RFC;
342 use crate::ln::msgs::DecodeError;
343 use super::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter};
344 use crate::util::ser::{self, FixedLengthReader, LengthReadableArgs, Writeable};
346 // Used for for testing various lengths of serialization.
347 #[derive(Debug, PartialEq, Eq)]
348 struct TestWriteable {
353 impl_writeable_tlv_based!(TestWriteable, {
354 (1, field1, required_vec),
355 (2, field2, required_vec),
356 (3, field3, required_vec),
360 fn test_chacha_stream_adapters() {
361 // Check that ChaChaPolyReadAdapter and ChaChaPolyWriteAdapter correctly encode and decode an
363 macro_rules! check_object_read_write {
365 // First, serialize the object, encrypted with ChaCha20Poly1305.
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[..];
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);
379 // Try a big object that will require multiple write buffers.
380 let big_writeable = TestWriteable {
382 field2: vec![44; 4192],
383 field3: vec![45; 4192 + 1],
385 check_object_read_write!(big_writeable);
387 // Try a small object that fits into one write buffer.
388 let small_writeable = TestWriteable {
393 check_object_read_write!(small_writeable);
396 fn do_chacha_stream_adapters_ser_macros() -> Result<(), DecodeError> {
397 let writeable = TestWriteable {
399 field2: vec![44; 4192],
400 field3: vec![45; 4192 + 1],
403 // First, serialize the object into a TLV stream, encrypted with ChaCha20Poly1305.
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),
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)),
416 assert_eq!(writeable, read_adapter.unwrap().readable);
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()