diff --git a/Cargo.lock b/Cargo.lock index 0ed9161..88c8a82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] @@ -74,6 +74,7 @@ name = "bip324" version = "0.5.0" dependencies = [ "bitcoin", + "chacha20-poly1305", "futures", "hex-conservative", "rand", @@ -119,9 +120,9 @@ checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" [[package]] name = "bitcoin-io" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340e09e8399c7bd8912f495af6aa58bea0c9214773417ffaa8f6460f93aaee56" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" [[package]] name = "bitcoin-units" @@ -156,9 +157,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "cargo_toml" @@ -172,9 +173,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.24" +version = "1.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" +checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" dependencies = [ "shlex", ] @@ -185,6 +186,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chacha20-poly1305" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ac8be588b1de2b7f1537ed39ba453a388d2cce60ce78ef5db449f71bebe58ba" + [[package]] name = "configure_me" version = "0.4.0" @@ -239,9 +246,9 @@ checksum = "6b6129284da9f7e5296cc22183a63f24300e945e297705dcc0672f7df01d62c8" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -253,9 +260,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -263,33 +270,33 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -315,15 +322,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "hermit-abi" @@ -360,9 +367,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", "hashbrown", @@ -381,9 +388,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.159" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] name = "lock_api" @@ -439,9 +446,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.4" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] @@ -477,9 +484,9 @@ checksum = "14248cc8eced350e20122a291613de29e4fa129ba2731818c4cdbb44fccd3e55" [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -498,9 +505,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -555,9 +562,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -567,9 +574,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -615,18 +622,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.210" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", @@ -684,9 +691,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.79" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -704,9 +711,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.37.0" +version = "1.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" dependencies = [ "backtrace", "bytes", @@ -723,9 +730,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", @@ -805,7 +812,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -826,6 +833,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" diff --git a/protocol/Cargo.toml b/protocol/Cargo.toml index 87b61bb..8d87a42 100644 --- a/protocol/Cargo.toml +++ b/protocol/Cargo.toml @@ -12,8 +12,8 @@ rust-version = "1.63.0" default = ["std"] async = ["std", "futures/std"] tokio = ["std", "tokio/io-util"] -std = ["alloc", "bitcoin/std", "rand/std", "rand/std_rng"] -alloc = [] +std = ["alloc", "bitcoin/std", "chacha20-poly1305/std", "rand/std", "rand/std_rng"] +alloc = ["chacha20-poly1305/alloc"] [dependencies] futures = { version = "0.3.30", default-features = false, optional = true } @@ -21,6 +21,7 @@ futures = { version = "0.3.30", default-features = false, optional = true } tokio = { version = ">=1.37.0, <1.39.0", default-features = false, optional = true } rand = { version = "0.8.0", default-features = false } bitcoin = { version = "0.32.4", default-features = false } +chacha20-poly1305 = { version = "0.1.1", default-features = false } [dev-dependencies] hex = { package = "hex-conservative", version = "0.2.0" } diff --git a/protocol/README.md b/protocol/README.md index a1277be..f50a969 100644 --- a/protocol/README.md +++ b/protocol/README.md @@ -29,8 +29,6 @@ BIP324 elects to use the ChaCha20Poly1305 Authenticated Encryption with Addition Poly1305 is a purpose-built MAC, as opposed to something like an HMAC using SHA256 which leverages an existing hash scheme to build a message authentication code. Purpose-built introduces new complexity, but also allows for increased performance. -ChaCha20 and Poly1305 are both implemented in this crate to keep dependencies to a minimum. - ## Development The implementation is tested against vectors from the BIP324 reference and a number of additional library tests. diff --git a/protocol/src/chacha20poly1305/chacha20.rs b/protocol/src/chacha20poly1305/chacha20.rs deleted file mode 100644 index 876dced..0000000 --- a/protocol/src/chacha20poly1305/chacha20.rs +++ /dev/null @@ -1,470 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 - -//! The ChaCha20 stream cipher based on RFC7539. - -/// The first four words (32-bit) of the ChaCha stream cipher state are constants. -const WORD_1: u32 = 0x61707865; -const WORD_2: u32 = 0x3320646e; -const WORD_3: u32 = 0x79622d32; -const WORD_4: u32 = 0x6b206574; - -/// Each quarter round of ChaCha scrambles 4 words (32-bit) of the state -/// using some Addition (mod 2^32), Rotation, and XOR (ARX). 8 quarter -/// rounds make up a round. A block is broken up into 16 32-bit words -/// and each quarter round takes 4 words as input. -const CHACHA_ROUND_INDICIES: [(usize, usize, usize, usize); 8] = [ - // The first 4 rounds are rows of a 4x4 matrix - // of the block broken up into 32-bit words. - (0, 4, 8, 12), - (1, 5, 9, 13), - (2, 6, 10, 14), - (3, 7, 11, 15), - // The first 4 rounds are diagonals of a 4x4 matrix - // of the block broken up into 32-bit words. - (0, 5, 10, 15), - (1, 6, 11, 12), - (2, 7, 8, 13), - (3, 4, 9, 14), -]; - -/// The cipher's block size is 64 bytes. -const CHACHA_BLOCKSIZE: usize = 64; - -/// The ChaCha20 stream cipher. -#[derive(Debug)] -pub(crate) struct ChaCha20 { - /// A 256 bit secret session key shared by the parties communitcating. - key: [u8; 32], - /// A 96 bit initialization vector (IV), or nonce. A key/nonce pair should only be used once. - nonce: [u8; 12], - /// Internal block index of keystream. - block_count: u32, - /// Interal byte offset index of the block_count. - seek_offset_bytes: usize, -} - -impl ChaCha20 { - /// Make a new instance of ChaCha20 from an index in the keystream. - pub fn new(key: [u8; 32], nonce: [u8; 12], seek: u32) -> Self { - let block_count = seek / 64; - let seek_offset_bytes = (seek % 64) as usize; - ChaCha20 { - key, - nonce, - block_count, - seek_offset_bytes, - } - } - - /// Make a new instance of ChaCha20 from a block in the keystream. - pub fn new_from_block(key: [u8; 32], nonce: [u8; 12], block: u32) -> Self { - ChaCha20 { - key, - nonce, - block_count: block, - seek_offset_bytes: 0, - } - } - - /// Apply the keystream to a message. - pub fn apply_keystream(&mut self, to: &mut [u8]) { - let num_full_blocks = to.len() / CHACHA_BLOCKSIZE; - let mut j = 0; - while j < num_full_blocks { - let kstream = keystream_at_slice( - self.key, - self.nonce, - self.block_count, - self.seek_offset_bytes, - ); - for (c, k) in to[j * CHACHA_BLOCKSIZE..(j + 1) * CHACHA_BLOCKSIZE] - .iter_mut() - .zip(kstream.iter()) - { - *c ^= *k - } - j += 1; - self.block_count += 1; - } - if to.len() % 64 > 0 { - let kstream = keystream_at_slice( - self.key, - self.nonce, - self.block_count, - self.seek_offset_bytes, - ); - for (c, k) in to[j * CHACHA_BLOCKSIZE..].iter_mut().zip(kstream.iter()) { - *c ^= *k - } - self.block_count += 1; - } - } - - /// Get the keystream block at a specified block. - pub(crate) fn get_keystream(&mut self, block: u32) -> [u8; 64] { - self.block(block); - keystream_at_slice( - self.key, - self.nonce, - self.block_count, - self.seek_offset_bytes, - ) - } - - /// Update the index of the keystream to the given byte. - pub(crate) fn seek(&mut self, seek: u32) { - self.block_count = seek / 64; - self.seek_offset_bytes = (seek % 64) as usize; - } - - /// Update the index of the keystream to a block. - pub(crate) fn block(&mut self, block: u32) { - self.block_count = block; - self.seek_offset_bytes = 0; - } -} - -fn quarter_round(state: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize) { - state[a] = state[a].wrapping_add(state[b]); - state[d] = (state[d] ^ state[a]).rotate_left(16); - state[c] = state[c].wrapping_add(state[d]); - state[b] = (state[b] ^ state[c]).rotate_left(12); - state[a] = state[a].wrapping_add(state[b]); - state[d] = (state[d] ^ state[a]).rotate_left(8); - state[c] = state[c].wrapping_add(state[d]); - state[b] = (state[b] ^ state[c]).rotate_left(7); -} - -fn double_round(state: &mut [u32; 16]) { - for (a, b, c, d) in CHACHA_ROUND_INDICIES { - quarter_round(state, a, b, c, d); - } -} - -fn chacha_block(state: &mut [u32; 16]) { - let initial_state = *state; - for _ in 0..10 { - double_round(state) - } - for (modified, initial) in state.iter_mut().zip(initial_state.iter()) { - *modified = modified.wrapping_add(*initial) - } -} - -fn prepare_state(key: [u8; 32], nonce: [u8; 12], count: u32) -> [u32; 16] { - let mut state: [u32; 16] = [0; 16]; - state[0] = WORD_1; - state[1] = WORD_2; - state[2] = WORD_3; - state[3] = WORD_4; - state[4] = u32::from_le_bytes( - key[0..4] - .try_into() - .expect("infalliable conversion to 4 byte array"), - ); - state[5] = u32::from_le_bytes( - key[4..8] - .try_into() - .expect("infalliable conversion to 4 byte array"), - ); - state[6] = u32::from_le_bytes( - key[8..12] - .try_into() - .expect("infalliable conversion to 4 byte array"), - ); - state[7] = u32::from_le_bytes( - key[12..16] - .try_into() - .expect("infalliable conversion to 4 byte array"), - ); - state[8] = u32::from_le_bytes( - key[16..20] - .try_into() - .expect("infalliable conversion to 4 byte array"), - ); - state[9] = u32::from_le_bytes( - key[20..24] - .try_into() - .expect("infalliable conversion to 4 byte array"), - ); - state[10] = u32::from_le_bytes( - key[24..28] - .try_into() - .expect("infalliable conversion to 4 byte array"), - ); - state[11] = u32::from_le_bytes( - key[28..32] - .try_into() - .expect("infalliable conversion to 4 byte array"), - ); - state[12] = count; - state[13] = u32::from_le_bytes( - nonce[0..4] - .try_into() - .expect("infalliable conversion to 4 byte array"), - ); - state[14] = u32::from_le_bytes( - nonce[4..8] - .try_into() - .expect("infalliable conversion to 4 byte array"), - ); - state[15] = u32::from_le_bytes( - nonce[8..12] - .try_into() - .expect("infalliable conversion to 4 byte array"), - ); - state -} - -fn keystream_from_state(state: &mut [u32; 16]) -> [u8; 64] { - let mut keystream: [u8; 64] = [0; 64]; - let mut index = 0; - for &element in state.iter() { - let bytes = element.to_le_bytes(); - keystream[index..index + 4].copy_from_slice(&bytes); - index += 4; - } - keystream -} - -fn keystream_at_slice(key: [u8; 32], nonce: [u8; 12], count: u32, seek: usize) -> [u8; 64] { - let mut keystream: [u8; 128] = [0; 128]; - let mut state = prepare_state(key, nonce, count); - chacha_block(&mut state); - let first_half = keystream_from_state(&mut state); - let mut state = prepare_state(key, nonce, count + 1); - chacha_block(&mut state); - let second_half = keystream_from_state(&mut state); - keystream[..64].copy_from_slice(&first_half); - keystream[64..].copy_from_slice(&second_half); - let kstream: [u8; 64] = keystream[seek..seek + 64].try_into().expect("infallable"); - kstream -} - -#[cfg(test)] -#[cfg(feature = "alloc")] -mod tests { - use super::*; - use alloc::vec::Vec; - use hex::prelude::*; - #[cfg(feature = "std")] - use rand::Rng; - - #[test] - fn test_quater_round() { - let a: u32 = 0x11111111; - let b: u32 = 0x01020304; - let c: u32 = 0x9b8d6f43; - let d: u32 = 0x01234567; - let mut state = [a, b, c, d, a, b, c, d, a, b, c, d, a, b, c, d]; - quarter_round(&mut state, 0, 1, 2, 3); - assert_eq!(state[0].to_be_bytes().to_lower_hex_string(), "ea2a92f4"); - assert_eq!(state[1].to_be_bytes().to_lower_hex_string(), "cb1cf8ce"); - assert_eq!(state[2].to_be_bytes().to_lower_hex_string(), "4581472e"); - assert_eq!(state[3].to_be_bytes().to_lower_hex_string(), "5881c4bb"); - } - - #[test] - #[allow(clippy::many_single_char_names)] - fn test_quater_round_on_block() { - let a: u32 = 0x879531e0; - let b: u32 = 0xc5ecf37d; - let c: u32 = 0x516461b1; - let d: u32 = 0xc9a62f8a; - let e: u32 = 0x44c20ef3; - let f: u32 = 0x3390af7f; - let g: u32 = 0xd9fc690b; - let h: u32 = 0x2a5f714c; - let i: u32 = 0x53372767; - let j: u32 = 0xb00a5631; - let k: u32 = 0x974c541a; - let l: u32 = 0x359e9963; - let m: u32 = 0x5c971061; - let n: u32 = 0x3d631689; - let o: u32 = 0x2098d9d6; - let p: u32 = 0x91dbd320; - let mut state = [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p]; - quarter_round(&mut state, 2, 7, 8, 13); - assert_eq!(state[2].to_be_bytes().to_lower_hex_string(), "bdb886dc"); - } - - #[test] - #[allow(clippy::many_single_char_names)] - fn test_block_fn() { - let a: u32 = 0x61707865; - let b: u32 = 0x3320646e; - let c: u32 = 0x79622d32; - let d: u32 = 0x6b206574; - let e: u32 = 0x03020100; - let f: u32 = 0x07060504; - let g: u32 = 0x0b0a0908; - let h: u32 = 0x0f0e0d0c; - let i: u32 = 0x13121110; - let j: u32 = 0x17161514; - let k: u32 = 0x1b1a1918; - let l: u32 = 0x1f1e1d1c; - let m: u32 = 0x00000001; - let n: u32 = 0x09000000; - let o: u32 = 0x4a000000; - let p: u32 = 0x00000000; - let mut state = [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p]; - chacha_block(&mut state); - assert_eq!(state[0].to_be_bytes().to_lower_hex_string(), "e4e7f110"); - assert_eq!(state[1].to_be_bytes().to_lower_hex_string(), "15593bd1"); - assert_eq!(state[2].to_be_bytes().to_lower_hex_string(), "1fdd0f50"); - assert_eq!(state[3].to_be_bytes().to_lower_hex_string(), "c47120a3"); - assert_eq!(state[4].to_be_bytes().to_lower_hex_string(), "c7f4d1c7"); - assert_eq!(state[5].to_be_bytes().to_lower_hex_string(), "0368c033"); - assert_eq!(state[6].to_be_bytes().to_lower_hex_string(), "9aaa2204"); - assert_eq!(state[7].to_be_bytes().to_lower_hex_string(), "4e6cd4c3"); - assert_eq!(state[8].to_be_bytes().to_lower_hex_string(), "466482d2"); - assert_eq!(state[9].to_be_bytes().to_lower_hex_string(), "09aa9f07"); - assert_eq!(state[10].to_be_bytes().to_lower_hex_string(), "05d7c214"); - assert_eq!(state[11].to_be_bytes().to_lower_hex_string(), "a2028bd9"); - assert_eq!(state[12].to_be_bytes().to_lower_hex_string(), "d19c12b5"); - assert_eq!(state[13].to_be_bytes().to_lower_hex_string(), "b94e16de"); - assert_eq!(state[14].to_be_bytes().to_lower_hex_string(), "e883d0cb"); - assert_eq!(state[15].to_be_bytes().to_lower_hex_string(), "4e3c50a2"); - } - - #[test] - #[allow(clippy::many_single_char_names)] - fn test_block_serialization() { - let a: u32 = 0x61707865; - let b: u32 = 0x3320646e; - let c: u32 = 0x79622d32; - let d: u32 = 0x6b206574; - let e: u32 = 0x03020100; - let f: u32 = 0x07060504; - let g: u32 = 0x0b0a0908; - let h: u32 = 0x0f0e0d0c; - let i: u32 = 0x13121110; - let j: u32 = 0x17161514; - let k: u32 = 0x1b1a1918; - let l: u32 = 0x1f1e1d1c; - let m: u32 = 0x00000001; - let n: u32 = 0x09000000; - let o: u32 = 0x4a000000; - let p: u32 = 0x00000000; - let mut state = [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p]; - chacha_block(&mut state); - assert_eq!(state[7].to_le_bytes().to_lower_hex_string(), "c3d46c4e"); - } - - #[test] - fn test_prepare_state() { - let key = Vec::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") - .unwrap(); - let key: [u8; 32] = key.try_into().unwrap(); - let nonce = Vec::from_hex("000000090000004a00000000").unwrap(); - let nonce: [u8; 12] = nonce.try_into().unwrap(); - let count = 1; - let state = prepare_state(key, nonce, count); - assert_eq!(state[4].to_be_bytes().to_lower_hex_string(), "03020100"); - assert_eq!(state[10].to_be_bytes().to_lower_hex_string(), "1b1a1918"); - assert_eq!(state[14].to_be_bytes().to_lower_hex_string(), "4a000000"); - assert_eq!(state[15].to_be_bytes().to_lower_hex_string(), "00000000"); - assert_eq!(state[12].to_be_bytes().to_lower_hex_string(), "00000001") - } - - #[test] - fn test_small_plaintext() { - let key = Vec::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") - .unwrap(); - let key: [u8; 32] = key.try_into().unwrap(); - let nonce = Vec::from_hex("000000090000004a00000000").unwrap(); - let nonce: [u8; 12] = nonce.try_into().unwrap(); - let count = 1; - let mut chacha = ChaCha20::new(key, nonce, count); - let mut binding = [8; 3]; - chacha.apply_keystream(&mut binding[..]); - let mut chacha = ChaCha20::new(key, nonce, count); - chacha.apply_keystream(&mut binding[..]); - assert_eq!([8; 3], binding); - } - - #[test] - fn test_modulo_64() { - let key = Vec::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") - .unwrap(); - let key: [u8; 32] = key.try_into().unwrap(); - let nonce = Vec::from_hex("000000000000004a00000000").unwrap(); - let nonce: [u8; 12] = nonce.try_into().unwrap(); - let count = 1; - let mut chacha = ChaCha20::new(key, nonce, count); - let mut binding = [8; 64]; - chacha.apply_keystream(&mut binding[..]); - let mut chacha = ChaCha20::new(key, nonce, count); - chacha.apply_keystream(&mut binding[..]); - assert_eq!([8; 64], binding); - } - - #[test] - fn test_rfc_standard() { - let key = Vec::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") - .unwrap(); - let key: [u8; 32] = key.try_into().unwrap(); - let nonce = Vec::from_hex("000000000000004a00000000").unwrap(); - let nonce: [u8; 12] = nonce.try_into().unwrap(); - let count = 64; - let mut chacha = ChaCha20::new(key, nonce, count); - let mut binding = *b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; - let to = binding.clone(); - chacha.apply_keystream(&mut binding[..]); - assert_eq!(binding[..], Vec::from_hex("6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65c5524733ab8f593dabcd62b3571639d624e65152ab8f530c359f0861d807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74a35be6b40b8eedf2785e42874d").unwrap()); - let mut chacha = ChaCha20::new(key, nonce, count); - chacha.apply_keystream(&mut binding[..]); - let binding = *b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; - assert_eq!(binding, to); - } - - #[test] - fn test_new_from_block() { - let key = Vec::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") - .unwrap(); - let key: [u8; 32] = key.try_into().unwrap(); - let nonce = Vec::from_hex("000000000000004a00000000").unwrap(); - let nonce: [u8; 12] = nonce.try_into().unwrap(); - let block: u32 = 1; - let mut chacha = ChaCha20::new_from_block(key, nonce, block); - let mut binding = *b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; - let to = binding.clone(); - chacha.apply_keystream(&mut binding[..]); - assert_eq!(binding[..], Vec::from_hex("6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65c5524733ab8f593dabcd62b3571639d624e65152ab8f530c359f0861d807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74a35be6b40b8eedf2785e42874d").unwrap()); - chacha.block(block); - chacha.apply_keystream(&mut binding[..]); - let binding = *b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; - assert_eq!(binding, to); - } - - #[cfg(feature = "std")] - fn gen_garbage(garbage_len: u32) -> Vec { - let mut rng = rand::thread_rng(); - let buffer: Vec = (0..garbage_len).map(|_| rng.gen()).collect(); - buffer - } - - #[test] - #[cfg(feature = "std")] - fn test_fuzz_other() { - for _ in 0..100 { - let garbage_key = gen_garbage(32); - let key = garbage_key.as_slice().try_into().unwrap(); - let garbage_nonce = gen_garbage(12); - let nonce = garbage_nonce.as_slice().try_into().unwrap(); - for i in 0..10 { - let count: u32 = i * 11; - let mut chacha = ChaCha20::new(key, nonce, count); - let message = gen_garbage(129); - let mut message2 = message.clone(); - let msg = &mut message2[..]; - chacha.apply_keystream(msg); - let mut cipher = ChaCha20::new(key, nonce, 0); - let mut buffer = message; - cipher.seek(count); - cipher.apply_keystream(&mut buffer); - assert_eq!(buffer.as_slice(), msg); - } - } - } -} diff --git a/protocol/src/chacha20poly1305/mod.rs b/protocol/src/chacha20poly1305/mod.rs deleted file mode 100644 index 391349d..0000000 --- a/protocol/src/chacha20poly1305/mod.rs +++ /dev/null @@ -1,169 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 - -// ChaCha20 is used directly by the length cipher. -pub(crate) mod chacha20; -mod poly1305; - -use chacha20::ChaCha20; -use poly1305::Poly1305; - -use core::fmt; - -/// Zero array for padding slices. -const ZEROES: [u8; 16] = [0u8; 16]; - -/// Errors encrypting and decrypting messages with ChaCha20 and Poly1305 authentication tags. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Error { - UnauthenticatedAdditionalData, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Error::UnauthenticatedAdditionalData => write!(f, "Unauthenticated aad."), - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Error::UnauthenticatedAdditionalData => None, - } - } -} - -/// Encrypt and decrypt content along with a authentication tag. -#[derive(Debug)] -pub struct ChaCha20Poly1305 { - key: [u8; 32], - nonce: [u8; 12], -} - -impl ChaCha20Poly1305 { - pub fn new(key: [u8; 32], nonce: [u8; 12]) -> Self { - ChaCha20Poly1305 { key, nonce } - } - - /// Encrypt content in place and return the poly1305 16-byte authentication tag. - /// - /// # Arguments - /// - /// - `content` - Plaintext to be encrypted in place. - /// - `aad` - Optional metadata covered by the authentication tag. - /// - /// # Returns - /// - /// The 16-byte authentication tag. - pub fn encrypt(self, content: &mut [u8], aad: Option<&[u8]>) -> [u8; 16] { - let mut chacha = ChaCha20::new_from_block(self.key, self.nonce, 1); - chacha.apply_keystream(content); - let keystream = chacha.get_keystream(0); - let mut poly = Poly1305::new(keystream[..32].try_into().expect("infallible conversion")); - let aad = aad.unwrap_or(&[]); - // AAD and ciphertext are padded if not 16-byte aligned. - poly.add(aad); - let aad_overflow = aad.len() % 16; - if aad_overflow > 0 { - poly.add(&ZEROES[0..(16 - aad_overflow)]); - } - - poly.add(content); - let text_overflow = content.len() % 16; - if text_overflow > 0 { - poly.add(&ZEROES[0..(16 - text_overflow)]); - } - - let aad_len = aad.len().to_le_bytes(); - let msg_len = content.len().to_le_bytes(); - let mut len_buffer = [0u8; 16]; - len_buffer[..aad_len.len()].copy_from_slice(&aad_len[..]); - for i in 0..msg_len.len() { - len_buffer[i + aad_len.len()] = msg_len[i] - } - poly.add(&len_buffer); - poly.tag() - } - - /// Decrypt the ciphertext in place if authentication tag is correct. - /// - /// # Arguments - /// - /// - `content` - Ciphertext to be decrypted in place. - /// - `tag` - 16-byte authentication tag. - /// - `aad` - Optional metadata covered by the authentication tag. - pub fn decrypt( - self, - content: &mut [u8], - tag: [u8; 16], - aad: Option<&[u8]>, - ) -> Result<(), Error> { - let mut chacha = ChaCha20::new_from_block(self.key, self.nonce, 0); - let keystream = chacha.get_keystream(0); - let mut poly = Poly1305::new(keystream[..32].try_into().expect("infallible conversion")); - let aad = aad.unwrap_or(&[]); - poly.add(aad); - // AAD and ciphertext are padded if not 16-byte aligned. - let aad_overflow = aad.len() % 16; - if aad_overflow > 0 { - poly.add(&ZEROES[0..(16 - aad_overflow)]); - } - poly.add(content); - let msg_overflow = content.len() % 16; - if msg_overflow > 0 { - poly.add(&ZEROES[0..(16 - msg_overflow)]); - } - - let aad_len = aad.len().to_le_bytes(); - let msg_len = content.len().to_le_bytes(); - let mut len_buffer = [0u8; 16]; - len_buffer[..aad_len.len()].copy_from_slice(&aad_len[..]); - for i in 0..msg_len.len() { - len_buffer[i + aad_len.len()] = msg_len[i] - } - poly.add(&len_buffer); - let derived_tag = poly.tag(); - if derived_tag.eq(&tag) { - let mut chacha = ChaCha20::new_from_block(self.key, self.nonce, 1); - chacha.apply_keystream(content); - Ok(()) - } else { - Err(Error::UnauthenticatedAdditionalData) - } - } -} - -#[cfg(test)] -#[cfg(feature = "alloc")] -mod tests { - use super::*; - use alloc::vec::Vec; - use hex::prelude::*; - - #[test] - fn test_rfc7539() { - let mut message = *b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; - let aad = Vec::from_hex("50515253c0c1c2c3c4c5c6c7").unwrap(); - let key: [u8; 32] = - Vec::from_hex("808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f") - .unwrap() - .as_slice() - .try_into() - .unwrap(); - let nonce: [u8; 12] = Vec::from_hex("070000004041424344454647") - .unwrap() - .as_slice() - .try_into() - .unwrap(); - let cipher = ChaCha20Poly1305::new(key, nonce); - let tag = cipher.encrypt(&mut message, Some(&aad)); - - let mut buffer = [0u8; 130]; - buffer[..message.len()].copy_from_slice(&message); - buffer[message.len()..].copy_from_slice(&tag); - - assert_eq!(&buffer.to_lower_hex_string(), "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b61161ae10b594f09e26a7e902ecbd0600691"); - } -} diff --git a/protocol/src/chacha20poly1305/poly1305.rs b/protocol/src/chacha20poly1305/poly1305.rs deleted file mode 100644 index 2d25ecc..0000000 --- a/protocol/src/chacha20poly1305/poly1305.rs +++ /dev/null @@ -1,255 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 - -//! Poly1305 one-time authenticator from RFC7539. -//! -//! Implementation heavily inspired by [this implementation in C](https://github.com/floodyberry/poly1305-donna/blob/master/poly1305-donna-32.h) -//! referred to as "Donna". Further reference to [this](https://loup-vaillant.fr/tutorials/poly1305-design) article was used to formulate the multiplication loop. - -/// 2^26 for the 26-bit limbs. -const BITMASK: u32 = 0x03ffffff; -/// Number is encoded in five 26-bit limbs. -const CARRY: u32 = 26; - -/// Poly1305 authenticator takes a 32-byte one-time key and a message and produces a 16-byte tag. -/// -/// 64-bit constant time multiplication and addition implementation. -#[derive(Debug)] -pub(crate) struct Poly1305 { - /// r part of the secret key. - r: [u32; 5], - /// s part of the secret key. - s: [u32; 4], - /// State used to create tag. - acc: [u32; 5], - /// Leftovers between adds. - leftovers: [u8; 16], - /// Track relevant leftover bytes. - leftovers_len: usize, -} - -impl Poly1305 { - /// Initialize authenticator with a 32-byte one-time secret key. - pub(crate) fn new(key: [u8; 32]) -> Self { - // Taken from donna. Assigns r to a 26-bit 5-limb number while simultaneously 'clamping' r. - let r0 = - u32::from_le_bytes(key[0..4].try_into().expect("infalliable conversion")) & 0x3ffffff; - let r1 = u32::from_le_bytes(key[3..7].try_into().expect("infalliable conversion")) >> 2 - & 0x03ffff03; - let r2 = u32::from_le_bytes(key[6..10].try_into().expect("infalliable conversion")) >> 4 - & 0x03ffc0ff; - let r3 = u32::from_le_bytes(key[9..13].try_into().expect("infalliable conversion")) >> 6 - & 0x03f03fff; - let r4 = u32::from_le_bytes(key[12..16].try_into().expect("infalliable conversion")) >> 8 - & 0x000fffff; - let r = [r0, r1, r2, r3, r4]; - let s0 = u32::from_le_bytes(key[16..20].try_into().expect("infalliable conversion")); - let s1 = u32::from_le_bytes(key[20..24].try_into().expect("infalliable conversion")); - let s2 = u32::from_le_bytes(key[24..28].try_into().expect("infalliable conversion")); - let s3 = u32::from_le_bytes(key[28..32].try_into().expect("infalliable conversion")); - let s = [s0, s1, s2, s3]; - let acc = [0; 5]; - - // Initilize leftovers to zero. - let leftovers = [0u8; 16]; - let leftovers_len = 0; - - Poly1305 { - r, - s, - acc, - leftovers, - leftovers_len, - } - } - - /// Add message to be authenticated, can be called multiple times before creating tag. - pub(crate) fn add(&mut self, message: &[u8]) { - // Deal with previous leftovers if message is long enough. - let fill = if self.leftovers_len > 0 && (self.leftovers_len + message.len() >= 16) { - 16 - self.leftovers_len - } else { - 0 - }; - if fill > 0 { - self.leftovers[self.leftovers_len..].copy_from_slice(&message[0..fill]); - - let msg_slice = prepare_padded_message_slice(&self.leftovers, false); - for (i, b) in msg_slice.iter().enumerate() { - self.acc[i] += *b; - } - self.r_times_a(); - self.leftovers_len = 0; - } - - // Remove prefix already processed in leftovers. - let remaining_message = &message[fill..]; - - // Add message to accumulator. - let mut i = 0; - while i < remaining_message.len() / 16 { - let msg_slice = - prepare_padded_message_slice(&remaining_message[i * 16..(i + 1) * 16], false); - for (i, b) in msg_slice.iter().enumerate() { - self.acc[i] += *b; - } - self.r_times_a(); - i += 1; - } - - // Save any leftovers. - if remaining_message.len() % 16 > 0 { - let message_index = remaining_message.len() - (remaining_message.len() % 16); - let new_len = self.leftovers_len + remaining_message.len() % 16; - self.leftovers[self.leftovers_len..new_len] - .copy_from_slice(&remaining_message[message_index..]); - self.leftovers_len = new_len; - } - } - - /// Generate authentication tag. - pub(crate) fn tag(&mut self) -> [u8; 16] { - // Add any remaining leftovers to accumulator. - if self.leftovers_len > 0 { - let msg_slice = - prepare_padded_message_slice(&self.leftovers[..self.leftovers_len], true); - for (i, b) in msg_slice.iter().enumerate() { - self.acc[i] += *b; - } - self.r_times_a(); - self.leftovers_len = 0; - } - - // Carry and mask. - for i in 1..4 { - self.acc[i + 1] += self.acc[i] >> CARRY; - } - self.acc[0] += (self.acc[4] >> CARRY) * 5; - self.acc[1] += self.acc[0] >> CARRY; - for i in 0..self.acc.len() { - self.acc[i] &= BITMASK; - } - // Reduce. - let mut t = self.acc; - t[0] += 5; - t[4] = t[4].wrapping_sub(1 << CARRY); - for i in 0..3 { - t[i + 1] += t[i] >> CARRY; - } - t[4] = t[4].wrapping_add(t[3] >> CARRY); - for t in t.iter_mut().take(4) { - *t &= BITMASK; - } - // Convert acc to a 4 item array. - let mask = (t[4] >> 31).wrapping_sub(1); - for (i, t) in t.iter().enumerate().take(self.acc.len()) { - self.acc[i] = t & mask | self.acc[i] & !mask; - } - // Voodoo from donna to convert to [u32; 4]. - let a0 = self.acc[0] | self.acc[1] << 26; - let a1 = self.acc[1] >> 6 | self.acc[2] << 20; - let a2 = self.acc[2] >> 12 | self.acc[3] << 14; - let a3 = self.acc[3] >> 18 | self.acc[4] << 8; - let a = [a0, a1, a2, a3]; - // a + s - let mut tag: [u64; 4] = [0; 4]; - for i in 0..4 { - tag[i] = a[i] as u64 + self.s[i] as u64; - } - - // Carry. - for i in 0..3 { - tag[i + 1] += tag[i] >> 32; - } - - // Return the 16 least significant bytes. - let mut ret: [u8; 16] = [0; 16]; - for i in 0..tag.len() { - let bytes = (tag[i] as u32).to_le_bytes(); - ret[i * 4..(i + 1) * 4].copy_from_slice(&bytes); - } - ret - } - - fn r_times_a(&mut self) { - // Multiply and reduce. - // While this looks complicated, it is a variation of schoolbook multiplication, - // described well in an article here: https://loup-vaillant.fr/tutorials/poly1305-design - let mut t = [0; 5]; - for i in 0..5 { - for (j, t) in t.iter_mut().enumerate() { - let modulus: u64 = if i > j { 5 } else { 1 }; - let start = (5 - i) % 5; - *t += modulus * self.r[i] as u64 * self.acc[(start + j) % 5] as u64; - } - } - // Carry. - for i in 0..4 { - t[i + 1] += t[i] >> CARRY; - } - // Mask. - for (i, t) in t.iter().enumerate().take(self.acc.len()) { - self.acc[i] = *t as u32 & BITMASK; - } - // Carry and mask first limb. - self.acc[0] += (t[4] >> CARRY) as u32 * 5; - self.acc[1] += self.acc[0] >> CARRY; - self.acc[0] &= BITMASK; - } -} - -// Encode 16-byte (tag sized), unless is_last flag set to true, piece of message into 5 26-bit limbs. -fn prepare_padded_message_slice(msg: &[u8], is_last: bool) -> [u32; 5] { - let hi_bit: u32 = if is_last { 0 } else { 1 << 24 }; - let mut fmt_msg = [0u8; 17]; - fmt_msg[..msg.len()].clone_from_slice(msg); - // Tack on a 1-byte so messages with buncha zeroes at the end don't have the same MAC. - fmt_msg[msg.len()] = 0x01; - // Encode number in five 26-bit limbs. - let m0 = u32::from_le_bytes(fmt_msg[0..4].try_into().expect("Valid subset of 32.")) & BITMASK; - let m1 = - u32::from_le_bytes(fmt_msg[3..7].try_into().expect("Valid subset of 32.")) >> 2 & BITMASK; - let m2 = - u32::from_le_bytes(fmt_msg[6..10].try_into().expect("Valid subset of 32.")) >> 4 & BITMASK; - let m3 = - u32::from_le_bytes(fmt_msg[9..13].try_into().expect("Valid subset of 32.")) >> 6 & BITMASK; - let m4 = - u32::from_le_bytes(fmt_msg[12..16].try_into().expect("Valid subset of 32.")) >> 8 | hi_bit; - [m0, m1, m2, m3, m4] -} - -fn _print_acc(num: &[u32; 5]) { - let a0 = num[0] | num[1] << 26; - let a1 = num[1] >> 6 | num[2] << 20; - let a2 = num[2] >> 12 | num[3] << 14; - let a3 = num[3] >> 18 | num[4] << 8; - let a = [a0, a1, a2, a3]; - let mut ret: [u8; 16] = [0; 16]; - for i in 0..a.len() { - let bytes = a[i].to_le_bytes(); - ret[i * 4..(i + 1) * 4].copy_from_slice(&bytes); - } - ret.reverse(); -} - -#[cfg(test)] -#[cfg(feature = "alloc")] -mod tests { - use super::*; - use alloc::vec::Vec; - use hex::prelude::*; - - #[test] - fn test_rfc7539_none_message() { - let key = Vec::from_hex("85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b") - .unwrap(); - let key = key.as_slice().try_into().unwrap(); - let mut poly = Poly1305::new(key); - let message = b"Cryptographic Forum Research Group"; - poly.add(message); - let tag = poly.tag(); - assert_eq!( - "a8061dc1305136c6c22b8baf0c0127a9", - tag.to_lower_hex_string() - ); - } -} diff --git a/protocol/src/fschacha20poly1305.rs b/protocol/src/fschacha20poly1305.rs index 3300612..696945f 100644 --- a/protocol/src/fschacha20poly1305.rs +++ b/protocol/src/fschacha20poly1305.rs @@ -3,11 +3,9 @@ //! Wrap ciphers with automatic re-keying in order to provide [forward secrecy](https://eprint.iacr.org/2001/035.pdf) within a session. //! Logic is covered by the BIP324 test vectors. +use chacha20_poly1305::{chacha20::ChaCha20, ChaCha20Poly1305, Key, Nonce}; use core::fmt; -use crate::chacha20poly1305::chacha20::ChaCha20; -use crate::chacha20poly1305::ChaCha20Poly1305; - /// Message lengths are encoded in three bytes. const LENGTH_BYTES: u32 = 3; /// Ciphers are re-keyed after 224 messages (or chunks). @@ -18,7 +16,7 @@ const REKEY_INITIAL_NONCE: [u8; 4] = [0xFF, 0xFF, 0xFF, 0xFF]; /// Errors encrypting and decrypting with [`FSChaCha20Poly1305`]. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Error { - Decryption(crate::chacha20poly1305::Error), + Decryption(chacha20_poly1305::Error), } impl fmt::Display for Error { @@ -44,14 +42,14 @@ impl std::error::Error for Error { /// FSChaCha20Poly1305 is used for message packets in BIP324. #[derive(Clone)] pub struct FSChaCha20Poly1305 { - key: [u8; 32], + key: Key, message_counter: u64, } impl FSChaCha20Poly1305 { pub fn new(key: [u8; 32]) -> Self { FSChaCha20Poly1305 { - key, + key: Key::new(key), message_counter: 0, } } @@ -77,9 +75,9 @@ impl FSChaCha20Poly1305 { rekey_nonce[4..].copy_from_slice(&self.nonce()[4..]); let mut plaintext = [0u8; 32]; - let cipher = ChaCha20Poly1305::new(self.key, rekey_nonce); + let cipher = ChaCha20Poly1305::new(self.key, Nonce::new(rekey_nonce)); cipher.encrypt(&mut plaintext, Some(aad)); - self.key = plaintext; + self.key = Key::new(plaintext); } self.message_counter += 1; @@ -96,7 +94,7 @@ impl FSChaCha20Poly1305 { /// /// The 16-byte authentication tag. pub fn encrypt(&mut self, aad: &[u8], content: &mut [u8]) -> [u8; 16] { - let cipher = ChaCha20Poly1305::new(self.key, self.nonce()); + let cipher = ChaCha20Poly1305::new(self.key, Nonce::new(self.nonce())); let tag = cipher.encrypt(content, Some(aad)); @@ -113,7 +111,7 @@ impl FSChaCha20Poly1305 { /// * `tag` - 16-byte authentication tag. /// * `aad` - Optional associated authenticated data covered by the authentication tag. pub fn decrypt(&mut self, aad: &[u8], content: &mut [u8], tag: [u8; 16]) -> Result<(), Error> { - let cipher = ChaCha20Poly1305::new(self.key, self.nonce()); + let cipher = ChaCha20Poly1305::new(self.key, Nonce::new(self.nonce())); cipher .decrypt(content, tag, Some(aad)) @@ -132,7 +130,7 @@ impl FSChaCha20Poly1305 { /// implicitly authenticated by the message packets. #[derive(Clone)] pub struct FSChaCha20 { - key: [u8; 32], + key: Key, block_counter: u32, chunk_counter: u32, } @@ -140,7 +138,7 @@ pub struct FSChaCha20 { impl FSChaCha20 { pub fn new(key: [u8; 32]) -> Self { FSChaCha20 { - key, + key: Key::new(key), block_counter: 0, chunk_counter: 0, } @@ -151,7 +149,7 @@ impl FSChaCha20 { let counter_mod = (self.chunk_counter / REKEY_INTERVAL as u32).to_le_bytes(); let mut nonce = [0u8; 12]; nonce[4..8].copy_from_slice(&counter_mod); - let mut cipher = ChaCha20::new(self.key, nonce, 0); + let mut cipher = ChaCha20::new(self.key, Nonce::new(nonce), 0); cipher.seek(self.block_counter); cipher.apply_keystream(chunk); self.block_counter += LENGTH_BYTES; @@ -160,7 +158,7 @@ impl FSChaCha20 { cipher.seek(self.block_counter); cipher.apply_keystream(&mut key_buffer); self.block_counter = 0; - self.key = key_buffer; + self.key = Key::new(key_buffer); } self.chunk_counter += 1; } diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index aa27b52..85d1a36 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -23,7 +23,6 @@ extern crate alloc; #[cfg(feature = "std")] extern crate std; -mod chacha20poly1305; mod fschacha20poly1305; mod hkdf; #[cfg(feature = "std")]