From 26c8c1219e274167eb4395cc61aab4d104f34788 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 19 Aug 2019 10:12:29 -0700 Subject: [PATCH 1/2] poly1305: Add `input_padded` method Add a function for inputting data into Poly1305 which is padded to its block size, a.k.a. the `pad16()` function from RFC 8439. This is useful for implementing authenticated encryption constructions using Salsa20 family ciphers (i.e. Salsa20Poly1305, ChaCha20Poly1305) which is the main use case for Poly1305 to begin with. --- poly1305/src/lib.rs | 19 +++++++++++++++++++ poly1305/tests/lib.rs | 23 +++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/poly1305/src/lib.rs b/poly1305/src/lib.rs index e118530..28e85b0 100644 --- a/poly1305/src/lib.rs +++ b/poly1305/src/lib.rs @@ -109,6 +109,25 @@ impl Poly1305 { self.leftover = m.len(); } + /// Input data into Poly1305, first padding it to Poly1305's block size + /// ala the `pad16()` function described in RFC 8439 section 2.8.1: + /// + /// + /// This is primarily useful for implementing Salsa20 family authenticated + /// encryption constructions. + pub fn input_padded(&mut self, data: &[u8]) { + self.input(data); + + // Pad associated data with `\0` if it's unaligned with the block size + let unaligned_len = data.len() % BLOCK_SIZE; + + if unaligned_len != 0 { + let pad = [0u8; BLOCK_SIZE]; + let pad_len = BLOCK_SIZE - unaligned_len; + self.input(&pad[..pad_len]); + } + } + /// Process input messages in a chained manner pub fn chain(mut self, data: &[u8]) -> Self { self.input(data); diff --git a/poly1305/tests/lib.rs b/poly1305/tests/lib.rs index af20dc1..97000b7 100644 --- a/poly1305/tests/lib.rs +++ b/poly1305/tests/lib.rs @@ -114,3 +114,26 @@ fn test_tls_vectors() { assert_eq!(&Poly1305::new(&key).chain(&msg[..]).result(), &expected[..]); } + +#[test] +fn padded_input() { + // poly1305 key and AAD from + let key = [ + 0x7b, 0xac, 0x2b, 0x25, 0x2d, 0xb4, 0x47, 0xaf, 0x09, 0xb6, 0x7a, 0x55, 0xa4, 0xe9, 0x55, + 0x84, 0x0a, 0xe1, 0xd6, 0x73, 0x10, 0x75, 0xd9, 0xeb, 0x2a, 0x93, 0x75, 0x78, 0x3e, 0xd5, + 0x53, 0xff, + ]; + + let msg = [ + 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + ]; + + let expected = [ + 0xad, 0xa5, 0x6c, 0xaa, 0x48, 0xf, 0xe6, 0xf5, 0x6, 0x70, 0x39, 0x24, 0x4a, 0x3d, 0x76, + 0xba, + ]; + + let mut poly = Poly1305::new(&key); + poly.input_padded(&msg); + assert_eq!(&expected[..], poly.result()); +} From 98cf577a8f9580eafbc3ef9f20caf15d5e5174f7 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 19 Aug 2019 10:31:31 -0700 Subject: [PATCH 2/2] poly1305: Use MacResult for representing tags This outsources constant time comparisons to `MacResult`, so downstream crates don't need to rely on additional crates to perform them beyond what `MacResult` is already using. --- poly1305/Cargo.toml | 1 + poly1305/src/lib.rs | 20 +++++++++++++------- poly1305/tests/lib.rs | 36 ++++++++++++++++++++---------------- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/poly1305/Cargo.toml b/poly1305/Cargo.toml index 235169c..be7b70f 100644 --- a/poly1305/Cargo.toml +++ b/poly1305/Cargo.toml @@ -12,6 +12,7 @@ readme = "README.md" [dependencies] byteorder = { version = "1", default-features = false } +crypto-mac = "0.7" zeroize = { version = "0.9", optional = true, default-features = false } [dev-dependencies] diff --git a/poly1305/src/lib.rs b/poly1305/src/lib.rs index 28e85b0..98cdda1 100644 --- a/poly1305/src/lib.rs +++ b/poly1305/src/lib.rs @@ -18,12 +18,15 @@ // TODO: replace with `u32::{from_le_bytes, to_le_bytes}` in libcore (1.32+) extern crate byteorder; +extern crate crypto_mac; #[cfg(feature = "zeroize")] extern crate zeroize; use byteorder::{ByteOrder, LE}; use core::cmp::min; +use crypto_mac::generic_array::{typenum::U16, GenericArray}; +use crypto_mac::MacResult; #[cfg(feature = "zeroize")] use zeroize::Zeroize; @@ -33,6 +36,9 @@ pub const KEY_SIZE: usize = 32; /// Size of the blocks Poly1305 acts upon pub const BLOCK_SIZE: usize = 16; +/// Poly1305 authentication tags +pub type Tag = MacResult; + /// The Poly1305 universal hash function. /// /// Note that Poly1305 is not a traditional MAC and is single-use only @@ -135,7 +141,7 @@ impl Poly1305 { } /// Get the hashed output - pub fn result(mut self) -> [u8; BLOCK_SIZE] { + pub fn result(mut self) -> Tag { if self.leftover > 0 { self.buffer[self.leftover] = 1; @@ -227,13 +233,13 @@ impl Poly1305 { f = u64::from(h3) + u64::from(self.pad[3]) + (f >> 32); h3 = f as u32; - let mut output = [0u8; BLOCK_SIZE]; - LE::write_u32(&mut output[0..4], h0); - LE::write_u32(&mut output[4..8], h1); - LE::write_u32(&mut output[8..12], h2); - LE::write_u32(&mut output[12..16], h3); + let mut tag = GenericArray::default(); + LE::write_u32(&mut tag[0..4], h0); + LE::write_u32(&mut tag[4..8], h1); + LE::write_u32(&mut tag[8..12], h2); + LE::write_u32(&mut tag[12..16], h3); - output + MacResult::new(tag) } /// Compute a single block of Poly1305 using the internal buffer diff --git a/poly1305/tests/lib.rs b/poly1305/tests/lib.rs index 97000b7..913de13 100644 --- a/poly1305/tests/lib.rs +++ b/poly1305/tests/lib.rs @@ -28,9 +28,10 @@ fn test_nacl_vector() { 0xd9, ]; - assert_eq!(&Poly1305::new(&key).chain(&msg).result(), &expected[..]); + let result1 = Poly1305::new(&key).chain(&msg).result(); + assert_eq!(&expected[..], result1.code().as_slice()); - let result = Poly1305::new(&key) + let result2 = Poly1305::new(&key) .chain(&msg[0..32]) .chain(&msg[32..96]) .chain(&msg[96..112]) @@ -44,7 +45,7 @@ fn test_nacl_vector() { .chain(&msg[130..131]) .result(); - assert_eq!(&result, &expected[..]); + assert_eq!(&expected[..], result2.code().as_slice()); } #[test] @@ -65,10 +66,9 @@ fn donna_self_test() { 0x00, ]; - assert_eq!( - &Poly1305::new(&wrap_key).chain(&wrap_msg).result(), - &wrap_mac[..] - ); + let result = Poly1305::new(&wrap_key).chain(&wrap_msg).result(); + + assert_eq!(&wrap_mac[..], result.code().as_slice()); let total_key = [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xff, @@ -88,31 +88,35 @@ fn donna_self_test() { key.copy_from_slice(&repeat(i as u8).take(KEY_SIZE).collect::>()); let msg: Vec = repeat(i as u8).take(256).collect(); - tpoly.input(&Poly1305::new(&key).chain(&msg[..i]).result()); + let tag = Poly1305::new(&key).chain(&msg[..i]).result(); + tpoly.input(tag.code().as_slice()); } - assert_eq!(&tpoly.result(), &total_mac[..]); + assert_eq!(&total_mac[..], tpoly.result().code().as_slice()); } #[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 = [ + + let msg1 = [0u8; 32]; + let expected1 = [ 0x49, 0xec, 0x78, 0x09, 0x0e, 0x48, 0x1e, 0xc6, 0xc2, 0x6b, 0x33, 0xb9, 0x1c, 0xcc, 0x03, 0x07, ]; - assert_eq!(&Poly1305::new(&key).chain(&msg).result(), &expected[..]); + let result1 = Poly1305::new(&key).chain(&msg1).result(); + assert_eq!(&expected1[..], result1.code().as_slice()); - let msg = b"Hello world!"; - let expected = [ + let msg2 = b"Hello world!"; + let expected2 = [ 0xa6, 0xf7, 0x45, 0x00, 0x8f, 0x81, 0xc9, 0x16, 0xa2, 0x0d, 0xcc, 0x74, 0xee, 0xf2, 0xb2, 0xf0, ]; - assert_eq!(&Poly1305::new(&key).chain(&msg[..]).result(), &expected[..]); + let result2 = Poly1305::new(&key).chain(&msg2[..]).result(); + assert_eq!(&expected2[..], result2.code().as_slice()); } #[test] @@ -135,5 +139,5 @@ fn padded_input() { let mut poly = Poly1305::new(&key); poly.input_padded(&msg); - assert_eq!(&expected[..], poly.result()); + assert_eq!(&expected[..], poly.result().code().as_slice()); }