From 58a6056afeb0c57c0301a6e6227d0d067284ad23 Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Wed, 13 Mar 2024 10:45:50 -1000 Subject: [PATCH] Prune: BigInt with proper Poly1305 --- Cargo.lock | 36 --------- Cargo.toml | 1 - src/chachapoly.rs | 189 +++++++++------------------------------------- src/error.rs | 27 ++++--- src/lib.rs | 7 +- src/poly1305.rs | 182 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 237 insertions(+), 205 deletions(-) create mode 100644 src/poly1305.rs diff --git a/Cargo.lock b/Cargo.lock index 7ad036a..be94a2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,12 +12,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - [[package]] name = "bip324" version = "0.1.0" @@ -26,7 +20,6 @@ dependencies = [ "chacha20", "chacha20poly1305", "hex", - "num-bigint", "rand", "secp256k1", ] @@ -162,35 +155,6 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" -[[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" -dependencies = [ - "autocfg", -] - [[package]] name = "opaque-debug" version = "0.3.0" diff --git a/Cargo.toml b/Cargo.toml index 3f54e34..7dd8044 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ rust-version = "1.56.1" secp256k1 = { version="0.28.2" } rand = "0.8.4" bitcoin_hashes = "0.13.0" -num-bigint = "0.4.4" [dev-dependencies] hex = "0.4.3" diff --git a/src/chachapoly.rs b/src/chachapoly.rs index beb5bb9..5e44302 100644 --- a/src/chachapoly.rs +++ b/src/chachapoly.rs @@ -1,7 +1,9 @@ use crate::chacha::ChaCha20; use crate::error; -use error::ChaCha20Poly1305DecryptionError; -use num_bigint::BigUint; +use error::ChaCha20Poly1305EncryptionError; +use crate::poly1305::Poly1305; +extern crate alloc; +pub use error::ChaCha20Poly1305DecryptionError; #[derive(Debug)] pub struct ChaCha20Poly1305 { @@ -14,20 +16,14 @@ impl ChaCha20Poly1305 { ChaCha20Poly1305 { key, nonce } } - pub fn encrypt<'a>( - self, - plaintext: &'a mut [u8], - aad: Option<&'a [u8]>, - buffer: &'a mut [u8], - ) -> &'a [u8] { + pub fn encrypt<'a>(self, plaintext: &'a mut [u8], aad: Option<&'a [u8]>, buffer: &'a mut [u8]) -> Result<&'a [u8], ChaCha20Poly1305EncryptionError> { + if plaintext.len() + 16 != buffer.len() { + return Err(ChaCha20Poly1305EncryptionError::IncorrectBuffer("The buffer provided was incorrect. Ensure the buffer is 16 bytes longer than the message.".to_string())); + } let mut chacha = ChaCha20::new_from_block(self.key, self.nonce, 1); chacha.apply_keystream(plaintext); let keystream = chacha.get_keystream(0); - let mut poly = Poly1305::new( - keystream[..32] - .try_into() - .expect("32 is a valid subset of 64."), - ); + let mut poly = Poly1305::new(keystream[..32].try_into().expect("32 is a valid subset of 64.")); let aad = aad.unwrap_or(&[]); poly.add(aad); poly.add(plaintext); @@ -39,7 +35,7 @@ impl ChaCha20Poly1305 { len_buffer[i + aad_len.len()] = msg_len[i] } poly.add(&len_buffer); - let tag = poly.get_tag(); + let tag = poly.tag(); for i in 0..plaintext.len() { if i < plaintext.len() { buffer[i] = plaintext[i] @@ -50,24 +46,16 @@ impl ChaCha20Poly1305 { buffer[plaintext.len() + i] = tag[i] } } - &buffer[..plaintext.len() + tag.len()] + Ok(&buffer[..plaintext.len() + tag.len()]) } - pub fn decrypt<'a>( - self, - ciphertext: &'a mut [u8], - aad: Option<&'a [u8]>, - ) -> Result<&'a [u8], ChaCha20Poly1305DecryptionError> { + pub fn decrypt<'a>(self, ciphertext: &'a mut [u8], aad: Option<&'a [u8]>) -> Result<&'a [u8], ChaCha20Poly1305DecryptionError> { 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("32 is a valid subset of 64."), - ); + let mut poly = Poly1305::new(keystream[..32].try_into().expect("32 is a valid subset of 64.")); let aad = aad.unwrap_or(&[]); if ciphertext.len() >= 16 { - let (received_msg, received_tag) = ciphertext.split_at_mut(ciphertext.len() - 16); + let (received_msg, received_tag) = ciphertext.split_at_mut(ciphertext.len()- 16); poly.add(aad); poly.add(received_msg); let aad_len = aad.len().to_le_bytes(); @@ -78,91 +66,18 @@ impl ChaCha20Poly1305 { len_buffer[i + aad_len.len()] = msg_len[i] } poly.add(&len_buffer); - let tag = poly.get_tag(); + let tag = poly.tag(); if tag.eq(received_tag) { let mut chacha = ChaCha20::new_from_block(self.key, self.nonce, 1); chacha.apply_keystream(received_msg); Ok(received_msg) } else { - Err( - ChaCha20Poly1305DecryptionError::UnauthenticatedAdditionalData( - "Computed tag did not match.".to_string(), - ), - ) + Err(ChaCha20Poly1305DecryptionError::UnauthenticatedAdditionalData("Computed tag did not match.".to_string())) } } else { - Err(ChaCha20Poly1305DecryptionError::CiphertextTooShort( - "Ciphertext must be at least 16 bytes.".to_string(), - )) - } - } -} - -#[derive(Debug)] -struct Poly1305 { - r: BigUint, - s: BigUint, - acc: BigUint, - modulus: BigUint, -} - -impl Poly1305 { - fn new(key: [u8; 32]) -> Self { - let mut r: [u8; 16] = key[..16] - .try_into() - .expect("Key is at least 16 bytes long."); - r[3] &= 15; - r[7] &= 15; - r[11] &= 15; - r[15] &= 15; - r[4] &= 252; - r[8] &= 252; - r[12] &= 252; - let r = BigUint::from_bytes_le(&r); - let s = BigUint::from_bytes_le(&key[16..]); - let acc = BigUint::from_slice(&[0]); - let base = BigUint::from_slice(&[2]); - let modulus = base.pow(130) - BigUint::from_slice(&[5]); - Poly1305 { r, s, acc, modulus } - } - - fn add(&mut self, message: &[u8]) { - let mut i = 0; - while i < message.len() / 16 { - let msg_slice = prepare_message_slice(&message[i * 16..(i + 1) * 16]); - let n = BigUint::from_bytes_le(&msg_slice); - self.acc = (self.r.clone() * (self.acc.clone() + n)) - .modpow(&BigUint::from_slice(&[1]), &self.modulus); - i += 1; - } - if message.len() % 16 > 0 { - let msg_slice = prepare_padded_message_slice(&message[i * 16..]); - let n = BigUint::from_bytes_le(&msg_slice); - self.acc = (self.r.clone() * (self.acc.clone() + n)) - .modpow(&BigUint::from_slice(&[1]), &self.modulus); + Err(ChaCha20Poly1305DecryptionError::CiphertextTooShort("Ciphertext must be at least 16 bytes.".to_string())) } } - - fn get_tag(self) -> [u8; 16] { - let tag = self.acc + self.s; - tag.to_bytes_le()[..16] - .try_into() - .expect("Message tag should be at least 16 bytes.") - } -} - -fn prepare_message_slice(msg: &[u8]) -> [u8; 17] { - let mut fmt_msg = [0u8; 17]; - fmt_msg[..msg.len()].copy_from_slice(msg); - fmt_msg[msg.len()] = 0x01; - fmt_msg -} - -fn prepare_padded_message_slice(msg: &[u8]) -> [u8; 17] { - let mut fmt_msg = [0u8; 17]; - fmt_msg[..msg.len()].copy_from_slice(msg); - fmt_msg[16] = 0x01; - fmt_msg } #[cfg(test)] @@ -170,100 +85,69 @@ mod tests { use super::*; use chacha20poly1305::{AeadInPlace, KeyInit, Nonce}; - #[test] - fn test_none_message() { - let key = hex::decode("85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b") - .unwrap(); - let key = key.as_slice().try_into().unwrap(); - let mut poly = Poly1305::new(key); - let message = b""; - poly.add(message); - let _tag = poly.get_tag(); - } - #[test] fn test_encrypt_other_with_aad() { - let key = hex::decode("85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b") - .unwrap(); - let key = key.as_slice().try_into().unwrap(); + let key = hex::decode("85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b").unwrap(); + let key: [u8; 32] = key.as_slice().try_into().unwrap(); let nonce = [0u8; 12]; let mut message = b"Cryptographic Forum Research Group".to_vec(); - let aad = b"some".to_vec(); + let aad = b"Some 17 bytes!!!!".to_vec(); let conformed_nonce = Nonce::from_slice(&nonce); - let other = chacha20poly1305::ChaCha20Poly1305::new_from_slice(key).expect("Key is valid."); - other - .encrypt_in_place(conformed_nonce, &aad, &mut message) - .unwrap(); + let other = chacha20poly1305::ChaCha20Poly1305::new_from_slice(&key).expect("Key is valid."); + other.encrypt_in_place(conformed_nonce, &aad, &mut message).unwrap(); let mut message2 = *b"Cryptographic Forum Research Group"; - let mut aad = *b"some"; + let mut aad = *b"Some 17 bytes!!!!"; let us = ChaCha20Poly1305::new(key.try_into().unwrap(), nonce); let mut buffer = [0u8; 50]; - us.encrypt( - message2.as_mut_slice(), - Some(aad.as_mut_slice()), - buffer.as_mut_slice(), - ); + us.encrypt(message2.as_mut_slice(), Some(aad.as_mut_slice()), buffer.as_mut_slice()).unwrap(); assert_eq!(hex::encode(message), hex::encode(buffer)); } #[test] fn test_encrypt_other_no_aad() { - let key = hex::decode("85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b") - .unwrap(); + let key = hex::decode("85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b").unwrap(); let key = key.as_slice().try_into().unwrap(); let nonce = [0u8; 12]; let mut message = b"Cryptographic Forum Research Group".to_vec(); let aad = b"".to_vec(); let conformed_nonce = Nonce::from_slice(&nonce); let other = chacha20poly1305::ChaCha20Poly1305::new_from_slice(key).expect("Key is valid."); - other - .encrypt_in_place(conformed_nonce, &aad, &mut message) - .unwrap(); + other.encrypt_in_place(conformed_nonce, &aad, &mut message).unwrap(); let mut message2 = *b"Cryptographic Forum Research Group"; let us = ChaCha20Poly1305::new(key.try_into().unwrap(), nonce); let mut buffer = [0u8; 50]; - us.encrypt(message2.as_mut_slice(), None, buffer.as_mut_slice()); + us.encrypt(message2.as_mut_slice(), None, buffer.as_mut_slice()).unwrap(); assert_eq!(hex::encode(message), hex::encode(buffer)); } #[test] fn test_encrypt_other_no_content() { - let key = hex::decode("85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b") - .unwrap(); + let key = hex::decode("85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b").unwrap(); let key = key.as_slice().try_into().unwrap(); let nonce = [0u8; 12]; let mut message = b"".to_vec(); let aad = b"Some secret".to_vec(); let conformed_nonce = Nonce::from_slice(&nonce); let other = chacha20poly1305::ChaCha20Poly1305::new_from_slice(key).expect("Key is valid."); - other - .encrypt_in_place(conformed_nonce, &aad, &mut message) - .unwrap(); + other.encrypt_in_place(conformed_nonce, &aad, &mut message).unwrap(); let mut message2 = *b""; let mut aad = *b"Some secret"; let us = ChaCha20Poly1305::new(key.try_into().unwrap(), nonce); let mut buffer = [0u8; 16]; - us.encrypt( - message2.as_mut_slice(), - Some(aad.as_mut_slice()), - buffer.as_mut_slice(), - ); + us.encrypt(message2.as_mut_slice(), Some(aad.as_mut_slice()), buffer.as_mut_slice()).unwrap(); assert_eq!(hex::encode(message), hex::encode(buffer)); } #[test] fn test_decrypt_other_no_aad() { - let key = hex::decode("85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b") - .unwrap(); + let key = hex::decode("85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b").unwrap(); let key = key.as_slice().try_into().unwrap(); let nonce = [0u8; 12]; let mut message = b"Cryptographic Forum Research Group".to_vec(); let aad = b"".to_vec(); let conformed_nonce = Nonce::from_slice(&nonce); let other = chacha20poly1305::ChaCha20Poly1305::new_from_slice(key).expect("Key is valid."); - other - .encrypt_in_place(conformed_nonce, &aad, &mut message) - .unwrap(); + other.encrypt_in_place(conformed_nonce, &aad, &mut message).unwrap(); let us = ChaCha20Poly1305::new(key.try_into().unwrap(), nonce); let plaintext = us.decrypt(message.as_mut_slice(), None).unwrap(); let message = b"Cryptographic Forum Research Group".to_vec(); @@ -272,20 +156,17 @@ mod tests { #[test] fn test_decrypt_other_no_content() { - let key = hex::decode("85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b") - .unwrap(); + let key = hex::decode("85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b").unwrap(); let key = key.as_slice().try_into().unwrap(); let nonce = [0u8; 12]; let mut message = b"".to_vec(); let aad = b"Cryptographic Forum Research Group".to_vec(); let conformed_nonce = Nonce::from_slice(&nonce); let other = chacha20poly1305::ChaCha20Poly1305::new_from_slice(key).expect("Key is valid."); - other - .encrypt_in_place(conformed_nonce, &aad, &mut message) - .unwrap(); + other.encrypt_in_place(conformed_nonce, &aad, &mut message).unwrap(); let us = ChaCha20Poly1305::new(key.try_into().unwrap(), nonce); let aad = *b"Cryptographic Forum Research Group"; let plaintext = us.decrypt(message.as_mut_slice(), Some(&aad)).unwrap(); assert!(plaintext.len() == 0); } -} +} \ No newline at end of file diff --git a/src/error.rs b/src/error.rs index b1b1296..3aa8573 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,7 @@ use std::error::Error; +extern crate alloc; +use core::fmt; +// use alloc::string::String; /// An error occured responding to an inbound handshake. #[derive(Debug)] @@ -96,24 +99,24 @@ pub enum ChaCha20Poly1305DecryptionError { CiphertextTooShort(String), } -impl std::fmt::Display for ChaCha20Poly1305DecryptionError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for ChaCha20Poly1305DecryptionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ChaCha20Poly1305DecryptionError::UnauthenticatedAdditionalData(s) => { - write!(f, "Cipher error: {}", s) - } - ChaCha20Poly1305DecryptionError::CiphertextTooShort(s) => { - write!(f, "Cipher error: {}", s) - } + ChaCha20Poly1305DecryptionError::UnauthenticatedAdditionalData(s) => write!(f, "Cipher error: {}", s), + ChaCha20Poly1305DecryptionError::CiphertextTooShort(s) => write!(f, "Cipher error: {}", s), } } } -impl Error for ChaCha20Poly1305DecryptionError { - fn source(&self) -> Option<&(dyn Error + 'static)> { +#[derive(Debug)] +pub enum ChaCha20Poly1305EncryptionError { + IncorrectBuffer(String) +} + +impl fmt::Display for ChaCha20Poly1305EncryptionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ChaCha20Poly1305DecryptionError::UnauthenticatedAdditionalData(_s) => None, - ChaCha20Poly1305DecryptionError::CiphertextTooShort(_s) => None, + ChaCha20Poly1305EncryptionError::IncorrectBuffer(s) => write!(f, "Cipher error: {}", s), } } } diff --git a/src/lib.rs b/src/lib.rs index 6c33ffc..089d214 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,6 +41,7 @@ mod chachapoly; mod error; mod hkdf; mod types; +mod poly1305; use chacha::ChaCha20; use chachapoly::ChaCha20Poly1305; @@ -266,7 +267,8 @@ impl FSChaCha20Poly1305 { CryptType::Encrypt => { let mut buffer = contents.clone(); buffer.extend([0u8; 16]); - cipher.encrypt(&mut contents, Some(&aad), &mut buffer); + cipher.encrypt(&mut contents, Some(&aad), &mut buffer) + .map_err(|e| FSChaChaError::Poly1305Encryption(e.to_string()))?; buffer.to_vec() } CryptType::Decrypt => { @@ -293,7 +295,8 @@ impl FSChaCha20Poly1305 { self.key, rekey_nonce.try_into().expect("Nonce is malformed."), ); - cipher.encrypt(&mut plaintext, Some(&aad), &mut buffer); + cipher.encrypt(&mut plaintext, Some(&aad), &mut buffer) + .map_err(|e| FSChaChaError::Poly1305Encryption(e.to_string()))?; self.key = buffer[0..32] .try_into() .expect("Cipher should be at least 32 bytes."); diff --git a/src/poly1305.rs b/src/poly1305.rs new file mode 100644 index 0000000..f99ee51 --- /dev/null +++ b/src/poly1305.rs @@ -0,0 +1,182 @@ +/// Implementation of Poly1305 function heavily inspired by the following [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. + +const BITMASK: u32 = 0x03ffffff; +const CARRY: u32 = 26; + +#[derive(Debug)] +pub(crate) struct Poly1305 { + r: [u32; 5], + s: [u32; 4], + acc: [u32; 5], +} + +impl Poly1305 { + 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("Valid subset of 32.")) & 0x3ffffff; + let r1 = u32::from_le_bytes(key[3..7].try_into().expect("Valid subset of 32.")) >> 2 & 0x03ffff03; + let r2 = u32::from_le_bytes(key[6..10].try_into().expect("Valid subset of 32.")) >> 4 & 0x03ffc0ff; + let r3 = u32::from_le_bytes(key[9..13].try_into().expect("Valid subset of 32.")) >> 6 & 0x03f03fff; + let r4 = u32::from_le_bytes(key[12..16].try_into().expect("Valid subset of 32.")) >> 8 & 0x000fffff; + let r = [r0, r1, r2, r3, r4]; + let s0 = u32::from_le_bytes(key[16..20].try_into().expect("Valid subset of 32.")); + let s1 = u32::from_le_bytes(key[20..24].try_into().expect("Valid subset of 32.")); + let s2 = u32::from_le_bytes(key[24..28].try_into().expect("Valid subset of 32.")); + let s3 = u32::from_le_bytes(key[28..32].try_into().expect("Valid subset of 32.")); + let s = [s0, s1, s2, s3]; + let acc = [0; 5]; + Poly1305 { r, s, acc } + } + + pub(crate) fn add(&mut self, message: &[u8]) { + let mut i = 0; + while i < message.len() / 16 { + let msg_slice = prepare_padded_message_slice(&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; + } + if message.len() % 16 > 0 { + let msg_slice = prepare_padded_message_slice(&message[i * 16..], true); + for (i, b) in msg_slice.iter().enumerate() { + self.acc[i] += *b; + } + self.r_times_a(); + } + } + + pub(crate) fn tag(&mut self) -> [u8; 16] { + // 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 in 0..5 { + let modulus: u64 = if i > j { + 5 + } else { + 1 + }; + let start = (5 - i) % 5; + t[j] += 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; + } +} + +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); + fmt_msg[16] = 0x01; + 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 = if is_last { + u32::from_le_bytes(fmt_msg[13..17].try_into().expect("Valid subset of 32.")) | hi_bit + } else { + 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(); + // println!("{:?}{}", num[0].to_le_bytes()[3], hex::encode(ret)); +} + +// #[cfg(test)] +// mod tests { +// use super::*; +// // fails to shortcut encryption and decryption. +// #[test] +// fn test_none_message() { +// let key = hex::decode("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",hex::encode(tag)); +// } +// } \ No newline at end of file