diff --git a/.github/workflows/cipher.yml b/.github/workflows/cipher.yml index d18dd4b64..6389b9858 100644 --- a/.github/workflows/cipher.yml +++ b/.github/workflows/cipher.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: rust: - - 1.41.0 # MSRV + - 1.56.0 # MSRV - stable target: - thumbv7em-none-eabi @@ -35,15 +35,16 @@ jobs: target: ${{ matrix.target }} override: true profile: minimal - - run: cargo build --target ${{ matrix.target }} --release --no-default-features - - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features rand_core + - run: cargo build --target ${{ matrix.target }} + - run: cargo build --target ${{ matrix.target }} --features rand_core + - run: cargo build --target ${{ matrix.target }} --features block-padding test: runs-on: ubuntu-latest strategy: matrix: rust: - - 1.41.0 # MSRV + - 1.56.0 # MSRV - stable steps: - uses: actions/checkout@v1 @@ -52,7 +53,8 @@ jobs: profile: minimal toolchain: ${{ matrix.rust }} - run: cargo check --all-features - - run: cargo test --release - - run: cargo test --features dev --release - - run: cargo test --features std --release - - run: cargo test --all-features --release + - run: cargo test + - run: cargo test --features block-padding + - run: cargo test --features dev + - run: cargo test --features std + - run: cargo test --all-features diff --git a/.gitignore b/.gitignore index 2f7896d1d..f1148d25a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ target/ +**/target/ +**/Cargo.lock diff --git a/Cargo.lock b/Cargo.lock index 96ff03900..65efb3529 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,9 +22,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.51" +version = "0.1.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e" +checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" dependencies = [ "proc-macro2", "quote", @@ -39,9 +39,9 @@ checksum = "847495c209977a90e8aad588b959d0ca9f5dc228096d29a6bd3defd53f35eaec" [[package]] name = "block-buffer" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" dependencies = [ "generic-array", ] @@ -58,16 +58,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cipher" -version = "0.4.0-pre" -dependencies = [ - "blobby", - "crypto-common 0.1.1", - "generic-array", - "rand_core", -] - [[package]] name = "cpufeatures" version = "0.2.1" @@ -80,40 +70,39 @@ dependencies = [ [[package]] name = "crypto-common" version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d6b536309245c849479fba3da410962a43ed8e51c26b729208ec0ac2798d0" dependencies = [ "generic-array", - "rand_core", ] [[package]] name = "crypto-common" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d6b536309245c849479fba3da410962a43ed8e51c26b729208ec0ac2798d0" +version = "0.1.2" dependencies = [ "generic-array", + "rand_core", ] [[package]] name = "digest" version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b" dependencies = [ - "blobby", "block-buffer", "crypto-common 0.1.1", "generic-array", - "subtle", ] [[package]] name = "digest" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b" +version = "0.10.2" dependencies = [ + "blobby", "block-buffer", - "crypto-common 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array", + "crypto-common 0.1.2", + "subtle", ] [[package]] @@ -128,9 +117,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" dependencies = [ "cfg-if", "libc", @@ -148,9 +137,9 @@ dependencies = [ [[package]] name = "heapless" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1ad878e07405df82b695089e63d278244344f80e764074d0bdfe99b89460f3" +checksum = "d076121838e03f862871315477528debffdb7462fb229216ecef91b1a3eb31eb" dependencies = [ "hash32", "spin", @@ -178,15 +167,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.109" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98a04dce437184842841303488f70d0188c5f51437d2a834dc097eafa909a01" +checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" [[package]] name = "lock_api" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" dependencies = [ "scopeguard", ] @@ -199,18 +188,18 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.33" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb37d2df5df740e582f28f8560cf425f52bb267d872fe58358eadb554909f07a" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.10" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" dependencies = [ "proc-macro2", ] @@ -238,14 +227,14 @@ checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.10.1", ] [[package]] name = "signature" version = "1.5.0" dependencies = [ - "digest 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.10.1", "hex-literal", "rand_core", "sha2", @@ -285,9 +274,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" dependencies = [ "proc-macro2", "quote", @@ -308,9 +297,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "unicode-xid" @@ -328,9 +317,9 @@ dependencies = [ [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" diff --git a/Cargo.toml b/Cargo.toml index 8484c3c8e..2c1cb7dc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,6 @@ [workspace] members = [ "aead", - "cipher", "crypto-common", "digest", "signature", diff --git a/README.md b/README.md index a52c945da..ca83276cb 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Collection of traits which describe functionality of cryptographic primitives. |---------------------|-----------|:---------:|:-----:|:----:| | [`aead`] | [Authenticated encryption] | [![crates.io](https://img.shields.io/crates/v/aead.svg)](https://crates.io/crates/aead) | [![Documentation](https://docs.rs/aead/badge.svg)](https://docs.rs/aead) | ![MSRV 1.41][msrv-1.41] | | [`async‑signature`] | [Digital signature] | [![crates.io](https://img.shields.io/crates/v/async-signature.svg)](https://crates.io/crates/async-signature) | [![Documentation](https://docs.rs/async-signature/badge.svg)](https://docs.rs/async-signature) | ![MSRV 1.41][msrv-1.41] | -| [`cipher`] | [Block] and [stream cipher] | [![crates.io](https://img.shields.io/crates/v/cipher.svg)](https://crates.io/crates/cipher) | [![Documentation](https://docs.rs/cipher/badge.svg)](https://docs.rs/cipher) | ![MSRV 1.41][msrv-1.41] | +| [`cipher`] | [Block] and [stream cipher] | [![crates.io](https://img.shields.io/crates/v/cipher.svg)](https://crates.io/crates/cipher) | [![Documentation](https://docs.rs/cipher/badge.svg)](https://docs.rs/cipher) | ![MSRV 1.56][msrv-1.56] | | [`crypto‑common`] | Common cryptographic traits | [![crates.io](https://img.shields.io/crates/v/crypto-common.svg)](https://crates.io/crates/crypto-common) | [![Documentation](https://docs.rs/crypto-common/badge.svg)](https://docs.rs/crypto-common) | ![MSRV 1.41][msrv-1.41] | | [`digest`] | [Cryptographic hash function] | [![crates.io](https://img.shields.io/crates/v/digest.svg)](https://crates.io/crates/digest) | [![Documentation](https://docs.rs/digest/badge.svg)](https://docs.rs/digest) | ![MSRV 1.41][msrv-1.41] | | [`elliptic‑curve`] | [Elliptic curve cryptography] | [![crates.io](https://img.shields.io/crates/v/elliptic-curve.svg)](https://crates.io/crates/elliptic-curve) | [![Documentation](https://docs.rs/elliptic-curve/badge.svg)](https://docs.rs/elliptic-curve) | ![MSRV 1.56][msrv-1.56] | diff --git a/cipher/CHANGELOG.md b/cipher/CHANGELOG.md index 77b955f1f..7eee95adc 100644 --- a/cipher/CHANGELOG.md +++ b/cipher/CHANGELOG.md @@ -5,11 +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). -## [Unreleased] +## 0.3.0 (2022-02-10) +### Changed +- Major rework of traits. Core functionality of block and stream ciphers +is defined using rank-2 closures with convinience methods built on top of +it. Expose block-level trait for stream ciphers and add generic wrapper +around it. The async stream cipher trait is defined as sub-trait of +mutable block cipher traits. ([#849]) + ### Added - Re-export `rand_core` ([#683]) [#683]: https://github.com/RustCrypto/traits/pull/683 +[#849]: https://github.com/RustCrypto/traits/pull/849 ## 0.3.0 (2021-04-28) ### Added diff --git a/cipher/Cargo.lock b/cipher/Cargo.lock new file mode 100644 index 000000000..2f5d35567 --- /dev/null +++ b/cipher/Cargo.lock @@ -0,0 +1,80 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "blobby" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "847495c209977a90e8aad588b959d0ca9f5dc228096d29a6bd3defd53f35eaec" + +[[package]] +name = "block-padding" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5808df4b2412175c4db3afb115c83d8d0cd26ca4f30a042026cddef8580e526a" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cipher" +version = "0.4.0" +dependencies = [ + "blobby", + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.2" +dependencies = [ + "generic-array", + "rand_core", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "inout" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1d8734d7f28aaff861d726dc3bc8003e2987d2fc26add21f5dab0c35d5c348a" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "zeroize" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c88870063c39ee00ec285a2f8d6a966e5b6fb2becc4e8dac77ed0d370ed6006" diff --git a/cipher/Cargo.toml b/cipher/Cargo.toml index 49a2b4c4e..4ef3d2a6e 100644 --- a/cipher/Cargo.toml +++ b/cipher/Cargo.toml @@ -1,25 +1,33 @@ [package] name = "cipher" description = "Traits for describing block ciphers and stream ciphers" -version = "0.4.0-pre" +version = "0.4.0" # Also update html_root_url in lib.rs when bumping this authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" readme = "README.md" -edition = "2018" +edition = "2021" +rust-version = "1.56" documentation = "https://docs.rs/cipher" repository = "https://github.com/RustCrypto/traits" keywords = ["crypto", "block-cipher", "stream-cipher", "trait"] categories = ["cryptography", "no-std"] +# Hack to allow this crate to coexist with pre-2021 edition crates +[workspace] +members = ["."] + [dependencies] -generic-array = "0.14" -crypto-common = { version = "0.1", path = "../crypto-common" } +crypto-common = { version = "0.1.2", path = "../crypto-common" } +inout = "0.1" +# optional dependencies blobby = { version = "0.3", optional = true } -rand_core = { version = "0.6", optional = true } +zeroize = { version = "1.5", optional = true, default-features = false } [features] -std = ["crypto-common/std", "rand_core/std"] +std = ["crypto-common/std", "inout/std"] +block-padding = ["inout/block-padding"] +rand_core = ["crypto-common/rand_core"] # Enable random key and IV generation methods dev = ["blobby"] [package.metadata.docs.rs] diff --git a/cipher/README.md b/cipher/README.md index b2c33abd7..9d117626d 100644 --- a/cipher/README.md +++ b/cipher/README.md @@ -16,7 +16,7 @@ implementations which use these traits. ## Minimum Supported Rust Version -Rust **1.41** or higher. +Rust **1.56** or higher. Minimum supported Rust version can be changed in the future, but it will be done with a minor version bump. diff --git a/cipher/src/block.rs b/cipher/src/block.rs index 1769c782e..9c22fe3e8 100644 --- a/cipher/src/block.rs +++ b/cipher/src/block.rs @@ -10,228 +10,640 @@ //! [2]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation //! [3]: https://en.wikipedia.org/wiki/Symmetric-key_algorithm -use crate::errors::InvalidLength; -use crate::{FromKey, FromKeyNonce}; -use core::convert::TryInto; -use generic_array::{typenum::Unsigned, ArrayLength, GenericArray}; +use crate::{ParBlocks, ParBlocksSizeUser}; +#[cfg(feature = "block-padding")] +use inout::{ + block_padding::{Padding, UnpadError}, + InOutBufReserved, PadError, +}; +use inout::{InOut, InOutBuf, NotEqualError}; + +pub use crypto_common::{generic_array::ArrayLength, typenum::Unsigned, Block, BlockSizeUser}; + +/// Marker trait for block ciphers. +pub trait BlockCipher: BlockSizeUser {} + +/// Trait implemented by block cipher encryption and decryption backends. +pub trait BlockBackend: ParBlocksSizeUser { + /// Process single inout block. + fn proc_block(&mut self, block: InOut<'_, '_, Block>); + + /// Process inout blocks in parallel. + #[inline(always)] + fn proc_par_blocks(&mut self, mut blocks: InOut<'_, '_, ParBlocks>) { + for i in 0..Self::ParBlocksSize::USIZE { + self.proc_block(blocks.get(i)); + } + } -/// Key for an algorithm that implements [`FromKey`]. -pub type BlockCipherKey = GenericArray::KeySize>; + /// Process buffer of inout blocks. Length of the buffer MUST be smaller + /// than `Self::ParBlocksSize`. + #[inline(always)] + fn proc_tail_blocks(&mut self, blocks: InOutBuf<'_, '_, Block>) { + assert!(blocks.len() < Self::ParBlocksSize::USIZE); + for block in blocks { + self.proc_block(block); + } + } -/// Block on which a [`BlockCipher`] operates. -pub type Block = GenericArray::BlockSize>; + /// Process single block in-place. + #[inline(always)] + fn proc_block_inplace(&mut self, block: &mut Block) { + self.proc_block(block.into()); + } -/// Block on which a [`BlockCipher`] operates in parallel. -pub type ParBlocks = GenericArray, ::ParBlocks>; + /// Process blocks in parallel in-place. + #[inline(always)] + fn proc_par_blocks_inplace(&mut self, blocks: &mut ParBlocks) { + self.proc_par_blocks(blocks.into()); + } -/// Trait which marks a type as being a block cipher. -pub trait BlockCipher { - /// Size of the block in bytes - type BlockSize: ArrayLength; + /// Process buffer of blocks in-place. Length of the buffer MUST be smaller + /// than `Self::ParBlocksSize`. + #[inline(always)] + fn proc_tail_blocks_inplace(&mut self, blocks: &mut [Block]) { + self.proc_tail_blocks(blocks.into()); + } +} - /// Number of blocks which can be processed in parallel by - /// cipher implementation - type ParBlocks: ArrayLength>; +/// Trait for [`BlockBackend`] users. +/// +/// This trait is used to define rank-2 closures. +pub trait BlockClosure: BlockSizeUser { + /// Execute closure with the provided block cipher backend. + fn call>(self, backend: &mut B); } /// Encrypt-only functionality for block ciphers. -pub trait BlockEncrypt: BlockCipher { - /// Encrypt block in-place - fn encrypt_block(&self, block: &mut Block); +pub trait BlockEncrypt: BlockSizeUser + Sized { + /// Encrypt data using backend provided to the rank-2 closure. + fn encrypt_with_backend(&self, f: impl BlockClosure); - /// Encrypt several blocks in parallel using instruction level parallelism - /// if possible. - /// - /// If `ParBlocks` equals to 1 it's equivalent to `encrypt_block`. + /// Encrypt single `inout` block. #[inline] - fn encrypt_par_blocks(&self, blocks: &mut ParBlocks) { - for block in blocks.iter_mut() { - self.encrypt_block(block); - } + fn encrypt_block_inout(&self, block: InOut<'_, '_, Block>) { + self.encrypt_with_backend(BlockCtx { block }); } - /// Encrypt a slice of blocks, leveraging parallelism when available. + /// Encrypt `inout` blocks. #[inline] - fn encrypt_blocks(&self, mut blocks: &mut [Block]) { - let pb = Self::ParBlocks::to_usize(); + fn encrypt_blocks_inout(&self, blocks: InOutBuf<'_, '_, Block>) { + self.encrypt_with_backend(BlocksCtx { blocks }); + } - if pb > 1 { - let mut iter = blocks.chunks_exact_mut(pb); + /// Encrypt single block in-place. + #[inline] + fn encrypt_block(&self, block: &mut Block) { + let block = block.into(); + self.encrypt_with_backend(BlockCtx { block }); + } - for chunk in &mut iter { - self.encrypt_par_blocks(chunk.try_into().unwrap()) - } + /// Encrypt `in_block` and write result to `out_block`. + #[inline] + fn encrypt_block_b2b(&self, in_block: &Block, out_block: &mut Block) { + let block = (in_block, out_block).into(); + self.encrypt_with_backend(BlockCtx { block }); + } - blocks = iter.into_remainder(); - } + /// Encrypt blocks in-place. + #[inline] + fn encrypt_blocks(&self, blocks: &mut [Block]) { + let blocks = blocks.into(); + self.encrypt_with_backend(BlocksCtx { blocks }); + } - for block in blocks { - self.encrypt_block(block); + /// Encrypt blocks buffer-to-buffer. + /// + /// Returns [`NotEqualError`] if provided `in_blocks` and `out_blocks` + /// have different lengths. + #[inline] + fn encrypt_blocks_b2b( + &self, + in_blocks: &[Block], + out_blocks: &mut [Block], + ) -> Result<(), NotEqualError> { + InOutBuf::new(in_blocks, out_blocks) + .map(|blocks| self.encrypt_with_backend(BlocksCtx { blocks })) + } + + /// Pad input and encrypt. Returns resulting ciphertext slice. + /// + /// Returns [`PadError`] if length of output buffer is not sufficient. + #[cfg(feature = "block-padding")] + #[cfg_attr(docsrs, doc(cfg(feature = "block-padding")))] + #[inline] + fn encrypt_padded_inout<'inp, 'out, P: Padding>( + &self, + data: InOutBufReserved<'inp, 'out, u8>, + ) -> Result<&'out [u8], PadError> { + let mut buf = data.into_padded_blocks::()?; + self.encrypt_blocks_inout(buf.get_blocks()); + if let Some(block) = buf.get_tail_block() { + self.encrypt_block_inout(block); } + Ok(buf.into_out()) + } + + /// Pad input and encrypt in-place. Returns resulting ciphertext slice. + /// + /// Returns [`PadError`] if length of output buffer is not sufficient. + #[cfg(feature = "block-padding")] + #[cfg_attr(docsrs, doc(cfg(feature = "block-padding")))] + #[inline] + fn encrypt_padded<'a, P: Padding>( + &self, + buf: &'a mut [u8], + msg_len: usize, + ) -> Result<&'a [u8], PadError> { + let buf = InOutBufReserved::from_mut_slice(buf, msg_len).map_err(|_| PadError)?; + self.encrypt_padded_inout::

