From: Matt Corallo Date: Mon, 15 Jan 2024 01:25:10 +0000 (+0000) Subject: Move cryptographic algorithms and utilities to a new `crypto` mod X-Git-Tag: v0.0.120~2^2 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=refs%2Fheads%2F2024-01-crypto-module;p=rust-lightning Move cryptographic algorithms and utilities to a new `crypto` mod As we'd generally like the `lightning` crate to, over time, have more modules rather than being very monolithic, we should move the cryptographic things into their own module, which we do here. We also take this opportunity to move stream adapters into their own module and make clear that the ChaChaPoly `decrypt` method is variable time. --- diff --git a/lightning/src/blinded_path/message.rs b/lightning/src/blinded_path/message.rs index 2631a05f6..3a5541fa1 100644 --- a/lightning/src/blinded_path/message.rs +++ b/lightning/src/blinded_path/message.rs @@ -8,7 +8,7 @@ use crate::ln::onion_utils; use crate::onion_message::packet::ControlTlvs; use crate::prelude::*; use crate::sign::{NodeSigner, Recipient}; -use crate::util::chacha20poly1305rfc::ChaChaPolyReadAdapter; +use crate::crypto::streams::ChaChaPolyReadAdapter; use crate::util::ser::{FixedLengthReader, LengthReadableArgs, Writeable, Writer}; use core::mem; diff --git a/lightning/src/blinded_path/utils.rs b/lightning/src/blinded_path/utils.rs index 7ddb39c1b..d4894a86a 100644 --- a/lightning/src/blinded_path/utils.rs +++ b/lightning/src/blinded_path/utils.rs @@ -19,7 +19,7 @@ use super::{BlindedHop, BlindedPath}; use crate::ln::msgs::DecodeError; use crate::ln::onion_utils; use crate::onion_message::messenger::Destination; -use crate::util::chacha20poly1305rfc::ChaChaPolyWriteAdapter; +use crate::crypto::streams::ChaChaPolyWriteAdapter; use crate::util::ser::{Readable, Writeable}; use crate::io; diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index bcc324a55..c81a48b78 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -4808,7 +4808,7 @@ mod tests { preimages_slice_to_htlcs!($preimages_slice).into_iter().map(|(htlc, _)| (htlc, None)).collect() } } - let dummy_sig = crate::util::crypto::sign(&secp_ctx, + let dummy_sig = crate::crypto::utils::sign(&secp_ctx, &bitcoin::secp256k1::Message::from_slice(&[42; 32]).unwrap(), &SecretKey::from_slice(&[42; 32]).unwrap()); diff --git a/lightning/src/crypto/chacha20.rs b/lightning/src/crypto/chacha20.rs new file mode 100644 index 000000000..d6fd3a7de --- /dev/null +++ b/lightning/src/crypto/chacha20.rs @@ -0,0 +1,686 @@ +// This file was stolen from rust-crypto. +// 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 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +#[cfg(not(fuzzing))] +mod real_chacha { + 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 ::core::ops::Add for u32x4 { + type Output = u32x4; + #[inline] + fn add(self, rhs: u32x4) -> u32x4 { + u32x4(self.0.wrapping_add(rhs.0), + self.1.wrapping_add(rhs.1), + self.2.wrapping_add(rhs.2), + self.3.wrapping_add(rhs.3)) + } + } + impl ::core::ops::Sub for u32x4 { + type Output = u32x4; + #[inline] + fn sub(self, rhs: u32x4) -> u32x4 { + u32x4(self.0.wrapping_sub(rhs.0), + self.1.wrapping_sub(rhs.1), + self.2.wrapping_sub(rhs.2), + self.3.wrapping_sub(rhs.3)) + } + } + impl ::core::ops::BitXor for u32x4 { + type Output = u32x4; + #[inline] + fn bitxor(self, rhs: u32x4) -> u32x4 { + u32x4(self.0 ^ rhs.0, self.1 ^ rhs.1, self.2 ^ rhs.2, self.3 ^ rhs.3) + } + } + impl ::core::ops::Shr for u32x4 { + type Output = u32x4; + #[inline] + fn shr(self, shr: u8) -> u32x4 { + u32x4(self.0 >> shr, self.1 >> shr, self.2 >> shr, self.3 >> shr) + } + } + impl ::core::ops::Shl for u32x4 { + type Output = u32x4; + #[inline] + fn shl(self, shl: u8) -> u32x4 { + u32x4(self.0 << shl, self.1 << shl, self.2 << shl, self.3 << shl) + } + } + impl u32x4 { + #[inline] + 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 { + a: u32x4, + b: u32x4, + c: u32x4, + d: u32x4 + } + + #[derive(Copy)] + pub struct ChaCha20 { + state : ChaChaState, + output : [u8; BLOCK_SIZE], + offset : usize, + } + + impl Clone for ChaCha20 { fn clone(&self) -> ChaCha20 { *self } } + + macro_rules! swizzle { + ($b: expr, $c: expr, $d: expr) => {{ + let u32x4(b10, b11, b12, b13) = $b; + $b = u32x4(b11, b12, b13, b10); + let u32x4(c10, c11, c12, c13) = $c; + $c = u32x4(c12, c13,c10, c11); + let u32x4(d10, d11, d12, d13) = $d; + $d = u32x4(d13, d10, d11, d12); + }} + } + + macro_rules! state_to_buffer { + ($state: expr, $output: expr) => {{ + let u32x4(a1, a2, a3, a4) = $state.a; + let u32x4(b1, b2, b3, b4) = $state.b; + let u32x4(c1, c2, c3, c4) = $state.c; + let u32x4(d1, d2, d3, d4) = $state.d; + let lens = [ + a1,a2,a3,a4, + b1,b2,b3,b4, + c1,c2,c3,c4, + d1,d2,d3,d4 + ]; + for i in 0..lens.len() { + $output[i*4..(i+1)*4].copy_from_slice(&lens[i].to_le_bytes()); + } + }} + } + + macro_rules! round{ + ($state: expr) => {{ + $state.a = $state.a + $state.b; + rotate!($state.d, $state.a, 16); + $state.c = $state.c + $state.d; + rotate!($state.b, $state.c, 12); + $state.a = $state.a + $state.b; + rotate!($state.d, $state.a, 8); + $state.c = $state.c + $state.d; + rotate!($state.b, $state.c, 7); + }} + } + + macro_rules! rotate { + ($a: expr, $b: expr, $rot: expr) => {{ + let v = $a ^ $b; + let r = 32 - $rot; + let right = v >> r; + $a = (v << $rot) ^ right + }} + } + + impl ChaCha20 { + pub fn new(key: &[u8], nonce: &[u8]) -> ChaCha20 { + assert!(key.len() == 16 || key.len() == 32); + assert!(nonce.len() == 8 || nonce.len() == 12); + + 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", + 32 => b"expand 32-byte k", + _ => unreachable!(), + }; + ChaChaState { + a: u32x4::from_bytes(&constant[0..16]), + b: u32x4::from_bytes(&key[0..16]), + c: if key.len() == 16 { + u32x4::from_bytes(&key[0..16]) + } else { + u32x4::from_bytes(&key[16..32]) + }, + d: if nonce.len() == 16 { + u32x4::from_bytes(&nonce[0..16]) + } else if nonce.len() == 12 { + let mut nonce4 = [0; 4*4]; + nonce4[4..].copy_from_slice(nonce); + u32x4::from_bytes(&nonce4) + } else { + let mut nonce4 = [0; 4*4]; + nonce4[8..].copy_from_slice(nonce); + u32x4::from_bytes(&nonce4) + } + } + } + + // put the the next BLOCK_SIZE keystream bytes into self.output + fn update(&mut self) { + let mut state = self.state; + + for _ in 0..10 { + round!(state); + swizzle!(state.b, state.c, state.d); + round!(state); + swizzle!(state.d, state.c, state.b); + } + state.a = state.a + self.state.a; + state.b = state.b + self.state.b; + state.c = state.c + self.state.c; + state.d = state.d + self.state.d; + + state_to_buffer!(state, self.output); + + self.state.d = self.state.d + u32x4(1, 0, 0, 0); + let u32x4(c12, _, _, _) = self.state.d; + if c12 == 0 { + // we could increment the other counter word with an 8 byte nonce + // but other implementations like boringssl have this same + // limitation + panic!("counter is exhausted"); + } + + self.offset = 0; + } + + #[inline] // Useful cause input may be 0s on stack that should be optimized out + pub fn process(&mut self, input: &[u8], output: &mut [u8]) { + assert!(input.len() == output.len()); + let len = input.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!(output.len() >= i + count); + assert!(input.len() >= i + count); + assert!(self.output.len() >= self.offset + count); + for j in 0..count { + output[i + j] = input[i + j] ^ self.output[self.offset + j]; + } + i += count; + 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(fuzzing))] +pub use self::real_chacha::ChaCha20; + +#[cfg(fuzzing)] +mod fuzzy_chacha { + pub struct ChaCha20 {} + + impl ChaCha20 { + pub fn new(key: &[u8], nonce: &[u8]) -> ChaCha20 { + assert!(key.len() == 16 || key.len() == 32); + assert!(nonce.len() == 8 || nonce.len() == 12); + 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(fuzzing)] +pub use self::fuzzy_chacha::ChaCha20; + +#[cfg(test)] +mod test { + use alloc::vec; + use alloc::vec::{Vec}; + use core::convert::TryInto; + use core::iter::repeat; + + use super::ChaCha20; + + #[test] + fn test_chacha20_256_tls_vectors() { + struct TestVector { + key: [u8; 32], + nonce: [u8; 8], + keystream: Vec, + } + // taken from http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04 + let test_vectors = vec!( + TestVector{ + key: [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ], + nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ], + keystream: vec!( + 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, + 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28, + 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, + 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7, + 0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d, + 0x77, 0x24, 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37, + 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c, + 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86, + ), + }, TestVector{ + key: [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + ], + nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ], + keystream: vec!( + 0x45, 0x40, 0xf0, 0x5a, 0x9f, 0x1f, 0xb2, 0x96, + 0xd7, 0x73, 0x6e, 0x7b, 0x20, 0x8e, 0x3c, 0x96, + 0xeb, 0x4f, 0xe1, 0x83, 0x46, 0x88, 0xd2, 0x60, + 0x4f, 0x45, 0x09, 0x52, 0xed, 0x43, 0x2d, 0x41, + 0xbb, 0xe2, 0xa0, 0xb6, 0xea, 0x75, 0x66, 0xd2, + 0xa5, 0xd1, 0xe7, 0xe2, 0x0d, 0x42, 0xaf, 0x2c, + 0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81, + 0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63, + ), + }, TestVector{ + key: [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ], + nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ], + keystream: vec!( + 0xde, 0x9c, 0xba, 0x7b, 0xf3, 0xd6, 0x9e, 0xf5, + 0xe7, 0x86, 0xdc, 0x63, 0x97, 0x3f, 0x65, 0x3a, + 0x0b, 0x49, 0xe0, 0x15, 0xad, 0xbf, 0xf7, 0x13, + 0x4f, 0xcb, 0x7d, 0xf1, 0x37, 0x82, 0x10, 0x31, + 0xe8, 0x5a, 0x05, 0x02, 0x78, 0xa7, 0x08, 0x45, + 0x27, 0x21, 0x4f, 0x73, 0xef, 0xc7, 0xfa, 0x5b, + 0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e, + 0x44, 0x5f, 0x41, 0xe3, + ), + }, TestVector{ + key: [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ], + nonce: [ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ], + keystream: vec!( + 0xef, 0x3f, 0xdf, 0xd6, 0xc6, 0x15, 0x78, 0xfb, + 0xf5, 0xcf, 0x35, 0xbd, 0x3d, 0xd3, 0x3b, 0x80, + 0x09, 0x63, 0x16, 0x34, 0xd2, 0x1e, 0x42, 0xac, + 0x33, 0x96, 0x0b, 0xd1, 0x38, 0xe5, 0x0d, 0x32, + 0x11, 0x1e, 0x4c, 0xaf, 0x23, 0x7e, 0xe5, 0x3c, + 0xa8, 0xad, 0x64, 0x26, 0x19, 0x4a, 0x88, 0x54, + 0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d, + 0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b, + ), + }, TestVector{ + 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, + ], + nonce: [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 ], + keystream: vec!( + 0xf7, 0x98, 0xa1, 0x89, 0xf1, 0x95, 0xe6, 0x69, + 0x82, 0x10, 0x5f, 0xfb, 0x64, 0x0b, 0xb7, 0x75, + 0x7f, 0x57, 0x9d, 0xa3, 0x16, 0x02, 0xfc, 0x93, + 0xec, 0x01, 0xac, 0x56, 0xf8, 0x5a, 0xc3, 0xc1, + 0x34, 0xa4, 0x54, 0x7b, 0x73, 0x3b, 0x46, 0x41, + 0x30, 0x42, 0xc9, 0x44, 0x00, 0x49, 0x17, 0x69, + 0x05, 0xd3, 0xbe, 0x59, 0xea, 0x1c, 0x53, 0xf1, + 0x59, 0x16, 0x15, 0x5c, 0x2b, 0xe8, 0x24, 0x1a, + 0x38, 0x00, 0x8b, 0x9a, 0x26, 0xbc, 0x35, 0x94, + 0x1e, 0x24, 0x44, 0x17, 0x7c, 0x8a, 0xde, 0x66, + 0x89, 0xde, 0x95, 0x26, 0x49, 0x86, 0xd9, 0x58, + 0x89, 0xfb, 0x60, 0xe8, 0x46, 0x29, 0xc9, 0xbd, + 0x9a, 0x5a, 0xcb, 0x1c, 0xc1, 0x18, 0xbe, 0x56, + 0x3e, 0xb9, 0xb3, 0xa4, 0xa4, 0x72, 0xf8, 0x2e, + 0x09, 0xa7, 0xe7, 0x78, 0x49, 0x2b, 0x56, 0x2e, + 0xf7, 0x13, 0x0e, 0x88, 0xdf, 0xe0, 0x31, 0xc7, + 0x9d, 0xb9, 0xd4, 0xf7, 0xc7, 0xa8, 0x99, 0x15, + 0x1b, 0x9a, 0x47, 0x50, 0x32, 0xb6, 0x3f, 0xc3, + 0x85, 0x24, 0x5f, 0xe0, 0x54, 0xe3, 0xdd, 0x5a, + 0x97, 0xa5, 0xf5, 0x76, 0xfe, 0x06, 0x40, 0x25, + 0xd3, 0xce, 0x04, 0x2c, 0x56, 0x6a, 0xb2, 0xc5, + 0x07, 0xb1, 0x38, 0xdb, 0x85, 0x3e, 0x3d, 0x69, + 0x59, 0x66, 0x09, 0x96, 0x54, 0x6c, 0xc9, 0xc4, + 0xa6, 0xea, 0xfd, 0xc7, 0x77, 0xc0, 0x40, 0xd7, + 0x0e, 0xaf, 0x46, 0xf7, 0x6d, 0xad, 0x39, 0x79, + 0xe5, 0xc5, 0x36, 0x0c, 0x33, 0x17, 0x16, 0x6a, + 0x1c, 0x89, 0x4c, 0x94, 0xa3, 0x71, 0x87, 0x6a, + 0x94, 0xdf, 0x76, 0x28, 0xfe, 0x4e, 0xaa, 0xf2, + 0xcc, 0xb2, 0x7d, 0x5a, 0xaa, 0xe0, 0xad, 0x7a, + 0xd0, 0xf9, 0xd4, 0xb6, 0xad, 0x3b, 0x54, 0x09, + 0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a, + 0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9, + ), + }, + ); + + for tv in test_vectors.iter() { + let mut c = ChaCha20::new(&tv.key, &tv.nonce); + let input: Vec = repeat(0).take(tv.keystream.len()).collect(); + let mut output: Vec = repeat(0).take(input.len()).collect(); + c.process(&input[..], &mut output[..]); + assert_eq!(output, tv.keystream); + } + } + + #[test] + fn test_chacha20_256_tls_vectors_96_nonce() { + struct TestVector { + key: [u8; 32], + nonce: [u8; 12], + keystream: Vec, + } + // taken from http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04 + let test_vectors = vec!( + TestVector{ + key: [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ], + nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ], + keystream: vec!( + 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, + 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28, + 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, + 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7, + 0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d, + 0x77, 0x24, 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37, + 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c, + 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86, + ), + }, TestVector{ + key: [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + ], + nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ], + keystream: vec!( + 0x45, 0x40, 0xf0, 0x5a, 0x9f, 0x1f, 0xb2, 0x96, + 0xd7, 0x73, 0x6e, 0x7b, 0x20, 0x8e, 0x3c, 0x96, + 0xeb, 0x4f, 0xe1, 0x83, 0x46, 0x88, 0xd2, 0x60, + 0x4f, 0x45, 0x09, 0x52, 0xed, 0x43, 0x2d, 0x41, + 0xbb, 0xe2, 0xa0, 0xb6, 0xea, 0x75, 0x66, 0xd2, + 0xa5, 0xd1, 0xe7, 0xe2, 0x0d, 0x42, 0xaf, 0x2c, + 0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81, + 0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63, + ), + }, TestVector{ + key: [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ], + nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ], + keystream: vec!( + 0xde, 0x9c, 0xba, 0x7b, 0xf3, 0xd6, 0x9e, 0xf5, + 0xe7, 0x86, 0xdc, 0x63, 0x97, 0x3f, 0x65, 0x3a, + 0x0b, 0x49, 0xe0, 0x15, 0xad, 0xbf, 0xf7, 0x13, + 0x4f, 0xcb, 0x7d, 0xf1, 0x37, 0x82, 0x10, 0x31, + 0xe8, 0x5a, 0x05, 0x02, 0x78, 0xa7, 0x08, 0x45, + 0x27, 0x21, 0x4f, 0x73, 0xef, 0xc7, 0xfa, 0x5b, + 0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e, + 0x44, 0x5f, 0x41, 0xe3, + ), + }, TestVector{ + key: [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ], + nonce: [ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ], + keystream: vec!( + 0xef, 0x3f, 0xdf, 0xd6, 0xc6, 0x15, 0x78, 0xfb, + 0xf5, 0xcf, 0x35, 0xbd, 0x3d, 0xd3, 0x3b, 0x80, + 0x09, 0x63, 0x16, 0x34, 0xd2, 0x1e, 0x42, 0xac, + 0x33, 0x96, 0x0b, 0xd1, 0x38, 0xe5, 0x0d, 0x32, + 0x11, 0x1e, 0x4c, 0xaf, 0x23, 0x7e, 0xe5, 0x3c, + 0xa8, 0xad, 0x64, 0x26, 0x19, 0x4a, 0x88, 0x54, + 0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d, + 0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b, + ), + }, TestVector{ + 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, + ], + nonce: [0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 ], + keystream: vec!( + 0xf7, 0x98, 0xa1, 0x89, 0xf1, 0x95, 0xe6, 0x69, + 0x82, 0x10, 0x5f, 0xfb, 0x64, 0x0b, 0xb7, 0x75, + 0x7f, 0x57, 0x9d, 0xa3, 0x16, 0x02, 0xfc, 0x93, + 0xec, 0x01, 0xac, 0x56, 0xf8, 0x5a, 0xc3, 0xc1, + 0x34, 0xa4, 0x54, 0x7b, 0x73, 0x3b, 0x46, 0x41, + 0x30, 0x42, 0xc9, 0x44, 0x00, 0x49, 0x17, 0x69, + 0x05, 0xd3, 0xbe, 0x59, 0xea, 0x1c, 0x53, 0xf1, + 0x59, 0x16, 0x15, 0x5c, 0x2b, 0xe8, 0x24, 0x1a, + 0x38, 0x00, 0x8b, 0x9a, 0x26, 0xbc, 0x35, 0x94, + 0x1e, 0x24, 0x44, 0x17, 0x7c, 0x8a, 0xde, 0x66, + 0x89, 0xde, 0x95, 0x26, 0x49, 0x86, 0xd9, 0x58, + 0x89, 0xfb, 0x60, 0xe8, 0x46, 0x29, 0xc9, 0xbd, + 0x9a, 0x5a, 0xcb, 0x1c, 0xc1, 0x18, 0xbe, 0x56, + 0x3e, 0xb9, 0xb3, 0xa4, 0xa4, 0x72, 0xf8, 0x2e, + 0x09, 0xa7, 0xe7, 0x78, 0x49, 0x2b, 0x56, 0x2e, + 0xf7, 0x13, 0x0e, 0x88, 0xdf, 0xe0, 0x31, 0xc7, + 0x9d, 0xb9, 0xd4, 0xf7, 0xc7, 0xa8, 0x99, 0x15, + 0x1b, 0x9a, 0x47, 0x50, 0x32, 0xb6, 0x3f, 0xc3, + 0x85, 0x24, 0x5f, 0xe0, 0x54, 0xe3, 0xdd, 0x5a, + 0x97, 0xa5, 0xf5, 0x76, 0xfe, 0x06, 0x40, 0x25, + 0xd3, 0xce, 0x04, 0x2c, 0x56, 0x6a, 0xb2, 0xc5, + 0x07, 0xb1, 0x38, 0xdb, 0x85, 0x3e, 0x3d, 0x69, + 0x59, 0x66, 0x09, 0x96, 0x54, 0x6c, 0xc9, 0xc4, + 0xa6, 0xea, 0xfd, 0xc7, 0x77, 0xc0, 0x40, 0xd7, + 0x0e, 0xaf, 0x46, 0xf7, 0x6d, 0xad, 0x39, 0x79, + 0xe5, 0xc5, 0x36, 0x0c, 0x33, 0x17, 0x16, 0x6a, + 0x1c, 0x89, 0x4c, 0x94, 0xa3, 0x71, 0x87, 0x6a, + 0x94, 0xdf, 0x76, 0x28, 0xfe, 0x4e, 0xaa, 0xf2, + 0xcc, 0xb2, 0x7d, 0x5a, 0xaa, 0xe0, 0xad, 0x7a, + 0xd0, 0xf9, 0xd4, 0xb6, 0xad, 0x3b, 0x54, 0x09, + 0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a, + 0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9, + ), + }, + ); + + for tv in test_vectors.iter() { + let mut c = ChaCha20::new(&tv.key, &tv.nonce); + let input: Vec = repeat(0).take(tv.keystream.len()).collect(); + let mut output: Vec = repeat(0).take(input.len()).collect(); + c.process(&input[..], &mut output[..]); + 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); + } +} diff --git a/lightning/src/crypto/chacha20poly1305rfc.rs b/lightning/src/crypto/chacha20poly1305rfc.rs new file mode 100644 index 000000000..4ca974421 --- /dev/null +++ b/lightning/src/crypto/chacha20poly1305rfc.rs @@ -0,0 +1,237 @@ +// ring has a garbage API so its use is avoided, but rust-crypto doesn't have RFC-variant poly1305 +// Instead, we steal rust-crypto's implementation and tweak it to match the RFC. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. +// +// This is a port of Andrew Moons poly1305-donna +// https://github.com/floodyberry/poly1305-donna + +#[cfg(not(fuzzing))] +mod real_chachapoly { + use super::super::chacha20::ChaCha20; + use super::super::poly1305::Poly1305; + use super::super::fixed_time_eq; + + #[derive(Clone, Copy)] + pub struct ChaCha20Poly1305RFC { + cipher: ChaCha20, + mac: Poly1305, + finished: bool, + data_len: usize, + aad_len: u64, + } + + impl ChaCha20Poly1305RFC { + #[inline] + fn pad_mac_16(mac: &mut Poly1305, len: usize) { + if len % 16 != 0 { + mac.input(&[0; 16][0..16 - (len % 16)]); + } + } + pub fn new(key: &[u8], nonce: &[u8], aad: &[u8]) -> ChaCha20Poly1305RFC { + assert!(key.len() == 16 || key.len() == 32); + assert!(nonce.len() == 12); + + // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant + assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0); + + let mut cipher = ChaCha20::new(key, &nonce[4..]); + let mut mac_key = [0u8; 64]; + let zero_key = [0u8; 64]; + cipher.process(&zero_key, &mut mac_key); + + let mut mac = Poly1305::new(&mac_key[..32]); + mac.input(aad); + ChaCha20Poly1305RFC::pad_mac_16(&mut mac, aad.len()); + + ChaCha20Poly1305RFC { + cipher, + mac, + finished: false, + data_len: 0, + aad_len: aad.len() as u64, + } + } + + pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) { + assert!(input.len() == output.len()); + assert!(self.finished == false); + self.cipher.process(input, output); + self.data_len += input.len(); + self.mac.input(output); + ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len); + self.finished = true; + self.mac.input(&self.aad_len.to_le_bytes()); + self.mac.input(&(self.data_len as u64).to_le_bytes()); + self.mac.raw_result(out_tag); + } + + pub fn encrypt_full_message_in_place(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) { + self.encrypt_in_place(input_output); + self.finish_and_get_tag(out_tag); + } + + // Encrypt `input_output` in-place. To finish and calculate the tag, use `finish_and_get_tag` + // below. + pub(in super::super) fn encrypt_in_place(&mut self, input_output: &mut [u8]) { + debug_assert!(self.finished == false); + self.cipher.process_in_place(input_output); + self.data_len += input_output.len(); + self.mac.input(input_output); + } + + // If we were previously encrypting with `encrypt_in_place`, this method can be used to finish + // encrypting and calculate the tag. + pub(in super::super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) { + debug_assert!(self.finished == false); + ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len); + self.finished = true; + self.mac.input(&self.aad_len.to_le_bytes()); + self.mac.input(&(self.data_len as u64).to_le_bytes()); + self.mac.raw_result(out_tag); + } + + /// Decrypt the `input`, checking the given `tag` prior to writing the decrypted contents + /// into `output`. Note that, because `output` is not touched until the `tag` is checked, + /// this decryption is *variable time*. + pub fn variable_time_decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> Result<(), ()> { + assert!(input.len() == output.len()); + assert!(self.finished == false); + + self.finished = true; + + self.mac.input(input); + + self.data_len += input.len(); + ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len); + self.mac.input(&self.aad_len.to_le_bytes()); + self.mac.input(&(self.data_len as u64).to_le_bytes()); + + let mut calc_tag = [0u8; 16]; + self.mac.raw_result(&mut calc_tag); + if fixed_time_eq(&calc_tag, tag) { + self.cipher.process(input, output); + Ok(()) + } else { + Err(()) + } + } + + pub fn check_decrypt_in_place(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> { + self.decrypt_in_place(input_output); + if self.finish_and_check_tag(tag) { Ok(()) } else { Err(()) } + } + + /// Decrypt in place, without checking the tag. Use `finish_and_check_tag` to check it + /// later when decryption finishes. + /// + /// Should never be `pub` because the public API should always enforce tag checking. + pub(in super::super) fn decrypt_in_place(&mut self, input_output: &mut [u8]) { + debug_assert!(self.finished == false); + self.mac.input(input_output); + self.data_len += input_output.len(); + self.cipher.process_in_place(input_output); + } + + /// If we were previously decrypting with `just_decrypt_in_place`, this method must be used + /// to check the tag. Returns whether or not the tag is valid. + pub(in super::super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool { + debug_assert!(self.finished == false); + self.finished = true; + ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len); + self.mac.input(&self.aad_len.to_le_bytes()); + self.mac.input(&(self.data_len as u64).to_le_bytes()); + + let mut calc_tag = [0u8; 16]; + self.mac.raw_result(&mut calc_tag); + if fixed_time_eq(&calc_tag, tag) { + true + } else { + false + } + } + } +} +#[cfg(not(fuzzing))] +pub use self::real_chachapoly::ChaCha20Poly1305RFC; + +#[cfg(fuzzing)] +mod fuzzy_chachapoly { + #[derive(Clone, Copy)] + pub struct ChaCha20Poly1305RFC { + tag: [u8; 16], + finished: bool, + } + impl ChaCha20Poly1305RFC { + pub fn new(key: &[u8], nonce: &[u8], _aad: &[u8]) -> ChaCha20Poly1305RFC { + assert!(key.len() == 16 || key.len() == 32); + assert!(nonce.len() == 12); + + // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant + assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0); + + let mut tag = [0; 16]; + tag.copy_from_slice(&key[0..16]); + + ChaCha20Poly1305RFC { + tag, + finished: false, + } + } + + pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) { + assert!(input.len() == output.len()); + assert!(self.finished == false); + + output.copy_from_slice(&input); + out_tag.copy_from_slice(&self.tag); + self.finished = true; + } + + pub fn encrypt_full_message_in_place(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) { + self.encrypt_in_place(input_output); + self.finish_and_get_tag(out_tag); + } + + pub(in super::super) fn encrypt_in_place(&mut self, _input_output: &mut [u8]) { + assert!(self.finished == false); + } + + pub(in super::super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) { + assert!(self.finished == false); + out_tag.copy_from_slice(&self.tag); + self.finished = true; + } + + pub fn variable_time_decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> Result<(), ()> { + assert!(input.len() == output.len()); + assert!(self.finished == false); + + if tag[..] != self.tag[..] { return Err(()); } + output.copy_from_slice(input); + self.finished = true; + Ok(()) + } + + pub fn check_decrypt_in_place(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> { + self.decrypt_in_place(input_output); + if self.finish_and_check_tag(tag) { Ok(()) } else { Err(()) } + } + + pub(in super::super) fn decrypt_in_place(&mut self, _input: &mut [u8]) { + assert!(self.finished == false); + } + + pub(in super::super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool { + if tag[..] != self.tag[..] { return false; } + self.finished = true; + true + } + } +} +#[cfg(fuzzing)] +pub use self::fuzzy_chachapoly::ChaCha20Poly1305RFC; diff --git a/lightning/src/crypto/mod.rs b/lightning/src/crypto/mod.rs new file mode 100644 index 000000000..6efd12066 --- /dev/null +++ b/lightning/src/crypto/mod.rs @@ -0,0 +1,8 @@ +use bitcoin::hashes::cmp::fixed_time_eq; + +pub(crate) mod chacha20; +#[cfg(not(fuzzing))] +pub(crate) mod poly1305; +pub(crate) mod chacha20poly1305rfc; +pub(crate) mod streams; +pub(crate) mod utils; diff --git a/lightning/src/crypto/poly1305.rs b/lightning/src/crypto/poly1305.rs new file mode 100644 index 000000000..a1b9fbac5 --- /dev/null +++ b/lightning/src/crypto/poly1305.rs @@ -0,0 +1,341 @@ +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +// This is a port of Andrew Moons poly1305-donna +// https://github.com/floodyberry/poly1305-donna + +use core::cmp::min; +use core::convert::TryInto; + +#[derive(Clone, Copy)] +pub struct Poly1305 { + r : [u32; 5], + h : [u32; 5], + pad : [u32; 4], + leftover : usize, + buffer : [u8; 16], + finalized : bool, +} + +impl Poly1305 { + pub fn new(key: &[u8]) -> Poly1305 { + assert!(key.len() == 32); + let mut poly = Poly1305{ r: [0u32; 5], h: [0u32; 5], pad: [0u32; 4], leftover: 0, buffer: [0u8; 16], finalized: false }; + + // r &= 0xffffffc0ffffffc0ffffffc0fffffff + poly.r[0] = (u32::from_le_bytes(key[ 0.. 4].try_into().expect("len is 4")) ) & 0x3ffffff; + poly.r[1] = (u32::from_le_bytes(key[ 3.. 7].try_into().expect("len is 4")) >> 2) & 0x3ffff03; + poly.r[2] = (u32::from_le_bytes(key[ 6..10].try_into().expect("len is 4")) >> 4) & 0x3ffc0ff; + poly.r[3] = (u32::from_le_bytes(key[ 9..13].try_into().expect("len is 4")) >> 6) & 0x3f03fff; + poly.r[4] = (u32::from_le_bytes(key[12..16].try_into().expect("len is 4")) >> 8) & 0x00fffff; + + poly.pad[0] = u32::from_le_bytes(key[16..20].try_into().expect("len is 4")); + poly.pad[1] = u32::from_le_bytes(key[20..24].try_into().expect("len is 4")); + poly.pad[2] = u32::from_le_bytes(key[24..28].try_into().expect("len is 4")); + poly.pad[3] = u32::from_le_bytes(key[28..32].try_into().expect("len is 4")); + + poly + } + + fn block(&mut self, m: &[u8]) { + let hibit : u32 = if self.finalized { 0 } else { 1 << 24 }; + + let r0 = self.r[0]; + let r1 = self.r[1]; + let r2 = self.r[2]; + let r3 = self.r[3]; + let r4 = self.r[4]; + + let s1 = r1 * 5; + let s2 = r2 * 5; + let s3 = r3 * 5; + let s4 = r4 * 5; + + let mut h0 = self.h[0]; + let mut h1 = self.h[1]; + let mut h2 = self.h[2]; + let mut h3 = self.h[3]; + let mut h4 = self.h[4]; + + // h += m + h0 += (u32::from_le_bytes(m[ 0.. 4].try_into().expect("len is 4")) ) & 0x3ffffff; + h1 += (u32::from_le_bytes(m[ 3.. 7].try_into().expect("len is 4")) >> 2) & 0x3ffffff; + h2 += (u32::from_le_bytes(m[ 6..10].try_into().expect("len is 4")) >> 4) & 0x3ffffff; + h3 += (u32::from_le_bytes(m[ 9..13].try_into().expect("len is 4")) >> 6) & 0x3ffffff; + h4 += (u32::from_le_bytes(m[12..16].try_into().expect("len is 4")) >> 8) | hibit; + + // h *= r + let d0 = (h0 as u64 * r0 as u64) + (h1 as u64 * s4 as u64) + (h2 as u64 * s3 as u64) + (h3 as u64 * s2 as u64) + (h4 as u64 * s1 as u64); + let mut d1 = (h0 as u64 * r1 as u64) + (h1 as u64 * r0 as u64) + (h2 as u64 * s4 as u64) + (h3 as u64 * s3 as u64) + (h4 as u64 * s2 as u64); + let mut d2 = (h0 as u64 * r2 as u64) + (h1 as u64 * r1 as u64) + (h2 as u64 * r0 as u64) + (h3 as u64 * s4 as u64) + (h4 as u64 * s3 as u64); + let mut d3 = (h0 as u64 * r3 as u64) + (h1 as u64 * r2 as u64) + (h2 as u64 * r1 as u64) + (h3 as u64 * r0 as u64) + (h4 as u64 * s4 as u64); + let mut d4 = (h0 as u64 * r4 as u64) + (h1 as u64 * r3 as u64) + (h2 as u64 * r2 as u64) + (h3 as u64 * r1 as u64) + (h4 as u64 * r0 as u64); + + // (partial) h %= p + let mut c : u32; + c = (d0 >> 26) as u32; h0 = d0 as u32 & 0x3ffffff; + d1 += c as u64; c = (d1 >> 26) as u32; h1 = d1 as u32 & 0x3ffffff; + d2 += c as u64; c = (d2 >> 26) as u32; h2 = d2 as u32 & 0x3ffffff; + d3 += c as u64; c = (d3 >> 26) as u32; h3 = d3 as u32 & 0x3ffffff; + d4 += c as u64; c = (d4 >> 26) as u32; h4 = d4 as u32 & 0x3ffffff; + h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff; + h1 += c; + + self.h[0] = h0; + self.h[1] = h1; + self.h[2] = h2; + self.h[3] = h3; + self.h[4] = h4; + } + + pub fn finish(&mut self) { + if self.leftover > 0 { + self.buffer[self.leftover] = 1; + for i in self.leftover+1..16 { + self.buffer[i] = 0; + } + self.finalized = true; + let tmp = self.buffer; + self.block(&tmp); + } + + // fully carry h + let mut h0 = self.h[0]; + let mut h1 = self.h[1]; + let mut h2 = self.h[2]; + let mut h3 = self.h[3]; + let mut h4 = self.h[4]; + + let mut c : u32; + c = h1 >> 26; h1 = h1 & 0x3ffffff; + h2 += c; c = h2 >> 26; h2 = h2 & 0x3ffffff; + h3 += c; c = h3 >> 26; h3 = h3 & 0x3ffffff; + h4 += c; c = h4 >> 26; h4 = h4 & 0x3ffffff; + h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff; + h1 += c; + + // compute h + -p + let mut g0 = h0.wrapping_add(5); c = g0 >> 26; g0 &= 0x3ffffff; + let mut g1 = h1.wrapping_add(c); c = g1 >> 26; g1 &= 0x3ffffff; + let mut g2 = h2.wrapping_add(c); c = g2 >> 26; g2 &= 0x3ffffff; + let mut g3 = h3.wrapping_add(c); c = g3 >> 26; g3 &= 0x3ffffff; + let mut g4 = h4.wrapping_add(c).wrapping_sub(1 << 26); + + // select h if h < p, or h + -p if h >= p + let mut mask = (g4 >> (32 - 1)).wrapping_sub(1); + g0 &= mask; + g1 &= mask; + g2 &= mask; + g3 &= mask; + g4 &= mask; + mask = !mask; + h0 = (h0 & mask) | g0; + h1 = (h1 & mask) | g1; + h2 = (h2 & mask) | g2; + h3 = (h3 & mask) | g3; + h4 = (h4 & mask) | g4; + + // h = h % (2^128) + h0 = ((h0 ) | (h1 << 26)) & 0xffffffff; + h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff; + h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff; + h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff; + + // h = mac = (h + pad) % (2^128) + let mut f : u64; + f = h0 as u64 + self.pad[0] as u64 ; h0 = f as u32; + f = h1 as u64 + self.pad[1] as u64 + (f >> 32); h1 = f as u32; + f = h2 as u64 + self.pad[2] as u64 + (f >> 32); h2 = f as u32; + f = h3 as u64 + self.pad[3] as u64 + (f >> 32); h3 = f as u32; + + self.h[0] = h0; + self.h[1] = h1; + self.h[2] = h2; + self.h[3] = h3; + } + + pub fn input(&mut self, data: &[u8]) { + assert!(!self.finalized); + let mut m = data; + + if self.leftover > 0 { + let want = min(16 - self.leftover, m.len()); + for i in 0..want { + self.buffer[self.leftover+i] = m[i]; + } + m = &m[want..]; + self.leftover += want; + + if self.leftover < 16 { + return; + } + + // self.block(self.buffer[..]); + let tmp = self.buffer; + self.block(&tmp); + + self.leftover = 0; + } + + while m.len() >= 16 { + self.block(&m[0..16]); + m = &m[16..]; + } + + for i in 0..m.len() { + self.buffer[i] = m[i]; + } + self.leftover = m.len(); + } + + pub fn raw_result(&mut self, output: &mut [u8]) { + assert!(output.len() >= 16); + if !self.finalized{ + self.finish(); + } + output[0..4].copy_from_slice(&self.h[0].to_le_bytes()); + output[4..8].copy_from_slice(&self.h[1].to_le_bytes()); + output[8..12].copy_from_slice(&self.h[2].to_le_bytes()); + output[12..16].copy_from_slice(&self.h[3].to_le_bytes()); + } +} + +#[cfg(test)] +mod test { + use core::iter::repeat; + use alloc::vec::Vec; + + use super::Poly1305; + + fn poly1305(key: &[u8], msg: &[u8], mac: &mut [u8]) { + let mut poly = Poly1305::new(key); + poly.input(msg); + poly.raw_result(mac); + } + + #[test] + fn test_nacl_vector() { + let key = [ + 0xee,0xa6,0xa7,0x25,0x1c,0x1e,0x72,0x91, + 0x6d,0x11,0xc2,0xcb,0x21,0x4d,0x3c,0x25, + 0x25,0x39,0x12,0x1d,0x8e,0x23,0x4e,0x65, + 0x2d,0x65,0x1f,0xa4,0xc8,0xcf,0xf8,0x80, + ]; + + let msg = [ + 0x8e,0x99,0x3b,0x9f,0x48,0x68,0x12,0x73, + 0xc2,0x96,0x50,0xba,0x32,0xfc,0x76,0xce, + 0x48,0x33,0x2e,0xa7,0x16,0x4d,0x96,0xa4, + 0x47,0x6f,0xb8,0xc5,0x31,0xa1,0x18,0x6a, + 0xc0,0xdf,0xc1,0x7c,0x98,0xdc,0xe8,0x7b, + 0x4d,0xa7,0xf0,0x11,0xec,0x48,0xc9,0x72, + 0x71,0xd2,0xc2,0x0f,0x9b,0x92,0x8f,0xe2, + 0x27,0x0d,0x6f,0xb8,0x63,0xd5,0x17,0x38, + 0xb4,0x8e,0xee,0xe3,0x14,0xa7,0xcc,0x8a, + 0xb9,0x32,0x16,0x45,0x48,0xe5,0x26,0xae, + 0x90,0x22,0x43,0x68,0x51,0x7a,0xcf,0xea, + 0xbd,0x6b,0xb3,0x73,0x2b,0xc0,0xe9,0xda, + 0x99,0x83,0x2b,0x61,0xca,0x01,0xb6,0xde, + 0x56,0x24,0x4a,0x9e,0x88,0xd5,0xf9,0xb3, + 0x79,0x73,0xf6,0x22,0xa4,0x3d,0x14,0xa6, + 0x59,0x9b,0x1f,0x65,0x4c,0xb4,0x5a,0x74, + 0xe3,0x55,0xa5, + ]; + + let expected = [ + 0xf3,0xff,0xc7,0x70,0x3f,0x94,0x00,0xe5, + 0x2a,0x7d,0xfb,0x4b,0x3d,0x33,0x05,0xd9, + ]; + + let mut mac = [0u8; 16]; + poly1305(&key, &msg, &mut mac); + assert_eq!(&mac[..], &expected[..]); + + let mut poly = Poly1305::new(&key); + poly.input(&msg[0..32]); + poly.input(&msg[32..96]); + poly.input(&msg[96..112]); + poly.input(&msg[112..120]); + poly.input(&msg[120..124]); + poly.input(&msg[124..126]); + poly.input(&msg[126..127]); + poly.input(&msg[127..128]); + poly.input(&msg[128..129]); + poly.input(&msg[129..130]); + poly.input(&msg[130..131]); + poly.raw_result(&mut mac); + assert_eq!(&mac[..], &expected[..]); + } + + #[test] + fn donna_self_test() { + let wrap_key = [ + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + let wrap_msg = [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + ]; + + let wrap_mac = [ + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + let mut mac = [0u8; 16]; + poly1305(&wrap_key, &wrap_msg, &mut mac); + assert_eq!(&mac[..], &wrap_mac[..]); + + let total_key = [ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xff, + 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + ]; + + let total_mac = [ + 0x64, 0xaf, 0xe2, 0xe8, 0xd6, 0xad, 0x7b, 0xbd, + 0xd2, 0x87, 0xf9, 0x7c, 0x44, 0x62, 0x3d, 0x39, + ]; + + let mut tpoly = Poly1305::new(&total_key); + for i in 0..256 { + let key: Vec = repeat(i as u8).take(32).collect(); + let msg: Vec = repeat(i as u8).take(256).collect(); + let mut mac = [0u8; 16]; + poly1305(&key[..], &msg[0..i], &mut mac); + tpoly.input(&mac); + } + tpoly.raw_result(&mut mac); + assert_eq!(&mac[..], &total_mac[..]); + } + + #[test] + fn test_tls_vectors() { + // from http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04 + let key = b"this is 32-byte key for Poly1305"; + let msg = [0u8; 32]; + let expected = [ + 0x49, 0xec, 0x78, 0x09, 0x0e, 0x48, 0x1e, 0xc6, + 0xc2, 0x6b, 0x33, 0xb9, 0x1c, 0xcc, 0x03, 0x07, + ]; + let mut mac = [0u8; 16]; + poly1305(key, &msg, &mut mac); + assert_eq!(&mac[..], &expected[..]); + + let msg = b"Hello world!"; + let expected= [ + 0xa6, 0xf7, 0x45, 0x00, 0x8f, 0x81, 0xc9, 0x16, + 0xa2, 0x0d, 0xcc, 0x74, 0xee, 0xf2, 0xb2, 0xf0, + ]; + poly1305(key, msg, &mut mac); + assert_eq!(&mac[..], &expected[..]); + } +} diff --git a/lightning/src/crypto/streams.rs b/lightning/src/crypto/streams.rs new file mode 100644 index 000000000..14921a386 --- /dev/null +++ b/lightning/src/crypto/streams.rs @@ -0,0 +1,211 @@ +use crate::crypto::chacha20::ChaCha20; +use crate::crypto::chacha20poly1305rfc::ChaCha20Poly1305RFC; + +use crate::ln::msgs::DecodeError; +use crate::util::ser::{FixedLengthReader, LengthRead, LengthReadableArgs, Readable, Writeable, Writer}; +use crate::io::{self, Read, Write}; + +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 { + let res = self.read.read(dest)?; + if res > 0 { + self.chacha.process_in_place(&mut dest[0..res]); + } + Ok(res) + } +} + +/// Enables the use of the serialization macros for objects that need to be simultaneously encrypted and +/// serialized. This allows us to avoid an intermediate Vec allocation. +pub(crate) struct ChaChaPolyWriteAdapter<'a, W: Writeable> { + pub rho: [u8; 32], + pub writeable: &'a W, +} + +impl<'a, W: Writeable> ChaChaPolyWriteAdapter<'a, W> { + #[allow(unused)] // This will be used for onion messages soon + pub fn new(rho: [u8; 32], writeable: &'a W) -> ChaChaPolyWriteAdapter<'a, W> { + Self { rho, writeable } + } +} + +impl<'a, T: Writeable> Writeable for ChaChaPolyWriteAdapter<'a, T> { + // Simultaneously write and encrypt Self::writeable. + fn write(&self, w: &mut W) -> Result<(), io::Error> { + let mut chacha = ChaCha20Poly1305RFC::new(&self.rho, &[0; 12], &[]); + let mut chacha_stream = ChaChaPolyWriter { chacha: &mut chacha, write: w }; + self.writeable.write(&mut chacha_stream)?; + let mut tag = [0 as u8; 16]; + chacha.finish_and_get_tag(&mut tag); + tag.write(w)?; + + Ok(()) + } +} + +/// Enables the use of the serialization macros for objects that need to be simultaneously decrypted and +/// deserialized. This allows us to avoid an intermediate Vec allocation. +pub(crate) struct ChaChaPolyReadAdapter { + pub readable: R, +} + +impl LengthReadableArgs<[u8; 32]> for ChaChaPolyReadAdapter { + // Simultaneously read and decrypt an object from a LengthRead, storing it in Self::readable. + // LengthRead must be used instead of std::io::Read because we need the total length to separate + // out the tag at the end. + fn read(mut r: &mut R, secret: [u8; 32]) -> Result { + if r.total_bytes() < 16 { return Err(DecodeError::InvalidValue) } + + let mut chacha = ChaCha20Poly1305RFC::new(&secret, &[0; 12], &[]); + let decrypted_len = r.total_bytes() - 16; + let s = FixedLengthReader::new(&mut r, decrypted_len); + let mut chacha_stream = ChaChaPolyReader { chacha: &mut chacha, read: s }; + let readable: T = Readable::read(&mut chacha_stream)?; + chacha_stream.read.eat_remaining()?; + + let mut tag = [0 as u8; 16]; + r.read_exact(&mut tag)?; + if !chacha.finish_and_check_tag(&tag) { + return Err(DecodeError::InvalidValue) + } + + Ok(Self { readable }) + } +} + + +/// Enables simultaneously reading and decrypting a ChaCha20Poly1305RFC stream from a std::io::Read. +struct ChaChaPolyReader<'a, R: Read> { + pub chacha: &'a mut ChaCha20Poly1305RFC, + pub read: R, +} + +impl<'a, R: Read> Read for ChaChaPolyReader<'a, R> { + // Decrypt bytes from Self::read into `dest`. + // `ChaCha20Poly1305RFC::finish_and_check_tag` must be called to check the tag after all reads + // complete. + fn read(&mut self, dest: &mut [u8]) -> Result { + let res = self.read.read(dest)?; + if res > 0 { + self.chacha.decrypt_in_place(&mut dest[0..res]); + } + Ok(res) + } +} + +/// Enables simultaneously writing and encrypting a byte stream into a Writer. +struct ChaChaPolyWriter<'a, W: Writer> { + pub chacha: &'a mut ChaCha20Poly1305RFC, + pub write: &'a mut W, +} + +impl<'a, W: Writer> Writer for ChaChaPolyWriter<'a, W> { + // Encrypt then write bytes from `src` into Self::write. + // `ChaCha20Poly1305RFC::finish_and_get_tag` can be called to retrieve the tag after all writes + // complete. + fn write_all(&mut self, src: &[u8]) -> Result<(), io::Error> { + let mut src_idx = 0; + while src_idx < src.len() { + let mut write_buffer = [0; 8192]; + let bytes_written = (&mut write_buffer[..]).write(&src[src_idx..]).expect("In-memory writes can't fail"); + self.chacha.encrypt_in_place(&mut write_buffer[..bytes_written]); + self.write.write_all(&write_buffer[..bytes_written])?; + src_idx += bytes_written; + } + Ok(()) + } +} + + +#[cfg(test)] +mod tests { + use crate::ln::msgs::DecodeError; + use super::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter}; + use crate::util::ser::{self, FixedLengthReader, LengthReadableArgs, Writeable}; + + // Used for for testing various lengths of serialization. + #[derive(Debug, PartialEq, Eq)] + struct TestWriteable { + field1: Vec, + field2: Vec, + field3: Vec, + } + impl_writeable_tlv_based!(TestWriteable, { + (1, field1, required_vec), + (2, field2, required_vec), + (3, field3, required_vec), + }); + + #[test] + fn test_chacha_stream_adapters() { + // Check that ChaChaPolyReadAdapter and ChaChaPolyWriteAdapter correctly encode and decode an + // encrypted object. + macro_rules! check_object_read_write { + ($obj: expr) => { + // First, serialize the object, encrypted with ChaCha20Poly1305. + let rho = [42; 32]; + let writeable_len = $obj.serialized_length() as u64 + 16; + let write_adapter = ChaChaPolyWriteAdapter::new(rho, &$obj); + let encrypted_writeable_bytes = write_adapter.encode(); + let encrypted_writeable = &encrypted_writeable_bytes[..]; + + // Now deserialize the object back and make sure it matches the original. + let mut rd = FixedLengthReader::new(encrypted_writeable, writeable_len); + let read_adapter = >::read(&mut rd, rho).unwrap(); + assert_eq!($obj, read_adapter.readable); + }; + } + + // Try a big object that will require multiple write buffers. + let big_writeable = TestWriteable { + field1: vec![43], + field2: vec![44; 4192], + field3: vec![45; 4192 + 1], + }; + check_object_read_write!(big_writeable); + + // Try a small object that fits into one write buffer. + let small_writeable = TestWriteable { + field1: vec![43], + field2: vec![44], + field3: vec![45], + }; + check_object_read_write!(small_writeable); + } + + fn do_chacha_stream_adapters_ser_macros() -> Result<(), DecodeError> { + let writeable = TestWriteable { + field1: vec![43], + field2: vec![44; 4192], + field3: vec![45; 4192 + 1], + }; + + // First, serialize the object into a TLV stream, encrypted with ChaCha20Poly1305. + let rho = [42; 32]; + let write_adapter = ChaChaPolyWriteAdapter::new(rho, &writeable); + let mut writer = ser::VecWriter(Vec::new()); + encode_tlv_stream!(&mut writer, { + (1, write_adapter, required), + }); + + // Now deserialize the object back and make sure it matches the original. + let mut read_adapter: Option> = None; + decode_tlv_stream!(&writer.0[..], { + (1, read_adapter, (option: LengthReadableArgs, rho)), + }); + assert_eq!(writeable, read_adapter.unwrap().readable); + + Ok(()) + } + + #[test] + fn chacha_stream_adapters_ser_macros() { + // Test that our stream adapters work as expected with the TLV macros. + // This also serves to test the `option: $trait` variant of the `_decode_tlv` ser macro. + do_chacha_stream_adapters_ser_macros().unwrap() + } +} diff --git a/lightning/src/crypto/utils.rs b/lightning/src/crypto/utils.rs new file mode 100644 index 000000000..98963c7c2 --- /dev/null +++ b/lightning/src/crypto/utils.rs @@ -0,0 +1,83 @@ +use bitcoin::hashes::{Hash, HashEngine}; +use bitcoin::hashes::hmac::{Hmac, HmacEngine}; +use bitcoin::hashes::sha256::Hash as Sha256; +use bitcoin::secp256k1::{Message, Secp256k1, SecretKey, ecdsa::Signature, Signing}; + +use crate::sign::EntropySource; + +use core::ops::Deref; + +macro_rules! hkdf_extract_expand { + ($salt: expr, $ikm: expr) => {{ + let mut hmac = HmacEngine::::new($salt); + hmac.input($ikm); + let prk = Hmac::from_engine(hmac).to_byte_array(); + let mut hmac = HmacEngine::::new(&prk[..]); + hmac.input(&[1; 1]); + let t1 = Hmac::from_engine(hmac).to_byte_array(); + let mut hmac = HmacEngine::::new(&prk[..]); + hmac.input(&t1); + hmac.input(&[2; 1]); + (t1, Hmac::from_engine(hmac).to_byte_array(), prk) + }}; + ($salt: expr, $ikm: expr, 2) => {{ + let (k1, k2, _) = hkdf_extract_expand!($salt, $ikm); + (k1, k2) + }}; + ($salt: expr, $ikm: expr, 5) => {{ + let (k1, k2, prk) = hkdf_extract_expand!($salt, $ikm); + + let mut hmac = HmacEngine::::new(&prk[..]); + hmac.input(&k2); + hmac.input(&[3; 1]); + let k3 = Hmac::from_engine(hmac).to_byte_array(); + + let mut hmac = HmacEngine::::new(&prk[..]); + hmac.input(&k3); + hmac.input(&[4; 1]); + let k4 = Hmac::from_engine(hmac).to_byte_array(); + + let mut hmac = HmacEngine::::new(&prk[..]); + hmac.input(&k4); + hmac.input(&[5; 1]); + let k5 = Hmac::from_engine(hmac).to_byte_array(); + + (k1, k2, k3, k4, k5) + }} +} + +pub fn hkdf_extract_expand_twice(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) { + hkdf_extract_expand!(salt, ikm, 2) +} + +pub fn hkdf_extract_expand_5x(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) { + hkdf_extract_expand!(salt, ikm, 5) +} + +#[inline] +pub fn sign(ctx: &Secp256k1, msg: &Message, sk: &SecretKey) -> Signature { + #[cfg(feature = "grind_signatures")] + let sig = ctx.sign_ecdsa_low_r(msg, sk); + #[cfg(not(feature = "grind_signatures"))] + let sig = ctx.sign_ecdsa(msg, sk); + sig +} + +#[inline] +#[allow(unused_variables)] +pub fn sign_with_aux_rand( + ctx: &Secp256k1, msg: &Message, sk: &SecretKey, entropy_source: &ES +) -> Signature where ES::Target: EntropySource { + #[cfg(feature = "grind_signatures")] + let sig = loop { + let sig = ctx.sign_ecdsa_with_noncedata(msg, sk, &entropy_source.get_secure_random_bytes()); + if sig.serialize_compact()[0] < 0x80 { + break sig; + } + }; + #[cfg(all(not(feature = "grind_signatures"), not(feature = "_test_vectors")))] + let sig = ctx.sign_ecdsa_with_noncedata(msg, sk, &entropy_source.get_secure_random_bytes()); + #[cfg(all(not(feature = "grind_signatures"), feature = "_test_vectors"))] + let sig = sign(ctx, msg, sk); + sig +} diff --git a/lightning/src/lib.rs b/lightning/src/lib.rs index 6eefb3983..160f63241 100644 --- a/lightning/src/lib.rs +++ b/lightning/src/lib.rs @@ -84,6 +84,8 @@ pub mod onion_message; pub mod blinded_path; pub mod events; +pub(crate) mod crypto; + #[cfg(feature = "std")] /// Re-export of either `core2::io` or `std::io`, depending on the `std` feature flag. pub use std::io; diff --git a/lightning/src/ln/chan_utils.rs b/lightning/src/ln/chan_utils.rs index 672e3aa86..18c4d8340 100644 --- a/lightning/src/ln/chan_utils.rs +++ b/lightning/src/ln/chan_utils.rs @@ -45,7 +45,7 @@ use crate::ln::channel::{INITIAL_COMMITMENT_NUMBER, ANCHOR_OUTPUT_VALUE_SATOSHI} use core::ops::Deref; use crate::chain; use crate::ln::features::ChannelTypeFeatures; -use crate::util::crypto::{sign, sign_with_aux_rand}; +use crate::crypto::utils::{sign, sign_with_aux_rand}; use super::channel_keys::{DelayedPaymentBasepoint, DelayedPaymentKey, HtlcKey, HtlcBasepoint, RevocationKey, RevocationBasepoint}; /// Maximum number of one-way in-flight HTLC (protocol-level value). diff --git a/lightning/src/ln/inbound_payment.rs b/lightning/src/ln/inbound_payment.rs index 2ee93a6c4..eeae514fd 100644 --- a/lightning/src/ln/inbound_payment.rs +++ b/lightning/src/ln/inbound_payment.rs @@ -18,8 +18,8 @@ use crate::sign::{KeyMaterial, EntropySource}; use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret}; use crate::ln::msgs; use crate::ln::msgs::MAX_VALUE_MSAT; -use crate::util::chacha20::ChaCha20; -use crate::util::crypto::hkdf_extract_expand_5x; +use crate::crypto::chacha20::ChaCha20; +use crate::crypto::utils::hkdf_extract_expand_5x; use crate::util::errors::APIError; use crate::util::logger::Logger; diff --git a/lightning/src/ln/monitor_tests.rs b/lightning/src/ln/monitor_tests.rs index 74740a6f2..b62701814 100644 --- a/lightning/src/ln/monitor_tests.rs +++ b/lightning/src/ln/monitor_tests.rs @@ -19,7 +19,7 @@ use crate::ln::channel; use crate::ln::channelmanager::{BREAKDOWN_TIMEOUT, PaymentId, RecipientOnionFields}; use crate::ln::msgs::ChannelMessageHandler; use crate::util::config::UserConfig; -use crate::util::crypto::sign; +use crate::crypto::utils::sign; use crate::util::ser::Writeable; use crate::util::scid_utils::block_from_scid; use crate::util::test_utils; diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 1794aefe7..efd04d143 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -53,7 +53,7 @@ use crate::io::{self, Cursor, Read}; use crate::io_extras::read_to_end; use crate::events::{EventsProvider, MessageSendEventsProvider}; -use crate::util::chacha20poly1305rfc::ChaChaPolyReadAdapter; +use crate::crypto::streams::ChaChaPolyReadAdapter; use crate::util::logger; use crate::util::ser::{LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer, WithoutLength, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname, TransactionU16LenLimited, BigSize}; use crate::util::base32; diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index ac0bb6189..d9455fc1d 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -14,7 +14,8 @@ use crate::ln::wire::Encode; use crate::routing::gossip::NetworkUpdate; use crate::routing::router::{BlindedTail, Path, RouteHop}; use crate::sign::NodeSigner; -use crate::util::chacha20::{ChaCha20, ChaChaReader}; +use crate::crypto::chacha20::ChaCha20; +use crate::crypto::streams::ChaChaReader; use crate::util::errors::{self, APIError}; use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer, LengthCalculatingWriter}; use crate::util::logger::Logger; diff --git a/lightning/src/ln/peer_channel_encryptor.rs b/lightning/src/ln/peer_channel_encryptor.rs index c9e1ac478..51e34bdb9 100644 --- a/lightning/src/ln/peer_channel_encryptor.rs +++ b/lightning/src/ln/peer_channel_encryptor.rs @@ -24,8 +24,8 @@ use bitcoin::secp256k1; use hex::DisplayHex; -use crate::util::chacha20poly1305rfc::ChaCha20Poly1305RFC; -use crate::util::crypto::hkdf_extract_expand_twice; +use crate::crypto::chacha20poly1305rfc::ChaCha20Poly1305RFC; +use crate::crypto::utils::hkdf_extract_expand_twice; use crate::util::ser::VecWriter; use core::ops::Deref; @@ -188,7 +188,7 @@ impl PeerChannelEncryptor { nonce[4..].copy_from_slice(&n.to_le_bytes()[..]); let mut chacha = ChaCha20Poly1305RFC::new(key, &nonce, h); - if !chacha.decrypt(&cyphertext[0..cyphertext.len() - 16], res, &cyphertext[cyphertext.len() - 16..]) { + if chacha.variable_time_decrypt(&cyphertext[0..cyphertext.len() - 16], res, &cyphertext[cyphertext.len() - 16..]).is_err() { return Err(LightningError{err: "Bad MAC".to_owned(), action: msgs::ErrorAction::DisconnectPeer{ msg: None }}); } Ok(()) diff --git a/lightning/src/onion_message/packet.rs b/lightning/src/onion_message/packet.rs index 5ce02c54d..d9349fdad 100644 --- a/lightning/src/onion_message/packet.rs +++ b/lightning/src/onion_message/packet.rs @@ -19,7 +19,7 @@ use crate::ln::msgs::DecodeError; use crate::ln::onion_utils; use super::messenger::CustomOnionMessageHandler; use super::offers::OffersMessage; -use crate::util::chacha20poly1305rfc::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter}; +use crate::crypto::streams::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter}; use crate::util::logger::Logger; use crate::util::ser::{BigSize, FixedLengthReader, LengthRead, LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer}; diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 277661862..0b19369a4 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -26,7 +26,7 @@ use crate::routing::scoring::{ChannelUsage, LockableScore, ScoreLookUp}; use crate::sign::EntropySource; use crate::util::ser::{Writeable, Readable, ReadableArgs, Writer}; use crate::util::logger::{Level, Logger}; -use crate::util::chacha20::ChaCha20; +use crate::crypto::chacha20::ChaCha20; use crate::io; use crate::prelude::*; @@ -3195,7 +3195,7 @@ mod tests { use crate::offers::invoice::BlindedPayInfo; use crate::util::config::UserConfig; use crate::util::test_utils as ln_test_utils; - use crate::util::chacha20::ChaCha20; + use crate::crypto::chacha20::ChaCha20; use crate::util::ser::{Readable, Writeable}; #[cfg(c_bindings)] use crate::util::ser::Writer; diff --git a/lightning/src/sign/mod.rs b/lightning/src/sign/mod.rs index 4e418f049..c4bb6fb80 100644 --- a/lightning/src/sign/mod.rs +++ b/lightning/src/sign/mod.rs @@ -38,7 +38,7 @@ use bitcoin::secp256k1::schnorr; use bitcoin::{secp256k1, Sequence, Witness, Txid}; use crate::util::transaction_utils; -use crate::util::crypto::{hkdf_extract_expand_twice, sign, sign_with_aux_rand}; +use crate::crypto::utils::{hkdf_extract_expand_twice, sign, sign_with_aux_rand}; use crate::util::ser::{Writeable, Writer, Readable, ReadableArgs}; use crate::chain::transaction::OutPoint; use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI; @@ -65,7 +65,7 @@ use crate::sign::ecdsa::{EcdsaChannelSigner, WriteableEcdsaChannelSigner}; #[cfg(taproot)] use crate::sign::taproot::TaprootChannelSigner; use crate::util::atomic_counter::AtomicCounter; -use crate::util::chacha20::ChaCha20; +use crate::crypto::chacha20::ChaCha20; use crate::util::invoice::construct_invoice_preimage; pub(crate) mod type_resolver; diff --git a/lightning/src/util/chacha20.rs b/lightning/src/util/chacha20.rs deleted file mode 100644 index 865a09fa0..000000000 --- a/lightning/src/util/chacha20.rs +++ /dev/null @@ -1,701 +0,0 @@ -// This file was stolen from rust-crypto. -// 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 or the MIT license -// , at your option. -// You may not use this file except in accordance with one or both of these -// licenses. - -use crate::io; - -#[cfg(not(fuzzing))] -mod real_chacha { - 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 ::core::ops::Add for u32x4 { - type Output = u32x4; - #[inline] - fn add(self, rhs: u32x4) -> u32x4 { - u32x4(self.0.wrapping_add(rhs.0), - self.1.wrapping_add(rhs.1), - self.2.wrapping_add(rhs.2), - self.3.wrapping_add(rhs.3)) - } - } - impl ::core::ops::Sub for u32x4 { - type Output = u32x4; - #[inline] - fn sub(self, rhs: u32x4) -> u32x4 { - u32x4(self.0.wrapping_sub(rhs.0), - self.1.wrapping_sub(rhs.1), - self.2.wrapping_sub(rhs.2), - self.3.wrapping_sub(rhs.3)) - } - } - impl ::core::ops::BitXor for u32x4 { - type Output = u32x4; - #[inline] - fn bitxor(self, rhs: u32x4) -> u32x4 { - u32x4(self.0 ^ rhs.0, self.1 ^ rhs.1, self.2 ^ rhs.2, self.3 ^ rhs.3) - } - } - impl ::core::ops::Shr for u32x4 { - type Output = u32x4; - #[inline] - fn shr(self, shr: u8) -> u32x4 { - u32x4(self.0 >> shr, self.1 >> shr, self.2 >> shr, self.3 >> shr) - } - } - impl ::core::ops::Shl for u32x4 { - type Output = u32x4; - #[inline] - fn shl(self, shl: u8) -> u32x4 { - u32x4(self.0 << shl, self.1 << shl, self.2 << shl, self.3 << shl) - } - } - impl u32x4 { - #[inline] - 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 { - a: u32x4, - b: u32x4, - c: u32x4, - d: u32x4 - } - - #[derive(Copy)] - pub struct ChaCha20 { - state : ChaChaState, - output : [u8; BLOCK_SIZE], - offset : usize, - } - - impl Clone for ChaCha20 { fn clone(&self) -> ChaCha20 { *self } } - - macro_rules! swizzle { - ($b: expr, $c: expr, $d: expr) => {{ - let u32x4(b10, b11, b12, b13) = $b; - $b = u32x4(b11, b12, b13, b10); - let u32x4(c10, c11, c12, c13) = $c; - $c = u32x4(c12, c13,c10, c11); - let u32x4(d10, d11, d12, d13) = $d; - $d = u32x4(d13, d10, d11, d12); - }} - } - - macro_rules! state_to_buffer { - ($state: expr, $output: expr) => {{ - let u32x4(a1, a2, a3, a4) = $state.a; - let u32x4(b1, b2, b3, b4) = $state.b; - let u32x4(c1, c2, c3, c4) = $state.c; - let u32x4(d1, d2, d3, d4) = $state.d; - let lens = [ - a1,a2,a3,a4, - b1,b2,b3,b4, - c1,c2,c3,c4, - d1,d2,d3,d4 - ]; - for i in 0..lens.len() { - $output[i*4..(i+1)*4].copy_from_slice(&lens[i].to_le_bytes()); - } - }} - } - - macro_rules! round{ - ($state: expr) => {{ - $state.a = $state.a + $state.b; - rotate!($state.d, $state.a, 16); - $state.c = $state.c + $state.d; - rotate!($state.b, $state.c, 12); - $state.a = $state.a + $state.b; - rotate!($state.d, $state.a, 8); - $state.c = $state.c + $state.d; - rotate!($state.b, $state.c, 7); - }} - } - - macro_rules! rotate { - ($a: expr, $b: expr, $rot: expr) => {{ - let v = $a ^ $b; - let r = 32 - $rot; - let right = v >> r; - $a = (v << $rot) ^ right - }} - } - - impl ChaCha20 { - pub fn new(key: &[u8], nonce: &[u8]) -> ChaCha20 { - assert!(key.len() == 16 || key.len() == 32); - assert!(nonce.len() == 8 || nonce.len() == 12); - - 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", - 32 => b"expand 32-byte k", - _ => unreachable!(), - }; - ChaChaState { - a: u32x4::from_bytes(&constant[0..16]), - b: u32x4::from_bytes(&key[0..16]), - c: if key.len() == 16 { - u32x4::from_bytes(&key[0..16]) - } else { - u32x4::from_bytes(&key[16..32]) - }, - d: if nonce.len() == 16 { - u32x4::from_bytes(&nonce[0..16]) - } else if nonce.len() == 12 { - let mut nonce4 = [0; 4*4]; - nonce4[4..].copy_from_slice(nonce); - u32x4::from_bytes(&nonce4) - } else { - let mut nonce4 = [0; 4*4]; - nonce4[8..].copy_from_slice(nonce); - u32x4::from_bytes(&nonce4) - } - } - } - - // put the the next BLOCK_SIZE keystream bytes into self.output - fn update(&mut self) { - let mut state = self.state; - - for _ in 0..10 { - round!(state); - swizzle!(state.b, state.c, state.d); - round!(state); - swizzle!(state.d, state.c, state.b); - } - state.a = state.a + self.state.a; - state.b = state.b + self.state.b; - state.c = state.c + self.state.c; - state.d = state.d + self.state.d; - - state_to_buffer!(state, self.output); - - self.state.d = self.state.d + u32x4(1, 0, 0, 0); - let u32x4(c12, _, _, _) = self.state.d; - if c12 == 0 { - // we could increment the other counter word with an 8 byte nonce - // but other implementations like boringssl have this same - // limitation - panic!("counter is exhausted"); - } - - self.offset = 0; - } - - #[inline] // Useful cause input may be 0s on stack that should be optimized out - pub fn process(&mut self, input: &[u8], output: &mut [u8]) { - assert!(input.len() == output.len()); - let len = input.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!(output.len() >= i + count); - assert!(input.len() >= i + count); - assert!(self.output.len() >= self.offset + count); - for j in 0..count { - output[i + j] = input[i + j] ^ self.output[self.offset + j]; - } - i += count; - 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(fuzzing))] -pub use self::real_chacha::ChaCha20; - -#[cfg(fuzzing)] -mod fuzzy_chacha { - pub struct ChaCha20 {} - - impl ChaCha20 { - pub fn new(key: &[u8], nonce: &[u8]) -> ChaCha20 { - assert!(key.len() == 16 || key.len() == 32); - assert!(nonce.len() == 8 || nonce.len() == 12); - 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(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 { - 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 crate::prelude::*; - use core::iter::repeat; - - use super::ChaCha20; - use std::convert::TryInto; - - #[test] - fn test_chacha20_256_tls_vectors() { - struct TestVector { - key: [u8; 32], - nonce: [u8; 8], - keystream: Vec, - } - // taken from http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04 - let test_vectors = vec!( - TestVector{ - key: [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ], - nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ], - keystream: vec!( - 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, - 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28, - 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, - 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7, - 0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d, - 0x77, 0x24, 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37, - 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c, - 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86, - ), - }, TestVector{ - key: [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - ], - nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ], - keystream: vec!( - 0x45, 0x40, 0xf0, 0x5a, 0x9f, 0x1f, 0xb2, 0x96, - 0xd7, 0x73, 0x6e, 0x7b, 0x20, 0x8e, 0x3c, 0x96, - 0xeb, 0x4f, 0xe1, 0x83, 0x46, 0x88, 0xd2, 0x60, - 0x4f, 0x45, 0x09, 0x52, 0xed, 0x43, 0x2d, 0x41, - 0xbb, 0xe2, 0xa0, 0xb6, 0xea, 0x75, 0x66, 0xd2, - 0xa5, 0xd1, 0xe7, 0xe2, 0x0d, 0x42, 0xaf, 0x2c, - 0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81, - 0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63, - ), - }, TestVector{ - key: [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ], - nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ], - keystream: vec!( - 0xde, 0x9c, 0xba, 0x7b, 0xf3, 0xd6, 0x9e, 0xf5, - 0xe7, 0x86, 0xdc, 0x63, 0x97, 0x3f, 0x65, 0x3a, - 0x0b, 0x49, 0xe0, 0x15, 0xad, 0xbf, 0xf7, 0x13, - 0x4f, 0xcb, 0x7d, 0xf1, 0x37, 0x82, 0x10, 0x31, - 0xe8, 0x5a, 0x05, 0x02, 0x78, 0xa7, 0x08, 0x45, - 0x27, 0x21, 0x4f, 0x73, 0xef, 0xc7, 0xfa, 0x5b, - 0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e, - 0x44, 0x5f, 0x41, 0xe3, - ), - }, TestVector{ - key: [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ], - nonce: [ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ], - keystream: vec!( - 0xef, 0x3f, 0xdf, 0xd6, 0xc6, 0x15, 0x78, 0xfb, - 0xf5, 0xcf, 0x35, 0xbd, 0x3d, 0xd3, 0x3b, 0x80, - 0x09, 0x63, 0x16, 0x34, 0xd2, 0x1e, 0x42, 0xac, - 0x33, 0x96, 0x0b, 0xd1, 0x38, 0xe5, 0x0d, 0x32, - 0x11, 0x1e, 0x4c, 0xaf, 0x23, 0x7e, 0xe5, 0x3c, - 0xa8, 0xad, 0x64, 0x26, 0x19, 0x4a, 0x88, 0x54, - 0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d, - 0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b, - ), - }, TestVector{ - 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, - ], - nonce: [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 ], - keystream: vec!( - 0xf7, 0x98, 0xa1, 0x89, 0xf1, 0x95, 0xe6, 0x69, - 0x82, 0x10, 0x5f, 0xfb, 0x64, 0x0b, 0xb7, 0x75, - 0x7f, 0x57, 0x9d, 0xa3, 0x16, 0x02, 0xfc, 0x93, - 0xec, 0x01, 0xac, 0x56, 0xf8, 0x5a, 0xc3, 0xc1, - 0x34, 0xa4, 0x54, 0x7b, 0x73, 0x3b, 0x46, 0x41, - 0x30, 0x42, 0xc9, 0x44, 0x00, 0x49, 0x17, 0x69, - 0x05, 0xd3, 0xbe, 0x59, 0xea, 0x1c, 0x53, 0xf1, - 0x59, 0x16, 0x15, 0x5c, 0x2b, 0xe8, 0x24, 0x1a, - 0x38, 0x00, 0x8b, 0x9a, 0x26, 0xbc, 0x35, 0x94, - 0x1e, 0x24, 0x44, 0x17, 0x7c, 0x8a, 0xde, 0x66, - 0x89, 0xde, 0x95, 0x26, 0x49, 0x86, 0xd9, 0x58, - 0x89, 0xfb, 0x60, 0xe8, 0x46, 0x29, 0xc9, 0xbd, - 0x9a, 0x5a, 0xcb, 0x1c, 0xc1, 0x18, 0xbe, 0x56, - 0x3e, 0xb9, 0xb3, 0xa4, 0xa4, 0x72, 0xf8, 0x2e, - 0x09, 0xa7, 0xe7, 0x78, 0x49, 0x2b, 0x56, 0x2e, - 0xf7, 0x13, 0x0e, 0x88, 0xdf, 0xe0, 0x31, 0xc7, - 0x9d, 0xb9, 0xd4, 0xf7, 0xc7, 0xa8, 0x99, 0x15, - 0x1b, 0x9a, 0x47, 0x50, 0x32, 0xb6, 0x3f, 0xc3, - 0x85, 0x24, 0x5f, 0xe0, 0x54, 0xe3, 0xdd, 0x5a, - 0x97, 0xa5, 0xf5, 0x76, 0xfe, 0x06, 0x40, 0x25, - 0xd3, 0xce, 0x04, 0x2c, 0x56, 0x6a, 0xb2, 0xc5, - 0x07, 0xb1, 0x38, 0xdb, 0x85, 0x3e, 0x3d, 0x69, - 0x59, 0x66, 0x09, 0x96, 0x54, 0x6c, 0xc9, 0xc4, - 0xa6, 0xea, 0xfd, 0xc7, 0x77, 0xc0, 0x40, 0xd7, - 0x0e, 0xaf, 0x46, 0xf7, 0x6d, 0xad, 0x39, 0x79, - 0xe5, 0xc5, 0x36, 0x0c, 0x33, 0x17, 0x16, 0x6a, - 0x1c, 0x89, 0x4c, 0x94, 0xa3, 0x71, 0x87, 0x6a, - 0x94, 0xdf, 0x76, 0x28, 0xfe, 0x4e, 0xaa, 0xf2, - 0xcc, 0xb2, 0x7d, 0x5a, 0xaa, 0xe0, 0xad, 0x7a, - 0xd0, 0xf9, 0xd4, 0xb6, 0xad, 0x3b, 0x54, 0x09, - 0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a, - 0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9, - ), - }, - ); - - for tv in test_vectors.iter() { - let mut c = ChaCha20::new(&tv.key, &tv.nonce); - let input: Vec = repeat(0).take(tv.keystream.len()).collect(); - let mut output: Vec = repeat(0).take(input.len()).collect(); - c.process(&input[..], &mut output[..]); - assert_eq!(output, tv.keystream); - } - } - - #[test] - fn test_chacha20_256_tls_vectors_96_nonce() { - struct TestVector { - key: [u8; 32], - nonce: [u8; 12], - keystream: Vec, - } - // taken from http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04 - let test_vectors = vec!( - TestVector{ - key: [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ], - nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ], - keystream: vec!( - 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, - 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28, - 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, - 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7, - 0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d, - 0x77, 0x24, 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37, - 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c, - 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86, - ), - }, TestVector{ - key: [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - ], - nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ], - keystream: vec!( - 0x45, 0x40, 0xf0, 0x5a, 0x9f, 0x1f, 0xb2, 0x96, - 0xd7, 0x73, 0x6e, 0x7b, 0x20, 0x8e, 0x3c, 0x96, - 0xeb, 0x4f, 0xe1, 0x83, 0x46, 0x88, 0xd2, 0x60, - 0x4f, 0x45, 0x09, 0x52, 0xed, 0x43, 0x2d, 0x41, - 0xbb, 0xe2, 0xa0, 0xb6, 0xea, 0x75, 0x66, 0xd2, - 0xa5, 0xd1, 0xe7, 0xe2, 0x0d, 0x42, 0xaf, 0x2c, - 0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81, - 0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63, - ), - }, TestVector{ - key: [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ], - nonce: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ], - keystream: vec!( - 0xde, 0x9c, 0xba, 0x7b, 0xf3, 0xd6, 0x9e, 0xf5, - 0xe7, 0x86, 0xdc, 0x63, 0x97, 0x3f, 0x65, 0x3a, - 0x0b, 0x49, 0xe0, 0x15, 0xad, 0xbf, 0xf7, 0x13, - 0x4f, 0xcb, 0x7d, 0xf1, 0x37, 0x82, 0x10, 0x31, - 0xe8, 0x5a, 0x05, 0x02, 0x78, 0xa7, 0x08, 0x45, - 0x27, 0x21, 0x4f, 0x73, 0xef, 0xc7, 0xfa, 0x5b, - 0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e, - 0x44, 0x5f, 0x41, 0xe3, - ), - }, TestVector{ - key: [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ], - nonce: [ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ], - keystream: vec!( - 0xef, 0x3f, 0xdf, 0xd6, 0xc6, 0x15, 0x78, 0xfb, - 0xf5, 0xcf, 0x35, 0xbd, 0x3d, 0xd3, 0x3b, 0x80, - 0x09, 0x63, 0x16, 0x34, 0xd2, 0x1e, 0x42, 0xac, - 0x33, 0x96, 0x0b, 0xd1, 0x38, 0xe5, 0x0d, 0x32, - 0x11, 0x1e, 0x4c, 0xaf, 0x23, 0x7e, 0xe5, 0x3c, - 0xa8, 0xad, 0x64, 0x26, 0x19, 0x4a, 0x88, 0x54, - 0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d, - 0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b, - ), - }, TestVector{ - 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, - ], - nonce: [0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 ], - keystream: vec!( - 0xf7, 0x98, 0xa1, 0x89, 0xf1, 0x95, 0xe6, 0x69, - 0x82, 0x10, 0x5f, 0xfb, 0x64, 0x0b, 0xb7, 0x75, - 0x7f, 0x57, 0x9d, 0xa3, 0x16, 0x02, 0xfc, 0x93, - 0xec, 0x01, 0xac, 0x56, 0xf8, 0x5a, 0xc3, 0xc1, - 0x34, 0xa4, 0x54, 0x7b, 0x73, 0x3b, 0x46, 0x41, - 0x30, 0x42, 0xc9, 0x44, 0x00, 0x49, 0x17, 0x69, - 0x05, 0xd3, 0xbe, 0x59, 0xea, 0x1c, 0x53, 0xf1, - 0x59, 0x16, 0x15, 0x5c, 0x2b, 0xe8, 0x24, 0x1a, - 0x38, 0x00, 0x8b, 0x9a, 0x26, 0xbc, 0x35, 0x94, - 0x1e, 0x24, 0x44, 0x17, 0x7c, 0x8a, 0xde, 0x66, - 0x89, 0xde, 0x95, 0x26, 0x49, 0x86, 0xd9, 0x58, - 0x89, 0xfb, 0x60, 0xe8, 0x46, 0x29, 0xc9, 0xbd, - 0x9a, 0x5a, 0xcb, 0x1c, 0xc1, 0x18, 0xbe, 0x56, - 0x3e, 0xb9, 0xb3, 0xa4, 0xa4, 0x72, 0xf8, 0x2e, - 0x09, 0xa7, 0xe7, 0x78, 0x49, 0x2b, 0x56, 0x2e, - 0xf7, 0x13, 0x0e, 0x88, 0xdf, 0xe0, 0x31, 0xc7, - 0x9d, 0xb9, 0xd4, 0xf7, 0xc7, 0xa8, 0x99, 0x15, - 0x1b, 0x9a, 0x47, 0x50, 0x32, 0xb6, 0x3f, 0xc3, - 0x85, 0x24, 0x5f, 0xe0, 0x54, 0xe3, 0xdd, 0x5a, - 0x97, 0xa5, 0xf5, 0x76, 0xfe, 0x06, 0x40, 0x25, - 0xd3, 0xce, 0x04, 0x2c, 0x56, 0x6a, 0xb2, 0xc5, - 0x07, 0xb1, 0x38, 0xdb, 0x85, 0x3e, 0x3d, 0x69, - 0x59, 0x66, 0x09, 0x96, 0x54, 0x6c, 0xc9, 0xc4, - 0xa6, 0xea, 0xfd, 0xc7, 0x77, 0xc0, 0x40, 0xd7, - 0x0e, 0xaf, 0x46, 0xf7, 0x6d, 0xad, 0x39, 0x79, - 0xe5, 0xc5, 0x36, 0x0c, 0x33, 0x17, 0x16, 0x6a, - 0x1c, 0x89, 0x4c, 0x94, 0xa3, 0x71, 0x87, 0x6a, - 0x94, 0xdf, 0x76, 0x28, 0xfe, 0x4e, 0xaa, 0xf2, - 0xcc, 0xb2, 0x7d, 0x5a, 0xaa, 0xe0, 0xad, 0x7a, - 0xd0, 0xf9, 0xd4, 0xb6, 0xad, 0x3b, 0x54, 0x09, - 0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a, - 0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9, - ), - }, - ); - - for tv in test_vectors.iter() { - let mut c = ChaCha20::new(&tv.key, &tv.nonce); - let input: Vec = repeat(0).take(tv.keystream.len()).collect(); - let mut output: Vec = repeat(0).take(input.len()).collect(); - c.process(&input[..], &mut output[..]); - 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); - } -} diff --git a/lightning/src/util/chacha20poly1305rfc.rs b/lightning/src/util/chacha20poly1305rfc.rs deleted file mode 100644 index d5792e0ac..000000000 --- a/lightning/src/util/chacha20poly1305rfc.rs +++ /dev/null @@ -1,427 +0,0 @@ -// ring has a garbage API so its use is avoided, but rust-crypto doesn't have RFC-variant poly1305 -// Instead, we steal rust-crypto's implementation and tweak it to match the RFC. -// -// This file is licensed under the Apache License, Version 2.0 or the MIT license -// , at your option. -// You may not use this file except in accordance with one or both of these -// licenses. -// -// This is a port of Andrew Moons poly1305-donna -// https://github.com/floodyberry/poly1305-donna - -use crate::ln::msgs::DecodeError; -use crate::util::ser::{FixedLengthReader, LengthRead, LengthReadableArgs, Readable, Writeable, Writer}; -use crate::io::{self, Read, Write}; - -#[cfg(not(fuzzing))] -mod real_chachapoly { - use crate::util::chacha20::ChaCha20; - use crate::util::poly1305::Poly1305; - use bitcoin::hashes::cmp::fixed_time_eq; - - #[derive(Clone, Copy)] - pub struct ChaCha20Poly1305RFC { - cipher: ChaCha20, - mac: Poly1305, - finished: bool, - data_len: usize, - aad_len: u64, - } - - impl ChaCha20Poly1305RFC { - #[inline] - fn pad_mac_16(mac: &mut Poly1305, len: usize) { - if len % 16 != 0 { - mac.input(&[0; 16][0..16 - (len % 16)]); - } - } - pub fn new(key: &[u8], nonce: &[u8], aad: &[u8]) -> ChaCha20Poly1305RFC { - assert!(key.len() == 16 || key.len() == 32); - assert!(nonce.len() == 12); - - // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant - assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0); - - let mut cipher = ChaCha20::new(key, &nonce[4..]); - let mut mac_key = [0u8; 64]; - let zero_key = [0u8; 64]; - cipher.process(&zero_key, &mut mac_key); - - let mut mac = Poly1305::new(&mac_key[..32]); - mac.input(aad); - ChaCha20Poly1305RFC::pad_mac_16(&mut mac, aad.len()); - - ChaCha20Poly1305RFC { - cipher, - mac, - finished: false, - data_len: 0, - aad_len: aad.len() as u64, - } - } - - pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) { - assert!(input.len() == output.len()); - assert!(self.finished == false); - self.cipher.process(input, output); - self.data_len += input.len(); - self.mac.input(output); - ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len); - self.finished = true; - self.mac.input(&self.aad_len.to_le_bytes()); - self.mac.input(&(self.data_len as u64).to_le_bytes()); - self.mac.raw_result(out_tag); - } - - pub fn encrypt_full_message_in_place(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) { - self.encrypt_in_place(input_output); - self.finish_and_get_tag(out_tag); - } - - // Encrypt `input_output` in-place. To finish and calculate the tag, use `finish_and_get_tag` - // below. - pub(super) fn encrypt_in_place(&mut self, input_output: &mut [u8]) { - debug_assert!(self.finished == false); - self.cipher.process_in_place(input_output); - self.data_len += input_output.len(); - self.mac.input(input_output); - } - - // If we were previously encrypting with `encrypt_in_place`, this method can be used to finish - // encrypting and calculate the tag. - pub(super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) { - debug_assert!(self.finished == false); - ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len); - self.finished = true; - self.mac.input(&self.aad_len.to_le_bytes()); - self.mac.input(&(self.data_len as u64).to_le_bytes()); - self.mac.raw_result(out_tag); - } - - pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool { - assert!(input.len() == output.len()); - assert!(self.finished == false); - - self.finished = true; - - self.mac.input(input); - - self.data_len += input.len(); - ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len); - self.mac.input(&self.aad_len.to_le_bytes()); - self.mac.input(&(self.data_len as u64).to_le_bytes()); - - let mut calc_tag = [0u8; 16]; - self.mac.raw_result(&mut calc_tag); - if fixed_time_eq(&calc_tag, tag) { - self.cipher.process(input, output); - true - } else { - false - } - } - - pub fn check_decrypt_in_place(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> { - self.decrypt_in_place(input_output); - if self.finish_and_check_tag(tag) { Ok(()) } else { Err(()) } - } - - /// Decrypt in place, without checking the tag. Use `finish_and_check_tag` to check it - /// later when decryption finishes. - /// - /// Should never be `pub` because the public API should always enforce tag checking. - pub(super) fn decrypt_in_place(&mut self, input_output: &mut [u8]) { - debug_assert!(self.finished == false); - self.mac.input(input_output); - self.data_len += input_output.len(); - self.cipher.process_in_place(input_output); - } - - /// If we were previously decrypting with `just_decrypt_in_place`, this method must be used - /// to check the tag. Returns whether or not the tag is valid. - pub(super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool { - debug_assert!(self.finished == false); - self.finished = true; - ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len); - self.mac.input(&self.aad_len.to_le_bytes()); - self.mac.input(&(self.data_len as u64).to_le_bytes()); - - let mut calc_tag = [0u8; 16]; - self.mac.raw_result(&mut calc_tag); - if fixed_time_eq(&calc_tag, tag) { - true - } else { - false - } - } - } -} -#[cfg(not(fuzzing))] -pub use self::real_chachapoly::ChaCha20Poly1305RFC; - -/// Enables simultaneously reading and decrypting a ChaCha20Poly1305RFC stream from a std::io::Read. -struct ChaChaPolyReader<'a, R: Read> { - pub chacha: &'a mut ChaCha20Poly1305RFC, - pub read: R, -} - -impl<'a, R: Read> Read for ChaChaPolyReader<'a, R> { - // Decrypt bytes from Self::read into `dest`. - // `ChaCha20Poly1305RFC::finish_and_check_tag` must be called to check the tag after all reads - // complete. - fn read(&mut self, dest: &mut [u8]) -> Result { - let res = self.read.read(dest)?; - if res > 0 { - self.chacha.decrypt_in_place(&mut dest[0..res]); - } - Ok(res) - } -} - -/// Enables simultaneously writing and encrypting a byte stream into a Writer. -struct ChaChaPolyWriter<'a, W: Writer> { - pub chacha: &'a mut ChaCha20Poly1305RFC, - pub write: &'a mut W, -} - -impl<'a, W: Writer> Writer for ChaChaPolyWriter<'a, W> { - // Encrypt then write bytes from `src` into Self::write. - // `ChaCha20Poly1305RFC::finish_and_get_tag` can be called to retrieve the tag after all writes - // complete. - fn write_all(&mut self, src: &[u8]) -> Result<(), io::Error> { - let mut src_idx = 0; - while src_idx < src.len() { - let mut write_buffer = [0; 8192]; - let bytes_written = (&mut write_buffer[..]).write(&src[src_idx..]).expect("In-memory writes can't fail"); - self.chacha.encrypt_in_place(&mut write_buffer[..bytes_written]); - self.write.write_all(&write_buffer[..bytes_written])?; - src_idx += bytes_written; - } - Ok(()) - } -} - -/// Enables the use of the serialization macros for objects that need to be simultaneously encrypted and -/// serialized. This allows us to avoid an intermediate Vec allocation. -pub(crate) struct ChaChaPolyWriteAdapter<'a, W: Writeable> { - pub rho: [u8; 32], - pub writeable: &'a W, -} - -impl<'a, W: Writeable> ChaChaPolyWriteAdapter<'a, W> { - #[allow(unused)] // This will be used for onion messages soon - pub fn new(rho: [u8; 32], writeable: &'a W) -> ChaChaPolyWriteAdapter<'a, W> { - Self { rho, writeable } - } -} - -impl<'a, T: Writeable> Writeable for ChaChaPolyWriteAdapter<'a, T> { - // Simultaneously write and encrypt Self::writeable. - fn write(&self, w: &mut W) -> Result<(), io::Error> { - let mut chacha = ChaCha20Poly1305RFC::new(&self.rho, &[0; 12], &[]); - let mut chacha_stream = ChaChaPolyWriter { chacha: &mut chacha, write: w }; - self.writeable.write(&mut chacha_stream)?; - let mut tag = [0 as u8; 16]; - chacha.finish_and_get_tag(&mut tag); - tag.write(w)?; - - Ok(()) - } -} - -/// Enables the use of the serialization macros for objects that need to be simultaneously decrypted and -/// deserialized. This allows us to avoid an intermediate Vec allocation. -pub(crate) struct ChaChaPolyReadAdapter { - pub readable: R, -} - -impl LengthReadableArgs<[u8; 32]> for ChaChaPolyReadAdapter { - // Simultaneously read and decrypt an object from a LengthRead, storing it in Self::readable. - // LengthRead must be used instead of std::io::Read because we need the total length to separate - // out the tag at the end. - fn read(mut r: &mut R, secret: [u8; 32]) -> Result { - if r.total_bytes() < 16 { return Err(DecodeError::InvalidValue) } - - let mut chacha = ChaCha20Poly1305RFC::new(&secret, &[0; 12], &[]); - let decrypted_len = r.total_bytes() - 16; - let s = FixedLengthReader::new(&mut r, decrypted_len); - let mut chacha_stream = ChaChaPolyReader { chacha: &mut chacha, read: s }; - let readable: T = Readable::read(&mut chacha_stream)?; - chacha_stream.read.eat_remaining()?; - - let mut tag = [0 as u8; 16]; - r.read_exact(&mut tag)?; - if !chacha.finish_and_check_tag(&tag) { - return Err(DecodeError::InvalidValue) - } - - Ok(Self { readable }) - } -} - -#[cfg(fuzzing)] -mod fuzzy_chachapoly { - #[derive(Clone, Copy)] - pub struct ChaCha20Poly1305RFC { - tag: [u8; 16], - finished: bool, - } - impl ChaCha20Poly1305RFC { - pub fn new(key: &[u8], nonce: &[u8], _aad: &[u8]) -> ChaCha20Poly1305RFC { - assert!(key.len() == 16 || key.len() == 32); - assert!(nonce.len() == 12); - - // Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant - assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0); - - let mut tag = [0; 16]; - tag.copy_from_slice(&key[0..16]); - - ChaCha20Poly1305RFC { - tag, - finished: false, - } - } - - pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) { - assert!(input.len() == output.len()); - assert!(self.finished == false); - - output.copy_from_slice(&input); - out_tag.copy_from_slice(&self.tag); - self.finished = true; - } - - pub fn encrypt_full_message_in_place(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) { - self.encrypt_in_place(input_output); - self.finish_and_get_tag(out_tag); - } - - pub(super) fn encrypt_in_place(&mut self, _input_output: &mut [u8]) { - assert!(self.finished == false); - } - - pub(super) fn finish_and_get_tag(&mut self, out_tag: &mut [u8]) { - assert!(self.finished == false); - out_tag.copy_from_slice(&self.tag); - self.finished = true; - } - - pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool { - assert!(input.len() == output.len()); - assert!(self.finished == false); - - if tag[..] != self.tag[..] { return false; } - output.copy_from_slice(input); - self.finished = true; - true - } - - pub fn check_decrypt_in_place(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> { - self.decrypt_in_place(input_output); - if self.finish_and_check_tag(tag) { Ok(()) } else { Err(()) } - } - - pub(super) fn decrypt_in_place(&mut self, _input: &mut [u8]) { - assert!(self.finished == false); - } - - pub(super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool { - if tag[..] != self.tag[..] { return false; } - self.finished = true; - true - } - } -} -#[cfg(fuzzing)] -pub use self::fuzzy_chachapoly::ChaCha20Poly1305RFC; - -#[cfg(test)] -mod tests { - use crate::ln::msgs::DecodeError; - use super::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter}; - use crate::util::ser::{self, FixedLengthReader, LengthReadableArgs, Writeable}; - - // Used for for testing various lengths of serialization. - #[derive(Debug, PartialEq, Eq)] - struct TestWriteable { - field1: Vec, - field2: Vec, - field3: Vec, - } - impl_writeable_tlv_based!(TestWriteable, { - (1, field1, required_vec), - (2, field2, required_vec), - (3, field3, required_vec), - }); - - #[test] - fn test_chacha_stream_adapters() { - // Check that ChaChaPolyReadAdapter and ChaChaPolyWriteAdapter correctly encode and decode an - // encrypted object. - macro_rules! check_object_read_write { - ($obj: expr) => { - // First, serialize the object, encrypted with ChaCha20Poly1305. - let rho = [42; 32]; - let writeable_len = $obj.serialized_length() as u64 + 16; - let write_adapter = ChaChaPolyWriteAdapter::new(rho, &$obj); - let encrypted_writeable_bytes = write_adapter.encode(); - let encrypted_writeable = &encrypted_writeable_bytes[..]; - - // Now deserialize the object back and make sure it matches the original. - let mut rd = FixedLengthReader::new(encrypted_writeable, writeable_len); - let read_adapter = >::read(&mut rd, rho).unwrap(); - assert_eq!($obj, read_adapter.readable); - }; - } - - // Try a big object that will require multiple write buffers. - let big_writeable = TestWriteable { - field1: vec![43], - field2: vec![44; 4192], - field3: vec![45; 4192 + 1], - }; - check_object_read_write!(big_writeable); - - // Try a small object that fits into one write buffer. - let small_writeable = TestWriteable { - field1: vec![43], - field2: vec![44], - field3: vec![45], - }; - check_object_read_write!(small_writeable); - } - - fn do_chacha_stream_adapters_ser_macros() -> Result<(), DecodeError> { - let writeable = TestWriteable { - field1: vec![43], - field2: vec![44; 4192], - field3: vec![45; 4192 + 1], - }; - - // First, serialize the object into a TLV stream, encrypted with ChaCha20Poly1305. - let rho = [42; 32]; - let write_adapter = ChaChaPolyWriteAdapter::new(rho, &writeable); - let mut writer = ser::VecWriter(Vec::new()); - encode_tlv_stream!(&mut writer, { - (1, write_adapter, required), - }); - - // Now deserialize the object back and make sure it matches the original. - let mut read_adapter: Option> = None; - decode_tlv_stream!(&writer.0[..], { - (1, read_adapter, (option: LengthReadableArgs, rho)), - }); - assert_eq!(writeable, read_adapter.unwrap().readable); - - Ok(()) - } - - #[test] - fn chacha_stream_adapters_ser_macros() { - // Test that our stream adapters work as expected with the TLV macros. - // This also serves to test the `option: $trait` variant of the `_decode_tlv` ser macro. - do_chacha_stream_adapters_ser_macros().unwrap() - } -} diff --git a/lightning/src/util/crypto.rs b/lightning/src/util/crypto.rs deleted file mode 100644 index 98963c7c2..000000000 --- a/lightning/src/util/crypto.rs +++ /dev/null @@ -1,83 +0,0 @@ -use bitcoin::hashes::{Hash, HashEngine}; -use bitcoin::hashes::hmac::{Hmac, HmacEngine}; -use bitcoin::hashes::sha256::Hash as Sha256; -use bitcoin::secp256k1::{Message, Secp256k1, SecretKey, ecdsa::Signature, Signing}; - -use crate::sign::EntropySource; - -use core::ops::Deref; - -macro_rules! hkdf_extract_expand { - ($salt: expr, $ikm: expr) => {{ - let mut hmac = HmacEngine::::new($salt); - hmac.input($ikm); - let prk = Hmac::from_engine(hmac).to_byte_array(); - let mut hmac = HmacEngine::::new(&prk[..]); - hmac.input(&[1; 1]); - let t1 = Hmac::from_engine(hmac).to_byte_array(); - let mut hmac = HmacEngine::::new(&prk[..]); - hmac.input(&t1); - hmac.input(&[2; 1]); - (t1, Hmac::from_engine(hmac).to_byte_array(), prk) - }}; - ($salt: expr, $ikm: expr, 2) => {{ - let (k1, k2, _) = hkdf_extract_expand!($salt, $ikm); - (k1, k2) - }}; - ($salt: expr, $ikm: expr, 5) => {{ - let (k1, k2, prk) = hkdf_extract_expand!($salt, $ikm); - - let mut hmac = HmacEngine::::new(&prk[..]); - hmac.input(&k2); - hmac.input(&[3; 1]); - let k3 = Hmac::from_engine(hmac).to_byte_array(); - - let mut hmac = HmacEngine::::new(&prk[..]); - hmac.input(&k3); - hmac.input(&[4; 1]); - let k4 = Hmac::from_engine(hmac).to_byte_array(); - - let mut hmac = HmacEngine::::new(&prk[..]); - hmac.input(&k4); - hmac.input(&[5; 1]); - let k5 = Hmac::from_engine(hmac).to_byte_array(); - - (k1, k2, k3, k4, k5) - }} -} - -pub fn hkdf_extract_expand_twice(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) { - hkdf_extract_expand!(salt, ikm, 2) -} - -pub fn hkdf_extract_expand_5x(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) { - hkdf_extract_expand!(salt, ikm, 5) -} - -#[inline] -pub fn sign(ctx: &Secp256k1, msg: &Message, sk: &SecretKey) -> Signature { - #[cfg(feature = "grind_signatures")] - let sig = ctx.sign_ecdsa_low_r(msg, sk); - #[cfg(not(feature = "grind_signatures"))] - let sig = ctx.sign_ecdsa(msg, sk); - sig -} - -#[inline] -#[allow(unused_variables)] -pub fn sign_with_aux_rand( - ctx: &Secp256k1, msg: &Message, sk: &SecretKey, entropy_source: &ES -) -> Signature where ES::Target: EntropySource { - #[cfg(feature = "grind_signatures")] - let sig = loop { - let sig = ctx.sign_ecdsa_with_noncedata(msg, sk, &entropy_source.get_secure_random_bytes()); - if sig.serialize_compact()[0] < 0x80 { - break sig; - } - }; - #[cfg(all(not(feature = "grind_signatures"), not(feature = "_test_vectors")))] - let sig = ctx.sign_ecdsa_with_noncedata(msg, sk, &entropy_source.get_secure_random_bytes()); - #[cfg(all(not(feature = "grind_signatures"), feature = "_test_vectors"))] - let sig = sign(ctx, msg, sk); - sig -} diff --git a/lightning/src/util/mod.rs b/lightning/src/util/mod.rs index e86885a83..f21160327 100644 --- a/lightning/src/util/mod.rs +++ b/lightning/src/util/mod.rs @@ -29,10 +29,6 @@ pub(crate) mod base32; pub(crate) mod atomic_counter; pub(crate) mod byte_utils; -pub(crate) mod chacha20; -#[cfg(not(fuzzing))] -pub(crate) mod poly1305; -pub(crate) mod chacha20poly1305rfc; pub(crate) mod transaction_utils; pub(crate) mod scid_utils; pub(crate) mod time; @@ -43,9 +39,6 @@ pub mod indexed_map; #[macro_use] pub(crate) mod macro_logger; -/// Cryptography utilities. -pub(crate) mod crypto; - // These have to come after macro_logger to build pub mod logger; pub mod config; diff --git a/lightning/src/util/poly1305.rs b/lightning/src/util/poly1305.rs deleted file mode 100644 index 1abda74b6..000000000 --- a/lightning/src/util/poly1305.rs +++ /dev/null @@ -1,341 +0,0 @@ -// This file is licensed under the Apache License, Version 2.0 or the MIT license -// , at your option. -// You may not use this file except in accordance with one or both of these -// licenses. - -// This is a port of Andrew Moons poly1305-donna -// https://github.com/floodyberry/poly1305-donna - -use core::cmp::min; -use core::convert::TryInto; - -#[derive(Clone, Copy)] -pub struct Poly1305 { - r : [u32; 5], - h : [u32; 5], - pad : [u32; 4], - leftover : usize, - buffer : [u8; 16], - finalized : bool, -} - -impl Poly1305 { - pub fn new(key: &[u8]) -> Poly1305 { - assert!(key.len() == 32); - let mut poly = Poly1305{ r: [0u32; 5], h: [0u32; 5], pad: [0u32; 4], leftover: 0, buffer: [0u8; 16], finalized: false }; - - // r &= 0xffffffc0ffffffc0ffffffc0fffffff - poly.r[0] = (u32::from_le_bytes(key[ 0.. 4].try_into().expect("len is 4")) ) & 0x3ffffff; - poly.r[1] = (u32::from_le_bytes(key[ 3.. 7].try_into().expect("len is 4")) >> 2) & 0x3ffff03; - poly.r[2] = (u32::from_le_bytes(key[ 6..10].try_into().expect("len is 4")) >> 4) & 0x3ffc0ff; - poly.r[3] = (u32::from_le_bytes(key[ 9..13].try_into().expect("len is 4")) >> 6) & 0x3f03fff; - poly.r[4] = (u32::from_le_bytes(key[12..16].try_into().expect("len is 4")) >> 8) & 0x00fffff; - - poly.pad[0] = u32::from_le_bytes(key[16..20].try_into().expect("len is 4")); - poly.pad[1] = u32::from_le_bytes(key[20..24].try_into().expect("len is 4")); - poly.pad[2] = u32::from_le_bytes(key[24..28].try_into().expect("len is 4")); - poly.pad[3] = u32::from_le_bytes(key[28..32].try_into().expect("len is 4")); - - poly - } - - fn block(&mut self, m: &[u8]) { - let hibit : u32 = if self.finalized { 0 } else { 1 << 24 }; - - let r0 = self.r[0]; - let r1 = self.r[1]; - let r2 = self.r[2]; - let r3 = self.r[3]; - let r4 = self.r[4]; - - let s1 = r1 * 5; - let s2 = r2 * 5; - let s3 = r3 * 5; - let s4 = r4 * 5; - - let mut h0 = self.h[0]; - let mut h1 = self.h[1]; - let mut h2 = self.h[2]; - let mut h3 = self.h[3]; - let mut h4 = self.h[4]; - - // h += m - h0 += (u32::from_le_bytes(m[ 0.. 4].try_into().expect("len is 4")) ) & 0x3ffffff; - h1 += (u32::from_le_bytes(m[ 3.. 7].try_into().expect("len is 4")) >> 2) & 0x3ffffff; - h2 += (u32::from_le_bytes(m[ 6..10].try_into().expect("len is 4")) >> 4) & 0x3ffffff; - h3 += (u32::from_le_bytes(m[ 9..13].try_into().expect("len is 4")) >> 6) & 0x3ffffff; - h4 += (u32::from_le_bytes(m[12..16].try_into().expect("len is 4")) >> 8) | hibit; - - // h *= r - let d0 = (h0 as u64 * r0 as u64) + (h1 as u64 * s4 as u64) + (h2 as u64 * s3 as u64) + (h3 as u64 * s2 as u64) + (h4 as u64 * s1 as u64); - let mut d1 = (h0 as u64 * r1 as u64) + (h1 as u64 * r0 as u64) + (h2 as u64 * s4 as u64) + (h3 as u64 * s3 as u64) + (h4 as u64 * s2 as u64); - let mut d2 = (h0 as u64 * r2 as u64) + (h1 as u64 * r1 as u64) + (h2 as u64 * r0 as u64) + (h3 as u64 * s4 as u64) + (h4 as u64 * s3 as u64); - let mut d3 = (h0 as u64 * r3 as u64) + (h1 as u64 * r2 as u64) + (h2 as u64 * r1 as u64) + (h3 as u64 * r0 as u64) + (h4 as u64 * s4 as u64); - let mut d4 = (h0 as u64 * r4 as u64) + (h1 as u64 * r3 as u64) + (h2 as u64 * r2 as u64) + (h3 as u64 * r1 as u64) + (h4 as u64 * r0 as u64); - - // (partial) h %= p - let mut c : u32; - c = (d0 >> 26) as u32; h0 = d0 as u32 & 0x3ffffff; - d1 += c as u64; c = (d1 >> 26) as u32; h1 = d1 as u32 & 0x3ffffff; - d2 += c as u64; c = (d2 >> 26) as u32; h2 = d2 as u32 & 0x3ffffff; - d3 += c as u64; c = (d3 >> 26) as u32; h3 = d3 as u32 & 0x3ffffff; - d4 += c as u64; c = (d4 >> 26) as u32; h4 = d4 as u32 & 0x3ffffff; - h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff; - h1 += c; - - self.h[0] = h0; - self.h[1] = h1; - self.h[2] = h2; - self.h[3] = h3; - self.h[4] = h4; - } - - pub fn finish(&mut self) { - if self.leftover > 0 { - self.buffer[self.leftover] = 1; - for i in self.leftover+1..16 { - self.buffer[i] = 0; - } - self.finalized = true; - let tmp = self.buffer; - self.block(&tmp); - } - - // fully carry h - let mut h0 = self.h[0]; - let mut h1 = self.h[1]; - let mut h2 = self.h[2]; - let mut h3 = self.h[3]; - let mut h4 = self.h[4]; - - let mut c : u32; - c = h1 >> 26; h1 = h1 & 0x3ffffff; - h2 += c; c = h2 >> 26; h2 = h2 & 0x3ffffff; - h3 += c; c = h3 >> 26; h3 = h3 & 0x3ffffff; - h4 += c; c = h4 >> 26; h4 = h4 & 0x3ffffff; - h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff; - h1 += c; - - // compute h + -p - let mut g0 = h0.wrapping_add(5); c = g0 >> 26; g0 &= 0x3ffffff; - let mut g1 = h1.wrapping_add(c); c = g1 >> 26; g1 &= 0x3ffffff; - let mut g2 = h2.wrapping_add(c); c = g2 >> 26; g2 &= 0x3ffffff; - let mut g3 = h3.wrapping_add(c); c = g3 >> 26; g3 &= 0x3ffffff; - let mut g4 = h4.wrapping_add(c).wrapping_sub(1 << 26); - - // select h if h < p, or h + -p if h >= p - let mut mask = (g4 >> (32 - 1)).wrapping_sub(1); - g0 &= mask; - g1 &= mask; - g2 &= mask; - g3 &= mask; - g4 &= mask; - mask = !mask; - h0 = (h0 & mask) | g0; - h1 = (h1 & mask) | g1; - h2 = (h2 & mask) | g2; - h3 = (h3 & mask) | g3; - h4 = (h4 & mask) | g4; - - // h = h % (2^128) - h0 = ((h0 ) | (h1 << 26)) & 0xffffffff; - h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff; - h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff; - h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff; - - // h = mac = (h + pad) % (2^128) - let mut f : u64; - f = h0 as u64 + self.pad[0] as u64 ; h0 = f as u32; - f = h1 as u64 + self.pad[1] as u64 + (f >> 32); h1 = f as u32; - f = h2 as u64 + self.pad[2] as u64 + (f >> 32); h2 = f as u32; - f = h3 as u64 + self.pad[3] as u64 + (f >> 32); h3 = f as u32; - - self.h[0] = h0; - self.h[1] = h1; - self.h[2] = h2; - self.h[3] = h3; - } - - pub fn input(&mut self, data: &[u8]) { - assert!(!self.finalized); - let mut m = data; - - if self.leftover > 0 { - let want = min(16 - self.leftover, m.len()); - for i in 0..want { - self.buffer[self.leftover+i] = m[i]; - } - m = &m[want..]; - self.leftover += want; - - if self.leftover < 16 { - return; - } - - // self.block(self.buffer[..]); - let tmp = self.buffer; - self.block(&tmp); - - self.leftover = 0; - } - - while m.len() >= 16 { - self.block(&m[0..16]); - m = &m[16..]; - } - - for i in 0..m.len() { - self.buffer[i] = m[i]; - } - self.leftover = m.len(); - } - - pub fn raw_result(&mut self, output: &mut [u8]) { - assert!(output.len() >= 16); - if !self.finalized{ - self.finish(); - } - output[0..4].copy_from_slice(&self.h[0].to_le_bytes()); - output[4..8].copy_from_slice(&self.h[1].to_le_bytes()); - output[8..12].copy_from_slice(&self.h[2].to_le_bytes()); - output[12..16].copy_from_slice(&self.h[3].to_le_bytes()); - } -} - -#[cfg(test)] -mod test { - use crate::prelude::*; - use core::iter::repeat; - - use crate::util::poly1305::Poly1305; - - fn poly1305(key: &[u8], msg: &[u8], mac: &mut [u8]) { - let mut poly = Poly1305::new(key); - poly.input(msg); - poly.raw_result(mac); - } - - #[test] - fn test_nacl_vector() { - let key = [ - 0xee,0xa6,0xa7,0x25,0x1c,0x1e,0x72,0x91, - 0x6d,0x11,0xc2,0xcb,0x21,0x4d,0x3c,0x25, - 0x25,0x39,0x12,0x1d,0x8e,0x23,0x4e,0x65, - 0x2d,0x65,0x1f,0xa4,0xc8,0xcf,0xf8,0x80, - ]; - - let msg = [ - 0x8e,0x99,0x3b,0x9f,0x48,0x68,0x12,0x73, - 0xc2,0x96,0x50,0xba,0x32,0xfc,0x76,0xce, - 0x48,0x33,0x2e,0xa7,0x16,0x4d,0x96,0xa4, - 0x47,0x6f,0xb8,0xc5,0x31,0xa1,0x18,0x6a, - 0xc0,0xdf,0xc1,0x7c,0x98,0xdc,0xe8,0x7b, - 0x4d,0xa7,0xf0,0x11,0xec,0x48,0xc9,0x72, - 0x71,0xd2,0xc2,0x0f,0x9b,0x92,0x8f,0xe2, - 0x27,0x0d,0x6f,0xb8,0x63,0xd5,0x17,0x38, - 0xb4,0x8e,0xee,0xe3,0x14,0xa7,0xcc,0x8a, - 0xb9,0x32,0x16,0x45,0x48,0xe5,0x26,0xae, - 0x90,0x22,0x43,0x68,0x51,0x7a,0xcf,0xea, - 0xbd,0x6b,0xb3,0x73,0x2b,0xc0,0xe9,0xda, - 0x99,0x83,0x2b,0x61,0xca,0x01,0xb6,0xde, - 0x56,0x24,0x4a,0x9e,0x88,0xd5,0xf9,0xb3, - 0x79,0x73,0xf6,0x22,0xa4,0x3d,0x14,0xa6, - 0x59,0x9b,0x1f,0x65,0x4c,0xb4,0x5a,0x74, - 0xe3,0x55,0xa5, - ]; - - let expected = [ - 0xf3,0xff,0xc7,0x70,0x3f,0x94,0x00,0xe5, - 0x2a,0x7d,0xfb,0x4b,0x3d,0x33,0x05,0xd9, - ]; - - let mut mac = [0u8; 16]; - poly1305(&key, &msg, &mut mac); - assert_eq!(&mac[..], &expected[..]); - - let mut poly = Poly1305::new(&key); - poly.input(&msg[0..32]); - poly.input(&msg[32..96]); - poly.input(&msg[96..112]); - poly.input(&msg[112..120]); - poly.input(&msg[120..124]); - poly.input(&msg[124..126]); - poly.input(&msg[126..127]); - poly.input(&msg[127..128]); - poly.input(&msg[128..129]); - poly.input(&msg[129..130]); - poly.input(&msg[130..131]); - poly.raw_result(&mut mac); - assert_eq!(&mac[..], &expected[..]); - } - - #[test] - fn donna_self_test() { - let wrap_key = [ - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - - let wrap_msg = [ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - ]; - - let wrap_mac = [ - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - - let mut mac = [0u8; 16]; - poly1305(&wrap_key, &wrap_msg, &mut mac); - assert_eq!(&mac[..], &wrap_mac[..]); - - let total_key = [ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xff, - 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - ]; - - let total_mac = [ - 0x64, 0xaf, 0xe2, 0xe8, 0xd6, 0xad, 0x7b, 0xbd, - 0xd2, 0x87, 0xf9, 0x7c, 0x44, 0x62, 0x3d, 0x39, - ]; - - let mut tpoly = Poly1305::new(&total_key); - for i in 0..256 { - let key: Vec = repeat(i as u8).take(32).collect(); - let msg: Vec = repeat(i as u8).take(256).collect(); - let mut mac = [0u8; 16]; - poly1305(&key[..], &msg[0..i], &mut mac); - tpoly.input(&mac); - } - tpoly.raw_result(&mut mac); - assert_eq!(&mac[..], &total_mac[..]); - } - - #[test] - fn test_tls_vectors() { - // from http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04 - let key = b"this is 32-byte key for Poly1305"; - let msg = [0u8; 32]; - let expected = [ - 0x49, 0xec, 0x78, 0x09, 0x0e, 0x48, 0x1e, 0xc6, - 0xc2, 0x6b, 0x33, 0xb9, 0x1c, 0xcc, 0x03, 0x07, - ]; - let mut mac = [0u8; 16]; - poly1305(key, &msg, &mut mac); - assert_eq!(&mac[..], &expected[..]); - - let msg = b"Hello world!"; - let expected= [ - 0xa6, 0xf7, 0x45, 0x00, 0x8f, 0x81, 0xc9, 0x16, - 0xa2, 0x0d, 0xcc, 0x74, 0xee, 0xf2, 0xb2, 0xf0, - ]; - poly1305(key, msg, &mut mac); - assert_eq!(&mac[..], &expected[..]); - } -} diff --git a/lightning/src/util/scid_utils.rs b/lightning/src/util/scid_utils.rs index fbbcc69a1..45b24fd14 100644 --- a/lightning/src/util/scid_utils.rs +++ b/lightning/src/util/scid_utils.rs @@ -69,7 +69,7 @@ pub(crate) mod fake_scid { use bitcoin::blockdata::constants::ChainHash; use bitcoin::network::constants::Network; use crate::sign::EntropySource; - use crate::util::chacha20::ChaCha20; + use crate::crypto::chacha20::ChaCha20; use crate::util::scid_utils; use core::convert::TryInto;