From 96ebbd7121bf012ad9a1223c5654b9b6fa3a0d26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Mon, 14 Mar 2022 06:15:18 +0300 Subject: [PATCH] universal-hash v0.5.0-pre --- .github/workflows/universal-hash.yml | 4 +- Cargo.lock | 14 +- README.md | 4 +- crypto/Cargo.toml | 2 +- universal-hash/Cargo.toml | 14 +- universal-hash/README.md | 6 +- universal-hash/src/lib.rs | 213 +++++++++++++-------------- 7 files changed, 123 insertions(+), 134 deletions(-) diff --git a/.github/workflows/universal-hash.yml b/.github/workflows/universal-hash.yml index a72fb941e..2df8b8f31 100644 --- a/.github/workflows/universal-hash.yml +++ b/.github/workflows/universal-hash.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: rust: - - 1.41.0 # MSRV + - 1.56.0 # MSRV - stable target: - thumbv7em-none-eabi @@ -50,7 +50,7 @@ jobs: strategy: matrix: rust: - - 1.41.0 # MSRV + - 1.56.0 # MSRV - stable steps: - uses: actions/checkout@v2 diff --git a/Cargo.lock b/Cargo.lock index 7b6fa4663..9e6e4136f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -242,7 +242,7 @@ dependencies = [ "elliptic-curve 0.12.2", "password-hash", "signature 1.5.0", - "universal-hash 0.4.1", + "universal-hash 0.5.0-pre", ] [[package]] @@ -755,7 +755,7 @@ checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" dependencies = [ "cpufeatures", "opaque-debug", - "universal-hash 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "universal-hash 0.4.1", ] [[package]] @@ -767,7 +767,7 @@ dependencies = [ "cfg-if", "cpufeatures", "opaque-debug", - "universal-hash 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "universal-hash 0.4.1", ] [[package]] @@ -1109,6 +1109,8 @@ checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" [[package]] name = "universal-hash" version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" dependencies = [ "generic-array", "subtle", @@ -1116,11 +1118,9 @@ dependencies = [ [[package]] name = "universal-hash" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +version = "0.5.0-pre" dependencies = [ - "generic-array", + "crypto-common 0.1.6", "subtle", ] diff --git a/README.md b/README.md index c06744b05..4f17baea3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # RustCrypto: Traits -[![Project Chat][chat-image]][chat-link] ![Apache2/MIT licensed][license-image] [![dependency status][deps-image]][deps-link] +[![Project Chat][chat-image]][chat-link] ![Apache2/MIT licensed][license-image] [![dependency status][deps-image]][deps-link] Collection of traits which describe functionality of cryptographic primitives. @@ -17,7 +17,7 @@ Collection of traits which describe functionality of cryptographic primitives. | [`kem`] | [Key encapsulation mechanism] | [![crates.io](https://img.shields.io/crates/v/kem.svg)](https://crates.io/crates/kem) | [![Documentation](https://docs.rs/kem/badge.svg)](https://docs.rs/kem) | ![MSRV 1.56][msrv-1.56] | | [`password-hash`] | [Password hashing] | [![crates.io](https://img.shields.io/crates/v/password-hash.svg)](https://crates.io/crates/password-hash) | [![Documentation](https://docs.rs/password-hash/badge.svg)](https://docs.rs/password-hash) | ![MSRV 1.57][msrv-1.57] | | [`signature`] | [Digital signature] | [![crates.io](https://img.shields.io/crates/v/signature.svg)](https://crates.io/crates/signature) | [![Documentation](https://docs.rs/signature/badge.svg)](https://docs.rs/signature) | ![MSRV 1.41][msrv-1.41] | -| [`universal‑hash`] | [Universal hash function] | [![crates.io](https://img.shields.io/crates/v/universal-hash.svg)](https://crates.io/crates/universal-hash) | [![Documentation](https://docs.rs/universal-hash/badge.svg)](https://docs.rs/universal-hash) | ![MSRV 1.41][msrv-1.41] | +| [`universal‑hash`] | [Universal hash function] | [![crates.io](https://img.shields.io/crates/v/universal-hash.svg)](https://crates.io/crates/universal-hash) | [![Documentation](https://docs.rs/universal-hash/badge.svg)](https://docs.rs/universal-hash) | ![MSRV 1.56][msrv-1.56] | ### Additional Crates diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index ec4fbd3ba..234b80ca8 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -22,7 +22,7 @@ elliptic-curve = { version = "0.12", optional = true, path = "../elliptic-curve" mac = { version = "0.11", package = "crypto-mac", optional = true } password-hash = { version = "0.4", optional = true, path = "../password-hash" } signature = { version = "1.5", optional = true, default-features = false, path = "../signature" } -universal-hash = { version = "0.4", optional = true, path = "../universal-hash" } +universal-hash = { version = "=0.5.0-pre", optional = true, path = "../universal-hash" } [features] std = [ diff --git a/universal-hash/Cargo.toml b/universal-hash/Cargo.toml index 2c15823ef..1ed6bd4a1 100644 --- a/universal-hash/Cargo.toml +++ b/universal-hash/Cargo.toml @@ -1,22 +1,24 @@ [package] name = "universal-hash" -version = "0.4.1" # Also update html_root_url in lib.rs when bumping this +version = "0.5.0-pre" +description = "Traits which describe the functionality of universal hash functions (UHFs)" authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" -description = "Trait for universal hash functions" +edition = "2021" +rust-version = "1.56" +readme = "README.md" documentation = "https://docs.rs/universal-hash" repository = "https://github.com/RustCrypto/traits" keywords = ["crypto", "mac"] categories = ["cryptography", "no-std"] -readme = "README.md" -edition = "2018" [dependencies] -generic-array = "0.14" +crypto-common = { version = "0.1.6", path = "../crypto-common" } subtle = { version = "=2.4", default-features = false } [features] -std = [] +std = ["crypto-common/std"] [package.metadata.docs.rs] all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/universal-hash/README.md b/universal-hash/README.md index 20fd50a82..9fd55a0cf 100644 --- a/universal-hash/README.md +++ b/universal-hash/README.md @@ -7,7 +7,7 @@ [![Project Chat][chat-image]][chat-link] [![Build Status][build-image]][build-link] -Traits which define functionality of [universal hash functions]. +Traits which describe functionality of [universal hash functions] (UHFs). See [RustCrypto/universal-hashes] for implementations which use this trait. @@ -15,7 +15,7 @@ See [RustCrypto/universal-hashes] for implementations which use this trait. ## Minimum Supported Rust Version -Rust **1.41** or higher. +Rust **1.56** or higher. Minimum supported Rust version can be changed in the future, but it will be done with a minor version bump. @@ -47,7 +47,7 @@ dual licensed as above, without any additional terms or conditions. [docs-image]: https://docs.rs/universal-hash/badge.svg [docs-link]: https://docs.rs/universal-hash/ [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg -[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260051-universal-hashes [build-image]: https://github.com/RustCrypto/traits/workflows/universal-hash/badge.svg?branch=master&event=push diff --git a/universal-hash/src/lib.rs b/universal-hash/src/lib.rs index dc802c815..91d4b13a9 100644 --- a/universal-hash/src/lib.rs +++ b/universal-hash/src/lib.rs @@ -18,46 +18,90 @@ //! [Universal Hash Functions]: https://en.wikipedia.org/wiki/Universal_hashing #![no_std] -#![forbid(unsafe_code)] #![doc( html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg", - html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg", - html_root_url = "https://docs.rs/universal-hash/0.4.1" + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg" )] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![deny(unsafe_code)] #![warn(missing_docs, rust_2018_idioms)] #[cfg(feature = "std")] extern crate std; -pub use generic_array::{self, typenum::consts}; +pub use crypto_common::{ + self, generic_array, + typenum::{self, consts}, + Block, Key, KeyInit, ParBlocks, +}; -use generic_array::typenum::Unsigned; +use core::slice; +use crypto_common::{BlockSizeUser, ParBlocksSizeUser}; use generic_array::{ArrayLength, GenericArray}; -use subtle::{Choice, ConstantTimeEq}; - -/// Keys to a [`UniversalHash`]. -pub type Key = GenericArray::KeySize>; - -/// Blocks are inputs to a [`UniversalHash`]. -pub type Block = GenericArray::BlockSize>; - -/// Instantiate a [`UniversalHash`] algorithm. -pub trait NewUniversalHash: Sized { - /// Size of the key for the universal hash function. - type KeySize: ArrayLength; +use subtle::ConstantTimeEq; +use typenum::Unsigned; + +/// Trait implemented by UHF backends. +pub trait UhfBackend: ParBlocksSizeUser { + /// Process single block. + fn proc_block(&mut self, block: &Block); + + /// Process several blocks in parallel. + #[inline(always)] + fn proc_par_blocks(&mut self, blocks: &ParBlocks) { + for block in blocks { + self.proc_block(block); + } + } +} - /// Instantiate a universal hash function with the given key. - fn new(key: &Key) -> Self; +/// Trait for [`UhfBackend`] users. +/// +/// This trait is used to define rank-2 closures. +pub trait UhfClosure: BlockSizeUser { + /// Execute closure with the provided UHF backend. + fn call>(self, backend: &mut B); } /// The [`UniversalHash`] trait defines a generic interface for universal hash /// functions. -pub trait UniversalHash: Clone { - /// Size of the inputs to and outputs from the universal hash function - type BlockSize: ArrayLength; +pub trait UniversalHash: BlockSizeUser + Sized { + /// Update hash function state using the provided rank-2 closure. + fn update_with_backend(&mut self, f: impl UhfClosure); + + /// Update hash function state with the provided block. + #[inline] + fn update(&mut self, blocks: &[Block]) { + struct Ctx<'a, BS: ArrayLength> { + blocks: &'a [Block], + } + + impl<'a, BS: ArrayLength> BlockSizeUser for Ctx<'a, BS> { + type BlockSize = BS; + } + + impl<'a, BS: ArrayLength> UhfClosure for Ctx<'a, BS> { + #[inline(always)] + fn call>(self, backend: &mut B) { + let pb = B::ParBlocksSize::USIZE; + if pb > 1 { + let (par_blocks, tail) = to_blocks(self.blocks); + for par_block in par_blocks { + backend.proc_par_blocks(par_block); + } + for block in tail { + backend.proc_block(block); + } + } else { + for block in self.blocks { + backend.proc_block(block); + } + } + } + } - /// Input a block into the universal hash function - fn update(&mut self, block: &Block); + self.update_with_backend(Ctx { blocks }); + } /// Input data into the universal hash function. If the length of the /// data is not a multiple of the block size, the remaining data is @@ -65,41 +109,30 @@ pub trait UniversalHash: Clone { /// /// This approach is frequently used by AEAD modes which use /// Message Authentication Codes (MACs) based on universal hashing. + #[inline] fn update_padded(&mut self, data: &[u8]) { - let mut chunks = data.chunks_exact(Self::BlockSize::to_usize()); + let (blocks, tail) = to_blocks(data); - for chunk in &mut chunks { - self.update(GenericArray::from_slice(chunk)); - } + self.update(blocks); - let rem = chunks.remainder(); - - if !rem.is_empty() { + if !tail.is_empty() { let mut padded_block = GenericArray::default(); - padded_block[..rem.len()].copy_from_slice(rem); - self.update(&padded_block); + padded_block[..tail.len()].copy_from_slice(tail); + self.update(slice::from_ref(&padded_block)); } } - /// Reset [`UniversalHash`] instance. - fn reset(&mut self); - - /// Obtain the [`Output`] of a [`UniversalHash`] function and consume it. - fn finalize(self) -> Output; + /// Retrieve result and consume hasher instance. + fn finalize(self) -> Block; - /// Obtain the [`Output`] of a [`UniversalHash`] computation and reset it back - /// to its initial state. - fn finalize_reset(&mut self) -> Output { - let res = self.clone().finalize(); - self.reset(); - res - } - - /// Verify the [`UniversalHash`] of the processed input matches a given [`Output`]. + /// Verify the [`UniversalHash`] of the processed input matches + /// a given `expected` value. + /// /// This is useful when constructing Message Authentication Codes (MACs) /// from universal hash functions. - fn verify(self, other: &Block) -> Result<(), Error> { - if self.finalize() == other.into() { + #[inline] + fn verify(self, expected: &Block) -> Result<(), Error> { + if self.finalize().ct_eq(expected).unwrap_u8() == 1 { Ok(()) } else { Err(Error) @@ -107,78 +140,32 @@ pub trait UniversalHash: Clone { } } -/// Outputs of universal hash functions which are a thin wrapper around a -/// byte array. Provides a safe [`Eq`] implementation that runs in constant time, -/// which is useful for implementing Message Authentication Codes (MACs) based -/// on universal hashing. -#[derive(Clone)] -pub struct Output { - bytes: GenericArray, -} - -impl Output -where - U: UniversalHash, -{ - /// Create a new [`Output`] block. - pub fn new(bytes: Block) -> Output { - Output { bytes } - } - - /// Get the inner [`GenericArray`] this type wraps - pub fn into_bytes(self) -> Block { - self.bytes - } -} - -impl From> for Output -where - U: UniversalHash, -{ - fn from(bytes: Block) -> Self { - Output { bytes } - } -} - -impl<'a, U> From<&'a Block> for Output -where - U: UniversalHash, -{ - fn from(bytes: &'a Block) -> Self { - bytes.clone().into() - } -} - -impl ConstantTimeEq for Output -where - U: UniversalHash, -{ - fn ct_eq(&self, other: &Self) -> Choice { - self.bytes.ct_eq(&other.bytes) - } -} - -impl PartialEq for Output -where - U: UniversalHash, -{ - fn eq(&self, x: &Output) -> bool { - self.ct_eq(x).unwrap_u8() == 1 - } -} - -impl Eq for Output {} - -/// Error type for when the [`Output`] of a [`UniversalHash`] -/// is not equal to the expected value. +/// Error type used by the [`UniversalHash::verify`] method +/// to indicate that UHF output is not equal the expected value. #[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] pub struct Error; impl core::fmt::Display for Error { + #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.write_str("UHF output mismatch") } } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for Error {} + +/// Split message into slice of blocks and leftover tail. +// TODO: replace with `slice::as_chunks` on migration to const generics +#[inline(always)] +fn to_blocks>(data: &[T]) -> (&[GenericArray], &[T]) { + let nb = data.len() / N::USIZE; + let (left, right) = data.split_at(nb * N::USIZE); + let p = left.as_ptr() as *const GenericArray; + // SAFETY: we guarantee that `blocks` does not point outside of `data` + // and `p` is valid for reads + #[allow(unsafe_code)] + let blocks = unsafe { slice::from_raw_parts(p, nb) }; + (blocks, right) +}