(buf) + } + + /// Pad input and encrypt buffer-to-buffer. Returns resulting ciphertext slice. + /// + /// Returns [`PadError`] if length of output buffer is not sufficient. + #[cfg(feature = "block-padding")] + #[cfg_attr(docsrs, doc(cfg(feature = "block-padding")))] + #[inline] + fn encrypt_padded_b2b<'a, P: Padding>( + &self, + msg: &[u8], + out_buf: &'a mut [u8], + ) -> Result<&'a [u8], PadError> { + let buf = InOutBufReserved::from_slices(msg, out_buf).map_err(|_| PadError)?; + self.encrypt_padded_inout::

(buf) } } /// Decrypt-only functionality for block ciphers. -pub trait BlockDecrypt: BlockCipher { - /// Decrypt block in-place - fn decrypt_block(&self, block: &mut Block); +pub trait BlockDecrypt: BlockSizeUser { + /// Decrypt data using backend provided to the rank-2 closure. + fn decrypt_with_backend(&self, f: impl BlockClosure); - /// Decrypt several blocks in parallel using instruction level parallelism - /// if possible. - /// - /// If `ParBlocks` equals to 1 it's equivalent to `decrypt_block`. + /// Decrypt single `inout` block. #[inline] - fn decrypt_par_blocks(&self, blocks: &mut ParBlocks) { - for block in blocks.iter_mut() { - self.decrypt_block(block); - } + fn decrypt_block_inout(&self, block: InOut<'_, '_, Block>) { + self.decrypt_with_backend(BlockCtx { block }); } - /// Decrypt a slice of blocks, leveraging parallelism when available. + /// Decrypt `inout` blocks. #[inline] - fn decrypt_blocks(&self, mut blocks: &mut [Block]) { - let pb = Self::ParBlocks::to_usize(); + fn decrypt_blocks_inout(&self, blocks: InOutBuf<'_, '_, Block>) { + self.decrypt_with_backend(BlocksCtx { blocks }); + } - if pb > 1 { - let mut iter = blocks.chunks_exact_mut(pb); + /// Decrypt single block in-place. + #[inline] + fn decrypt_block(&self, block: &mut Block) { + let block = block.into(); + self.decrypt_with_backend(BlockCtx { block }); + } - for chunk in &mut iter { - self.decrypt_par_blocks(chunk.try_into().unwrap()) - } + /// Decrypt `in_block` and write result to `out_block`. + #[inline] + fn decrypt_block_b2b(&self, in_block: &Block, out_block: &mut Block) { + let block = (in_block, out_block).into(); + self.decrypt_with_backend(BlockCtx { block }); + } - blocks = iter.into_remainder(); + /// Decrypt blocks in-place. + #[inline] + fn decrypt_blocks(&self, blocks: &mut [Block]) { + let blocks = blocks.into(); + self.decrypt_with_backend(BlocksCtx { blocks }); + } + + /// Decrypt blocks buffer-to-buffer. + /// + /// Returns [`NotEqualError`] if provided `in_blocks` and `out_blocks` + /// have different lengths. + #[inline] + fn decrypt_blocks_b2b( + &self, + in_blocks: &[Block], + out_blocks: &mut [Block], + ) -> Result<(), NotEqualError> { + InOutBuf::new(in_blocks, out_blocks) + .map(|blocks| self.decrypt_with_backend(BlocksCtx { blocks })) + } + + /// Decrypt input and unpad it. Returns resulting ciphertext slice. + /// + /// Returns [`UnpadError`] if padding is malformed or if input length is + /// not multiple of `Self::BlockSize`. + #[cfg(feature = "block-padding")] + #[cfg_attr(docsrs, doc(cfg(feature = "block-padding")))] + #[inline] + fn decrypt_padded_inout<'inp, 'out, P: Padding>( + &self, + data: InOutBuf<'inp, 'out, u8>, + ) -> Result<&'out [u8], UnpadError> { + let (mut blocks, tail) = data.into_chunks(); + if !tail.is_empty() { + return Err(UnpadError); } + self.decrypt_blocks_inout(blocks.reborrow()); + P::unpad_blocks(blocks.into_out()) + } - for block in blocks { - self.decrypt_block(block); + /// Decrypt input and unpad it in-place. Returns resulting ciphertext slice. + /// + /// Returns [`UnpadError`] if padding is malformed or if input length is + /// not multiple of `Self::BlockSize`. + #[cfg(feature = "block-padding")] + #[cfg_attr(docsrs, doc(cfg(feature = "block-padding")))] + #[inline] + fn decrypt_padded<'a, P: Padding>( + &self, + buf: &'a mut [u8], + ) -> Result<&'a [u8], UnpadError> { + self.decrypt_padded_inout::

(buf.into()) + } + + /// Decrypt input and unpad it buffer-to-buffer. Returns resulting + /// ciphertext slice. + /// + /// Returns [`UnpadError`] if padding is malformed or if input length is + /// not multiple of `Self::BlockSize`. + #[cfg(feature = "block-padding")] + #[cfg_attr(docsrs, doc(cfg(feature = "block-padding")))] + #[inline] + fn decrypt_padded_b2b<'a, P: Padding>( + &self, + in_buf: &[u8], + out_buf: &'a mut [u8], + ) -> Result<&'a [u8], UnpadError> { + if out_buf.len() < in_buf.len() { + return Err(UnpadError); } + let n = in_buf.len(); + // note: `new` always returns `Ok` here + let buf = InOutBuf::new(in_buf, &mut out_buf[..n]).map_err(|_| UnpadError)?; + self.decrypt_padded_inout::

(buf) } } -/// Encrypt-only functionality for block ciphers with mutable access to `self`. +/// Encrypt-only functionality for block ciphers and modes with mutable access to `self`. /// -/// The main use case for this trait is hardware encryption engines which -/// require `&mut self` access to an underlying hardware peripheral. -pub trait BlockEncryptMut: BlockCipher { - /// Encrypt block in-place - fn encrypt_block_mut(&mut self, block: &mut Block); -} +/// The main use case for this trait is blocks modes, but it also can be used +/// for hardware cryptographic engines which require `&mut self` access to an +/// underlying hardware peripheral. +pub trait BlockEncryptMut: BlockSizeUser + Sized { + /// Encrypt data using backend provided to the rank-2 closure. + fn encrypt_with_backend_mut(&mut self, f: impl BlockClosure); + + /// Encrypt single `inout` block. + #[inline] + fn encrypt_block_inout_mut(&mut self, block: InOut<'_, '_, Block>) { + self.encrypt_with_backend_mut(BlockCtx { block }); + } -/// Decrypt-only functionality for block ciphers with mutable access to `self`. -/// -/// The main use case for this trait is hardware encryption engines which -/// require `&mut self` access to an underlying hardware peripheral. -pub trait BlockDecryptMut: BlockCipher { - /// Decrypt block in-place - fn decrypt_block_mut(&mut self, block: &mut Block); -} + /// Encrypt `inout` blocks. + #[inline] + fn encrypt_blocks_inout_mut(&mut self, blocks: InOutBuf<'_, '_, Block>) { + self.encrypt_with_backend_mut(BlocksCtx { blocks }); + } -impl BlockEncryptMut for Alg { + /// Encrypt single block in-place. + #[inline] fn encrypt_block_mut(&mut self, block: &mut Block) { - self.encrypt_block(block); + let block = block.into(); + self.encrypt_with_backend_mut(BlockCtx { block }); } -} -impl BlockDecryptMut for Alg { - fn decrypt_block_mut(&mut self, block: &mut Block) { - self.decrypt_block(block); + /// Encrypt `in_block` and write result to `out_block`. + #[inline] + fn encrypt_block_b2b_mut(&mut self, in_block: &Block, out_block: &mut Block) { + let block = (in_block, out_block).into(); + self.encrypt_with_backend_mut(BlockCtx { block }); } -} -// Impls of block cipher traits for reference types + /// Encrypt blocks in-place. + #[inline] + fn encrypt_blocks_mut(&mut self, blocks: &mut [Block]) { + let blocks = blocks.into(); + self.encrypt_with_backend_mut(BlocksCtx { blocks }); + } -impl BlockCipher for &Alg { - type BlockSize = Alg::BlockSize; - type ParBlocks = Alg::ParBlocks; -} + /// Encrypt blocks buffer-to-buffer. + /// + /// Returns [`NotEqualError`] if provided `in_blocks` and `out_blocks` + /// have different lengths. + #[inline] + fn encrypt_blocks_b2b_mut( + &mut self, + in_blocks: &[Block], + out_blocks: &mut [Block], + ) -> Result<(), NotEqualError> { + InOutBuf::new(in_blocks, out_blocks) + .map(|blocks| self.encrypt_with_backend_mut(BlocksCtx { blocks })) + } -impl BlockEncrypt for &Alg { + /// Pad input and encrypt. Returns resulting ciphertext slice. + /// + /// Returns [`PadError`] if length of output buffer is not sufficient. + #[cfg(feature = "block-padding")] + #[cfg_attr(docsrs, doc(cfg(feature = "block-padding")))] #[inline] - fn encrypt_block(&self, block: &mut Block) { - Alg::encrypt_block(self, block); + fn encrypt_padded_inout_mut<'inp, 'out, P: Padding>( + mut self, + data: InOutBufReserved<'inp, 'out, u8>, + ) -> Result<&'out [u8], PadError> { + let mut buf = data.into_padded_blocks::()?; + self.encrypt_blocks_inout_mut(buf.get_blocks()); + if let Some(block) = buf.get_tail_block() { + self.encrypt_block_inout_mut(block); + } + Ok(buf.into_out()) } + /// Pad input and encrypt in-place. Returns resulting ciphertext slice. + /// + /// Returns [`PadError`] if length of output buffer is not sufficient. + #[cfg(feature = "block-padding")] + #[cfg_attr(docsrs, doc(cfg(feature = "block-padding")))] #[inline] - fn encrypt_par_blocks(&self, blocks: &mut ParBlocks) { - Alg::encrypt_par_blocks(self, blocks); + fn encrypt_padded_mut<'a, P: Padding>( + self, + buf: &'a mut [u8], + msg_len: usize, + ) -> Result<&'a [u8], PadError> { + let buf = InOutBufReserved::from_mut_slice(buf, msg_len).map_err(|_| PadError)?; + self.encrypt_padded_inout_mut::

(buf) } + /// Pad input and encrypt buffer-to-buffer. Returns resulting ciphertext slice. + /// + /// Returns [`PadError`] if length of output buffer is not sufficient. + #[cfg(feature = "block-padding")] + #[cfg_attr(docsrs, doc(cfg(feature = "block-padding")))] #[inline] - fn encrypt_blocks(&self, blocks: &mut [Block]) { - Alg::encrypt_blocks(self, blocks); + fn encrypt_padded_b2b_mut<'a, P: Padding>( + self, + msg: &[u8], + out_buf: &'a mut [u8], + ) -> Result<&'a [u8], PadError> { + let buf = InOutBufReserved::from_slices(msg, out_buf).map_err(|_| PadError)?; + self.encrypt_padded_inout_mut::

