diff --git a/.github/workflows/belt-mac.yml b/.github/workflows/belt-mac.yml index 9f02252..310d3cc 100644 --- a/.github/workflows/belt-mac.yml +++ b/.github/workflows/belt-mac.yml @@ -59,3 +59,4 @@ jobs: toolchain: ${{ matrix.rust }} - run: cargo test --release --no-default-features - run: cargo test --release + - run: cargo test --release --all-features diff --git a/.github/workflows/cbc-mac.yml b/.github/workflows/cbc-mac.yml index 876cf30..88f1f67 100644 --- a/.github/workflows/cbc-mac.yml +++ b/.github/workflows/cbc-mac.yml @@ -60,3 +60,4 @@ jobs: toolchain: ${{ matrix.rust }} - run: cargo test --release --no-default-features - run: cargo test --release + - run: cargo test --release --all-features diff --git a/.github/workflows/cmac.yml b/.github/workflows/cmac.yml index 2e26307..a50d606 100644 --- a/.github/workflows/cmac.yml +++ b/.github/workflows/cmac.yml @@ -59,3 +59,4 @@ jobs: toolchain: ${{ matrix.rust }} - run: cargo test --release --no-default-features - run: cargo test --release + - run: cargo test --release --all-features diff --git a/.github/workflows/hmac.yml b/.github/workflows/hmac.yml index 45cd752..e887750 100644 --- a/.github/workflows/hmac.yml +++ b/.github/workflows/hmac.yml @@ -58,5 +58,5 @@ jobs: with: toolchain: ${{ matrix.rust }} - run: cargo test --release --no-default-features - - run: cargo test --release --features reset - run: cargo test --release + - run: cargo test --release --all-features diff --git a/.github/workflows/pmac.yml b/.github/workflows/pmac.yml index 7c584b8..a4ef239 100644 --- a/.github/workflows/pmac.yml +++ b/.github/workflows/pmac.yml @@ -59,3 +59,4 @@ jobs: toolchain: ${{ matrix.rust }} - run: cargo test --release --no-default-features - run: cargo test --release + - run: cargo test --release --all-features diff --git a/.github/workflows/retail-mac.yml b/.github/workflows/retail-mac.yml index 3094bb3..87dd816 100644 --- a/.github/workflows/retail-mac.yml +++ b/.github/workflows/retail-mac.yml @@ -59,3 +59,4 @@ jobs: toolchain: ${{ matrix.rust }} - run: cargo test --release --no-default-features - run: cargo test --release + - run: cargo test --release --all-features diff --git a/Cargo.lock b/Cargo.lock index 0d85193..fc80ee1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,6 +45,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a229bfd78e4827c91b9b95784f69492c1b77c1ab75a45a8a037b139215086f94" dependencies = [ "hybrid-array", + "zeroize", ] [[package]] @@ -101,8 +102,7 @@ dependencies = [ [[package]] name = "crypto-common" version = "0.2.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170d71b5b14dec99db7739f6fc7d6ec2db80b78c3acb77db48392ccc3d8a9ea0" +source = "git+https://github.com/RustCrypto/traits#915474f1ed5be0a19fd102d5f75ef8e04c765416" dependencies = [ "hybrid-array", ] @@ -128,13 +128,13 @@ dependencies = [ [[package]] name = "digest" version = "0.11.0-pre.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c478574b20020306f98d61c8ca3322d762e1ff08117422ac6106438605ea516" +source = "git+https://github.com/RustCrypto/traits#915474f1ed5be0a19fd102d5f75ef8e04c765416" dependencies = [ "blobby", "block-buffer", "crypto-common", "subtle", + "zeroize", ] [[package]] @@ -157,9 +157,9 @@ dependencies = [ [[package]] name = "hybrid-array" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dab50e193aebe510fe0e40230145820e02f48dae0cf339ea4204e6e708ff7bd" +checksum = "891d15931895091dea5c47afa5b3c9a01ba634b311919fd4d41388fa0e3d76af" dependencies = [ "typenum", "zeroize", @@ -186,9 +186,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.170" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "magma" @@ -202,8 +202,7 @@ dependencies = [ [[package]] name = "md-5" version = "0.11.0-pre.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f97ce75b16c61e8ffe9363ca30092ff9da9daed3c7312296eef978c4ecb2d28" +source = "git+https://github.com/RustCrypto/hashes#7d44caf065dbeb3f10a372a26a8b9f1c927f8433" dependencies = [ "cfg-if", "digest", @@ -233,8 +232,7 @@ dependencies = [ [[package]] name = "sha1" version = "0.11.0-pre.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55f44e40722caefdd99383c25d3ae52a1094a1951215ae76f68837ece4e7f566" +source = "git+https://github.com/RustCrypto/hashes#7d44caf065dbeb3f10a372a26a8b9f1c927f8433" dependencies = [ "cfg-if", "cpufeatures", @@ -244,8 +242,7 @@ dependencies = [ [[package]] name = "sha2" version = "0.11.0-pre.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b4241d1a56954dce82cecda5c8e9c794eef6f53abe5e5216bac0a0ea71ffa7" +source = "git+https://github.com/RustCrypto/hashes#7d44caf065dbeb3f10a372a26a8b9f1c927f8433" dependencies = [ "cfg-if", "cpufeatures", @@ -255,8 +252,7 @@ dependencies = [ [[package]] name = "streebog" version = "0.11.0-pre.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a275f3837c892b08e1e992cf3ef597aa2aecea6d985a6db2035b367673cfe1" +source = "git+https://github.com/RustCrypto/hashes#7d44caf065dbeb3f10a372a26a8b9f1c927f8433" dependencies = [ "digest", ] diff --git a/Cargo.toml b/Cargo.toml index b8c3abb..f4cd9f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,15 @@ members = [ [profile.dev] opt-level = 2 + +[patch.crates-io] +# https://github.com/RustCrypto/traits/pull/1787 +# https://github.com/RustCrypto/traits/pull/1799 +digest = { git = "https://github.com/RustCrypto/traits" } +crypto-common = { git = "https://github.com/RustCrypto/traits" } + +# https://github.com/RustCrypto/hashes/pull/678 +streebog = { git = "https://github.com/RustCrypto/hashes" } +sha1 = { git = "https://github.com/RustCrypto/hashes" } +sha2 = { git = "https://github.com/RustCrypto/hashes" } +md-5 = { git = "https://github.com/RustCrypto/hashes" } diff --git a/belt-mac/CHANGELOG.md b/belt-mac/CHANGELOG.md index afe586b..db46f89 100644 --- a/belt-mac/CHANGELOG.md +++ b/belt-mac/CHANGELOG.md @@ -5,5 +5,18 @@ 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). +## 0.2.0 (UNRELEASED) +### Changed +- Edition changed to 2024 and MSRV bumped to 1.85 +- Relax MSRV policy and allow MSRV bumps in patch releases +- Update to `digest` v0.11 +- Update to `cipher` v0.5 +- Replace type aliases with newtypes ([#186]) + +### Removed +- `std` crate feature ([#186]) + +[#186]: https://github.com/RustCrypto/MACs/pull/186 + ## 0.1.0 (2023-04-03) Initial Release \ No newline at end of file diff --git a/belt-mac/Cargo.toml b/belt-mac/Cargo.toml index 724efb1..67202db 100644 --- a/belt-mac/Cargo.toml +++ b/belt-mac/Cargo.toml @@ -22,9 +22,7 @@ digest = { version = "=0.11.0-pre.10", features = ["dev"] } hex-literal = "1" [features] -std = ["digest/std"] -zeroize = ["cipher/zeroize"] +zeroize = ["cipher/zeroize", "digest/zeroize"] [package.metadata.docs.rs] all-features = true -rustdoc-args = ["--cfg", "docsrs"] diff --git a/belt-mac/benches/mod.rs b/belt-mac/benches/mod.rs index 5061481..8433217 100644 --- a/belt-mac/benches/mod.rs +++ b/belt-mac/benches/mod.rs @@ -1,12 +1,11 @@ #![feature(test)] extern crate test; -use belt_block::BeltBlock; use belt_mac::{BeltMac, KeyInit}; use test::Bencher; digest::bench_update!( - BeltMac::::new(&Default::default()); + BeltMac::new(&Default::default()); belt_mac_10 10; belt_mac_100 100; belt_mac_1000 1000; diff --git a/belt-mac/src/block_api.rs b/belt-mac/src/block_api.rs new file mode 100644 index 0000000..6177a0c --- /dev/null +++ b/belt-mac/src/block_api.rs @@ -0,0 +1,184 @@ +use belt_block::BeltBlock; +use cipher::{BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt}; +use core::fmt; +use digest::{ + MacMarker, Output, OutputSizeUser, Reset, + array::{Array, ArraySize}, + block_api::{ + AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, FixedOutputCore, Lazy, + UpdateCore, + }, + crypto_common::{BlockSizes, InnerInit, InnerUser}, +}; + +#[cfg(feature = "zeroize")] +use digest::zeroize::{Zeroize, ZeroizeOnDrop}; + +/// Generic core BeltMac instance, which operates over blocks. +#[derive(Clone)] +pub struct BeltMacCore +where + C: BlockCipherEncrypt + Clone, +{ + cipher: C, + state: Block, + r: Block, +} + +impl BlockSizeUser for BeltMacCore +where + C: BlockCipherEncrypt + Clone, +{ + type BlockSize = C::BlockSize; +} + +impl OutputSizeUser for BeltMacCore +where + C: BlockCipherEncrypt + Clone, +{ + type OutputSize = C::BlockSize; +} + +impl InnerUser for BeltMacCore +where + C: BlockCipherEncrypt + Clone, +{ + type Inner = C; +} + +impl MacMarker for BeltMacCore where C: BlockCipherEncrypt + Clone {} + +impl InnerInit for BeltMacCore +where + C: BlockCipherEncrypt + Clone, +{ + #[inline] + fn inner_init(cipher: C) -> Self { + let state = Default::default(); + let mut r = Default::default(); + cipher.encrypt_block(&mut r); + Self { cipher, state, r } + } +} + +impl BufferKindUser for BeltMacCore +where + C: BlockCipherEncrypt + Clone, +{ + type BufferKind = Lazy; +} + +impl UpdateCore for BeltMacCore +where + C: BlockCipherEncrypt + Clone, +{ + #[inline] + fn update_blocks(&mut self, blocks: &[Block]) { + struct Closure<'a, N: BlockSizes> { + state: &'a mut Block, + blocks: &'a [Block], + } + + impl BlockSizeUser for Closure<'_, N> { + type BlockSize = N; + } + + impl BlockCipherEncClosure for Closure<'_, N> { + #[inline(always)] + fn call>(self, backend: &B) { + for block in self.blocks { + xor(self.state, block); + backend.encrypt_block((self.state).into()); + } + } + } + + let Self { cipher, state, .. } = self; + cipher.encrypt_with_backend(Closure { state, blocks }) + } +} + +impl Reset for BeltMacCore +where + C: BlockCipherEncrypt + Clone, +{ + #[inline(always)] + fn reset(&mut self) { + self.state = Default::default(); + } +} + +impl FixedOutputCore for BeltMacCore +where + C: BlockCipherEncrypt + Clone, +{ + #[inline] + fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output) { + let pos = buffer.get_pos(); + let mut buf = buffer.pad_with_zeros(); + + let cipher = &mut self.cipher; + let r = &self.r; + let bs = r.len(); + let mut new_r = Block::::default(); + if pos == bs { + // phi1 + let (h1, h2) = new_r.split_at_mut(bs - 4); + h1.copy_from_slice(&r[4..]); + for i in 0..4 { + h2[i] = r[i] ^ r[4 + i]; + } + } else { + buf[pos] = 0x80; + // phi2 + let (h1, h2) = new_r.split_at_mut(4); + for i in 0..4 { + h1[i] = r[i] ^ r[bs - 4 + i]; + } + h2.copy_from_slice(&r[..bs - 4]); + } + + let mut state = self.state.clone(); + xor(&mut state, &buf); + xor(&mut state, &new_r); + cipher.encrypt_block_b2b(&state, out); + } +} + +impl AlgorithmName for BeltMacCore +where + C: BlockCipherEncrypt + Clone, +{ + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("BeltMac") + } +} + +impl fmt::Debug for BeltMacCore +where + C: BlockCipherEncrypt + Clone, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("BeltMacCore { ... }") + } +} + +#[cfg(feature = "zeroize")] +impl Drop for BeltMacCore +where + C: BlockCipherEncrypt + Clone, +{ + fn drop(&mut self) { + self.state.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +impl ZeroizeOnDrop for BeltMacCore where C: BlockCipherEncrypt + Clone + ZeroizeOnDrop {} + +#[inline(always)] +fn xor(buf: &mut Array, data: &Array) { + for i in 0..N::USIZE { + buf[i] ^= data[i]; + } +} diff --git a/belt-mac/src/lib.rs b/belt-mac/src/lib.rs index ae8a426..9cfb035 100644 --- a/belt-mac/src/lib.rs +++ b/belt-mac/src/lib.rs @@ -5,207 +5,21 @@ html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg" )] #![forbid(unsafe_code)] -#![cfg_attr(docsrs, feature(doc_cfg))] -#![warn(missing_docs, rust_2018_idioms)] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![warn(missing_docs)] pub use digest::{self, KeyInit, Mac}; -use belt_block::BeltBlock; -use cipher::{BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt}; -use core::fmt; -use digest::{ - MacMarker, Output, OutputSizeUser, Reset, - array::{ - Array, ArraySize, - typenum::{IsLess, Le, NonZero, U256}, - }, - block_buffer::Lazy, - core_api::{ - AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore, - UpdateCore, - }, - crypto_common::{BlockSizes, InnerInit, InnerUser}, -}; +/// Block-level implementation. +pub mod block_api; -#[cfg(feature = "zeroize")] -use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; +use cipher::BlockCipherEncrypt; -/// Generic BeltMac instance. -pub type BeltMac = CoreWrapper>; +digest::buffer_fixed!( + /// BeltMac instance generic over block cipher. + pub struct GenericBeltMac(block_api::BeltMacCore); + impl: ResetMacTraits AlgorithmName InnerInit; +); -#[derive(Clone)] -/// Generic core BeltMac instance, which operates over blocks. -pub struct BeltMacCore -where - C: BlockCipherEncrypt + Clone, -{ - cipher: C, - state: Block, - r: Block, -} - -impl BlockSizeUser for BeltMacCore -where - C: BlockCipherEncrypt + Clone, -{ - type BlockSize = C::BlockSize; -} - -impl OutputSizeUser for BeltMacCore -where - C: BlockCipherEncrypt + Clone, -{ - type OutputSize = C::BlockSize; -} - -impl InnerUser for BeltMacCore -where - C: BlockCipherEncrypt + Clone, -{ - type Inner = C; -} - -impl MacMarker for BeltMacCore where C: BlockCipherEncrypt + Clone {} - -impl InnerInit for BeltMacCore -where - C: BlockCipherEncrypt + Clone, -{ - #[inline] - fn inner_init(cipher: C) -> Self { - let state = Default::default(); - let mut r = Default::default(); - cipher.encrypt_block(&mut r); - Self { cipher, state, r } - } -} - -impl BufferKindUser for BeltMacCore -where - C: BlockCipherEncrypt + Clone, -{ - type BufferKind = Lazy; -} - -impl UpdateCore for BeltMacCore -where - C: BlockCipherEncrypt + Clone, -{ - #[inline] - fn update_blocks(&mut self, blocks: &[Block]) { - struct Closure<'a, N: BlockSizes> { - state: &'a mut Block, - blocks: &'a [Block], - } - - impl BlockSizeUser for Closure<'_, N> { - type BlockSize = N; - } - - impl BlockCipherEncClosure for Closure<'_, N> { - #[inline(always)] - fn call>(self, backend: &B) { - for block in self.blocks { - xor(self.state, block); - backend.encrypt_block((self.state).into()); - } - } - } - - let Self { cipher, state, .. } = self; - cipher.encrypt_with_backend(Closure { state, blocks }) - } -} - -impl Reset for BeltMacCore -where - C: BlockCipherEncrypt + Clone, -{ - #[inline(always)] - fn reset(&mut self) { - self.state = Default::default(); - } -} - -impl FixedOutputCore for BeltMacCore -where - C: BlockCipherEncrypt + Clone, - C::BlockSize: IsLess, - Le: NonZero, -{ - #[inline] - fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output) { - let pos = buffer.get_pos(); - let mut buf = buffer.pad_with_zeros(); - - let cipher = &mut self.cipher; - let r = &self.r; - let bs = r.len(); - let mut new_r = Block::::default(); - if pos == bs { - // phi1 - let (h1, h2) = new_r.split_at_mut(bs - 4); - h1.copy_from_slice(&r[4..]); - for i in 0..4 { - h2[i] = r[i] ^ r[4 + i]; - } - } else { - buf[pos] = 0x80; - // phi2 - let (h1, h2) = new_r.split_at_mut(4); - for i in 0..4 { - h1[i] = r[i] ^ r[bs - 4 + i]; - } - h2.copy_from_slice(&r[..bs - 4]); - } - - let mut state = self.state.clone(); - xor(&mut state, &buf); - xor(&mut state, &new_r); - cipher.encrypt_block_b2b(&state, out); - } -} - -impl AlgorithmName for BeltMacCore -where - C: BlockCipherEncrypt + Clone + AlgorithmName, -{ - fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("BeltMac<")?; - ::write_alg_name(f)?; - f.write_str(">") - } -} - -impl fmt::Debug for BeltMacCore -where - C: BlockCipherEncrypt + Clone + AlgorithmName, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("BeltMacCore<")?; - ::write_alg_name(f)?; - f.write_str("> { ... }") - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl Drop for BeltMacCore -where - C: BlockCipherEncrypt + Clone, -{ - fn drop(&mut self) { - self.state.zeroize(); - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl ZeroizeOnDrop for BeltMacCore where C: BlockCipherEncrypt + Clone + ZeroizeOnDrop {} - -#[inline(always)] -fn xor(buf: &mut Array, data: &Array) { - for i in 0..N::USIZE { - buf[i] ^= data[i]; - } -} +/// BeltMac instance. +pub type BeltMac = GenericBeltMac; diff --git a/cbc-mac/CHANGELOG.md b/cbc-mac/CHANGELOG.md index 58b335f..0400c81 100644 --- a/cbc-mac/CHANGELOG.md +++ b/cbc-mac/CHANGELOG.md @@ -5,6 +5,19 @@ 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). +## 0.2.0 (UNRELEASED) +### Changed +- Edition changed to 2024 and MSRV bumped to 1.85 +- Relax MSRV policy and allow MSRV bumps in patch releases +- Update to `digest` v0.11 +- Update to `cipher` v0.5 +- Replace type aliases with newtypes ([#186]) + +### Removed +- `std` crate feature ([#186]) + +[#186]: https://github.com/RustCrypto/MACs/pull/186 + ## 0.1.1 (2022-02-17) ### Fixed - Minimal versions build ([#108]) diff --git a/cbc-mac/Cargo.toml b/cbc-mac/Cargo.toml index 99a21bc..e842b00 100644 --- a/cbc-mac/Cargo.toml +++ b/cbc-mac/Cargo.toml @@ -23,9 +23,7 @@ aes = "0.9.0-pre.3" des = "0.9.0-pre.3" [features] -std = ["digest/std"] -zeroize = ["cipher/zeroize"] +zeroize = ["cipher/zeroize", "digest/zeroize"] [package.metadata.docs.rs] all-features = true -rustdoc-args = ["--cfg", "docsrs"] diff --git a/cbc-mac/src/block_api.rs b/cbc-mac/src/block_api.rs new file mode 100644 index 0000000..aaa11fe --- /dev/null +++ b/cbc-mac/src/block_api.rs @@ -0,0 +1,163 @@ +use cipher::{BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt}; +use core::fmt; +use digest::{ + MacMarker, Output, OutputSizeUser, Reset, + array::{Array, ArraySize}, + block_api::{ + AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, Eager, FixedOutputCore, + UpdateCore, + }, + crypto_common::{BlockSizes, InnerInit, InnerUser}, +}; + +#[cfg(feature = "zeroize")] +use digest::zeroize::{Zeroize, ZeroizeOnDrop}; + +/// Generic core CMAC instance, which operates over blocks. +#[derive(Clone)] +pub struct CbcMacCore +where + C: BlockCipherEncrypt + Clone, +{ + cipher: C, + state: Block, +} + +impl BlockSizeUser for CbcMacCore +where + C: BlockCipherEncrypt + Clone, +{ + type BlockSize = C::BlockSize; +} + +impl OutputSizeUser for CbcMacCore +where + C: BlockCipherEncrypt + Clone, +{ + type OutputSize = C::BlockSize; +} + +impl InnerUser for CbcMacCore +where + C: BlockCipherEncrypt + Clone, +{ + type Inner = C; +} + +impl MacMarker for CbcMacCore where C: BlockCipherEncrypt + Clone {} + +impl InnerInit for CbcMacCore +where + C: BlockCipherEncrypt + Clone, +{ + #[inline] + fn inner_init(cipher: C) -> Self { + let state = Default::default(); + Self { cipher, state } + } +} + +impl BufferKindUser for CbcMacCore +where + C: BlockCipherEncrypt + Clone, +{ + type BufferKind = Eager; +} + +impl UpdateCore for CbcMacCore +where + C: BlockCipherEncrypt + Clone, +{ + #[inline] + fn update_blocks(&mut self, blocks: &[Block]) { + struct Closure<'a, N: BlockSizes> { + state: &'a mut Block, + blocks: &'a [Block], + } + + impl BlockSizeUser for Closure<'_, N> { + type BlockSize = N; + } + + impl BlockCipherEncClosure for Closure<'_, N> { + #[inline(always)] + fn call>(self, backend: &B) { + for block in self.blocks { + xor(self.state, block); + backend.encrypt_block((self.state).into()); + } + } + } + + let Self { cipher, state } = self; + cipher.encrypt_with_backend(Closure { state, blocks }) + } +} + +impl Reset for CbcMacCore +where + C: BlockCipherEncrypt + Clone, +{ + #[inline(always)] + fn reset(&mut self) { + self.state = Default::default(); + } +} + +impl FixedOutputCore for CbcMacCore +where + C: BlockCipherEncrypt + Clone, +{ + #[inline] + fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output) { + let Self { state, cipher } = self; + let pos = buffer.get_pos(); + if pos != 0 { + xor(state, &buffer.pad_with_zeros()); + cipher.encrypt_block(state); + } + out.copy_from_slice(state); + } +} + +impl AlgorithmName for CbcMacCore +where + C: BlockCipherEncrypt + Clone + AlgorithmName, +{ + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("CbcMac<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for CbcMacCore +where + C: BlockCipherEncrypt + Clone + AlgorithmName, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("CbcMacCore<")?; + ::write_alg_name(f)?; + f.write_str("> { ... }") + } +} + +#[cfg(feature = "zeroize")] +impl Drop for CbcMacCore +where + C: BlockCipherEncrypt + Clone, +{ + fn drop(&mut self) { + self.state.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +impl ZeroizeOnDrop for CbcMacCore where C: BlockCipherEncrypt + Clone + ZeroizeOnDrop {} + +#[inline(always)] +fn xor(buf: &mut Array, data: &Array) { + for i in 0..N::USIZE { + buf[i] ^= data[i]; + } +} diff --git a/cbc-mac/src/lib.rs b/cbc-mac/src/lib.rs index 15705bd..40a2ef5 100644 --- a/cbc-mac/src/lib.rs +++ b/cbc-mac/src/lib.rs @@ -22,190 +22,36 @@ //! mac.verify_slice(&correct).unwrap(); //! ``` //! -//! [CBC-MAC]: https://en.wikipedia.org/wiki/CBC-MAC#Security_with_fixed_and_variable-length_messages +//! [CBC-MAC]: https://en.wikipedia.org/wiki/CBC-MAC #![no_std] #![doc( html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg" )] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![deny(unsafe_code)] -#![cfg_attr(docsrs, feature(doc_cfg))] -#![warn(missing_docs, rust_2018_idioms)] +#![warn(missing_docs)] pub use digest::{self, KeyInit, Mac}; -use cipher::{BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt}; -use core::fmt; -use digest::{ - MacMarker, Output, OutputSizeUser, Reset, - array::{ - Array, ArraySize, - typenum::{IsLess, Le, NonZero, U256}, - }, - block_buffer::Eager, - core_api::{ - AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore, - UpdateCore, - }, - crypto_common::{BlockSizes, InnerInit, InnerUser}, -}; - -#[cfg(feature = "zeroize")] -use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; - -/// Generic CMAC instance. -pub type CbcMac = CoreWrapper>; - -/// Generic core CMAC instance, which operates over blocks. -#[derive(Clone)] -pub struct CbcMacCore -where - C: BlockCipherEncrypt + Clone, -{ - cipher: C, - state: Block, -} - -impl BlockSizeUser for CbcMacCore -where - C: BlockCipherEncrypt + Clone, -{ - type BlockSize = C::BlockSize; -} - -impl OutputSizeUser for CbcMacCore -where - C: BlockCipherEncrypt + Clone, -{ - type OutputSize = C::BlockSize; -} +mod block_api; -impl InnerUser for CbcMacCore -where - C: BlockCipherEncrypt + Clone, -{ - type Inner = C; -} - -impl MacMarker for CbcMacCore where C: BlockCipherEncrypt + Clone {} - -impl InnerInit for CbcMacCore -where - C: BlockCipherEncrypt + Clone, -{ - #[inline] - fn inner_init(cipher: C) -> Self { - let state = Default::default(); - Self { cipher, state } - } -} - -impl BufferKindUser for CbcMacCore -where - C: BlockCipherEncrypt + Clone, -{ - type BufferKind = Eager; -} - -impl UpdateCore for CbcMacCore -where - C: BlockCipherEncrypt + Clone, -{ - #[inline] - fn update_blocks(&mut self, blocks: &[Block]) { - struct Closure<'a, N: BlockSizes> { - state: &'a mut Block, - blocks: &'a [Block], - } - - impl BlockSizeUser for Closure<'_, N> { - type BlockSize = N; - } - - impl BlockCipherEncClosure for Closure<'_, N> { - #[inline(always)] - fn call>(self, backend: &B) { - for block in self.blocks { - xor(self.state, block); - backend.encrypt_block((self.state).into()); - } - } - } - - let Self { cipher, state } = self; - cipher.encrypt_with_backend(Closure { state, blocks }) - } -} - -impl Reset for CbcMacCore -where - C: BlockCipherEncrypt + Clone, -{ - #[inline(always)] - fn reset(&mut self) { - self.state = Default::default(); - } -} +use cipher::{AlgorithmName, BlockCipherEncrypt}; +use core::fmt; +use digest::block_api::CoreProxy; -impl FixedOutputCore for CbcMacCore -where - C: BlockCipherEncrypt + Clone, - C::BlockSize: IsLess, - Le: NonZero, -{ - #[inline] - fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output) { - let Self { state, cipher } = self; - let pos = buffer.get_pos(); - if pos != 0 { - xor(state, &buffer.pad_with_zeros()); - cipher.encrypt_block(state); - } - out.copy_from_slice(state); - } -} +digest::buffer_fixed!( + /// Generic CBC-MAC instance. + pub struct CbcMac(block_api::CbcMacCore); + impl: ResetMacTraits InnerInit; +); -impl AlgorithmName for CbcMacCore +impl AlgorithmName for CbcMac where C: BlockCipherEncrypt + Clone + AlgorithmName, { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("CbcMac<")?; - ::write_alg_name(f)?; - f.write_str(">") - } -} - -impl fmt::Debug for CbcMacCore -where - C: BlockCipherEncrypt + Clone + AlgorithmName, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("CbcMacCore<")?; - ::write_alg_name(f)?; - f.write_str("> { ... }") - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl Drop for CbcMacCore -where - C: BlockCipherEncrypt + Clone, -{ - fn drop(&mut self) { - self.state.zeroize(); - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl ZeroizeOnDrop for CbcMacCore where C: BlockCipherEncrypt + Clone + ZeroizeOnDrop {} - -#[inline(always)] -fn xor(buf: &mut Array, data: &Array) { - for i in 0..N::USIZE { - buf[i] ^= data[i]; + ::Core::write_alg_name(f) } } diff --git a/cmac/CHANGELOG.md b/cmac/CHANGELOG.md index b9b493c..27be9fa 100644 --- a/cmac/CHANGELOG.md +++ b/cmac/CHANGELOG.md @@ -5,6 +5,19 @@ 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). +## 0.8.0 (UNRELEASED) +### Changed +- Edition changed to 2024 and MSRV bumped to 1.85 +- Relax MSRV policy and allow MSRV bumps in patch releases +- Update to `digest` v0.11 +- Update to `cipher` v0.5 +- Replace type aliases with newtypes ([#186]) + +### Removed +- `std` crate feature ([#186]) + +[#186]: https://github.com/RustCrypto/MACs/pull/186 + ## 0.7.2 (2022-03-14) ### Changed - Do not include large CAVP test vectors in published packages ([#128]) diff --git a/cmac/Cargo.toml b/cmac/Cargo.toml index ab63c2f..fde7bfc 100644 --- a/cmac/Cargo.toml +++ b/cmac/Cargo.toml @@ -28,9 +28,7 @@ kuznyechik = "0.9.0-pre.3" magma = "0.10.0-pre.3" [features] -std = ["digest/std"] -zeroize = ["cipher/zeroize"] +zeroize = ["cipher/zeroize", "digest/zeroize"] [package.metadata.docs.rs] all-features = true -rustdoc-args = ["--cfg", "docsrs"] diff --git a/cmac/src/block_api.rs b/cmac/src/block_api.rs new file mode 100644 index 0000000..7663734 --- /dev/null +++ b/cmac/src/block_api.rs @@ -0,0 +1,155 @@ +use cipher::{BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt}; +use core::fmt; +use dbl::Dbl; +use digest::{ + MacMarker, Output, OutputSizeUser, Reset, + array::{Array, ArraySize}, + block_api::{ + AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, FixedOutputCore, Lazy, + UpdateCore, + }, + crypto_common::{BlockSizes, InnerInit, InnerUser}, +}; + +#[cfg(feature = "zeroize")] +use digest::zeroize::{Zeroize, ZeroizeOnDrop}; + +/// Generic core CMAC instance, which operates over blocks. +#[derive(Clone)] +pub struct CmacCore { + cipher: C, + state: Block, +} + +impl BlockSizeUser for CmacCore { + type BlockSize = C::BlockSize; +} + +impl OutputSizeUser for CmacCore { + type OutputSize = C::BlockSize; +} + +impl InnerUser for CmacCore { + type Inner = C; +} + +impl MacMarker for CmacCore {} + +impl InnerInit for CmacCore { + #[inline] + fn inner_init(cipher: C) -> Self { + let state = Default::default(); + Self { cipher, state } + } +} + +impl BufferKindUser for CmacCore { + type BufferKind = Lazy; +} + +impl UpdateCore for CmacCore { + #[inline] + fn update_blocks(&mut self, blocks: &[Block]) { + struct Closure<'a, N: BlockSizes> { + state: &'a mut Block, + blocks: &'a [Block], + } + + impl BlockSizeUser for Closure<'_, N> { + type BlockSize = N; + } + + impl BlockCipherEncClosure for Closure<'_, N> { + #[inline(always)] + fn call>(self, backend: &B) { + for block in self.blocks { + xor(self.state, block); + backend.encrypt_block((self.state).into()); + } + } + } + + let Self { cipher, state } = self; + cipher.encrypt_with_backend(Closure { state, blocks }) + } +} + +impl Reset for CmacCore { + #[inline(always)] + fn reset(&mut self) { + self.state = Default::default(); + } +} + +impl FixedOutputCore for CmacCore { + #[inline] + fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output) { + let Self { state, cipher } = self; + let pos = buffer.get_pos(); + let buf = buffer.pad_with_zeros(); + + let mut subkey = Default::default(); + cipher.encrypt_block(&mut subkey); + let key1 = C::dbl(subkey); + + xor(state, &buf); + if pos == buf.len() { + xor(state, &key1); + } else { + state[pos] ^= 0x80; + let key2 = C::dbl(key1); + xor(state, &key2); + } + cipher.encrypt_block(state); + out.copy_from_slice(state); + } +} + +impl AlgorithmName for CmacCore { + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Cmac<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for CmacCore { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("CmacCore { ... }") + } +} + +impl Drop for CmacCore { + fn drop(&mut self) { + #[cfg(feature = "zeroize")] + { + self.state.zeroize(); + } + } +} + +#[cfg(feature = "zeroize")] +impl ZeroizeOnDrop for CmacCore {} + +#[inline(always)] +fn xor(buf: &mut Array, data: &Array) { + for i in 0..N::USIZE { + buf[i] ^= data[i]; + } +} + +/// Helper trait implemented for cipher supported by CMAC +pub trait CmacCipher: BlockSizeUser + BlockCipherEncrypt + Clone { + /// Double block. See the [`Dbl`] trait docs for more information. + fn dbl(block: Block) -> Block; +} + +impl CmacCipher for C +where + Self: BlockSizeUser + BlockCipherEncrypt + Clone, + Block: Dbl, +{ + fn dbl(block: Block) -> Block { + block.dbl() + } +} diff --git a/cmac/src/lib.rs b/cmac/src/lib.rs index a8ba4ec..e55bfbe 100644 --- a/cmac/src/lib.rs +++ b/cmac/src/lib.rs @@ -44,217 +44,27 @@ html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg" )] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![forbid(unsafe_code)] -#![cfg_attr(docsrs, feature(doc_cfg))] -#![warn(missing_docs, rust_2018_idioms)] +#![warn(missing_docs)] pub use digest::{self, KeyInit, Mac}; -use cipher::{BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt}; -use core::fmt; -use dbl::Dbl; -use digest::{ - MacMarker, Output, OutputSizeUser, Reset, - array::{ - Array, ArraySize, - typenum::{IsLess, Le, NonZero, U256}, - }, - block_buffer::Lazy, - core_api::{ - AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore, - UpdateCore, - }, - crypto_common::{BlockSizes, InnerInit, InnerUser}, -}; - -#[cfg(feature = "zeroize")] -use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; - -/// Generic CMAC instance. -pub type Cmac = CoreWrapper>; - -/// Generic core CMAC instance, which operates over blocks. -#[derive(Clone)] -pub struct CmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, -{ - cipher: C, - state: Block, -} - -impl BlockSizeUser for CmacCore -where - C: BlockCipherEncrypt + BlockSizeUser + Clone, - Block: Dbl, -{ - type BlockSize = C::BlockSize; -} - -impl OutputSizeUser for CmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, -{ - type OutputSize = C::BlockSize; -} - -impl InnerUser for CmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, -{ - type Inner = C; -} - -impl MacMarker for CmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, -{ -} - -impl InnerInit for CmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, -{ - #[inline] - fn inner_init(cipher: C) -> Self { - let state = Default::default(); - Self { cipher, state } - } -} - -impl BufferKindUser for CmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, -{ - type BufferKind = Lazy; -} - -impl UpdateCore for CmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, -{ - #[inline] - fn update_blocks(&mut self, blocks: &[Block]) { - struct Closure<'a, N: BlockSizes> { - state: &'a mut Block, - blocks: &'a [Block], - } - - impl BlockSizeUser for Closure<'_, N> { - type BlockSize = N; - } - - impl BlockCipherEncClosure for Closure<'_, N> { - #[inline(always)] - fn call>(self, backend: &B) { - for block in self.blocks { - xor(self.state, block); - backend.encrypt_block((self.state).into()); - } - } - } +/// Block-level implementation. +pub mod block_api; - let Self { cipher, state } = self; - cipher.encrypt_with_backend(Closure { state, blocks }) - } -} - -impl Reset for CmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, -{ - #[inline(always)] - fn reset(&mut self) { - self.state = Default::default(); - } -} - -impl FixedOutputCore for CmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, - C::BlockSize: IsLess, - Le: NonZero, -{ - #[inline] - fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output) { - let Self { state, cipher } = self; - let pos = buffer.get_pos(); - let buf = buffer.pad_with_zeros(); +use block_api::CmacCipher; +use core::fmt; +use digest::block_api::{AlgorithmName, CoreProxy}; - let mut subkey = Default::default(); - cipher.encrypt_block(&mut subkey); - let key1 = subkey.dbl(); +digest::buffer_fixed!( + /// Generic CMAC instance. + pub struct Cmac(block_api::CmacCore); + impl: ResetMacTraits InnerInit; +); - xor(state, &buf); - if pos == buf.len() { - xor(state, &key1); - } else { - state[pos] ^= 0x80; - let key2 = key1.dbl(); - xor(state, &key2); - } - cipher.encrypt_block(state); - out.copy_from_slice(state); - } -} - -impl AlgorithmName for CmacCore -where - C: BlockCipherEncrypt + Clone + AlgorithmName, - Block: Dbl, -{ +impl AlgorithmName for Cmac { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Cmac<")?; - ::write_alg_name(f)?; - f.write_str(">") - } -} - -impl fmt::Debug for CmacCore -where - C: BlockCipherEncrypt + Clone + AlgorithmName, - Block: Dbl, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("CmacCore<")?; - ::write_alg_name(f)?; - f.write_str("> { ... }") - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl Drop for CmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, -{ - fn drop(&mut self) { - self.state.zeroize(); - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl ZeroizeOnDrop for CmacCore -where - C: BlockCipherEncrypt + Clone + ZeroizeOnDrop, - Block: Dbl, -{ -} - -#[inline(always)] -fn xor(buf: &mut Array, data: &Array) { - for i in 0..N::USIZE { - buf[i] ^= data[i]; + ::Core::write_alg_name(f) } } diff --git a/hmac/CHANGELOG.md b/hmac/CHANGELOG.md index cde26d6..003f27a 100644 --- a/hmac/CHANGELOG.md +++ b/hmac/CHANGELOG.md @@ -5,6 +5,21 @@ 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). +## 0.13.0 (UNRELEASED) +### Added +- `HmacReset` and `SimpleHmacReset` types [#186] + +### Changed +- Edition changed to 2024 and MSRV bumped to 1.85 +- Relax MSRV policy and allow MSRV bumps in patch releases +- Update to `digest` v0.11 +- Replace type aliases with newtypes ([#186]) + +### Removed +- `std` and `reset` crate features ([#186]) + +[#186]: https://github.com/RustCrypto/MACs/pull/186 + ## 0.12.1 (2022-02-17) ### Fixed - Minimal versions build ([#108]) diff --git a/hmac/Cargo.toml b/hmac/Cargo.toml index 7c4239b..cc45a20 100644 --- a/hmac/Cargo.toml +++ b/hmac/Cargo.toml @@ -24,8 +24,7 @@ streebog = { version = "=0.11.0-pre.5", default-features = false } hex-literal = "1" [features] -std = ["digest/std"] -reset = [] # Enable ability to reset HMAC instances +zeroize = ["digest/zeroize"] [package.metadata.docs.rs] all-features = true diff --git a/hmac/src/block_api.rs b/hmac/src/block_api.rs new file mode 100644 index 0000000..bb69945 --- /dev/null +++ b/hmac/src/block_api.rs @@ -0,0 +1,241 @@ +use crate::utils::{IPAD, OPAD, get_der_key}; +use core::{fmt, slice}; +use digest::{ + Digest, HashMarker, InvalidLength, KeyInit, MacMarker, Output, Reset, + block_api::{ + AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreProxy, FixedOutputCore, + OutputSizeUser, UpdateCore, + }, + block_buffer::Eager, + crypto_common::{Key, KeySizeUser}, +}; + +/// Trait implemented by eager hashes which expose their block-level core. +pub trait EagerHash: BlockSizeUser + Digest { + /// Block-level core type of the hash. + type Core: HashMarker + + UpdateCore + + FixedOutputCore + + BlockSizeUser::BlockSize> + + BufferKindUser + + Default + + Clone; +} + +impl EagerHash for T +where + T: CoreProxy + BlockSizeUser + Digest, + ::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BlockSizeUser::BlockSize> + + BufferKindUser + + Default + + Clone, +{ + type Core = T::Core; +} + +/// Generic core HMAC instance, which operates over blocks. +pub struct HmacCore { + digest: D::Core, + opad_digest: D::Core, +} + +impl Clone for HmacCore { + fn clone(&self) -> Self { + Self { + digest: self.digest.clone(), + opad_digest: self.opad_digest.clone(), + } + } +} + +impl MacMarker for HmacCore {} + +impl BufferKindUser for HmacCore { + type BufferKind = Eager; +} + +impl KeySizeUser for HmacCore { + type KeySize = <::Core as BlockSizeUser>::BlockSize; +} + +impl BlockSizeUser for HmacCore { + type BlockSize = <::Core as BlockSizeUser>::BlockSize; +} + +impl OutputSizeUser for HmacCore { + type OutputSize = <::Core as OutputSizeUser>::OutputSize; +} + +impl KeyInit for HmacCore { + #[inline(always)] + fn new(key: &Key) -> Self { + Self::new_from_slice(key.as_slice()).unwrap() + } + + #[inline(always)] + fn new_from_slice(key: &[u8]) -> Result { + let mut buf = get_der_key::(key); + buf.iter_mut().for_each(|b: &mut u8| *b ^= IPAD); + + let mut digest = D::Core::default(); + digest.update_blocks(slice::from_ref(&buf)); + + buf.iter_mut().for_each(|b: &mut u8| *b ^= IPAD ^ OPAD); + + let mut opad_digest = D::Core::default(); + opad_digest.update_blocks(slice::from_ref(&buf)); + + Ok(Self { + opad_digest, + digest, + }) + } +} + +impl UpdateCore for HmacCore { + #[inline(always)] + fn update_blocks(&mut self, blocks: &[Block]) { + self.digest.update_blocks(blocks); + } +} + +impl FixedOutputCore for HmacCore { + #[inline(always)] + fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output) { + let mut hash = Output::::default(); + self.digest.finalize_fixed_core(buffer, &mut hash); + // finalize_fixed_core should reset the buffer as well, but + // to be extra safe we reset it explicitly again. + buffer.reset(); + let h = &mut self.opad_digest; + buffer.digest_blocks(&hash, |b| h.update_blocks(b)); + h.finalize_fixed_core(buffer, out); + } +} + +impl AlgorithmName for HmacCore { + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Hmac<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for HmacCore +where + D::Core: AlgorithmName, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("HmacCore { ... }") + } +} + +/// Generic core HMAC instance, which operates over blocks. +pub struct HmacResetCore { + digest: D::Core, + opad_digest: D::Core, + ipad_digest: D::Core, +} + +impl Clone for HmacResetCore { + fn clone(&self) -> Self { + Self { + digest: self.digest.clone(), + opad_digest: self.opad_digest.clone(), + ipad_digest: self.ipad_digest.clone(), + } + } +} + +impl MacMarker for HmacResetCore {} + +impl BufferKindUser for HmacResetCore { + type BufferKind = Eager; +} + +impl KeySizeUser for HmacResetCore { + type KeySize = <::Core as BlockSizeUser>::BlockSize; +} + +impl BlockSizeUser for HmacResetCore { + type BlockSize = <::Core as BlockSizeUser>::BlockSize; +} + +impl OutputSizeUser for HmacResetCore { + type OutputSize = <::Core as OutputSizeUser>::OutputSize; +} + +impl KeyInit for HmacResetCore { + #[inline(always)] + fn new(key: &Key) -> Self { + Self::new_from_slice(key.as_slice()).unwrap() + } + + #[inline(always)] + fn new_from_slice(key: &[u8]) -> Result { + let mut buf = get_der_key::(key); + buf.iter_mut().for_each(|b: &mut u8| *b ^= IPAD); + + let mut digest = D::Core::default(); + digest.update_blocks(slice::from_ref(&buf)); + + buf.iter_mut().for_each(|b: &mut u8| *b ^= IPAD ^ OPAD); + + let mut opad_digest = D::Core::default(); + opad_digest.update_blocks(slice::from_ref(&buf)); + + Ok(Self { + ipad_digest: digest.clone(), + opad_digest, + digest, + }) + } +} + +impl UpdateCore for HmacResetCore { + #[inline(always)] + fn update_blocks(&mut self, blocks: &[Block]) { + self.digest.update_blocks(blocks); + } +} + +impl FixedOutputCore for HmacResetCore { + #[inline(always)] + fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output) { + let mut hash = Output::::default(); + self.digest.finalize_fixed_core(buffer, &mut hash); + // finalize_fixed_core should reset the buffer as well, but + // to be extra safe we reset it explicitly again. + buffer.reset(); + let mut h = self.opad_digest.clone(); + buffer.digest_blocks(&hash, |b| h.update_blocks(b)); + h.finalize_fixed_core(buffer, out); + } +} + +impl Reset for HmacResetCore { + #[inline(always)] + fn reset(&mut self) { + self.digest = self.ipad_digest.clone(); + } +} + +impl AlgorithmName for HmacResetCore { + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Hmac<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for HmacResetCore +where + D::Core: AlgorithmName, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("HmacResetCore { ... }") + } +} diff --git a/hmac/src/lib.rs b/hmac/src/lib.rs index 37efc49..3389646 100644 --- a/hmac/src/lib.rs +++ b/hmac/src/lib.rs @@ -4,13 +4,25 @@ //! implements the [`digest`] crate traits. You can find compatible crates //! (e.g. [`sha2`]) in the [`RustCrypto/hashes`] repository. //! -//! This crate provides two HMAC implementation [`Hmac`] and [`SimpleHmac`]. -//! The first one is a buffered wrapper around block-level [`HmacCore`]. -//! Internally it uses efficient state representation, but works only with +//! This crate provides four HMAC implementations: [`Hmac`], [`HmacReset`], +//! [`SimpleHmac`], and [`SimpleHmacReset`]. +//! +//! The first two types are buffered wrappers around block-level +//! [`block_api::HmacCore`] and [`block_api::HmacResetCore`] types respectively. +//! Internally they uses efficient state representation, but work only with //! hash functions which expose block-level API and consume blocks eagerly //! (e.g. it will not work with the BLAKE2 family of hash functions). -//! On the other hand, [`SimpleHmac`] is a bit less efficient memory-wise, -//! but works with all hash functions which implement the [`Digest`] trait. +//! +//! On the other hand, [`SimpleHmac`] and [`SimpleHmacReset`] are a bit less +//! efficient, but work with all hash functions which implement +//! the [`Digest`][digest::Digest] trait. +//! +//! [`Hmac`] and [`SimpleHmac`] do not support resetting MAC state (i.e. they +//! do not implement the [`Reset`] and [`FixedOutputReset`] traits). Use +//! [`HmacReset`] or [`SimpleHmacReset`] if you want to reuse MAC state. +//! +//! [`Reset`]: digest::Reset +//! [`FixedOutputReset`]: digest::FixedOutputReset //! //! # Examples //! Let us demonstrate how to use HMAC using the SHA-256 hash function. @@ -71,12 +83,6 @@ //! to remove potential panic. This is done by truncating hash output to the hash //! block size if needed. //! -//! # Crate features -//! - `std`: enables functionality dependent on `std` (e.g. implementation of -//! the [`Error`][std::error::Error] trait for error types) -//! - `reset`: enables implementation of the [`Reset`][digest::Reset] trait -//! (note that it makes HMAC states bigger) -//! //! [`digest`]: https://docs.rs/digest //! [`sha2`]: https://docs.rs/sha2 //! [`RustCrypto/hashes`]: https://github.com/RustCrypto/hashes @@ -86,50 +92,45 @@ html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg" )] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![forbid(unsafe_code)] -#![cfg_attr(docsrs, feature(doc_cfg))] -#![warn(missing_docs, rust_2018_idioms)] - -#[cfg(feature = "std")] -extern crate std; +#![warn(missing_docs)] pub use digest::{self, KeyInit, Mac}; -use digest::{ - Digest, - core_api::{Block, BlockSizeUser}, -}; - -mod optim; +/// Block-level implementation. +pub mod block_api; mod simple; +mod simple_reset; +mod utils; -pub use optim::{EagerHash, Hmac, HmacCore}; pub use simple::SimpleHmac; +pub use simple_reset::SimpleHmacReset; + +use block_api::EagerHash; +use core::fmt; +use digest::block_api::{AlgorithmName, CoreProxy}; + +digest::buffer_fixed!( + /// Generic HMAC instance. + pub struct Hmac(block_api::HmacCore); + impl: MacTraits KeyInit; +); + +impl AlgorithmName for Hmac { + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::Core::write_alg_name(f) + } +} -const IPAD: u8 = 0x36; -const OPAD: u8 = 0x5C; +digest::buffer_fixed!( + /// Generic HMAC instance with reset support. + pub struct HmacReset(block_api::HmacResetCore); + impl: ResetMacTraits KeyInit; +); -fn get_der_key(key: &[u8]) -> Block { - let mut der_key = Block::::default(); - // The key that HMAC processes must be the same as the block size of the - // underlying hash function. If the provided key is smaller than that, - // we just pad it with zeros. If its larger, we hash it and then pad it - // with zeros. - if key.len() <= der_key.len() { - der_key[..key.len()].copy_from_slice(key); - } else { - let hash = D::digest(key); - // All commonly used hash functions have block size bigger - // than output hash size, but to be extra rigorous we - // handle the potential uncommon cases as well. - // The condition is calculated at compile time, so this - // branch gets removed from the final binary. - if hash.len() <= der_key.len() { - der_key[..hash.len()].copy_from_slice(&hash); - } else { - let n = der_key.len(); - der_key.copy_from_slice(&hash[..n]); - } +impl AlgorithmName for HmacReset { + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::Core::write_alg_name(f) } - der_key } diff --git a/hmac/src/optim.rs b/hmac/src/optim.rs deleted file mode 100644 index 9d78731..0000000 --- a/hmac/src/optim.rs +++ /dev/null @@ -1,160 +0,0 @@ -use super::{IPAD, OPAD, get_der_key}; -use core::{fmt, slice}; -use digest::{ - HashMarker, InvalidLength, KeyInit, MacMarker, Output, - block_buffer::Eager, - core_api::{ - AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore, - OutputSizeUser, UpdateCore, - }, - crypto_common::{Key, KeySizeUser}, -}; - -/// Generic HMAC instance. -pub type Hmac = CoreWrapper>; - -/// Trait implemented by eager hashes which expose their block-level core. -pub trait EagerHash { - /// Block-level core type of the hash. - type Core: HashMarker - + UpdateCore - + FixedOutputCore - + BufferKindUser - + Default - + Clone; -} - -impl EagerHash for CoreWrapper -where - C: HashMarker - + UpdateCore - + FixedOutputCore - + BufferKindUser - + Default - + Clone, -{ - type Core = C; -} - -/// Generic core HMAC instance, which operates over blocks. -pub struct HmacCore { - digest: D::Core, - opad_digest: D::Core, - #[cfg(feature = "reset")] - ipad_digest: D::Core, -} - -impl Clone for HmacCore { - fn clone(&self) -> Self { - Self { - digest: self.digest.clone(), - opad_digest: self.opad_digest.clone(), - #[cfg(feature = "reset")] - ipad_digest: self.ipad_digest.clone(), - } - } -} - -impl MacMarker for HmacCore {} - -impl BufferKindUser for HmacCore { - type BufferKind = Eager; -} - -impl KeySizeUser for HmacCore { - type KeySize = <::Core as BlockSizeUser>::BlockSize; -} - -impl BlockSizeUser for HmacCore { - type BlockSize = <::Core as BlockSizeUser>::BlockSize; -} - -impl OutputSizeUser for HmacCore { - type OutputSize = <::Core as OutputSizeUser>::OutputSize; -} - -impl KeyInit for HmacCore { - #[inline(always)] - fn new(key: &Key) -> Self { - Self::new_from_slice(key.as_slice()).unwrap() - } - - #[inline(always)] - fn new_from_slice(key: &[u8]) -> Result { - let mut buf = get_der_key::>(key); - for b in buf.iter_mut() { - *b ^= IPAD; - } - let mut digest = D::Core::default(); - digest.update_blocks(slice::from_ref(&buf)); - - for b in buf.iter_mut() { - *b ^= IPAD ^ OPAD; - } - - let mut opad_digest = D::Core::default(); - opad_digest.update_blocks(slice::from_ref(&buf)); - - Ok(Self { - #[cfg(feature = "reset")] - ipad_digest: digest.clone(), - opad_digest, - digest, - }) - } -} - -impl UpdateCore for HmacCore { - #[inline(always)] - fn update_blocks(&mut self, blocks: &[Block]) { - self.digest.update_blocks(blocks); - } -} - -impl FixedOutputCore for HmacCore { - #[inline(always)] - fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output) { - let mut hash = Output::::default(); - self.digest.finalize_fixed_core(buffer, &mut hash); - // finalize_fixed_core should reset the buffer as well, but - // to be extra safe we reset it explicitly again. - buffer.reset(); - #[cfg(not(feature = "reset"))] - let h = &mut self.opad_digest; - #[cfg(feature = "reset")] - let mut h = self.opad_digest.clone(); - buffer.digest_blocks(&hash, |b| h.update_blocks(b)); - h.finalize_fixed_core(buffer, out); - } -} - -#[cfg(feature = "reset")] -#[cfg_attr(docsrs, doc(cfg(feature = "reset")))] -impl digest::Reset for HmacCore { - #[inline(always)] - fn reset(&mut self) { - self.digest = self.ipad_digest.clone(); - } -} - -impl AlgorithmName for HmacCore -where - D::Core: AlgorithmName, -{ - fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Hmac<")?; - ::write_alg_name(f)?; - f.write_str(">") - } -} - -impl fmt::Debug for HmacCore -where - D::Core: AlgorithmName, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("HmacCore<")?; - ::write_alg_name(f)?; - f.write_str("> { ... }") - } -} diff --git a/hmac/src/simple.rs b/hmac/src/simple.rs index dc39be6..f81d045 100644 --- a/hmac/src/simple.rs +++ b/hmac/src/simple.rs @@ -1,11 +1,9 @@ -use super::{IPAD, OPAD, get_der_key}; +use crate::utils::{IPAD, OPAD, get_der_key}; use core::fmt; use digest::{ Digest, FixedOutput, KeyInit, MacMarker, Output, OutputSizeUser, Update, crypto_common::{Block, BlockSizeUser, InvalidLength, Key, KeySizeUser}, }; -#[cfg(feature = "reset")] -use digest::{FixedOutputReset, Reset}; /// Simplified HMAC instance able to operate over hash functions /// which do not expose block-level API and hash functions which @@ -14,8 +12,6 @@ use digest::{FixedOutputReset, Reset}; pub struct SimpleHmac { digest: D, opad_key: Block, - #[cfg(feature = "reset")] - ipad_key: Block, } impl KeySizeUser for SimpleHmac { @@ -31,24 +27,17 @@ impl KeyInit for SimpleHmac { #[inline] fn new_from_slice(key: &[u8]) -> Result { - let der_key = get_der_key::(key); - let mut ipad_key = der_key.clone(); - for b in ipad_key.iter_mut() { - *b ^= IPAD; - } + let mut buf = get_der_key::(key); + buf.iter_mut().for_each(|b: &mut u8| *b ^= IPAD); + let mut digest = D::new(); - digest.update(&ipad_key); + digest.update(&buf); - let mut opad_key = der_key; - for b in opad_key.iter_mut() { - *b ^= OPAD; - } + buf.iter_mut().for_each(|b: &mut u8| *b ^= OPAD ^ IPAD); Ok(Self { digest, - opad_key, - #[cfg(feature = "reset")] - ipad_key, + opad_key: buf, }) } } @@ -75,32 +64,6 @@ impl FixedOutput for SimpleHmac { impl fmt::Debug for SimpleHmac { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SimpleHmac") - .field("digest", &self.digest) - // TODO: replace with `finish_non_exhaustive` on MSRV - // bump to 1.53 - .field("..", &"..") - .finish() - } -} - -#[cfg(feature = "reset")] -#[cfg_attr(docsrs, doc(cfg(feature = "reset")))] -impl Reset for SimpleHmac { - fn reset(&mut self) { - Reset::reset(&mut self.digest); - self.digest.update(&self.ipad_key); - } -} - -#[cfg(feature = "reset")] -#[cfg_attr(docsrs, doc(cfg(feature = "reset")))] -impl FixedOutputReset for SimpleHmac { - fn finalize_into_reset(&mut self, out: &mut Output) { - let mut h = D::new(); - Update::update(&mut h, &self.opad_key); - Update::update(&mut h, &self.digest.finalize_reset()); - Update::update(&mut self.digest, &self.ipad_key); - Digest::finalize_into(h, out); + f.write_str("SimpleHmac { ... }") } } diff --git a/hmac/src/simple_reset.rs b/hmac/src/simple_reset.rs new file mode 100644 index 0000000..dd72f45 --- /dev/null +++ b/hmac/src/simple_reset.rs @@ -0,0 +1,92 @@ +use crate::utils::{IPAD, OPAD, get_der_key}; +use core::fmt; +use digest::{ + Digest, FixedOutput, KeyInit, MacMarker, Output, OutputSizeUser, Update, + crypto_common::{Block, BlockSizeUser, InvalidLength, Key, KeySizeUser}, +}; +use digest::{FixedOutputReset, Reset}; + +/// Simplified HMAC instance with reset support able to operate +/// over hash functions which do not expose block-level API and +/// hash functions which process blocks lazily (e.g. BLAKE2). +#[derive(Clone)] +pub struct SimpleHmacReset { + digest: D, + opad_key: Block, + ipad_key: Block, +} + +impl KeySizeUser for SimpleHmacReset { + type KeySize = D::BlockSize; +} + +impl MacMarker for SimpleHmacReset {} + +impl KeyInit for SimpleHmacReset { + fn new(key: &Key) -> Self { + Self::new_from_slice(key.as_slice()).unwrap() + } + + #[inline] + fn new_from_slice(key: &[u8]) -> Result { + let der_key = get_der_key::(key); + + let mut ipad_key = der_key.clone(); + ipad_key.iter_mut().for_each(|b: &mut u8| *b ^= IPAD); + + let mut digest = D::new(); + digest.update(&ipad_key); + + let mut opad_key = der_key; + opad_key.iter_mut().for_each(|b: &mut u8| *b ^= OPAD); + + Ok(Self { + digest, + opad_key, + ipad_key, + }) + } +} + +impl Update for SimpleHmacReset { + #[inline(always)] + fn update(&mut self, data: &[u8]) { + self.digest.update(data); + } +} + +impl OutputSizeUser for SimpleHmacReset { + type OutputSize = D::OutputSize; +} + +impl FixedOutput for SimpleHmacReset { + fn finalize_into(self, out: &mut Output) { + let mut h = D::new(); + h.update(&self.opad_key); + h.update(self.digest.finalize()); + h.finalize_into(out); + } +} + +impl fmt::Debug for SimpleHmacReset { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("SimpleResetHmac") + } +} + +impl Reset for SimpleHmacReset { + fn reset(&mut self) { + Reset::reset(&mut self.digest); + self.digest.update(&self.ipad_key); + } +} + +impl FixedOutputReset for SimpleHmacReset { + fn finalize_into_reset(&mut self, out: &mut Output) { + let mut h = D::new(); + Update::update(&mut h, &self.opad_key); + Update::update(&mut h, &self.digest.finalize_reset()); + Update::update(&mut self.digest, &self.ipad_key); + Digest::finalize_into(h, out); + } +} diff --git a/hmac/src/utils.rs b/hmac/src/utils.rs new file mode 100644 index 0000000..833108c --- /dev/null +++ b/hmac/src/utils.rs @@ -0,0 +1,32 @@ +use digest::{ + Digest, + block_api::{Block, BlockSizeUser}, +}; + +pub(crate) const IPAD: u8 = 0x36; +pub(crate) const OPAD: u8 = 0x5C; + +pub(crate) fn get_der_key(key: &[u8]) -> Block { + let mut der_key = Block::::default(); + // The key that HMAC processes must be the same as the block size of the + // underlying hash function. If the provided key is smaller than that, + // we just pad it with zeros. If its larger, we hash it and then pad it + // with zeros. + if key.len() <= der_key.len() { + der_key[..key.len()].copy_from_slice(key); + } else { + let hash = D::digest(key); + // All commonly used hash functions have block size bigger + // than output hash size, but to be extra rigorous we + // handle the potential uncommon cases as well. + // The condition is calculated at compile time, so this + // branch gets removed from the final binary. + if hash.len() <= der_key.len() { + der_key[..hash.len()].copy_from_slice(&hash); + } else { + let n = der_key.len(); + der_key.copy_from_slice(&hash[..n]); + } + } + der_key +} diff --git a/hmac/tests/mod.rs b/hmac/tests/mod.rs index 7057384..82f39cc 100644 --- a/hmac/tests/mod.rs +++ b/hmac/tests/mod.rs @@ -1,97 +1,56 @@ -#[cfg(not(feature = "reset"))] -use digest::new_mac_test as test; -#[cfg(feature = "reset")] -use digest::new_resettable_mac_test as test; -use hmac::{Hmac, HmacCore, SimpleHmac}; -use sha1::Sha1; -use sha2::{Sha224, Sha256, Sha384, Sha512}; -use streebog::{Streebog256, Streebog512}; - -#[test] -fn test_debug_impls() { - fn needs_debug() {} - - needs_debug::>(); - needs_debug::>(); - needs_debug::>(); +macro_rules! test { + ($mod_name:ident, $test_name:expr, $hash:ty $(, $t:ident)?) => { + mod $mod_name { + digest::new_mac_test!(hmac, $test_name, hmac::Hmac<$hash> $(, $t)?); + digest::new_mac_test!(simple_hmac, $test_name, hmac::SimpleHmac<$hash> $(, $t)?); + digest::new_resettable_mac_test!( + hmac_reset, + $test_name, + hmac::HmacReset<$hash> + $(, $t)? + ); + digest::new_resettable_mac_test!( + simple_reset_hmac, + $test_name, + hmac::SimpleHmacReset<$hash> + $(, $t)? + ); + } + }; } // Test vectors from RFC 2104, plus wiki test -test!(hmac_md5_rfc2104, "md5", Hmac); -test!(hmac_md5_rfc2104_simple, "md5", SimpleHmac); +test!(md5_rfc2104, "md5", md5::Md5); // Test vectors from RFC 4231 -test!(hmac_sha224_rfc4231, "sha224", Hmac); -test!(hmac_sha256_rfc4231, "sha256", Hmac); -test!(hmac_sha384_rfc4231, "sha384", Hmac); -test!(hmac_sha512_rfc4231, "sha512", Hmac); -test!(hmac_sha224_rfc4231_simple, "sha224", SimpleHmac); -test!(hmac_sha256_rfc4231_simple, "sha256", SimpleHmac); -test!(hmac_sha384_rfc4231_simple, "sha384", SimpleHmac); -test!(hmac_sha512_rfc4231_simple, "sha512", SimpleHmac); +test!(sha224_rfc4231, "sha224", sha2::Sha224); +test!(sha256_rfc4231, "sha256", sha2::Sha256); +test!(sha384_rfc4231, "sha384", sha2::Sha384); +test!(sha512_rfc4231, "sha512", sha2::Sha512); // Test vectors from R 50.1.113-2016: -// https://tc26.ru/standard/rs/Р 50.1.113-2016.pdf -test!(hmac_streebog256, "streebog256", Hmac); -test!(hmac_streebog512, "streebog512", Hmac); -test!( - hmac_streebog256_simple, - "streebog256", - SimpleHmac -); -test!( - hmac_streebog512_simple, - "streebog512", - SimpleHmac -); +// https://tc26.ru/standard/rs/Р%2050.1.113-2016.pdf +test!(treebog256, "streebog256", streebog::Streebog256); +test!(streebog512, "streebog512", streebog::Streebog512); // Tests from Project Wycheproof: // https://github.com/google/wycheproof +test!(sha1_wycheproof, "wycheproof-sha1", sha1::Sha1, trunc_left); test!( - hmac_sha1_wycheproof, - "wycheproof-sha1", - Hmac, - trunc_left, -); -test!( - hmac_sha256_wycheproof, - "wycheproof-sha256", - Hmac, - trunc_left, -); -test!( - hmac_sha384_wycheproof, - "wycheproof-sha384", - Hmac, - trunc_left, -); -test!( - hmac_sha512_wycheproof, - "wycheproof-sha512", - Hmac, - trunc_left, -); -test!( - hmac_sha1_wycheproof_simple, - "wycheproof-sha1", - SimpleHmac, - trunc_left, -); -test!( - hmac_sha256_wycheproof_simple, + sha256_wycheproof, "wycheproof-sha256", - SimpleHmac, - trunc_left, + sha2::Sha256, + trunc_left ); test!( - hmac_sha384_wycheproof_simple, + sha384_wycheproof, "wycheproof-sha384", - SimpleHmac, - trunc_left, + sha2::Sha384, + trunc_left ); test!( - hmac_sha512_wycheproof_simple, + sha512_wycheproof, "wycheproof-sha512", - SimpleHmac, - trunc_left, + sha2::Sha512, + trunc_left ); diff --git a/pmac/CHANGELOG.md b/pmac/CHANGELOG.md index 2cee94a..c4a7a3e 100644 --- a/pmac/CHANGELOG.md +++ b/pmac/CHANGELOG.md @@ -5,6 +5,19 @@ 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). +## 0.8.0 (UNRELEASED) +### Changed +- Edition changed to 2024 and MSRV bumped to 1.85 +- Relax MSRV policy and allow MSRV bumps in patch releases +- Update to `digest` v0.11 +- Update to `cipher` v0.5 +- Replace type aliases with newtypes ([#186]) + +### Removed +- `std` crate feature ([#186]) + +[#186]: https://github.com/RustCrypto/MACs/pull/186 + ## 0.7.1 (2022-02-17) ### Fixed - Minimal versions build ([#108]) diff --git a/pmac/Cargo.toml b/pmac/Cargo.toml index bdb0fed..2a65d3b 100644 --- a/pmac/Cargo.toml +++ b/pmac/Cargo.toml @@ -22,9 +22,7 @@ aes = "0.9.0-pre.3" digest = { version = "=0.11.0-pre.10", features = ["dev"] } [features] -std = ["digest/std"] -zeroize = ["cipher/zeroize"] +zeroize = ["cipher/zeroize", "digest/zeroize"] [package.metadata.docs.rs] all-features = true -rustdoc-args = ["--cfg", "docsrs"] diff --git a/pmac/src/block_api.rs b/pmac/src/block_api.rs new file mode 100644 index 0000000..6b1faea --- /dev/null +++ b/pmac/src/block_api.rs @@ -0,0 +1,236 @@ +use cipher::{BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, ParBlocks}; +use core::fmt; +use dbl::Dbl; +use digest::{ + MacMarker, Output, OutputSizeUser, Reset, + array::{Array, ArraySize}, + block_api::{ + AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, FixedOutputCore, Lazy, + UpdateCore, + }, + crypto_common::{InnerInit, InnerUser}, + typenum::Unsigned, +}; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; + +/// Generic PMAC instance +/// +/// `LC_SIZE` regulates size of pre-computed table used in PMAC computation. +/// With `LC_SIZE = 20` and for 128-bit block cipher the table is sufficient +/// for 16*2^20 = 16 MiB of input data. For longer messages the `l` value will +/// be computed on the fly from the last table value, which will be a bit less +/// efficient. +#[derive(Clone)] +pub struct PmacCore { + state: PmacState, + cipher: C, +} + +#[derive(Clone)] +struct PmacState { + counter: usize, + l_inv: Block, + l_cache: [Block; LC_SIZE], + tag: Block, + offset: Block, +} + +impl PmacState { + #[inline(always)] + fn next_offset(&mut self) -> &Block { + let ntz = self.counter.trailing_zeros() as usize; + self.counter += 1; + let l = if ntz < LC_SIZE { + self.l_cache[ntz].clone() + } else { + let mut block = self.l_cache[LC_SIZE - 1].clone(); + for _ in LC_SIZE - 1..ntz { + block = C::dbl(block); + } + block + }; + xor(&mut self.offset, &l); + &self.offset + } +} + +impl Drop for PmacState { + fn drop(&mut self) { + #[cfg(feature = "zeroize")] + { + self.counter.zeroize(); + self.l_inv.zeroize(); + self.l_cache.iter_mut().for_each(|c| c.zeroize()); + self.tag.zeroize(); + self.offset.zeroize(); + } + } +} + +impl BlockSizeUser for PmacCore { + type BlockSize = C::BlockSize; +} + +impl OutputSizeUser for PmacCore { + type OutputSize = C::BlockSize; +} + +impl InnerUser for PmacCore { + type Inner = C; +} + +impl MacMarker for PmacCore {} + +impl Reset for PmacCore { + #[inline(always)] + fn reset(&mut self) { + self.state.tag = Default::default(); + self.state.offset = Default::default(); + self.state.counter = 1; + } +} + +impl BufferKindUser for PmacCore { + type BufferKind = Lazy; +} + +impl AlgorithmName for PmacCore { + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Pmac<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for PmacCore { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("PmacCore { ... }") + } +} + +impl InnerInit for PmacCore { + #[inline] + fn inner_init(cipher: C) -> Self { + let mut l = Default::default(); + cipher.encrypt_block(&mut l); + let l_inv = C::inv_dbl(l.clone()); + + let l_cache = [(); LC_SIZE].map(|_| { + let next_l = C::dbl(l.clone()); + core::mem::replace(&mut l, next_l) + }); + + let state = PmacState { + l_cache, + l_inv, + tag: Default::default(), + offset: Default::default(), + counter: 1, + }; + Self { cipher, state } + } +} + +impl UpdateCore for PmacCore { + #[inline] + fn update_blocks(&mut self, blocks: &[Block]) { + struct Closure<'a, C: PmacCipher, const LC_SIZE: usize> { + state: &'a mut PmacState, + blocks: &'a [Block], + } + + impl BlockSizeUser for Closure<'_, C, LC_SIZE> { + type BlockSize = C::BlockSize; + } + + impl BlockCipherEncClosure for Closure<'_, C, LC_SIZE> { + #[inline(always)] + fn call>(self, backend: &B) { + let Self { mut blocks, state } = self; + if B::ParBlocksSize::USIZE > 1 { + // TODO: replace with `slice::as_chunks` on stabilization + // and migration to const generics + let mut iter = blocks.chunks_exact(B::ParBlocksSize::USIZE); + for chunk in &mut iter { + let mut tmp = ParBlocks::::try_from(chunk).expect("size mismatch"); + + for block in tmp.iter_mut() { + xor(block, state.next_offset()); + } + + backend.encrypt_par_blocks((&mut tmp).into()); + + for t in tmp.iter() { + xor(&mut state.tag, t); + } + } + blocks = iter.remainder(); + } + + for block in blocks { + let mut block = block.clone(); + xor(&mut block, state.next_offset()); + backend.encrypt_block((&mut block).into()); + xor(&mut state.tag, &block); + } + } + } + + let Self { cipher, state } = self; + cipher.encrypt_with_backend(Closure { blocks, state }) + } +} + +impl FixedOutputCore for PmacCore { + #[inline] + fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output) { + let Self { + cipher, + state: PmacState { tag, l_inv, .. }, + } = self; + let pos = buffer.get_pos(); + let buf = buffer.pad_with_zeros(); + if pos == buf.len() { + xor(tag, &buf); + xor(tag, l_inv); + } else { + tag[pos] ^= 0x80; + xor(tag, &buf); + } + cipher.encrypt_block_b2b(tag, out); + } +} + +#[cfg(feature = "zeroize")] +impl ZeroizeOnDrop for PmacCore {} + +#[inline(always)] +fn xor(buf: &mut Array, data: &Array) { + for i in 0..N::USIZE { + buf[i] ^= data[i]; + } +} + +/// Helper trait implemented for cipher supported by CMAC +pub trait PmacCipher: BlockSizeUser + BlockCipherEncrypt + Clone { + /// Double block. See the [`Dbl`] trait docs for more information. + fn dbl(block: Block) -> Block; + /// Reverse double block.. See the [`Dbl`] trait docs for more information. + fn inv_dbl(block: Block) -> Block; +} + +impl PmacCipher for C +where + Self: BlockSizeUser + BlockCipherEncrypt + Clone, + Block: Dbl, +{ + fn dbl(block: Block) -> Block { + block.dbl() + } + + fn inv_dbl(block: Block) -> Block { + block.inv_dbl() + } +} diff --git a/pmac/src/lib.rs b/pmac/src/lib.rs index 936e70e..a3fb675 100644 --- a/pmac/src/lib.rs +++ b/pmac/src/lib.rs @@ -44,324 +44,27 @@ html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg" )] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![forbid(unsafe_code)] -#![cfg_attr(docsrs, feature(doc_cfg))] -#![warn(missing_docs, rust_2018_idioms)] - -#[cfg(feature = "std")] -extern crate std; +#![warn(missing_docs)] pub use digest::{self, KeyInit, Mac}; -use cipher::{BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, ParBlocks}; -use core::fmt; -use dbl::Dbl; -use digest::{ - MacMarker, Output, OutputSizeUser, Reset, - array::{ - Array, ArraySize, - typenum::{IsLess, Le, NonZero, U256, Unsigned}, - }, - block_buffer::Lazy, - core_api::{ - AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore, - UpdateCore, - }, - crypto_common::{BlockSizes, InnerInit, InnerUser}, -}; - -#[cfg(feature = "zeroize")] -use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; - -/// Generic CMAC instance. -pub type Pmac = CoreWrapper>; - -/// Generic PMAC instance -/// -/// `LC_SIZE` regulates size of pre-computed table used in PMAC computation. -/// With `LC_SIZE = 20` and for 128-bit block cipher the table is sufficient -/// for 16*2^20 = 16 MiB of input data. For longer messages the `l` value will -/// be computed on the fly from the last table value, which will be a bit less -/// efficient. -// TODO: make LC_SIZE default to 20 on stabilization of -// https://github.com/rust-lang/rust/issues/44580 -#[derive(Clone)] -pub struct PmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, -{ - state: PmacState, - cipher: C, -} - -#[derive(Clone)] -struct PmacState -where - N: BlockSizes, - Array: Dbl, -{ - counter: usize, - l_inv: Block, - l_cache: [Block; LC_SIZE], - tag: Block, - offset: Block, -} - -impl BlockSizeUser for PmacState -where - N: BlockSizes, - Array: Dbl, -{ - type BlockSize = N; -} +/// Block-level implementation. +pub mod block_api; -impl PmacState -where - N: BlockSizes, - Array: Dbl, -{ - #[inline(always)] - fn next_offset(&mut self) -> &Block { - let ntz = self.counter.trailing_zeros() as usize; - self.counter += 1; - let l = if ntz < LC_SIZE { - self.l_cache[ntz].clone() - } else { - let mut block = self.l_cache[LC_SIZE - 1].clone(); - for _ in LC_SIZE - 1..ntz { - block = block.dbl(); - } - block - }; - xor(&mut self.offset, &l); - &self.offset - } -} - -#[cfg(feature = "zeroize")] -impl Drop for PmacState -where - N: BlockSizes, - Array: Dbl, -{ - fn drop(&mut self) { - self.counter.zeroize(); - self.l_inv.zeroize(); - self.l_cache.iter_mut().for_each(|c| c.zeroize()); - self.tag.zeroize(); - self.offset.zeroize(); - } -} - -impl BlockSizeUser for PmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, -{ - type BlockSize = C::BlockSize; -} - -impl OutputSizeUser for PmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, -{ - type OutputSize = C::BlockSize; -} - -impl InnerUser for PmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, -{ - type Inner = C; -} - -impl MacMarker for PmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, -{ -} - -impl Reset for PmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, -{ - #[inline(always)] - fn reset(&mut self) { - self.state.tag = Default::default(); - self.state.offset = Default::default(); - self.state.counter = 1; - } -} +use block_api::PmacCipher; +use core::fmt; +use digest::block_api::{AlgorithmName, CoreProxy}; -impl BufferKindUser for PmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, -{ - type BufferKind = Lazy; -} +digest::buffer_fixed!( + /// Generic PMAC instance with `LC_SIZE` = 20. + pub struct Pmac(block_api::PmacCore); + impl: ResetMacTraits InnerInit; +); -impl AlgorithmName for PmacCore -where - C: BlockCipherEncrypt + Clone + AlgorithmName, - Block: Dbl, -{ +impl AlgorithmName for Pmac { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Pmac<")?; - ::write_alg_name(f)?; - f.write_str(">") - } -} - -impl fmt::Debug for PmacCore -where - C: BlockCipherEncrypt + Clone + AlgorithmName, - Block: Dbl, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("PmacCore<")?; - ::write_alg_name(f)?; - f.write_str("> { ... }") - } -} - -impl InnerInit for PmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, -{ - #[inline] - fn inner_init(cipher: C) -> Self { - let mut l = Default::default(); - cipher.encrypt_block(&mut l); - let l_inv = l.clone().inv_dbl(); - - let l_cache = [(); LC_SIZE].map(|_| { - let next_l = l.clone().dbl(); - core::mem::replace(&mut l, next_l) - }); - - let state = PmacState { - l_cache, - l_inv, - tag: Default::default(), - offset: Default::default(), - counter: 1, - }; - Self { cipher, state } - } -} - -impl UpdateCore for PmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, -{ - #[inline] - fn update_blocks(&mut self, blocks: &[Block]) { - struct Closure<'a, N, const LC_SIZE: usize> - where - N: BlockSizes, - Array: Dbl, - { - state: &'a mut PmacState, - blocks: &'a [Block], - } - - impl BlockSizeUser for Closure<'_, N, LC_SIZE> - where - N: BlockSizes, - Array: Dbl, - { - type BlockSize = N; - } - - impl BlockCipherEncClosure for Closure<'_, N, LC_SIZE> - where - N: BlockSizes, - Array: Dbl, - { - #[inline(always)] - fn call>(self, backend: &B) { - let Self { mut blocks, state } = self; - if B::ParBlocksSize::USIZE > 1 { - // TODO: replace with `slice::as_chunks` on stabilization - // and migration to const generics - let mut iter = blocks.chunks_exact(B::ParBlocksSize::USIZE); - for chunk in &mut iter { - let mut tmp = ParBlocks::::try_from(chunk).expect("size mismatch"); - - for block in tmp.iter_mut() { - xor(block, state.next_offset()); - } - - backend.encrypt_par_blocks((&mut tmp).into()); - - for t in tmp.iter() { - xor(&mut state.tag, t); - } - } - blocks = iter.remainder(); - } - - for block in blocks { - let mut block = block.clone(); - xor(&mut block, state.next_offset()); - backend.encrypt_block((&mut block).into()); - xor(&mut state.tag, &block); - } - } - } - - let Self { cipher, state } = self; - cipher.encrypt_with_backend(Closure { blocks, state }) - } -} - -impl FixedOutputCore for PmacCore -where - C: BlockCipherEncrypt + Clone, - Block: Dbl, - C::BlockSize: IsLess, - Le: NonZero, -{ - #[inline] - fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output) { - let Self { - cipher, - state: PmacState { tag, l_inv, .. }, - } = self; - let pos = buffer.get_pos(); - let buf = buffer.pad_with_zeros(); - if pos == buf.len() { - xor(tag, &buf); - xor(tag, l_inv); - } else { - tag[pos] ^= 0x80; - xor(tag, &buf); - } - cipher.encrypt_block_b2b(tag, out); - } -} - -#[cfg(feature = "zeroize")] -impl ZeroizeOnDrop for PmacCore -where - C: BlockCipherEncrypt + Clone + ZeroizeOnDrop, - Block: Dbl, - C::BlockSize: IsLess, - Le: NonZero, -{ -} - -#[inline(always)] -fn xor(buf: &mut Array, data: &Array) { - for i in 0..N::USIZE { - buf[i] ^= data[i]; + ::Core::write_alg_name(f) } } diff --git a/retail-mac/CHANGELOG.md b/retail-mac/CHANGELOG.md index dae238b..956a57d 100644 --- a/retail-mac/CHANGELOG.md +++ b/retail-mac/CHANGELOG.md @@ -5,7 +5,5 @@ 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). -## 0.1.0 (2024-??-??) -- Initial release ([#170]) - -[#170]: https://github.com/RustCrypto/MACs/pull/170 +## 0.1.0 (UNRELEASED) +- Initial release diff --git a/retail-mac/Cargo.toml b/retail-mac/Cargo.toml index 02ed7ba..3cc7ad9 100644 --- a/retail-mac/Cargo.toml +++ b/retail-mac/Cargo.toml @@ -23,9 +23,7 @@ aes = "0.9.0-pre.2" des = "0.9.0-pre.2" [features] -std = ["digest/std"] -zeroize = ["cipher/zeroize"] +zeroize = ["cipher/zeroize", "digest/zeroize"] [package.metadata.docs.rs] all-features = true -rustdoc-args = ["--cfg", "docsrs"] diff --git a/retail-mac/src/block_api.rs b/retail-mac/src/block_api.rs new file mode 100644 index 0000000..287d209 --- /dev/null +++ b/retail-mac/src/block_api.rs @@ -0,0 +1,191 @@ +use cipher::{ + BlockCipherDecrypt, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, + InvalidLength, KeySizeUser, +}; +use core::{fmt, ops::Mul}; +use digest::{ + Key, KeyInit, MacMarker, Output, OutputSizeUser, Reset, + array::{Array, ArraySize}, + block_api::{ + AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, Eager, FixedOutputCore, + UpdateCore, + }, + crypto_common::BlockSizes, + typenum::{Prod, U2}, +}; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; + +/// Generic core Retail MAC instance, which operates over blocks. +#[derive(Clone)] +pub struct RetailMacCore +where + C: BlockCipherEncrypt + BlockCipherDecrypt + Clone, +{ + cipher: C, + cipher_prime: C, + state: Block, +} + +impl BlockSizeUser for RetailMacCore +where + C: BlockCipherEncrypt + BlockCipherDecrypt + Clone, +{ + type BlockSize = C::BlockSize; +} + +impl OutputSizeUser for RetailMacCore +where + C: BlockCipherEncrypt + BlockCipherDecrypt + Clone, +{ + type OutputSize = C::BlockSize; +} + +impl KeySizeUser for RetailMacCore +where + C: BlockCipherEncrypt + BlockCipherDecrypt + Clone, + ::BlockSize: Mul, + Prod<::BlockSize, U2>: ArraySize, +{ + type KeySize = Prod<::BlockSize, U2>; +} + +impl MacMarker for RetailMacCore where C: BlockCipherEncrypt + BlockCipherDecrypt + Clone {} + +impl BufferKindUser for RetailMacCore +where + C: BlockCipherEncrypt + BlockCipherDecrypt + Clone, +{ + type BufferKind = Eager; +} + +impl KeyInit for RetailMacCore +where + C: BlockCipherEncrypt + BlockCipherDecrypt + Clone + KeyInit, + ::BlockSize: Mul, + Prod<::BlockSize, U2>: ArraySize, +{ + #[inline(always)] + fn new(key: &Key) -> Self { + Self::new_from_slice(key.as_slice()).unwrap() + } + + #[inline(always)] + fn new_from_slice(key: &[u8]) -> Result { + let cipher = C::new_from_slice(&key[..key.len() / 2])?; + let cipher_prime = C::new_from_slice(&key[key.len() / 2..])?; + Ok(Self { + cipher, + cipher_prime, + state: Block::::default(), + }) + } +} + +impl UpdateCore for RetailMacCore +where + C: BlockCipherEncrypt + BlockCipherDecrypt + Clone, +{ + #[inline] + fn update_blocks(&mut self, blocks: &[Block]) { + struct Closure<'a, N: BlockSizes> { + state: &'a mut Block, + blocks: &'a [Block], + } + + impl BlockSizeUser for Closure<'_, N> { + type BlockSize = N; + } + + impl BlockCipherEncClosure for Closure<'_, N> { + #[inline(always)] + fn call>(self, backend: &B) { + for block in self.blocks { + xor(self.state, block); + backend.encrypt_block((self.state).into()); + } + } + } + + let Self { cipher, state, .. } = self; + cipher.encrypt_with_backend(Closure { state, blocks }) + } +} + +impl Reset for RetailMacCore +where + C: BlockCipherEncrypt + BlockCipherDecrypt + Clone, +{ + #[inline(always)] + fn reset(&mut self) { + self.state = Default::default(); + } +} + +impl FixedOutputCore for RetailMacCore +where + C: BlockCipherEncrypt + BlockCipherDecrypt + Clone, +{ + #[inline] + fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output) { + let Self { + state, + cipher, + cipher_prime, + } = self; + let pos = buffer.get_pos(); + if pos != 0 { + xor(state, &buffer.pad_with_zeros()); + cipher.encrypt_block(state); + } + cipher_prime.decrypt_block(state); + cipher.encrypt_block(state); + out.copy_from_slice(state); + } +} + +impl AlgorithmName for RetailMacCore +where + C: BlockCipherEncrypt + BlockCipherDecrypt + Clone + AlgorithmName, +{ + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("RetailMac<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for RetailMacCore +where + C: BlockCipherEncrypt + BlockCipherDecrypt + Clone + AlgorithmName, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("RetailMacCore<")?; + ::write_alg_name(f)?; + f.write_str("> { ... }") + } +} + +#[cfg(feature = "zeroize")] +impl Drop for RetailMacCore +where + C: BlockCipherEncrypt + BlockCipherDecrypt + Clone, +{ + fn drop(&mut self) { + self.state.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +impl ZeroizeOnDrop for RetailMacCore where + C: BlockCipherEncrypt + BlockCipherDecrypt + Clone + ZeroizeOnDrop +{ +} + +#[inline(always)] +fn xor(buf: &mut Array, data: &Array) { + for i in 0..N::USIZE { + buf[i] ^= data[i]; + } +} diff --git a/retail-mac/src/lib.rs b/retail-mac/src/lib.rs index 8c19121..4cf1cb0 100644 --- a/retail-mac/src/lib.rs +++ b/retail-mac/src/lib.rs @@ -2,7 +2,7 @@ //! implemented in pure Rust and generic over block cipher. //! //! **WARNING!** The algorithm has known weaknesses in case of variable-length -//! messages. See the Wikipedia article for (CBC-MAC)[CBC-MAC] for more information. +//! messages. See the Wikipedia article for [CBC-MAC] for more information. //! //! # Examples //! @@ -36,210 +36,68 @@ html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg" )] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![deny(unsafe_code)] -#![cfg_attr(docsrs, feature(doc_cfg))] -#![warn(missing_docs, rust_2018_idioms)] +#![warn(missing_docs)] pub use digest::{self, Key, KeyInit, Mac}; -use cipher::{ - BlockCipherDecrypt, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, - InvalidLength, KeySizeUser, -}; -use core::fmt; +/// Block-level implementation. +pub mod block_api; + +use block_api::RetailMacCore; +use cipher::{AlgorithmName, BlockCipherDecrypt, BlockCipherEncrypt, BlockSizeUser, KeySizeUser}; +use core::{fmt, ops::Mul}; use digest::{ - MacMarker, Output, OutputSizeUser, Reset, - array::{ - Array, ArraySize, - typenum::{IsLess, Le, NonZero, U2, U256}, - }, - block_buffer::Eager, - core_api::{ - AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore, - UpdateCore, - }, - crypto_common::BlockSizes, + InvalidLength, + array::ArraySize, + block_api::CoreProxy, + typenum::{Prod, U2}, }; -#[cfg(feature = "zeroize")] -use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; - -/// Generic Retail MAC instance. -pub type RetailMac = CoreWrapper>; - -/// Generic core Retail MAC instance, which operates over blocks. -#[derive(Clone)] -pub struct RetailMacCore -where - C: BlockCipherEncrypt + BlockCipherDecrypt + Clone, -{ - cipher: C, - cipher_prime: C, - state: Block, -} - -impl BlockSizeUser for RetailMacCore -where - C: BlockCipherEncrypt + BlockCipherDecrypt + Clone, -{ - type BlockSize = C::BlockSize; -} +digest::buffer_fixed!( + /// Generic Retail MAC instance. + pub struct RetailMac(RetailMacCore); + impl: ResetMacTraits; +); -impl OutputSizeUser for RetailMacCore +impl KeySizeUser for RetailMac where C: BlockCipherEncrypt + BlockCipherDecrypt + Clone, + ::BlockSize: Mul, + Prod<::BlockSize, U2>: ArraySize, { - type OutputSize = C::BlockSize; + type KeySize = Prod<::BlockSize, U2>; } -impl KeySizeUser for RetailMacCore -where - C: BlockCipherEncrypt + BlockCipherDecrypt + Clone, - ::BlockSize: core::ops::Mul, - <::BlockSize as core::ops::Mul>::Output: ArraySize, -{ - type KeySize = <::BlockSize as core::ops::Mul>::Output; -} - -impl MacMarker for RetailMacCore where C: BlockCipherEncrypt + BlockCipherDecrypt + Clone {} - -impl BufferKindUser for RetailMacCore -where - C: BlockCipherEncrypt + BlockCipherDecrypt + Clone, -{ - type BufferKind = Eager; -} - -impl KeyInit for RetailMacCore +impl KeyInit for RetailMac where C: BlockCipherEncrypt + BlockCipherDecrypt + Clone + KeyInit, - ::BlockSize: core::ops::Mul, - <::BlockSize as core::ops::Mul>::Output: ArraySize, + ::BlockSize: Mul, + Prod<::BlockSize, U2>: ArraySize, { #[inline(always)] fn new(key: &Key) -> Self { - Self::new_from_slice(key.as_slice()).unwrap() + Self { + core: KeyInit::new(key), + buffer: Default::default(), + } } #[inline(always)] fn new_from_slice(key: &[u8]) -> Result { - let cipher = C::new_from_slice(&key[..key.len() / 2])?; - let cipher_prime = C::new_from_slice(&key[key.len() / 2..])?; - Ok(Self { - cipher, - cipher_prime, - state: Block::::default(), + KeyInit::new_from_slice(key).map(|core| Self { + core, + buffer: Default::default(), }) } } -impl UpdateCore for RetailMacCore -where - C: BlockCipherEncrypt + BlockCipherDecrypt + Clone, -{ - #[inline] - fn update_blocks(&mut self, blocks: &[Block]) { - struct Closure<'a, N: BlockSizes> { - state: &'a mut Block, - blocks: &'a [Block], - } - - impl BlockSizeUser for Closure<'_, N> { - type BlockSize = N; - } - - impl BlockCipherEncClosure for Closure<'_, N> { - #[inline(always)] - fn call>(self, backend: &B) { - for block in self.blocks { - xor(self.state, block); - backend.encrypt_block((self.state).into()); - } - } - } - - let Self { cipher, state, .. } = self; - cipher.encrypt_with_backend(Closure { state, blocks }) - } -} - -impl Reset for RetailMacCore -where - C: BlockCipherEncrypt + BlockCipherDecrypt + Clone, -{ - #[inline(always)] - fn reset(&mut self) { - self.state = Default::default(); - } -} - -impl FixedOutputCore for RetailMacCore -where - C: BlockCipherEncrypt + BlockCipherDecrypt + Clone, - C::BlockSize: IsLess, - Le: NonZero, -{ - #[inline] - fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output) { - let Self { - state, - cipher, - cipher_prime, - } = self; - let pos = buffer.get_pos(); - if pos != 0 { - xor(state, &buffer.pad_with_zeros()); - cipher.encrypt_block(state); - } - cipher_prime.decrypt_block(state); - cipher.encrypt_block(state); - out.copy_from_slice(state); - } -} - -impl AlgorithmName for RetailMacCore +impl AlgorithmName for RetailMac where C: BlockCipherEncrypt + BlockCipherDecrypt + Clone + AlgorithmName, { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("RetailMac<")?; - ::write_alg_name(f)?; - f.write_str(">") - } -} - -impl fmt::Debug for RetailMacCore -where - C: BlockCipherEncrypt + BlockCipherDecrypt + Clone + AlgorithmName, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("RetailMacCore<")?; - ::write_alg_name(f)?; - f.write_str("> { ... }") - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl Drop for RetailMacCore -where - C: BlockCipherEncrypt + BlockCipherDecrypt + Clone, -{ - fn drop(&mut self) { - self.state.zeroize(); - } -} - -#[cfg(feature = "zeroize")] -#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] -impl ZeroizeOnDrop for RetailMacCore where - C: BlockCipherEncrypt + BlockCipherDecrypt + Clone + ZeroizeOnDrop -{ -} - -#[inline(always)] -fn xor(buf: &mut Array, data: &Array) { - for i in 0..N::USIZE { - buf[i] ^= data[i]; + ::Core::write_alg_name(f) } }