diff --git a/.github/workflows/crypto-common.yml b/.github/workflows/crypto-common.yml index 287c49535..bc45c25b4 100644 --- a/.github/workflows/crypto-common.yml +++ b/.github/workflows/crypto-common.yml @@ -52,6 +52,5 @@ jobs: profile: minimal - run: cargo check --all-features - run: cargo test - - run: cargo test --features core-api - run: cargo test --features std - run: cargo test --all-features diff --git a/.github/workflows/crypto-mac.yml b/.github/workflows/crypto-mac.yml index 29186ab72..dc80f3871 100644 --- a/.github/workflows/crypto-mac.yml +++ b/.github/workflows/crypto-mac.yml @@ -54,7 +54,6 @@ jobs: profile: minimal - run: cargo check --all-features - run: cargo test - - run: cargo test --features core-api - run: cargo test --features dev - run: cargo test --features std - run: cargo test --all-features diff --git a/.github/workflows/digest.yml b/.github/workflows/digest.yml index a518cd23a..c48c45c2f 100644 --- a/.github/workflows/digest.yml +++ b/.github/workflows/digest.yml @@ -52,7 +52,6 @@ jobs: profile: minimal - run: cargo check --all-features - run: cargo test --release - - run: cargo test --features core-api --release - run: cargo test --features dev --release - run: cargo test --features alloc --release - run: cargo test --features std --release diff --git a/Cargo.lock b/Cargo.lock index e2867fdaf..9cadc58bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "aead" version = "0.4.3" @@ -52,9 +54,8 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.0-pre.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4b13c429c0b48d55a541108e23c7795eb821ee65b50c2f719f4f7fc5a022dcf" +version = "0.10.0" +source = "git+https://github.com/RustCrypto/utils?branch=pad_error#f207c0b0017d4f7bd4845058f1e3597d36dfad60" dependencies = [ "block-padding", "generic-array", @@ -62,9 +63,8 @@ dependencies = [ [[package]] name = "block-padding" -version = "0.3.0-pre" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3992d179d1dd2fa87869057217d43cf88ad31d4e44738d159a5a6caafdf63ae6" +version = "0.3.0" +source = "git+https://github.com/RustCrypto/utils?branch=pad_error#f207c0b0017d4f7bd4845058f1e3597d36dfad60" dependencies = [ "generic-array", ] @@ -83,13 +83,12 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cipher" -version = "0.4.0-pre" +version = "0.4.0" dependencies = [ "blobby", - "block-buffer 0.10.0-pre.4", "crypto-common", "generic-array", - "rand_core", + "inout", ] [[package]] @@ -103,22 +102,10 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.0-pre" +version = "0.1.0" dependencies = [ - "block-buffer 0.10.0-pre.4", - "generic-array", -] - -[[package]] -name = "crypto-mac" -version = "0.12.0-pre" -dependencies = [ - "blobby", - "cipher", - "crypto-common", "generic-array", "rand_core", - "subtle", ] [[package]] @@ -132,11 +119,13 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.0-pre.3" +version = "0.10.0" dependencies = [ "blobby", + "block-buffer 0.10.0", "crypto-common", "generic-array", + "subtle", ] [[package]] @@ -171,9 +160,9 @@ dependencies = [ [[package]] name = "heapless" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe65ef062f1af5b1b189842b0bc45bd671c38e1d22c6aa22e6ada03d01026d53" +checksum = "9c1ad878e07405df82b695089e63d278244344f80e764074d0bdfe99b89460f3" dependencies = [ "hash32", "spin", @@ -199,11 +188,19 @@ dependencies = [ "proc-macro-hack", ] +[[package]] +name = "inout" +version = "0.1.0" +source = "git+https://github.com/RustCrypto/utils?branch=pad_error#f207c0b0017d4f7bd4845058f1e3597d36dfad60" +dependencies = [ + "generic-array", +] + [[package]] name = "libc" -version = "0.2.104" +version = "0.2.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b2f96d100e1cf1929e7719b7edb3b90ab5298072638fccd77be9ce942ecdfce" +checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" [[package]] name = "lock_api" @@ -237,9 +234,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.30" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70" +checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" dependencies = [ "unicode-xid", ] @@ -325,9 +322,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.80" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" +checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" dependencies = [ "proc-macro2", "quote", @@ -358,14 +355,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" -[[package]] -name = "universal-hash" -version = "0.4.1" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "version_check" version = "0.9.3" diff --git a/Cargo.toml b/Cargo.toml index 10ed45ce0..7f217547d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,10 +3,12 @@ members = [ "aead", "cipher", "crypto-common", - "crypto-mac", "digest", "password-hash", "signature", "signature/async", - "universal-hash", ] + +[patch.crates-io] +block-buffer = { git = "https://github.com/RustCrypto/utils", branch = "pad_error" } +inout = { git = "https://github.com/RustCrypto/utils", branch = "pad_error" } diff --git a/cipher/Cargo.toml b/cipher/Cargo.toml index 017e3c028..272b0acde 100644 --- a/cipher/Cargo.toml +++ b/cipher/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cipher" description = "Traits for describing block ciphers and stream ciphers" -version = "0.4.0-pre" +version = "0.4.0" authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" readme = "README.md" @@ -13,17 +13,17 @@ categories = ["cryptography", "no-std"] [dependencies] generic-array = "0.14" -crypto-common = { version = "=0.1.0-pre", path = "../crypto-common" } +crypto-common = { version = "0.1", path = "../crypto-common" } +#block-buffer = { version = "0.10.0", features = ["block-padding"] } +# TODO: remove and use re-import from block-buffer +inout = "0.1" # optional dependencies -block-buffer = { version = "=0.10.0-pre.4", features = ["block-padding"], optional = true } blobby = { version = "0.3", optional = true } -rand_core = { version = "0.6", optional = true } [features] -default = ["mode_wrapper"] -std = ["crypto-common/std", "rand_core/std"] -mode_wrapper = ["block-buffer"] +std = ["crypto-common/std"] +rand_core = ["crypto-common/rand_core"] dev = ["blobby"] [package.metadata.docs.rs] diff --git a/cipher/src/block.rs b/cipher/src/block.rs index 1769c782e..8a999c7ec 100644 --- a/cipher/src/block.rs +++ b/cipher/src/block.rs @@ -10,228 +10,398 @@ //! [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 generic_array::typenum::U1; +use inout::{InOut, InOutBuf, InSrc, InTmpOutBuf, NotEqualError}; -/// Key for an algorithm that implements [`FromKey`]. -pub type BlockCipherKey = GenericArray::KeySize>; +pub use crypto_common::{Block, BlockSizeUser}; -/// Block on which a [`BlockCipher`] operates. -pub type Block = GenericArray::BlockSize>; - -/// Block on which a [`BlockCipher`] operates in parallel. -pub type ParBlocks = GenericArray, ::ParBlocks>; - -/// Trait which marks a type as being a block cipher. -pub trait BlockCipher { - /// Size of the block in bytes - type BlockSize: ArrayLength; - - /// Number of blocks which can be processed in parallel by - /// cipher implementation - type ParBlocks: ArrayLength>; -} +/// Marker trait for block ciphers. +pub trait BlockCipher: BlockSizeUser {} /// 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 { + /// Encrypt single `inout` block. + fn encrypt_block_inout(&self, block: InOut<'_, Block>); + + /// Encrypt `inout` blocks with given pre and post hooks. + fn encrypt_blocks_with_pre( + &self, + blocks: InOutBuf<'_, Block>, + pre_fn: impl FnMut(InTmpOutBuf<'_, Block>) -> InSrc, + post_fn: impl FnMut(InTmpOutBuf<'_, Block>), + ) { + blocks.process_chunks::( + self, + pre_fn, + post_fn, + |state, mut chunk| state.encrypt_block_inout(chunk.get(0)), + |state, mut chunk| state.encrypt_block_inout(chunk.get(0)), + ) + } - /// Encrypt several blocks in parallel using instruction level parallelism - /// if possible. - /// - /// If `ParBlocks` equals to 1 it's equivalent to `encrypt_block`. + /// Encrypt single block in-place. #[inline] - fn encrypt_par_blocks(&self, blocks: &mut ParBlocks) { - for block in blocks.iter_mut() { - self.encrypt_block(block); - } + fn encrypt_block(&self, block: &mut Block) { + self.encrypt_block_inout(block.into()) } - /// Encrypt a slice of blocks, leveraging parallelism when available. + /// Encrypt single block block-to-block, i.e. encrypt + /// block from `in_block` and write result to `out_block`. #[inline] - fn encrypt_blocks(&self, mut blocks: &mut [Block]) { - let pb = Self::ParBlocks::to_usize(); - - if pb > 1 { - let mut iter = blocks.chunks_exact_mut(pb); + fn encrypt_block_b2b(&self, in_block: &Block, out_block: &mut Block) { + self.encrypt_block_inout((in_block, out_block).into()) + } - for chunk in &mut iter { - self.encrypt_par_blocks(chunk.try_into().unwrap()) - } + /// Encrypt `inout` blocks with given post hook. + fn encrypt_blocks_inout( + &self, + blocks: InOutBuf<'_, Block>, + post_fn: impl FnMut(InTmpOutBuf<'_, Block>), + ) { + self.encrypt_blocks_with_pre(blocks, |_| InSrc::In, post_fn) + } - blocks = iter.into_remainder(); - } + /// Encrypt blocks in-place with given post hook. + fn encrypt_blocks(&self, blocks: &mut [Block], mut post_fn: impl FnMut(&[Block])) { + self.encrypt_blocks_with_pre( + blocks.into(), + |_| InSrc::In, + |mut buf| { + buf.copy_tmp2out(); + post_fn(buf.get_out()); + }, + ) + } - for block in blocks { - self.encrypt_block(block); - } + /// Encrypt blocks buffer-to-buffer with given post hook. + /// + /// Returns [`NotEqualError`] if provided `in_blocks` and `out_blocks` + /// have different lengths. + fn encrypt_blocks_b2b( + &self, + in_blocks: &[Block], + out_blocks: &mut [Block], + mut post_fn: impl FnMut(&[Block]), + ) -> Result<(), NotEqualError> { + self.encrypt_blocks_with_pre( + InOutBuf::new(in_blocks, out_blocks)?, + |_| InSrc::In, + |mut buf| { + buf.copy_tmp2out(); + post_fn(buf.get_out()); + }, + ); + Ok(()) } } /// 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 single `inout` block. + fn decrypt_block_inout(&self, block: InOut<'_, Block>); + + /// Decrypt `inout` blocks with given pre and post hooks. + fn decrypt_blocks_with_pre( + &self, + blocks: InOutBuf<'_, Block>, + pre_fn: impl FnMut(InTmpOutBuf<'_, Block>) -> InSrc, + post_fn: impl FnMut(InTmpOutBuf<'_, Block>), + ) { + blocks.process_chunks::( + self, + pre_fn, + post_fn, + |state, mut chunk| state.decrypt_block_inout(chunk.get(0)), + |state, mut chunk| state.decrypt_block_inout(chunk.get(0)), + ) + } - /// Decrypt several blocks in parallel using instruction level parallelism - /// if possible. - /// - /// If `ParBlocks` equals to 1 it's equivalent to `decrypt_block`. + /// Decrypt single block in-place. #[inline] - fn decrypt_par_blocks(&self, blocks: &mut ParBlocks) { - for block in blocks.iter_mut() { - self.decrypt_block(block); - } + fn decrypt_block(&self, block: &mut Block) { + self.decrypt_block_inout(block.into()) } - /// Decrypt a slice of blocks, leveraging parallelism when available. + /// Decrypt single block block-to-block, i.e. encrypt + /// block from `in_block` and write result to `out_block`. #[inline] - fn decrypt_blocks(&self, mut blocks: &mut [Block]) { - let pb = Self::ParBlocks::to_usize(); - - if pb > 1 { - let mut iter = blocks.chunks_exact_mut(pb); - - for chunk in &mut iter { - self.decrypt_par_blocks(chunk.try_into().unwrap()) - } + fn decrypt_block_b2b(&self, in_block: &Block, out_block: &mut Block) { + self.decrypt_block_inout((in_block, out_block).into()) + } - blocks = iter.into_remainder(); - } + /// Decrypt `inout` blocks with given post hook. + fn decrypt_blocks_inout( + &self, + blocks: InOutBuf<'_, Block>, + post_fn: impl FnMut(InTmpOutBuf<'_, Block>), + ) { + self.decrypt_blocks_with_pre(blocks, |_| InSrc::In, post_fn) + } - for block in blocks { - self.decrypt_block(block); - } + /// Decrypt blocks in-place with given post hook. + fn decrypt_blocks(&self, blocks: &mut [Block], mut post_fn: impl FnMut(&[Block])) { + self.decrypt_blocks_with_pre( + blocks.into(), + |_| InSrc::In, + |mut buf| { + buf.copy_tmp2out(); + post_fn(buf.get_out()); + }, + ) } -} -/// Encrypt-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 BlockEncryptMut: BlockCipher { - /// Encrypt block in-place - fn encrypt_block_mut(&mut self, block: &mut Block); + /// Decrypt blocks buffer-to-buffer with given post hook. + /// + /// Returns [`NotEqualError`] if provided `in_blocks` and `out_blocks` + /// have different lengths. + fn decrypt_blocks_b2b( + &self, + in_blocks: &[Block], + out_blocks: &mut [Block], + mut post_fn: impl FnMut(&[Block]), + ) -> Result<(), NotEqualError> { + self.decrypt_blocks_with_pre( + InOutBuf::new(in_blocks, out_blocks)?, + |_| InSrc::In, + |mut buf| { + buf.copy_tmp2out(); + post_fn(buf.get_out()); + }, + ); + Ok(()) + } } -/// Decrypt-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 BlockDecryptMut: BlockCipher { - /// Decrypt block in-place - fn decrypt_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 { + /// Encrypt single `inout` block. + fn encrypt_block_inout_mut(&mut self, block: InOut<'_, Block>); + + /// Encrypt `inout` blocks with given pre and post hooks. + fn encrypt_blocks_with_pre_mut( + &mut self, + blocks: InOutBuf<'_, Block>, + pre_fn: impl FnMut(InTmpOutBuf<'_, Block>) -> InSrc, + post_fn: impl FnMut(InTmpOutBuf<'_, Block>), + ) { + blocks.process_chunks::( + self, + pre_fn, + post_fn, + |state, mut chunk| state.encrypt_block_inout_mut(chunk.get(0)), + |state, mut chunk| state.encrypt_block_inout_mut(chunk.get(0)), + ) + } -impl BlockEncryptMut for Alg { + /// Encrypt block in-place. + #[inline] fn encrypt_block_mut(&mut self, block: &mut Block) { - self.encrypt_block(block); + self.encrypt_block_inout_mut(block.into()) } -} -impl BlockDecryptMut for Alg { - fn decrypt_block_mut(&mut self, block: &mut Block) { - self.decrypt_block(block); + /// Encrypt single block block-to-block, i.e. encrypt + /// block from `in_block` and write result to `out_block`. + #[inline] + fn encrypt_block_b2b_mut(&mut self, in_block: &Block, out_block: &mut Block) { + self.encrypt_block_inout_mut((in_block, out_block).into()) } -} -// Impls of block cipher traits for reference types + /// Encrypt `inout` blocks with given post hook. + fn encrypt_blocks_inout_mut( + &mut self, + blocks: InOutBuf<'_, Block>, + post_fn: impl FnMut(InTmpOutBuf<'_, Block>), + ) { + self.encrypt_blocks_with_pre_mut(blocks, |_| InSrc::In, post_fn) + } + + /// Encrypt blocks in-place with given post hook. + fn encrypt_blocks_mut( + &mut self, + blocks: &mut [Block], + mut post_fn: impl FnMut(&[Block]), + ) { + self.encrypt_blocks_with_pre_mut( + blocks.into(), + |_| InSrc::In, + |mut buf| { + buf.copy_tmp2out(); + post_fn(buf.get_out()); + }, + ) + } -impl BlockCipher for &Alg { - type BlockSize = Alg::BlockSize; - type ParBlocks = Alg::ParBlocks; + /// Encrypt blocks buffer-to-buffer with given post hook. + /// + /// Returns [`NotEqualError`] if provided `in_blocks` and `out_blocks` + /// have different lengths. + fn decrypt_blocks_b2b_mut( + &mut self, + in_blocks: &[Block], + out_blocks: &mut [Block], + mut post_fn: impl FnMut(&[Block]), + ) -> Result<(), NotEqualError> { + self.encrypt_blocks_with_pre_mut( + InOutBuf::new(in_blocks, out_blocks)?, + |_| InSrc::In, + |mut buf| { + buf.copy_tmp2out(); + post_fn(buf.get_out()); + }, + ); + Ok(()) + } } -impl BlockEncrypt for &Alg { - #[inline] - fn encrypt_block(&self, block: &mut Block) { - Alg::encrypt_block(self, block); +/// 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 { + /// Decrypt single `inout` block. + fn decrypt_block_inout_mut(&mut self, block: InOut<'_, Block>); + + /// Decrypt `inout` blocks with given pre and post hooks. + fn decrypt_blocks_with_pre_mut( + &mut self, + blocks: InOutBuf<'_, Block>, + pre_fn: impl FnMut(InTmpOutBuf<'_, Block>) -> InSrc, + post_fn: impl FnMut(InTmpOutBuf<'_, Block>), + ) { + blocks.process_chunks::( + self, + pre_fn, + post_fn, + |state, mut chunk| state.decrypt_block_inout_mut(chunk.get(0)), + |state, mut chunk| state.decrypt_block_inout_mut(chunk.get(0)), + ) } + /// Decrypt single block in-place. #[inline] - fn encrypt_par_blocks(&self, blocks: &mut ParBlocks) { - Alg::encrypt_par_blocks(self, blocks); + fn decrypt_block_mut(&mut self, block: &mut Block) { + self.decrypt_block_inout_mut(block.into()) } + /// Decrypt single block block-to-block, i.e. encrypt + /// block from `in_block` and write result to `out_block`. #[inline] - fn encrypt_blocks(&self, blocks: &mut [Block]) { - Alg::encrypt_blocks(self, blocks); + fn decrypt_block_b2b_mut(&mut self, in_block: &Block, out_block: &mut Block) { + self.decrypt_block_inout_mut((in_block, out_block).into()) } -} -impl BlockDecrypt for &Alg { - #[inline] - fn decrypt_block(&self, block: &mut Block) { - Alg::decrypt_block(self, block); + /// Decrypt `inout` blocks with given post hook. + fn decrypt_blocks_inout_mut( + &mut self, + blocks: InOutBuf<'_, Block>, + post_fn: impl FnMut(InTmpOutBuf<'_, Block>), + ) { + self.decrypt_blocks_with_pre_mut(blocks, |_| InSrc::In, post_fn) } - #[inline] - fn decrypt_par_blocks(&self, blocks: &mut ParBlocks) { - Alg::decrypt_par_blocks(self, blocks); + /// Decrypt blocks in-place with given post hook. + fn decrypt_blocks_mut( + &mut self, + blocks: &mut [Block], + mut post_fn: impl FnMut(&[Block]), + ) { + self.decrypt_blocks_with_pre_mut( + blocks.into(), + |_| InSrc::In, + |mut buf| { + buf.copy_tmp2out(); + post_fn(buf.get_out()); + }, + ) } - #[inline] - fn decrypt_blocks(&self, blocks: &mut [Block]) { - Alg::decrypt_blocks(self, blocks); + /// Decrypt blocks buffer-to-buffer with given post hook. + /// + /// Returns [`NotEqualError`] if provided `in_blocks` and `out_blocks` + /// have different lengths. + fn decrypt_blocks_b2b_mut( + &mut self, + in_blocks: &[Block], + out_blocks: &mut [Block], + mut post_fn: impl FnMut(&[Block]), + ) -> Result<(), NotEqualError> { + self.decrypt_blocks_with_pre_mut( + InOutBuf::new(in_blocks, out_blocks)?, + |_| InSrc::In, + |mut buf| { + buf.copy_tmp2out(); + post_fn(buf.get_out()); + }, + ); + Ok(()) } } -/// Trait for types which can be initialized from a block cipher. -pub trait FromBlockCipher { - /// Block cipher used for initialization. - type BlockCipher: BlockCipher; +impl BlockEncryptMut for Alg { + #[inline] + fn encrypt_block_inout_mut(&mut self, block: InOut<'_, Block>) { + self.encrypt_block_inout(block) + } - /// Initialize instance from block cipher. - fn from_block_cipher(cipher: Self::BlockCipher) -> Self; + fn encrypt_blocks_with_pre_mut( + &mut self, + blocks: InOutBuf<'_, Block>, + pre_fn: impl FnMut(InTmpOutBuf<'_, Block>) -> InSrc, + post_fn: impl FnMut(InTmpOutBuf<'_, Block>), + ) { + self.encrypt_blocks_with_pre(blocks, pre_fn, post_fn) + } } -/// 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; - - /// Initialize instance from block cipher and nonce. - fn from_block_cipher_nonce( - cipher: Self::BlockCipher, - nonce: &GenericArray, - ) -> Self; +impl BlockDecryptMut for Alg { + #[inline] + fn decrypt_block_inout_mut(&mut self, block: InOut<'_, Block>) { + self.decrypt_block_inout(block) + } + + fn decrypt_blocks_with_pre_mut( + &mut self, + blocks: InOutBuf<'_, Block>, + pre_fn: impl FnMut(InTmpOutBuf<'_, Block>) -> InSrc, + post_fn: impl FnMut(InTmpOutBuf<'_, Block>), + ) { + self.decrypt_blocks_with_pre(blocks, pre_fn, post_fn) + } } -impl FromKeyNonce for T -where - T: FromBlockCipherNonce, - T::BlockCipher: FromKey, -{ - type KeySize = ::KeySize; - type NonceSize = T::NonceSize; +impl BlockEncrypt for &Alg { + #[inline] + fn encrypt_block_inout(&self, block: InOut<'_, Block>) { + Alg::encrypt_block_inout(self, block) + } - fn new( - key: &GenericArray, - nonce: &GenericArray, - ) -> Self { - Self::from_block_cipher_nonce(T::BlockCipher::new(key), nonce) + fn encrypt_blocks_with_pre( + &self, + blocks: InOutBuf<'_, Block>, + pre_fn: impl FnMut(InTmpOutBuf<'_, Block>) -> InSrc, + post_fn: impl FnMut(InTmpOutBuf<'_, Block>), + ) { + Alg::encrypt_blocks_with_pre(self, blocks, pre_fn, post_fn) } } -impl FromKey for T -where - T: FromBlockCipher, - T::BlockCipher: FromKey, -{ - type KeySize = ::KeySize; - - fn new(key: &GenericArray) -> Self { - Self::from_block_cipher(T::BlockCipher::new(key)) +impl BlockDecrypt for &Alg { + #[inline] + fn decrypt_block_inout(&self, block: InOut<'_, Block>) { + Alg::decrypt_block_inout(self, block) } - fn new_from_slice(key: &[u8]) -> Result { - T::BlockCipher::new_from_slice(key) - .map_err(|_| InvalidLength) - .map(Self::from_block_cipher) + fn decrypt_blocks_with_pre( + &self, + blocks: InOutBuf<'_, Block>, + pre_fn: impl FnMut(InTmpOutBuf<'_, Block>) -> InSrc, + post_fn: impl FnMut(InTmpOutBuf<'_, Block>), + ) { + Alg::decrypt_blocks_with_pre(self, blocks, pre_fn, post_fn) } } diff --git a/cipher/src/dev/block.rs b/cipher/src/dev/block.rs index fc53b9435..fec627d97 100644 --- a/cipher/src/dev/block.rs +++ b/cipher/src/dev/block.rs @@ -11,19 +11,19 @@ macro_rules! block_cipher_test { fn $name() { use cipher::generic_array::{typenum::Unsigned, GenericArray}; use cipher::{ - blobby::Blob3Iterator, BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher, + blobby::Blob3Iterator, 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,91 @@ 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, + ); } } - // test if cipher can be cloned - let key = Default::default(); - let _ = <$cipher as NewBlockCipher>::new(&key).clone(); } }; } -/// Define block cipher benchmark +/// Define block encryptor benchmark #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "dev")))] -macro_rules! block_cipher_bench { - ($cipher:path, $key_len:expr) => { - extern crate test; - - use cipher::{BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher}; - use test::Bencher; - +macro_rules! block_encryptor_bench { + ($cipher:path, $block_name:ident, $blocks_name:ident $(,)? ) => { #[bench] - pub fn encrypt(bh: &mut Bencher) { - let state = <$cipher>::new_from_slice(&[1u8; $key_len]).unwrap(); + pub fn $block_name(bh: &mut test::Bencher) { + use cipher::{BlockEncryptMut, KeyInit}; + + let key = Default::default(); + let mut cipher = test::black_box(<$cipher>::new(&key)); let mut block = Default::default(); bh.iter(|| { - state.encrypt_block(&mut block); + cipher.encrypt_block_mut(&mut block); test::black_box(&block); }); bh.bytes = block.len() as u64; } #[bench] - pub fn decrypt(bh: &mut Bencher) { - let state = <$cipher>::new_from_slice(&[1u8; $key_len]).unwrap(); + pub fn $blocks_name(bh: &mut test::Bencher) { + use cipher::{BlockEncryptMut, KeyInit}; + + let key = Default::default(); + let mut cipher = test::black_box(<$cipher>::new(&key)); + let mut blocks = vec![Default::default(); 16 * 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 { + ($cipher:path, $block_name:ident, $blocks_name:ident $(,)? ) => { + #[bench] + pub fn $block_name(bh: &mut test::Bencher) { + use cipher::{BlockEncryptMut, KeyInit}; + + let key = Default::default(); + let mut cipher = test::black_box(<$cipher>::new(&key)); let mut block = Default::default(); bh.iter(|| { - state.decrypt_block(&mut block); + cipher.encrypt_block_mut(&mut block); test::black_box(&block); }); bh.bytes = block.len() as u64; } + + #[bench] + pub fn $blocks_name(bh: &mut test::Bencher) { + use cipher::{BlockEncryptMut, KeyInit}; + + let key = Default::default(); + let mut cipher = test::black_box(<$cipher>::new(&key)); + let mut blocks = vec![Default::default(); 16 * 1024]; + + bh.iter(|| { + cipher.encrypt_blocks_mut(&mut blocks, |_| {}); + test::black_box(&blocks); + }); + 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..a89d4763d 100644 --- a/cipher/src/dev/stream.rs +++ b/cipher/src/dev/stream.rs @@ -8,7 +8,7 @@ macro_rules! stream_cipher_test { #[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()) @@ -97,7 +97,7 @@ macro_rules! stream_cipher_async_test { #[test] fn $name() { use cipher::generic_array::GenericArray; - use cipher::{blobby::Blob4Iterator, AsyncStreamCipher, NewCipher}; + use cipher::{blobby::Blob4Iterator, AsyncStreamCipher, KeyIvInit}; fn run_test( key: &[u8], @@ -172,7 +172,7 @@ macro_rules! stream_cipher_sync_bench { ($cipher:path) => { extern crate test; - use cipher::{generic_array::GenericArray, NewCipher, StreamCipher}; + use cipher::{generic_array::GenericArray, KeyIvInit, StreamCipher}; use test::Bencher; #[inline(never)] @@ -224,7 +224,7 @@ macro_rules! stream_cipher_async_bench { ($cipher:path) => { extern crate test; - use cipher::{generic_array::GenericArray, AsyncStreamCipher, NewCipher}; + use cipher::{generic_array::GenericArray, AsyncStreamCipher, KeyIvInit}; use test::Bencher; #[inline(never)] diff --git a/cipher/src/errors.rs b/cipher/src/errors.rs index 644495e80..acd2f488a 100644 --- a/cipher/src/errors.rs +++ b/cipher/src/errors.rs @@ -2,18 +2,23 @@ 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 {} +impl std::error::Error for StreamCipherError {} /// The error type returned when a cipher position can not be represented /// by the requested type. @@ -26,45 +31,11 @@ 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")] 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..d9c0e82be 100644 --- a/cipher/src/lib.rs +++ b/cipher/src/lib.rs @@ -1,8 +1,9 @@ //! 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))] @@ -10,130 +11,36 @@ html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg" )] -#![forbid(unsafe_code)] +// #![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; - #[cfg(feature = "dev")] pub use blobby; +//pub use block_buffer; + mod block; #[cfg(feature = "dev")] mod dev; -pub mod errors; -mod mode; +mod errors; mod stream; +mod stream_core; +mod stream_wrapper; -#[cfg(feature = "mode_wrapper")] -mod mode_wrapper; - -pub use crate::{block::*, mode::*, stream::*}; +pub use crate::{block::*, errors::*, stream::*, stream_core::*, stream_wrapper::*}; +pub use crypto_common::{ + Block, InnerIvInit, InvalidLength, Iv, IvSizeUser, Key, KeyInit, KeyIvInit, KeySizeUser, +}; 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)) - } -} - -/// 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 - } +/// Trait for loading current IV state. +pub trait IvState: IvSizeUser { + /// Returns current IV state. + fn iv_state(&self) -> Iv; } 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 e33dd15a1..f3b445c01 100644 --- a/cipher/src/stream.rs +++ b/cipher/src/stream.rs @@ -3,48 +3,126 @@ //! 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; + +/// Marker trait for block-level asynchronous stream ciphers +pub trait AsyncStreamCipher: BlockEncryptMut + BlockDecryptMut + Sized { + /// Encrypt data using `InOutBuf`. + fn encrypt_inout(mut self, data: InOutBuf<'_, u8>, mut post_fn: impl FnMut(&[Block])) { + let (blocks, tail) = data.into_chunks(); + self.encrypt_blocks_inout_mut(blocks, |mut buf| { + buf.copy_tmp2out(); + post_fn(buf.get_out()); + }); + 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>, mut post_fn: impl FnMut(&[Block])) { + let (blocks, tail) = data.into_chunks(); + self.decrypt_blocks_inout_mut(blocks, |mut buf| { + buf.copy_tmp2out(); + post_fn(buf.get_out()); + }); + 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]) { + self.encrypt_inout(buf.into(), |_| {}); + } + + /// Decrypt data in place. + fn decrypt(self, buf: &mut [u8]) { + self.decrypt_inout(buf.into(), |_| {}); + } +} /// Synchronous stream cipher core trait. pub trait StreamCipher { - /// Apply keystream to the data. + /// Apply keystream to data behind `buf` and return explicit result. /// - /// It will XOR generated keystream with the data, which can be both - /// encryption and decryption. + /// 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(&mut self, buf: InOutBuf<'_, u8>) -> Result<(), StreamCipherError>; + + /// Apply keystream to `inout` data. + /// + /// 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(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.into()).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(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 [`StreamCipherError`] 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 /// @@ -63,72 +141,56 @@ pub trait StreamCipherSeek { } } -/// 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); + fn apply_keystream_inout(&mut self, buf: InOutBuf<'_, u8>) { + C::apply_keystream_inout(self, buf); } #[inline] - fn try_apply_keystream(&mut self, data: &mut [u8]) -> Result<(), LoopError> { - C::try_apply_keystream(self, data) + fn try_apply_keystream(&mut self, buf: InOutBuf<'_, u8>) -> Result<(), StreamCipherError> { + C::try_apply_keystream(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: usize, bs: usize) + -> 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: usize) -> Result<(T, usize), 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: usize, bs: usize) -> 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: usize) -> Result<(T, usize), OverflowError> { let bs = bs as Self; let byte = self % bs; let block = T::try_from(self/bs).map_err(|_| OverflowError)?; - Ok((block, byte as u8)) + Ok((block, byte as usize)) } } )* }; } -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..263b17238 --- /dev/null +++ b/cipher/src/stream_core.rs @@ -0,0 +1,121 @@ +use crate::errors::StreamCipherError; +use core::convert::{TryFrom, TryInto}; +use crypto_common::{Block, BlockSizeUser}; +use generic_array::typenum::Unsigned; +use inout::InOutBuf; + +/// 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; + + /// Apply keystream blocks with pre and post callbacks using + /// parallel block processing if possible. + /// + /// WARNING: this method does not check number of remaining blocks! + fn apply_keystream_blocks( + &mut self, + blocks: InOutBuf<'_, Block>, + pre_fn: impl FnMut(&[Block]), + post_fn: impl FnMut(&[Block]), + ); + + /// 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. + 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(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 mut t = InOutBuf::from_mut(&mut block); + self.apply_keystream_blocks(t.reborrow(), |_| {}, |_| {}); + 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. + 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 } diff --git a/cipher/src/stream_wrapper.rs b/cipher/src/stream_wrapper.rs new file mode 100644 index 000000000..1b4971967 --- /dev/null +++ b/cipher/src/stream_wrapper.rs @@ -0,0 +1,176 @@ +use crate::{ + errors::StreamCipherError, Block, OverflowError, SeekNum, StreamCipher, StreamCipherCore, + StreamCipherSeek, StreamCipherSeekCore, +}; +use crypto_common::{BlockSizeUser, Iv, IvSizeUser, Key, KeyInit, KeyIvInit, KeySizeUser}; +use generic_array::typenum::Unsigned; +use inout::InOutBuf; + +/// Wrapper around [`StreamCipherCore`] implementations. +/// +/// It handles data buffering and implements the slice-based traits. +#[derive(Clone, Default)] +pub struct StreamCipherCoreWrapper { + core: T, + buffer: Block, + pos: usize, +} + +impl StreamCipherCoreWrapper { + /// Return reference to the core type. + pub fn get_core(&self) -> &T { + &self.core + } + + /// Return current cursor position. + #[inline] + fn get_pos(&self) -> usize { + if self.pos >= T::BlockSize::USIZE { + // 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; + } + + /// 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 { + #[inline] + fn try_apply_keystream(&mut self, mut data: InOutBuf<'_, u8>) -> Result<(), StreamCipherError> { + self.check_remaining(data.len())?; + + let pos = self.get_pos(); + let r = self.remaining(); + let n = data.len(); + if pos != 0 { + if n < r { + // double slicing allows to remove panic branches + data.xor(&self.buffer[pos..][..n]); + self.set_pos_unchecked(pos + n); + return Ok(()); + } + let (mut left, right) = data.split_at(r); + data = right; + left.xor(&self.buffer[pos..]); + } + + let (blocks, mut leftover) = data.into_chunks(); + self.core.apply_keystream_blocks(blocks, |_| {}, |_| {}); + + let n = leftover.len(); + if n != 0 { + let mut block = Default::default(); + self.core + .apply_keystream_blocks(InOutBuf::from_mut(&mut block), |_| {}, |_| {}); + leftover.xor(&block[..n]); + self.buffer = block; + } + self.set_pos_unchecked(n); + + Ok(()) + } +} + +impl StreamCipherSeek for StreamCipherCoreWrapper { + fn try_current_pos(&self) -> Result { + let Self { core, pos, .. } = self; + let bs = T::BlockSize::USIZE; + SN::from_block_byte(core.get_block_pos(), *pos, bs) + } + + fn try_seek(&mut self, new_pos: SN) -> Result<(), StreamCipherError> { + let Self { core, buffer, pos } = self; + let bs = T::BlockSize::USIZE; + let (block_pos, byte_pos) = new_pos.into_block_byte(bs)?; + core.set_block_pos(block_pos); + if byte_pos != 0 { + let mut block = Default::default(); + let buf = InOutBuf::from_mut(&mut block); + core.apply_keystream_blocks(buf, |_| {}, |_| {}); + *buffer = block; + } + *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 { + type KeySize = T::KeySize; +} + +impl IvSizeUser for StreamCipherCoreWrapper { + type IvSize = T::IvSize; +} + +impl KeyIvInit for StreamCipherCoreWrapper { + #[inline] + fn new(key: &Key, iv: &Iv) -> Self { + Self { + core: T::new(key, iv), + buffer: Default::default(), + pos: 0, + } + } +} + +impl KeyInit for StreamCipherCoreWrapper { + #[inline] + fn new(key: &Key) -> Self { + Self { + core: T::new(key), + buffer: Default::default(), + pos: 0, + } + } +} diff --git a/crypto-common/Cargo.toml b/crypto-common/Cargo.toml index 3fa429be3..b38f4bb9d 100644 --- a/crypto-common/Cargo.toml +++ b/crypto-common/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "crypto-common" description = "Common cryptographic traits" -version = "0.1.0-pre" +version = "0.1.0" authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" readme = "README.md" @@ -13,9 +13,7 @@ categories = ["cryptography", "no-std"] [dependencies] generic-array = "0.14" -block-buffer = { version = "0.10.0-pre.2", optional = true } +rand_core = { version = "0.6", optional = true } [features] -block-padding = ["block-buffer/block-padding"] -core-api = ["block-buffer"] std = [] diff --git a/crypto-common/src/core_api.rs b/crypto-common/src/core_api.rs deleted file mode 100644 index 7def7a076..000000000 --- a/crypto-common/src/core_api.rs +++ /dev/null @@ -1,132 +0,0 @@ -//! Low-level core API traits. -use super::{FixedOutput, FixedOutputReset, Reset, Update}; -use block_buffer::DigestBuffer; -use core::fmt; -use generic_array::{ArrayLength, GenericArray}; - -/// Trait for types which consume data in blocks. -#[cfg(feature = "core-api")] -#[cfg_attr(docsrs, doc(cfg(feature = "core-api")))] -pub trait UpdateCore { - /// Block size in bytes. - type BlockSize: ArrayLength; - /// Block buffer type over which value operates. - type Buffer: DigestBuffer; - - /// Update state using the provided data blocks. - fn update_blocks(&mut self, blocks: &[GenericArray]); -} - -/// Core trait for hash functions with fixed output size. -#[cfg(feature = "core-api")] -#[cfg_attr(docsrs, doc(cfg(feature = "core-api")))] -pub trait FixedOutputCore: UpdateCore { - /// Size of result in bytes. - type OutputSize: ArrayLength; - - /// Finalize state using remaining data stored in the provided block buffer, - /// write result into provided array using and leave value in a dirty state. - fn finalize_fixed_core( - &mut self, - buffer: &mut Self::Buffer, - out: &mut GenericArray, - ); -} - -/// Trait which stores algorithm name constant, used in `Debug` implementations. -pub trait AlgorithmName { - /// Write algorithm name into `f`. - fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result; -} - -/// Wrapper around [`UpdateCore`] implementations. -/// -/// It handles data buffering and implements the slice-based traits. -#[derive(Clone, Default)] -pub struct CoreWrapper { - core: T, - buffer: T::Buffer, -} - -impl CoreWrapper { - /// Create new wrapper from `core`. - #[inline] - pub fn from_core(core: T) -> Self { - let buffer = Default::default(); - Self { core, buffer } - } - - /// Decompose wrapper into inner parts. - #[inline] - pub fn decompose(self) -> (T, T::Buffer) { - let Self { core, buffer } = self; - (core, buffer) - } -} - -impl CoreWrapper { - /// Apply function to core and buffer, return its result, - /// and reset core and buffer. - pub fn apply_reset(&mut self, mut f: impl FnMut(&mut T, &mut T::Buffer) -> V) -> V { - let Self { core, buffer } = self; - let res = f(core, buffer); - core.reset(); - buffer.reset(); - res - } -} - -impl fmt::Debug for CoreWrapper { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - T::write_alg_name(f)?; - f.write_str(" { .. }") - } -} - -impl Reset for CoreWrapper { - #[inline] - fn reset(&mut self) { - self.core.reset(); - self.buffer.reset(); - } -} - -impl Update for CoreWrapper { - #[inline] - fn update(&mut self, input: &[u8]) { - let Self { core, buffer } = self; - buffer.digest_blocks(input, |blocks| core.update_blocks(blocks)); - } -} - -impl FixedOutput for CoreWrapper { - type OutputSize = D::OutputSize; - - #[inline] - fn finalize_into(mut self, out: &mut GenericArray) { - let Self { core, buffer } = &mut self; - core.finalize_fixed_core(buffer, out); - } -} - -impl FixedOutputReset for CoreWrapper { - #[inline] - fn finalize_into_reset(&mut self, out: &mut GenericArray) { - self.apply_reset(|core, buffer| core.finalize_fixed_core(buffer, out)); - } -} - -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl std::io::Write for CoreWrapper { - #[inline] - fn write(&mut self, buf: &[u8]) -> std::io::Result { - Update::update(self, buf); - Ok(buf.len()) - } - - #[inline] - fn flush(&mut self) -> std::io::Result<()> { - Ok(()) - } -} diff --git a/crypto-common/src/lib.rs b/crypto-common/src/lib.rs index 5869bc677..a11626d37 100644 --- a/crypto-common/src/lib.rs +++ b/crypto-common/src/lib.rs @@ -12,56 +12,262 @@ #[cfg(feature = "std")] extern crate std; -use generic_array::{ArrayLength, GenericArray}; +use core::fmt; +use generic_array::{typenum::Unsigned, ArrayLength, GenericArray}; +#[cfg(feature = "rand_core")] +use rand_core::{CryptoRng, RngCore}; -#[cfg(feature = "core-api")] -#[cfg_attr(docsrs, doc(cfg(feature = "core-api")))] -pub use block_buffer; +/// Block on which [`BlockSizeUser`] implementors operate. +pub type Block = GenericArray::BlockSize>; +/// Output array of [`OutputSizeUser`] implementors. +pub type Output = GenericArray::OutputSize>; +/// Key used by [`KeySizeUser`] implementors. +pub type Key = GenericArray::KeySize>; +/// Initialization vector (nonce) used by [`IvSizeUser`] implementors. +pub type Iv = GenericArray::IvSize>; -#[cfg(feature = "core-api")] -#[cfg_attr(docsrs, doc(cfg(feature = "core-api")))] -pub mod core_api; +/// Types which process data in blocks. +pub trait BlockSizeUser { + /// Size of the block in bytes. + type BlockSize: ArrayLength + 'static; +} -/// Trait for types which consume data. -pub trait Update { - /// Update state using the provided data. - fn update(&mut self, data: &[u8]); +impl BlockSizeUser for &T { + type BlockSize = T::BlockSize; } -/// Trait for types which return fixed-sized result after finalization. -pub trait FixedOutput: Sized { - /// Size of result in bytes. - type OutputSize: ArrayLength; +impl BlockSizeUser for &mut T { + type BlockSize = T::BlockSize; +} - /// Consume value and write result into provided array. - fn finalize_into(self, out: &mut GenericArray); +/// Types which return data with the given size. +pub trait OutputSizeUser { + /// Size of the output in bytes. + type OutputSize: ArrayLength + 'static; +} + +/// Types which use key for initialization. +/// +/// Generally it's used indirectly via [`KeyInit`] or [`KeyIvInit`]. +pub trait KeySizeUser { + /// Key size in bytes. + type KeySize: ArrayLength + 'static; +} + +/// Types which use initialization vector (nonce) for initialization. +/// +/// Generally it's used indirectly via [`KeyIvInit`] or [`InnerIvInit`]. +pub trait IvSizeUser { + /// Initialization vector size in bytes. + type IvSize: ArrayLength + 'static; +} + +/// Types which use another type for initialization. +/// +/// Generally it's used indirectly via [`InnerInit`] or [`InnerIvInit`]. +pub trait InnerUser { + /// Inner type. + type Inner; +} - /// Retrieve result and consume the hasher instance. +/// Resettable types. +pub trait Reset { + /// Reset state to its initial value. + fn reset(&mut self); +} + +/// Trait which stores algorithm name constant, used in `Debug` implementations. +pub trait AlgorithmName { + /// Write algorithm name into `f`. + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result; +} + +/// Types which can be initialized from key. +pub trait KeyInit: KeySizeUser + Sized { + /// Create new value from fixed size key. + fn new(key: &Key) -> Self; + + /// Create new value from variable size key. + fn new_from_slice(key: &[u8]) -> Result { + if key.len() != Self::KeySize::to_usize() { + Err(InvalidLength) + } else { + Ok(Self::new(Key::::from_slice(key))) + } + } + + /// Generate random key using the provided [`CryptoRng`]. + #[cfg(feature = "rand_core")] + #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] #[inline] - fn finalize_fixed(self) -> GenericArray { - let mut out = Default::default(); - self.finalize_into(&mut out); - out + fn generate_key(mut rng: impl CryptoRng + RngCore) -> Key { + let mut key = Key::::default(); + rng.fill_bytes(&mut key); + key } } -/// Trait for types which return fixed-sized result after finalization and reset -/// values into its initial state. -pub trait FixedOutputReset: FixedOutput + Reset { - /// Write result into provided array and reset value to its initial state. - fn finalize_into_reset(&mut self, out: &mut GenericArray); +/// Types which can be initialized from key and initialization vector (nonce). +pub trait KeyIvInit: KeySizeUser + IvSizeUser + Sized { + /// Create new value from fixed length key and nonce. + fn new(key: &Key, iv: &Iv) -> Self; + + /// Create new value from variable length key and nonce. + #[inline] + fn new_from_slices(key: &[u8], iv: &[u8]) -> Result { + let key_len = Self::KeySize::USIZE; + let iv_len = Self::IvSize::USIZE; + if key.len() != key_len || iv.len() != iv_len { + Err(InvalidLength) + } else { + Ok(Self::new( + Key::::from_slice(key), + Iv::::from_slice(iv), + )) + } + } + + /// Generate random key using the provided [`CryptoRng`]. + #[cfg(feature = "rand_core")] + #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] + #[inline] + fn generate_key(mut rng: impl CryptoRng + RngCore) -> Key { + let mut key = Key::::default(); + rng.fill_bytes(&mut key); + key + } + + /// Generate random IV using the provided [`CryptoRng`]. + #[cfg(feature = "rand_core")] + #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] + #[inline] + fn generate_iv(mut rng: impl CryptoRng + RngCore) -> Iv { + let mut iv = Iv::::default(); + rng.fill_bytes(&mut iv); + iv + } - /// Retrieve result and reset the hasher instance. + /// Generate random key and nonce using the provided [`CryptoRng`]. + #[cfg(feature = "rand_core")] + #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] #[inline] - fn finalize_fixed_reset(&mut self) -> GenericArray { - let mut out = Default::default(); - self.finalize_into_reset(&mut out); - out + fn generate_key_iv(mut rng: impl CryptoRng + RngCore) -> (Key, Iv) { + (Self::generate_key(&mut rng), Self::generate_iv(&mut rng)) } } -/// Trait for resetting values to initial state. -pub trait Reset { - /// Reset value to its initial state. - fn reset(&mut self); +/// Types which can be initialized from another type (usually block ciphers). +/// +/// Usually used for initializing types from block ciphers. +pub trait InnerInit: InnerUser + Sized { + /// Initialize value from the `inner`. + fn inner_init(inner: Self::Inner) -> Self; } + +/// Types which can be initialized from another type and additional initialization +/// vector/nonce. +/// +/// Usually used for initializing types from block ciphers. +pub trait InnerIvInit: InnerUser + IvSizeUser + Sized { + /// Initialize value using `inner` and `iv` array. + fn inner_iv_init(inner: Self::Inner, iv: &Iv) -> Self; + + /// Initialize value using `inner` and `iv` slice. + fn inner_iv_slice_init(inner: Self::Inner, iv: &[u8]) -> Result { + if iv.len() != Self::IvSize::to_usize() { + Err(InvalidLength) + } else { + Ok(Self::inner_iv_init(inner, Iv::::from_slice(iv))) + } + } + + /// Generate random IV using the provided [`CryptoRng`]. + #[cfg(feature = "rand_core")] + #[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] + #[inline] + fn generate_iv(mut rng: impl CryptoRng + RngCore) -> Iv { + let mut iv = Iv::::default(); + rng.fill_bytes(&mut iv); + iv + } +} + +impl KeySizeUser for T +where + T: InnerUser, + T::Inner: KeySizeUser, +{ + type KeySize = ::KeySize; +} + +impl KeyIvInit for T +where + T: InnerIvInit, + T::Inner: KeyInit, +{ + #[inline] + fn new(key: &Key, iv: &Iv) -> Self { + Self::inner_iv_init(T::Inner::new(key), iv) + } + + #[inline] + fn new_from_slices(key: &[u8], iv: &[u8]) -> Result { + T::Inner::new_from_slice(key).and_then(|i| T::inner_iv_slice_init(i, iv)) + } +} + +impl KeyInit for T +where + T: InnerInit, + T::Inner: KeyInit, +{ + #[inline] + fn new(key: &Key) -> Self { + Self::inner_init(T::Inner::new(key)) + } + + #[inline] + fn new_from_slice(key: &[u8]) -> Result { + T::Inner::new_from_slice(key) + .map_err(|_| InvalidLength) + .map(Self::inner_init) + } +} + +// Unfortunately this blanket impl is impossible without mutually +// exclusive traits, see: https://github.com/rust-lang/rfcs/issues/1053 +// or at the very least without: https://github.com/rust-lang/rust/issues/20400 +/* +impl KeyIvInit for T +where + T: InnerInit, + T::Inner: KeyIvInit, +{ + #[inline] + fn new(key: &Key, iv: &Iv) -> Self { + Self::inner_init(T::Inner::new(key, iv)) + } + + #[inline] + fn new_from_slices(key: &[u8], iv: &[u8]) -> Result { + T::Inner::new_from_slice(key) + .map_err(|_| InvalidLength) + .map(Self::inner_init) + } +} +*/ + +/// The error type returned when key and/or IV used in the [`KeyInit`], +/// [`KeyIvInit`], and [`InnerIvInit`] slice-based methods had +/// an invalid length. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub struct InvalidLength; + +impl fmt::Display for InvalidLength { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str("Invalid Length") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for InvalidLength {} diff --git a/crypto-mac/CHANGELOG.md b/crypto-mac/CHANGELOG.md deleted file mode 100644 index 7488081b1..000000000 --- a/crypto-mac/CHANGELOG.md +++ /dev/null @@ -1,104 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] -### Added -- Re-export `rand_core` ([#683]) - -[#683]: https://github.com/RustCrypto/traits/pull/683 - -## 0.11.1 (2021-07-20) -### Changed -- Pin `subtle` dependency to v2.4 ([#691]) - -[#691]: https://github.com/RustCrypto/traits/pull/691 - -## 0.11.0 (2021-04-28) -### Added -- `generate_key` method to `New*` trait ([#513]) - -### Changed -- Renamed `new_var` to `new_from_slice` ([#442]) -- Bump `cipher` dependency to v0.3 ([#621]) - -[#442]: https://github.com/RustCrypto/traits/pull/442 -[#513]: https://github.com/RustCrypto/traits/pull/513 -[#621]: https://github.com/RustCrypto/traits/pull/621 - -## 0.10.1 (2021-07-20) -### Changed -- Pin `subtle` dependency to v2.4 ([#690]) - -[#690]: https://github.com/RustCrypto/traits/pull/690 - -## 0.10.0 (2020-10-15) -### Changed -- Replace `block-cipher` crate with new `cipher` crate ([#337], [#338]) - -[#338]: https://github.com/RustCrypto/traits/pull/338 -[#337]: https://github.com/RustCrypto/traits/pull/337 - -## 0.9.1 (2020-08-12) -### Added -- Re-export the `block-cipher` crate ([#257]) - -[#257]: https://github.com/RustCrypto/traits/pull/257 - -## 0.9.0 (2020-08-10) -### Added -- `FromBlockCipher` trait and blanket implementation of the `NewMac` trait -for it ([#217]) - -### Changed -- Updated test vectors storage to `blobby v0.3` ([#217]) - -### Removed -- `impl_write!` macro ([#217]) - -[#217]: https://github.com/RustCrypto/traits/pull/217 - -## 0.8.0 (2020-06-04) -### Added -- `impl_write!` macro ([#134]) - -### Changed -- Bump `generic-array` dependency to v0.14 ([#144]) -- Split `Mac` initialization into `NewMac` trait ([#133]) -- Rename `MacResult` => `Output`, `code` => `into_bytes` ([#114]) -- Rename `Input::input` to `Update::update` ([#111]) -- Update to 2018 edition ([#108]) -- Bump `subtle` dependency from v1.0 to v2.0 ([#33]) - -[#144]: https://github.com/RustCrypto/traits/pull/95 -[#134]: https://github.com/RustCrypto/traits/pull/134 -[#133]: https://github.com/RustCrypto/traits/pull/133 -[#114]: https://github.com/RustCrypto/traits/pull/114 -[#111]: https://github.com/RustCrypto/traits/pull/111 -[#108]: https://github.com/RustCrypto/traits/pull/108 -[#33]: https://github.com/RustCrypto/traits/pull/33 - -## 0.7.0 (2018-10-01) - -## 0.6.2 (2018-06-21) - -## 0.6.1 (2018-06-20) - -## 0.6.0 (2017-11-26) - -## 0.5.2 (2017-11-20) - -## 0.5.1 (2017-11-15) - -## 0.5.0 (2017-11-14) - -## 0.4.0 (2017-06-12) - -## 0.3.0 (2017-05-14) - -## 0.2.0 (2017-05-14) - -## 0.1.0 (2016-10-14) diff --git a/crypto-mac/Cargo.toml b/crypto-mac/Cargo.toml deleted file mode 100644 index 725f75fa4..000000000 --- a/crypto-mac/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "crypto-mac" -description = "Trait for Message Authentication Code (MAC) algorithms" -version = "0.12.0-pre" # Also update html_root_url in lib.rs when bumping this -authors = ["RustCrypto Developers"] -license = "MIT OR Apache-2.0" -readme = "README.md" -edition = "2018" -documentation = "https://docs.rs/crypto-mac" -repository = "https://github.com/RustCrypto/traits" -keywords = ["crypto", "mac"] -categories = ["cryptography", "no-std"] - -[dependencies] -generic-array = "0.14" -crypto-common = { version = "=0.1.0-pre", path = "../crypto-common" } -cipher = { version = "=0.4.0-pre", path = "../cipher" } -subtle = { version = "=2.4", default-features = false } - -blobby = { version = "0.3", optional = true } -rand_core = { version = "0.6", optional = true } - -[features] -dev = ["blobby"] -core-api = ["crypto-common/core-api"] -std = ["crypto-common/std", "rand_core/std"] - -[package.metadata.docs.rs] -all-features = true -rustdoc-args = ["--cfg", "docsrs"] diff --git a/crypto-mac/LICENSE-APACHE b/crypto-mac/LICENSE-APACHE deleted file mode 100644 index 78173fa2e..000000000 --- a/crypto-mac/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/crypto-mac/LICENSE-MIT b/crypto-mac/LICENSE-MIT deleted file mode 100644 index 8dcb85b30..000000000 --- a/crypto-mac/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2017 Artyom Pavlov - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/crypto-mac/README.md b/crypto-mac/README.md deleted file mode 100644 index 889d6c244..000000000 --- a/crypto-mac/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# RustCrypto: Message Authentication Code Traits - -[![crate][crate-image]][crate-link] -[![Docs][docs-image]][docs-link] -![Apache2/MIT licensed][license-image] -![Rust Version][rustc-image] -[![Project Chat][chat-image]][chat-link] -[![Build Status][build-image]][build-link] - -Traits for [Message Authentication Code] (MAC) algorithms. - -See [RustCrypto/MACs] for implementations which use this trait. - -[Documentation][docs-link] - -## Minimum Supported Rust Version - -Rust **1.41** or higher. - -Minimum supported Rust version can be changed in the future, but it will be -done with a minor version bump. - -## SemVer Policy - -- All on-by-default features of this library are covered by SemVer -- MSRV is considered exempt from SemVer as noted above - -## License - -Licensed under either of: - - * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) - * [MIT license](http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in the work by you, as defined in the Apache-2.0 license, shall be -dual licensed as above, without any additional terms or conditions. - -[//]: # (badges) - -[crate-image]: https://img.shields.io/crates/v/crypto-mac.svg -[crate-link]: https://crates.io/crates/crypto-mac -[docs-image]: https://docs.rs/crypto-mac/badge.svg -[docs-link]: https://docs.rs/crypto-mac/ -[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg -[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg -[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg -[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260044-MACs -[build-image]: https://github.com/RustCrypto/traits/workflows/crypto-mac/badge.svg?branch=master&event=push -[build-link]: https://github.com/RustCrypto/traits/actions?query=workflow%3Acrypto-mac - -[//]: # (general links) - -[Message Authentication Code]: https://en.wikipedia.org/wiki/Message_authentication_code -[RustCrypto/MACs]: https://github.com/RustCrypto/MACs diff --git a/crypto-mac/src/core_api.rs b/crypto-mac/src/core_api.rs deleted file mode 100644 index 7f7e6a6ec..000000000 --- a/crypto-mac/src/core_api.rs +++ /dev/null @@ -1,2 +0,0 @@ -//! Low-level core API traits. -pub use crypto_common::core_api::{AlgorithmName, CoreWrapper, FixedOutputCore, UpdateCore}; diff --git a/crypto-mac/src/dev.rs b/crypto-mac/src/dev.rs deleted file mode 100644 index 1c397e631..000000000 --- a/crypto-mac/src/dev.rs +++ /dev/null @@ -1,148 +0,0 @@ -//! Development-related functionality - -pub use blobby; - -/// Define test -#[macro_export] -#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] -macro_rules! new_test { - ($name:ident, $test_name:expr, $mac:ty) => { - #[test] - fn $name() { - use crypto_mac::dev::blobby::Blob3Iterator; - use crypto_mac::{Mac, NewMac}; - - fn run_test(key: &[u8], input: &[u8], tag: &[u8]) -> Option<&'static str> { - let mut mac = <$mac as NewMac>::new_from_slice(key).unwrap(); - mac.update(input); - let result = mac.finalize_reset(); - if &result.into_bytes()[..] != tag { - return Some("whole message"); - } - // test if reset worked correctly - mac.update(input); - if mac.verify(&tag).is_err() { - return Some("after reset"); - } - - let mut mac = <$mac as NewMac>::new_from_slice(key).unwrap(); - // test reading byte by byte - for i in 0..input.len() { - mac.update(&input[i..i + 1]); - } - if let Err(_) = mac.verify(tag) { - return Some("message byte-by-byte"); - } - None - } - - let data = include_bytes!(concat!("data/", $test_name, ".blb")); - - for (i, row) in Blob3Iterator::new(data).unwrap().enumerate() { - let [key, input, tag] = row.unwrap(); - if let Some(desc) = run_test(key, input, tag) { - panic!( - "\n\ - Failed test №{}: {}\n\ - key:\t{:?}\n\ - input:\t{:?}\n\ - tag:\t{:?}\n", - i, desc, key, input, tag, - ); - } - } - } - }; -} - -/// Define test that allows for truncated tag. -#[macro_export] -#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] -macro_rules! new_trunc_test { - ($name:ident, $test_name:expr, $mac:ty) => { - #[test] - fn $name() { - use crypto_mac::dev::blobby::Blob3Iterator; - use crypto_mac::generic_array::typenum::Unsigned; - use crypto_mac::{Mac, NewMac}; - - fn run_test(key: &[u8], input: &[u8], tag: &[u8]) -> Option<&'static str> { - let mut mac = <$mac as NewMac>::new_from_slice(key).unwrap(); - mac.update(input); - let result = mac.finalize_reset(); - let mut len = <$mac as Mac>::OutputSize::to_usize(); - if tag.len() < len { - len = tag.len(); - } - if &result.into_bytes()[..len] != tag { - return Some("whole message"); - } - // test if reset worked correctly - mac.update(input); - let result = mac.finalize(); - if &result.into_bytes()[..len] != tag { - return Some("after reset"); - } - - let mut mac = <$mac as NewMac>::new_from_slice(key).unwrap(); - // test reading byte by byte - for i in 0..input.len() { - mac.update(&input[i..i + 1]); - } - let result = mac.finalize(); - if &result.into_bytes()[..len] != tag { - return Some("message byte-by-byte"); - } - None - } - - let data = include_bytes!(concat!("data/", $test_name, ".blb")); - - for (i, row) in Blob3Iterator::new(data).unwrap().enumerate() { - let [key, input, tag] = row.unwrap(); - if let Some(desc) = run_test(key, input, tag) { - panic!( - "\n\ - Failed test №{}: {}\n\ - key:\t{:?}\n\ - input:\t{:?}\n\ - tag:\t{:?}\n", - i, desc, key, input, tag, - ); - } - } - } - }; -} - -/// Define benchmark -#[macro_export] -#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] -macro_rules! bench { - ($name:ident, $engine:path, $bs:expr) => { - #[bench] - fn $name(b: &mut Bencher) { - let key = Default::default(); - let mut mac = <$engine>::new(&key); - let data = [0; $bs]; - - b.iter(|| { - mac.update(&data); - }); - - b.bytes = $bs; - } - }; - - ($engine:path) => { - extern crate test; - - use crypto_mac::{Mac, NewMac}; - use test::Bencher; - - $crate::bench!(bench1_10, $engine, 10); - $crate::bench!(bench2_100, $engine, 100); - $crate::bench!(bench3_1000, $engine, 1000); - $crate::bench!(bench3_10000, $engine, 10000); - }; -} diff --git a/crypto-mac/src/lib.rs b/crypto-mac/src/lib.rs deleted file mode 100644 index e5e2f2ba7..000000000 --- a/crypto-mac/src/lib.rs +++ /dev/null @@ -1,123 +0,0 @@ -//! This crate provides trait for Message Authentication Code (MAC) algorithms. - -#![no_std] -#![cfg_attr(docsrs, feature(doc_cfg))] -#![doc( - html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg", - html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg", - html_root_url = "https://docs.rs/crypto-mac/0.12.0-pre" -)] -#![forbid(unsafe_code)] -#![warn(missing_docs, rust_2018_idioms)] - -#[cfg(feature = "std")] -extern crate std; - -#[cfg(feature = "rand_core")] -#[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))] -pub use rand_core; - -#[cfg(feature = "cipher")] -pub use cipher; -#[cfg(feature = "cipher")] -use cipher::{BlockCipher, NewBlockCipher}; - -#[cfg(feature = "dev")] -#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] -pub mod dev; - -#[cfg(feature = "core-api")] -#[cfg_attr(docsrs, doc(cfg(feature = "core-api")))] -pub mod core_api; - -pub use cipher::{errors::InvalidLength, FromKey}; -pub use crypto_common::{FixedOutput, FixedOutputReset, Reset, Update}; -pub use generic_array::{self, typenum::consts}; - -use core::fmt; -use generic_array::GenericArray; -use subtle::{Choice, ConstantTimeEq}; - -/// Key for an algorithm that implements [`FromKey`]. -pub type Key = GenericArray::KeySize>; - -/// Convinience super-trait covering functionality of Message Authentication algorithms. -pub trait Mac: FromKey + Update + FixedOutput { - /// Obtain the result of a [`Mac`] computation as a [`Output`] and consume - /// [`Mac`] instance. - fn finalize(self) -> Output { - Output::new(self.finalize_fixed()) - } - - /// Obtain the result of a [`Mac`] computation as a [`Output`] and reset - /// [`Mac`] instance. - fn finalize_reset(&mut self) -> Output - where - Self: FixedOutputReset, - { - Output::new(self.finalize_fixed_reset()) - } - - /// Check if tag/code value is correct for the processed input. - fn verify(self, tag: &[u8]) -> Result<(), MacError> { - let choice = self.finalize().bytes.ct_eq(tag); - - if choice.unwrap_u8() == 1 { - Ok(()) - } else { - Err(MacError) - } - } -} - -impl Mac for T {} - -/// [`Output`] is a thin wrapper around bytes array which provides a safe `Eq` -/// implementation that runs in a fixed time. -#[derive(Clone)] -pub struct Output { - bytes: GenericArray, -} - -impl Output { - /// Create a new MAC [`Output`]. - pub fn new(bytes: GenericArray) -> Output { - Output { bytes } - } - - /// Get the MAC tag/code value as a byte array. - /// - /// Be very careful using this method, since incorrect use of the tag value - /// may permit timing attacks which defeat the security provided by the - /// [`Mac`] trait. - pub fn into_bytes(self) -> GenericArray { - self.bytes - } -} - -impl ConstantTimeEq for Output { - fn ct_eq(&self, other: &Self) -> Choice { - self.bytes.ct_eq(&other.bytes) - } -} - -impl PartialEq for Output { - fn eq(&self, x: &Output) -> bool { - self.ct_eq(x).unwrap_u8() == 1 - } -} - -impl Eq for Output {} - -/// Error type for signaling failed MAC verification -#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] -pub struct MacError; - -impl fmt::Display for MacError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("failed MAC verification") - } -} - -#[cfg(feature = "std")] -impl std::error::Error for MacError {} diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 9d9fe398f..3facaf69f 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -26,7 +26,7 @@ elliptic-curve = { version = "=0.11.0-pre", optional = true, path = "../elliptic mac = { version = "0.11", package = "crypto-mac", optional = true } password-hash = { version = "0.3", optional = true, path = "../password-hash" } signature = { version = "1.3.0", optional = true, default-features = false, path = "../signature" } -universal-hash = { version = "0.4", optional = true, path = "../universal-hash" } +universal-hash = { version = "0.4", optional = true } [features] std = [ diff --git a/digest/Cargo.toml b/digest/Cargo.toml index 61852b9d9..37a442d36 100644 --- a/digest/Cargo.toml +++ b/digest/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "digest" description = "Traits for cryptographic hash functions" -version = "0.10.0-pre.3" +version = "0.10.0" authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" readme = "README.md" @@ -13,16 +13,18 @@ categories = ["cryptography", "no-std"] [dependencies] generic-array = "0.14" -crypto-common = { version = "=0.1.0-pre", path = "../crypto-common" } +block-buffer = "0.10" +crypto-common = { version = "0.1", path = "../crypto-common", default-features = false } +subtle = { version = "=2.4", default-features = false, optional = true } blobby = { version = "0.3", optional = true } [features] +mac = ["subtle"] # Enable MAC traits alloc = [] std = ["alloc", "crypto-common/std"] dev = ["blobby"] -core-api = ["crypto-common/core-api"] -block-padding = ["crypto-common/block-padding"] +block-padding = ["block-buffer/block-padding"] [package.metadata.docs.rs] all-features = true diff --git a/digest/src/core_api.rs b/digest/src/core_api.rs index 63e573d33..efeee4919 100644 --- a/digest/src/core_api.rs +++ b/digest/src/core_api.rs @@ -1,43 +1,67 @@ -//! Low-level core API traits. +//! Low-level traits operating on blocks and wrappers around them. //! //! Usage of traits in this module in user code is discouraged. Instead use //! core algorithm wrapped by the wrapper types, which implement the //! higher-level traits. use crate::InvalidOutputSize; -use crate::{ExtendableOutput, Reset}; -use generic_array::{ArrayLength, GenericArray}; +use generic_array::ArrayLength; -pub use crypto_common::core_api::{AlgorithmName, CoreWrapper, FixedOutputCore, UpdateCore}; +pub use crypto_common::{AlgorithmName, Block, BlockSizeUser, OutputSizeUser, Reset}; + +use block_buffer::{BlockBuffer, BufferKind}; +use crypto_common::Output; mod ct_variable; mod rt_variable; +mod wrapper; mod xof_reader; pub use ct_variable::CtVariableCoreWrapper; pub use rt_variable::RtVariableCoreWrapper; +pub use wrapper::{CoreProxy, CoreWrapper}; pub use xof_reader::XofReaderCoreWrapper; +/// Buffer type used by type which implements [`BufferKindUser`]. +pub type Buffer = + BlockBuffer<::BlockSize, ::BufferKind>; + +/// Types which consume data in blocks. +pub trait UpdateCore: BlockSizeUser { + /// Update state using the provided data blocks. + fn update_blocks(&mut self, blocks: &[Block]); +} + +/// Types which use [`BlockBuffer`] functionality. +pub trait BufferKindUser: BlockSizeUser { + /// Block buffer kind over which type operates. + type BufferKind: BufferKind; +} + +/// Core trait for hash functions with fixed output size. +pub trait FixedOutputCore: UpdateCore + BufferKindUser + OutputSizeUser { + /// Finalize state using remaining data stored in the provided block buffer, + /// write result into provided array and leave `self` in a dirty state. + fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output); +} + /// Core trait for hash functions with extendable (XOF) output size. -pub trait ExtendableOutputCore: UpdateCore { +pub trait ExtendableOutputCore: UpdateCore + BufferKindUser { /// XOF reader core state. type ReaderCore: XofReaderCore; /// Retrieve XOF reader using remaining data stored in the block buffer /// and leave hasher in a dirty state. - fn finalize_xof_core(&mut self, buffer: &mut Self::Buffer) -> Self::ReaderCore; + fn finalize_xof_core(&mut self, buffer: &mut Buffer) -> Self::ReaderCore; } /// Core reader trait for extendable-output function (XOF) result. -pub trait XofReaderCore { - /// Block size in bytes. - type BlockSize: ArrayLength; - +pub trait XofReaderCore: BlockSizeUser { /// Read next XOF block. - fn read_block(&mut self) -> GenericArray; + fn read_block(&mut self) -> Block; } /// Core trait for hash functions with variable output size. -pub trait VariableOutputCore: UpdateCore + Sized { +pub trait VariableOutputCore: UpdateCore + BufferKindUser + Sized { /// Maximum output size. type MaxOutputSize: ArrayLength; @@ -52,29 +76,8 @@ pub trait VariableOutputCore: UpdateCore + Sized { /// `output_size` must be equal to `output_size` used during construction. fn finalize_variable_core( &mut self, - buffer: &mut Self::Buffer, + buffer: &mut Buffer, output_size: usize, f: impl FnOnce(&[u8]), ); } - -impl ExtendableOutput for CoreWrapper { - type Reader = XofReaderCoreWrapper; - - #[inline] - fn finalize_xof(self) -> Self::Reader { - let (mut core, mut buffer) = self.decompose(); - let core = core.finalize_xof_core(&mut buffer); - let buffer = Default::default(); - Self::Reader { core, buffer } - } - - #[inline] - fn finalize_xof_reset(&mut self) -> Self::Reader { - self.apply_reset(|core, buffer| { - let core = core.finalize_xof_core(buffer); - let buffer = Default::default(); - Self::Reader { core, buffer } - }) - } -} diff --git a/digest/src/core_api/ct_variable.rs b/digest/src/core_api/ct_variable.rs index 11474326f..a843ccd1f 100644 --- a/digest/src/core_api/ct_variable.rs +++ b/digest/src/core_api/ct_variable.rs @@ -1,5 +1,11 @@ -use super::{AlgorithmName, FixedOutputCore, Reset, UpdateCore, VariableOutputCore}; +use super::{ + AlgorithmName, Buffer, BufferKindUser, FixedOutputCore, Reset, UpdateCore, VariableOutputCore, +}; +use crate::HashMarker; +#[cfg(feature = "mac")] +use crate::MacMarker; use core::{fmt, marker::PhantomData}; +use crypto_common::{Block, BlockSizeUser, OutputSizeUser}; use generic_array::{ typenum::{IsLessOrEqual, LeEq, NonZero}, ArrayLength, GenericArray, @@ -18,33 +24,72 @@ where _out: PhantomData, } -impl UpdateCore for CtVariableCoreWrapper +impl HashMarker for CtVariableCoreWrapper +where + T: VariableOutputCore + HashMarker, + OutSize: ArrayLength + IsLessOrEqual, + LeEq: NonZero, +{ +} + +#[cfg(feature = "mac")] +impl MacMarker for CtVariableCoreWrapper +where + T: VariableOutputCore + MacMarker, + OutSize: ArrayLength + IsLessOrEqual, + LeEq: NonZero, +{ +} + +impl BlockSizeUser for CtVariableCoreWrapper where T: VariableOutputCore, OutSize: ArrayLength + IsLessOrEqual, LeEq: NonZero, { type BlockSize = T::BlockSize; - type Buffer = T::Buffer; +} +impl UpdateCore for CtVariableCoreWrapper +where + T: VariableOutputCore, + OutSize: ArrayLength + IsLessOrEqual, + LeEq: NonZero, +{ #[inline] - fn update_blocks(&mut self, blocks: &[GenericArray]) { + fn update_blocks(&mut self, blocks: &[Block]) { self.inner.update_blocks(blocks); } } -impl FixedOutputCore for CtVariableCoreWrapper +impl OutputSizeUser for CtVariableCoreWrapper where T: VariableOutputCore, - OutSize: ArrayLength + IsLessOrEqual, + OutSize: ArrayLength + IsLessOrEqual + 'static, LeEq: NonZero, { type OutputSize = OutSize; +} + +impl BufferKindUser for CtVariableCoreWrapper +where + T: VariableOutputCore, + OutSize: ArrayLength + IsLessOrEqual, + LeEq: NonZero, +{ + type BufferKind = T::BufferKind; +} +impl FixedOutputCore for CtVariableCoreWrapper +where + T: VariableOutputCore, + OutSize: ArrayLength + IsLessOrEqual + 'static, + LeEq: NonZero, +{ #[inline] fn finalize_fixed_core( &mut self, - buffer: &mut Self::Buffer, + buffer: &mut Buffer, out: &mut GenericArray, ) { self.inner @@ -62,7 +107,7 @@ where fn default() -> Self { Self { inner: T::new(OutSize::USIZE).unwrap(), - _out: Default::default(), + _out: PhantomData, } } } diff --git a/digest/src/core_api/rt_variable.rs b/digest/src/core_api/rt_variable.rs index c9af06ae3..c41487fdb 100644 --- a/digest/src/core_api/rt_variable.rs +++ b/digest/src/core_api/rt_variable.rs @@ -1,7 +1,10 @@ use super::{AlgorithmName, UpdateCore, VariableOutputCore}; +use crate::HashMarker; +#[cfg(feature = "mac")] +use crate::MacMarker; use crate::{InvalidOutputSize, Reset, Update, VariableOutput}; +use block_buffer::BlockBuffer; use core::fmt; -use crypto_common::block_buffer::DigestBuffer; use generic_array::typenum::Unsigned; /// Wrapper around [`VariableOutputCore`] which selects output size @@ -12,10 +15,15 @@ where T: VariableOutputCore + UpdateCore, { core: T, - buffer: T::Buffer, + buffer: BlockBuffer, output_size: usize, } +impl HashMarker for RtVariableCoreWrapper where T: VariableOutputCore + HashMarker {} + +#[cfg(feature = "mac")] +impl MacMarker for RtVariableCoreWrapper where T: VariableOutputCore + MacMarker {} + impl Reset for RtVariableCoreWrapper where T: VariableOutputCore + UpdateCore, diff --git a/digest/src/core_api/wrapper.rs b/digest/src/core_api/wrapper.rs new file mode 100644 index 000000000..ef4862540 --- /dev/null +++ b/digest/src/core_api/wrapper.rs @@ -0,0 +1,159 @@ +use super::{ + AlgorithmName, Buffer, BufferKindUser, ExtendableOutputCore, FixedOutputCore, OutputSizeUser, + Reset, UpdateCore, XofReaderCoreWrapper, +}; +use crate::{ExtendableOutput, FixedOutput, FixedOutputReset, HashMarker, Update}; +use block_buffer::BlockBuffer; +use core::fmt; +use crypto_common::{InvalidLength, Key, KeyInit, KeySizeUser, Output}; + +#[cfg(feature = "mac")] +use crate::MacMarker; + +/// Wrapper around [`BufferKindUser`]. +/// +/// It handles data buffering and implements the slice-based traits. +#[derive(Clone, Default)] +pub struct CoreWrapper { + core: T, + buffer: BlockBuffer, +} + +impl HashMarker for CoreWrapper {} + +#[cfg(feature = "mac")] +impl MacMarker for CoreWrapper {} + +impl CoreWrapper { + /// Create new wrapper from `core`. + #[inline] + pub fn from_core(core: T) -> Self { + let buffer = Default::default(); + Self { core, buffer } + } + + /// Decompose wrapper into inner parts. + #[inline] + pub fn decompose(self) -> (T, Buffer) { + let Self { core, buffer } = self; + (core, buffer) + } +} + +impl KeySizeUser for CoreWrapper { + type KeySize = T::KeySize; +} + +impl KeyInit for CoreWrapper { + fn new(key: &Key) -> Self { + Self { + core: T::new(key), + buffer: Default::default(), + } + } + + fn new_from_slice(key: &[u8]) -> Result { + Ok(Self { + core: T::new_from_slice(key)?, + buffer: Default::default(), + }) + } +} + +impl fmt::Debug for CoreWrapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + T::write_alg_name(f)?; + f.write_str(" { .. }") + } +} + +impl Reset for CoreWrapper { + #[inline] + fn reset(&mut self) { + self.core.reset(); + self.buffer.reset(); + } +} + +impl Update for CoreWrapper { + #[inline] + fn update(&mut self, input: &[u8]) { + let Self { core, buffer } = self; + buffer.digest_blocks(input, |blocks| core.update_blocks(blocks)); + } +} + +impl OutputSizeUser for CoreWrapper { + type OutputSize = D::OutputSize; +} + +impl FixedOutput for CoreWrapper { + #[inline] + fn finalize_into(mut self, out: &mut Output) { + let Self { core, buffer } = &mut self; + core.finalize_fixed_core(buffer, out); + } +} + +impl FixedOutputReset for CoreWrapper { + #[inline] + fn finalize_into_reset(&mut self, out: &mut Output) { + let Self { core, buffer } = self; + core.finalize_fixed_core(buffer, out); + core.reset(); + buffer.reset(); + } +} + +impl ExtendableOutput for CoreWrapper { + type Reader = XofReaderCoreWrapper; + + #[inline] + fn finalize_xof(self) -> Self::Reader { + let (mut core, mut buffer) = self.decompose(); + let core = core.finalize_xof_core(&mut buffer); + let buffer = Default::default(); + Self::Reader { core, buffer } + } + + #[inline] + fn finalize_xof_reset(&mut self) -> Self::Reader { + let Self { core, buffer } = self; + let core = core.finalize_xof_core(buffer); + let buffer = Default::default(); + Self::Reader { core, buffer } + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl std::io::Write for CoreWrapper { + #[inline] + fn write(&mut self, buf: &[u8]) -> std::io::Result { + Update::update(self, buf); + Ok(buf.len()) + } + + #[inline] + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + +/// A proxy trait to a core type implemented by [`CoreWrapper`] +// TODO: replace with an inherent associated type on stabilization: +// https://github.com/rust-lang/rust/issues/8995 +pub trait CoreProxy: sealed::Sealed { + /// Type wrapped by [`CoreWrapper`]. + type Core; +} + +mod sealed { + pub trait Sealed {} +} + +impl sealed::Sealed for CoreWrapper {} + +impl CoreProxy for CoreWrapper { + type Core = T; +} diff --git a/digest/src/core_api/xof_reader.rs b/digest/src/core_api/xof_reader.rs index 5d7b83520..b9daf9fa5 100644 --- a/digest/src/core_api/xof_reader.rs +++ b/digest/src/core_api/xof_reader.rs @@ -1,7 +1,7 @@ use super::{AlgorithmName, XofReaderCore}; use crate::XofReader; +use block_buffer::EagerBuffer; use core::fmt; -use crypto_common::block_buffer::BlockBuffer; /// Wrapper around [`XofReaderCore`] implementations. /// @@ -9,7 +9,7 @@ use crypto_common::block_buffer::BlockBuffer; #[derive(Clone, Default)] pub struct XofReaderCoreWrapper { pub(super) core: T, - pub(super) buffer: BlockBuffer, + pub(super) buffer: EagerBuffer, } impl fmt::Debug for XofReaderCoreWrapper { @@ -23,7 +23,11 @@ impl XofReader for XofReaderCoreWrapper { #[inline] fn read(&mut self, buffer: &mut [u8]) { let Self { core, buffer: buf } = self; - buf.set_data(buffer, || core.read_block()); + buf.set_data(buffer, |blocks| { + for block in blocks { + *block = core.read_block(); + } + }); } } diff --git a/digest/src/dev.rs b/digest/src/dev.rs index 926ebbd53..c1200d748 100644 --- a/digest/src/dev.rs +++ b/digest/src/dev.rs @@ -9,7 +9,7 @@ use core::fmt::Debug; #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "dev")))] macro_rules! new_test { - ($name:ident, $test_name:expr, $hasher:ty, $test_func:ident) => { + ($name:ident, $test_name:expr, $hasher:ty, $test_func:ident $(,)?) => { #[test] fn $name() { use digest::dev::blobby::Blob2Iterator; @@ -244,3 +244,163 @@ macro_rules! bench { $crate::bench!(bench4_10000, $engine, 10000); }; } + +/// Define MAC test +#[macro_export] +#[cfg(feature = "mac")] +#[cfg_attr(docsrs, doc(cfg(all(feature = "dev", feature = "mac"))))] +macro_rules! new_mac_test { + ($name:ident, $test_name:expr, $mac:ty $(,)?) => { + digest::new_mac_test!($name, $test_name, $mac, ""); + }; + ($name:ident, $test_name:expr, $mac:ty, trunc_left $(,)?) => { + digest::new_mac_test!($name, $test_name, $mac, "left"); + }; + ($name:ident, $test_name:expr, $mac:ty, trunc_right $(,)?) => { + digest::new_mac_test!($name, $test_name, $mac, "right"); + }; + ($name:ident, $test_name:expr, $mac:ty, $trunc:expr $(,)?) => { + #[test] + fn $name() { + use core::cmp::min; + use digest::dev::blobby::Blob3Iterator; + use digest::Mac; + + fn run_test(key: &[u8], input: &[u8], tag: &[u8]) -> Option<&'static str> { + let mac0 = <$mac as Mac>::new_from_slice(key).unwrap(); + + let mut mac = mac0.clone(); + mac.update(input); + let result = mac.finalize().into_bytes(); + let n = tag.len(); + let result_bytes = match $trunc { + "left" => &result[..n], + "right" => &result[result.len() - n..], + _ => &result[..], + }; + if result_bytes != tag { + return Some("whole message"); + } + + // test reading different chunk sizes + for chunk_size in 1..min(64, input.len()) { + let mut mac = mac0.clone(); + for chunk in input.chunks(chunk_size) { + mac.update(chunk); + } + let res = match $trunc { + "left" => mac.verify_truncated_left(tag), + "right" => mac.verify_truncated_right(tag), + _ => mac.verify_slice(tag), + }; + if res.is_err() { + return Some("chunked message"); + } + } + + None + } + + let data = include_bytes!(concat!("data/", $test_name, ".blb")); + + for (i, row) in Blob3Iterator::new(data).unwrap().enumerate() { + let [key, input, tag] = row.unwrap(); + if let Some(desc) = run_test(key, input, tag) { + panic!( + "\n\ + Failed test №{}: {}\n\ + key:\t{:?}\n\ + input:\t{:?}\n\ + tag:\t{:?}\n", + i, desc, key, input, tag, + ); + } + } + } + }; +} + +/// Define new test for a resettable MAC +#[macro_export] +#[cfg(feature = "mac")] +#[cfg_attr(docsrs, doc(cfg(all(feature = "dev", feature = "mac"))))] +macro_rules! new_resettable_mac_test { + ($name:ident, $test_name:expr, $mac:ty $(,)?) => { + digest::new_resettable_mac_test!($name, $test_name, $mac, ""); + }; + ($name:ident, $test_name:expr, $mac:ty, trunc_left $(,)?) => { + digest::new_resettable_mac_test!($name, $test_name, $mac, "left"); + }; + ($name:ident, $test_name:expr, $mac:ty, trunc_right $(,)?) => { + digest::new_resettable_mac_test!($name, $test_name, $mac, "right"); + }; + ($name:ident, $test_name:expr, $mac:ty, $trunc:expr $(,)?) => { + #[test] + fn $name() { + use core::cmp::min; + use digest::dev::blobby::Blob3Iterator; + use digest::Mac; + + fn run_test(key: &[u8], input: &[u8], tag: &[u8]) -> Option<&'static str> { + let mac0 = <$mac as Mac>::new_from_slice(key).unwrap(); + + let mut mac = mac0.clone(); + mac.update(input); + let result = mac.finalize_reset().into_bytes(); + let n = tag.len(); + let result_bytes = match $trunc { + "left" => &result[..n], + "right" => &result[result.len() - n..], + _ => &result[..], + }; + if result_bytes != tag { + return Some("whole message"); + } + + // test if reset worked correctly + mac.update(input); + let res = match $trunc { + "left" => mac.verify_truncated_left(tag), + "right" => mac.verify_truncated_right(tag), + _ => mac.verify_slice(tag), + }; + if res.is_err() { + return Some("after reset"); + } + + // test reading different chunk sizes + for chunk_size in 1..min(64, input.len()) { + let mut mac = mac0.clone(); + for chunk in input.chunks(chunk_size) { + mac.update(chunk); + } + let res = match $trunc { + "left" => mac.verify_truncated_left(tag), + "right" => mac.verify_truncated_right(tag), + _ => mac.verify_slice(tag), + }; + if res.is_err() { + return Some("chunked message"); + } + } + None + } + + let data = include_bytes!(concat!("data/", $test_name, ".blb")); + + for (i, row) in Blob3Iterator::new(data).unwrap().enumerate() { + let [key, input, tag] = row.unwrap(); + if let Some(desc) = run_test(key, input, tag) { + panic!( + "\n\ + Failed test №{}: {}\n\ + key:\t{:?}\n\ + input:\t{:?}\n\ + tag:\t{:?}\n", + i, desc, key, input, tag, + ); + } + } + } + }; +} diff --git a/digest/src/digest.rs b/digest/src/digest.rs index 7c277c9a6..251d4aa85 100644 --- a/digest/src/digest.rs +++ b/digest/src/digest.rs @@ -1,17 +1,20 @@ -use super::{FixedOutput, FixedOutputReset, Update}; +use super::{FixedOutput, FixedOutputReset, Reset, Update}; +use core::fmt; +use crypto_common::{Output, OutputSizeUser}; use generic_array::typenum::Unsigned; -use generic_array::{ArrayLength, GenericArray}; -/// The `Digest` trait specifies an interface common for digest functions. -/// -/// It's a convenience wrapper around [`Update`], [`FixedOutput`], -/// [`Reset`][`crate::Reset`], [`Clone`], and [`Default`] traits. -/// -/// It also provides additional convenience methods. -pub trait Digest { - /// Output size for `Digest` - type OutputSize: ArrayLength; +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + +/// Marker trait for cryptographic hash functions. +pub trait HashMarker {} +/// Convinience wrapper trait covering functionality of cryptographic hash +/// functions with fixed output size. +/// +/// This trait wraps [`Update`], [`FixedOutput`], [`Default`], and +/// [`HashMarker`] traits and provides additional convenience methods. +pub trait Digest: OutputSizeUser { /// Create new hasher instance fn new() -> Self; @@ -44,9 +47,7 @@ pub trait Digest { fn digest(data: impl AsRef<[u8]>) -> Output; } -impl Digest for D { - type OutputSize = ::OutputSize; - +impl Digest for D { #[inline] fn new() -> Self { Self::default() @@ -102,5 +103,123 @@ impl Digest for D { } } -/// Fixed of fixed-sized hash-function used by [`Digest`] methods. -pub type Output = GenericArray::OutputSize>; +/// Modification of the [`Digest`] trait suitable for trait objects. +pub trait DynDigest { + /// Digest input data. + /// + /// This method can be called repeatedly for use with streaming messages. + fn update(&mut self, data: &[u8]); + + /// Retrieve result and reset hasher instance + #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + fn finalize_reset(&mut self) -> Box<[u8]> { + let mut result = vec![0; self.output_size()]; + self.finalize_into_reset(&mut result).unwrap(); + result.into_boxed_slice() + } + + /// Retrieve result and consume boxed hasher instance + #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + #[allow(clippy::boxed_local)] + fn finalize(mut self: Box) -> Box<[u8]> { + let mut result = vec![0; self.output_size()]; + self.finalize_into_reset(&mut result).unwrap(); + result.into_boxed_slice() + } + + /// Write result into provided array and consume the hasher instance. + /// + /// Returns error if buffer length is not equal to `output_size`. + fn finalize_into(self, buf: &mut [u8]) -> Result<(), InvalidBufferLength>; + + /// Write result into provided array and reset the hasher instance. + /// + /// Returns error if buffer length is not equal to `output_size`. + fn finalize_into_reset(&mut self, out: &mut [u8]) -> Result<(), InvalidBufferLength>; + + /// Reset hasher instance to its initial state. + fn reset(&mut self); + + /// Get output size of the hasher + fn output_size(&self) -> usize; + + /// Clone hasher state into a boxed trait object + #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + fn box_clone(&self) -> Box; +} + +impl DynDigest for D { + fn update(&mut self, data: &[u8]) { + Update::update(self, data); + } + + #[cfg(feature = "alloc")] + fn finalize_reset(&mut self) -> Box<[u8]> { + FixedOutputReset::finalize_fixed_reset(self) + .to_vec() + .into_boxed_slice() + } + + #[cfg(feature = "alloc")] + fn finalize(self: Box) -> Box<[u8]> { + FixedOutput::finalize_fixed(*self) + .to_vec() + .into_boxed_slice() + } + + fn finalize_into(self, buf: &mut [u8]) -> Result<(), InvalidBufferLength> { + if buf.len() == self.output_size() { + FixedOutput::finalize_into(self, Output::::from_mut_slice(buf)); + Ok(()) + } else { + Err(InvalidBufferLength) + } + } + + fn finalize_into_reset(&mut self, buf: &mut [u8]) -> Result<(), InvalidBufferLength> { + if buf.len() == self.output_size() { + FixedOutputReset::finalize_into_reset(self, Output::::from_mut_slice(buf)); + Ok(()) + } else { + Err(InvalidBufferLength) + } + } + + fn reset(&mut self) { + Reset::reset(self); + } + + fn output_size(&self) -> usize { + ::OutputSize::to_usize() + } + + #[cfg(feature = "alloc")] + fn box_clone(&self) -> Box { + Box::new(self.clone()) + } +} + +#[cfg(feature = "alloc")] +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +impl Clone for Box { + fn clone(&self) -> Self { + self.box_clone() + } +} + +/// Buffer length is not equal to the hash output size. +#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] +#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] +pub struct InvalidBufferLength; + +impl fmt::Display for InvalidBufferLength { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("invalid buffer length") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for InvalidBufferLength {} diff --git a/digest/src/dyn_digest.rs b/digest/src/dyn_digest.rs index ecafe7945..0e1d3fab3 100644 --- a/digest/src/dyn_digest.rs +++ b/digest/src/dyn_digest.rs @@ -1,5 +1,6 @@ -use super::{FixedOutput, FixedOutputReset, Reset, Update}; +use super::{FixedOutputReset, Reset, Update}; use core::fmt; +use crypto_common::OutputSizeUser; use generic_array::{typenum::Unsigned, GenericArray}; #[cfg(feature = "alloc")] @@ -92,7 +93,7 @@ impl DynDigest for D { } fn output_size(&self) -> usize { - ::OutputSize::to_usize() + ::OutputSize::to_usize() } #[cfg(feature = "alloc")] diff --git a/digest/src/lib.rs b/digest/src/lib.rs index 2991ded68..aebad936d 100644 --- a/digest/src/lib.rs +++ b/digest/src/lib.rs @@ -1,15 +1,15 @@ //! This crate provides traits which describe functionality of cryptographic hash -//! functions. +//! functions and Message Authentication algorithms. //! -//! Traits in this repository are organized into high-level convenience traits, -//! mid-level traits which expose more fine-grained functionality, and -//! low-level traits intended to only be used by algorithm implementations: +//! Traits in this repository are organized into the following levels: //! -//! - **High-level convenience traits**: [`Digest`], [`DynDigest`]. They are wrappers -//! around lower-level traits for most common hash-function use-cases. -//! - **Mid-level traits**: [`Update`], [`FixedOutput`], [`ExtendableOutput`], [`Reset`]. -//! These traits atomically describe available functionality of hash function -//! implementations. +//! - **High-level convenience traits**: [`Digest`], [`DynDigest`], [`Mac`]. +//! Wrappers around lower-level traits for most common use-cases. +//! - **Mid-level traits**: [`Update`], [`FixedOutput`], [`ExtendableOutput`], +//! [`VariableOutput`], [`Reset`], [`XofReader`]. These traits atomically +//! describe available functionality of an algorithm. +//! - **Marker traits**: [`HashMarker`], [`MacMarker`]. Used to distinguish +//! different algorithm classes. //! - **Low-level traits** defined in the [`core_api`] module. These traits //! operate at a block-level and do not contain any built-in buffering. //! They are intended to be implemented by low-level algorithm providers only @@ -17,11 +17,11 @@ //! usually shouldn't be used in application-level code. //! //! Additionally hash functions implement traits from the standard library: -//! [`Default`], [`Clone`], [`Write`][std::io::Write]. The latter is +//! [`Default`], [`Clone`], [`Write`]. The latter is //! feature-gated behind `std` feature, which is usually enabled by default //! by hash implementation crates. //! -//! The [`Digest`] trait is the most commonly used trait. +//! [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html #![no_std] #![cfg_attr(docsrs, feature(doc_cfg))] @@ -46,21 +46,58 @@ use alloc::boxed::Box; #[cfg_attr(docsrs, doc(cfg(feature = "dev")))] pub mod dev; -#[cfg(feature = "core-api")] -#[cfg_attr(docsrs, doc(cfg(feature = "core-api")))] pub mod core_api; mod digest; -mod dyn_digest; +#[cfg(feature = "mac")] +mod mac; + +pub use block_buffer; +pub use crypto_common; + +pub use crate::digest::{Digest, DynDigest, HashMarker, InvalidBufferLength}; +#[cfg(feature = "mac")] +pub use crypto_common::{InnerInit, InvalidLength, Key, KeyInit}; +pub use crypto_common::{Output, OutputSizeUser, Reset}; +pub use generic_array::{self, typenum::consts}; +#[cfg(feature = "mac")] +pub use mac::{CtOutput, Mac, MacError, MacMarker}; -pub use crate::digest::{Digest, Output}; use core::fmt; -#[cfg(feature = "core-api")] -#[cfg_attr(docsrs, doc(cfg(feature = "core-api")))] -pub use crypto_common::block_buffer; -pub use dyn_digest::{DynDigest, InvalidBufferLength}; -pub use generic_array::{self, typenum::consts, GenericArray}; -pub use crypto_common::{FixedOutput, FixedOutputReset, Reset, Update}; +/// Types which consume data with byte granularity. +pub trait Update { + /// Update state using the provided data. + fn update(&mut self, data: &[u8]); +} + +/// Types which return fixed-sized result after finalization. +pub trait FixedOutput: OutputSizeUser + Sized { + /// Consume value and write result into provided array. + fn finalize_into(self, out: &mut Output); + + /// Retrieve result and consume the hasher instance. + #[inline] + fn finalize_fixed(self) -> Output { + let mut out = Default::default(); + self.finalize_into(&mut out); + out + } +} + +/// Types which return fixed-sized result after finalization and reset +/// values into its initial state. +pub trait FixedOutputReset: FixedOutput + Reset { + /// Write result into provided array and reset value to its initial state. + fn finalize_into_reset(&mut self, out: &mut Output); + + /// Retrieve result and reset the hasher instance. + #[inline] + fn finalize_fixed_reset(&mut self) -> Output { + let mut out = Default::default(); + self.finalize_into_reset(&mut out); + out + } +} /// Trait for describing readers which are used to extract extendable output /// from XOF (extendable-output function) result. @@ -84,7 +121,7 @@ pub trait XofReader { } /// Trait which describes extendable-output functions (XOF). -pub trait ExtendableOutput: Sized + Update + Default + Reset { +pub trait ExtendableOutput: Sized + Update + Reset { /// Reader type Reader: XofReader; @@ -95,7 +132,10 @@ pub trait ExtendableOutput: Sized + Update + Default + Reset { fn finalize_xof_reset(&mut self) -> Self::Reader; /// Compute hash of `data` and write it to `output`. - fn digest_xof(input: impl AsRef<[u8]>, output: &mut [u8]) { + fn digest_xof(input: impl AsRef<[u8]>, output: &mut [u8]) + where + Self: Default, + { let mut hasher = Self::default(); hasher.update(input.as_ref()); hasher.finalize_xof().read(output); diff --git a/digest/src/mac.rs b/digest/src/mac.rs new file mode 100644 index 000000000..df1d0c2b1 --- /dev/null +++ b/digest/src/mac.rs @@ -0,0 +1,208 @@ +use crate::{FixedOutput, FixedOutputReset, Update}; +use crypto_common::{InvalidLength, Key, KeyInit, KeySizeUser, Output, OutputSizeUser}; + +use core::fmt; +use generic_array::typenum::Unsigned; +use subtle::{Choice, ConstantTimeEq}; + +/// Marker trait for Message Authentication algorithms. +#[cfg_attr(docsrs, doc(cfg(feature = "mac")))] +pub trait MacMarker {} + +/// Convinience wrapper trait covering functionality of Message Authentication algorithms. +/// +/// This trait wraps [`KeyInit`], [`Update`], [`FixedOutput`], and [`MacMarker`] +/// traits and provides additional convenience methods. +#[cfg_attr(docsrs, doc(cfg(feature = "mac")))] +pub trait Mac: KeySizeUser + OutputSizeUser + Sized { + /// Create new value from fixed size key. + fn new(key: &Key) -> Self; + + /// Create new value from variable size key. + fn new_from_slice(key: &[u8]) -> Result; + + /// Update state using the provided data. + fn update(&mut self, data: &[u8]); + + /// Obtain the result of a [`Mac`] computation as a [`CtOutput`] and consume + /// [`Mac`] instance. + fn finalize(self) -> CtOutput; + + /// Obtain the result of a [`Mac`] computation as a [`CtOutput`] and reset + /// [`Mac`] instance. + fn finalize_reset(&mut self) -> CtOutput + where + Self: FixedOutputReset; + + /// Check if tag/code value is correct for the processed input. + fn verify(self, tag: &Output) -> Result<(), MacError>; + + /// Check truncated tag correctness using all bytes + /// of calculated tag. + /// + /// Returns `Error` if `tag` is not valid or not equal in length + /// to MAC's output. + fn verify_slice(self, tag: &[u8]) -> Result<(), MacError>; + + /// Check truncated tag correctness using left side bytes + /// (i.e. `tag[..n]`) of calculated tag. + /// + /// Returns `Error` if `tag` is not valid or empty. + fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError>; + + /// Check truncated tag correctness using right side bytes + /// (i.e. `tag[n..]`) of calculated tag. + /// + /// Returns `Error` if `tag` is not valid or empty. + fn verify_truncated_right(self, tag: &[u8]) -> Result<(), MacError>; +} + +impl Mac for T { + #[inline(always)] + fn new(key: &Key) -> Self { + KeyInit::new(key) + } + + #[inline(always)] + fn new_from_slice(key: &[u8]) -> Result { + KeyInit::new_from_slice(key) + } + + #[inline] + fn update(&mut self, data: &[u8]) { + Update::update(self, data); + } + + #[inline] + fn finalize(self) -> CtOutput { + CtOutput::new(self.finalize_fixed()) + } + + #[inline(always)] + fn finalize_reset(&mut self) -> CtOutput + where + Self: FixedOutputReset, + { + CtOutput::new(self.finalize_fixed_reset()) + } + + #[inline] + fn verify(self, tag: &Output) -> Result<(), MacError> { + if self.finalize() == tag.into() { + Ok(()) + } else { + Err(MacError) + } + } + + #[inline] + fn verify_slice(self, tag: &[u8]) -> Result<(), MacError> { + let n = tag.len(); + if n != Self::OutputSize::USIZE { + return Err(MacError); + } + let choice = self.finalize_fixed().ct_eq(&tag); + if choice.unwrap_u8() == 1 { + Ok(()) + } else { + Err(MacError) + } + } + + fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError> { + let n = tag.len(); + if n == 0 || n > Self::OutputSize::USIZE { + return Err(MacError); + } + let choice = self.finalize_fixed()[..n].ct_eq(tag); + + if choice.unwrap_u8() == 1 { + Ok(()) + } else { + Err(MacError) + } + } + + fn verify_truncated_right(self, tag: &[u8]) -> Result<(), MacError> { + let n = tag.len(); + if n == 0 || n > Self::OutputSize::USIZE { + return Err(MacError); + } + let m = Self::OutputSize::USIZE - n; + let choice = self.finalize_fixed()[m..].ct_eq(tag); + + if choice.unwrap_u8() == 1 { + Ok(()) + } else { + Err(MacError) + } + } +} + +/// Fixed size output value which provides a safe [`Eq`] implementation that +/// runs in constant time. +/// +/// It is useful for implementing Message Authentication Codes (MACs). +#[derive(Clone)] +#[cfg_attr(docsrs, doc(cfg(feature = "subtle")))] +pub struct CtOutput { + bytes: Output, +} + +impl CtOutput { + /// Create a new [`CtOutput`] value. + #[inline(always)] + pub fn new(bytes: Output) -> Self { + Self { bytes } + } + + /// Get the inner [`Output`] array this type wraps. + #[inline(always)] + pub fn into_bytes(self) -> Output { + self.bytes + } +} + +impl From> for CtOutput { + #[inline(always)] + fn from(bytes: Output) -> Self { + Self { bytes } + } +} + +impl<'a, T: OutputSizeUser> From<&'a Output> for CtOutput { + #[inline(always)] + fn from(bytes: &'a Output) -> Self { + bytes.clone().into() + } +} + +impl ConstantTimeEq for CtOutput { + #[inline(always)] + fn ct_eq(&self, other: &Self) -> Choice { + self.bytes.ct_eq(&other.bytes) + } +} + +impl PartialEq for CtOutput { + #[inline(always)] + fn eq(&self, x: &CtOutput) -> bool { + self.ct_eq(x).unwrap_u8() == 1 + } +} + +impl Eq for CtOutput {} + +/// Error type for when the [`Output`] of a [`Mac`] +/// is not equal to the expected value. +#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] +pub struct MacError; + +impl fmt::Display for MacError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("MAC tag mismatch") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for MacError {} diff --git a/universal-hash/Cargo.toml b/universal-hash/Cargo.toml index 2c15823ef..7e4b65718 100644 --- a/universal-hash/Cargo.toml +++ b/universal-hash/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "universal-hash" -version = "0.4.1" # Also update html_root_url in lib.rs when bumping this +version = "0.5.0" # Also update html_root_url in lib.rs when bumping this authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" description = "Trait for universal hash functions" @@ -13,7 +13,7 @@ edition = "2018" [dependencies] generic-array = "0.14" -subtle = { version = "=2.4", default-features = false } +crypto-common = { version = "0.1", features = ["subtle"], path = "../crypto-common" } [features] std = [] diff --git a/universal-hash/src/lib.rs b/universal-hash/src/lib.rs index dc802c815..ed558ac8b 100644 --- a/universal-hash/src/lib.rs +++ b/universal-hash/src/lib.rs @@ -18,11 +18,10 @@ //! [Universal Hash Functions]: https://en.wikipedia.org/wiki/Universal_hashing #![no_std] -#![forbid(unsafe_code)] #![doc( html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg", - html_root_url = "https://docs.rs/universal-hash/0.4.1" + html_root_url = "https://docs.rs/universal-hash/0.5.0" )] #![warn(missing_docs, rust_2018_idioms)] @@ -31,34 +30,17 @@ extern crate std; pub use generic_array::{self, typenum::consts}; -use generic_array::typenum::Unsigned; -use generic_array::{ArrayLength, GenericArray}; -use subtle::{Choice, ConstantTimeEq}; +use core::slice; +use generic_array::{typenum::Unsigned, GenericArray}; -/// Keys to a [`UniversalHash`]. -pub type Key = GenericArray::KeySize>; - -/// Blocks are inputs to a [`UniversalHash`]. -pub type Block = GenericArray::BlockSize>; - -/// Instantiate a [`UniversalHash`] algorithm. -pub trait NewUniversalHash: Sized { - /// Size of the key for the universal hash function. - type KeySize: ArrayLength; - - /// Instantiate a universal hash function with the given key. - fn new(key: &Key) -> Self; -} +pub use crypto_common::{ + Block, BlockSizeUser, CtOutput, FixedOutput, FixedOutputReset, Key, KeyInit, KeySizeUser, + Output, Reset, UpdateCore, +}; /// The [`UniversalHash`] trait defines a generic interface for universal hash /// functions. -pub trait UniversalHash: Clone { - /// Size of the inputs to and outputs from the universal hash function - type BlockSize: ArrayLength; - - /// Input a block into the universal hash function - fn update(&mut self, block: &Block); - +pub trait UniversalHash: KeyInit + UpdateCore + FixedOutput { /// Input data into the universal hash function. If the length of the /// data is not a multiple of the block size, the remaining data is /// padded with zeroes up to the `BlockSize`. @@ -66,39 +48,45 @@ pub trait UniversalHash: Clone { /// This approach is frequently used by AEAD modes which use /// Message Authentication Codes (MACs) based on universal hashing. fn update_padded(&mut self, data: &[u8]) { - let mut chunks = data.chunks_exact(Self::BlockSize::to_usize()); - - for chunk in &mut chunks { - self.update(GenericArray::from_slice(chunk)); - } - - let rem = chunks.remainder(); - - if !rem.is_empty() { + // TODO: replace with `array_chunks` on migration to const generics + let (blocks, tail) = unsafe { + let blocks_len = data.len() / Self::BlockSize::USIZE; + let tail_start = Self::BlockSize::USIZE * blocks_len; + let tail_len = data.len() - tail_start; + ( + slice::from_raw_parts(data.as_ptr() as *const Block, blocks_len), + slice::from_raw_parts(data.as_ptr().add(tail_start), tail_len), + ) + }; + + self.update_blocks(blocks); + + if !tail.is_empty() { let mut padded_block = GenericArray::default(); - padded_block[..rem.len()].copy_from_slice(rem); - self.update(&padded_block); + padded_block[..tail.len()].copy_from_slice(tail); + self.update_blocks(slice::from_ref(&padded_block)); } } - /// Reset [`UniversalHash`] instance. - fn reset(&mut self); - /// Obtain the [`Output`] of a [`UniversalHash`] function and consume it. - fn finalize(self) -> Output; + fn finalize(self) -> CtOutput { + CtOutput::new(self.finalize_fixed()) + } /// Obtain the [`Output`] of a [`UniversalHash`] computation and reset it back /// to its initial state. - fn finalize_reset(&mut self) -> Output { - let res = self.clone().finalize(); - self.reset(); - res + #[inline] + fn finalize_reset(&mut self) -> CtOutput + where + Self: FixedOutputReset, + { + CtOutput::new(self.finalize_fixed_reset()) } /// Verify the [`UniversalHash`] of the processed input matches a given [`Output`]. /// This is useful when constructing Message Authentication Codes (MACs) /// from universal hash functions. - fn verify(self, other: &Block) -> Result<(), Error> { + fn verify(self, other: &Output) -> Result<(), Error> { if self.finalize() == other.into() { Ok(()) } else { @@ -107,68 +95,6 @@ pub trait UniversalHash: Clone { } } -/// Outputs of universal hash functions which are a thin wrapper around a -/// byte array. Provides a safe [`Eq`] implementation that runs in constant time, -/// which is useful for implementing Message Authentication Codes (MACs) based -/// on universal hashing. -#[derive(Clone)] -pub struct Output { - bytes: GenericArray, -} - -impl Output -where - U: UniversalHash, -{ - /// Create a new [`Output`] block. - pub fn new(bytes: Block) -> Output { - Output { bytes } - } - - /// Get the inner [`GenericArray`] this type wraps - pub fn into_bytes(self) -> Block { - self.bytes - } -} - -impl From> for Output -where - U: UniversalHash, -{ - fn from(bytes: Block) -> Self { - Output { bytes } - } -} - -impl<'a, U> From<&'a Block> for Output -where - U: UniversalHash, -{ - fn from(bytes: &'a Block) -> Self { - bytes.clone().into() - } -} - -impl ConstantTimeEq for Output -where - U: UniversalHash, -{ - fn ct_eq(&self, other: &Self) -> Choice { - self.bytes.ct_eq(&other.bytes) - } -} - -impl PartialEq for Output -where - U: UniversalHash, -{ - fn eq(&self, x: &Output) -> bool { - self.ct_eq(x).unwrap_u8() == 1 - } -} - -impl Eq for Output {} - /// Error type for when the [`Output`] of a [`UniversalHash`] /// is not equal to the expected value. #[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]