diff --git a/.github/workflows/crypto-common.yml b/.github/workflows/crypto-common.yml index 287c49535..bc45c25b4 100644 --- a/.github/workflows/crypto-common.yml +++ b/.github/workflows/crypto-common.yml @@ -52,6 +52,5 @@ jobs: profile: minimal - run: cargo check --all-features - run: cargo test - - run: cargo test --features core-api - run: cargo test --features std - run: cargo test --all-features diff --git a/.github/workflows/crypto-mac.yml b/.github/workflows/crypto-mac.yml deleted file mode 100644 index 29186ab72..000000000 --- a/.github/workflows/crypto-mac.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: crypto-mac - -on: - pull_request: - paths: - - "crypto-mac/**" - - "Cargo.*" - push: - branches: master - -defaults: - run: - working-directory: crypto-mac - -env: - CARGO_INCREMENTAL: 0 - RUSTFLAGS: "-Dwarnings" - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - rust: - - 1.41.0 # MSRV - - stable - target: - - thumbv7em-none-eabi - - wasm32-unknown-unknown - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.rust }} - target: ${{ matrix.target }} - override: true - profile: minimal - - run: cargo build --target ${{ matrix.target }} --release --no-default-features - - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features rand_core - - test: - runs-on: ubuntu-latest - strategy: - matrix: - rust: - - 1.41.0 # MSRV - - stable - steps: - - uses: actions/checkout@v1 - - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.rust }} - override: true - profile: minimal - - run: cargo check --all-features - - run: cargo test - - run: cargo test --features core-api - - run: cargo test --features dev - - run: cargo test --features std - - run: cargo test --all-features diff --git a/.github/workflows/digest.yml b/.github/workflows/digest.yml index a518cd23a..c48c45c2f 100644 --- a/.github/workflows/digest.yml +++ b/.github/workflows/digest.yml @@ -52,7 +52,6 @@ jobs: profile: minimal - run: cargo check --all-features - run: cargo test --release - - run: cargo test --features core-api --release - run: cargo test --features dev --release - run: cargo test --features alloc --release - run: cargo test --features std --release diff --git a/Cargo.lock b/Cargo.lock index fe92941cb..83fb77374 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,19 +54,8 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.0-pre.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4b13c429c0b48d55a541108e23c7795eb821ee65b50c2f719f4f7fc5a022dcf" -dependencies = [ - "block-padding", - "generic-array", -] - -[[package]] -name = "block-padding" -version = "0.3.0-pre" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3992d179d1dd2fa87869057217d43cf88ad31d4e44738d159a5a6caafdf63ae6" +version = "0.10.0" +source = "git+https://github.com/RustCrypto/utils?branch=block-buffer/v0.10#99517be56ffecddd6cd0f136f856f565ac7ef62c" dependencies = [ "generic-array", ] @@ -88,7 +77,6 @@ name = "cipher" version = "0.4.0-pre" dependencies = [ "blobby", - "block-buffer 0.10.0-pre.4", "crypto-common", "generic-array", "rand_core", @@ -105,22 +93,10 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.0-pre" -dependencies = [ - "block-buffer 0.10.0-pre.4", - "generic-array", -] - -[[package]] -name = "crypto-mac" -version = "0.12.0-pre" +version = "0.1.0" dependencies = [ - "blobby", - "cipher", - "crypto-common", "generic-array", "rand_core", - "subtle", ] [[package]] @@ -134,11 +110,13 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.0-pre.3" +version = "0.10.0" dependencies = [ "blobby", + "block-buffer 0.10.0", "crypto-common", "generic-array", + "subtle", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 10ed45ce0..0389aef3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,10 +3,12 @@ members = [ "aead", "cipher", "crypto-common", - "crypto-mac", "digest", "password-hash", "signature", "signature/async", "universal-hash", ] + +[patch.crates-io] +block-buffer = { git = "https://github.com/RustCrypto/utils", branch = "block-buffer/v0.10" } diff --git a/cipher/Cargo.toml b/cipher/Cargo.toml index 017e3c028..49a2b4c4e 100644 --- a/cipher/Cargo.toml +++ b/cipher/Cargo.toml @@ -13,17 +13,13 @@ categories = ["cryptography", "no-std"] [dependencies] generic-array = "0.14" -crypto-common = { version = "=0.1.0-pre", path = "../crypto-common" } +crypto-common = { version = "0.1", path = "../crypto-common" } -# optional dependencies -block-buffer = { version = "=0.10.0-pre.4", features = ["block-padding"], optional = true } blobby = { version = "0.3", optional = true } rand_core = { version = "0.6", optional = true } [features] -default = ["mode_wrapper"] std = ["crypto-common/std", "rand_core/std"] -mode_wrapper = ["block-buffer"] dev = ["blobby"] [package.metadata.docs.rs] diff --git a/crypto-common/Cargo.toml b/crypto-common/Cargo.toml index 3fa429be3..b38f4bb9d 100644 --- a/crypto-common/Cargo.toml +++ b/crypto-common/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "crypto-common" description = "Common cryptographic traits" -version = "0.1.0-pre" +version = "0.1.0" authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" readme = "README.md" @@ -13,9 +13,7 @@ categories = ["cryptography", "no-std"] [dependencies] generic-array = "0.14" -block-buffer = { version = "0.10.0-pre.2", optional = true } +rand_core = { version = "0.6", optional = true } [features] -block-padding = ["block-buffer/block-padding"] -core-api = ["block-buffer"] std = [] diff --git a/crypto-common/src/core_api.rs b/crypto-common/src/core_api.rs deleted file mode 100644 index 7def7a076..000000000 --- a/crypto-common/src/core_api.rs +++ /dev/null @@ -1,132 +0,0 @@ -//! Low-level core API traits. -use super::{FixedOutput, FixedOutputReset, Reset, Update}; -use block_buffer::DigestBuffer; -use core::fmt; -use generic_array::{ArrayLength, GenericArray}; - -/// Trait for types which consume data in blocks. -#[cfg(feature = "core-api")] -#[cfg_attr(docsrs, doc(cfg(feature = "core-api")))] -pub trait UpdateCore { - /// Block size in bytes. - type BlockSize: ArrayLength; - /// Block buffer type over which value operates. - type Buffer: DigestBuffer; - - /// Update state using the provided data blocks. - fn update_blocks(&mut self, blocks: &[GenericArray]); -} - -/// Core trait for hash functions with fixed output size. -#[cfg(feature = "core-api")] -#[cfg_attr(docsrs, doc(cfg(feature = "core-api")))] -pub trait FixedOutputCore: UpdateCore { - /// Size of result in bytes. - type OutputSize: ArrayLength; - - /// Finalize state using remaining data stored in the provided block buffer, - /// write result into provided array using and leave value in a dirty state. - fn finalize_fixed_core( - &mut self, - buffer: &mut Self::Buffer, - out: &mut GenericArray, - ); -} - -/// Trait which stores algorithm name constant, used in `Debug` implementations. -pub trait AlgorithmName { - /// Write algorithm name into `f`. - fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result; -} - -/// Wrapper around [`UpdateCore`] implementations. -/// -/// It handles data buffering and implements the slice-based traits. -#[derive(Clone, Default)] -pub struct CoreWrapper { - core: T, - buffer: T::Buffer, -} - -impl CoreWrapper { - /// Create new wrapper from `core`. - #[inline] - pub fn from_core(core: T) -> Self { - let buffer = Default::default(); - Self { core, buffer } - } - - /// Decompose wrapper into inner parts. - #[inline] - pub fn decompose(self) -> (T, T::Buffer) { - let Self { core, buffer } = self; - (core, buffer) - } -} - -impl CoreWrapper { - /// Apply function to core and buffer, return its result, - /// and reset core and buffer. - pub fn apply_reset(&mut self, mut f: impl FnMut(&mut T, &mut T::Buffer) -> V) -> V { - let Self { core, buffer } = self; - let res = f(core, buffer); - core.reset(); - buffer.reset(); - res - } -} - -impl fmt::Debug for CoreWrapper { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - T::write_alg_name(f)?; - f.write_str(" { .. }") - } -} - -impl Reset for CoreWrapper { - #[inline] - fn reset(&mut self) { - self.core.reset(); - self.buffer.reset(); - } -} - -impl Update for CoreWrapper { - #[inline] - fn update(&mut self, input: &[u8]) { - let Self { core, buffer } = self; - buffer.digest_blocks(input, |blocks| core.update_blocks(blocks)); - } -} - -impl FixedOutput for CoreWrapper { - type OutputSize = D::OutputSize; - - #[inline] - fn finalize_into(mut self, out: &mut GenericArray) { - let Self { core, buffer } = &mut self; - core.finalize_fixed_core(buffer, out); - } -} - -impl FixedOutputReset for CoreWrapper { - #[inline] - fn finalize_into_reset(&mut self, out: &mut GenericArray) { - self.apply_reset(|core, buffer| core.finalize_fixed_core(buffer, out)); - } -} - -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl std::io::Write for CoreWrapper { - #[inline] - fn write(&mut self, buf: &[u8]) -> std::io::Result { - Update::update(self, buf); - Ok(buf.len()) - } - - #[inline] - fn flush(&mut self) -> std::io::Result<()> { - Ok(()) - } -} diff --git a/crypto-common/src/lib.rs b/crypto-common/src/lib.rs index 5869bc677..a11626d37 100644 --- a/crypto-common/src/lib.rs +++ b/crypto-common/src/lib.rs @@ -12,56 +12,262 @@ #[cfg(feature = "std")] extern crate std; -use generic_array::{ArrayLength, GenericArray}; +use core::fmt; +use generic_array::{typenum::Unsigned, ArrayLength, GenericArray}; +#[cfg(feature = "rand_core")] +use rand_core::{CryptoRng, RngCore}; -#[cfg(feature = "core-api")] -#[cfg_attr(docsrs, doc(cfg(feature = "core-api")))] -pub use block_buffer; +/// Block on which [`BlockSizeUser`] implementors operate. +pub type Block = GenericArray::BlockSize>; +/// Output array of [`OutputSizeUser`] implementors. +pub type Output = GenericArray::OutputSize>; +/// Key used by [`KeySizeUser`] implementors. +pub type Key = GenericArray::KeySize>; +/// Initialization vector (nonce) used by [`IvSizeUser`] implementors. +pub type Iv = GenericArray::IvSize>; -#[cfg(feature = "core-api")] -#[cfg_attr(docsrs, doc(cfg(feature = "core-api")))] -pub mod core_api; +/// Types which process data in blocks. +pub trait BlockSizeUser { + /// Size of the block in bytes. + type BlockSize: ArrayLength + 'static; +} -/// Trait for types which consume data. -pub trait Update { - /// Update state using the provided data. - fn update(&mut self, data: &[u8]); +impl BlockSizeUser for &T { + type BlockSize = T::BlockSize; } -/// Trait for types which return fixed-sized result after finalization. -pub trait FixedOutput: Sized { - /// Size of result in bytes. - type OutputSize: ArrayLength; +impl BlockSizeUser for &mut T { + type BlockSize = T::BlockSize; +} - /// Consume value and write result into provided array. - fn finalize_into(self, out: &mut GenericArray); +/// Types which return data with the given size. +pub trait OutputSizeUser { + /// Size of the output in bytes. + type OutputSize: ArrayLength + 'static; +} + +/// Types which use key for initialization. +/// +/// Generally it's used indirectly via [`KeyInit`] or [`KeyIvInit`]. +pub trait KeySizeUser { + /// Key size in bytes. + type KeySize: ArrayLength + 'static; +} + +/// Types which use initialization vector (nonce) for initialization. +/// +/// Generally it's used indirectly via [`KeyIvInit`] or [`InnerIvInit`]. +pub trait IvSizeUser { + /// Initialization vector size in bytes. + type IvSize: ArrayLength + 'static; +} + +/// Types which use another type for initialization. +/// +/// Generally it's used indirectly via [`InnerInit`] or [`InnerIvInit`]. +pub trait InnerUser { + /// Inner type. + type Inner; +} - /// Retrieve result and consume the hasher instance. +/// Resettable types. +pub trait Reset { + /// Reset state to its initial value. + fn reset(&mut self); +} + +/// Trait which stores algorithm name constant, used in `Debug` implementations. +pub trait AlgorithmName { + /// Write algorithm name into `f`. + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result; +} + +/// Types which can be initialized from key. +pub trait KeyInit: KeySizeUser + Sized { + /// Create new value from fixed size key. + fn new(key: &Key) -> Self; + + /// Create new value from variable size key. + fn new_from_slice(key: &[u8]) -> Result { + if key.len() != Self::KeySize::to_usize() { + Err(InvalidLength) + } else { + Ok(Self::new(Key::::from_slice(key))) + } + } + + /// Generate random key using the provided [`CryptoRng`]. + #[cfg(feature = "rand_core")] + #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] #[inline] - fn finalize_fixed(self) -> GenericArray { - let mut out = Default::default(); - self.finalize_into(&mut out); - out + fn generate_key(mut rng: impl CryptoRng + RngCore) -> Key { + let mut key = Key::::default(); + rng.fill_bytes(&mut key); + key } } -/// Trait for types which return fixed-sized result after finalization and reset -/// values into its initial state. -pub trait FixedOutputReset: FixedOutput + Reset { - /// Write result into provided array and reset value to its initial state. - fn finalize_into_reset(&mut self, out: &mut GenericArray); +/// Types which can be initialized from key and initialization vector (nonce). +pub trait KeyIvInit: KeySizeUser + IvSizeUser + Sized { + /// Create new value from fixed length key and nonce. + fn new(key: &Key, iv: &Iv) -> Self; + + /// Create new value from variable length key and nonce. + #[inline] + fn new_from_slices(key: &[u8], iv: &[u8]) -> Result { + let key_len = Self::KeySize::USIZE; + let iv_len = Self::IvSize::USIZE; + if key.len() != key_len || iv.len() != iv_len { + Err(InvalidLength) + } else { + Ok(Self::new( + Key::::from_slice(key), + Iv::::from_slice(iv), + )) + } + } + + /// Generate random key using the provided [`CryptoRng`]. + #[cfg(feature = "rand_core")] + #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] + #[inline] + fn generate_key(mut rng: impl CryptoRng + RngCore) -> Key { + let mut key = Key::::default(); + rng.fill_bytes(&mut key); + key + } + + /// Generate random IV using the provided [`CryptoRng`]. + #[cfg(feature = "rand_core")] + #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] + #[inline] + fn generate_iv(mut rng: impl CryptoRng + RngCore) -> Iv { + let mut iv = Iv::::default(); + rng.fill_bytes(&mut iv); + iv + } - /// Retrieve result and reset the hasher instance. + /// Generate random key and nonce using the provided [`CryptoRng`]. + #[cfg(feature = "rand_core")] + #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] #[inline] - fn finalize_fixed_reset(&mut self) -> GenericArray { - let mut out = Default::default(); - self.finalize_into_reset(&mut out); - out + fn generate_key_iv(mut rng: impl CryptoRng + RngCore) -> (Key, Iv) { + (Self::generate_key(&mut rng), Self::generate_iv(&mut rng)) } } -/// Trait for resetting values to initial state. -pub trait Reset { - /// Reset value to its initial state. - fn reset(&mut self); +/// Types which can be initialized from another type (usually block ciphers). +/// +/// Usually used for initializing types from block ciphers. +pub trait InnerInit: InnerUser + Sized { + /// Initialize value from the `inner`. + fn inner_init(inner: Self::Inner) -> Self; } + +/// Types which can be initialized from another type and additional initialization +/// vector/nonce. +/// +/// Usually used for initializing types from block ciphers. +pub trait InnerIvInit: InnerUser + IvSizeUser + Sized { + /// Initialize value using `inner` and `iv` array. + fn inner_iv_init(inner: Self::Inner, iv: &Iv) -> Self; + + /// Initialize value using `inner` and `iv` slice. + fn inner_iv_slice_init(inner: Self::Inner, iv: &[u8]) -> Result { + if iv.len() != Self::IvSize::to_usize() { + Err(InvalidLength) + } else { + Ok(Self::inner_iv_init(inner, Iv::::from_slice(iv))) + } + } + + /// Generate random IV using the provided [`CryptoRng`]. + #[cfg(feature = "rand_core")] + #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] + #[inline] + fn generate_iv(mut rng: impl CryptoRng + RngCore) -> Iv { + let mut iv = Iv::::default(); + rng.fill_bytes(&mut iv); + iv + } +} + +impl KeySizeUser for T +where + T: InnerUser, + T::Inner: KeySizeUser, +{ + type KeySize = ::KeySize; +} + +impl KeyIvInit for T +where + T: InnerIvInit, + T::Inner: KeyInit, +{ + #[inline] + fn new(key: &Key, iv: &Iv) -> Self { + Self::inner_iv_init(T::Inner::new(key), iv) + } + + #[inline] + fn new_from_slices(key: &[u8], iv: &[u8]) -> Result { + T::Inner::new_from_slice(key).and_then(|i| T::inner_iv_slice_init(i, iv)) + } +} + +impl KeyInit for T +where + T: InnerInit, + T::Inner: KeyInit, +{ + #[inline] + fn new(key: &Key) -> Self { + Self::inner_init(T::Inner::new(key)) + } + + #[inline] + fn new_from_slice(key: &[u8]) -> Result { + T::Inner::new_from_slice(key) + .map_err(|_| InvalidLength) + .map(Self::inner_init) + } +} + +// Unfortunately this blanket impl is impossible without mutually +// exclusive traits, see: https://github.com/rust-lang/rfcs/issues/1053 +// or at the very least without: https://github.com/rust-lang/rust/issues/20400 +/* +impl KeyIvInit for T +where + T: InnerInit, + T::Inner: KeyIvInit, +{ + #[inline] + fn new(key: &Key, iv: &Iv) -> Self { + Self::inner_init(T::Inner::new(key, iv)) + } + + #[inline] + fn new_from_slices(key: &[u8], iv: &[u8]) -> Result { + T::Inner::new_from_slice(key) + .map_err(|_| InvalidLength) + .map(Self::inner_init) + } +} +*/ + +/// The error type returned when key and/or IV used in the [`KeyInit`], +/// [`KeyIvInit`], and [`InnerIvInit`] slice-based methods had +/// an invalid length. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub struct InvalidLength; + +impl fmt::Display for InvalidLength { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str("Invalid Length") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for InvalidLength {} diff --git a/crypto-mac/CHANGELOG.md b/crypto-mac/CHANGELOG.md deleted file mode 100644 index 7488081b1..000000000 --- a/crypto-mac/CHANGELOG.md +++ /dev/null @@ -1,104 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] -### Added -- Re-export `rand_core` ([#683]) - -[#683]: https://github.com/RustCrypto/traits/pull/683 - -## 0.11.1 (2021-07-20) -### Changed -- Pin `subtle` dependency to v2.4 ([#691]) - -[#691]: https://github.com/RustCrypto/traits/pull/691 - -## 0.11.0 (2021-04-28) -### Added -- `generate_key` method to `New*` trait ([#513]) - -### Changed -- Renamed `new_var` to `new_from_slice` ([#442]) -- Bump `cipher` dependency to v0.3 ([#621]) - -[#442]: https://github.com/RustCrypto/traits/pull/442 -[#513]: https://github.com/RustCrypto/traits/pull/513 -[#621]: https://github.com/RustCrypto/traits/pull/621 - -## 0.10.1 (2021-07-20) -### Changed -- Pin `subtle` dependency to v2.4 ([#690]) - -[#690]: https://github.com/RustCrypto/traits/pull/690 - -## 0.10.0 (2020-10-15) -### Changed -- Replace `block-cipher` crate with new `cipher` crate ([#337], [#338]) - -[#338]: https://github.com/RustCrypto/traits/pull/338 -[#337]: https://github.com/RustCrypto/traits/pull/337 - -## 0.9.1 (2020-08-12) -### Added -- Re-export the `block-cipher` crate ([#257]) - -[#257]: https://github.com/RustCrypto/traits/pull/257 - -## 0.9.0 (2020-08-10) -### Added -- `FromBlockCipher` trait and blanket implementation of the `NewMac` trait -for it ([#217]) - -### Changed -- Updated test vectors storage to `blobby v0.3` ([#217]) - -### Removed -- `impl_write!` macro ([#217]) - -[#217]: https://github.com/RustCrypto/traits/pull/217 - -## 0.8.0 (2020-06-04) -### Added -- `impl_write!` macro ([#134]) - -### Changed -- Bump `generic-array` dependency to v0.14 ([#144]) -- Split `Mac` initialization into `NewMac` trait ([#133]) -- Rename `MacResult` => `Output`, `code` => `into_bytes` ([#114]) -- Rename `Input::input` to `Update::update` ([#111]) -- Update to 2018 edition ([#108]) -- Bump `subtle` dependency from v1.0 to v2.0 ([#33]) - -[#144]: https://github.com/RustCrypto/traits/pull/95 -[#134]: https://github.com/RustCrypto/traits/pull/134 -[#133]: https://github.com/RustCrypto/traits/pull/133 -[#114]: https://github.com/RustCrypto/traits/pull/114 -[#111]: https://github.com/RustCrypto/traits/pull/111 -[#108]: https://github.com/RustCrypto/traits/pull/108 -[#33]: https://github.com/RustCrypto/traits/pull/33 - -## 0.7.0 (2018-10-01) - -## 0.6.2 (2018-06-21) - -## 0.6.1 (2018-06-20) - -## 0.6.0 (2017-11-26) - -## 0.5.2 (2017-11-20) - -## 0.5.1 (2017-11-15) - -## 0.5.0 (2017-11-14) - -## 0.4.0 (2017-06-12) - -## 0.3.0 (2017-05-14) - -## 0.2.0 (2017-05-14) - -## 0.1.0 (2016-10-14) diff --git a/crypto-mac/Cargo.toml b/crypto-mac/Cargo.toml deleted file mode 100644 index 725f75fa4..000000000 --- a/crypto-mac/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "crypto-mac" -description = "Trait for Message Authentication Code (MAC) algorithms" -version = "0.12.0-pre" # Also update html_root_url in lib.rs when bumping this -authors = ["RustCrypto Developers"] -license = "MIT OR Apache-2.0" -readme = "README.md" -edition = "2018" -documentation = "https://docs.rs/crypto-mac" -repository = "https://github.com/RustCrypto/traits" -keywords = ["crypto", "mac"] -categories = ["cryptography", "no-std"] - -[dependencies] -generic-array = "0.14" -crypto-common = { version = "=0.1.0-pre", path = "../crypto-common" } -cipher = { version = "=0.4.0-pre", path = "../cipher" } -subtle = { version = "=2.4", default-features = false } - -blobby = { version = "0.3", optional = true } -rand_core = { version = "0.6", optional = true } - -[features] -dev = ["blobby"] -core-api = ["crypto-common/core-api"] -std = ["crypto-common/std", "rand_core/std"] - -[package.metadata.docs.rs] -all-features = true -rustdoc-args = ["--cfg", "docsrs"] diff --git a/crypto-mac/LICENSE-APACHE b/crypto-mac/LICENSE-APACHE deleted file mode 100644 index 78173fa2e..000000000 --- a/crypto-mac/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/crypto-mac/LICENSE-MIT b/crypto-mac/LICENSE-MIT deleted file mode 100644 index 8dcb85b30..000000000 --- a/crypto-mac/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2017 Artyom Pavlov - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/crypto-mac/README.md b/crypto-mac/README.md deleted file mode 100644 index 889d6c244..000000000 --- a/crypto-mac/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# RustCrypto: Message Authentication Code Traits - -[![crate][crate-image]][crate-link] -[![Docs][docs-image]][docs-link] -![Apache2/MIT licensed][license-image] -![Rust Version][rustc-image] -[![Project Chat][chat-image]][chat-link] -[![Build Status][build-image]][build-link] - -Traits for [Message Authentication Code] (MAC) algorithms. - -See [RustCrypto/MACs] for implementations which use this trait. - -[Documentation][docs-link] - -## Minimum Supported Rust Version - -Rust **1.41** or higher. - -Minimum supported Rust version can be changed in the future, but it will be -done with a minor version bump. - -## SemVer Policy - -- All on-by-default features of this library are covered by SemVer -- MSRV is considered exempt from SemVer as noted above - -## License - -Licensed under either of: - - * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) - * [MIT license](http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in the work by you, as defined in the Apache-2.0 license, shall be -dual licensed as above, without any additional terms or conditions. - -[//]: # (badges) - -[crate-image]: https://img.shields.io/crates/v/crypto-mac.svg -[crate-link]: https://crates.io/crates/crypto-mac -[docs-image]: https://docs.rs/crypto-mac/badge.svg -[docs-link]: https://docs.rs/crypto-mac/ -[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 -[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg -[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260044-MACs -[build-image]: https://github.com/RustCrypto/traits/workflows/crypto-mac/badge.svg?branch=master&event=push -[build-link]: https://github.com/RustCrypto/traits/actions?query=workflow%3Acrypto-mac - -[//]: # (general links) - -[Message Authentication Code]: https://en.wikipedia.org/wiki/Message_authentication_code -[RustCrypto/MACs]: https://github.com/RustCrypto/MACs diff --git a/crypto-mac/src/core_api.rs b/crypto-mac/src/core_api.rs deleted file mode 100644 index 7f7e6a6ec..000000000 --- a/crypto-mac/src/core_api.rs +++ /dev/null @@ -1,2 +0,0 @@ -//! Low-level core API traits. -pub use crypto_common::core_api::{AlgorithmName, CoreWrapper, FixedOutputCore, UpdateCore}; diff --git a/crypto-mac/src/dev.rs b/crypto-mac/src/dev.rs deleted file mode 100644 index 1c397e631..000000000 --- a/crypto-mac/src/dev.rs +++ /dev/null @@ -1,148 +0,0 @@ -//! Development-related functionality - -pub use blobby; - -/// Define test -#[macro_export] -#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] -macro_rules! new_test { - ($name:ident, $test_name:expr, $mac:ty) => { - #[test] - fn $name() { - use crypto_mac::dev::blobby::Blob3Iterator; - use crypto_mac::{Mac, NewMac}; - - fn run_test(key: &[u8], input: &[u8], tag: &[u8]) -> Option<&'static str> { - let mut mac = <$mac as NewMac>::new_from_slice(key).unwrap(); - mac.update(input); - let result = mac.finalize_reset(); - if &result.into_bytes()[..] != tag { - return Some("whole message"); - } - // test if reset worked correctly - mac.update(input); - if mac.verify(&tag).is_err() { - return Some("after reset"); - } - - let mut mac = <$mac as NewMac>::new_from_slice(key).unwrap(); - // test reading byte by byte - for i in 0..input.len() { - mac.update(&input[i..i + 1]); - } - if let Err(_) = mac.verify(tag) { - return Some("message byte-by-byte"); - } - None - } - - let data = include_bytes!(concat!("data/", $test_name, ".blb")); - - for (i, row) in Blob3Iterator::new(data).unwrap().enumerate() { - let [key, input, tag] = row.unwrap(); - if let Some(desc) = run_test(key, input, tag) { - panic!( - "\n\ - Failed test №{}: {}\n\ - key:\t{:?}\n\ - input:\t{:?}\n\ - tag:\t{:?}\n", - i, desc, key, input, tag, - ); - } - } - } - }; -} - -/// Define test that allows for truncated tag. -#[macro_export] -#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] -macro_rules! new_trunc_test { - ($name:ident, $test_name:expr, $mac:ty) => { - #[test] - fn $name() { - use crypto_mac::dev::blobby::Blob3Iterator; - use crypto_mac::generic_array::typenum::Unsigned; - use crypto_mac::{Mac, NewMac}; - - fn run_test(key: &[u8], input: &[u8], tag: &[u8]) -> Option<&'static str> { - let mut mac = <$mac as NewMac>::new_from_slice(key).unwrap(); - mac.update(input); - let result = mac.finalize_reset(); - let mut len = <$mac as Mac>::OutputSize::to_usize(); - if tag.len() < len { - len = tag.len(); - } - if &result.into_bytes()[..len] != tag { - return Some("whole message"); - } - // test if reset worked correctly - mac.update(input); - let result = mac.finalize(); - if &result.into_bytes()[..len] != tag { - return Some("after reset"); - } - - let mut mac = <$mac as NewMac>::new_from_slice(key).unwrap(); - // test reading byte by byte - for i in 0..input.len() { - mac.update(&input[i..i + 1]); - } - let result = mac.finalize(); - if &result.into_bytes()[..len] != tag { - return Some("message byte-by-byte"); - } - None - } - - let data = include_bytes!(concat!("data/", $test_name, ".blb")); - - for (i, row) in Blob3Iterator::new(data).unwrap().enumerate() { - let [key, input, tag] = row.unwrap(); - if let Some(desc) = run_test(key, input, tag) { - panic!( - "\n\ - Failed test №{}: {}\n\ - key:\t{:?}\n\ - input:\t{:?}\n\ - tag:\t{:?}\n", - i, desc, key, input, tag, - ); - } - } - } - }; -} - -/// Define benchmark -#[macro_export] -#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] -macro_rules! bench { - ($name:ident, $engine:path, $bs:expr) => { - #[bench] - fn $name(b: &mut Bencher) { - let key = Default::default(); - let mut mac = <$engine>::new(&key); - let data = [0; $bs]; - - b.iter(|| { - mac.update(&data); - }); - - b.bytes = $bs; - } - }; - - ($engine:path) => { - extern crate test; - - use crypto_mac::{Mac, NewMac}; - use test::Bencher; - - $crate::bench!(bench1_10, $engine, 10); - $crate::bench!(bench2_100, $engine, 100); - $crate::bench!(bench3_1000, $engine, 1000); - $crate::bench!(bench3_10000, $engine, 10000); - }; -} diff --git a/crypto-mac/src/lib.rs b/crypto-mac/src/lib.rs deleted file mode 100644 index e5e2f2ba7..000000000 --- a/crypto-mac/src/lib.rs +++ /dev/null @@ -1,123 +0,0 @@ -//! This crate provides trait for Message Authentication Code (MAC) algorithms. - -#![no_std] -#![cfg_attr(docsrs, feature(doc_cfg))] -#![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/crypto-mac/0.12.0-pre" -)] -#![forbid(unsafe_code)] -#![warn(missing_docs, rust_2018_idioms)] - -#[cfg(feature = "std")] -extern crate std; - -#[cfg(feature = "rand_core")] -#[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] -pub use rand_core; - -#[cfg(feature = "cipher")] -pub use cipher; -#[cfg(feature = "cipher")] -use cipher::{BlockCipher, NewBlockCipher}; - -#[cfg(feature = "dev")] -#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] -pub mod dev; - -#[cfg(feature = "core-api")] -#[cfg_attr(docsrs, doc(cfg(feature = "core-api")))] -pub mod core_api; - -pub use cipher::{errors::InvalidLength, FromKey}; -pub use crypto_common::{FixedOutput, FixedOutputReset, Reset, Update}; -pub use generic_array::{self, typenum::consts}; - -use core::fmt; -use generic_array::GenericArray; -use subtle::{Choice, ConstantTimeEq}; - -/// Key for an algorithm that implements [`FromKey`]. -pub type Key = GenericArray::KeySize>; - -/// Convinience super-trait covering functionality of Message Authentication algorithms. -pub trait Mac: FromKey + Update + FixedOutput { - /// Obtain the result of a [`Mac`] computation as a [`Output`] and consume - /// [`Mac`] instance. - fn finalize(self) -> Output { - Output::new(self.finalize_fixed()) - } - - /// Obtain the result of a [`Mac`] computation as a [`Output`] and reset - /// [`Mac`] instance. - fn finalize_reset(&mut self) -> Output - where - Self: FixedOutputReset, - { - Output::new(self.finalize_fixed_reset()) - } - - /// Check if tag/code value is correct for the processed input. - fn verify(self, tag: &[u8]) -> Result<(), MacError> { - let choice = self.finalize().bytes.ct_eq(tag); - - if choice.unwrap_u8() == 1 { - Ok(()) - } else { - Err(MacError) - } - } -} - -impl Mac for T {} - -/// [`Output`] is a thin wrapper around bytes array which provides a safe `Eq` -/// implementation that runs in a fixed time. -#[derive(Clone)] -pub struct Output { - bytes: GenericArray, -} - -impl Output { - /// Create a new MAC [`Output`]. - pub fn new(bytes: GenericArray) -> Output { - Output { bytes } - } - - /// Get the MAC tag/code value as a byte array. - /// - /// Be very careful using this method, since incorrect use of the tag value - /// may permit timing attacks which defeat the security provided by the - /// [`Mac`] trait. - pub fn into_bytes(self) -> GenericArray { - self.bytes - } -} - -impl ConstantTimeEq for Output { - fn ct_eq(&self, other: &Self) -> Choice { - self.bytes.ct_eq(&other.bytes) - } -} - -impl PartialEq for Output { - fn eq(&self, x: &Output) -> bool { - self.ct_eq(x).unwrap_u8() == 1 - } -} - -impl Eq for Output {} - -/// Error type for signaling failed MAC verification -#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] -pub struct MacError; - -impl fmt::Display for MacError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("failed MAC verification") - } -} - -#[cfg(feature = "std")] -impl std::error::Error for MacError {} diff --git a/digest/Cargo.toml b/digest/Cargo.toml index 61852b9d9..1490a7300 100644 --- a/digest/Cargo.toml +++ b/digest/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "digest" description = "Traits for cryptographic hash functions" -version = "0.10.0-pre.3" +version = "0.10.0" authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" readme = "README.md" @@ -13,16 +13,17 @@ categories = ["cryptography", "no-std"] [dependencies] generic-array = "0.14" -crypto-common = { version = "=0.1.0-pre", path = "../crypto-common" } +block-buffer = "0.10" +crypto-common = { version = "0.1", path = "../crypto-common" } +subtle = { version = "=2.4", default-features = false, optional = true } blobby = { version = "0.3", optional = true } [features] +mac = ["subtle"] # Enable MAC traits alloc = [] std = ["alloc", "crypto-common/std"] dev = ["blobby"] -core-api = ["crypto-common/core-api"] -block-padding = ["crypto-common/block-padding"] [package.metadata.docs.rs] all-features = true diff --git a/digest/src/core_api.rs b/digest/src/core_api.rs index 63e573d33..10833fbb9 100644 --- a/digest/src/core_api.rs +++ b/digest/src/core_api.rs @@ -1,43 +1,79 @@ -//! Low-level core API traits. +//! Low-level traits operating on blocks and wrappers around them. //! //! Usage of traits in this module in user code is discouraged. Instead use //! core algorithm wrapped by the wrapper types, which implement the //! higher-level traits. use crate::InvalidOutputSize; -use crate::{ExtendableOutput, Reset}; -use generic_array::{ArrayLength, GenericArray}; +use generic_array::{ArrayLength, typenum::{U256, IsLess, Le, NonZero}}; -pub use crypto_common::core_api::{AlgorithmName, CoreWrapper, FixedOutputCore, UpdateCore}; +pub use crypto_common::{AlgorithmName, Block, BlockSizeUser, OutputSizeUser, Reset}; + +use block_buffer::{BlockBuffer, BufferKind}; +use crypto_common::Output; mod ct_variable; mod rt_variable; +mod wrapper; mod xof_reader; pub use ct_variable::CtVariableCoreWrapper; pub use rt_variable::RtVariableCoreWrapper; +pub use wrapper::{CoreProxy, CoreWrapper}; pub use xof_reader::XofReaderCoreWrapper; +/// Buffer type used by type which implements [`BufferKindUser`]. +pub type Buffer = + BlockBuffer<::BlockSize, ::BufferKind>; + +/// Types which consume data in blocks. +pub trait UpdateCore: BlockSizeUser { + /// Update state using the provided data blocks. + fn update_blocks(&mut self, blocks: &[Block]); +} + +/// Types which use [`BlockBuffer`] functionality. +pub trait BufferKindUser: BlockSizeUser { + /// Block buffer kind over which type operates. + type BufferKind: BufferKind; +} + +/// Core trait for hash functions with fixed output size. +pub trait FixedOutputCore: UpdateCore + BufferKindUser + OutputSizeUser +where + Self::BlockSize: IsLess, + Le: NonZero, +{ + /// Finalize state using remaining data stored in the provided block buffer, + /// write result into provided array and leave `self` in a dirty state. + fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output); +} + /// Core trait for hash functions with extendable (XOF) output size. -pub trait ExtendableOutputCore: UpdateCore { +pub trait ExtendableOutputCore: UpdateCore + BufferKindUser +where + Self::BlockSize: IsLess, + Le: NonZero, +{ /// XOF reader core state. type ReaderCore: XofReaderCore; /// Retrieve XOF reader using remaining data stored in the block buffer /// and leave hasher in a dirty state. - fn finalize_xof_core(&mut self, buffer: &mut Self::Buffer) -> Self::ReaderCore; + fn finalize_xof_core(&mut self, buffer: &mut Buffer) -> Self::ReaderCore; } /// Core reader trait for extendable-output function (XOF) result. -pub trait XofReaderCore { - /// Block size in bytes. - type BlockSize: ArrayLength; - +pub trait XofReaderCore: BlockSizeUser { /// Read next XOF block. - fn read_block(&mut self) -> GenericArray; + fn read_block(&mut self) -> Block; } /// Core trait for hash functions with variable output size. -pub trait VariableOutputCore: UpdateCore + Sized { +pub trait VariableOutputCore: UpdateCore + BufferKindUser + Sized +where + Self::BlockSize: IsLess, + Le: NonZero, +{ /// Maximum output size. type MaxOutputSize: ArrayLength; @@ -52,29 +88,8 @@ pub trait VariableOutputCore: UpdateCore + Sized { /// `output_size` must be equal to `output_size` used during construction. fn finalize_variable_core( &mut self, - buffer: &mut Self::Buffer, + buffer: &mut Buffer, output_size: usize, f: impl FnOnce(&[u8]), ); } - -impl ExtendableOutput for CoreWrapper { - type Reader = XofReaderCoreWrapper; - - #[inline] - fn finalize_xof(self) -> Self::Reader { - let (mut core, mut buffer) = self.decompose(); - let core = core.finalize_xof_core(&mut buffer); - let buffer = Default::default(); - Self::Reader { core, buffer } - } - - #[inline] - fn finalize_xof_reset(&mut self) -> Self::Reader { - self.apply_reset(|core, buffer| { - let core = core.finalize_xof_core(buffer); - let buffer = Default::default(); - Self::Reader { core, buffer } - }) - } -} diff --git a/digest/src/core_api/ct_variable.rs b/digest/src/core_api/ct_variable.rs index 11474326f..2dc4c076e 100644 --- a/digest/src/core_api/ct_variable.rs +++ b/digest/src/core_api/ct_variable.rs @@ -1,7 +1,13 @@ -use super::{AlgorithmName, FixedOutputCore, Reset, UpdateCore, VariableOutputCore}; +use super::{ + AlgorithmName, Buffer, BufferKindUser, FixedOutputCore, Reset, UpdateCore, VariableOutputCore, +}; +use crate::HashMarker; +#[cfg(feature = "mac")] +use crate::MacMarker; use core::{fmt, marker::PhantomData}; +use crypto_common::{Block, BlockSizeUser, OutputSizeUser}; use generic_array::{ - typenum::{IsLessOrEqual, LeEq, NonZero}, + typenum::{IsLessOrEqual, IsLess, Le, LeEq, NonZero, U256}, ArrayLength, GenericArray, }; @@ -13,38 +19,93 @@ where T: VariableOutputCore, OutSize: ArrayLength + IsLessOrEqual, LeEq: NonZero, + T::BlockSize: IsLess, + Le: NonZero, { inner: T, _out: PhantomData, } -impl UpdateCore for CtVariableCoreWrapper +impl HashMarker for CtVariableCoreWrapper +where + T: VariableOutputCore + HashMarker, + OutSize: ArrayLength + IsLessOrEqual, + LeEq: NonZero, + T::BlockSize: IsLess, + Le: NonZero, +{ +} + +#[cfg(feature = "mac")] +impl MacMarker for CtVariableCoreWrapper +where + T: VariableOutputCore + MacMarker, + OutSize: ArrayLength + IsLessOrEqual, + LeEq: NonZero, + T::BlockSize: IsLess, + Le: NonZero, +{ +} + +impl BlockSizeUser for CtVariableCoreWrapper where T: VariableOutputCore, OutSize: ArrayLength + IsLessOrEqual, LeEq: NonZero, + T::BlockSize: IsLess, + Le: NonZero, { type BlockSize = T::BlockSize; - type Buffer = T::Buffer; +} +impl UpdateCore for CtVariableCoreWrapper +where + T: VariableOutputCore, + OutSize: ArrayLength + IsLessOrEqual, + LeEq: NonZero, + T::BlockSize: IsLess, + Le: NonZero, +{ #[inline] - fn update_blocks(&mut self, blocks: &[GenericArray]) { + fn update_blocks(&mut self, blocks: &[Block]) { self.inner.update_blocks(blocks); } } -impl FixedOutputCore for CtVariableCoreWrapper +impl OutputSizeUser for CtVariableCoreWrapper where T: VariableOutputCore, - OutSize: ArrayLength + IsLessOrEqual, + OutSize: ArrayLength + IsLessOrEqual + 'static, LeEq: NonZero, + T::BlockSize: IsLess, + Le: NonZero, { type OutputSize = OutSize; +} + +impl BufferKindUser for CtVariableCoreWrapper +where + T: VariableOutputCore, + OutSize: ArrayLength + IsLessOrEqual, + LeEq: NonZero, + T::BlockSize: IsLess, + Le: NonZero, +{ + type BufferKind = T::BufferKind; +} +impl FixedOutputCore for CtVariableCoreWrapper +where + T: VariableOutputCore, + OutSize: ArrayLength + IsLessOrEqual + 'static, + LeEq: NonZero, + T::BlockSize: IsLess, + Le: NonZero, +{ #[inline] fn finalize_fixed_core( &mut self, - buffer: &mut Self::Buffer, + buffer: &mut Buffer, out: &mut GenericArray, ) { self.inner @@ -57,12 +118,14 @@ where T: VariableOutputCore, OutSize: ArrayLength + IsLessOrEqual, LeEq: NonZero, + T::BlockSize: IsLess, + Le: NonZero, { #[inline] fn default() -> Self { Self { inner: T::new(OutSize::USIZE).unwrap(), - _out: Default::default(), + _out: PhantomData, } } } @@ -72,6 +135,8 @@ where T: VariableOutputCore, OutSize: ArrayLength + IsLessOrEqual, LeEq: NonZero, + T::BlockSize: IsLess, + Le: NonZero, { #[inline] fn reset(&mut self) { @@ -84,6 +149,8 @@ where T: VariableOutputCore + AlgorithmName, OutSize: ArrayLength + IsLessOrEqual, LeEq: NonZero, + T::BlockSize: IsLess, + Le: NonZero, { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { T::write_alg_name(f)?; diff --git a/digest/src/core_api/rt_variable.rs b/digest/src/core_api/rt_variable.rs index c9af06ae3..9a63f4d25 100644 --- a/digest/src/core_api/rt_variable.rs +++ b/digest/src/core_api/rt_variable.rs @@ -1,8 +1,11 @@ use super::{AlgorithmName, UpdateCore, VariableOutputCore}; +use crate::HashMarker; +#[cfg(feature = "mac")] +use crate::MacMarker; use crate::{InvalidOutputSize, Reset, Update, VariableOutput}; +use block_buffer::BlockBuffer; use core::fmt; -use crypto_common::block_buffer::DigestBuffer; -use generic_array::typenum::Unsigned; +use generic_array::typenum::{Unsigned, Le, IsLess, U256, NonZero}; /// Wrapper around [`VariableOutputCore`] which selects output size /// at run time. @@ -10,15 +13,35 @@ use generic_array::typenum::Unsigned; pub struct RtVariableCoreWrapper where T: VariableOutputCore + UpdateCore, + T::BlockSize: IsLess, + Le: NonZero, { core: T, - buffer: T::Buffer, + buffer: BlockBuffer, output_size: usize, } +impl HashMarker for RtVariableCoreWrapper +where + T: VariableOutputCore + HashMarker, + T::BlockSize: IsLess, + Le: NonZero, +{} + +#[cfg(feature = "mac")] +#[cfg_attr(docsrs, doc(cfg(feature = "mac")))] +impl MacMarker for RtVariableCoreWrapper +where + T: VariableOutputCore + MacMarker, + T::BlockSize: IsLess, + Le: NonZero, +{} + impl Reset for RtVariableCoreWrapper where T: VariableOutputCore + UpdateCore, + T::BlockSize: IsLess, + Le: NonZero, { #[inline] fn reset(&mut self) { @@ -36,6 +59,8 @@ where impl Update for RtVariableCoreWrapper where T: VariableOutputCore + UpdateCore, + T::BlockSize: IsLess, + Le: NonZero, { #[inline] fn update(&mut self, input: &[u8]) { @@ -47,6 +72,8 @@ where impl VariableOutput for RtVariableCoreWrapper where T: VariableOutputCore + UpdateCore, + T::BlockSize: IsLess, + Le: NonZero, { const MAX_OUTPUT_SIZE: usize = T::MaxOutputSize::USIZE; @@ -86,6 +113,8 @@ where impl fmt::Debug for RtVariableCoreWrapper where T: VariableOutputCore + UpdateCore + AlgorithmName, + T::BlockSize: IsLess, + Le: NonZero, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { T::write_alg_name(f)?; @@ -98,6 +127,8 @@ where impl std::io::Write for RtVariableCoreWrapper where T: VariableOutputCore + UpdateCore, + T::BlockSize: IsLess, + Le: NonZero, { #[inline] fn write(&mut self, buf: &[u8]) -> std::io::Result { diff --git a/digest/src/core_api/wrapper.rs b/digest/src/core_api/wrapper.rs new file mode 100644 index 000000000..b354cad9b --- /dev/null +++ b/digest/src/core_api/wrapper.rs @@ -0,0 +1,238 @@ +use super::{ + AlgorithmName, Buffer, BufferKindUser, ExtendableOutputCore, FixedOutputCore, OutputSizeUser, + Reset, UpdateCore, XofReaderCoreWrapper, +}; +use crate::{ExtendableOutput, FixedOutput, FixedOutputReset, HashMarker, Update}; +use block_buffer::BlockBuffer; +use core::fmt; +use crypto_common::{InvalidLength, Key, KeyInit, KeySizeUser, Output, BlockSizeUser}; +use generic_array::typenum::{NonZero, IsLess, Le, U256}; + +#[cfg(feature = "mac")] +use crate::MacMarker; + +/// Wrapper around [`BufferKindUser`]. +/// +/// It handles data buffering and implements the slice-based traits. +#[derive(Clone, Default)] +pub struct CoreWrapper +where + T: BufferKindUser, + T::BlockSize: IsLess, + Le: NonZero, +{ + core: T, + buffer: BlockBuffer, +} + +impl HashMarker for CoreWrapper +where + T: BufferKindUser + HashMarker, + T::BlockSize: IsLess, + Le: NonZero, +{} + +#[cfg(feature = "mac")] +#[cfg_attr(docsrs, doc(cfg(feature = "mac")))] +impl MacMarker for CoreWrapper +where + T: BufferKindUser + MacMarker, + T::BlockSize: IsLess, + Le: NonZero, +{} + +impl CoreWrapper +where + T: BufferKindUser, + T::BlockSize: IsLess, + Le: NonZero, +{ + /// Create new wrapper from `core`. + #[inline] + pub fn from_core(core: T) -> Self { + let buffer = Default::default(); + Self { core, buffer } + } + + /// Decompose wrapper into inner parts. + #[inline] + pub fn decompose(self) -> (T, Buffer) { + let Self { core, buffer } = self; + (core, buffer) + } +} + +impl KeySizeUser for CoreWrapper +where + T: BufferKindUser + KeySizeUser, + T::BlockSize: IsLess, + Le: NonZero, +{ + type KeySize = T::KeySize; +} + +impl KeyInit for CoreWrapper +where + T: BufferKindUser + KeyInit, + T::BlockSize: IsLess, + Le: NonZero, +{ + fn new(key: &Key) -> Self { + Self { + core: T::new(key), + buffer: Default::default(), + } + } + + fn new_from_slice(key: &[u8]) -> Result { + Ok(Self { + core: T::new_from_slice(key)?, + buffer: Default::default(), + }) + } +} + +impl fmt::Debug for CoreWrapper +where + T: BufferKindUser + AlgorithmName, + T::BlockSize: IsLess, + Le: NonZero, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + T::write_alg_name(f)?; + f.write_str(" { .. }") + } +} + +impl Reset for CoreWrapper +where + T: BufferKindUser + Reset, + T::BlockSize: IsLess, + Le: NonZero, +{ + #[inline] + fn reset(&mut self) { + self.core.reset(); + self.buffer.reset(); + } +} + +impl Update for CoreWrapper +where + T: BufferKindUser + UpdateCore, + T::BlockSize: IsLess, + Le: NonZero, +{ + #[inline] + fn update(&mut self, input: &[u8]) { + let Self { core, buffer } = self; + buffer.digest_blocks(input, |blocks| core.update_blocks(blocks)); + } +} + +impl OutputSizeUser for CoreWrapper +where + T: BufferKindUser + OutputSizeUser, + T::BlockSize: IsLess, + Le: NonZero, +{ + type OutputSize = T::OutputSize; +} + +impl FixedOutput for CoreWrapper +where + T: FixedOutputCore, + T::BlockSize: IsLess, + Le: NonZero, +{ + #[inline] + fn finalize_into(mut self, out: &mut Output) { + let Self { core, buffer } = &mut self; + core.finalize_fixed_core(buffer, out); + } +} + +impl FixedOutputReset for CoreWrapper +where + T: FixedOutputCore + Reset, + T::BlockSize: IsLess, + Le: NonZero, +{ + #[inline] + fn finalize_into_reset(&mut self, out: &mut Output) { + let Self { core, buffer } = self; + core.finalize_fixed_core(buffer, out); + core.reset(); + buffer.reset(); + } +} + +impl ExtendableOutput for CoreWrapper +where + T: ExtendableOutputCore + Reset, + T::BlockSize: IsLess, + Le: NonZero, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + type Reader = XofReaderCoreWrapper; + + #[inline] + fn finalize_xof(self) -> Self::Reader { + let (mut core, mut buffer) = self.decompose(); + let core = core.finalize_xof_core(&mut buffer); + let buffer = Default::default(); + Self::Reader { core, buffer } + } + + #[inline] + fn finalize_xof_reset(&mut self) -> Self::Reader { + let Self { core, buffer } = self; + let core = core.finalize_xof_core(buffer); + let buffer = Default::default(); + Self::Reader { core, buffer } + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl std::io::Write for CoreWrapper { + #[inline] + fn write(&mut self, buf: &[u8]) -> std::io::Result { + Update::update(self, buf); + Ok(buf.len()) + } + + #[inline] + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + +/// A proxy trait to a core type implemented by [`CoreWrapper`] +// TODO: replace with an inherent associated type on stabilization: +// https://github.com/rust-lang/rust/issues/8995 +pub trait CoreProxy: sealed::Sealed { + /// Type wrapped by [`CoreWrapper`]. + type Core; +} + +mod sealed { + pub trait Sealed {} +} + +impl sealed::Sealed for CoreWrapper +where + T: BufferKindUser, + T::BlockSize: IsLess, + Le: NonZero, +{} + +impl CoreProxy for CoreWrapper +where + T: BufferKindUser, + T::BlockSize: IsLess, + Le: NonZero, +{ + type Core = T; +} diff --git a/digest/src/core_api/xof_reader.rs b/digest/src/core_api/xof_reader.rs index 5d7b83520..03d0e078e 100644 --- a/digest/src/core_api/xof_reader.rs +++ b/digest/src/core_api/xof_reader.rs @@ -1,35 +1,60 @@ use super::{AlgorithmName, XofReaderCore}; use crate::XofReader; +use block_buffer::EagerBuffer; use core::fmt; -use crypto_common::block_buffer::BlockBuffer; +use generic_array::typenum::{IsLess, Le, U256, NonZero}; /// Wrapper around [`XofReaderCore`] implementations. /// /// It handles data buffering and implements the mid-level traits. #[derive(Clone, Default)] -pub struct XofReaderCoreWrapper { +pub struct XofReaderCoreWrapper +where + T: XofReaderCore, + T::BlockSize: IsLess, + Le: NonZero, +{ pub(super) core: T, - pub(super) buffer: BlockBuffer, + pub(super) buffer: EagerBuffer, } -impl fmt::Debug for XofReaderCoreWrapper { +impl fmt::Debug for XofReaderCoreWrapper +where + T: XofReaderCore + AlgorithmName, + T::BlockSize: IsLess, + Le: NonZero, +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { T::write_alg_name(f)?; f.write_str(" { .. }") } } -impl XofReader for XofReaderCoreWrapper { +impl XofReader for XofReaderCoreWrapper +where + T: XofReaderCore, + T::BlockSize: IsLess, + Le: NonZero, +{ #[inline] fn read(&mut self, buffer: &mut [u8]) { let Self { core, buffer: buf } = self; - buf.set_data(buffer, || core.read_block()); + buf.set_data(buffer, |blocks| { + for block in blocks { + *block = core.read_block(); + } + }); } } #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl std::io::Read for XofReaderCoreWrapper { +impl std::io::Read for XofReaderCoreWrapper +where + T: XofReaderCore, + T::BlockSize: IsLess, + Le: NonZero, +{ #[inline] fn read(&mut self, buf: &mut [u8]) -> std::io::Result { XofReader::read(self, buf); diff --git a/digest/src/dev.rs b/digest/src/dev.rs index 926ebbd53..c1200d748 100644 --- a/digest/src/dev.rs +++ b/digest/src/dev.rs @@ -9,7 +9,7 @@ use core::fmt::Debug; #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "dev")))] macro_rules! new_test { - ($name:ident, $test_name:expr, $hasher:ty, $test_func:ident) => { + ($name:ident, $test_name:expr, $hasher:ty, $test_func:ident $(,)?) => { #[test] fn $name() { use digest::dev::blobby::Blob2Iterator; @@ -244,3 +244,163 @@ macro_rules! bench { $crate::bench!(bench4_10000, $engine, 10000); }; } + +/// Define MAC test +#[macro_export] +#[cfg(feature = "mac")] +#[cfg_attr(docsrs, doc(cfg(all(feature = "dev", feature = "mac"))))] +macro_rules! new_mac_test { + ($name:ident, $test_name:expr, $mac:ty $(,)?) => { + digest::new_mac_test!($name, $test_name, $mac, ""); + }; + ($name:ident, $test_name:expr, $mac:ty, trunc_left $(,)?) => { + digest::new_mac_test!($name, $test_name, $mac, "left"); + }; + ($name:ident, $test_name:expr, $mac:ty, trunc_right $(,)?) => { + digest::new_mac_test!($name, $test_name, $mac, "right"); + }; + ($name:ident, $test_name:expr, $mac:ty, $trunc:expr $(,)?) => { + #[test] + fn $name() { + use core::cmp::min; + use digest::dev::blobby::Blob3Iterator; + use digest::Mac; + + fn run_test(key: &[u8], input: &[u8], tag: &[u8]) -> Option<&'static str> { + let mac0 = <$mac as Mac>::new_from_slice(key).unwrap(); + + let mut mac = mac0.clone(); + mac.update(input); + let result = mac.finalize().into_bytes(); + let n = tag.len(); + let result_bytes = match $trunc { + "left" => &result[..n], + "right" => &result[result.len() - n..], + _ => &result[..], + }; + if result_bytes != tag { + return Some("whole message"); + } + + // test reading different chunk sizes + for chunk_size in 1..min(64, input.len()) { + let mut mac = mac0.clone(); + for chunk in input.chunks(chunk_size) { + mac.update(chunk); + } + let res = match $trunc { + "left" => mac.verify_truncated_left(tag), + "right" => mac.verify_truncated_right(tag), + _ => mac.verify_slice(tag), + }; + if res.is_err() { + return Some("chunked message"); + } + } + + None + } + + let data = include_bytes!(concat!("data/", $test_name, ".blb")); + + for (i, row) in Blob3Iterator::new(data).unwrap().enumerate() { + let [key, input, tag] = row.unwrap(); + if let Some(desc) = run_test(key, input, tag) { + panic!( + "\n\ + Failed test №{}: {}\n\ + key:\t{:?}\n\ + input:\t{:?}\n\ + tag:\t{:?}\n", + i, desc, key, input, tag, + ); + } + } + } + }; +} + +/// Define new test for a resettable MAC +#[macro_export] +#[cfg(feature = "mac")] +#[cfg_attr(docsrs, doc(cfg(all(feature = "dev", feature = "mac"))))] +macro_rules! new_resettable_mac_test { + ($name:ident, $test_name:expr, $mac:ty $(,)?) => { + digest::new_resettable_mac_test!($name, $test_name, $mac, ""); + }; + ($name:ident, $test_name:expr, $mac:ty, trunc_left $(,)?) => { + digest::new_resettable_mac_test!($name, $test_name, $mac, "left"); + }; + ($name:ident, $test_name:expr, $mac:ty, trunc_right $(,)?) => { + digest::new_resettable_mac_test!($name, $test_name, $mac, "right"); + }; + ($name:ident, $test_name:expr, $mac:ty, $trunc:expr $(,)?) => { + #[test] + fn $name() { + use core::cmp::min; + use digest::dev::blobby::Blob3Iterator; + use digest::Mac; + + fn run_test(key: &[u8], input: &[u8], tag: &[u8]) -> Option<&'static str> { + let mac0 = <$mac as Mac>::new_from_slice(key).unwrap(); + + let mut mac = mac0.clone(); + mac.update(input); + let result = mac.finalize_reset().into_bytes(); + let n = tag.len(); + let result_bytes = match $trunc { + "left" => &result[..n], + "right" => &result[result.len() - n..], + _ => &result[..], + }; + if result_bytes != tag { + return Some("whole message"); + } + + // test if reset worked correctly + mac.update(input); + let res = match $trunc { + "left" => mac.verify_truncated_left(tag), + "right" => mac.verify_truncated_right(tag), + _ => mac.verify_slice(tag), + }; + if res.is_err() { + return Some("after reset"); + } + + // test reading different chunk sizes + for chunk_size in 1..min(64, input.len()) { + let mut mac = mac0.clone(); + for chunk in input.chunks(chunk_size) { + mac.update(chunk); + } + let res = match $trunc { + "left" => mac.verify_truncated_left(tag), + "right" => mac.verify_truncated_right(tag), + _ => mac.verify_slice(tag), + }; + if res.is_err() { + return Some("chunked message"); + } + } + None + } + + let data = include_bytes!(concat!("data/", $test_name, ".blb")); + + for (i, row) in Blob3Iterator::new(data).unwrap().enumerate() { + let [key, input, tag] = row.unwrap(); + if let Some(desc) = run_test(key, input, tag) { + panic!( + "\n\ + Failed test №{}: {}\n\ + key:\t{:?}\n\ + input:\t{:?}\n\ + tag:\t{:?}\n", + i, desc, key, input, tag, + ); + } + } + } + }; +} diff --git a/digest/src/digest.rs b/digest/src/digest.rs index 7c277c9a6..251d4aa85 100644 --- a/digest/src/digest.rs +++ b/digest/src/digest.rs @@ -1,17 +1,20 @@ -use super::{FixedOutput, FixedOutputReset, Update}; +use super::{FixedOutput, FixedOutputReset, Reset, Update}; +use core::fmt; +use crypto_common::{Output, OutputSizeUser}; use generic_array::typenum::Unsigned; -use generic_array::{ArrayLength, GenericArray}; -/// The `Digest` trait specifies an interface common for digest functions. -/// -/// It's a convenience wrapper around [`Update`], [`FixedOutput`], -/// [`Reset`][`crate::Reset`], [`Clone`], and [`Default`] traits. -/// -/// It also provides additional convenience methods. -pub trait Digest { - /// Output size for `Digest` - type OutputSize: ArrayLength; +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + +/// Marker trait for cryptographic hash functions. +pub trait HashMarker {} +/// Convinience wrapper trait covering functionality of cryptographic hash +/// functions with fixed output size. +/// +/// This trait wraps [`Update`], [`FixedOutput`], [`Default`], and +/// [`HashMarker`] traits and provides additional convenience methods. +pub trait Digest: OutputSizeUser { /// Create new hasher instance fn new() -> Self; @@ -44,9 +47,7 @@ pub trait Digest { fn digest(data: impl AsRef<[u8]>) -> Output; } -impl Digest for D { - type OutputSize = ::OutputSize; - +impl Digest for D { #[inline] fn new() -> Self { Self::default() @@ -102,5 +103,123 @@ impl Digest for D { } } -/// Fixed of fixed-sized hash-function used by [`Digest`] methods. -pub type Output = GenericArray::OutputSize>; +/// Modification of the [`Digest`] trait suitable for trait objects. +pub trait DynDigest { + /// Digest input data. + /// + /// This method can be called repeatedly for use with streaming messages. + fn update(&mut self, data: &[u8]); + + /// Retrieve result and reset hasher instance + #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + fn finalize_reset(&mut self) -> Box<[u8]> { + let mut result = vec![0; self.output_size()]; + self.finalize_into_reset(&mut result).unwrap(); + result.into_boxed_slice() + } + + /// Retrieve result and consume boxed hasher instance + #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + #[allow(clippy::boxed_local)] + fn finalize(mut self: Box) -> Box<[u8]> { + let mut result = vec![0; self.output_size()]; + self.finalize_into_reset(&mut result).unwrap(); + result.into_boxed_slice() + } + + /// Write result into provided array and consume the hasher instance. + /// + /// Returns error if buffer length is not equal to `output_size`. + fn finalize_into(self, buf: &mut [u8]) -> Result<(), InvalidBufferLength>; + + /// Write result into provided array and reset the hasher instance. + /// + /// Returns error if buffer length is not equal to `output_size`. + fn finalize_into_reset(&mut self, out: &mut [u8]) -> Result<(), InvalidBufferLength>; + + /// Reset hasher instance to its initial state. + fn reset(&mut self); + + /// Get output size of the hasher + fn output_size(&self) -> usize; + + /// Clone hasher state into a boxed trait object + #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + fn box_clone(&self) -> Box; +} + +impl DynDigest for D { + fn update(&mut self, data: &[u8]) { + Update::update(self, data); + } + + #[cfg(feature = "alloc")] + fn finalize_reset(&mut self) -> Box<[u8]> { + FixedOutputReset::finalize_fixed_reset(self) + .to_vec() + .into_boxed_slice() + } + + #[cfg(feature = "alloc")] + fn finalize(self: Box) -> Box<[u8]> { + FixedOutput::finalize_fixed(*self) + .to_vec() + .into_boxed_slice() + } + + fn finalize_into(self, buf: &mut [u8]) -> Result<(), InvalidBufferLength> { + if buf.len() == self.output_size() { + FixedOutput::finalize_into(self, Output::::from_mut_slice(buf)); + Ok(()) + } else { + Err(InvalidBufferLength) + } + } + + fn finalize_into_reset(&mut self, buf: &mut [u8]) -> Result<(), InvalidBufferLength> { + if buf.len() == self.output_size() { + FixedOutputReset::finalize_into_reset(self, Output::::from_mut_slice(buf)); + Ok(()) + } else { + Err(InvalidBufferLength) + } + } + + fn reset(&mut self) { + Reset::reset(self); + } + + fn output_size(&self) -> usize { + ::OutputSize::to_usize() + } + + #[cfg(feature = "alloc")] + fn box_clone(&self) -> Box { + Box::new(self.clone()) + } +} + +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +impl Clone for Box { + fn clone(&self) -> Self { + self.box_clone() + } +} + +/// Buffer length is not equal to the hash output size. +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] +pub struct InvalidBufferLength; + +impl fmt::Display for InvalidBufferLength { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("invalid buffer length") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for InvalidBufferLength {} diff --git a/digest/src/dyn_digest.rs b/digest/src/dyn_digest.rs index ecafe7945..0e1d3fab3 100644 --- a/digest/src/dyn_digest.rs +++ b/digest/src/dyn_digest.rs @@ -1,5 +1,6 @@ -use super::{FixedOutput, FixedOutputReset, Reset, Update}; +use super::{FixedOutputReset, Reset, Update}; use core::fmt; +use crypto_common::OutputSizeUser; use generic_array::{typenum::Unsigned, GenericArray}; #[cfg(feature = "alloc")] @@ -92,7 +93,7 @@ impl DynDigest for D { } fn output_size(&self) -> usize { - ::OutputSize::to_usize() + ::OutputSize::to_usize() } #[cfg(feature = "alloc")] diff --git a/digest/src/lib.rs b/digest/src/lib.rs index 2991ded68..aebad936d 100644 --- a/digest/src/lib.rs +++ b/digest/src/lib.rs @@ -1,15 +1,15 @@ //! This crate provides traits which describe functionality of cryptographic hash -//! functions. +//! functions and Message Authentication algorithms. //! -//! Traits in this repository are organized into high-level convenience traits, -//! mid-level traits which expose more fine-grained functionality, and -//! low-level traits intended to only be used by algorithm implementations: +//! Traits in this repository are organized into the following levels: //! -//! - **High-level convenience traits**: [`Digest`], [`DynDigest`]. They are wrappers -//! around lower-level traits for most common hash-function use-cases. -//! - **Mid-level traits**: [`Update`], [`FixedOutput`], [`ExtendableOutput`], [`Reset`]. -//! These traits atomically describe available functionality of hash function -//! implementations. +//! - **High-level convenience traits**: [`Digest`], [`DynDigest`], [`Mac`]. +//! Wrappers around lower-level traits for most common use-cases. +//! - **Mid-level traits**: [`Update`], [`FixedOutput`], [`ExtendableOutput`], +//! [`VariableOutput`], [`Reset`], [`XofReader`]. These traits atomically +//! describe available functionality of an algorithm. +//! - **Marker traits**: [`HashMarker`], [`MacMarker`]. Used to distinguish +//! different algorithm classes. //! - **Low-level traits** defined in the [`core_api`] module. These traits //! operate at a block-level and do not contain any built-in buffering. //! They are intended to be implemented by low-level algorithm providers only @@ -17,11 +17,11 @@ //! usually shouldn't be used in application-level code. //! //! Additionally hash functions implement traits from the standard library: -//! [`Default`], [`Clone`], [`Write`][std::io::Write]. The latter is +//! [`Default`], [`Clone`], [`Write`]. The latter is //! feature-gated behind `std` feature, which is usually enabled by default //! by hash implementation crates. //! -//! The [`Digest`] trait is the most commonly used trait. +//! [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html #![no_std] #![cfg_attr(docsrs, feature(doc_cfg))] @@ -46,21 +46,58 @@ use alloc::boxed::Box; #[cfg_attr(docsrs, doc(cfg(feature = "dev")))] pub mod dev; -#[cfg(feature = "core-api")] -#[cfg_attr(docsrs, doc(cfg(feature = "core-api")))] pub mod core_api; mod digest; -mod dyn_digest; +#[cfg(feature = "mac")] +mod mac; + +pub use block_buffer; +pub use crypto_common; + +pub use crate::digest::{Digest, DynDigest, HashMarker, InvalidBufferLength}; +#[cfg(feature = "mac")] +pub use crypto_common::{InnerInit, InvalidLength, Key, KeyInit}; +pub use crypto_common::{Output, OutputSizeUser, Reset}; +pub use generic_array::{self, typenum::consts}; +#[cfg(feature = "mac")] +pub use mac::{CtOutput, Mac, MacError, MacMarker}; -pub use crate::digest::{Digest, Output}; use core::fmt; -#[cfg(feature = "core-api")] -#[cfg_attr(docsrs, doc(cfg(feature = "core-api")))] -pub use crypto_common::block_buffer; -pub use dyn_digest::{DynDigest, InvalidBufferLength}; -pub use generic_array::{self, typenum::consts, GenericArray}; -pub use crypto_common::{FixedOutput, FixedOutputReset, Reset, Update}; +/// Types which consume data with byte granularity. +pub trait Update { + /// Update state using the provided data. + fn update(&mut self, data: &[u8]); +} + +/// Types which return fixed-sized result after finalization. +pub trait FixedOutput: OutputSizeUser + Sized { + /// Consume value and write result into provided array. + fn finalize_into(self, out: &mut Output); + + /// Retrieve result and consume the hasher instance. + #[inline] + fn finalize_fixed(self) -> Output { + let mut out = Default::default(); + self.finalize_into(&mut out); + out + } +} + +/// Types which return fixed-sized result after finalization and reset +/// values into its initial state. +pub trait FixedOutputReset: FixedOutput + Reset { + /// Write result into provided array and reset value to its initial state. + fn finalize_into_reset(&mut self, out: &mut Output); + + /// Retrieve result and reset the hasher instance. + #[inline] + fn finalize_fixed_reset(&mut self) -> Output { + let mut out = Default::default(); + self.finalize_into_reset(&mut out); + out + } +} /// Trait for describing readers which are used to extract extendable output /// from XOF (extendable-output function) result. @@ -84,7 +121,7 @@ pub trait XofReader { } /// Trait which describes extendable-output functions (XOF). -pub trait ExtendableOutput: Sized + Update + Default + Reset { +pub trait ExtendableOutput: Sized + Update + Reset { /// Reader type Reader: XofReader; @@ -95,7 +132,10 @@ pub trait ExtendableOutput: Sized + Update + Default + Reset { fn finalize_xof_reset(&mut self) -> Self::Reader; /// Compute hash of `data` and write it to `output`. - fn digest_xof(input: impl AsRef<[u8]>, output: &mut [u8]) { + fn digest_xof(input: impl AsRef<[u8]>, output: &mut [u8]) + where + Self: Default, + { let mut hasher = Self::default(); hasher.update(input.as_ref()); hasher.finalize_xof().read(output); diff --git a/digest/src/mac.rs b/digest/src/mac.rs new file mode 100644 index 000000000..df1d0c2b1 --- /dev/null +++ b/digest/src/mac.rs @@ -0,0 +1,208 @@ +use crate::{FixedOutput, FixedOutputReset, Update}; +use crypto_common::{InvalidLength, Key, KeyInit, KeySizeUser, Output, OutputSizeUser}; + +use core::fmt; +use generic_array::typenum::Unsigned; +use subtle::{Choice, ConstantTimeEq}; + +/// Marker trait for Message Authentication algorithms. +#[cfg_attr(docsrs, doc(cfg(feature = "mac")))] +pub trait MacMarker {} + +/// Convinience wrapper trait covering functionality of Message Authentication algorithms. +/// +/// This trait wraps [`KeyInit`], [`Update`], [`FixedOutput`], and [`MacMarker`] +/// traits and provides additional convenience methods. +#[cfg_attr(docsrs, doc(cfg(feature = "mac")))] +pub trait Mac: KeySizeUser + OutputSizeUser + Sized { + /// Create new value from fixed size key. + fn new(key: &Key) -> Self; + + /// Create new value from variable size key. + fn new_from_slice(key: &[u8]) -> Result; + + /// Update state using the provided data. + fn update(&mut self, data: &[u8]); + + /// Obtain the result of a [`Mac`] computation as a [`CtOutput`] and consume + /// [`Mac`] instance. + fn finalize(self) -> CtOutput; + + /// Obtain the result of a [`Mac`] computation as a [`CtOutput`] and reset + /// [`Mac`] instance. + fn finalize_reset(&mut self) -> CtOutput + where + Self: FixedOutputReset; + + /// Check if tag/code value is correct for the processed input. + fn verify(self, tag: &Output) -> Result<(), MacError>; + + /// Check truncated tag correctness using all bytes + /// of calculated tag. + /// + /// Returns `Error` if `tag` is not valid or not equal in length + /// to MAC's output. + fn verify_slice(self, tag: &[u8]) -> Result<(), MacError>; + + /// Check truncated tag correctness using left side bytes + /// (i.e. `tag[..n]`) of calculated tag. + /// + /// Returns `Error` if `tag` is not valid or empty. + fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError>; + + /// Check truncated tag correctness using right side bytes + /// (i.e. `tag[n..]`) of calculated tag. + /// + /// Returns `Error` if `tag` is not valid or empty. + fn verify_truncated_right(self, tag: &[u8]) -> Result<(), MacError>; +} + +impl Mac for T { + #[inline(always)] + fn new(key: &Key) -> Self { + KeyInit::new(key) + } + + #[inline(always)] + fn new_from_slice(key: &[u8]) -> Result { + KeyInit::new_from_slice(key) + } + + #[inline] + fn update(&mut self, data: &[u8]) { + Update::update(self, data); + } + + #[inline] + fn finalize(self) -> CtOutput { + CtOutput::new(self.finalize_fixed()) + } + + #[inline(always)] + fn finalize_reset(&mut self) -> CtOutput + where + Self: FixedOutputReset, + { + CtOutput::new(self.finalize_fixed_reset()) + } + + #[inline] + fn verify(self, tag: &Output) -> Result<(), MacError> { + if self.finalize() == tag.into() { + Ok(()) + } else { + Err(MacError) + } + } + + #[inline] + fn verify_slice(self, tag: &[u8]) -> Result<(), MacError> { + let n = tag.len(); + if n != Self::OutputSize::USIZE { + return Err(MacError); + } + let choice = self.finalize_fixed().ct_eq(&tag); + if choice.unwrap_u8() == 1 { + Ok(()) + } else { + Err(MacError) + } + } + + fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError> { + let n = tag.len(); + if n == 0 || n > Self::OutputSize::USIZE { + return Err(MacError); + } + let choice = self.finalize_fixed()[..n].ct_eq(tag); + + if choice.unwrap_u8() == 1 { + Ok(()) + } else { + Err(MacError) + } + } + + fn verify_truncated_right(self, tag: &[u8]) -> Result<(), MacError> { + let n = tag.len(); + if n == 0 || n > Self::OutputSize::USIZE { + return Err(MacError); + } + let m = Self::OutputSize::USIZE - n; + let choice = self.finalize_fixed()[m..].ct_eq(tag); + + if choice.unwrap_u8() == 1 { + Ok(()) + } else { + Err(MacError) + } + } +} + +/// Fixed size output value which provides a safe [`Eq`] implementation that +/// runs in constant time. +/// +/// It is useful for implementing Message Authentication Codes (MACs). +#[derive(Clone)] +#[cfg_attr(docsrs, doc(cfg(feature = "subtle")))] +pub struct CtOutput { + bytes: Output, +} + +impl CtOutput { + /// Create a new [`CtOutput`] value. + #[inline(always)] + pub fn new(bytes: Output) -> Self { + Self { bytes } + } + + /// Get the inner [`Output`] array this type wraps. + #[inline(always)] + pub fn into_bytes(self) -> Output { + self.bytes + } +} + +impl From> for CtOutput { + #[inline(always)] + fn from(bytes: Output) -> Self { + Self { bytes } + } +} + +impl<'a, T: OutputSizeUser> From<&'a Output> for CtOutput { + #[inline(always)] + fn from(bytes: &'a Output) -> Self { + bytes.clone().into() + } +} + +impl ConstantTimeEq for CtOutput { + #[inline(always)] + fn ct_eq(&self, other: &Self) -> Choice { + self.bytes.ct_eq(&other.bytes) + } +} + +impl PartialEq for CtOutput { + #[inline(always)] + fn eq(&self, x: &CtOutput) -> bool { + self.ct_eq(x).unwrap_u8() == 1 + } +} + +impl Eq for CtOutput {} + +/// Error type for when the [`Output`] of a [`Mac`] +/// is not equal to the expected value. +#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] +pub struct MacError; + +impl fmt::Display for MacError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("MAC tag mismatch") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for MacError {}