From 06b8528a559933cbf4b4b5e6226673d981e1b44c Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Fri, 19 Sep 2025 13:00:23 +0200 Subject: [PATCH 1/2] Introduce `chacha20poly1305` crate, check compatibility We take a dependency on `rust-bitcoin`'s new-ish `chacha20poly1305` crate and check compatibility with our previous hand-rolled implementation (might be redundant, but better safe than sorry). --- Cargo.toml | 1 + src/crypto/chacha20poly1305.rs | 45 ++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index f3477fb..db07506 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ serde = { version = "1.0.196", default-features = false, features = ["serde_deri serde_json = { version = "1.0.113", default-features = false, optional = true } bitcoin_hashes = "0.14.0" +chacha20-poly1305 = "0.1.2" [target.'cfg(genproto)'.build-dependencies] prost-build = { version = "0.11.3" } diff --git a/src/crypto/chacha20poly1305.rs b/src/crypto/chacha20poly1305.rs index c8576fd..176ebd1 100644 --- a/src/crypto/chacha20poly1305.rs +++ b/src/crypto/chacha20poly1305.rs @@ -198,3 +198,48 @@ mod real_chachapoly { } pub use self::real_chachapoly::ChaCha20Poly1305; + +mod tests { + #[test] + fn check_chacha_compat_old_to_new() { + let data_encryption_key = [42u8; 32]; + let mut nonce = [0u8; 12]; + nonce[4..].copy_from_slice(&[48u8; 8]); + let aad = b"asdf"; + + let mut old_cipher = super::ChaCha20Poly1305::new(&data_encryption_key, &nonce, aad); + + let mut tag = [0u8; 16]; + let mut very_secret_data = vec![89u8; 128]; + let expected_very_secret_data = very_secret_data.clone(); + old_cipher.encrypt_inplace(&mut very_secret_data, &mut tag); + + let new_key = chacha20_poly1305::Key::new(data_encryption_key); + let new_nonce = chacha20_poly1305::Nonce::new(nonce); + let new_cipher = chacha20_poly1305::ChaCha20Poly1305::new(new_key, new_nonce); + + new_cipher.decrypt(&mut very_secret_data, tag, Some(aad)).unwrap(); + assert_eq!(very_secret_data, expected_very_secret_data); + } + + #[test] + fn check_chacha_compat_new_to_old() { + let data_encryption_key = [42u8; 32]; + let mut nonce = [0u8; 12]; + nonce[4..].copy_from_slice(&[48u8; 8]); + let aad = b"asdf"; + + let new_key = chacha20_poly1305::Key::new(data_encryption_key.clone()); + let new_nonce = chacha20_poly1305::Nonce::new(nonce.clone()); + let new_cipher = chacha20_poly1305::ChaCha20Poly1305::new(new_key, new_nonce); + + let mut very_secret_data = vec![89u8; 128]; + let expected_very_secret_data = very_secret_data.clone(); + let tag = new_cipher.encrypt(&mut very_secret_data, Some(aad)); + + let mut old_cipher = super::ChaCha20Poly1305::new(&data_encryption_key, &nonce, aad); + + old_cipher.decrypt_inplace(&mut very_secret_data, &tag).unwrap(); + assert_eq!(very_secret_data, expected_very_secret_data); + } +} From c1e572ee5fd9e576daa237924d35a9b50b8f9fed Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Fri, 19 Sep 2025 13:21:55 +0200 Subject: [PATCH 2/2] Switch to use `rust-bitcoin`'s `chacha20poly1305` implementation We delete our homebrewn implementation and switch to use the recently-published `chacha20_poly1305` crate. --- src/crypto/chacha20.rs | 495 --------------------------------- src/crypto/chacha20poly1305.rs | 245 ---------------- src/crypto/mod.rs | 3 - src/crypto/poly1305.rs | 391 -------------------------- src/lib.rs | 3 - src/util/key_obfuscator.rs | 33 ++- src/util/storable_builder.rs | 36 ++- 7 files changed, 44 insertions(+), 1162 deletions(-) delete mode 100644 src/crypto/chacha20.rs delete mode 100644 src/crypto/chacha20poly1305.rs delete mode 100644 src/crypto/mod.rs delete mode 100644 src/crypto/poly1305.rs diff --git a/src/crypto/chacha20.rs b/src/crypto/chacha20.rs deleted file mode 100644 index 6bc4971..0000000 --- a/src/crypto/chacha20.rs +++ /dev/null @@ -1,495 +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. - -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; - 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; - 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; - 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; - fn shr(self, rhs: u32x4) -> u32x4 { - u32x4(self.0 >> rhs.0, self.1 >> rhs.1, self.2 >> rhs.2, self.3 >> rhs.3) - } - } - - impl ::core::ops::Shl for u32x4 { - type Output = u32x4; - fn shl(self, rhs: u32x4) -> u32x4 { - u32x4(self.0 << rhs.0, self.1 << rhs.1, self.2 << rhs.2, self.3 << rhs.3) - } - } - - impl u32x4 { - fn from_bytes(bytes: &[u8]) -> Self { - assert_eq!(bytes.len(), 4 * 4); - Self( - u32::from_le_bytes(bytes[0 * 4..1 * 4].try_into().expect("len is 4")), - u32::from_le_bytes(bytes[1 * 4..2 * 4].try_into().expect("len is 4")), - u32::from_le_bytes(bytes[2 * 4..3 * 4].try_into().expect("len is 4")), - u32::from_le_bytes(bytes[3 * 4..4 * 4].try_into().expect("len is 4")), - ) - } - } - - const BLOCK_SIZE: usize = 64; - - #[derive(Clone, Copy)] - struct ChaChaState { - 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, S16); - $state.c = $state.c + $state.d; - rotate!($state.b, $state.c, S12); - $state.a = $state.a + $state.b; - rotate!($state.d, $state.a, S8); - $state.c = $state.c + $state.d; - rotate!($state.b, $state.c, S7); - }}; - } - - macro_rules! rotate { - ($a: expr, $b: expr, $c:expr) => {{ - let v = $a ^ $b; - let r = S32 - $c; - let right = v >> r; - $a = (v << $c) ^ right - }}; - } - - const S32: u32x4 = u32x4(32, 32, 32, 32); - const S16: u32x4 = u32x4(16, 16, 16, 16); - const S12: u32x4 = u32x4(12, 12, 12, 12); - const S8: u32x4 = u32x4(8, 8, 8, 8); - const S7: u32x4 = u32x4(7, 7, 7, 7); - - 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 } - } - - 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; - } - } - } -} - -pub use self::real_chacha::ChaCha20; - -#[cfg(test)] -mod test { - 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); - } - } -} diff --git a/src/crypto/chacha20poly1305.rs b/src/crypto/chacha20poly1305.rs deleted file mode 100644 index 176ebd1..0000000 --- a/src/crypto/chacha20poly1305.rs +++ /dev/null @@ -1,245 +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 - -mod real_chachapoly { - use crate::crypto::chacha20::ChaCha20; - use crate::crypto::poly1305::Poly1305; - use core::ptr::{read_volatile, write_volatile}; - - #[derive(Clone, Copy)] - pub struct ChaCha20Poly1305 { - cipher: ChaCha20, - mac: Poly1305, - finished: bool, - data_len: usize, - aad_len: u64, - } - - #[allow(dead_code, unused)] - impl ChaCha20Poly1305 { - #[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]) -> ChaCha20Poly1305 { - 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); - ChaCha20Poly1305::pad_mac_16(&mut mac, aad.len()); - - ChaCha20Poly1305 { - 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); - ChaCha20Poly1305::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_inplace(&mut self, input_output: &mut [u8], out_tag: &mut [u8]) { - assert!(self.finished == false); - self.encrypt_in_place(input_output); - self.finish_and_get_tag(out_tag); - } - - pub fn decrypt_inplace(&mut self, input_output: &mut [u8], tag: &[u8]) -> Result<(), ()> { - assert!(self.finished == false); - self.decrypt_in_place(input_output); - if self.finish_and_check_tag(tag) { - Ok(()) - } else { - Err(()) - } - } - - // 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); - ChaCha20Poly1305::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(); - ChaCha20Poly1305::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 ChaCha20Poly1305::fixed_time_eq(&calc_tag, tag) { - self.cipher.process(input, output); - true - } else { - false - } - } - - // 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 `decrypt_in_place`, this method must be used to finish - // decrypting and check the tag. Returns whether the tag is valid. - pub(super) fn finish_and_check_tag(&mut self, tag: &[u8]) -> bool { - debug_assert!(self.finished == false); - self.finished = true; - ChaCha20Poly1305::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 ChaCha20Poly1305::fixed_time_eq(&calc_tag, tag) { - true - } else { - false - } - } - pub(super) fn fixed_time_eq(a: &[u8], b: &[u8]) -> bool { - assert!(a.len() == b.len()); - let count = a.len(); - let lhs = &a[..count]; - let rhs = &b[..count]; - - let mut r: u8 = 0; - for i in 0..count { - let mut rs = unsafe { read_volatile(&r) }; - rs |= lhs[i] ^ rhs[i]; - unsafe { - write_volatile(&mut r, rs); - } - } - { - let mut t = unsafe { read_volatile(&r) }; - t |= t >> 4; - unsafe { - write_volatile(&mut r, t); - } - } - { - let mut t = unsafe { read_volatile(&r) }; - t |= t >> 2; - unsafe { - write_volatile(&mut r, t); - } - } - { - let mut t = unsafe { read_volatile(&r) }; - t |= t >> 1; - unsafe { - write_volatile(&mut r, t); - } - } - unsafe { (read_volatile(&r) & 1) == 0 } - } - } -} - -pub use self::real_chachapoly::ChaCha20Poly1305; - -mod tests { - #[test] - fn check_chacha_compat_old_to_new() { - let data_encryption_key = [42u8; 32]; - let mut nonce = [0u8; 12]; - nonce[4..].copy_from_slice(&[48u8; 8]); - let aad = b"asdf"; - - let mut old_cipher = super::ChaCha20Poly1305::new(&data_encryption_key, &nonce, aad); - - let mut tag = [0u8; 16]; - let mut very_secret_data = vec![89u8; 128]; - let expected_very_secret_data = very_secret_data.clone(); - old_cipher.encrypt_inplace(&mut very_secret_data, &mut tag); - - let new_key = chacha20_poly1305::Key::new(data_encryption_key); - let new_nonce = chacha20_poly1305::Nonce::new(nonce); - let new_cipher = chacha20_poly1305::ChaCha20Poly1305::new(new_key, new_nonce); - - new_cipher.decrypt(&mut very_secret_data, tag, Some(aad)).unwrap(); - assert_eq!(very_secret_data, expected_very_secret_data); - } - - #[test] - fn check_chacha_compat_new_to_old() { - let data_encryption_key = [42u8; 32]; - let mut nonce = [0u8; 12]; - nonce[4..].copy_from_slice(&[48u8; 8]); - let aad = b"asdf"; - - let new_key = chacha20_poly1305::Key::new(data_encryption_key.clone()); - let new_nonce = chacha20_poly1305::Nonce::new(nonce.clone()); - let new_cipher = chacha20_poly1305::ChaCha20Poly1305::new(new_key, new_nonce); - - let mut very_secret_data = vec![89u8; 128]; - let expected_very_secret_data = very_secret_data.clone(); - let tag = new_cipher.encrypt(&mut very_secret_data, Some(aad)); - - let mut old_cipher = super::ChaCha20Poly1305::new(&data_encryption_key, &nonce, aad); - - old_cipher.decrypt_inplace(&mut very_secret_data, &tag).unwrap(); - assert_eq!(very_secret_data, expected_very_secret_data); - } -} diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs deleted file mode 100644 index a5d7bc7..0000000 --- a/src/crypto/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub(crate) mod chacha20; -pub(crate) mod chacha20poly1305; -pub(crate) mod poly1305; diff --git a/src/crypto/poly1305.rs b/src/crypto/poly1305.rs deleted file mode 100644 index 3a56d20..0000000 --- a/src/crypto/poly1305.rs +++ /dev/null @@ -1,391 +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, -} - -#[allow(dead_code, unused)] -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 crate::crypto::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/src/lib.rs b/src/lib.rs index d418d62..2f808e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,8 +23,5 @@ pub mod types; /// Contains utils for encryption, requests-retries etc. pub mod util; -// Encryption-Decryption related crate-only helpers. -pub(crate) mod crypto; - /// A collection of header providers. pub mod headers; diff --git a/src/util/key_obfuscator.rs b/src/util/key_obfuscator.rs index 056e050..21d1e14 100644 --- a/src/util/key_obfuscator.rs +++ b/src/util/key_obfuscator.rs @@ -4,7 +4,7 @@ use base64::prelude::BASE64_STANDARD_NO_PAD; use base64::Engine; use bitcoin_hashes::{sha256, Hash, HashEngine, Hmac, HmacEngine}; -use crate::crypto::chacha20poly1305::ChaCha20Poly1305; +use chacha20_poly1305::{ChaCha20Poly1305, Key, Nonce}; /// [`KeyObfuscator`] is a utility to obfuscate and deobfuscate storage /// keys to be used for VSS operations. @@ -74,9 +74,14 @@ impl KeyObfuscator { // Split obfuscated_key into ciphertext, tag(for ciphertext), wrapped_nonce, tag(for wrapped_nonce). let (ciphertext, remaining) = obfuscated_key_bytes .split_at(obfuscated_key_bytes.len() - TAG_LENGTH - NONCE_LENGTH - TAG_LENGTH); - let (tag, remaining) = remaining.split_at(TAG_LENGTH); - let (wrapped_nonce_bytes, wrapped_nonce_tag) = remaining.split_at(NONCE_LENGTH); - debug_assert_eq!(wrapped_nonce_tag.len(), TAG_LENGTH); + let (tag_bytes, remaining) = remaining.split_at(TAG_LENGTH); + let mut tag = [0u8; TAG_LENGTH]; + tag.copy_from_slice(&tag_bytes); + + let (wrapped_nonce_bytes, wrapped_nonce_tag_bytes) = remaining.split_at(NONCE_LENGTH); + + let mut wrapped_nonce_tag = [0u8; TAG_LENGTH]; + wrapped_nonce_tag.copy_from_slice(&wrapped_nonce_tag_bytes[..TAG_LENGTH]); // Unwrap wrapped_nonce to get nonce. let mut wrapped_nonce = [0u8; NONCE_LENGTH]; @@ -90,9 +95,10 @@ impl KeyObfuscator { })?; // Decrypt ciphertext using nonce. - let mut cipher = ChaCha20Poly1305::new(&self.obfuscation_key, &wrapped_nonce, &[]); + let cipher = + ChaCha20Poly1305::new(Key::new(self.obfuscation_key), Nonce::new(wrapped_nonce)); let mut ciphertext = ciphertext.to_vec(); - cipher.decrypt_inplace(&mut ciphertext, tag).map_err(|_| { + cipher.decrypt(&mut ciphertext, tag, None).map_err(|_| { let msg = format!("Failed to decrypt key: {}, Invalid Tag.", obfuscated_key); Error::new(ErrorKind::InvalidData, msg) })?; @@ -110,25 +116,24 @@ impl KeyObfuscator { /// Encrypts the given plaintext in-place using a HMAC generated nonce. fn encrypt( &self, mut plaintext: &mut [u8], initial_nonce_material: &[u8], - ) -> ([u8; 12], [u8; 16]) { + ) -> ([u8; NONCE_LENGTH], [u8; TAG_LENGTH]) { let nonce = self.generate_synthetic_nonce(initial_nonce_material); - let mut cipher = ChaCha20Poly1305::new(&self.obfuscation_key, &nonce, &[]); - let mut tag = [0u8; TAG_LENGTH]; - cipher.encrypt_inplace(&mut plaintext, &mut tag); + let cipher = ChaCha20Poly1305::new(Key::new(self.obfuscation_key), Nonce::new(nonce)); + let tag = cipher.encrypt(&mut plaintext, None); (nonce, tag) } /// Decrypts the given ciphertext in-place using a HMAC generated nonce. fn decrypt( - &self, mut ciphertext: &mut [u8], initial_nonce_material: &[u8], tag: &[u8], + &self, mut ciphertext: &mut [u8], initial_nonce_material: &[u8], tag: [u8; TAG_LENGTH], ) -> Result<(), ()> { let nonce = self.generate_synthetic_nonce(initial_nonce_material); - let mut cipher = ChaCha20Poly1305::new(&self.obfuscation_key, &nonce, &[]); - cipher.decrypt_inplace(&mut ciphertext, tag) + let cipher = ChaCha20Poly1305::new(Key::new(self.obfuscation_key), Nonce::new(nonce)); + cipher.decrypt(&mut ciphertext, tag, None).map_err(|_| ()) } /// Generate a HMAC based nonce using provided `initial_nonce_material`. - fn generate_synthetic_nonce(&self, initial_nonce_material: &[u8]) -> [u8; 12] { + fn generate_synthetic_nonce(&self, initial_nonce_material: &[u8]) -> [u8; NONCE_LENGTH] { let hmac = Self::hkdf(&self.hashing_key, initial_nonce_material); let mut nonce = [0u8; NONCE_LENGTH]; nonce[4..].copy_from_slice(&hmac[..8]); diff --git a/src/util/storable_builder.rs b/src/util/storable_builder.rs index 41d254b..c8c53ae 100644 --- a/src/util/storable_builder.rs +++ b/src/util/storable_builder.rs @@ -1,7 +1,8 @@ -use crate::crypto::chacha20poly1305::ChaCha20Poly1305; use crate::types::{EncryptionMetadata, PlaintextBlob, Storable}; use ::prost::Message; -use std::borrow::Borrow; + +use chacha20_poly1305::{ChaCha20Poly1305, Key, Nonce}; + use std::io; use std::io::{Error, ErrorKind}; @@ -30,6 +31,8 @@ pub trait EntropySource { } const CHACHA20_CIPHER_NAME: &'static str = "ChaCha20Poly1305"; +const TAG_LENGTH: usize = 16; +const NONCE_LENGTH: usize = 12; impl StorableBuilder { /// Creates a [`Storable`] that can be serialized and stored as `value` in [`PutObjectRequest`]. @@ -43,19 +46,18 @@ impl StorableBuilder { pub fn build( &self, input: Vec, version: i64, data_encryption_key: &[u8; 32], aad: &[u8], ) -> Storable { - let mut nonce = vec![0u8; 12]; + let mut nonce = [0u8; NONCE_LENGTH]; self.entropy_source.fill_bytes(&mut nonce[4..]); let mut data_blob = PlaintextBlob { value: input, version }.encode_to_vec(); - let mut cipher = ChaCha20Poly1305::new(data_encryption_key, &nonce, aad); - let mut tag = vec![0u8; 16]; - cipher.encrypt_inplace(&mut data_blob, &mut tag); + let cipher = ChaCha20Poly1305::new(Key::new(*data_encryption_key), Nonce::new(nonce)); + let tag = cipher.encrypt(&mut data_blob, Some(aad)); Storable { data: data_blob, encryption_metadata: Some(EncryptionMetadata { - nonce, - tag, + nonce: nonce.to_vec(), + tag: tag.to_vec(), cipher_format: CHACHA20_CIPHER_NAME.to_string(), }), } @@ -71,11 +73,23 @@ impl StorableBuilder { let encryption_metadata = storable .encryption_metadata .ok_or_else(|| Error::new(ErrorKind::InvalidData, "Invalid Metadata"))?; - let mut cipher = - ChaCha20Poly1305::new(data_encryption_key, &encryption_metadata.nonce, aad); + + if encryption_metadata.nonce.len() != NONCE_LENGTH { + return Err(Error::new(ErrorKind::InvalidData, "Invalid Metadata")); + } + let mut nonce = [0u8; NONCE_LENGTH]; + nonce.copy_from_slice(&encryption_metadata.nonce); + + let cipher = ChaCha20Poly1305::new(Key::new(*data_encryption_key), Nonce::new(nonce)); + + if encryption_metadata.tag.len() != TAG_LENGTH { + return Err(Error::new(ErrorKind::InvalidData, "Invalid Metadata")); + } + let mut tag = [0u8; TAG_LENGTH]; + tag.copy_from_slice(&encryption_metadata.tag); cipher - .decrypt_inplace(&mut storable.data, encryption_metadata.tag.borrow()) + .decrypt(&mut storable.data, tag, Some(aad)) .map_err(|_| Error::new(ErrorKind::InvalidData, "Invalid Tag"))?; let data_blob = PlaintextBlob::decode(&storable.data[..])