(buf) } } -impl BlockDecrypt for &Alg { +/// Decrypt-only functionality for block ciphers and modes with mutable access to `self`. +/// +/// The main use case for this trait is blocks modes, but it also can be used +/// for hardware cryptographic engines which require `&mut self` access to an +/// underlying hardware peripheral. +pub trait BlockDecryptMut: BlockSizeUser + Sized { + /// Decrypt data using backend provided to the rank-2 closure. + fn decrypt_with_backend_mut(&mut self, f: impl BlockClosure); + + /// Decrypt single `inout` block. #[inline] - fn decrypt_block(&self, block: &mut Block) { - Alg::decrypt_block(self, block); + fn decrypt_block_inout_mut(&mut self, block: InOut<'_, '_, Block>) { + self.decrypt_with_backend_mut(BlockCtx { block }); } + /// Decrypt `inout` blocks. #[inline] - fn decrypt_par_blocks(&self, blocks: &mut ParBlocks) { - Alg::decrypt_par_blocks(self, blocks); + fn decrypt_blocks_inout_mut(&mut self, blocks: InOutBuf<'_, '_, Block>) { + self.decrypt_with_backend_mut(BlocksCtx { blocks }); } + /// Decrypt single block in-place. #[inline] - fn decrypt_blocks(&self, blocks: &mut [Block]) { - Alg::decrypt_blocks(self, blocks); + fn decrypt_block_mut(&mut self, block: &mut Block) { + let block = block.into(); + self.decrypt_with_backend_mut(BlockCtx { block }); + } + + /// Decrypt `in_block` and write result to `out_block`. + #[inline] + fn decrypt_block_b2b_mut(&mut self, in_block: &Block, out_block: &mut Block) { + let block = (in_block, out_block).into(); + self.decrypt_with_backend_mut(BlockCtx { block }); } -} -/// Trait for types which can be initialized from a block cipher. -pub trait FromBlockCipher { - /// Block cipher used for initialization. - type BlockCipher: BlockCipher; + /// Decrypt blocks in-place. + #[inline] + fn decrypt_blocks_mut(&mut self, blocks: &mut [Block]) { + let blocks = blocks.into(); + self.decrypt_with_backend_mut(BlocksCtx { blocks }); + } - /// Initialize instance from block cipher. - fn from_block_cipher(cipher: Self::BlockCipher) -> Self; + /// Decrypt blocks buffer-to-buffer. + /// + /// Returns [`NotEqualError`] if provided `in_blocks` and `out_blocks` + /// have different lengths. + #[inline] + fn decrypt_blocks_b2b_mut( + &mut self, + in_blocks: &[Block], + out_blocks: &mut [Block], + ) -> Result<(), NotEqualError> { + InOutBuf::new(in_blocks, out_blocks) + .map(|blocks| self.decrypt_with_backend_mut(BlocksCtx { blocks })) + } + + /// Decrypt input and unpad it. Returns resulting ciphertext slice. + /// + /// Returns [`UnpadError`] if padding is malformed or if input length is + /// not multiple of `Self::BlockSize`. + #[cfg(feature = "block-padding")] + #[cfg_attr(docsrs, doc(cfg(feature = "block-padding")))] + #[inline] + fn decrypt_padded_inout_mut<'inp, 'out, P: Padding>( + mut self, + data: InOutBuf<'inp, 'out, u8>, + ) -> Result<&'out [u8], UnpadError> { + let (mut blocks, tail) = data.into_chunks(); + if !tail.is_empty() { + return Err(UnpadError); + } + self.decrypt_blocks_inout_mut(blocks.reborrow()); + P::unpad_blocks(blocks.into_out()) + } + + /// Decrypt input and unpad it in-place. Returns resulting ciphertext slice. + /// + /// Returns [`UnpadError`] if padding is malformed or if input length is + /// not multiple of `Self::BlockSize`. + #[cfg(feature = "block-padding")] + #[cfg_attr(docsrs, doc(cfg(feature = "block-padding")))] + #[inline] + fn decrypt_padded_mut<'a, P: Padding>( + self, + buf: &'a mut [u8], + ) -> Result<&'a [u8], UnpadError> { + self.decrypt_padded_inout_mut::

(buf.into()) + } + + /// Decrypt input and unpad it buffer-to-buffer. Returns resulting + /// ciphertext slice. + /// + /// Returns [`UnpadError`] if padding is malformed or if input length is + /// not multiple of `Self::BlockSize`. + #[cfg(feature = "block-padding")] + #[cfg_attr(docsrs, doc(cfg(feature = "block-padding")))] + #[inline] + fn decrypt_padded_b2b_mut<'a, P: Padding>( + self, + in_buf: &[u8], + out_buf: &'a mut [u8], + ) -> Result<&'a [u8], UnpadError> { + if out_buf.len() < in_buf.len() { + return Err(UnpadError); + } + let n = in_buf.len(); + // note: `new` always returns `Ok` here + let buf = InOutBuf::new(in_buf, &mut out_buf[..n]).map_err(|_| UnpadError)?; + self.decrypt_padded_inout_mut::

