diff --git a/.github/workflows/hmac.yml b/.github/workflows/hmac.yml index 9a2dc56..d058bef 100644 --- a/.github/workflows/hmac.yml +++ b/.github/workflows/hmac.yml @@ -53,4 +53,5 @@ jobs: toolchain: ${{ matrix.rust }} override: true - run: cargo test --release --no-default-features + - run: cargo test --release --features reset - run: cargo test --release diff --git a/Cargo.lock b/Cargo.lock index 88497db..a86ec70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "aes" version = "0.7.5" @@ -14,26 +16,19 @@ dependencies = [ [[package]] name = "blobby" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc52553543ecb104069b0ff9e0fcc5c739ad16202935528a112d974e8f1a4ee8" +checksum = "847495c209977a90e8aad588b959d0ca9f5dc228096d29a6bd3defd53f35eaec" [[package]] name = "block-buffer" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95" dependencies = [ - "block-padding", "generic-array", ] -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - [[package]] name = "byteorder" version = "1.4.3" @@ -69,13 +64,22 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cc47dfc37fe9455a291c6aef98ac51376b771fa9779aa7a2f9a86f0700a7a20" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" dependencies = [ "libc", ] +[[package]] +name = "crypto-common" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567569e659735adb39ff2d4c20600f7cd78be5471f8c58ab162bce3c03fdbc5f" +dependencies = [ + "generic-array", +] + [[package]] name = "crypto-mac" version = "0.11.1" @@ -118,11 +122,15 @@ dependencies = [ [[package]] name = "digest" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +checksum = "8549e6bfdecd113b7e221fe60b433087f6957387a20f8118ebca9b12af19143d" dependencies = [ + "blobby", + "block-buffer", + "crypto-common", "generic-array", + "subtle", ] [[package]] @@ -156,11 +164,12 @@ dependencies = [ [[package]] name = "hmac" -version = "0.11.0" +version = "0.12.0" dependencies = [ - "crypto-mac", "digest", + "hex-literal", "md-5", + "sha-1", "sha2", "streebog", ] @@ -176,9 +185,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.101" +version = "0.2.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" +checksum = "f98a04dce437184842841303488f70d0188c5f51437d2a834dc097eafa909a01" [[package]] name = "magma" @@ -192,13 +201,11 @@ dependencies = [ [[package]] name = "md-5" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" +checksum = "e6a38fc55c8bbc10058782919516f88826e70320db6d206aebc49611d24216ae" dependencies = [ - "block-buffer", "digest", - "opaque-debug", ] [[package]] @@ -222,41 +229,48 @@ version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" -version = "0.9.8" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" +checksum = "900d964dd36bb15bcf2f2b35694c072feab74969a54f2bbeec7a2d725d2bdcb6" dependencies = [ - "block-buffer", "cfg-if", "cpufeatures", "digest", - "opaque-debug", ] [[package]] name = "streebog" -version = "0.9.2" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9a21c1a3920381f27c666a81ad2481abc005900ac871a80b479e1869d54e753" +checksum = "5f2a93b52a311873ee038192d8a95dc3bad1d638ac926c2afee0ea9887ecfaf0" dependencies = [ - "block-buffer", "digest", - "opaque-debug", ] [[package]] name = "subtle" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "typenum" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" [[package]] name = "version_check" diff --git a/README.md b/README.md index d5ef9bd..5e0d9e9 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,21 @@ -# RustCrypto: Message Authentication Codes ![Rust Version][rustc-image] [![Project Chat][chat-image]][chat-link] [![dependency status][deps-image]][deps-link] +# RustCrypto: Message Authentication Codes + +[![Project Chat][chat-image]][chat-link] [![dependency status][deps-image]][deps-link] ![Apache2/MIT licensed][license-image] Collection of [Message Authentication Code][1] (MAC) algorithms written in pure Rust. -## Crates +## Supported Algorithms + +| Algorithm | Crate | Crates.io | Documentation | MSRV | +|-----------|----------|:-------------:|:-------------:|:----:| +| [CMAC] | [`cmac`] | [![crates.io](https://img.shields.io/crates/v/cmac.svg)](https://crates.io/crates/cmac) | [![Documentation](https://docs.rs/cmac/badge.svg)](https://docs.rs/cmac) | ![MSRV 1.41][msrv-1.41] | +| [DAA] | [`daa`] | [![crates.io](https://img.shields.io/crates/v/daa.svg)](https://crates.io/crates/daa) | [![Documentation](https://docs.rs/daa/badge.svg)](https://docs.rs/daa) | ![MSRV 1.41][msrv-1.41] | +| [HMAC] | [`hmac`] | [![crates.io](https://img.shields.io/crates/v/hmac.svg)](https://crates.io/crates/hmac) | [![Documentation](https://docs.rs/hmac/badge.svg)](https://docs.rs/hmac) | ![MSRV 1.41][msrv-1.41] | +| [PMAC] | [`pmac`] | [![crates.io](https://img.shields.io/crates/v/pmac.svg)](https://crates.io/crates/pmac) | [![Documentation](https://docs.rs/pmac/badge.svg)](https://docs.rs/pmac) | ![MSRV 1.41][msrv-1.41] | + +### Minimum Supported Rust Version (MSRV) Policy -| Name | Algorithm | Crates.io | Documentation | Build Status | -|--------|-----------|---------------|---------------|--------------| -| `cmac` | [CMAC] | [![crates.io](https://img.shields.io/crates/v/cmac.svg)](https://crates.io/crates/cmac) | [![Documentation](https://docs.rs/cmac/badge.svg)](https://docs.rs/cmac) | [![Build](https://github.com/RustCrypto/MACs/workflows/cmac/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/MACs/actions?query=workflow:cmac+branch:master) -| `daa` | [DAA] | [![crates.io](https://img.shields.io/crates/v/daa.svg)](https://crates.io/crates/daa) | [![Documentation](https://docs.rs/daa/badge.svg)](https://docs.rs/daa) | [![Build](https://github.com/RustCrypto/MACs/workflows/daa/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/MACs/actions?query=workflow:daa+branch:master) -| `hmac` | [HMAC] | [![crates.io](https://img.shields.io/crates/v/hmac.svg)](https://crates.io/crates/hmac) | [![Documentation](https://docs.rs/hmac/badge.svg)](https://docs.rs/hmac) | [![Build](https://github.com/RustCrypto/MACs/workflows/hmac/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/MACs/actions?query=workflow:hmac+branch:master) -| `pmac` | [PMAC] | [![crates.io](https://img.shields.io/crates/v/pmac.svg)](https://crates.io/crates/pmac) | [![Documentation](https://docs.rs/pmac/badge.svg)](https://docs.rs/pmac) | [![Build](https://github.com/RustCrypto/MACs/workflows/pmac/badge.svg?branch=master&event=push)](https://github.com/RustCrypto/MACs/actions?query=workflow:pmac+branch:master) +MSRV bumps are considered breaking changes and will be performed only with minor version bump. ## License @@ -22,16 +28,23 @@ 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 +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) -[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 +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg [deps-image]: https://deps.rs/repo/github/RustCrypto/MACs/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/MACs +[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg + +[//]: # (crates) + +[`cmac`]: ./cmac +[`daa`]: ./daa +[`hmac`]: ./hmac +[`pmac`]: ./pmac [//]: # (footnotes) diff --git a/hmac/CHANGELOG.md b/hmac/CHANGELOG.md index 77a2f7b..1bcf688 100644 --- a/hmac/CHANGELOG.md +++ b/hmac/CHANGELOG.md @@ -5,6 +5,16 @@ 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.12.0 (2021-12-07) +### Changed +- Bump `digest` crate dependency to v0.10 and remove `crypto-mac` ([#97]) +- Use a more efficient state representation by using block-level hash API ([#97]) + +### Added +- `SimpleHmac` as a less constrained alternative to `Hmac` ([#97]) + +[#97]: https://github.com/RustCrypto/MACs/pull/97 + ## 0.11.0 (2021-04-29) ### Changed - Bump `crypto-mac` crate dependency to v0.11 ([#73]) diff --git a/hmac/Cargo.toml b/hmac/Cargo.toml index 23544f6..d285310 100644 --- a/hmac/Cargo.toml +++ b/hmac/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hmac" -version = "0.11.0" +version = "0.12.0" # Also update html_root_url in lib.rs when bumping this description = "Generic implementation of Hash-based Message Authentication Code (HMAC)" authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" @@ -12,14 +12,16 @@ readme = "README.md" edition = "2018" [dependencies] -crypto-mac = "0.11" -digest = "0.9" +digest = { version = "0.10", features = ["mac"] } [dev-dependencies] -crypto-mac = { version = "0.11", features = ["dev"] } -md-5 = { version = "0.9", default-features = false } -sha2 = { version = "0.9", default-features = false } -streebog = { version = "0.9", default-features = false } +digest = { version = "0.10", features = ["dev"] } +md-5 = { version = "0.10", default-features = false } +sha-1 = { version = "0.10", default-features = false } +sha2 = { version = "0.10", default-features = false } +streebog = { version = "0.10", default-features = false } +hex-literal = "0.2" [features] -std = ["crypto-mac/std"] +std = ["digest/std"] +reset = [] # Enable ability to reset HMAC instances diff --git a/hmac/README.md b/hmac/README.md index ab50b2c..52d4ef9 100644 --- a/hmac/README.md +++ b/hmac/README.md @@ -5,7 +5,6 @@ ![Apache2/MIT licensed][license-image] ![Rust Version][rustc-image] [![Project Chat][chat-image]][chat-link] -[![Build Status][build-image]][build-link] Pure Rust implementation of the [Hash-based Message Authentication Code (HMAC)][1]. @@ -48,8 +47,6 @@ dual licensed as above, without any additional terms or conditions. [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/MACs/workflows/hmac/badge.svg?branch=master&event=push -[build-link]: https://github.com/RustCrypto/MACs/actions?query=workflow%3Ahmac [//]: # (general links) diff --git a/hmac/src/lib.rs b/hmac/src/lib.rs index b3f3666..303c7c5 100644 --- a/hmac/src/lib.rs +++ b/hmac/src/lib.rs @@ -1,213 +1,131 @@ //! Generic implementation of Hash-based Message Authentication Code (HMAC). //! -//! To use it you'll need a cryptographic hash function implementation from -//! RustCrypto project. You can either import specific crate (e.g. `sha2`), or -//! meta-crate `crypto-hashes` which reexport all related crates. +//! To use it you will need a cryptographic hash function implementation which +//! 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 +//! 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. //! //! # Usage -//! Let us demonstrate how to use HMAC using SHA256 as an example. +//! Let us demonstrate how to use HMAC using the SHA-256 hash function. +//! +//! In the following examples [`Hmac`] is interchangeable with [`SimpleHmac`]. //! -//! To get the authentication code: +//! To get authentication code: //! //! ```rust //! use sha2::Sha256; -//! use hmac::{Hmac, Mac, NewMac}; +//! use hmac::{Hmac, Mac}; +//! use hex_literal::hex; //! //! // Create alias for HMAC-SHA256 //! type HmacSha256 = Hmac; //! -//! // Create HMAC-SHA256 instance which implements `Mac` trait //! let mut mac = HmacSha256::new_from_slice(b"my secret and secure key") //! .expect("HMAC can take key of any size"); //! mac.update(b"input message"); //! -//! // `result` has type `Output` which is a thin wrapper around array of +//! // `result` has type `CtOutput` which is a thin wrapper around array of //! // bytes for providing constant time equality check //! let result = mac.finalize(); -//! // To get underlying array use `into_bytes` method, but be careful, since -//! // incorrect use of the code value may permit timing attacks which defeat -//! // the security provided by the `Output` +//! // To get underlying array use `into_bytes`, but be careful, since +//! // incorrect use of the code value may permit timing attacks which defeats +//! // the security provided by the `CtOutput` //! let code_bytes = result.into_bytes(); +//! let expected = hex!(" +//! 97d2a569059bbcd8ead4444ff99071f4 +//! c01d005bcefe0d3567e1be628e5fdcd9 +//! "); +//! assert_eq!(code_bytes[..], expected[..]); //! ``` //! //! To verify the message: //! //! ```rust //! # use sha2::Sha256; -//! # use hmac::{Hmac, Mac, NewMac}; +//! # use hmac::{Hmac, Mac}; +//! # use hex_literal::hex; //! # type HmacSha256 = Hmac; //! let mut mac = HmacSha256::new_from_slice(b"my secret and secure key") //! .expect("HMAC can take key of any size"); //! //! mac.update(b"input message"); //! -//! # let code_bytes = mac.clone().finalize().into_bytes(); -//! // `verify` will return `Ok(())` if code is correct, `Err(MacError)` otherwise -//! mac.verify(&code_bytes).unwrap(); +//! let code_bytes = hex!(" +//! 97d2a569059bbcd8ead4444ff99071f4 +//! c01d005bcefe0d3567e1be628e5fdcd9 +//! "); +//! // `verify_slice` will return `Ok(())` if code is correct, `Err(MacError)` otherwise +//! mac.verify_slice(&code_bytes[..]).unwrap(); //! ``` //! //! # Block and input sizes //! Usually it is assumed that block size is larger than output size. Due to the //! generic nature of the implementation, this edge case must be handled as well -//! to remove potential panic scenario. This is done by truncating hash output -//! to the hash block size if needed. +//! to remove potential panic. This is done by truncating hash output to the hash +//! block size if needed. +//! +//! [`digest`]: https://docs.rs/digest +//! [`sha2`]: https://docs.rs/sha2 +//! [`RustCrypto/hashes`]: https://github.com/RustCrypto/hashes #![no_std] #![doc( - html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg", - html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg" + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", + html_root_url = "https://docs.rs/hmac/0.12.0" )] +#![cfg_attr(docsrs, feature(doc_cfg))] #![forbid(unsafe_code)] #![warn(missing_docs, rust_2018_idioms)] #[cfg(feature = "std")] extern crate std; -pub use crypto_mac::{self, Mac, NewMac}; pub use digest; +pub use digest::Mac; -use core::{cmp::min, fmt}; -use crypto_mac::{ - generic_array::{sequence::GenericSequence, ArrayLength, GenericArray}, - InvalidKeyLength, Output, +use digest::{ + core_api::{Block, BlockSizeUser}, + Digest, }; -use digest::{BlockInput, FixedOutput, Reset, Update}; - -const IPAD: u8 = 0x36; -const OPAD: u8 = 0x5C; - -/// The `Hmac` struct represents an HMAC using a given hash function `D`. -pub struct Hmac -where - D: Update + BlockInput + FixedOutput + Reset + Default + Clone, - D::BlockSize: ArrayLength, -{ - digest: D, - i_key_pad: GenericArray, - opad_digest: D, -} - -impl Clone for Hmac -where - D: Update + BlockInput + FixedOutput + Reset + Default + Clone, - D::BlockSize: ArrayLength, -{ - fn clone(&self) -> Hmac { - Hmac { - digest: self.digest.clone(), - i_key_pad: self.i_key_pad.clone(), - opad_digest: self.opad_digest.clone(), - } - } -} -impl fmt::Debug for Hmac -where - D: Update + BlockInput + FixedOutput + Reset + Default + Clone + fmt::Debug, - D::BlockSize: ArrayLength, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Hmac") - .field("digest", &self.digest) - .field("i_key_pad", &self.i_key_pad) - .field("opad_digest", &self.opad_digest) - .finish() - } -} +mod optim; +mod simple; -impl NewMac for Hmac -where - D: Update + BlockInput + FixedOutput + Reset + Default + Clone, - D::BlockSize: ArrayLength, - D::OutputSize: ArrayLength, -{ - type KeySize = D::BlockSize; +pub use optim::{Hmac, HmacCore}; +pub use simple::SimpleHmac; - fn new(key: &GenericArray) -> Self { - Self::new_from_slice(key.as_slice()).unwrap() - } - - #[inline] - fn new_from_slice(key: &[u8]) -> Result { - let mut hmac = Self { - digest: Default::default(), - i_key_pad: GenericArray::generate(|_| IPAD), - opad_digest: Default::default(), - }; - - let mut opad = GenericArray::::generate(|_| OPAD); - debug_assert!(hmac.i_key_pad.len() == opad.len()); +const IPAD: u8 = 0x36; +const OPAD: u8 = 0x5C; - // The key that Hmac processes must be the same as the block size of the - // underlying Digest. 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() <= hmac.i_key_pad.len() { - for (k_idx, k_itm) in key.iter().enumerate() { - hmac.i_key_pad[k_idx] ^= *k_itm; - opad[k_idx] ^= *k_itm; - } +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 calcualted 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 mut digest = D::default(); - digest.update(key); - let output = digest.finalize_fixed(); - // `n` is calculated at compile time and will equal - // D::OutputSize. This is used to ensure panic-free code - let n = min(output.len(), hmac.i_key_pad.len()); - for idx in 0..n { - hmac.i_key_pad[idx] ^= output[idx]; - opad[idx] ^= output[idx]; - } + let n = der_key.len(); + der_key.copy_from_slice(&hash[..n]); } - - hmac.digest.update(&hmac.i_key_pad); - hmac.opad_digest.update(&opad); - - Ok(hmac) - } -} - -impl Mac for Hmac -where - D: Update + BlockInput + FixedOutput + Reset + Default + Clone, - D::BlockSize: ArrayLength, - D::OutputSize: ArrayLength, -{ - type OutputSize = D::OutputSize; - - #[inline] - fn update(&mut self, data: &[u8]) { - self.digest.update(data); - } - - #[inline] - fn finalize(self) -> Output { - let mut opad_digest = self.opad_digest.clone(); - let hash = self.digest.finalize_fixed(); - opad_digest.update(&hash); - Output::new(opad_digest.finalize_fixed()) - } - - #[inline] - fn reset(&mut self) { - self.digest.reset(); - self.digest.update(&self.i_key_pad); - } -} - -#[cfg(feature = "std")] -impl std::io::Write for Hmac -where - D: Update + BlockInput + FixedOutput + Reset + Default + Clone, - D::BlockSize: ArrayLength, - D::OutputSize: ArrayLength, -{ - fn write(&mut self, buf: &[u8]) -> std::io::Result { - Mac::update(self, buf); - Ok(buf.len()) - } - - fn flush(&mut self) -> std::io::Result<()> { - Ok(()) } + der_key } diff --git a/hmac/src/optim.rs b/hmac/src/optim.rs new file mode 100644 index 0000000..32d6277 --- /dev/null +++ b/hmac/src/optim.rs @@ -0,0 +1,280 @@ +use super::{get_der_key, IPAD, OPAD}; +use core::{fmt, slice}; +#[cfg(feature = "reset")] +use digest::Reset; +use digest::{ + block_buffer::Eager, + core_api::{ + AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreProxy, CoreWrapper, + FixedOutputCore, OutputSizeUser, UpdateCore, + }, + crypto_common::{Key, KeySizeUser}, + generic_array::typenum::{IsLess, Le, NonZero, U256}, + HashMarker, InvalidLength, KeyInit, MacMarker, Output, +}; + +/// Generic HMAC instance. +pub type Hmac = CoreWrapper>; + +/// Generic core HMAC instance, which operates over blocks. +pub struct HmacCore +where + D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + digest: D::Core, + opad_digest: D::Core, + #[cfg(feature = "reset")] + ipad_digest: D::Core, +} + +impl Clone for HmacCore +where + D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + 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 +where + D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ +} + +impl BufferKindUser for HmacCore +where + D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + type BufferKind = Eager; +} + +impl KeySizeUser for HmacCore +where + D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + type KeySize = <::Core as BlockSizeUser>::BlockSize; +} + +impl BlockSizeUser for HmacCore +where + D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + type BlockSize = <::Core as BlockSizeUser>::BlockSize; +} + +impl OutputSizeUser for HmacCore +where + D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + type OutputSize = <::Core as OutputSizeUser>::OutputSize; +} + +impl KeyInit for HmacCore +where + D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + #[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 +where + D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + #[inline(always)] + fn update_blocks(&mut self, blocks: &[Block]) { + self.digest.update_blocks(blocks); + } +} + +impl FixedOutputCore for HmacCore +where + D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + #[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 Reset for HmacCore +where + D: CoreProxy, + D::Core: HashMarker + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + #[inline(always)] + fn reset(&mut self) { + self.digest = self.ipad_digest.clone(); + } +} + +impl AlgorithmName for HmacCore +where + D: CoreProxy, + D::Core: HashMarker + + AlgorithmName + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + 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: CoreProxy, + D::Core: HashMarker + + AlgorithmName + + UpdateCore + + FixedOutputCore + + BufferKindUser + + Default + + Clone, + ::BlockSize: IsLess, + Le<::BlockSize, U256>: NonZero, +{ + 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 new file mode 100644 index 0000000..e28c4c3 --- /dev/null +++ b/hmac/src/simple.rs @@ -0,0 +1,106 @@ +use super::{get_der_key, IPAD, OPAD}; +use core::fmt; +use digest::{ + crypto_common::{Block, BlockSizeUser, InvalidLength, Key, KeySizeUser}, + Digest, FixedOutput, KeyInit, MacMarker, Output, OutputSizeUser, Update, +}; +#[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 +/// process blocks lazily (e.g. BLAKE2). +#[derive(Clone)] +pub struct SimpleHmac { + digest: D, + opad_key: Block, + #[cfg(feature = "reset")] + ipad_key: Block, +} + +impl KeySizeUser for SimpleHmac { + type KeySize = D::BlockSize; +} + +impl MacMarker for SimpleHmac {} + +impl KeyInit for SimpleHmac { + 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(); + for b in ipad_key.iter_mut() { + *b ^= IPAD; + } + let mut digest = D::new(); + digest.update(&ipad_key); + + let mut opad_key = der_key; + for b in opad_key.iter_mut() { + *b ^= OPAD; + } + + Ok(Self { + digest, + opad_key, + #[cfg(feature = "reset")] + ipad_key, + }) + } +} + +impl Update for SimpleHmac { + #[inline(always)] + fn update(&mut self, data: &[u8]) { + self.digest.update(data); + } +} + +impl OutputSizeUser for SimpleHmac { + type OutputSize = D::OutputSize; +} + +impl FixedOutput for SimpleHmac { + 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 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); + } +} diff --git a/hmac/tests/data/wycheproof-sha1.blb b/hmac/tests/data/wycheproof-sha1.blb new file mode 100644 index 0000000..a4f8f43 Binary files /dev/null and b/hmac/tests/data/wycheproof-sha1.blb differ diff --git a/hmac/tests/data/wycheproof-sha256.blb b/hmac/tests/data/wycheproof-sha256.blb new file mode 100644 index 0000000..62f1d35 Binary files /dev/null and b/hmac/tests/data/wycheproof-sha256.blb differ diff --git a/hmac/tests/data/wycheproof-sha384.blb b/hmac/tests/data/wycheproof-sha384.blb new file mode 100644 index 0000000..69a7874 Binary files /dev/null and b/hmac/tests/data/wycheproof-sha384.blb differ diff --git a/hmac/tests/data/wycheproof-sha512.blb b/hmac/tests/data/wycheproof-sha512.blb new file mode 100644 index 0000000..e061fd5 Binary files /dev/null and b/hmac/tests/data/wycheproof-sha512.blb differ diff --git a/hmac/tests/lib.rs b/hmac/tests/lib.rs deleted file mode 100644 index 38714a0..0000000 --- a/hmac/tests/lib.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! Test vectors from: -//! - md5: RFC 2104, plus wiki test -//! - sha2: RFC 4231 - -#![no_std] - -use crypto_mac::new_test; -use hmac::Hmac; - -new_test!(hmac_md5, "md5", Hmac); -new_test!(hmac_sha224, "sha224", Hmac); -new_test!(hmac_sha256, "sha256", Hmac); -new_test!(hmac_sha384, "sha384", Hmac); -new_test!(hmac_sha512, "sha512", Hmac); -// Test vectors from R 50.1.113-2016: -// https://tc26.ru/standard/rs/Р 50.1.113-2016.pdf -new_test!(hmac_streebog256, "streebog256", Hmac); -new_test!(hmac_streebog512, "streebog512", Hmac); diff --git a/hmac/tests/optim.rs b/hmac/tests/optim.rs new file mode 100644 index 0000000..02e28c7 --- /dev/null +++ b/hmac/tests/optim.rs @@ -0,0 +1,49 @@ +#[cfg(not(feature = "reset"))] +use digest::new_mac_test as new_test; +#[cfg(feature = "reset")] +use digest::new_resettable_mac_test as new_test; +use hmac::Hmac; +use sha1::Sha1; +use sha2::{Sha224, Sha256, Sha384, Sha512}; +use streebog::{Streebog256, Streebog512}; + +// Test vectors from RFC 2104, plus wiki test +new_test!(rfc2104_hmac_md5, "md5", Hmac); + +// Test vectors from RFC 4231 +new_test!(rfc4231_hmac_sha224, "sha224", Hmac); +new_test!(rfc4231_hmac_sha256, "sha256", Hmac); +new_test!(rfc4231_hmac_sha384, "sha384", Hmac); +new_test!(rfc4231_hmac_sha512, "sha512", Hmac); + +// Tests from Project Wycheproof: +// https://github.com/google/wycheproof +new_test!( + wycheproof_hmac_sha1, + "wycheproof-sha1", + Hmac, + trunc_left, +); +new_test!( + wycheproof_hmac_sha256, + "wycheproof-sha256", + Hmac, + trunc_left, +); +new_test!( + wycheproof_hmac_sha384, + "wycheproof-sha384", + Hmac, + trunc_left, +); +new_test!( + wycheproof_hmac_sha512, + "wycheproof-sha512", + Hmac, + trunc_left, +); + +// Test vectors from R 50.1.113-2016: +// https://tc26.ru/standard/rs/Р 50.1.113-2016.pdf +new_test!(hmac_streebog256, "streebog256", Hmac); +new_test!(hmac_streebog512, "streebog512", Hmac); diff --git a/hmac/tests/simple.rs b/hmac/tests/simple.rs new file mode 100644 index 0000000..f75bea7 --- /dev/null +++ b/hmac/tests/simple.rs @@ -0,0 +1,57 @@ +#[cfg(not(feature = "reset"))] +use digest::new_mac_test as new_test; +#[cfg(feature = "reset")] +use digest::new_resettable_mac_test as new_test; +use hmac::SimpleHmac; +use sha1::Sha1; +use sha2::{Sha224, Sha256, Sha384, Sha512}; +use streebog::{Streebog256, Streebog512}; + +// Test vectors from RFC 2104, plus wiki test +new_test!(rfc2104_simple_hmac_md5, "md5", SimpleHmac); + +// Test vectors from RFC 4231 +new_test!(rfc4231_simple_hmac_sha224, "sha224", SimpleHmac); +new_test!(rfc4231_simple_hmac_sha256, "sha256", SimpleHmac); +new_test!(rfc4231_simple_hmac_sha384, "sha384", SimpleHmac); +new_test!(rfc4231_simple_hmac_sha512, "sha512", SimpleHmac); + +// Tests from Project Wycheproof: +// https://github.com/google/wycheproof +new_test!( + wycheproof_simple_hmac_sha1, + "wycheproof-sha1", + SimpleHmac, + trunc_left, +); +new_test!( + wycheproof_simple_hmac_sha256, + "wycheproof-sha256", + SimpleHmac, + trunc_left, +); +new_test!( + wycheproof_simple_hmac_sha384, + "wycheproof-sha384", + SimpleHmac, + trunc_left, +); +new_test!( + wycheproof_simple_hmac_sha512, + "wycheproof-sha512", + SimpleHmac, + trunc_left, +); + +// Test vectors from R 50.1.113-2016: +// https://tc26.ru/standard/rs/Р 50.1.113-2016.pdf +new_test!( + simple_hmac_streebog256, + "streebog256", + SimpleHmac +); +new_test!( + simple_hmac_streebog512, + "streebog512", + SimpleHmac +);