Merge pull request #2677 from Evanfeenstra/public-onion-utils
[rust-lightning] / lightning / src / util / chacha20.rs
index c96577da02c4a31ec1a3827b0ac38d9003e1a831..f46b344f2ce144c235e40e041743e707570e43a5 100644 (file)
@@ -2,22 +2,24 @@
 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
 
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
+use crate::io;
 
-#[cfg(not(feature = "fuzztarget"))]
+#[cfg(not(fuzzing))]
 mod real_chacha {
-       use std::cmp;
-       use util::byte_utils::{slice_to_le32, le32_to_array};
+       use core::cmp;
+       use core::convert::TryInto;
 
        #[derive(Clone, Copy, PartialEq, Eq)]
        #[allow(non_camel_case_types)]
        struct u32x4(pub u32, pub u32, pub u32, pub u32);
-       impl ::std::ops::Add for u32x4 {
+       impl ::core::ops::Add for u32x4 {
                type Output = u32x4;
                fn add(self, rhs: u32x4) -> u32x4 {
                        u32x4(self.0.wrapping_add(rhs.0),
@@ -26,7 +28,7 @@ mod real_chacha {
                              self.3.wrapping_add(rhs.3))
                }
        }
-       impl ::std::ops::Sub for u32x4 {
+       impl ::core::ops::Sub for u32x4 {
                type Output = u32x4;
                fn sub(self, rhs: u32x4) -> u32x4 {
                        u32x4(self.0.wrapping_sub(rhs.0),
@@ -35,24 +37,37 @@ mod real_chacha {
                              self.3.wrapping_sub(rhs.3))
                }
        }
-       impl ::std::ops::BitXor for u32x4 {
+       impl ::core::ops::BitXor for u32x4 {
                type Output = u32x4;
                fn bitxor(self, rhs: u32x4) -> u32x4 {
                        u32x4(self.0 ^ rhs.0, self.1 ^ rhs.1, self.2 ^ rhs.2, self.3 ^ rhs.3)
                }
        }
-       impl ::std::ops::Shr<u32x4> for u32x4 {
+       impl ::core::ops::Shr<u32x4> for u32x4 {
                type Output = u32x4;
                fn shr(self, rhs: u32x4) -> u32x4 {
                        u32x4(self.0 >> rhs.0, self.1 >> rhs.1, self.2 >> rhs.2, self.3 >> rhs.3)
                }
        }
-       impl ::std::ops::Shl<u32x4> for u32x4 {
+       impl ::core::ops::Shl<u32x4> for u32x4 {
                type Output = u32x4;
                fn shl(self, rhs: u32x4) -> u32x4 {
                        u32x4(self.0 << rhs.0, self.1 << rhs.1, self.2 << rhs.2, self.3 << rhs.3)
                }
        }
+       impl u32x4 {
+               fn from_bytes(bytes: &[u8]) -> Self {
+                       assert_eq!(bytes.len(), 4*4);
+                       Self (
+                               u32::from_le_bytes(bytes[0*4..1*4].try_into().expect("len is 4")),
+                               u32::from_le_bytes(bytes[1*4..2*4].try_into().expect("len is 4")),
+                               u32::from_le_bytes(bytes[2*4..3*4].try_into().expect("len is 4")),
+                               u32::from_le_bytes(bytes[3*4..4*4].try_into().expect("len is 4")),
+                       )
+               }
+       }
+
+       const BLOCK_SIZE: usize = 64;
 
        #[derive(Clone,Copy)]
        struct ChaChaState {
@@ -65,7 +80,7 @@ mod real_chacha {
        #[derive(Copy)]
        pub struct ChaCha20 {
                state  : ChaChaState,
-               output : [u8; 64],
+               output : [u8; BLOCK_SIZE],
                offset : usize,
        }
 
@@ -95,7 +110,7 @@ mod real_chacha {
                                d1,d2,d3,d4
                        ];
                        for i in 0..lens.len() {
-                               $output[i*4..(i+1)*4].copy_from_slice(&le32_to_array(lens[i]));
+                               $output[i*4..(i+1)*4].copy_from_slice(&lens[i].to_le_bytes());
                        }
                }}
        }
@@ -133,7 +148,39 @@ mod real_chacha {
                        assert!(key.len() == 16 || key.len() == 32);
                        assert!(nonce.len() == 8 || nonce.len() == 12);
 
-                       ChaCha20{ state: ChaCha20::expand(key, nonce), output: [0u8; 64], offset: 64 }
+                       ChaCha20{ state: ChaCha20::expand(key, nonce), output: [0u8; BLOCK_SIZE], offset: 64 }
+               }
+
+               /// Get one block from a ChaCha stream.
+               pub fn get_single_block(key: &[u8; 32], nonce: &[u8; 16]) -> [u8; 32] {
+                       let mut chacha = ChaCha20 { state: ChaCha20::expand(key, nonce), output: [0u8; BLOCK_SIZE], offset: 64 };
+                       let mut chacha_bytes = [0; 32];
+                       chacha.process_in_place(&mut chacha_bytes);
+                       chacha_bytes
+               }
+
+               /// Encrypts `src` into `dest` using a single block from a ChaCha stream. Passing `dest` as
+               /// `src` in a second call will decrypt it.
+               pub fn encrypt_single_block(
+                       key: &[u8; 32], nonce: &[u8; 16], dest: &mut [u8], src: &[u8]
+               ) {
+                       debug_assert_eq!(dest.len(), src.len());
+                       debug_assert!(dest.len() <= 32);
+
+                       let block = ChaCha20::get_single_block(key, nonce);
+                       for i in 0..dest.len() {
+                               dest[i] = block[i] ^ src[i];
+                       }
+               }
+
+               /// Same as `encrypt_single_block` only operates on a fixed-size input in-place.
+               pub fn encrypt_single_block_in_place(
+                       key: &[u8; 32], nonce: &[u8; 16], bytes: &mut [u8; 32]
+               ) {
+                       let block = ChaCha20::get_single_block(key, nonce);
+                       for i in 0..bytes.len() {
+                               bytes[i] = block[i] ^ bytes[i];
+                       }
                }
 
                fn expand(key: &[u8], nonce: &[u8]) -> ChaChaState {
@@ -143,59 +190,28 @@ mod real_chacha {
                                _  => unreachable!(),
                        };
                        ChaChaState {
-                               a: u32x4(
-                                       slice_to_le32(&constant[0..4]),
-                                       slice_to_le32(&constant[4..8]),
-                                       slice_to_le32(&constant[8..12]),
-                                       slice_to_le32(&constant[12..16])
-                               ),
-                               b: u32x4(
-                                       slice_to_le32(&key[0..4]),
-                                       slice_to_le32(&key[4..8]),
-                                       slice_to_le32(&key[8..12]),
-                                       slice_to_le32(&key[12..16])
-                               ),
+                               a: u32x4::from_bytes(&constant[0..16]),
+                               b: u32x4::from_bytes(&key[0..16]),
                                c: if key.len() == 16 {
-                                       u32x4(
-                                               slice_to_le32(&key[0..4]),
-                                               slice_to_le32(&key[4..8]),
-                                               slice_to_le32(&key[8..12]),
-                                               slice_to_le32(&key[12..16])
-                                       )
+                                       u32x4::from_bytes(&key[0..16])
                                } else {
-                                       u32x4(
-                                               slice_to_le32(&key[16..20]),
-                                               slice_to_le32(&key[20..24]),
-                                               slice_to_le32(&key[24..28]),
-                                               slice_to_le32(&key[28..32])
-                                       )
+                                       u32x4::from_bytes(&key[16..32])
                                },
                                d: if nonce.len() == 16 {
-                                       u32x4(
-                                               slice_to_le32(&nonce[0..4]),
-                                               slice_to_le32(&nonce[4..8]),
-                                               slice_to_le32(&nonce[8..12]),
-                                               slice_to_le32(&nonce[12..16])
-                                       )
+                                       u32x4::from_bytes(&nonce[0..16])
                                } else if nonce.len() == 12 {
-                                       u32x4(
-                                               0,
-                                               slice_to_le32(&nonce[0..4]),
-                                               slice_to_le32(&nonce[4..8]),
-                                               slice_to_le32(&nonce[8..12])
-                                       )
+                                       let mut nonce4 = [0; 4*4];
+                                       nonce4[4..].copy_from_slice(nonce);
+                                       u32x4::from_bytes(&nonce4)
                                } else {
-                                       u32x4(
-                                               0,
-                                               0,
-                                               slice_to_le32(&nonce[0..4]),
-                                               slice_to_le32(&nonce[4..8])
-                                       )
+                                       let mut nonce4 = [0; 4*4];
+                                       nonce4[8..].copy_from_slice(nonce);
+                                       u32x4::from_bytes(&nonce4)
                                }
                        }
                }
 
-               // put the the next 64 keystream bytes into self.output
+               // put the the next BLOCK_SIZE keystream bytes into self.output
                fn update(&mut self) {
                        let mut state = self.state;
 
@@ -232,12 +248,12 @@ mod real_chacha {
                        while i < len {
                                // If there is no keystream available in the output buffer,
                                // generate the next block.
-                               if self.offset == 64 {
+                               if self.offset == BLOCK_SIZE {
                                        self.update();
                                }
 
                                // Process the min(available keystream, remaining input length).
-                               let count = cmp::min(64 - self.offset, len - i);
+                               let count = cmp::min(BLOCK_SIZE - self.offset, len - i);
                                // explicitly assert lengths to avoid bounds checks:
                                assert!(output.len() >= i + count);
                                assert!(input.len() >= i + count);
@@ -249,12 +265,41 @@ mod real_chacha {
                                self.offset += count;
                        }
                }
+
+               pub fn process_in_place(&mut self, input_output: &mut [u8]) {
+                       let len = input_output.len();
+                       let mut i = 0;
+                       while i < len {
+                               // If there is no keystream available in the output buffer,
+                               // generate the next block.
+                               if self.offset == BLOCK_SIZE {
+                                       self.update();
+                               }
+
+                               // Process the min(available keystream, remaining input length).
+                               let count = cmp::min(BLOCK_SIZE - self.offset, len - i);
+                               // explicitly assert lengths to avoid bounds checks:
+                               assert!(input_output.len() >= i + count);
+                               assert!(self.output.len() >= self.offset + count);
+                               for j in 0..count {
+                                       input_output[i + j] ^= self.output[self.offset + j];
+                               }
+                               i += count;
+                               self.offset += count;
+                       }
+               }
+
+               #[cfg(test)]
+               pub fn seek_to_block(&mut self, block_offset: u32) {
+                       self.state.d.0 = block_offset;
+                       self.update();
+               }
        }
 }
-#[cfg(not(feature = "fuzztarget"))]
+#[cfg(not(fuzzing))]
 pub use self::real_chacha::ChaCha20;
 
-#[cfg(feature = "fuzztarget")]
+#[cfg(fuzzing)]
 mod fuzzy_chacha {
        pub struct ChaCha20 {}
 
@@ -265,19 +310,52 @@ mod fuzzy_chacha {
                        Self {}
                }
 
+               pub fn get_single_block(_key: &[u8; 32], _nonce: &[u8; 16]) -> [u8; 32] {
+                       [0; 32]
+               }
+
+               pub fn encrypt_single_block(
+                       _key: &[u8; 32], _nonce: &[u8; 16], dest: &mut [u8], src: &[u8]
+               ) {
+                       debug_assert_eq!(dest.len(), src.len());
+                       debug_assert!(dest.len() <= 32);
+               }
+
+               pub fn encrypt_single_block_in_place(
+                       _key: &[u8; 32], _nonce: &[u8; 16], _bytes: &mut [u8; 32]
+               ) {}
+
                pub fn process(&mut self, input: &[u8], output: &mut [u8]) {
                        output.copy_from_slice(input);
                }
+
+               pub fn process_in_place(&mut self, _input_output: &mut [u8]) {}
        }
 }
-#[cfg(feature = "fuzztarget")]
+#[cfg(fuzzing)]
 pub use self::fuzzy_chacha::ChaCha20;
 
+pub(crate) struct ChaChaReader<'a, R: io::Read> {
+       pub chacha: &'a mut ChaCha20,
+       pub read: R,
+}
+impl<'a, R: io::Read> io::Read for ChaChaReader<'a, R> {
+       fn read(&mut self, dest: &mut [u8]) -> Result<usize, io::Error> {
+               let res = self.read.read(dest)?;
+               if res > 0 {
+                       self.chacha.process_in_place(&mut dest[0..res]);
+               }
+               Ok(res)
+       }
+}
+
 #[cfg(test)]
 mod test {
-       use std::iter::repeat;
+       use crate::prelude::*;
+       use core::iter::repeat;
 
        use super::ChaCha20;
+       use std::convert::TryInto;
 
        #[test]
        fn test_chacha20_256_tls_vectors() {
@@ -285,7 +363,7 @@ mod test {
                        key:   [u8; 32],
                        nonce: [u8; 8],
                        keystream: Vec<u8>,
-               };
+               }
                // taken from http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04
                let test_vectors = vec!(
                        TestVector{
@@ -420,7 +498,7 @@ mod test {
                        key:   [u8; 32],
                        nonce: [u8; 12],
                        keystream: Vec<u8>,
-               };
+               }
                // taken from http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04
                let test_vectors = vec!(
                        TestVector{
@@ -548,4 +626,76 @@ mod test {
                        assert_eq!(output, tv.keystream);
                }
        }
+
+       #[test]
+       fn get_single_block() {
+               // Test that `get_single_block` (which takes a 16-byte nonce) is equivalent to getting a block
+               // using a 12-byte nonce, with the block starting at the counter offset given by the remaining 4
+               // bytes.
+               let key = [
+                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+                       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+                       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+               ];
+               let nonce_16bytes = [
+                       0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b
+               ];
+               let counter_pos = &nonce_16bytes[..4];
+               let nonce_12bytes = &nonce_16bytes[4..];
+
+               // Initialize a ChaCha20 instance with its counter starting at 0.
+               let mut chacha20 = ChaCha20::new(&key, nonce_12bytes);
+               // Seek its counter to the block at counter_pos.
+               chacha20.seek_to_block(u32::from_le_bytes(counter_pos.try_into().unwrap()));
+               let mut block_bytes = [0; 32];
+               chacha20.process_in_place(&mut block_bytes);
+
+               assert_eq!(ChaCha20::get_single_block(&key, &nonce_16bytes), block_bytes);
+       }
+
+       #[test]
+       fn encrypt_single_block() {
+               let key = [
+                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+                       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+                       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+               ];
+               let nonce = [
+                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                       0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+               ];
+               let bytes = [1; 32];
+
+               let mut encrypted_bytes = [0; 32];
+               ChaCha20::encrypt_single_block(&key, &nonce, &mut encrypted_bytes, &bytes);
+
+               let mut decrypted_bytes = [0; 32];
+               ChaCha20::encrypt_single_block(&key, &nonce, &mut decrypted_bytes, &encrypted_bytes);
+
+               assert_eq!(bytes, decrypted_bytes);
+       }
+
+       #[test]
+       fn encrypt_single_block_in_place() {
+               let key = [
+                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+                       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+                       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+               ];
+               let nonce = [
+                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                       0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+               ];
+               let unencrypted_bytes = [1; 32];
+               let mut bytes = unencrypted_bytes;
+
+               ChaCha20::encrypt_single_block_in_place(&key, &nonce, &mut bytes);
+               assert_ne!(bytes, unencrypted_bytes);
+
+               ChaCha20::encrypt_single_block_in_place(&key, &nonce, &mut bytes);
+               assert_eq!(bytes, unencrypted_bytes);
+       }
 }