(buf) + } } -/// Trait for types which can be initialized from a block cipher and nonce. -pub trait FromBlockCipherNonce { - /// Block cipher used for initialization. - type BlockCipher: BlockCipher; - /// Nonce size in bytes. - type NonceSize: ArrayLength; +impl BlockEncryptMut for Alg { + fn encrypt_with_backend_mut(&mut self, f: impl BlockClosure) { + self.encrypt_with_backend(f); + } +} - /// Initialize instance from block cipher and nonce. - fn from_block_cipher_nonce( - cipher: Self::BlockCipher, - nonce: &GenericArray, - ) -> Self; +impl BlockDecryptMut for Alg { + fn decrypt_with_backend_mut(&mut self, f: impl BlockClosure) { + self.decrypt_with_backend(f); + } } -impl FromKeyNonce for T -where - T: FromBlockCipherNonce, - T::BlockCipher: FromKey, -{ - type KeySize = ::KeySize; - type NonceSize = T::NonceSize; +impl BlockCipher for &Alg {} - fn new( - key: &GenericArray, - nonce: &GenericArray, - ) -> Self { - Self::from_block_cipher_nonce(T::BlockCipher::new(key), nonce) +impl BlockEncrypt for &Alg { + fn encrypt_with_backend(&self, f: impl BlockClosure) { + Alg::encrypt_with_backend(self, f); } } -impl FromKey for T -where - T: FromBlockCipher, - T::BlockCipher: FromKey, -{ - type KeySize = ::KeySize; +impl BlockDecrypt for &Alg { + fn decrypt_with_backend(&self, f: impl BlockClosure) { + Alg::decrypt_with_backend(self, f); + } +} + +/// Closure used in methods which operate over separate blocks. +struct BlockCtx<'inp, 'out, BS: ArrayLength> { + block: InOut<'inp, 'out, Block>, +} - fn new(key: &GenericArray) -> Self { - Self::from_block_cipher(T::BlockCipher::new(key)) +impl<'inp, 'out, BS: ArrayLength> BlockSizeUser for BlockCtx<'inp, 'out, BS> { + type BlockSize = BS; +} + +impl<'inp, 'out, BS: ArrayLength> BlockClosure for BlockCtx<'inp, 'out, BS> { + #[inline(always)] + fn call>(self, backend: &mut B) { + backend.proc_block(self.block); } +} - fn new_from_slice(key: &[u8]) -> Result { - T::BlockCipher::new_from_slice(key) - .map_err(|_| InvalidLength) - .map(Self::from_block_cipher) +/// Closure used in methods which operate over slice of blocks. +struct BlocksCtx<'inp, 'out, BS: ArrayLength> { + blocks: InOutBuf<'inp, 'out, Block>, +} + +impl<'inp, 'out, BS: ArrayLength> BlockSizeUser for BlocksCtx<'inp, 'out, BS> { + type BlockSize = BS; +} + +impl<'inp, 'out, BS: ArrayLength> BlockClosure for BlocksCtx<'inp, 'out, BS> { + #[inline(always)] + fn call>(self, backend: &mut B) { + if B::ParBlocksSize::USIZE > 1 { + let (chunks, tail) = self.blocks.into_chunks(); + for chunk in chunks { + backend.proc_par_blocks(chunk); + } + backend.proc_tail_blocks(tail); + } else { + for block in self.blocks { + backend.proc_block(block); + } + } } } + +/// Implement simple block backend +#[macro_export] +macro_rules! impl_simple_block_encdec { + ( + <$($N:ident$(:$b0:ident$(+$b:ident)*)?),*> + $cipher:ident, $block_size:ty, $state:ident, $block:ident, + encrypt: $enc_block:block + decrypt: $dec_block:block + ) => { + impl<$($N$(:$b0$(+$b)*)?),*> $crate::BlockSizeUser for $cipher<$($N),*> { + type BlockSize = $block_size; + } + + impl<$($N$(:$b0$(+$b)*)?),*> $crate::BlockEncrypt for $cipher<$($N),*> { + fn encrypt_with_backend(&self, f: impl $crate::BlockClosure) { + struct EncBack<'a, $($N$(:$b0$(+$b)*)?),* >(&'a $cipher<$($N),*>); + + impl<'a, $($N$(:$b0$(+$b)*)?),* > $crate::BlockSizeUser for EncBack<'a, $($N),*> { + type BlockSize = $block_size; + } + + impl<'a, $($N$(:$b0$(+$b)*)?),* > $crate::ParBlocksSizeUser for EncBack<'a, $($N),*> { + type ParBlocksSize = $crate::consts::U1; + } + + impl<'a, $($N$(:$b0$(+$b)*)?),* > $crate::BlockBackend for EncBack<'a, $($N),*> { + #[inline(always)] + fn proc_block( + &mut self, + mut $block: $crate::inout::InOut<'_, '_, $crate::Block> + ) { + let $state: &$cipher<$($N),*> = self.0; + $enc_block + } + } + + f.call(&mut EncBack(self)) + } + } + + impl<$($N$(:$b0$(+$b)*)?),*> $crate::BlockDecrypt for $cipher<$($N),*> { + fn decrypt_with_backend(&self, f: impl $crate::BlockClosure) { + struct DecBack<'a, $($N$(:$b0$(+$b)*)?),* >(&'a $cipher<$($N),*>); + + impl<'a, $($N$(:$b0$(+$b)*)?),* > $crate::BlockSizeUser for DecBack<'a, $($N),*> { + type BlockSize = $block_size; + } + + impl<'a, $($N$(:$b0$(+$b)*)?),* > $crate::ParBlocksSizeUser for DecBack<'a, $($N),*> { + type ParBlocksSize = $crate::consts::U1; + } + + impl<'a, $($N$(:$b0$(+$b)*)?),* > $crate::BlockBackend for DecBack<'a, $($N),*> { + #[inline(always)] + fn proc_block( + &mut self, + mut $block: $crate::inout::InOut<'_, '_, $crate::Block> + ) { + let $state: &$cipher<$($N),*> = self.0; + $dec_block + } + } + + f.call(&mut DecBack(self)) + } + } + }; + ( + $cipher:ident, $block_size:ty, $state:ident, $block:ident, + encrypt: $enc_block:block + decrypt: $dec_block:block + ) => { + $crate::impl_simple_block_encdec!( + <> $cipher, $block_size, $state, $block, + encrypt: $enc_block + decrypt: $dec_block + ); + }; +} diff --git a/cipher/src/dev/block.rs b/cipher/src/dev/block.rs index fc53b9435..d3115c5d8 100644 --- a/cipher/src/dev/block.rs +++ b/cipher/src/dev/block.rs @@ -6,24 +6,24 @@ pub use blobby; #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "dev")))] macro_rules! block_cipher_test { - ($name:ident, $test_name:expr, $cipher:ty) => { + ($name:ident, $test_name:expr, $cipher:ty $(,)?) => { #[test] fn $name() { - use cipher::generic_array::{typenum::Unsigned, GenericArray}; use cipher::{ - blobby::Blob3Iterator, BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher, + blobby::Blob3Iterator, generic_array::GenericArray, typenum::Unsigned, + BlockDecryptMut, BlockEncryptMut, BlockSizeUser, KeyInit, }; fn run_test(key: &[u8], pt: &[u8], ct: &[u8]) -> bool { - let state = <$cipher as NewBlockCipher>::new_from_slice(key).unwrap(); + let mut state = <$cipher as KeyInit>::new_from_slice(key).unwrap(); let mut block = GenericArray::clone_from_slice(pt); - state.encrypt_block(&mut block); + state.encrypt_block_mut(&mut block); if ct != block.as_slice() { return false; } - state.decrypt_block(&mut block); + state.decrypt_block_mut(&mut block); if pt != block.as_slice() { return false; } @@ -32,15 +32,12 @@ macro_rules! block_cipher_test { } fn run_par_test(key: &[u8], pt: &[u8]) -> bool { - type ParBlocks = <$cipher as BlockCipher>::ParBlocks; - type BlockSize = <$cipher as BlockCipher>::BlockSize; - type Block = GenericArray; - type ParBlock = GenericArray; + type Block = cipher::Block<$cipher>; - let state = <$cipher as NewBlockCipher>::new_from_slice(key).unwrap(); + let mut state = <$cipher as KeyInit>::new_from_slice(key).unwrap(); let block = Block::clone_from_slice(pt); - let mut blocks1 = ParBlock::default(); + let mut blocks1 = vec![block; 101]; for (i, b) in blocks1.iter_mut().enumerate() { *b = block; b[0] = b[0].wrapping_add(i as u8); @@ -49,9 +46,9 @@ macro_rules! block_cipher_test { // check that `encrypt_blocks` and `encrypt_block` // result in the same ciphertext - state.encrypt_blocks(&mut blocks1); + state.encrypt_blocks_mut(&mut blocks1); for b in blocks2.iter_mut() { - state.encrypt_block(b); + state.encrypt_block_mut(b); } if blocks1 != blocks2 { return false; @@ -59,9 +56,9 @@ macro_rules! block_cipher_test { // check that `encrypt_blocks` and `encrypt_block` // result in the same plaintext - state.decrypt_blocks(&mut blocks1); + state.decrypt_blocks_mut(&mut blocks1); for b in blocks2.iter_mut() { - state.decrypt_block(b); + state.decrypt_block_mut(b); } if blocks1 != blocks2 { return false; @@ -70,7 +67,6 @@ macro_rules! block_cipher_test { true } - let pb = <$cipher as BlockCipher>::ParBlocks::to_usize(); let data = include_bytes!(concat!("data/", $test_name, ".blb")); for (i, row) in Blob3Iterator::new(data).unwrap().enumerate() { let [key, pt, ct] = row.unwrap(); @@ -86,58 +82,308 @@ macro_rules! block_cipher_test { } // test parallel blocks encryption/decryption - if pb != 1 { - if !run_par_test(key, pt) { - panic!( - "\n\ - Failed parallel test №{}\n\ - key:\t{:?}\n\ - plaintext:\t{:?}\n\ - ciphertext:\t{:?}\n", - i, key, pt, ct, - ); - } + if !run_par_test(key, pt) { + panic!( + "\n\ + Failed parallel test №{}\n\ + key:\t{:?}\n\ + plaintext:\t{:?}\n\ + ciphertext:\t{:?}\n", + i, key, pt, ct, + ); + } + } + } + }; +} + +/// Define block mode encryption test +#[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] +macro_rules! block_mode_enc_test { + ($name:ident, $test_name:expr, $cipher:ty $(,)?) => { + #[test] + fn $name() { + use cipher::{ + blobby::Blob4Iterator, generic_array::GenericArray, inout::InOutBuf, + typenum::Unsigned, BlockEncryptMut, BlockSizeUser, KeyIvInit, + }; + + fn run_test(key: &[u8], iv: &[u8], pt: &[u8], ct: &[u8]) -> bool { + assert_eq!(pt.len(), ct.len()); + // test block-by-block processing + let mut state = <$cipher as KeyIvInit>::new_from_slices(key, iv).unwrap(); + + let mut out = vec![0u8; ct.len()]; + let mut buf = InOutBuf::new(pt, &mut out).unwrap(); + let (blocks, tail) = buf.reborrow().into_chunks(); + assert_eq!(tail.len(), 0); + for block in blocks { + state.encrypt_block_inout_mut(block); + } + if buf.get_out() != ct { + return false; + } + + // test multi-block processing + let mut state = <$cipher as KeyIvInit>::new_from_slices(key, iv).unwrap(); + buf.get_out().iter_mut().for_each(|b| *b = 0); + let (blocks, _) = buf.reborrow().into_chunks(); + state.encrypt_blocks_inout_mut(blocks); + if buf.get_out() != ct { + return false; + } + + true + } + + let data = include_bytes!(concat!("data/", $test_name, ".blb")); + for (i, row) in Blob4Iterator::new(data).unwrap().enumerate() { + let [key, iv, pt, ct] = row.unwrap(); + if !run_test(key, iv, pt, ct) { + panic!( + "\n\ + Failed test №{}\n\ + key:\t{:?}\n\ + iv:\t{:?}\n\ + plaintext:\t{:?}\n\ + ciphertext:\t{:?}\n", + i, key, iv, pt, ct, + ); + } + } + } + }; +} + +/// Define block mode decryption test +#[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] +macro_rules! block_mode_dec_test { + ($name:ident, $test_name:expr, $cipher:ty $(,)?) => { + #[test] + fn $name() { + use cipher::{ + blobby::Blob4Iterator, generic_array::GenericArray, inout::InOutBuf, + typenum::Unsigned, BlockDecryptMut, BlockSizeUser, KeyIvInit, + }; + + fn run_test(key: &[u8], iv: &[u8], pt: &[u8], ct: &[u8]) -> bool { + assert_eq!(pt.len(), ct.len()); + // test block-by-block processing + let mut state = <$cipher as KeyIvInit>::new_from_slices(key, iv).unwrap(); + + let mut out = vec![0u8; pt.len()]; + let mut buf = InOutBuf::new(ct, &mut out).unwrap(); + let (blocks, tail) = buf.reborrow().into_chunks(); + assert_eq!(tail.len(), 0); + for block in blocks { + state.decrypt_block_inout_mut(block); + } + if buf.get_out() != pt { + return false; + } + + // test multi-block processing + let mut state = <$cipher as KeyIvInit>::new_from_slices(key, iv).unwrap(); + buf.get_out().iter_mut().for_each(|b| *b = 0); + let (blocks, _) = buf.reborrow().into_chunks(); + state.decrypt_blocks_inout_mut(blocks); + if buf.get_out() != pt { + return false; + } + + true + } + + let data = include_bytes!(concat!("data/", $test_name, ".blb")); + for (i, row) in Blob4Iterator::new(data).unwrap().enumerate() { + let [key, iv, pt, ct] = row.unwrap(); + if !run_test(key, iv, pt, ct) { + panic!( + "\n\ + Failed test №{}\n\ + key:\t{:?}\n\ + iv:\t{:?}\n\ + plaintext:\t{:?}\n\ + ciphertext:\t{:?}\n", + i, key, iv, pt, ct, + ); } } - // test if cipher can be cloned - let key = Default::default(); - let _ = <$cipher as NewBlockCipher>::new(&key).clone(); } }; } -/// Define block cipher benchmark +/// Define `IvState` test #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "dev")))] -macro_rules! block_cipher_bench { - ($cipher:path, $key_len:expr) => { - extern crate test; +macro_rules! iv_state_test { + ($name:ident, $cipher:ty, encrypt $(,)?) => { + $crate::iv_state_test!($name, $cipher, encrypt_blocks_mut); + }; + ($name:ident, $cipher:ty, decrypt $(,)?) => { + $crate::iv_state_test!($name, $cipher, decrypt_blocks_mut); + }; + ($name:ident, $cipher:ty, apply_ks $(,)?) => { + $crate::iv_state_test!($name, $cipher, apply_keystream_blocks); + }; + ($name:ident, $cipher:ty, $method:ident $(,)?) => { + #[test] + fn $name() { + use cipher::*; + + let mut blocks = [Block::<$cipher>::default(); 32]; + + for (i, block) in blocks.iter_mut().enumerate() { + for (j, b) in block.iter_mut().enumerate() { + *b = (i + j) as u8; + } + } + + let mut key = Key::<$cipher>::default(); + let mut iv = Iv::<$cipher>::default(); + key.iter_mut().for_each(|b| *b = 0x42); + iv.iter_mut().for_each(|b| *b = 0x24); - use cipher::{BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher}; - use test::Bencher; + let mut cipher = <$cipher>::new(&key, &iv); + let mut target = blocks.clone(); + cipher.$method(&mut target); + + for i in 0..32 { + let mut blocks = blocks.clone(); + let (b1, b2) = blocks.split_at_mut(i); + let mut cipher1 = <$cipher>::new(&key, &iv); + cipher1.$method(b1); + let temp_iv = cipher1.iv_state(); + let mut cipher2 = <$cipher>::new(&key, &temp_iv); + cipher2.$method(b2); + assert_eq!(blocks, target); + } + } + }; +} +/// Define block encryptor benchmark +#[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] +macro_rules! block_encryptor_bench { + (Key: $cipher:ty, $block_name:ident, $blocks_name:ident $(,)? ) => { + $crate::block_encryptor_bench!( + { + use $crate::KeyInit; + let key = test::black_box(Default::default()); + <$cipher>::new(&key) + }, + $cipher, + $block_name, + $blocks_name, + ); + }; + (KeyIv: $cipher:ty, $block_name:ident, $blocks_name:ident $(,)? ) => { + $crate::block_encryptor_bench!( + { + use $crate::KeyIvInit; + let key = test::black_box(Default::default()); + let iv = test::black_box(Default::default()); + <$cipher>::new(&key, &iv) + }, + $cipher, + $block_name, + $blocks_name, + ); + }; + ($init:block, $cipher:ty, $block_name:ident, $blocks_name:ident $(,)? ) => { + #[bench] + pub fn $block_name(bh: &mut test::Bencher) { + use cipher::BlockEncryptMut; + + let mut cipher = $init; + let mut blocks = vec![Default::default(); 1024]; + + bh.iter(|| { + for block in blocks.iter_mut() { + cipher.encrypt_block_mut(block); + } + test::black_box(&blocks); + }); + bh.bytes = (blocks.len() * blocks[0].len()) as u64; + } + + #[bench] + pub fn $blocks_name(bh: &mut test::Bencher) { + use cipher::BlockEncryptMut; + + let mut cipher = $init; + let mut blocks = vec![Default::default(); 1024]; + + bh.iter(|| { + cipher.encrypt_blocks_mut(&mut blocks); + test::black_box(&blocks); + }); + bh.bytes = (blocks.len() * blocks[0].len()) as u64; + } + }; +} + +/// Define block decryptor benchmark +#[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] +macro_rules! block_decryptor_bench { + (Key: $cipher:ty, $block_name:ident, $blocks_name:ident $(,)? ) => { + $crate::block_decryptor_bench!( + { + use $crate::KeyInit; + let key = test::black_box(Default::default()); + <$cipher>::new(&key) + }, + $cipher, + $block_name, + $blocks_name, + ); + }; + (KeyIv: $cipher:ty, $block_name:ident, $blocks_name:ident $(,)? ) => { + $crate::block_decryptor_bench!( + { + use $crate::KeyIvInit; + let key = test::black_box(Default::default()); + let iv = test::black_box(Default::default()); + <$cipher>::new(&key, &iv) + }, + $cipher, + $block_name, + $blocks_name, + ); + }; + ($init:block, $cipher:ty, $block_name:ident, $blocks_name:ident $(,)? ) => { #[bench] - pub fn encrypt(bh: &mut Bencher) { - let state = <$cipher>::new_from_slice(&[1u8; $key_len]).unwrap(); - let mut block = Default::default(); + pub fn $block_name(bh: &mut test::Bencher) { + use cipher::BlockDecryptMut; + + let mut cipher = $init; + let mut blocks = vec![Default::default(); 1024]; bh.iter(|| { - state.encrypt_block(&mut block); - test::black_box(&block); + for block in blocks.iter_mut() { + cipher.decrypt_block_mut(block); + } + test::black_box(&blocks); }); - bh.bytes = block.len() as u64; + bh.bytes = (blocks.len() * blocks[0].len()) as u64; } #[bench] - pub fn decrypt(bh: &mut Bencher) { - let state = <$cipher>::new_from_slice(&[1u8; $key_len]).unwrap(); - let mut block = Default::default(); + pub fn $blocks_name(bh: &mut test::Bencher) { + use cipher::BlockDecryptMut; + + let mut cipher = $init; + let mut blocks = vec![Default::default(); 1024]; bh.iter(|| { - state.decrypt_block(&mut block); - test::black_box(&block); + cipher.decrypt_blocks_mut(&mut blocks); + test::black_box(&blocks); }); - bh.bytes = block.len() as u64; + bh.bytes = (blocks.len() * blocks[0].len()) as u64; } }; } diff --git a/cipher/src/dev/stream.rs b/cipher/src/dev/stream.rs index 43757ffa1..0496345d4 100644 --- a/cipher/src/dev/stream.rs +++ b/cipher/src/dev/stream.rs @@ -4,11 +4,11 @@ #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "dev")))] macro_rules! stream_cipher_test { - ($name:ident, $cipher:ty, $test_name:expr) => { + ($name:ident, $test_name:expr, $cipher:ty $(,)?) => { #[test] fn $name() { use cipher::generic_array::GenericArray; - use cipher::{blobby::Blob4Iterator, NewCipher, StreamCipher}; + use cipher::{blobby::Blob4Iterator, KeyIvInit, StreamCipher}; let data = include_bytes!(concat!("data/", $test_name, ".blb")); for (i, row) in Blob4Iterator::new(data).unwrap().enumerate() { @@ -44,7 +44,7 @@ macro_rules! stream_cipher_seek_test { #[test] fn $name() { use cipher::generic_array::GenericArray; - use cipher::{NewCipher, StreamCipher, StreamCipherSeek}; + use cipher::{KeyIvInit, StreamCipher, StreamCipherSeek}; fn get_cipher() -> $cipher { <$cipher>::new(&Default::default(), &Default::default()) @@ -89,153 +89,57 @@ macro_rules! stream_cipher_seek_test { }; } -/// Test core functionality of asynchronous stream cipher +/// Create stream cipher benchmarks #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "dev")))] -macro_rules! stream_cipher_async_test { - ($name:ident, $test_name:expr, $cipher:ty) => { - #[test] - fn $name() { - use cipher::generic_array::GenericArray; - use cipher::{blobby::Blob4Iterator, AsyncStreamCipher, NewCipher}; - - fn run_test( - key: &[u8], - iv: &[u8], - plaintext: &[u8], - ciphertext: &[u8], - ) -> Option<&'static str> { - for n in 1..=plaintext.len() { - let mut mode = <$cipher>::new_from_slices(key, iv).unwrap(); - let mut buf = plaintext.to_vec(); - for chunk in buf.chunks_mut(n) { - mode.encrypt(chunk); - } - if buf != &ciphertext[..] { - return Some("encrypt"); - } - } - - for n in 1..=plaintext.len() { - let mut mode = <$cipher>::new_from_slices(key, iv).unwrap(); - let mut buf = ciphertext.to_vec(); - for chunk in buf.chunks_mut(n) { - mode.decrypt(chunk); - } - if buf != &plaintext[..] { - return Some("decrypt"); - } - } - - None - } - - let data = include_bytes!(concat!("data/", $test_name, ".blb")); - - for (i, row) in Blob4Iterator::new(data).unwrap().enumerate() { - let [key, iv, pt, ct] = row.unwrap(); - if let Some(desc) = run_test(key, iv, pt, ct) { - panic!( - "\n\ - Failed test №{}: {}\n\ - key:\t{:?}\n\ - iv:\t{:?}\n\ - plaintext:\t{:?}\n\ - ciphertext:\t{:?}\n", - i, desc, key, iv, pt, ct, - ); - } - } - } +macro_rules! stream_cipher_bench { + ( + $cipher:ty; + $($name:ident $bs:expr;)* + ) => { + $crate::stream_cipher_bench!( + Init: { + use $crate::KeyIvInit; + let key = test::black_box(Default::default()); + let iv = test::black_box(Default::default()); + <$cipher>::new(&key, &iv) + }; + $($name $bs;)* + ); }; -} - -/// Create synchronous stream cipher benchmarks -#[macro_export] -#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] -macro_rules! stream_cipher_sync_bench { - ($name:ident, $cipher:path, $data_len:expr) => { - #[bench] - pub fn $name(bh: &mut Bencher) { - let key = Default::default(); - let nonce = Default::default(); - let mut cipher = <$cipher>::new(&key, &nonce); - let mut data = get_data($data_len); - - bh.iter(|| { - cipher.apply_keystream(&mut data); - test::black_box(&data); - }); - bh.bytes = data.len() as u64; - } - }; - ($cipher:path) => { - extern crate test; - - use cipher::{generic_array::GenericArray, NewCipher, StreamCipher}; - use test::Bencher; - - #[inline(never)] - fn get_data(n: usize) -> Vec { - vec![77; n] - } - - $crate::stream_cipher_sync_bench!(bench1_10, $cipher, 10); - $crate::stream_cipher_sync_bench!(bench2_100, $cipher, 100); - $crate::stream_cipher_sync_bench!(bench3_1000, $cipher, 1000); - $crate::stream_cipher_sync_bench!(bench4_10000, $cipher, 10000); - $crate::stream_cipher_sync_bench!(bench5_100000, $cipher, 100000); - }; -} - -/// Create asynchronous stream cipher benchmarks -#[macro_export] -#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] -macro_rules! stream_cipher_async_bench { - ($enc_name:ident, $dec_name:ident, $cipher:path, $data_len:expr) => { - #[bench] - pub fn $enc_name(bh: &mut Bencher) { - let key = Default::default(); - let nonce = Default::default(); - let mut cipher = <$cipher>::new(&key, &nonce); - let mut data = get_data($data_len); - - bh.iter(|| { - cipher.encrypt(&mut data); - test::black_box(&data); - }); - bh.bytes = data.len() as u64; - } - - #[bench] - pub fn $dec_name(bh: &mut Bencher) { - let key = Default::default(); - let nonce = Default::default(); - let mut cipher = <$cipher>::new(&key, &nonce); - let mut data = get_data($data_len); - - bh.iter(|| { - cipher.decrypt(&mut data); - test::black_box(&data); - }); - bh.bytes = data.len() as u64; - } + ( + Key: $cipher:ty; + $($name:ident $bs:expr;)* + ) => { + $crate::stream_cipher_bench!( + Init: { + use $crate::KeyInit; + let key = test::black_box(Default::default()); + let iv = test::black_box(Default::default()); + <$cipher>::new(&key, &iv) + }; + $($name $bs;)* + ); }; - ($cipher:path) => { - extern crate test; - - use cipher::{generic_array::GenericArray, AsyncStreamCipher, NewCipher}; - use test::Bencher; - - #[inline(never)] - fn get_data(n: usize) -> Vec { - vec![77; n] - } - - $crate::stream_cipher_async_bench!(encrypt_10, decrypt_10, $cipher, 10); - $crate::stream_cipher_async_bench!(encrypt_100, decrypt_100, $cipher, 100); - $crate::stream_cipher_async_bench!(encrypt_1000, decrypt_1000, $cipher, 1000); - $crate::stream_cipher_async_bench!(encrypt_10000, decrypt_10000, $cipher, 10000); - $crate::stream_cipher_async_bench!(encrypt_100000, decrypt_100000, $cipher, 100000); + ( + Init: $init:expr; + $($name:ident $bs:expr;)* + ) => { + $( + #[bench] + fn $name(b: &mut test::Bencher) { + use $crate::StreamCipher; + + let mut cipher = $init; + let mut buf = vec![0; $bs]; + + b.iter(|| { + cipher.apply_keystream(&mut buf); + test::black_box(&buf); + }); + + b.bytes = $bs; + } + )* }; } diff --git a/cipher/src/errors.rs b/cipher/src/errors.rs index 644495e80..0dc32740e 100644 --- a/cipher/src/errors.rs +++ b/cipher/src/errors.rs @@ -2,18 +2,24 @@ use core::fmt; -/// The error type returned when stream cipher has reached the end of a keystream. +/// This error is returned by the [`StreamCipher`][crate::stream::StreamCipher] +/// trait methods. +/// +/// Usually it's used in cases when stream cipher has reached the end +/// of a keystream, but also can be used if lengths of provided input +/// and output buffers are not equal. #[derive(Copy, Clone, Debug)] -pub struct LoopError; +pub struct StreamCipherError; -impl fmt::Display for LoopError { +impl fmt::Display for StreamCipherError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.write_str("Loop Error") } } #[cfg(feature = "std")] -impl std::error::Error for LoopError {} +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl std::error::Error for StreamCipherError {} /// The error type returned when a cipher position can not be represented /// by the requested type. @@ -26,45 +32,12 @@ impl fmt::Display for OverflowError { } } -impl From for LoopError { - fn from(_: OverflowError) -> LoopError { - LoopError +impl From for StreamCipherError { + fn from(_: OverflowError) -> StreamCipherError { + StreamCipherError } } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for OverflowError {} - -/// The error type returned when key and/or nonce used in the [`FromKey`] -/// and [`FromKeyNonce`] slice-based methods had an invalid length. -/// -/// [`FromKey`]: crate::FromKey -/// [`FromKeyNonce`]: crate::FromKeyNonce -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub struct InvalidLength; - -impl fmt::Display for InvalidLength { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.write_str("Invalid Length") - } -} - -#[cfg(feature = "std")] -impl std::error::Error for InvalidLength {} - -/// The error type returned by the [`BlockModeEncryptWrapper`] and -/// [`BlockModeDecryptWrapper`] types. -/// -/// [`BlockModeEncryptWrapper`]: crate::BlockModeEncryptWrapper -/// [`BlockModeDecryptWrapper`]: crate::BlockModeDecryptWrapper -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub struct BlockModeError; - -impl fmt::Display for BlockModeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.write_str("Invalid Length") - } -} - -#[cfg(feature = "std")] -impl std::error::Error for BlockModeError {} diff --git a/cipher/src/lib.rs b/cipher/src/lib.rs index 4a9a3ebe1..fff7fd98e 100644 --- a/cipher/src/lib.rs +++ b/cipher/src/lib.rs @@ -1,24 +1,35 @@ //! This crate defines a set of traits which describe the functionality of -//! [block ciphers][1] and [stream ciphers][2]. +//! [block ciphers][1], [block modes][2], and [stream ciphers][3]. //! //! [1]: https://en.wikipedia.org/wiki/Block_cipher -//! [2]: https://en.wikipedia.org/wiki/Stream_cipher +//! [2]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation +//! [3]: https://en.wikipedia.org/wiki/Stream_cipher #![no_std] #![cfg_attr(docsrs, feature(doc_cfg))] #![doc( - html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg", - html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg" + html_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/cipher/0.4.0" )] -#![forbid(unsafe_code)] #![warn(missing_docs, rust_2018_idioms)] +pub use crypto_common; +pub use inout; + #[cfg(feature = "std")] extern crate std; #[cfg(feature = "rand_core")] #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] -pub use rand_core; +pub use crypto_common::rand_core; + +#[cfg(feature = "block-padding")] +#[cfg_attr(docsrs, doc(cfg(feature = "block-padding")))] +pub use inout::block_padding; + +#[cfg(feature = "zeroize")] +pub use zeroize; #[cfg(feature = "dev")] pub use blobby; @@ -26,114 +37,31 @@ pub use blobby; mod block; #[cfg(feature = "dev")] mod dev; -pub mod errors; -mod mode; +mod errors; mod stream; - -#[cfg(feature = "mode_wrapper")] -mod mode_wrapper; - -pub use crate::{block::*, mode::*, stream::*}; -pub use generic_array::{self, typenum::consts}; -#[cfg(feature = "mode_wrapper")] -pub use mode_wrapper::{BlockModeDecryptWrapper, BlockModeEncryptWrapper}; - -use crate::errors::InvalidLength; -use generic_array::{typenum::Unsigned, ArrayLength, GenericArray}; -#[cfg(feature = "rand_core")] -use rand_core::{CryptoRng, RngCore}; - -// note: ideally the following traits would be defined in the `crypto-common` crate, -// but it would make impossible the generic impls over `T: FromBlockCipher(Nonce)` -// in the `block` module, see the following link for proposal to change it: -// https://internals.rust-lang.org/t/14125 - -/// Trait for types which can be created from key and nonce. -pub trait FromKeyNonce: Sized { - /// Key size in bytes. - type KeySize: ArrayLength; - - /// Nonce size in bytes. - type NonceSize: ArrayLength; - - /// Create new value from fixed length key and nonce. - fn new( - key: &GenericArray, - nonce: &GenericArray, - ) -> Self; - - /// Create new value from variable length key and nonce. - #[inline] - fn new_from_slices(key: &[u8], nonce: &[u8]) -> Result { - let kl = Self::KeySize::to_usize(); - let nl = Self::NonceSize::to_usize(); - if key.len() != kl || nonce.len() != nl { - Err(InvalidLength) - } else { - let key = GenericArray::from_slice(key); - let nonce = GenericArray::from_slice(nonce); - Ok(Self::new(key, nonce)) - } - } - - /// Generate a random key using the provided [`CryptoRng`]. - #[cfg(feature = "rand_core")] - #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] - #[inline] - fn generate_key(mut rng: impl CryptoRng + RngCore) -> GenericArray { - let mut key = GenericArray::::default(); - rng.fill_bytes(&mut key); - key - } - - /// Generate a random nonce using the provided [`CryptoRng`]. - #[cfg(feature = "rand_core")] - #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] - #[inline] - fn generate_nonce(mut rng: impl CryptoRng + RngCore) -> GenericArray { - let mut nonce = GenericArray::::default(); - rng.fill_bytes(&mut nonce); - nonce - } - - /// Generate random key and nonce using the provided [`CryptoRng`]. - #[cfg(feature = "rand_core")] - #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] - #[inline] - fn generate_key_nonce( - mut rng: impl CryptoRng + RngCore, - ) -> ( - GenericArray, - GenericArray, - ) { - (Self::generate_key(&mut rng), Self::generate_nonce(&mut rng)) - } +mod stream_core; +mod stream_wrapper; + +pub use crate::{block::*, errors::*, stream::*, stream_core::*, stream_wrapper::*}; +pub use crypto_common::{ + generic_array, + typenum::{self, consts}, + AlgorithmName, Block, InnerIvInit, InvalidLength, Iv, IvSizeUser, Key, KeyInit, KeyIvInit, + KeySizeUser, +}; +use generic_array::{ArrayLength, GenericArray}; + +/// Trait for loading current IV state. +pub trait IvState: IvSizeUser { + /// Returns current IV state. + fn iv_state(&self) -> Iv; } -/// Trait for types which can be created from key. -pub trait FromKey: Sized { - /// Key size in bytes. - type KeySize: ArrayLength; - - /// Create new value from fixed size key. - fn new(key: &GenericArray) -> Self; - - /// Create new value from variable size key. - fn new_from_slice(key: &[u8]) -> Result { - if key.len() != Self::KeySize::to_usize() { - Err(InvalidLength) - } else { - Ok(Self::new(GenericArray::from_slice(key))) - } - } - - /// Generate a random key using the provided [`CryptoRng`]. - #[cfg(feature = "rand_core")] - #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] - #[inline] - fn generate_key(mut rng: impl CryptoRng + RngCore) -> GenericArray { - let mut key = GenericArray::::default(); - rng.fill_bytes(&mut key); - key - } +/// Types which process blocks in parallel. +pub trait ParBlocksSizeUser: BlockSizeUser { + /// Number of blocks which can be processed in parallel. + type ParBlocksSize: ArrayLength>; } + +/// Parallel blocks on which [`ParBlocksSizeUser`] implementors operate. +pub type ParBlocks = GenericArray, ::ParBlocksSize>; diff --git a/cipher/src/mode.rs b/cipher/src/mode.rs deleted file mode 100644 index 336f50cfa..000000000 --- a/cipher/src/mode.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::{BlockCipher, FromKeyNonce}; -use generic_array::{ArrayLength, GenericArray}; - -/// Trait for types which implement a block cipher [mode of operation][1]. -/// -/// [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation -pub trait BlockMode { - /// Size of the block in bytes - type BlockSize: ArrayLength; -} - -/// Trait for a block cipher mode of operation block-level encryptor. -/// -/// This trait operates only on blocks, for convinient slice-based methods with padding -/// see the [`BlockModeEncryptWrapper`][crate::BlockModeEncryptWrapper] type. -pub trait BlockModeEncrypt: BlockMode { - /// Encrypt blocks of data. - fn encrypt_blocks(&mut self, blocks: &mut [GenericArray]); -} - -/// Trait for a block cipher mode of operation block-level decryptor. -/// -/// This trait operates only on blocks, for convinient slice-based methods with padding -/// see the [`BlockModeDecryptWrapper`][crate::BlockModeDecryptWrapper] type. -pub trait BlockModeDecrypt: BlockMode { - /// Decrypt blocks of data. - fn decrypt_blocks(&mut self, blocks: &mut [GenericArray]); -} - -/// Trait for a block mode, used to obtain the current state in the form of an IV -/// that can initialize a BlockMode later and resume the original operation. -/// -/// The IV value SHOULD be used for resuming operations only and MUST NOT be -/// exposed to attackers. Failing to comply with this requirement breaks -/// unpredictability and opens attack venues (see e.g. [1], sec. 3.6.2). -/// -/// [1]: https://www.cs.umd.edu/~jkatz/imc.html -pub trait BlockModeIvState: FromKeyNonce { - /// Returns the IV needed to process the following block. This value MUST - /// NOT be exposed to attackers. - fn iv_state(&self) -> GenericArray; -} diff --git a/cipher/src/mode_wrapper.rs b/cipher/src/mode_wrapper.rs deleted file mode 100644 index 36f379c3e..000000000 --- a/cipher/src/mode_wrapper.rs +++ /dev/null @@ -1,194 +0,0 @@ -//! Convinience wrapper around types which implement `BlockMode`. - -use crate::errors::BlockModeError; -use crate::{BlockModeDecrypt, BlockModeEncrypt, FromBlockCipherNonce}; -use block_buffer::{block_padding::Padding, BlockBuffer, LazyBlockBuffer}; -use core::{marker::PhantomData, slice::from_mut}; -use generic_array::{typenum::Unsigned, GenericArray}; - -/// Convinience wrapper around the [`BlockModeEncrypt`] trait, which handles -/// data buffering and provides slice-based methods. -pub struct BlockModeEncryptWrapper> { - inner: M, - buffer: BlockBuffer, - _p: PhantomData

