From 89f62777dbcab73e8ae9dce94976cc44a23f288d Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 26 Aug 2019 14:07:03 -0700 Subject: [PATCH] poly1305/polyval: Switch from MacResult to built-in Tag types Eliminates the dependency on `crypto-mac` by using a built-in `Tag` type which impl's `subtle::ConstantTimeEq`. Ideally this would probably get extracted into some other trait, e.g. `UniversalHash`, but for now just use concrete types per crate. --- poly1305/Cargo.toml | 5 ++- poly1305/README.md | 12 +++++- poly1305/benches/poly1305.rs | 7 ++-- poly1305/src/lib.rs | 52 ++++++++++++++++++++------ poly1305/tests/lib.rs | 16 ++++---- polyval/Cargo.toml | 3 +- polyval/README.md | 10 +++++ polyval/benches/polyval.rs | 5 ++- polyval/src/field/backend/mod.rs | 2 +- polyval/src/field/backend/pclmulqdq.rs | 4 +- polyval/src/field/backend/soft.rs | 4 +- polyval/src/field/mod.rs | 26 ++++++------- polyval/src/lib.rs | 47 ++++++++++++++--------- polyval/src/tag.rs | 32 ++++++++++++++++ polyval/tests/lib.rs | 4 +- 15 files changed, 163 insertions(+), 66 deletions(-) create mode 100644 polyval/src/tag.rs diff --git a/poly1305/Cargo.toml b/poly1305/Cargo.toml index 1915f68..911ce8c 100644 --- a/poly1305/Cargo.toml +++ b/poly1305/Cargo.toml @@ -2,7 +2,7 @@ name = "poly1305" version = "0.2.0" authors = ["RustCrypto Developers"] -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" description = "The Poly1305 universal hash function and message authentication code" documentation = "https://docs.rs/poly1305" repository = "https://github.com/RustCrypto/MACs" @@ -12,11 +12,12 @@ readme = "README.md" [dependencies] byteorder = { version = "1", default-features = false } -crypto-mac = "0.7" +subtle = { version = "2", default-features = false } zeroize = { version = "0.9", optional = true, default-features = false } [dev-dependencies] crypto-mac = { version = "0.7", features = ["dev"] } [badges] +maintenance = { status = "experimental" } travis-ci = { repository = "RustCrypto/hashes" } diff --git a/poly1305/README.md b/poly1305/README.md index 74c7ea6..e664b4c 100644 --- a/poly1305/README.md +++ b/poly1305/README.md @@ -3,7 +3,8 @@ [![crate][crate-image]][crate-link] [![Docs][docs-image]][docs-link] ![Apache2/MIT licensed][license-image] -![Rust Version][rustc-image] +![MSRV][rustc-image] +![Maintenance Status: Experimental][maintenance-image] [![Build Status][build-image]][build-link] [Poly1305][1] is a [universal hash function][2] which, when combined with a cipher, @@ -14,6 +15,14 @@ In practice, Poly1305 is primarily combined with ciphers from the [Documentation][docs-link] +## Security Warning + +No security audits of this crate have ever been performed, and it has not been +thoroughly assessed to ensure its operation is constant-time on common CPU +architectures. + +USE AT YOUR OWN RISK! + ## License Licensed under either of: @@ -37,6 +46,7 @@ dual licensed as above, without any additional terms or conditions. [docs-link]: https://docs.rs/poly1305/ [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg [rustc-image]: https://img.shields.io/badge/rustc-1.27+-blue.svg +[maintenance-image]: https://img.shields.io/badge/maintenance-experimental-blue.svg [build-image]: https://travis-ci.org/RustCrypto/MACs.svg?branch=master [build-link]: https://travis-ci.org/RustCrypto/MACs diff --git a/poly1305/benches/poly1305.rs b/poly1305/benches/poly1305.rs index 7ad1f7c..6ad9166 100644 --- a/poly1305/benches/poly1305.rs +++ b/poly1305/benches/poly1305.rs @@ -8,7 +8,7 @@ use crypto_mac::generic_array::{ GenericArray, }; use crypto_mac::MacResult; -use poly1305::Poly1305; +use poly1305::{Poly1305, Block}; use std::convert::TryInto; bench!(Poly1305Mac); @@ -42,8 +42,7 @@ impl Mac for Poly1305Mac { } fn result(self) -> MacResult { - let mut mac = GenericArray::default(); - mac.copy_from_slice(&self.0.result()); - MacResult::new(mac) + let tag: Block = self.0.result().into(); + MacResult::new(tag.into()) } } diff --git a/poly1305/src/lib.rs b/poly1305/src/lib.rs index 98cdda1..c8b3f13 100644 --- a/poly1305/src/lib.rs +++ b/poly1305/src/lib.rs @@ -18,26 +18,28 @@ // TODO: replace with `u32::{from_le_bytes, to_le_bytes}` in libcore (1.32+) extern crate byteorder; -extern crate crypto_mac; +pub extern crate subtle; #[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; +use subtle::{Choice, ConstantTimeEq}; #[cfg(feature = "zeroize")] use zeroize::Zeroize; /// Size of a Poly1305 key pub const KEY_SIZE: usize = 32; +/// Poly1305 keys (32-bytes) +pub type Key = [u8; KEY_SIZE]; + /// Size of the blocks Poly1305 acts upon pub const BLOCK_SIZE: usize = 16; -/// Poly1305 authentication tags -pub type Tag = MacResult; +/// Poly1305 blocks (16-bytes) +pub type Block = [u8; BLOCK_SIZE]; /// The Poly1305 universal hash function. /// @@ -51,18 +53,18 @@ pub struct Poly1305 { h: [u32; 5], pad: [u32; 4], leftover: usize, - buffer: [u8; BLOCK_SIZE], + buffer: Block, } impl Poly1305 { /// Initialize Poly1305 with the given key - pub fn new(key: &[u8; KEY_SIZE]) -> Poly1305 { + pub fn new(key: &Key) -> Poly1305 { let mut poly = Poly1305 { r: [0u32; 5], h: [0u32; 5], pad: [0u32; 4], leftover: 0, - buffer: [0u8; BLOCK_SIZE], + buffer: Block::default(), }; // r &= 0xffffffc0ffffffc0ffffffc0fffffff @@ -128,7 +130,7 @@ impl Poly1305 { let unaligned_len = data.len() % BLOCK_SIZE; if unaligned_len != 0 { - let pad = [0u8; BLOCK_SIZE]; + let pad = Block::default(); let pad_len = BLOCK_SIZE - unaligned_len; self.input(&pad[..pad_len]); } @@ -233,13 +235,13 @@ impl Poly1305 { f = u64::from(h3) + u64::from(self.pad[3]) + (f >> 32); h3 = f as u32; - let mut tag = GenericArray::default(); + let mut tag = Block::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); - MacResult::new(tag) + Tag::new(tag) } /// Compute a single block of Poly1305 using the internal buffer @@ -344,3 +346,31 @@ impl Drop for Poly1305 { self.buffer.zeroize(); } } + +/// Poly1305 authentication tags +pub struct Tag(Block); + +impl Tag { + /// Create a new Poly1305 authentication tag + fn new(tag: Block) -> Self { + Tag(tag) + } +} + +impl AsRef for Tag { + fn as_ref(&self) -> &Block { + &self.0 + } +} + +impl ConstantTimeEq for Tag { + fn ct_eq(&self, other: &Self) -> Choice { + self.0.ct_eq(other.0.as_ref()) + } +} + +impl From for Block { + fn from(tag: Tag) -> Block { + tag.0 + } +} diff --git a/poly1305/tests/lib.rs b/poly1305/tests/lib.rs index 913de13..1e9a711 100644 --- a/poly1305/tests/lib.rs +++ b/poly1305/tests/lib.rs @@ -29,7 +29,7 @@ fn test_nacl_vector() { ]; let result1 = Poly1305::new(&key).chain(&msg).result(); - assert_eq!(&expected[..], result1.code().as_slice()); + assert_eq!(&expected[..], result1.as_ref()); let result2 = Poly1305::new(&key) .chain(&msg[0..32]) @@ -45,7 +45,7 @@ fn test_nacl_vector() { .chain(&msg[130..131]) .result(); - assert_eq!(&expected[..], result2.code().as_slice()); + assert_eq!(&expected[..], result2.as_ref()); } #[test] @@ -68,7 +68,7 @@ fn donna_self_test() { let result = Poly1305::new(&wrap_key).chain(&wrap_msg).result(); - assert_eq!(&wrap_mac[..], result.code().as_slice()); + assert_eq!(&wrap_mac[..], result.as_ref()); let total_key = [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xff, @@ -89,10 +89,10 @@ fn donna_self_test() { let msg: Vec = repeat(i as u8).take(256).collect(); let tag = Poly1305::new(&key).chain(&msg[..i]).result(); - tpoly.input(tag.code().as_slice()); + tpoly.input(tag.as_ref()); } - assert_eq!(&total_mac[..], tpoly.result().code().as_slice()); + assert_eq!(&total_mac[..], tpoly.result().as_ref()); } #[test] @@ -107,7 +107,7 @@ fn test_tls_vectors() { ]; let result1 = Poly1305::new(&key).chain(&msg1).result(); - assert_eq!(&expected1[..], result1.code().as_slice()); + assert_eq!(&expected1[..], result1.as_ref()); let msg2 = b"Hello world!"; let expected2 = [ @@ -116,7 +116,7 @@ fn test_tls_vectors() { ]; let result2 = Poly1305::new(&key).chain(&msg2[..]).result(); - assert_eq!(&expected2[..], result2.code().as_slice()); + assert_eq!(&expected2[..], result2.as_ref()); } #[test] @@ -139,5 +139,5 @@ fn padded_input() { let mut poly = Poly1305::new(&key); poly.input_padded(&msg); - assert_eq!(&expected[..], poly.result().code().as_slice()); + assert_eq!(&expected[..], poly.result().as_ref()); } diff --git a/polyval/Cargo.toml b/polyval/Cargo.toml index 316fbb1..db6ace0 100644 --- a/polyval/Cargo.toml +++ b/polyval/Cargo.toml @@ -15,7 +15,7 @@ categories = ["cryptography", "no-std"] [dependencies] byteorder = { version = "1", optional = true, default-features = false } -crypto-mac = "0.7" +subtle = { version = "2", default-features = false } [dev-dependencies] crypto-mac = { version = "0.7", features = ["dev"] } @@ -25,6 +25,7 @@ hex-literal = "0.1" insecure-soft = ["byteorder"] [badges] +maintenance = { status = "experimental" } travis-ci = { repository = "RustCrypto/hashes" } [package.metadata.docs.rs] diff --git a/polyval/README.md b/polyval/README.md index 21e7a9e..11abc3e 100644 --- a/polyval/README.md +++ b/polyval/README.md @@ -4,6 +4,7 @@ [![Docs][docs-image]][docs-link] ![Apache2/MIT licensed][license-image] ![Rust Version][rustc-image] +![Maintenance Status: Experimental][maintenance-image] [![Build Status][build-image]][build-link] [POLYVAL][1] ([RFC 8452][2]) is a [universal hash function][3] which operates @@ -16,6 +17,14 @@ closely related to [GHASH][6] and therefore can also be used to implement [Documentation][docs-link] +## Security Warning + +No security audits of this crate have ever been performed, and it has not been +thoroughly assessed to ensure its operation is constant-time on common CPU +architectures. + +USE AT YOUR OWN RISK! + ## License Licensed under either of: @@ -39,6 +48,7 @@ dual licensed as above, without any additional terms or conditions. [docs-link]: https://docs.rs/polyval/ [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg [rustc-image]: https://img.shields.io/badge/rustc-1.32+-blue.svg +[maintenance-image]: https://img.shields.io/badge/maintenance-experimental-blue.svg [build-image]: https://travis-ci.org/RustCrypto/MACs.svg?branch=master [build-link]: https://travis-ci.org/RustCrypto/MACs diff --git a/polyval/benches/polyval.rs b/polyval/benches/polyval.rs index 49c1335..1beca78 100644 --- a/polyval/benches/polyval.rs +++ b/polyval/benches/polyval.rs @@ -74,7 +74,8 @@ impl Mac for PolyvalMac { } fn result(self) -> MacResult { - self.poly.result() + let tag: Block = self.poly.result().into(); + MacResult::new(tag.into()) } } @@ -82,6 +83,6 @@ impl PolyvalMac { /// Input the current internal buffer into POLYVAL fn block(&mut self) { let elem = self.buffer; - self.poly.input(elem) + self.poly.input_block(elem) } } diff --git a/polyval/src/field/backend/mod.rs b/polyval/src/field/backend/mod.rs index 1354da1..f6a0290 100644 --- a/polyval/src/field/backend/mod.rs +++ b/polyval/src/field/backend/mod.rs @@ -50,7 +50,7 @@ pub(crate) use self::pclmulqdq::M128i; pub(crate) use self::soft::U64x2 as M128i; /// Trait representing the arithmetic operations we expect on the XMM registers -pub trait Xmm: +pub trait Backend: BitXor + Clmul + Copy + From + Into + From { /// Swap the hi and low 64-bit halves of the register diff --git a/polyval/src/field/backend/pclmulqdq.rs b/polyval/src/field/backend/pclmulqdq.rs index 091511d..ca7a201 100644 --- a/polyval/src/field/backend/pclmulqdq.rs +++ b/polyval/src/field/backend/pclmulqdq.rs @@ -9,7 +9,7 @@ use core::arch::x86::*; #[cfg(target_arch = "x86_64")] use core::arch::x86_64::*; -use super::Xmm; +use super::Backend; use core::ops::BitXor; use field::clmul::{self, Clmul}; use Block; @@ -60,7 +60,7 @@ impl Clmul for M128i { } } -impl Xmm for M128i { +impl Backend for M128i { fn shuffle(self) -> Self { M128i(unsafe { shufpd1(self.0) }) } diff --git a/polyval/src/field/backend/soft.rs b/polyval/src/field/backend/soft.rs index 0d06f64..2a0fd19 100644 --- a/polyval/src/field/backend/soft.rs +++ b/polyval/src/field/backend/soft.rs @@ -5,7 +5,7 @@ // TODO(tarcieri): performance-oriented constant-time implementation // See: -use super::Xmm; +use super::Backend; use byteorder::{ByteOrder, LE}; use core::ops::BitXor; use field::clmul::{self, Clmul}; @@ -86,7 +86,7 @@ impl Clmul for U64x2 { } } -impl Xmm for U64x2 { +impl Backend for U64x2 { fn shuffle(self) -> Self { U64x2([self.0[1], self.0[0]]) } diff --git a/polyval/src/field/mod.rs b/polyval/src/field/mod.rs index 9db0aab..7dd172a 100644 --- a/polyval/src/field/mod.rs +++ b/polyval/src/field/mod.rs @@ -17,7 +17,7 @@ pub mod backend; pub mod clmul; -use self::backend::Xmm; +use self::backend::Backend; use super::Block; use core::ops::{Add, Mul}; @@ -32,12 +32,12 @@ const MASK: u128 = 1 << 127 | 1 << 126 | 1 << 121 | 1; /// POLYVAL field element. #[derive(Copy, Clone)] -pub(crate) struct FieldElement(X); +pub(crate) struct Element(B); -impl FieldElement { +impl Element { /// Load a `FieldElement` from its bytestring representation. pub fn from_bytes(bytes: Block) -> Self { - FieldElement(bytes.into()) + Element(bytes.into()) } /// Serialize this `FieldElement` as a bytestring. @@ -48,17 +48,17 @@ impl FieldElement { /// Fast reduction modulo x^128 + x^127 + x^126 +x^121 + 1 (Gueron 2012) /// Algorithm 4: "Montgomery reduction" fn reduce(self) -> Self { - let mask = X::from(MASK); + let mask = B::from(MASK); let a = mask.clmul(self.0, 0x01); let b = self.0.shuffle() ^ a; let c = mask.clmul(b, 0x01); let d = b.shuffle() ^ c; - FieldElement(d) + Element(d) } } #[allow(clippy::suspicious_arithmetic_impl)] -impl Add for FieldElement { +impl Add for Element { type Output = Self; /// Adds two POLYVAL field elements. @@ -69,12 +69,12 @@ impl Add for FieldElement { /// /// [RFC 8452 Section 3]: https://tools.ietf.org/html/rfc8452#section-3 fn add(self, rhs: Self) -> Self { - FieldElement(self.0 ^ rhs.0) + Element(self.0 ^ rhs.0) } } #[allow(clippy::suspicious_arithmetic_impl)] -impl Mul for FieldElement { +impl Mul for Element { type Output = Self; /// Computes POLYVAL multiplication over GF(2^128). @@ -92,12 +92,12 @@ impl Mul for FieldElement { let t3 = self.0.clmul(rhs.0, 0x10); let t4 = self.0.clmul(rhs.0, 0x11); let t5 = t2 ^ t3; - FieldElement(t4 ^ t5.shr64()) + FieldElement(t1 ^ t5.shl64()).reduce() + Element(t4 ^ t5.shr64()) + Element(t1 ^ t5.shl64()).reduce() } } -impl From for FieldElement { - fn from(element: X) -> FieldElement { - FieldElement(element) +impl From for Element { + fn from(element: B) -> Element { + Element(element) } } diff --git a/polyval/src/lib.rs b/polyval/src/lib.rs index cf6d503..6da50ab 100644 --- a/polyval/src/lib.rs +++ b/polyval/src/lib.rs @@ -47,17 +47,16 @@ #![doc(html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo_small.png")] #![deny(missing_docs)] -extern crate crypto_mac; - // TODO: replace with `u64::from_le_bytes`/`u128::to_le_bytes` in libcore (1.32+) #[cfg(feature = "insecure-soft")] extern crate byteorder; +pub extern crate subtle; pub mod field; +pub mod tag; -use self::field::FieldElement; -use crypto_mac::generic_array::typenum::U16; -use crypto_mac::MacResult; +use self::field::Element; +pub use self::tag::Tag; // TODO(tarcieri): runtime selection of CLMUL vs soft backend when both are available use self::field::backend::M128i; @@ -68,46 +67,60 @@ pub const BLOCK_SIZE: usize = 16; /// POLYVAL blocks (16-bytes) pub type Block = [u8; BLOCK_SIZE]; -/// POLYVAL authentication tags -pub type Tag = MacResult; - /// **POLYVAL**: GHASH-like universal hash over GF(2^128). #[allow(non_snake_case)] #[derive(Clone)] #[repr(align(16))] pub struct Polyval { /// GF(2^128) field element input blocks are multiplied by - H: FieldElement, + H: Element, /// Field element representing the computed universal hash - S: FieldElement, + S: Element, } impl Polyval { /// Initialize POLYVAL with the given `H` field element pub fn new(h: Block) -> Self { Self { - H: FieldElement::from_bytes(h), - S: FieldElement::from_bytes(Block::default()), + H: Element::from_bytes(h), + S: Element::from_bytes(Block::default()), } } /// Input a field element `X` to be authenticated into POLYVAL. - pub fn input(&mut self, x: Block) { + pub fn input_block(&mut self, x: Block) { // "The sum of any two elements in the field is the result of XORing them." // -- RFC 8452 Section 3 - let sum = self.S + FieldElement::from_bytes(x); + let sum = self.S + Element::from_bytes(x); self.S = sum * self.H; } + /// Input data into POLYVAL, first padding it to the block size + /// ala the `right_pad_to_multiple_of_16_bytes()` function described in + /// RFC 8452 Section 4: + /// + pub fn input_padded(&mut self, data: &[u8]) { + for chunk in data.chunks(BLOCK_SIZE) { + if chunk.len() == BLOCK_SIZE { + // TODO(tarcieri): replace with `TryInto` in Rust 1.34+ + self.input_block(unsafe { *(chunk.as_ptr() as *const Block) }); + } else { + let mut padded_block = [0u8; BLOCK_SIZE]; + padded_block[..chunk.len()].copy_from_slice(chunk); + self.input_block(padded_block); + } + } + } + /// Process input blocks in a chained manner - pub fn chain(mut self, x: Block) -> Self { - self.input(x); + pub fn chain_block(mut self, x: Block) -> Self { + self.input_block(x); self } /// Get POLYVAL result (i.e. computed `S` field element) pub fn result(self) -> Tag { - Tag::new(self.S.to_bytes().into()) + Tag::new(self.S.to_bytes()) } } diff --git a/polyval/src/tag.rs b/polyval/src/tag.rs new file mode 100644 index 0000000..759ee88 --- /dev/null +++ b/polyval/src/tag.rs @@ -0,0 +1,32 @@ +//! POLYVAL authentication tags + +use subtle::{Choice, ConstantTimeEq}; +use Block; + +/// POLYVAL authentication tags +pub struct Tag(Block); + +impl Tag { + /// Create a new POLYVAL authentication tag + pub(crate) fn new(tag: Block) -> Self { + Tag(tag) + } +} + +impl AsRef for Tag { + fn as_ref(&self) -> &Block { + &self.0 + } +} + +impl ConstantTimeEq for Tag { + fn ct_eq(&self, other: &Self) -> Choice { + self.0.ct_eq(other.0.as_ref()) + } +} + +impl From for Block { + fn from(tag: Tag) -> Block { + tag.0 + } +} diff --git a/polyval/tests/lib.rs b/polyval/tests/lib.rs index e590783..bb70a9c 100644 --- a/polyval/tests/lib.rs +++ b/polyval/tests/lib.rs @@ -18,6 +18,6 @@ const POLYVAL_RESULT: Block = hex!("f7a3b47b846119fae5b7866cf5e5b77e"); #[test] fn rfc_8452_test_vector() { - let result = Polyval::new(H).chain(X_1).chain(X_2).result(); - assert_eq!(result.code().as_slice(), &POLYVAL_RESULT); + let result = Polyval::new(H).chain_block(X_1).chain_block(X_2).result(); + assert_eq!(result.as_ref(), &POLYVAL_RESULT); }