X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Futil%2Fchacha20.rs;h=f46b344f2ce144c235e40e041743e707570e43a5;hb=2e33acbd9c5ebd605829859a982822df6e0f1723;hp=69e4ed8d113d1cdb5a101a5fa1b60338a68d13be;hpb=c05347f48a4ff2789ae261ee8c8f3f4277489420;p=rust-lightning diff --git a/lightning/src/util/chacha20.rs b/lightning/src/util/chacha20.rs index 69e4ed8d..f46b344f 100644 --- a/lightning/src/util/chacha20.rs +++ b/lightning/src/util/chacha20.rs @@ -9,12 +9,12 @@ // You may not use this file except in accordance with one or both of these // licenses. -use std::io; +use crate::io; -#[cfg(not(feature = "fuzztarget"))] +#[cfg(not(fuzzing))] mod real_chacha { use core::cmp; - use util::byte_utils::{slice_to_le32, le32_to_array}; + use core::convert::TryInto; #[derive(Clone, Copy, PartialEq, Eq)] #[allow(non_camel_case_types)] @@ -55,6 +55,17 @@ mod real_chacha { 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; @@ -99,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()); } }} } @@ -140,6 +151,38 @@ mod real_chacha { 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 { let constant = match key.len() { 16 => b"expand 16-byte k", @@ -147,54 +190,23 @@ 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) } } } @@ -276,12 +288,18 @@ mod real_chacha { 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 {} @@ -292,6 +310,21 @@ 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); } @@ -299,7 +332,7 @@ mod fuzzy_chacha { 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> { @@ -318,10 +351,11 @@ impl<'a, R: io::Read> io::Read for ChaChaReader<'a, R> { #[cfg(test)] mod test { - use prelude::*; + use crate::prelude::*; use core::iter::repeat; use super::ChaCha20; + use std::convert::TryInto; #[test] fn test_chacha20_256_tls_vectors() { @@ -592,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); + } }