, -} - -impl FromBlockCipherNonce for BlockModeEncryptWrapper -where - M: BlockModeEncrypt + FromBlockCipherNonce, - P: Padding, -{ - type BlockCipher = M::BlockCipher; - type NonceSize = M::NonceSize; - - fn from_block_cipher_nonce( - cipher: Self::BlockCipher, - nonce: &GenericArray, - ) -> Self { - Self { - inner: M::from_block_cipher_nonce(cipher, nonce), - buffer: Default::default(), - _p: Default::default(), - } - } -} - -impl BlockModeEncryptWrapper -where - M: BlockModeEncrypt, - P: Padding, -{ - /// Encrypt part of a plaintext. - /// - /// This mehthod MUST be used in conjuction with the [`encrypt_final`][Self::encrypt_final] method, - /// otherwise plaintext will not be properly padded and may be truncated. - /// - /// The method encrypts plaintext in `data`, writes the resulting plaintext - /// into `out_buf`, and returns it in the `Ok` variant. If a whole message - /// can not be processed, it caches plaintext leftovers into inner buffer - /// for future use. - /// - /// It's recommended for `out_buf` to be at least one block longer than - /// `data`, otherwise the method can return `Err(BlockModeError)` if there is - /// not enough space for encrypted blocks. - #[inline] - pub fn encrypt_part<'a>( - &mut self, - plaintext: &[u8], - out_buf: &'a mut [u8], - ) -> Result<&'a [u8], BlockModeError> { - let Self { inner, buffer, .. } = self; - buffer - .block_mode_processing(plaintext, out_buf, |blocks| inner.encrypt_blocks(blocks)) - .map_err(|_| BlockModeError) - } - - /// Pad and encrypt plaintext. - /// - /// The method pads `plaintext` and encrypts it writing the resulting - /// ciphertext into `out_buf`. - /// - /// It's recommended for `out_buf` to be at least one block longer than - /// `data`, otherwise the method can return `Err(BlockModeError)` if there is - /// not enough space for encrypted blocks. - #[inline] - pub fn encrypt_final<'a>( - mut self, - plaintext: &[u8], - out_buf: &'a mut [u8], - ) -> Result<&'a [u8], BlockModeError> { - let Self { inner, buffer, .. } = &mut self; - let res_len = buffer - .block_mode_processing(plaintext, out_buf, |blocks| inner.encrypt_blocks(blocks)) - .map_err(|_| BlockModeError)? - .len(); - let final_block = buffer.pad_with::

(); - inner.encrypt_blocks(from_mut(final_block)); - - let bs = M::BlockSize::USIZE; - let final_len = res_len.checked_add(bs).ok_or(BlockModeError)?; - let buf = out_buf.get_mut(..final_len).ok_or(BlockModeError)?; - // note: even though `buf[t..]` and `buf[res_len..]` are guaranteed to be - // equivalent, compiler generates a panic branch for the latter. - let t = final_len - bs; - debug_assert_eq!(t, res_len); - buf[t..].copy_from_slice(final_block); - Ok(buf) - } -} - -/// Convinience wrapper around the [`BlockModeDecrypt`] trait, which handles -/// data buffering and provides slice-based methods. -pub struct BlockModeDecryptWrapper> { - inner: M, - buffer: LazyBlockBuffer, - _p: PhantomData

, -} - -impl FromBlockCipherNonce for BlockModeDecryptWrapper -where - M: BlockModeDecrypt + FromBlockCipherNonce, - P: Padding, -{ - type BlockCipher = M::BlockCipher; - type NonceSize = M::NonceSize; - - fn from_block_cipher_nonce( - cipher: Self::BlockCipher, - nonce: &GenericArray, - ) -> Self { - Self { - inner: M::from_block_cipher_nonce(cipher, nonce), - buffer: Default::default(), - _p: Default::default(), - } - } -} - -impl BlockModeDecryptWrapper -where - M: BlockModeDecrypt, - P: Padding, -{ - /// Decrypt part of a ciphertext. - /// - /// This mehthod MUST be used in conjuction with the [`decrypt_final`] method, - /// otherwise plaintext will not be properly padded and may be truncated. - /// - /// The method decrypts `ciphertext`, writes the resulting plaintext - /// into `out_buf`, and returns it in the `Ok` variant. If a whole message - /// can not be processed, it caches ciphertext leftovers into inner buffer - /// for future use. - /// - /// It's recommended for `out_buf` to be at least one block longer than - /// `data`, otherwise the method can return `Err(BlockModeError)` if there is - /// not enough space for encrypted blocks. - /// - /// [`decrypt_final`]: Self::decrypt_final - #[inline] - pub fn decrypt_part<'a>( - &mut self, - ciphertext: &[u8], - out_buf: &'a mut [u8], - ) -> Result<&'a [u8], BlockModeError> { - let Self { inner, buffer, .. } = self; - buffer - .block_mode_processing(ciphertext, out_buf, |blocks| inner.decrypt_blocks(blocks)) - .map_err(|_| BlockModeError) - } - - /// Pad and decrypt plaintext. - /// - /// The method decrypts ciphertext, writes the resulting plaintext into - /// into `out_buf`, and unpads it. - /// - /// It's recommended for `out_buf` to be at least one block longer than - /// `data`, otherwise the method can return `Err(BlockModeError)` if there is - /// not enough space for encrypted blocks. - #[inline] - pub fn decrypt_final<'a>( - mut self, - ciphertext: &[u8], - out_buf: &'a mut [u8], - ) -> Result<&'a [u8], BlockModeError> { - let Self { inner, buffer, .. } = &mut self; - let res_len = buffer - .block_mode_processing(ciphertext, out_buf, |blocks| inner.decrypt_blocks(blocks)) - .map_err(|_| BlockModeError)? - .len(); - let final_block = buffer.get_full_block().ok_or(BlockModeError)?; - inner.decrypt_blocks(from_mut(final_block)); - let tail = P::unpad(final_block).map_err(|_| BlockModeError)?; - - let tail_len = tail.len(); - let final_len = res_len.checked_add(tail_len).ok_or(BlockModeError)?; - let buf = out_buf.get_mut(..final_len).ok_or(BlockModeError)?; - // note: even though `buf[t..]` and `buf[res_len..]` are guaranteed to be - // equivalent, compiler generates a panic branch for the latter. - let t = final_len - tail_len; - debug_assert_eq!(t, res_len); - buf[t..].copy_from_slice(tail); - Ok(buf) - } -} diff --git a/cipher/src/stream.rs b/cipher/src/stream.rs index 0e7d88ede..6f0fa67ee 100644 --- a/cipher/src/stream.rs +++ b/cipher/src/stream.rs @@ -3,48 +3,159 @@ //! See [RustCrypto/stream-ciphers](https://github.com/RustCrypto/stream-ciphers) //! for ciphers implementation. -use crate::errors::{LoopError, OverflowError}; -use core::convert::{TryFrom, TryInto}; +use crate::errors::{OverflowError, StreamCipherError}; +use crate::stream_core::Counter; +use crate::{Block, BlockDecryptMut, BlockEncryptMut}; +use inout::{InOutBuf, NotEqualError}; + +/// Marker trait for block-level asynchronous stream ciphers +pub trait AsyncStreamCipher: Sized { + /// Encrypt data using `InOutBuf`. + fn encrypt_inout(mut self, data: InOutBuf<'_, '_, u8>) + where + Self: BlockEncryptMut, + { + let (blocks, mut tail) = data.into_chunks(); + self.encrypt_blocks_inout_mut(blocks); + let mut block = Block::::default(); + let n = tail.len(); + if n != 0 { + block[..n].copy_from_slice(tail.get_in()); + self.encrypt_block_mut(&mut block); + tail.get_out().copy_from_slice(&block[..n]); + } + } + + /// Decrypt data using `InOutBuf`. + fn decrypt_inout(mut self, data: InOutBuf<'_, '_, u8>) + where + Self: BlockDecryptMut, + { + let (blocks, mut tail) = data.into_chunks(); + self.decrypt_blocks_inout_mut(blocks); + let mut block = Block::::default(); + let n = tail.len(); + if n != 0 { + block[..n].copy_from_slice(tail.get_in()); + self.decrypt_block_mut(&mut block); + tail.get_out().copy_from_slice(&block[..n]); + } + } + /// Encrypt data in place. + fn encrypt(self, buf: &mut [u8]) + where + Self: BlockEncryptMut, + { + self.encrypt_inout(buf.into()); + } + + /// Decrypt data in place. + fn decrypt(self, buf: &mut [u8]) + where + Self: BlockDecryptMut, + { + self.decrypt_inout(buf.into()); + } + + /// Encrypt data from buffer to buffer. + fn encrypt_b2b(self, in_buf: &[u8], out_buf: &mut [u8]) -> Result<(), NotEqualError> + where + Self: BlockEncryptMut, + { + InOutBuf::new(in_buf, out_buf).map(|b| self.encrypt_inout(b)) + } + + /// Decrypt data from buffer to buffer. + fn decrypt_b2b(self, in_buf: &[u8], out_buf: &mut [u8]) -> Result<(), NotEqualError> + where + Self: BlockDecryptMut, + { + InOutBuf::new(in_buf, out_buf).map(|b| self.decrypt_inout(b)) + } +} /// Synchronous stream cipher core trait. pub trait StreamCipher { - /// Apply keystream to the data. + /// Apply keystream to `inout` data. + /// + /// If end of the keystream will be achieved with the given data length, + /// method will return [`StreamCipherError`] without modifying provided `data`. + fn try_apply_keystream_inout( + &mut self, + buf: InOutBuf<'_, '_, u8>, + ) -> Result<(), StreamCipherError>; + + /// Apply keystream to data behind `buf`. + /// + /// If end of the keystream will be achieved with the given data length, + /// method will return [`StreamCipherError`] without modifying provided `data`. + #[inline] + fn try_apply_keystream(&mut self, buf: &mut [u8]) -> Result<(), StreamCipherError> { + self.try_apply_keystream_inout(buf.into()) + } + + /// Apply keystream to `inout` data. /// - /// It will XOR generated keystream with the data, which can be both - /// encryption and decryption. + /// It will XOR generated keystream with the data behind `in` pointer + /// and will write result to `out` pointer. /// /// # Panics /// If end of the keystream will be reached with the given data length, /// method will panic without modifying the provided `data`. #[inline] - fn apply_keystream(&mut self, data: &mut [u8]) { - self.try_apply_keystream(data).unwrap(); + fn apply_keystream_inout(&mut self, buf: InOutBuf<'_, '_, u8>) { + self.try_apply_keystream_inout(buf).unwrap(); } - /// Apply keystream to the data, but return an error if end of a keystream - /// will be reached. + /// Apply keystream to data in-place. /// - /// If end of the keystream will be achieved with the given data length, - /// method will return `Err(LoopError)` without modifying provided `data`. - fn try_apply_keystream(&mut self, data: &mut [u8]) -> Result<(), LoopError>; + /// It will XOR generated keystream with `data` and will write result + /// to the same buffer. + /// + /// # Panics + /// If end of the keystream will be reached with the given data length, + /// method will panic without modifying the provided `data`. + #[inline] + fn apply_keystream(&mut self, buf: &mut [u8]) { + self.try_apply_keystream(buf).unwrap(); + } + + /// Apply keystream to data buffer-to-buffer. + /// + /// It will XOR generated keystream with data from the `input` buffer + /// and will write result to the `output` buffer. + /// + /// Returns [`StreamCipherError`] if provided `in_blocks` and `out_blocks` + /// have different lengths or if end of the keystream will be reached with + /// the given input data length. + #[inline] + fn apply_keystream_b2b( + &mut self, + input: &[u8], + output: &mut [u8], + ) -> Result<(), StreamCipherError> { + InOutBuf::new(input, output) + .map_err(|_| StreamCipherError) + .and_then(|buf| self.try_apply_keystream_inout(buf)) + } } /// Trait for seekable stream ciphers. /// /// Methods of this trait are generic over the [`SeekNum`] trait, which is -/// implemented for primitive numeric types, i.e.: `i/u8`, `i/u16`, `i/u32`, -/// `i/u64`, `i/u128`, and `i/usize`. +/// implemented for primitive numeric types, i.e.: `i32`, `u32`, `u64`, +/// `u128`, and `usize`. pub trait StreamCipherSeek { /// Try to get current keystream position /// - /// Returns [`LoopError`] if position can not be represented by type `T` + /// Returns [`OverflowError`] if position can not be represented by type `T` fn try_current_pos(&self) -> Result; /// Try to seek to the given position /// - /// Returns [`LoopError`] if provided position value is bigger than + /// Returns [`StreamCipherError`] if provided position value is bigger than /// keystream length. - fn try_seek(&mut self, pos: T) -> Result<(), LoopError>; + fn try_seek(&mut self, pos: T) -> Result<(), StreamCipherError>; /// Get current keystream position /// @@ -57,70 +168,51 @@ pub trait StreamCipherSeek { /// Seek to the given position /// /// # Panics - /// If provided position value is bigger than keystream length + /// If provided position value is bigger than keystream leangth fn seek(&mut self, pos: T) { self.try_seek(pos).unwrap() } } -/// Asynchronous stream cipher core trait. -pub trait AsyncStreamCipher { - /// Encrypt data in place. - fn encrypt(&mut self, data: &mut [u8]); - - /// Decrypt data in place. - fn decrypt(&mut self, data: &mut [u8]); -} - impl StreamCipher for &mut C { #[inline] - fn apply_keystream(&mut self, data: &mut [u8]) { - C::apply_keystream(self, data); - } - - #[inline] - fn try_apply_keystream(&mut self, data: &mut [u8]) -> Result<(), LoopError> { - C::try_apply_keystream(self, data) + fn try_apply_keystream_inout( + &mut self, + buf: InOutBuf<'_, '_, u8>, + ) -> Result<(), StreamCipherError> { + C::try_apply_keystream_inout(self, buf) } } /// Trait implemented for numeric types which can be used with the /// [`StreamCipherSeek`] trait. /// -/// This trait is implemented for primitive numeric types, i.e. `i/u8`, -/// `u16`, `u32`, `u64`, `u128`, `usize`, and `i32`. It is not intended -/// to be implemented in third-party crates. -#[rustfmt::skip] -pub trait SeekNum: - Sized - + TryInto + TryFrom + TryInto + TryFrom - + TryInto + TryFrom + TryInto + TryFrom - + TryInto + TryFrom + TryInto + TryFrom - + TryInto + TryFrom + TryInto + TryFrom - + TryInto + TryFrom + TryInto + TryFrom - + TryInto + TryFrom + TryInto + TryFrom -{ +/// This trait is implemented for `i32`, `u32`, `u64`, `u128`, and `usize`. +/// It is not intended to be implemented in third-party crates. +pub trait SeekNum: Sized { /// Try to get position for block number `block`, byte position inside /// block `byte`, and block size `bs`. - fn from_block_byte(block: T, byte: u8, bs: u8) -> Result; + fn from_block_byte(block: T, byte: u8, bs: u8) -> Result; /// Try to get block number and bytes position for given block size `bs`. - #[allow(clippy::wrong_self_convention)] - fn to_block_byte(self, bs: u8) -> Result<(T, u8), OverflowError>; + fn into_block_byte(self, bs: u8) -> Result<(T, u8), OverflowError>; } macro_rules! impl_seek_num { {$($t:ty )*} => { $( impl SeekNum for $t { - fn from_block_byte>(block: T, byte: u8, bs: u8) -> Result { + fn from_block_byte(block: T, byte: u8, bs: u8) -> Result { debug_assert!(byte < bs); - let block = block.try_into().map_err(|_| OverflowError)?; + let mut block: Self = block.try_into().map_err(|_| OverflowError)?; + if byte != 0 { + block -= 1; + } let pos = block.checked_mul(bs as Self).ok_or(OverflowError)? + (byte as Self); Ok(pos) } - fn to_block_byte>(self, bs: u8) -> Result<(T, u8), OverflowError> { + fn into_block_byte(self, bs: u8) -> Result<(T, u8), OverflowError> { let bs = bs as Self; let byte = self % bs; let block = T::try_from(self/bs).map_err(|_| OverflowError)?; @@ -131,4 +223,4 @@ macro_rules! impl_seek_num { }; } -impl_seek_num! { u8 u16 u32 u64 u128 usize i32 } +impl_seek_num! { i32 u32 u64 u128 usize } diff --git a/cipher/src/stream_core.rs b/cipher/src/stream_core.rs new file mode 100644 index 000000000..bb4d9a43a --- /dev/null +++ b/cipher/src/stream_core.rs @@ -0,0 +1,301 @@ +use crate::{ParBlocks, ParBlocksSizeUser, StreamCipherError}; +use crypto_common::{ + generic_array::{ArrayLength, GenericArray}, + typenum::Unsigned, + Block, BlockSizeUser, +}; +use inout::{InOut, InOutBuf}; + +/// Trait implemented by stream cipher backends. +pub trait StreamBackend: ParBlocksSizeUser { + /// Generate keystream block. + fn gen_ks_block(&mut self, block: &mut Block); + + /// Generate keystream blocks in parallel. + #[inline(always)] + fn gen_par_ks_blocks(&mut self, blocks: &mut ParBlocks) { + for block in blocks { + self.gen_ks_block(block); + } + } + + /// Generate keystream blocks. Length of the buffer MUST be smaller + /// than `Self::ParBlocksSize`. + #[inline(always)] + fn gen_tail_blocks(&mut self, blocks: &mut [Block]) { + assert!(blocks.len() < Self::ParBlocksSize::USIZE); + for block in blocks { + self.gen_ks_block(block); + } + } +} + +/// Trait for [`StreamBackend`] users. +/// +/// This trait is used to define rank-2 closures. +pub trait StreamClosure: BlockSizeUser { + /// Execute closure with the provided stream cipher backend. + fn call>(self, backend: &mut B); +} + +/// Block-level synchronous stream ciphers. +pub trait StreamCipherCore: BlockSizeUser + Sized { + /// Return number of remaining blocks before cipher wraps around. + /// + /// Returns `None` if number of remaining blocks can not be computed + /// (e.g. in ciphers based on the sponge construction) or it's too big + /// to fit into `usize`. + fn remaining_blocks(&self) -> Option; + + /// Process data using backend provided to the rank-2 closure. + fn process_with_backend(&mut self, f: impl StreamClosure); + + /// Write keystream block. + /// + /// WARNING: this method does not check number of remaining blocks! + #[inline] + fn write_keystream_block(&mut self, block: &mut Block) { + self.process_with_backend(WriteBlockCtx { block }); + } + + /// Write keystream blocks. + /// + /// WARNING: this method does not check number of remaining blocks! + #[inline] + fn write_keystream_blocks(&mut self, blocks: &mut [Block]) { + self.process_with_backend(WriteBlocksCtx { blocks }); + } + + /// Apply keystream block. + /// + /// WARNING: this method does not check number of remaining blocks! + #[inline] + fn apply_keystream_block_inout(&mut self, block: InOut<'_, '_, Block>) { + self.process_with_backend(ApplyBlockCtx { block }); + } + + /// Apply keystream blocks. + /// + /// WARNING: this method does not check number of remaining blocks! + #[inline] + fn apply_keystream_blocks(&mut self, blocks: &mut [Block]) { + self.process_with_backend(ApplyBlocksCtx { + blocks: blocks.into(), + }); + } + + /// Apply keystream blocks. + /// + /// WARNING: this method does not check number of remaining blocks! + #[inline] + fn apply_keystream_blocks_inout(&mut self, blocks: InOutBuf<'_, '_, Block>) { + self.process_with_backend(ApplyBlocksCtx { blocks }); + } + + /// Try to apply keystream to data not divided into blocks. + /// + /// Consumes cipher since it may consume final keystream block only + /// partially. + /// + /// Returns an error if number of remaining blocks is not sufficient + /// for processing the input data. + #[inline] + fn try_apply_keystream_partial( + mut self, + mut buf: InOutBuf<'_, '_, u8>, + ) -> Result<(), StreamCipherError> { + if let Some(rem) = self.remaining_blocks() { + let blocks = if buf.len() % Self::BlockSize::USIZE == 0 { + buf.len() % Self::BlockSize::USIZE + } else { + buf.len() % Self::BlockSize::USIZE + 1 + }; + if blocks > rem { + return Err(StreamCipherError); + } + } + + if buf.len() > Self::BlockSize::USIZE { + let (blocks, tail) = buf.into_chunks(); + self.apply_keystream_blocks_inout(blocks); + buf = tail; + } + let n = buf.len(); + if n == 0 { + return Ok(()); + } + let mut block = Block::::default(); + block[..n].copy_from_slice(buf.get_in()); + let t = InOutBuf::from_mut(&mut block); + self.apply_keystream_blocks_inout(t); + buf.get_out().copy_from_slice(&block[..n]); + Ok(()) + } + + /// Try to apply keystream to data not divided into blocks. + /// + /// Consumes cipher since it may consume final keystream block only + /// partially. + /// + /// # Panics + /// If number of remaining blocks is not sufficient for processing the + /// input data. + #[inline] + fn apply_keystream_partial(self, buf: InOutBuf<'_, '_, u8>) { + self.try_apply_keystream_partial(buf).unwrap() + } +} + +// note: unfortunately, currently we can not write blanket impls of +// `BlockEncryptMut` and `BlockDecryptMut` for `T: StreamCipherCore` +// since it requires mutually exlusive traits, see: +// https://github.com/rust-lang/rfcs/issues/1053 + +/// Counter type usable with [`StreamCipherCore`]. +/// +/// This trait is implemented for `i32`, `u32`, `u64`, `u128`, and `usize`. +/// It's not intended to be implemented in third-party crates, but doing so +/// is not forbidden. +pub trait Counter: + TryFrom + + TryFrom + + TryFrom + + TryFrom + + TryFrom + + TryInto + + TryInto + + TryInto + + TryInto + + TryInto +{ +} + +/// Block-level seeking trait for stream ciphers. +pub trait StreamCipherSeekCore: StreamCipherCore { + /// Counter type used inside stream cipher. + type Counter: Counter; + + /// Get current block position. + fn get_block_pos(&self) -> Self::Counter; + + /// Set block position. + fn set_block_pos(&mut self, pos: Self::Counter); +} + +macro_rules! impl_counter { + {$($t:ty )*} => { + $( impl Counter for $t { } )* + }; +} + +impl_counter! { u32 u64 u128 } + +/// Partition buffer into 2 parts: buffer of arrays and tail. +/// +/// In case if `N` is less or equal to 1, buffer of arrays has length +/// of zero and tail is equal to `self`. +#[inline] +fn into_chunks>(buf: &mut [T]) -> (&mut [GenericArray], &mut [T]) { + use core::slice; + if N::USIZE <= 1 { + return (&mut [], buf); + } + let chunks_len = buf.len() / N::USIZE; + let tail_pos = N::USIZE * chunks_len; + let tail_len = buf.len() - tail_pos; + unsafe { + let ptr = buf.as_mut_ptr(); + let chunks = slice::from_raw_parts_mut(ptr as *mut GenericArray, chunks_len); + let tail = slice::from_raw_parts_mut(ptr.add(tail_pos), tail_len); + (chunks, tail) + } +} + +struct WriteBlockCtx<'a, BS: ArrayLength> { + block: &'a mut Block, +} +impl<'a, BS: ArrayLength> BlockSizeUser for WriteBlockCtx<'a, BS> { + type BlockSize = BS; +} +impl<'a, BS: ArrayLength> StreamClosure for WriteBlockCtx<'a, BS> { + #[inline(always)] + fn call>(self, backend: &mut B) { + backend.gen_ks_block(self.block); + } +} + +struct WriteBlocksCtx<'a, BS: ArrayLength> { + blocks: &'a mut [Block], +} +impl<'a, BS: ArrayLength> BlockSizeUser for WriteBlocksCtx<'a, BS> { + type BlockSize = BS; +} +impl<'a, BS: ArrayLength> StreamClosure for WriteBlocksCtx<'a, BS> { + #[inline(always)] + fn call>(self, backend: &mut B) { + if B::ParBlocksSize::USIZE > 1 { + let (chunks, tail) = into_chunks::<_, B::ParBlocksSize>(self.blocks); + for chunk in chunks { + backend.gen_par_ks_blocks(chunk); + } + backend.gen_tail_blocks(tail); + } else { + for block in self.blocks { + backend.gen_ks_block(block); + } + } + } +} + +struct ApplyBlockCtx<'inp, 'out, BS: ArrayLength> { + block: InOut<'inp, 'out, Block>, +} + +impl<'inp, 'out, BS: ArrayLength> BlockSizeUser for ApplyBlockCtx<'inp, 'out, BS> { + type BlockSize = BS; +} + +impl<'inp, 'out, BS: ArrayLength> StreamClosure for ApplyBlockCtx<'inp, 'out, BS> { + #[inline(always)] + fn call>(mut self, backend: &mut B) { + let mut t = Default::default(); + backend.gen_ks_block(&mut t); + self.block.xor_in2out(&t); + } +} + +struct ApplyBlocksCtx<'inp, 'out, BS: ArrayLength> { + blocks: InOutBuf<'inp, 'out, Block>, +} + +impl<'inp, 'out, BS: ArrayLength> BlockSizeUser for ApplyBlocksCtx<'inp, 'out, BS> { + type BlockSize = BS; +} + +impl<'inp, 'out, BS: ArrayLength> StreamClosure for ApplyBlocksCtx<'inp, 'out, BS> { + #[inline(always)] + #[allow(clippy::needless_range_loop)] + fn call>(self, backend: &mut B) { + if B::ParBlocksSize::USIZE > 1 { + let (chunks, mut tail) = self.blocks.into_chunks::(); + for mut chunk in chunks { + let mut tmp = Default::default(); + backend.gen_par_ks_blocks(&mut tmp); + chunk.xor_in2out(&tmp); + } + let n = tail.len(); + let mut buf = GenericArray::<_, B::ParBlocksSize>::default(); + let ks = &mut buf[..n]; + backend.gen_tail_blocks(ks); + for i in 0..n { + tail.get(i).xor_in2out(&ks[i]); + } + } else { + for mut block in self.blocks { + let mut t = Default::default(); + backend.gen_ks_block(&mut t); + block.xor_in2out(&t); + } + } + } +} diff --git a/cipher/src/stream_wrapper.rs b/cipher/src/stream_wrapper.rs new file mode 100644 index 000000000..21f861638 --- /dev/null +++ b/cipher/src/stream_wrapper.rs @@ -0,0 +1,239 @@ +use crate::{ + errors::StreamCipherError, Block, OverflowError, SeekNum, StreamCipher, StreamCipherCore, + StreamCipherSeek, StreamCipherSeekCore, +}; +use crypto_common::{ + typenum::{IsLess, Le, NonZero, Unsigned, U256}, + BlockSizeUser, Iv, IvSizeUser, Key, KeyInit, KeyIvInit, KeySizeUser, +}; +use inout::InOutBuf; +#[cfg(feature = "zeroize")] +use zeroize::{Zeroize, ZeroizeOnDrop}; + +/// Wrapper around [`StreamCipherCore`] implementations. +/// +/// It handles data buffering and implements the slice-based traits. +#[derive(Clone, Default)] +pub struct StreamCipherCoreWrapper +where + T::BlockSize: IsLess, + Le: NonZero, +{ + core: T, + buffer: Block, + pos: u8, +} + +impl StreamCipherCoreWrapper +where + T::BlockSize: IsLess, + Le: NonZero, +{ + /// Return reference to the core type. + pub fn get_core(&self) -> &T { + &self.core + } + + /// Return reference to the core type. + pub fn from_core(core: T) -> Self { + Self { + core, + buffer: Default::default(), + pos: 0, + } + } + + /// Return current cursor position. + #[inline] + fn get_pos(&self) -> usize { + let pos = self.pos as usize; + if pos >= T::BlockSize::USIZE { + debug_assert!(false); + // SAFETY: `pos` is set only to values smaller than block size + unsafe { core::hint::unreachable_unchecked() } + } + self.pos as usize + } + + /// Return size of the internall buffer in bytes. + #[inline] + fn size(&self) -> usize { + T::BlockSize::USIZE + } + + #[inline] + fn set_pos_unchecked(&mut self, pos: usize) { + debug_assert!(pos < T::BlockSize::USIZE); + self.pos = pos as u8; + } + + /// Return number of remaining bytes in the internall buffer. + #[inline] + fn remaining(&self) -> usize { + self.size() - self.get_pos() + } + + fn check_remaining(&self, dlen: usize) -> Result<(), StreamCipherError> { + let rem_blocks = match self.core.remaining_blocks() { + Some(v) => v, + None => return Ok(()), + }; + + let bytes = if self.pos == 0 { + dlen + } else { + let rem = self.remaining(); + if dlen > rem { + dlen - rem + } else { + return Ok(()); + } + }; + let bs = T::BlockSize::USIZE; + let blocks = if bytes % bs == 0 { + bytes / bs + } else { + bytes / bs + 1 + }; + if blocks > rem_blocks { + Err(StreamCipherError) + } else { + Ok(()) + } + } +} + +impl StreamCipher for StreamCipherCoreWrapper +where + T::BlockSize: IsLess, + Le: NonZero, +{ + #[inline] + fn try_apply_keystream_inout( + &mut self, + mut data: InOutBuf<'_, '_, u8>, + ) -> Result<(), StreamCipherError> { + self.check_remaining(data.len())?; + + let pos = self.get_pos(); + if pos != 0 { + let rem = &self.buffer[pos..]; + let n = data.len(); + if n < rem.len() { + data.xor_in2out(&rem[..n]); + self.set_pos_unchecked(pos + n); + return Ok(()); + } + let (mut left, right) = data.split_at(rem.len()); + data = right; + left.xor_in2out(rem); + } + + let (blocks, mut leftover) = data.into_chunks(); + self.core.apply_keystream_blocks_inout(blocks); + + let n = leftover.len(); + if n != 0 { + self.core.write_keystream_block(&mut self.buffer); + leftover.xor_in2out(&self.buffer[..n]); + } + self.set_pos_unchecked(n); + + Ok(()) + } +} + +impl StreamCipherSeek for StreamCipherCoreWrapper +where + T::BlockSize: IsLess, + Le: NonZero, +{ + fn try_current_pos(&self) -> Result { + let Self { core, pos, .. } = self; + SN::from_block_byte(core.get_block_pos(), *pos, T::BlockSize::U8) + } + + fn try_seek(&mut self, new_pos: SN) -> Result<(), StreamCipherError> { + let Self { core, buffer, pos } = self; + let (block_pos, byte_pos) = new_pos.into_block_byte(T::BlockSize::U8)?; + core.set_block_pos(block_pos); + if byte_pos != 0 { + self.core.write_keystream_block(buffer); + } + *pos = byte_pos; + Ok(()) + } +} + +// Note: ideally we would only implement the InitInner trait and everything +// else would be handled by blanket impls, but unfortunately it will +// not work properly without mutually exclusive traits, see: +// https://github.com/rust-lang/rfcs/issues/1053 + +impl KeySizeUser for StreamCipherCoreWrapper +where + T::BlockSize: IsLess, + Le: NonZero, +{ + type KeySize = T::KeySize; +} + +impl IvSizeUser for StreamCipherCoreWrapper +where + T::BlockSize: IsLess, + Le: NonZero, +{ + type IvSize = T::IvSize; +} + +impl KeyIvInit for StreamCipherCoreWrapper +where + T::BlockSize: IsLess, + Le: NonZero, +{ + #[inline] + fn new(key: &Key, iv: &Iv) -> Self { + Self { + core: T::new(key, iv), + buffer: Default::default(), + pos: 0, + } + } +} + +impl KeyInit for StreamCipherCoreWrapper +where + T::BlockSize: IsLess, + Le: NonZero, +{ + #[inline] + fn new(key: &Key) -> Self { + Self { + core: T::new(key), + buffer: Default::default(), + pos: 0, + } + } +} + +#[cfg(feature = "zeroize")] +impl Drop for StreamCipherCoreWrapper +where + T: BlockSizeUser, + T::BlockSize: IsLess, + Le: NonZero, +{ + fn drop(&mut self) { + self.buffer.zeroize(); + self.pos.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +impl ZeroizeOnDrop for StreamCipherCoreWrapper +where + T: BlockSizeUser + ZeroizeOnDrop, + T::BlockSize: IsLess, + Le: NonZero, +{ +} diff --git a/crypto-common/CHANGELOG.md b/crypto-common/CHANGELOG.md index b5cf73540..98541eaad 100644 --- a/crypto-common/CHANGELOG.md +++ b/crypto-common/CHANGELOG.md @@ -5,6 +5,14 @@ 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.2 (2022-02-10) +### Added +- Re-export `generic-array` and `typenum`. Enable `more_lengths` feature on +`generic-array`. Add `key_size`, `iv_size`, `block_size`, and `output_size` +helper methods. ([#849]) + +[#849]: https://github.com/RustCrypto/traits/pull/849 + ## 0.1.1 (2021-12-14) ### Added - `rand_core` re-export and proper exposure of key/IV generation methods on docs.rs ([#847]) diff --git a/crypto-common/Cargo.toml b/crypto-common/Cargo.toml index 1b629b109..67249f0b0 100644 --- a/crypto-common/Cargo.toml +++ b/crypto-common/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "crypto-common" description = "Common cryptographic traits" -version = "0.1.1" # Also update html_root_url in lib.rs when bumping this +version = "0.1.2" # Also update html_root_url in lib.rs when bumping this authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" readme = "README.md" @@ -12,7 +12,7 @@ keywords = ["crypto", "traits"] categories = ["cryptography", "no-std"] [dependencies] -generic-array = "0.14" +generic-array = { version = "0.14.4", features = ["more_lengths"] } rand_core = { version = "0.6", optional = true } [features] diff --git a/crypto-common/src/lib.rs b/crypto-common/src/lib.rs index f1e1570bc..62807bbb4 100644 --- a/crypto-common/src/lib.rs +++ b/crypto-common/src/lib.rs @@ -16,6 +16,9 @@ extern crate std; #[cfg(feature = "rand_core")] pub use rand_core; +pub use generic_array; +pub use generic_array::typenum; + use core::fmt; use generic_array::{typenum::Unsigned, ArrayLength, GenericArray}; #[cfg(feature = "rand_core")] @@ -34,6 +37,11 @@ pub type Iv = GenericArray::IvSize>; pub trait BlockSizeUser { /// Size of the block in bytes. type BlockSize: ArrayLength + 'static; + + /// Return block size in bytes. + fn block_size() -> usize { + Self::BlockSize::USIZE + } } impl BlockSizeUser for &T { @@ -48,6 +56,11 @@ impl BlockSizeUser for &mut T { pub trait OutputSizeUser { /// Size of the output in bytes. type OutputSize: ArrayLength + 'static; + + /// Return output size in bytes. + fn output_size() -> usize { + Self::OutputSize::USIZE + } } /// Types which use key for initialization. @@ -56,6 +69,11 @@ pub trait OutputSizeUser { pub trait KeySizeUser { /// Key size in bytes. type KeySize: ArrayLength + 'static; + + /// Return key size in bytes. + fn key_size() -> usize { + Self::KeySize::USIZE + } } /// Types which use initialization vector (nonce) for initialization. @@ -64,6 +82,11 @@ pub trait KeySizeUser { pub trait IvSizeUser { /// Initialization vector size in bytes. type IvSize: ArrayLength + 'static; + + /// Return IV size in bytes. + fn iv_size() -> usize { + Self::IvSize::USIZE + } } /// Types which use another type for initialization. diff --git a/digest/CHANGELOG.md b/digest/CHANGELOG.md index f72a9825c..5e18564c6 100644 --- a/digest/CHANGELOG.md +++ b/digest/CHANGELOG.md @@ -5,13 +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.10.2 (2022-02-10) +### Changed +- Relaxed bounds on the `Mac` trait ([#849]) + +[#849]: https://github.com/RustCrypto/traits/pull/849 + ## 0.10.1 (2021-12-14) ### Added - `Update::chain` and `Digest::new_with_prefix` methods. ([#846]) - `Mac::generate_key` method. ([#847]) ### Fixed -- Doc cfg attribute for CtOutput and MacError. ([#842]) +- Doc cfg attribute for `CtOutput` and `MacError`. ([#842]) - Expose `KeyInit::generate_key` method in docs. ([#847]) [#842]: https://github.com/RustCrypto/traits/pull/842 diff --git a/digest/Cargo.toml b/digest/Cargo.toml index 037216dda..0b9020fa7 100644 --- a/digest/Cargo.toml +++ b/digest/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "digest" description = "Traits for cryptographic hash functions" -version = "0.10.1" # Also update html_root_url in lib.rs when bumping this +version = "0.10.2" # Also update html_root_url in lib.rs when bumping this authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" readme = "README.md" @@ -12,8 +12,7 @@ keywords = ["digest", "crypto", "hash"] categories = ["cryptography", "no-std"] [dependencies] -generic-array = "0.14.4" -crypto-common = { version = "0.1.1", path = "../crypto-common" } +crypto-common = { version = "0.1.2", path = "../crypto-common" } block-buffer = { version = "0.10", optional = true } subtle = { version = "=2.4", default-features = false, optional = true } diff --git a/digest/src/core_api.rs b/digest/src/core_api.rs index 6783e1985..2cf384efe 100644 --- a/digest/src/core_api.rs +++ b/digest/src/core_api.rs @@ -4,12 +4,14 @@ //! core algorithm wrapped by the wrapper types, which implement the //! higher-level traits. use crate::InvalidOutputSize; -use generic_array::typenum::{IsLess, Le, NonZero, U256}; pub use crypto_common::{AlgorithmName, Block, BlockSizeUser, OutputSizeUser, Reset}; use block_buffer::{BlockBuffer, BufferKind}; -use crypto_common::Output; +use crypto_common::{ + typenum::{IsLess, Le, NonZero, U256}, + Output, +}; mod ct_variable; mod rt_variable; diff --git a/digest/src/core_api/ct_variable.rs b/digest/src/core_api/ct_variable.rs index c69544912..40efff65c 100644 --- a/digest/src/core_api/ct_variable.rs +++ b/digest/src/core_api/ct_variable.rs @@ -6,10 +6,10 @@ use crate::HashMarker; #[cfg(feature = "mac")] use crate::MacMarker; use core::{fmt, marker::PhantomData}; -use crypto_common::{Block, BlockSizeUser, OutputSizeUser}; -use generic_array::{ +use crypto_common::{ + generic_array::{ArrayLength, GenericArray}, typenum::{IsLess, IsLessOrEqual, Le, LeEq, NonZero, U256}, - ArrayLength, GenericArray, + Block, BlockSizeUser, OutputSizeUser, }; /// Wrapper around [`VariableOutputCore`] which selects output size diff --git a/digest/src/core_api/rt_variable.rs b/digest/src/core_api/rt_variable.rs index 0c2233fe0..3dae748f5 100644 --- a/digest/src/core_api/rt_variable.rs +++ b/digest/src/core_api/rt_variable.rs @@ -5,7 +5,7 @@ use crate::{HashMarker, InvalidBufferSize}; use crate::{InvalidOutputSize, Reset, Update, VariableOutput, VariableOutputReset}; use block_buffer::BlockBuffer; use core::fmt; -use generic_array::typenum::{IsLess, Le, NonZero, Unsigned, U256}; +use crypto_common::typenum::{IsLess, Le, NonZero, Unsigned, U256}; /// Wrapper around [`VariableOutputCore`] which selects output size /// at run time. diff --git a/digest/src/core_api/wrapper.rs b/digest/src/core_api/wrapper.rs index f600642c2..4ad0f7eea 100644 --- a/digest/src/core_api/wrapper.rs +++ b/digest/src/core_api/wrapper.rs @@ -7,8 +7,10 @@ use crate::{ }; use block_buffer::BlockBuffer; use core::fmt; -use crypto_common::{BlockSizeUser, InvalidLength, Key, KeyInit, KeySizeUser, Output}; -use generic_array::typenum::{IsLess, Le, NonZero, U256}; +use crypto_common::{ + typenum::{IsLess, Le, NonZero, U256}, + BlockSizeUser, InvalidLength, Key, KeyInit, KeySizeUser, Output, +}; #[cfg(feature = "mac")] use crate::MacMarker; diff --git a/digest/src/core_api/xof_reader.rs b/digest/src/core_api/xof_reader.rs index 5b41479b0..e18ac133a 100644 --- a/digest/src/core_api/xof_reader.rs +++ b/digest/src/core_api/xof_reader.rs @@ -2,7 +2,7 @@ use super::{AlgorithmName, XofReaderCore}; use crate::XofReader; use block_buffer::EagerBuffer; use core::fmt; -use generic_array::typenum::{IsLess, Le, NonZero, U256}; +use crypto_common::typenum::{IsLess, Le, NonZero, U256}; /// Wrapper around [`XofReaderCore`] implementations. /// diff --git a/digest/src/digest.rs b/digest/src/digest.rs index 1ede72eb1..bd9fd62e4 100644 --- a/digest/src/digest.rs +++ b/digest/src/digest.rs @@ -1,6 +1,5 @@ use super::{FixedOutput, FixedOutputReset, InvalidBufferSize, Reset, Update}; -use crypto_common::{Output, OutputSizeUser}; -use generic_array::typenum::Unsigned; +use crypto_common::{typenum::Unsigned, Output, OutputSizeUser}; #[cfg(feature = "alloc")] use alloc::boxed::Box; @@ -24,6 +23,7 @@ pub trait Digest: OutputSizeUser { fn update(&mut self, data: impl AsRef<[u8]>); /// Process input data in a chained manner. + #[must_use] fn chain_update(self, data: impl AsRef<[u8]>) -> Self; /// Retrieve result and consume hasher instance. diff --git a/digest/src/lib.rs b/digest/src/lib.rs index b896bb910..0fc7ede42 100644 --- a/digest/src/lib.rs +++ b/digest/src/lib.rs @@ -28,7 +28,7 @@ #![doc( 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/digest/0.10.1" + html_root_url = "https://docs.rs/digest/0.10.2" )] #![warn(missing_docs, rust_2018_idioms)] @@ -40,6 +40,7 @@ extern crate alloc; extern crate std; #[cfg(feature = "rand_core")] +#[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] pub use crypto_common::rand_core; #[cfg(feature = "alloc")] @@ -62,10 +63,9 @@ pub use block_buffer; pub use crypto_common; pub use crate::digest::{Digest, DynDigest, HashMarker}; +pub use crypto_common::{generic_array, typenum, typenum::consts, Output, OutputSizeUser, Reset}; #[cfg(feature = "mac")] pub use crypto_common::{InnerInit, InvalidLength, Key, KeyInit}; -pub use crypto_common::{Output, OutputSizeUser, Reset}; -pub use generic_array::{self, typenum::consts}; #[cfg(feature = "mac")] pub use mac::{CtOutput, Mac, MacError, MacMarker}; @@ -77,6 +77,7 @@ pub trait Update { fn update(&mut self, data: &[u8]); /// Digest input data in a chained manner. + #[must_use] fn chain(mut self, data: impl AsRef<[u8]>) -> Self where Self: Sized, diff --git a/digest/src/mac.rs b/digest/src/mac.rs index 672ce93e3..76f8df2c9 100644 --- a/digest/src/mac.rs +++ b/digest/src/mac.rs @@ -1,10 +1,10 @@ use crate::{FixedOutput, FixedOutputReset, Update}; -use crypto_common::{InvalidLength, Key, KeyInit, KeySizeUser, Output, OutputSizeUser, Reset}; +use crypto_common::{InvalidLength, Key, KeyInit, Output, OutputSizeUser, Reset}; #[cfg(feature = "rand_core")] use crate::rand_core::{CryptoRng, RngCore}; use core::fmt; -use generic_array::typenum::Unsigned; +use crypto_common::typenum::Unsigned; use subtle::{Choice, ConstantTimeEq}; /// Marker trait for Message Authentication algorithms. @@ -16,21 +16,31 @@ pub trait MacMarker {} /// This trait wraps [`KeyInit`], [`Update`], [`FixedOutput`], and [`MacMarker`] /// traits and provides additional convenience methods. #[cfg_attr(docsrs, doc(cfg(feature = "mac")))] -pub trait Mac: KeySizeUser + OutputSizeUser + Sized { +pub trait Mac: OutputSizeUser + Sized { /// Create new value from fixed size key. - fn new(key: &Key) -> Self; + fn new(key: &Key) -> Self + where + Self: KeyInit; /// Generate random key using the provided [`CryptoRng`]. #[cfg(feature = "rand_core")] #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] - fn generate_key(rng: impl CryptoRng + RngCore) -> Key; + fn generate_key(rng: impl CryptoRng + RngCore) -> Key + where + Self: KeyInit; /// Create new value from variable size key. - fn new_from_slice(key: &[u8]) -> Result; + fn new_from_slice(key: &[u8]) -> Result + where + Self: KeyInit; /// Update state using the provided data. fn update(&mut self, data: &[u8]); + /// Process input data in a chained manner. + #[must_use] + fn chain_update(self, data: impl AsRef<[u8]>) -> Self; + /// Obtain the result of a [`Mac`] computation as a [`CtOutput`] and consume /// [`Mac`] instance. fn finalize(self) -> CtOutput; @@ -69,14 +79,20 @@ pub trait Mac: KeySizeUser + OutputSizeUser + Sized { fn verify_truncated_right(self, tag: &[u8]) -> Result<(), MacError>; } -impl Mac for T { +impl Mac for T { #[inline(always)] - fn new(key: &Key) -> Self { + fn new(key: &Key) -> Self + where + Self: KeyInit, + { KeyInit::new(key) } #[inline(always)] - fn new_from_slice(key: &[u8]) -> Result { + fn new_from_slice(key: &[u8]) -> Result + where + Self: KeyInit, + { KeyInit::new_from_slice(key) } @@ -85,6 +101,12 @@ impl Mac for T { Update::update(self, data); } + #[inline] + fn chain_update(mut self, data: impl AsRef<[u8]>) -> Self { + Update::update(&mut self, data.as_ref()); + self + } + #[inline] fn finalize(self) -> CtOutput { CtOutput::new(self.finalize_fixed()) @@ -161,7 +183,10 @@ impl Mac for T { #[cfg(feature = "rand_core")] #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] #[inline] - fn generate_key(rng: impl CryptoRng + RngCore) -> Key { + fn generate_key(rng: impl CryptoRng + RngCore) -> Key + where + Self: KeyInit, + { ::generate_key(rng) } }