diff --git a/.github/workflows/cipher.yml b/.github/workflows/cipher.yml index 28ae4e6ec..321e5e6d1 100644 --- a/.github/workflows/cipher.yml +++ b/.github/workflows/cipher.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: rust: - - 1.56.0 # MSRV + - 1.65.0 # MSRV - stable target: - thumbv7em-none-eabi @@ -57,7 +57,7 @@ jobs: strategy: matrix: rust: - - 1.56.0 # MSRV + - 1.65.0 # MSRV - stable steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/crypto-common.yml b/.github/workflows/crypto-common.yml index e761daa9b..fca4878f0 100644 --- a/.github/workflows/crypto-common.yml +++ b/.github/workflows/crypto-common.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: rust: - - 1.56.0 # MSRV + - 1.65.0 # MSRV - stable target: - thumbv7em-none-eabi @@ -46,7 +46,7 @@ jobs: strategy: matrix: rust: - - 1.56.0 # MSRV + - 1.65.0 # MSRV - stable steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/digest.yml b/.github/workflows/digest.yml index 2a91753d9..4e42a7d4c 100644 --- a/.github/workflows/digest.yml +++ b/.github/workflows/digest.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: rust: - - 1.57.0 # MSRV + - 1.65.0 # MSRV - stable target: - thumbv7em-none-eabi @@ -34,21 +34,20 @@ jobs: with: toolchain: ${{ matrix.rust }} targets: ${{ matrix.target }} - # Isolate this crate from workspace which is otherwise MSRV 1.56 due to 2021 edition crates - - run: rm ../Cargo.toml - run: cargo build --target ${{ matrix.target }} - minimal-versions: - uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master - with: - working-directory: ${{ github.workflow }} + # TODO(tarcieri): re-enable after next `crypto-common` release + #minimal-versions: + # uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master + # with: + # working-directory: ${{ github.workflow }} test: runs-on: ubuntu-latest strategy: matrix: rust: - - 1.57.0 # MSRV + - 1.65.0 # MSRV - stable steps: - uses: actions/checkout@v3 diff --git a/Cargo.lock b/Cargo.lock index c75684544..51a2501f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,7 +31,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if", - "cipher 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cipher 0.4.4", "cpufeatures", ] @@ -43,7 +43,7 @@ checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" dependencies = [ "aead 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "aes", - "cipher 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cipher 0.4.4", "ctr", "ghash", "subtle", @@ -137,20 +137,17 @@ dependencies = [ [[package]] name = "block-buffer" version = "0.11.0-pre" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b6a33d658e9ef24ba0c4566d3d023d7978ca2ea3efb65e4eacd4586695892b" +source = "git+https://github.com/RustCrypto/utils.git?branch=migrate-to-hybrid-array#36baf45e5b0a7c02643e902ab279c5adf242ecae" dependencies = [ - "crypto-common 0.2.0-pre (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array", + "crypto-common 0.2.0-pre", ] [[package]] name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +version = "0.4.0-pre" +source = "git+https://github.com/RustCrypto/utils.git?branch=migrate-to-hybrid-array#36baf45e5b0a7c02643e902ab279c5adf242ecae" dependencies = [ - "generic-array", + "hybrid-array", ] [[package]] @@ -187,7 +184,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", - "cipher 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cipher 0.4.4", "cpufeatures", ] @@ -199,7 +196,7 @@ checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ "aead 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "chacha20", - "cipher 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cipher 0.4.4", "poly1305", "zeroize", ] @@ -207,21 +204,21 @@ dependencies = [ [[package]] name = "cipher" version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "blobby", "crypto-common 0.1.6", - "inout", + "inout 0.1.3", "zeroize", ] [[package]] name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +version = "0.5.0-pre" dependencies = [ - "crypto-common 0.1.6", - "inout", + "blobby", + "crypto-common 0.2.0-pre", + "inout 0.2.0-pre", "zeroize", ] @@ -291,20 +288,9 @@ dependencies = [ name = "crypto-common" version = "0.2.0-pre" dependencies = [ - "generic-array", + "hybrid-array", "rand_core 0.6.4", - "typenum", -] - -[[package]] -name = "crypto-common" -version = "0.2.0-pre" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6faaa83e7700e0832cbbf84854d4c356270526907d9b14fab927fc7a9b5befb8" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "typenum", + "serializable-state-derive", ] [[package]] @@ -323,7 +309,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cipher 0.4.4", ] [[package]] @@ -339,6 +325,41 @@ dependencies = [ "zeroize", ] +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "der" version = "0.4.5" @@ -384,7 +405,7 @@ dependencies = [ "blobby", "block-buffer 0.11.0-pre", "const-oid 0.9.5", - "crypto-common 0.2.0-pre (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-common 0.2.0-pre", "subtle", ] @@ -462,6 +483,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "generic-array" version = "0.14.7" @@ -609,16 +636,38 @@ dependencies = [ "zeroize", ] +[[package]] +name = "hybrid-array" +version = "0.2.0-pre.4" +source = "git+https://github.com/RustCrypto/utils.git?branch=migrate-to-hybrid-array#36baf45e5b0a7c02643e902ab279c5adf242ecae" +dependencies = [ + "typenum", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "inout" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "block-padding", "generic-array", ] +[[package]] +name = "inout" +version = "0.2.0-pre" +source = "git+https://github.com/RustCrypto/utils.git?branch=migrate-to-hybrid-array#36baf45e5b0a7c02643e902ab279c5adf242ecae" +dependencies = [ + "block-padding", + "hybrid-array", +] + [[package]] name = "jobserver" version = "0.1.26" @@ -787,18 +836,18 @@ checksum = "97e91cb6af081c6daad5fa705f8adb0634c027662052cb3174bdf2957bf07e25" [[package]] name = "proc-macro2" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.28" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -902,6 +951,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serializable-state-derive" +version = "0.1.0" +dependencies = [ + "crypto-common 0.2.0-pre", + "darling", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sha2" version = "0.9.9" @@ -980,6 +1040,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "subtle" version = "2.5.0" @@ -988,9 +1054,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "2.0.22" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", @@ -999,9 +1065,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" diff --git a/Cargo.toml b/Cargo.toml index e7b7482b6..a55ecba72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,9 @@ exclude = [ "crypto", "elliptic-curve" ] + +[patch.crates-io] +block-buffer = { git = "https://github.com/RustCrypto/utils.git", branch = "migrate-to-hybrid-array" } +crypto-common = { path = "crypto-common" } +hybrid-array = { git = "https://github.com/RustCrypto/utils.git", branch = "migrate-to-hybrid-array" } +inout = { git = "https://github.com/RustCrypto/utils.git", branch = "migrate-to-hybrid-array" } diff --git a/cipher/Cargo.toml b/cipher/Cargo.toml index c36b31cc5..60e9426f0 100644 --- a/cipher/Cargo.toml +++ b/cipher/Cargo.toml @@ -1,20 +1,20 @@ [package] name = "cipher" description = "Traits for describing block ciphers and stream ciphers" -version = "0.4.4" +version = "0.5.0-pre" authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" readme = "README.md" edition = "2021" -rust-version = "1.56" +rust-version = "1.65" documentation = "https://docs.rs/cipher" repository = "https://github.com/RustCrypto/traits" keywords = ["crypto", "block-cipher", "stream-cipher", "trait"] categories = ["cryptography", "no-std"] [dependencies] -crypto-common = "0.1.6" -inout = "0.1" +crypto-common = "=0.2.0-pre" +inout = "=0.2.0-pre" # optional dependencies blobby = { version = "0.3", optional = true } diff --git a/cipher/README.md b/cipher/README.md index 9d117626d..716973c1c 100644 --- a/cipher/README.md +++ b/cipher/README.md @@ -16,7 +16,7 @@ implementations which use these traits. ## Minimum Supported Rust Version -Rust **1.56** or higher. +Rust **1.65** or higher. Minimum supported Rust version can be changed in the future, but it will be done with a minor version bump. @@ -48,7 +48,7 @@ dual licensed as above, without any additional terms or conditions. [docs-image]: https://docs.rs/cipher/badge.svg [docs-link]: https://docs.rs/cipher/ [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 +[rustc-image]: https://img.shields.io/badge/rustc-1.65+-blue.svg [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260050-traits [build-image]: https://github.com/RustCrypto/traits/workflows/cipher/badge.svg?branch=master&event=push diff --git a/cipher/src/block.rs b/cipher/src/block.rs index 0742ced14..7b8c99cf9 100644 --- a/cipher/src/block.rs +++ b/cipher/src/block.rs @@ -13,6 +13,7 @@ use crate::{ParBlocks, ParBlocksSizeUser}; #[cfg(all(feature = "block-padding", feature = "alloc"))] use alloc::{vec, vec::Vec}; +use crypto_common::BlockSizes; #[cfg(feature = "block-padding")] use inout::{ block_padding::{Padding, UnpadError}, @@ -20,7 +21,7 @@ use inout::{ }; use inout::{InOut, InOutBuf, NotEqualError}; -pub use crypto_common::{generic_array::ArrayLength, typenum::Unsigned, Block, BlockSizeUser}; +pub use crypto_common::{array::ArraySize, typenum::Unsigned, Block, BlockSizeUser}; /// Marker trait for block ciphers. pub trait BlockCipher: BlockSizeUser {} @@ -593,15 +594,15 @@ impl BlockDecrypt for &Alg { } /// Closure used in methods which operate over separate blocks. -struct BlockCtx<'inp, 'out, BS: ArrayLength> { +struct BlockCtx<'inp, 'out, BS: BlockSizes> { block: InOut<'inp, 'out, Block>, } -impl<'inp, 'out, BS: ArrayLength> BlockSizeUser for BlockCtx<'inp, 'out, BS> { +impl<'inp, 'out, BS: BlockSizes> BlockSizeUser for BlockCtx<'inp, 'out, BS> { type BlockSize = BS; } -impl<'inp, 'out, BS: ArrayLength> BlockClosure for BlockCtx<'inp, 'out, BS> { +impl<'inp, 'out, BS: BlockSizes> BlockClosure for BlockCtx<'inp, 'out, BS> { #[inline(always)] fn call>(self, backend: &mut B) { backend.proc_block(self.block); @@ -609,15 +610,15 @@ impl<'inp, 'out, BS: ArrayLength> BlockClosure for BlockCtx<'inp, 'out, BS> } /// Closure used in methods which operate over slice of blocks. -struct BlocksCtx<'inp, 'out, BS: ArrayLength> { +struct BlocksCtx<'inp, 'out, BS: BlockSizes> { blocks: InOutBuf<'inp, 'out, Block>, } -impl<'inp, 'out, BS: ArrayLength> BlockSizeUser for BlocksCtx<'inp, 'out, BS> { +impl<'inp, 'out, BS: BlockSizes> BlockSizeUser for BlocksCtx<'inp, 'out, BS> { type BlockSize = BS; } -impl<'inp, 'out, BS: ArrayLength> BlockClosure for BlocksCtx<'inp, 'out, BS> { +impl<'inp, 'out, BS: BlockSizes> BlockClosure for BlocksCtx<'inp, 'out, BS> { #[inline(always)] fn call>(self, backend: &mut B) { if B::ParBlocksSize::USIZE > 1 { diff --git a/cipher/src/dev/block.rs b/cipher/src/dev/block.rs index d3115c5d8..ee0995dba 100644 --- a/cipher/src/dev/block.rs +++ b/cipher/src/dev/block.rs @@ -10,14 +10,14 @@ macro_rules! block_cipher_test { #[test] fn $name() { use cipher::{ - blobby::Blob3Iterator, generic_array::GenericArray, typenum::Unsigned, - BlockDecryptMut, BlockEncryptMut, BlockSizeUser, KeyInit, + array::Array, blobby::Blob3Iterator, typenum::Unsigned, BlockDecryptMut, + BlockEncryptMut, BlockSizeUser, KeyInit, }; fn run_test(key: &[u8], pt: &[u8], ct: &[u8]) -> bool { let mut state = <$cipher as KeyInit>::new_from_slice(key).unwrap(); - let mut block = GenericArray::clone_from_slice(pt); + let mut block = Array::clone_from_slice(pt); state.encrypt_block_mut(&mut block); if ct != block.as_slice() { return false; @@ -105,8 +105,8 @@ macro_rules! block_mode_enc_test { #[test] fn $name() { use cipher::{ - blobby::Blob4Iterator, generic_array::GenericArray, inout::InOutBuf, - typenum::Unsigned, BlockEncryptMut, BlockSizeUser, KeyIvInit, + array::Array, blobby::Blob4Iterator, inout::InOutBuf, typenum::Unsigned, + BlockEncryptMut, BlockSizeUser, KeyIvInit, }; fn run_test(key: &[u8], iv: &[u8], pt: &[u8], ct: &[u8]) -> bool { @@ -164,8 +164,8 @@ macro_rules! block_mode_dec_test { #[test] fn $name() { use cipher::{ - blobby::Blob4Iterator, generic_array::GenericArray, inout::InOutBuf, - typenum::Unsigned, BlockDecryptMut, BlockSizeUser, KeyIvInit, + array::Array, blobby::Blob4Iterator, inout::InOutBuf, typenum::Unsigned, + BlockDecryptMut, BlockSizeUser, KeyIvInit, }; fn run_test(key: &[u8], iv: &[u8], pt: &[u8], ct: &[u8]) -> bool { diff --git a/cipher/src/dev/stream.rs b/cipher/src/dev/stream.rs index 0496345d4..d65f80c70 100644 --- a/cipher/src/dev/stream.rs +++ b/cipher/src/dev/stream.rs @@ -7,7 +7,7 @@ macro_rules! stream_cipher_test { ($name:ident, $test_name:expr, $cipher:ty $(,)?) => { #[test] fn $name() { - use cipher::generic_array::GenericArray; + use cipher::array::Array; use cipher::{blobby::Blob4Iterator, KeyIvInit, StreamCipher}; let data = include_bytes!(concat!("data/", $test_name, ".blb")); @@ -43,7 +43,7 @@ macro_rules! stream_cipher_seek_test { ($name:ident, $cipher:ty) => { #[test] fn $name() { - use cipher::generic_array::GenericArray; + use cipher::array::Array; use cipher::{KeyIvInit, StreamCipher, StreamCipherSeek}; fn get_cipher() -> $cipher { diff --git a/cipher/src/lib.rs b/cipher/src/lib.rs index 87813d940..a8af951b8 100644 --- a/cipher/src/lib.rs +++ b/cipher/src/lib.rs @@ -48,7 +48,7 @@ mod stream_wrapper; pub use crate::{block::*, errors::*, stream::*, stream_core::*, stream_wrapper::*}; pub use crypto_common::{ - generic_array, + array, typenum::{self, consts}, AlgorithmName, Block, InnerIvInit, InvalidLength, Iv, IvSizeUser, Key, KeyInit, KeyIvInit, KeySizeUser, ParBlocks, ParBlocksSizeUser, diff --git a/cipher/src/stream_core.rs b/cipher/src/stream_core.rs index 1e4b302ea..5a9232dbe 100644 --- a/cipher/src/stream_core.rs +++ b/cipher/src/stream_core.rs @@ -1,8 +1,8 @@ use crate::{ParBlocks, ParBlocksSizeUser, StreamCipherError}; use crypto_common::{ - generic_array::{ArrayLength, GenericArray}, + array::{Array, ArraySize}, typenum::Unsigned, - Block, BlockSizeUser, + Block, BlockSizeUser, BlockSizes, }; use inout::{InOut, InOutBuf}; @@ -195,7 +195,7 @@ impl_counter! { u32 u64 u128 } /// In case if `N` is less or equal to 1, buffer of arrays has length /// of zero and tail is equal to `self`. #[inline] -fn into_chunks>(buf: &mut [T]) -> (&mut [GenericArray], &mut [T]) { +fn into_chunks(buf: &mut [T]) -> (&mut [Array], &mut [T]) { use core::slice; if N::USIZE <= 1 { return (&mut [], buf); @@ -205,32 +205,32 @@ fn into_chunks>(buf: &mut [T]) -> (&mut [GenericArray let tail_len = buf.len() - tail_pos; unsafe { let ptr = buf.as_mut_ptr(); - let chunks = slice::from_raw_parts_mut(ptr as *mut GenericArray, chunks_len); + let chunks = slice::from_raw_parts_mut(ptr as *mut Array, chunks_len); let tail = slice::from_raw_parts_mut(ptr.add(tail_pos), tail_len); (chunks, tail) } } -struct WriteBlockCtx<'a, BS: ArrayLength> { +struct WriteBlockCtx<'a, BS: BlockSizes> { block: &'a mut Block, } -impl<'a, BS: ArrayLength> BlockSizeUser for WriteBlockCtx<'a, BS> { +impl<'a, BS: BlockSizes> BlockSizeUser for WriteBlockCtx<'a, BS> { type BlockSize = BS; } -impl<'a, BS: ArrayLength> StreamClosure for WriteBlockCtx<'a, BS> { +impl<'a, BS: BlockSizes> StreamClosure for WriteBlockCtx<'a, BS> { #[inline(always)] fn call>(self, backend: &mut B) { backend.gen_ks_block(self.block); } } -struct WriteBlocksCtx<'a, BS: ArrayLength> { +struct WriteBlocksCtx<'a, BS: BlockSizes> { blocks: &'a mut [Block], } -impl<'a, BS: ArrayLength> BlockSizeUser for WriteBlocksCtx<'a, BS> { +impl<'a, BS: BlockSizes> BlockSizeUser for WriteBlocksCtx<'a, BS> { type BlockSize = BS; } -impl<'a, BS: ArrayLength> StreamClosure for WriteBlocksCtx<'a, BS> { +impl<'a, BS: BlockSizes> StreamClosure for WriteBlocksCtx<'a, BS> { #[inline(always)] fn call>(self, backend: &mut B) { if B::ParBlocksSize::USIZE > 1 { @@ -247,15 +247,15 @@ impl<'a, BS: ArrayLength> StreamClosure for WriteBlocksCtx<'a, BS> { } } -struct ApplyBlockCtx<'inp, 'out, BS: ArrayLength> { +struct ApplyBlockCtx<'inp, 'out, BS: BlockSizes> { block: InOut<'inp, 'out, Block>, } -impl<'inp, 'out, BS: ArrayLength> BlockSizeUser for ApplyBlockCtx<'inp, 'out, BS> { +impl<'inp, 'out, BS: BlockSizes> BlockSizeUser for ApplyBlockCtx<'inp, 'out, BS> { type BlockSize = BS; } -impl<'inp, 'out, BS: ArrayLength> StreamClosure for ApplyBlockCtx<'inp, 'out, BS> { +impl<'inp, 'out, BS: BlockSizes> StreamClosure for ApplyBlockCtx<'inp, 'out, BS> { #[inline(always)] fn call>(mut self, backend: &mut B) { let mut t = Default::default(); @@ -264,15 +264,15 @@ impl<'inp, 'out, BS: ArrayLength> StreamClosure for ApplyBlockCtx<'inp, 'out } } -struct ApplyBlocksCtx<'inp, 'out, BS: ArrayLength> { +struct ApplyBlocksCtx<'inp, 'out, BS: BlockSizes> { blocks: InOutBuf<'inp, 'out, Block>, } -impl<'inp, 'out, BS: ArrayLength> BlockSizeUser for ApplyBlocksCtx<'inp, 'out, BS> { +impl<'inp, 'out, BS: BlockSizes> BlockSizeUser for ApplyBlocksCtx<'inp, 'out, BS> { type BlockSize = BS; } -impl<'inp, 'out, BS: ArrayLength> StreamClosure for ApplyBlocksCtx<'inp, 'out, BS> { +impl<'inp, 'out, BS: BlockSizes> StreamClosure for ApplyBlocksCtx<'inp, 'out, BS> { #[inline(always)] #[allow(clippy::needless_range_loop)] fn call>(self, backend: &mut B) { @@ -284,7 +284,7 @@ impl<'inp, 'out, BS: ArrayLength> StreamClosure for ApplyBlocksCtx<'inp, 'ou chunk.xor_in2out(&tmp); } let n = tail.len(); - let mut buf = GenericArray::<_, B::ParBlocksSize>::default(); + let mut buf = Array::<_, B::ParBlocksSize>::default(); let ks = &mut buf[..n]; backend.gen_tail_blocks(ks); for i in 0..n { diff --git a/crypto-common/Cargo.toml b/crypto-common/Cargo.toml index 63fca3de6..dad9611f5 100644 --- a/crypto-common/Cargo.toml +++ b/crypto-common/Cargo.toml @@ -13,8 +13,8 @@ keywords = ["crypto", "traits"] categories = ["cryptography", "no-std"] [dependencies] -generic-array = { version = "0.14.6", features = ["more_lengths"] } -typenum = "1.14" # earlier versions of typenum don't satisfy the 'static bound on U* types +hybrid-array = "=0.2.0-pre.4" +serializable-state-derive = { path = "serializable-state-derive" } # optional dependencies rand_core = { version = "0.6.4", optional = true } diff --git a/crypto-common/README.md b/crypto-common/README.md index ba35578f8..21008623a 100644 --- a/crypto-common/README.md +++ b/crypto-common/README.md @@ -2,10 +2,10 @@ [![crate][crate-image]][crate-link] [![Docs][docs-image]][docs-link] +[![Build Status][build-image]][build-link] ![Apache2/MIT licensed][license-image] ![Rust Version][rustc-image] [![Project Chat][chat-image]][chat-link] -[![Build Status][build-image]][build-link] Common traits used by cryptographic algorithms. Users should generally use higher-level trait crates instead of this one. @@ -14,7 +14,7 @@ higher-level trait crates instead of this one. ## Minimum Supported Rust Version -Rust **1.56** or higher. +Rust **1.65** or higher. Minimum supported Rust version can be changed in the future, but it will be done with a minor version bump. @@ -46,7 +46,7 @@ dual licensed as above, without any additional terms or conditions. [docs-image]: https://docs.rs/crypto-common/badge.svg [docs-link]: https://docs.rs/crypto-common/ [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg -[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.65+-blue.svg [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260041-hashes [build-image]: https://github.com/RustCrypto/traits/workflows/crypto-common/badge.svg?branch=master&event=push diff --git a/crypto-common/serializable-state-derive/Cargo.toml b/crypto-common/serializable-state-derive/Cargo.toml new file mode 100644 index 000000000..bedc5048a --- /dev/null +++ b/crypto-common/serializable-state-derive/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "serializable-state-derive" +description = "Macro implementation of #[derive(SerializableState)]" +version = "0.1.0" +authors = ["Ruslan Piasetskyi "] +license = "MIT OR Apache-2.0" +edition = "2018" +documentation = "https://docs.rs/serializable-state-derive" +repository = "https://github.com/RustCrypto/traits" +keywords = ["serialization", "no_std", "derive"] +categories = ["cryptography", "no-std"] + +[lib] +proc-macro = true + +[dependencies] +darling = "0.20.3" +proc-macro2 = "1.0.69" +quote = "1.0.33" +syn = "2.0.38" + +[dev-dependencies] +crypto-common = { version = "0.2.0-pre", path = ".." } diff --git a/crypto-common/serializable-state-derive/src/lib.rs b/crypto-common/serializable-state-derive/src/lib.rs new file mode 100644 index 000000000..b3ab9747d --- /dev/null +++ b/crypto-common/serializable-state-derive/src/lib.rs @@ -0,0 +1,195 @@ +extern crate proc_macro; + +use darling::FromDeriveInput; +use proc_macro2::{Ident, TokenStream}; +use quote::{format_ident, quote, quote_spanned}; +use syn::{ + parse_macro_input, punctuated::Iter, spanned::Spanned, Data, DeriveInput, Field, Fields, + Generics, Index, +}; + +const CRATE_NAME: &str = "crypto_common"; + +#[derive(FromDeriveInput, Default)] +#[darling(default, attributes(serializable_state))] +struct Opts { + crate_path: Option, +} + +#[proc_macro_derive(SerializableState, attributes(serializable_state))] +pub fn derive_serializable_state(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input); + let crate_path = get_crate_path(&input); + let struct_name = input.ident; + + let serialized_state_size = generate_serializable_state_size(&input.data, &crate_path); + let serialize_logic = generate_serialize_logic(&input.data); + let deserialize_logic = generate_deserialize_logic(&input.data); + + check_generics(&input.generics); + + let expanded = quote! { + impl #crate_path::SerializableState for #struct_name { + type SerializedStateSize = #serialized_state_size; + + fn serialize(&self) -> #crate_path::SerializedState { + use #crate_path::{SerializableState, SerializedState}; + + #serialize_logic + } + + fn deserialize(_serialized_state: &#crate_path::SerializedState) -> ::core::result::Result { + use #crate_path::SerializableState; + + #deserialize_logic + } + } + }; + + proc_macro::TokenStream::from(expanded) +} + +fn check_generics(generics: &Generics) { + if generics.params.iter().next().is_some() { + panic!("Generics are not supported yet. Please implement SerializableState on your own.") + } +} + +fn generate_serializable_state_size(data: &Data, crate_path: &TokenStream) -> TokenStream { + match *data { + Data::Struct(ref data) => match data.fields { + Fields::Named(ref fields) => { + serializable_state_size_from_fields(fields.named.iter(), crate_path) + } + Fields::Unnamed(ref fields) => { + serializable_state_size_from_fields(fields.unnamed.iter(), crate_path) + } + Fields::Unit => quote! { #crate_path::typenum::U0 }, + }, + Data::Enum(_) | Data::Union(_) => unimplemented!(), + } +} + +fn generate_serialize_logic(data: &Data) -> TokenStream { + match *data { + Data::Struct(ref data) => match data.fields { + Fields::Named(ref fields) => serialize_logic_from_fields(fields.named.iter()), + Fields::Unnamed(ref fields) => serialize_logic_from_fields(fields.unnamed.iter()), + Fields::Unit => quote! { SerializedState::::default() }, + }, + Data::Enum(_) | Data::Union(_) => unimplemented!(), + } +} + +fn generate_deserialize_logic(data: &Data) -> TokenStream { + match *data { + Data::Struct(ref data) => match data.fields { + Fields::Named(ref fields) => deserialize_logic_from_fields(fields.named.iter(), true), + Fields::Unnamed(ref fields) => { + deserialize_logic_from_fields(fields.unnamed.iter(), false) + } + Fields::Unit => quote! { Ok(Self {}) }, + }, + Data::Enum(_) | Data::Union(_) => unimplemented!(), + } +} + +fn serializable_state_size_from_fields( + mut fields: Iter, + crate_path: &TokenStream, +) -> TokenStream { + match fields.next() { + None => quote! { #crate_path::typenum::U0 }, + Some(first) => { + let ty = &first.ty; + let mut size = quote_spanned! { first.span() => <#ty as #crate_path::SerializableState>::SerializedStateSize }; + fields.for_each(|field| { + let ty = &field.ty; + size = quote_spanned! { + field.span() => #crate_path::typenum::Sum<<#ty as #crate_path::SerializableState>::SerializedStateSize, #size> + }; + }); + size + } + } +} + +fn serialize_logic_from_fields(mut fields: Iter) -> TokenStream { + match fields.next() { + None => quote! { SerializedState::::default() }, + Some(first) => { + let field_name = get_field_name(0, &first.ident, None); + let mut code = quote! { self.#field_name.serialize() }; + fields.enumerate().for_each(|(i, field)| { + let field_name = get_field_name(i + 1, &field.ident, None); + code = + quote_spanned! { field.span() => #code.concat(self.#field_name.serialize()) }; + }); + code + } + } +} + +fn deserialize_logic_from_fields(fields: Iter, named: bool) -> TokenStream { + let mut skip_first = fields.clone(); + match skip_first.next() { + None => quote! { Ok(Self {}) }, + Some(first) => { + let mut code = quote!(); + fields.enumerate().for_each(|(i, field)| { + let field_name = get_field_name(i, &field.ident, Some("serialized_")); + let ty = &field.ty; + code = quote_spanned! { + field.span() => + #code + let (#field_name, _serialized_state) = _serialized_state.split_ref::<<#ty as SerializableState>::SerializedStateSize>(); + let #field_name = <#ty>::deserialize(#field_name)?; + }; + }); + let first = get_field_name(0, &first.ident, Some("serialized_")); + let recurce = skip_first.enumerate().map(|(i, field)| { + let field_name = get_field_name(i + 1, &field.ident, Some("serialized_")); + quote_spanned! { field.span() => #field_name } + }); + + let construction = if named { + quote! { Self { #first #(, #recurce)* } } + } else { + quote! { Self ( #first #(, #recurce)* ) } + }; + + quote! { + #code + + Ok(#construction) + } + } + } +} + +fn get_field_name(i: usize, ident: &Option, unnamed_prefix: Option<&str>) -> TokenStream { + match ident { + Some(ident) => quote! { #ident }, + None => match unnamed_prefix { + None => { + let index = Index::from(i); + quote! { #index } + } + Some(unnamed_prefix) => { + let ident = format_ident!("{}{}", unnamed_prefix, i); + quote! { #ident } + } + }, + } +} + +fn get_crate_path(input: &DeriveInput) -> TokenStream { + let crate_path = format_ident!( + "{}", + Opts::from_derive_input(input) + .expect("Unknown options") + .crate_path + .unwrap_or(CRATE_NAME.into()) + ); + quote! { #crate_path } +} diff --git a/crypto-common/serializable-state-derive/tests/serialization-tests.rs b/crypto-common/serializable-state-derive/tests/serialization-tests.rs new file mode 100644 index 000000000..f62ea5d02 --- /dev/null +++ b/crypto-common/serializable-state-derive/tests/serialization-tests.rs @@ -0,0 +1,71 @@ +use crypto_common as crypto_common_crate; +use crypto_common::SerializableState; + +macro_rules! serialization_test { + ($name:ident, $type: ty, $obj: expr, $serialized_state: expr) => { + #[test] + fn $name() { + let obj = $obj; + + let serialized_state = obj.serialize(); + assert_eq!(serialized_state.as_slice(), $serialized_state); + + let deserialized_obj = <$type>::deserialize(&serialized_state).unwrap(); + assert_eq!(deserialized_obj, obj); + } + }; +} + +#[derive(SerializableState, PartialEq, Debug)] +#[serializable_state(crate_path = "crypto_common_crate")] +struct StructWithNamedFields { + a: u8, + b: u64, + c: [u16; 3], +} + +serialization_test!( + struct_with_named_fields_serialization_test, + StructWithNamedFields, + StructWithNamedFields { + a: 0x42, + b: 0x1122334455667788, + c: [0xAABB, 0xCCDD, 0xEEFF], + }, + &[0x42, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0xBB, 0xAA, 0xDD, 0xCC, 0xFF, 0xEE] +); + +#[derive(SerializableState, PartialEq, Debug)] +struct StructWithZeroNamedFields {} + +serialization_test!( + struct_with_zero_named_fields_serialization_test, + StructWithZeroNamedFields, + StructWithZeroNamedFields {}, + &[] +); + +#[derive(SerializableState, PartialEq, Debug)] +struct StructWithUnnamedFields([u8; 5], u32); + +serialization_test!( + struct_with_unnamed_fields_serialization_test, + StructWithUnnamedFields, + StructWithUnnamedFields([0x11, 0x22, 0x33, 0x44, 0x55], 0xAABBCCDD), + &[0x11, 0x22, 0x33, 0x44, 0x55, 0xDD, 0xCC, 0xBB, 0xAA] +); + +#[derive(SerializableState, PartialEq, Debug)] +struct StructWithZeroUnnamedFields(); + +serialization_test!( + struct_with_zero_unnamed_fields_serialization_test, + StructWithZeroUnnamedFields, + StructWithZeroUnnamedFields(), + &[] +); + +#[derive(SerializableState, PartialEq, Debug)] +struct UnitStruct; + +serialization_test!(unit_struct_serialization_test, UnitStruct, UnitStruct, &[]); diff --git a/crypto-common/src/lib.rs b/crypto-common/src/lib.rs index f4d3993da..bca929dfb 100644 --- a/crypto-common/src/lib.rs +++ b/crypto-common/src/lib.rs @@ -15,28 +15,31 @@ extern crate std; #[cfg(feature = "rand_core")] pub use rand_core; -pub use generic_array; -pub use generic_array::typenum; +pub use hybrid_array as array; +pub use hybrid_array::typenum; use core::fmt; -use generic_array::{typenum::Unsigned, ArrayLength, GenericArray}; +use hybrid_array::{typenum::Unsigned, Array, ArraySize, ByteArray}; #[cfg(feature = "rand_core")] use rand_core::CryptoRngCore; +mod serializable_state; +pub use serializable_state::{DeserializeStateError, SerializableState, SerializedState}; + /// Block on which [`BlockSizeUser`] implementors operate. -pub type Block = GenericArray::BlockSize>; +pub type Block = ByteArray<::BlockSize>; /// Parallel blocks on which [`ParBlocksSizeUser`] implementors operate. -pub type ParBlocks = GenericArray, ::ParBlocksSize>; +pub type ParBlocks = Array, ::ParBlocksSize>; /// Output array of [`OutputSizeUser`] implementors. -pub type Output = GenericArray::OutputSize>; +pub type Output = ByteArray<::OutputSize>; /// Key used by [`KeySizeUser`] implementors. -pub type Key = GenericArray::KeySize>; +pub type Key = ByteArray<::KeySize>; /// Initialization vector (nonce) used by [`IvSizeUser`] implementors. -pub type Iv = GenericArray::IvSize>; +pub type Iv = ByteArray<::IvSize>; /// Types which process data in blocks. pub trait BlockSizeUser { @@ -59,12 +62,12 @@ impl BlockSizeUser for &mut T { } /// Trait implemented for supported block sizes, i.e. for types from `U1` to `U255`. -pub trait BlockSizes: ArrayLength + sealed::BlockSizes + 'static {} +pub trait BlockSizes: ArraySize + sealed::BlockSizes + 'static {} -impl + sealed::BlockSizes> BlockSizes for T {} +impl BlockSizes for T {} mod sealed { - use generic_array::typenum::{Gr, IsGreater, IsLess, Le, NonZero, Unsigned, U1, U256}; + use crate::typenum::{Gr, IsGreater, IsLess, Le, NonZero, Unsigned, U1, U256}; pub trait BlockSizes {} @@ -80,13 +83,13 @@ mod sealed { /// Types which can process blocks in parallel. pub trait ParBlocksSizeUser: BlockSizeUser { /// Number of blocks which can be processed in parallel. - type ParBlocksSize: ArrayLength>; + type ParBlocksSize: ArraySize; } /// Types which return data with the given size. pub trait OutputSizeUser { /// Size of the output in bytes. - type OutputSize: ArrayLength + 'static; + type OutputSize: ArraySize + 'static; /// Return output size in bytes. #[inline(always)] @@ -100,7 +103,7 @@ pub trait OutputSizeUser { /// Generally it's used indirectly via [`KeyInit`] or [`KeyIvInit`]. pub trait KeySizeUser { /// Key size in bytes. - type KeySize: ArrayLength + 'static; + type KeySize: ArraySize + 'static; /// Return key size in bytes. #[inline(always)] @@ -114,7 +117,7 @@ pub trait KeySizeUser { /// Generally it's used indirectly via [`KeyIvInit`] or [`InnerIvInit`]. pub trait IvSizeUser { /// Initialization vector size in bytes. - type IvSize: ArrayLength + 'static; + type IvSize: ArraySize + 'static; /// Return IV size in bytes. #[inline(always)] @@ -151,11 +154,9 @@ pub trait KeyInit: KeySizeUser + Sized { /// Create new value from variable size key. #[inline] fn new_from_slice(key: &[u8]) -> Result { - if key.len() != Self::KeySize::to_usize() { - Err(InvalidLength) - } else { - Ok(Self::new(Key::::from_slice(key))) - } + <&Key>::try_from(key) + .map(Self::new) + .map_err(|_| InvalidLength) } /// Generate random key using the provided [`CryptoRngCore`]. @@ -177,16 +178,9 @@ pub trait KeyIvInit: KeySizeUser + IvSizeUser + Sized { /// 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), - )) - } + let key = <&Key>::try_from(key).map_err(|_| InvalidLength)?; + let iv = <&Iv>::try_from(iv).map_err(|_| InvalidLength)?; + Ok(Self::new(key, iv)) } /// Generate random key using the provided [`CryptoRngCore`]. @@ -237,11 +231,8 @@ pub trait InnerIvInit: InnerUser + IvSizeUser + Sized { /// Initialize value using `inner` and `iv` slice. #[inline] 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))) - } + let iv = <&Iv>::try_from(iv).map_err(|_| InvalidLength)?; + Ok(Self::inner_iv_init(inner, iv)) } /// Generate random IV using the provided [`CryptoRngCore`]. diff --git a/crypto-common/src/serializable_state.rs b/crypto-common/src/serializable_state.rs new file mode 100644 index 000000000..245a3dc10 --- /dev/null +++ b/crypto-common/src/serializable_state.rs @@ -0,0 +1,347 @@ +use crate::array::{ + self, + typenum::{Prod, Unsigned, U1, U16, U2, U4, U8}, + ArraySize, ByteArray, +}; +use core::{convert::TryInto, default::Default, fmt}; + +pub use serializable_state_derive::*; + +/// Serialized internal state. +pub type SerializedState = ByteArray<::SerializedStateSize>; + +/// The error type returned when an object cannot be deserialized from the state. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub struct DeserializeStateError; + +impl fmt::Display for DeserializeStateError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str("Deserialization error") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for DeserializeStateError {} + +/// Types which can serialize the internal state and be restored from it. +/// +/// # SECURITY WARNING +/// +/// Serialized state may contain sensitive data. +pub trait SerializableState +where + Self: Sized, +{ + /// Size of serialized internal state. + type SerializedStateSize: ArraySize; + + /// Serialize and return internal state. + fn serialize(&self) -> SerializedState; + /// Create an object from serialized internal state. + fn deserialize(serialized_state: &SerializedState) + -> Result; +} + +macro_rules! impl_seializable_state_unsigned { + ($type: ty, $type_size: ty) => { + impl SerializableState for $type { + type SerializedStateSize = $type_size; + + fn serialize(&self) -> SerializedState { + self.to_le_bytes().into() + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + Ok(<$type>::from_le_bytes((*serialized_state).into())) + } + } + }; +} + +impl_seializable_state_unsigned!(u8, U1); +impl_seializable_state_unsigned!(u16, U2); +impl_seializable_state_unsigned!(u32, U4); +impl_seializable_state_unsigned!(u64, U8); +impl_seializable_state_unsigned!(u128, U16); + +macro_rules! impl_serializable_state_u8_array { + ($($n: ty),*) => { + $( + impl SerializableState for [u8; <$n>::USIZE] { + type SerializedStateSize = $n; + + fn serialize(&self) -> SerializedState { + (*self).into() + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + Ok((*serialized_state).into()) + } + } + )* + }; +} + +macro_rules! impl_serializable_state_type_array { + ($type: ty, $type_size: ty, $n: ty) => { + impl SerializableState for [$type; <$n>::USIZE] { + type SerializedStateSize = Prod<$n, $type_size>; + + fn serialize(&self) -> SerializedState { + let mut serialized_state = SerializedState::::default(); + for (val, chunk) in self + .iter() + .zip(serialized_state.chunks_exact_mut(<$type_size>::USIZE)) + { + chunk.copy_from_slice(&val.to_le_bytes()); + } + + serialized_state + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let mut array = [0; <$n>::USIZE]; + for (val, chunk) in array + .iter_mut() + .zip(serialized_state.chunks_exact(<$type_size>::USIZE)) + { + *val = <$type>::from_le_bytes(chunk.try_into().unwrap()); + } + Ok(array) + } + } + }; +} + +macro_rules! impl_serializable_state_u16_array { + ($($n: ty),*) => { + $( + impl_serializable_state_type_array!(u16, U2, $n); + )* + }; +} + +macro_rules! impl_serializable_state_u32_array { + ($($n: ty),*) => { + $( + impl_serializable_state_type_array!(u32, U4, $n); + )* + }; +} + +macro_rules! impl_serializable_state_u64_array { + ($($n: ty),*) => { + $( + impl_serializable_state_type_array!(u64, U8, $n); + )* + }; +} + +macro_rules! impl_serializable_state_u128_array { + ($($n: ty),*) => { + $( + impl_serializable_state_type_array!(u128, U8, $n); + )* + }; +} + +impl_serializable_state_u8_array! { + array::typenum::U1, + array::typenum::U2, + array::typenum::U3, + array::typenum::U4, + array::typenum::U5, + array::typenum::U6, + array::typenum::U7, + array::typenum::U8, + array::typenum::U9, + array::typenum::U10, + array::typenum::U11, + array::typenum::U12, + array::typenum::U13, + array::typenum::U14, + array::typenum::U15, + array::typenum::U16, + array::typenum::U17, + array::typenum::U18, + array::typenum::U19, + array::typenum::U20, + array::typenum::U21, + array::typenum::U22, + array::typenum::U23, + array::typenum::U24, + array::typenum::U25, + array::typenum::U26, + array::typenum::U27, + array::typenum::U28, + array::typenum::U29, + array::typenum::U30, + array::typenum::U31, + array::typenum::U32, + array::typenum::U33, + array::typenum::U34, + array::typenum::U35, + array::typenum::U36, + array::typenum::U37, + array::typenum::U38, + array::typenum::U39, + array::typenum::U40, + array::typenum::U41, + array::typenum::U42, + array::typenum::U43, + array::typenum::U44, + array::typenum::U45, + array::typenum::U46, + array::typenum::U47, + array::typenum::U48, + array::typenum::U49, + array::typenum::U50, + array::typenum::U51, + array::typenum::U52, + array::typenum::U53, + array::typenum::U54, + array::typenum::U55, + array::typenum::U56, + array::typenum::U57, + array::typenum::U58, + array::typenum::U59, + array::typenum::U60, + array::typenum::U61, + array::typenum::U62, + array::typenum::U63, + array::typenum::U64, + array::typenum::U96, + array::typenum::U128, + array::typenum::U192, + array::typenum::U256, + array::typenum::U384, + array::typenum::U448, + array::typenum::U512, + array::typenum::U768, + array::typenum::U896, + array::typenum::U1024, + array::typenum::U2048, + array::typenum::U4096, + array::typenum::U8192 +} + +impl_serializable_state_u16_array! { + array::typenum::U1, + array::typenum::U2, + array::typenum::U3, + array::typenum::U4, + array::typenum::U5, + array::typenum::U6, + array::typenum::U7, + array::typenum::U8, + array::typenum::U9, + array::typenum::U10, + array::typenum::U11, + array::typenum::U12, + array::typenum::U13, + array::typenum::U14, + array::typenum::U15, + array::typenum::U16, + array::typenum::U17, + array::typenum::U18, + array::typenum::U19, + array::typenum::U20, + array::typenum::U21, + array::typenum::U22, + array::typenum::U23, + array::typenum::U24, + array::typenum::U25, + array::typenum::U26, + array::typenum::U27, + array::typenum::U28, + array::typenum::U29, + array::typenum::U30, + array::typenum::U31, + array::typenum::U32, + array::typenum::U48, + array::typenum::U96, + array::typenum::U128, + array::typenum::U192, + array::typenum::U256, + array::typenum::U384, + array::typenum::U448, + array::typenum::U512, + array::typenum::U2048, + array::typenum::U4096 +} + +impl_serializable_state_u32_array! { + array::typenum::U1, + array::typenum::U2, + array::typenum::U3, + array::typenum::U4, + array::typenum::U5, + array::typenum::U6, + array::typenum::U7, + array::typenum::U8, + array::typenum::U9, + array::typenum::U10, + array::typenum::U11, + array::typenum::U12, + array::typenum::U13, + array::typenum::U14, + array::typenum::U15, + array::typenum::U16, + array::typenum::U24, + array::typenum::U32, + array::typenum::U48, + array::typenum::U64, + array::typenum::U96, + array::typenum::U128, + array::typenum::U192, + array::typenum::U256, + array::typenum::U512, + array::typenum::U1024, + array::typenum::U2048 +} + +impl_serializable_state_u64_array! { + array::typenum::U1, + array::typenum::U2, + array::typenum::U3, + array::typenum::U4, + array::typenum::U5, + array::typenum::U6, + array::typenum::U7, + array::typenum::U8, + array::typenum::U12, + array::typenum::U16, + array::typenum::U24, + array::typenum::U32, + array::typenum::U48, + array::typenum::U64, + array::typenum::U96, + array::typenum::U128, + array::typenum::U256, + array::typenum::U512, + array::typenum::U1024 +} + +impl_serializable_state_u128_array! { + array::typenum::U1, + array::typenum::U2, + array::typenum::U3, + array::typenum::U4, + array::typenum::U6, + array::typenum::U8, + array::typenum::U12, + array::typenum::U16, + array::typenum::U24, + array::typenum::U32, + array::typenum::U48, + array::typenum::U64, + array::typenum::U128, + array::typenum::U256, + array::typenum::U512 +} diff --git a/digest/Cargo.toml b/digest/Cargo.toml index 9e936809c..261494a01 100644 --- a/digest/Cargo.toml +++ b/digest/Cargo.toml @@ -6,17 +6,17 @@ authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" readme = "README.md" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" documentation = "https://docs.rs/digest" repository = "https://github.com/RustCrypto/traits" keywords = ["digest", "crypto", "hash"] categories = ["cryptography", "no-std"] [dependencies] -crypto-common = "0.2.0-pre" +crypto-common = "=0.2.0-pre" # optional dependencies -block-buffer = { version = "0.11.0-pre", optional = true } +block-buffer = { version = "=0.11.0-pre", optional = true } subtle = { version = "2.4", default-features = false, optional = true } blobby = { version = "0.3", optional = true } const-oid = { version = "0.9", optional = true } diff --git a/digest/README.md b/digest/README.md index 1d5f1f8fb..3748879a6 100644 --- a/digest/README.md +++ b/digest/README.md @@ -16,7 +16,7 @@ See [RustCrypto/hashes][1] for implementations which use this trait. ## Minimum Supported Rust Version -Rust **1.57** or higher. +Rust **1.65** or higher. Minimum supported Rust version can be changed in the future, but it will be done with a minor version bump. @@ -56,7 +56,7 @@ let hash = hasher.finalize(); println!("Result: {:x}", hash); ``` -In this example `hash` has type [`GenericArray`][2], which is a generic +In this example `hash` has type [`Array`][2], which is a generic alternative to `[u8; 64]`. Alternatively you can use chained approach, which is equivalent to the previous @@ -147,7 +147,7 @@ dual licensed as above, without any additional terms or conditions. [docs-image]: https://docs.rs/digest/badge.svg [docs-link]: https://docs.rs/digest/ [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg -[rustc-image]: https://img.shields.io/badge/rustc-1.57+-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.65+-blue.svg [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260041-hashes [build-image]: https://github.com/RustCrypto/traits/workflows/digest/badge.svg?branch=master&event=push @@ -157,7 +157,7 @@ dual licensed as above, without any additional terms or conditions. [0]: https://en.wikipedia.org/wiki/Cryptographic_hash_function [1]: https://github.com/RustCrypto/hashes -[2]: https://docs.rs/generic-array +[2]: https://docs.rs/hybrid-array [3]: https://doc.rust-lang.org/std/io/trait.Read.html [4]: https://doc.rust-lang.org/std/io/trait.Write.html [5]: https://en.wikipedia.org/wiki/Hash-based_message_authentication_code diff --git a/digest/src/core_api/ct_variable.rs b/digest/src/core_api/ct_variable.rs index 0011a9262..ad21d9648 100644 --- a/digest/src/core_api/ct_variable.rs +++ b/digest/src/core_api/ct_variable.rs @@ -7,11 +7,16 @@ use crate::HashMarker; use crate::MacMarker; #[cfg(feature = "oid")] use const_oid::{AssociatedOid, ObjectIdentifier}; -use core::{fmt, marker::PhantomData}; +use core::{ + fmt, + marker::PhantomData, + ops::{Add, Sub}, +}; use crypto_common::{ - generic_array::{ArrayLength, GenericArray}, - typenum::{IsLessOrEqual, LeEq, NonZero}, - Block, BlockSizeUser, OutputSizeUser, + array::{Array, ArraySize, ByteArray}, + typenum::{Diff, IsLess, IsLessOrEqual, Le, LeEq, NonZero, Sum, U1, U256}, + Block, BlockSizeUser, DeserializeStateError, OutputSizeUser, SerializableState, + SerializedState, }; /// Dummy type used with [`CtVariableCoreWrapper`] in cases when @@ -25,7 +30,7 @@ pub struct NoOid; pub struct CtVariableCoreWrapper where T: VariableOutputCore, - OutSize: ArrayLength + IsLessOrEqual, + OutSize: ArraySize + IsLessOrEqual, LeEq: NonZero, { inner: T, @@ -35,7 +40,7 @@ where impl HashMarker for CtVariableCoreWrapper where T: VariableOutputCore + HashMarker, - OutSize: ArrayLength + IsLessOrEqual, + OutSize: ArraySize + IsLessOrEqual, LeEq: NonZero, { } @@ -44,7 +49,7 @@ where impl MacMarker for CtVariableCoreWrapper where T: VariableOutputCore + MacMarker, - OutSize: ArrayLength + IsLessOrEqual, + OutSize: ArraySize + IsLessOrEqual, LeEq: NonZero, { } @@ -52,7 +57,7 @@ where impl BlockSizeUser for CtVariableCoreWrapper where T: VariableOutputCore, - OutSize: ArrayLength + IsLessOrEqual, + OutSize: ArraySize + IsLessOrEqual, LeEq: NonZero, { type BlockSize = T::BlockSize; @@ -61,7 +66,7 @@ where impl UpdateCore for CtVariableCoreWrapper where T: VariableOutputCore, - OutSize: ArrayLength + IsLessOrEqual, + OutSize: ArraySize + IsLessOrEqual, LeEq: NonZero, { #[inline] @@ -73,7 +78,7 @@ where impl OutputSizeUser for CtVariableCoreWrapper where T: VariableOutputCore, - OutSize: ArrayLength + IsLessOrEqual + 'static, + OutSize: ArraySize + IsLessOrEqual + 'static, LeEq: NonZero, { type OutputSize = OutSize; @@ -82,7 +87,7 @@ where impl BufferKindUser for CtVariableCoreWrapper where T: VariableOutputCore, - OutSize: ArrayLength + IsLessOrEqual, + OutSize: ArraySize + IsLessOrEqual, LeEq: NonZero, { type BufferKind = T::BufferKind; @@ -91,14 +96,14 @@ where impl FixedOutputCore for CtVariableCoreWrapper where T: VariableOutputCore, - OutSize: ArrayLength + IsLessOrEqual + 'static, + OutSize: ArraySize + IsLessOrEqual + 'static, LeEq: NonZero, { #[inline] fn finalize_fixed_core( &mut self, buffer: &mut Buffer, - out: &mut GenericArray, + out: &mut Array, ) { let mut full_res = Default::default(); self.inner.finalize_variable_core(buffer, &mut full_res); @@ -114,7 +119,7 @@ where impl Default for CtVariableCoreWrapper where T: VariableOutputCore, - OutSize: ArrayLength + IsLessOrEqual, + OutSize: ArraySize + IsLessOrEqual, LeEq: NonZero, { #[inline] @@ -129,7 +134,7 @@ where impl Reset for CtVariableCoreWrapper where T: VariableOutputCore, - OutSize: ArrayLength + IsLessOrEqual, + OutSize: ArraySize + IsLessOrEqual, LeEq: NonZero, { #[inline] @@ -141,7 +146,7 @@ where impl AlgorithmName for CtVariableCoreWrapper where T: VariableOutputCore + AlgorithmName, - OutSize: ArrayLength + IsLessOrEqual, + OutSize: ArraySize + IsLessOrEqual, LeEq: NonZero, { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -157,7 +162,7 @@ impl AssociatedOid for CtVariableCoreWrapper where T: VariableOutputCore, O: AssociatedOid, - OutSize: ArrayLength + IsLessOrEqual, + OutSize: ArraySize + IsLessOrEqual, LeEq: NonZero, { const OID: ObjectIdentifier = O::OID; @@ -178,3 +183,40 @@ macro_rules! impl_oid_carrier { } }; } + +impl SerializableState for CtVariableCoreWrapper +where + T: VariableOutputCore + SerializableState, + OutSize: ArraySize + IsLessOrEqual, + LeEq: NonZero, + T::BlockSize: IsLess, + Le: NonZero, + T::SerializedStateSize: Add, + Sum: Sub + ArraySize, + Diff, T::SerializedStateSize>: ArraySize, +{ + type SerializedStateSize = Sum; + + fn serialize(&self) -> SerializedState { + let serialized_inner = self.inner.serialize(); + let serialized_outsize = ByteArray::::clone_from_slice(&[OutSize::U8]); + + serialized_inner.concat(serialized_outsize) + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_inner, serialized_outsize) = + serialized_state.split_ref::(); + + if serialized_outsize[0] != OutSize::U8 { + return Err(DeserializeStateError); + } + + Ok(Self { + inner: T::deserialize(serialized_inner)?, + _out: PhantomData, + }) + } +} diff --git a/digest/src/core_api/rt_variable.rs b/digest/src/core_api/rt_variable.rs index 94708ee77..8d10f3db7 100644 --- a/digest/src/core_api/rt_variable.rs +++ b/digest/src/core_api/rt_variable.rs @@ -1,11 +1,19 @@ -use super::{AlgorithmName, TruncSide, UpdateCore, VariableOutputCore}; +use super::{AlgorithmName, BlockSizeUser, TruncSide, UpdateCore, VariableOutputCore}; #[cfg(feature = "mac")] use crate::MacMarker; use crate::{HashMarker, InvalidBufferSize}; use crate::{InvalidOutputSize, Reset, Update, VariableOutput, VariableOutputReset}; use block_buffer::BlockBuffer; -use core::fmt; -use crypto_common::typenum::Unsigned; +use core::{ + convert::TryInto, + fmt, + ops::{Add, Sub}, +}; +use crypto_common::{ + array::{ArraySize, ByteArray}, + typenum::{Diff, IsLess, Le, NonZero, Sum, Unsigned, U1, U256}, + DeserializeStateError, SerializableState, SerializedState, +}; /// Wrapper around [`VariableOutputCore`] which selects output size /// at run time. @@ -51,6 +59,15 @@ impl HashMarker for RtVariableCoreWrapper where T: VariableOutputCore + Ha #[cfg_attr(docsrs, doc(cfg(feature = "mac")))] impl MacMarker for RtVariableCoreWrapper where T: VariableOutputCore + MacMarker {} +impl BlockSizeUser for RtVariableCoreWrapper +where + T: VariableOutputCore, + T::BlockSize: IsLess, + Le: NonZero, +{ + type BlockSize = T::BlockSize; +} + impl Reset for RtVariableCoreWrapper where T: VariableOutputCore + UpdateCore + Reset, @@ -119,6 +136,67 @@ where } } +impl SerializableState for RtVariableCoreWrapper +where + T: VariableOutputCore + UpdateCore + SerializableState, + T::BlockSize: IsLess, + Le: NonZero, + T::SerializedStateSize: Add, + Sum: Add + ArraySize, + Sum, T::BlockSize>: Add + ArraySize, + Sum, T::BlockSize>, U1>: + Sub + ArraySize, + Diff, T::BlockSize>, U1>, T::SerializedStateSize>: + Sub + ArraySize, + Diff< + Diff, T::BlockSize>, U1>, T::SerializedStateSize>, + U1, + >: Sub + ArraySize, + Diff< + Diff< + Diff< + Sum, T::BlockSize>, U1>, + T::SerializedStateSize, + >, + U1, + >, + T::BlockSize, + >: ArraySize, +{ + type SerializedStateSize = Sum, T::BlockSize>, U1>; + + fn serialize(&self) -> SerializedState { + let serialized_core = self.core.serialize(); + let serialized_pos = + ByteArray::::clone_from_slice(&[self.buffer.get_pos().try_into().unwrap()]); + let serialized_data = self.buffer.clone().pad_with_zeros(); + let serialized_output_size = + ByteArray::::clone_from_slice(&[self.output_size.try_into().unwrap()]); + + serialized_core + .concat(serialized_pos) + .concat(serialized_data) + .concat(serialized_output_size) + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_core, remaining_buffer) = + serialized_state.split_ref::(); + let (serialized_pos, remaining_buffer) = remaining_buffer.split_ref::(); + let (serialized_data, serialized_output_size) = + remaining_buffer.split_ref::(); + + Ok(Self { + core: T::deserialize(serialized_core)?, + buffer: BlockBuffer::try_new(&serialized_data[..serialized_pos[0].into()]) + .map_err(|_| DeserializeStateError)?, + output_size: serialized_output_size[0].into(), + }) + } +} + #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::io::Write for RtVariableCoreWrapper diff --git a/digest/src/core_api/wrapper.rs b/digest/src/core_api/wrapper.rs index af77b11c8..ddb899c10 100644 --- a/digest/src/core_api/wrapper.rs +++ b/digest/src/core_api/wrapper.rs @@ -6,8 +6,17 @@ use crate::{ ExtendableOutput, ExtendableOutputReset, FixedOutput, FixedOutputReset, HashMarker, Update, }; use block_buffer::BlockBuffer; -use core::fmt; -use crypto_common::{BlockSizeUser, InvalidLength, Key, KeyInit, KeySizeUser, Output}; +use core::{ + convert::TryInto, + fmt, + ops::{Add, Sub}, +}; +use crypto_common::{ + array::{ArraySize, ByteArray}, + typenum::{Diff, IsLess, Le, NonZero, Sum, U1, U256}, + BlockSizeUser, DeserializeStateError, InvalidLength, Key, KeyInit, KeySizeUser, Output, + SerializableState, SerializedState, +}; #[cfg(feature = "mac")] use crate::MacMarker; @@ -193,6 +202,47 @@ where const OID: ObjectIdentifier = T::OID; } +impl SerializableState for CoreWrapper +where + T: BufferKindUser + SerializableState, + T::BlockSize: IsLess, + Le: NonZero, + T::SerializedStateSize: Add, + Sum: Add + ArraySize, + Sum, T::BlockSize>: Sub + ArraySize, + Diff, T::BlockSize>, T::SerializedStateSize>: + Sub + ArraySize, + Diff, T::BlockSize>, T::SerializedStateSize>, U1>: + ArraySize, +{ + type SerializedStateSize = Sum, T::BlockSize>; + + fn serialize(&self) -> SerializedState { + let serialized_core = self.core.serialize(); + let serialized_pos = + ByteArray::::clone_from_slice(&[self.buffer.get_pos().try_into().unwrap()]); + let serialized_data = self.buffer.clone().pad_with_zeros(); + + serialized_core + .concat(serialized_pos) + .concat(serialized_data) + } + + fn deserialize( + serialized_state: &SerializedState, + ) -> Result { + let (serialized_core, remaining_buffer) = + serialized_state.split_ref::(); + let (serialized_pos, serialized_data) = remaining_buffer.split_ref::(); + + Ok(Self { + core: T::deserialize(serialized_core)?, + buffer: BlockBuffer::try_new(&serialized_data[..serialized_pos[0].into()]) + .map_err(|_| DeserializeStateError)?, + }) + } +} + #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::io::Write for CoreWrapper diff --git a/digest/src/dev.rs b/digest/src/dev.rs index 438089574..8f2ba8be6 100644 --- a/digest/src/dev.rs +++ b/digest/src/dev.rs @@ -39,6 +39,77 @@ macro_rules! new_test { }; } +/// Define hash function serialization test +#[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] +macro_rules! hash_serialization_test { + ($name:ident, $hasher:ty, $expected_serialized_state:expr) => { + #[test] + fn $name() { + use digest::{ + crypto_common::{BlockSizeUser, SerializableState}, + typenum::Unsigned, + Digest, + }; + + let mut h = <$hasher>::new(); + + h.update(&[0x13; <$hasher as BlockSizeUser>::BlockSize::USIZE + 1]); + + let serialized_state = h.serialize(); + assert_eq!(serialized_state.as_slice(), $expected_serialized_state); + + let mut h = <$hasher>::deserialize(&serialized_state).unwrap(); + + h.update(&[0x13; <$hasher as BlockSizeUser>::BlockSize::USIZE + 1]); + let output1 = h.finalize(); + + let mut h = <$hasher>::new(); + h.update(&[0x13; 2 * (<$hasher as BlockSizeUser>::BlockSize::USIZE + 1)]); + let output2 = h.finalize(); + + assert_eq!(output1, output2); + } + }; +} + +/// Define hash function serialization test +#[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] +macro_rules! hash_rt_outsize_serialization_test { + ($name:ident, $hasher:ty, $expected_serialized_state:expr) => { + #[test] + fn $name() { + use digest::{ + crypto_common::{BlockSizeUser, SerializableState}, + typenum::Unsigned, + Digest, Update, VariableOutput, + }; + const HASH_OUTPUT_SIZE: usize = <$hasher>::MAX_OUTPUT_SIZE - 1; + + let mut h = <$hasher>::new(HASH_OUTPUT_SIZE).unwrap(); + + h.update(&[0x13; <$hasher as BlockSizeUser>::BlockSize::USIZE + 1]); + + let serialized_state = h.serialize(); + assert_eq!(serialized_state.as_slice(), $expected_serialized_state); + + let mut h = <$hasher>::deserialize(&serialized_state).unwrap(); + + h.update(&[0x13; <$hasher as BlockSizeUser>::BlockSize::USIZE + 1]); + let mut output1 = [0; HASH_OUTPUT_SIZE]; + h.finalize_variable(&mut output1).unwrap(); + + let mut h = <$hasher>::new(HASH_OUTPUT_SIZE).unwrap(); + h.update(&[0x13; 2 * (<$hasher as BlockSizeUser>::BlockSize::USIZE + 1)]); + let mut output2 = [0; HASH_OUTPUT_SIZE]; + h.finalize_variable(&mut output2).unwrap(); + + assert_eq!(output1, output2); + } + }; +} + /// Define [`Update`][crate::Update] impl benchmark #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "dev")))] diff --git a/digest/src/digest.rs b/digest/src/digest.rs index 9373550ca..29aeb5dac 100644 --- a/digest/src/digest.rs +++ b/digest/src/digest.rs @@ -196,21 +196,15 @@ impl DynDigest for D { } fn finalize_into(self, buf: &mut [u8]) -> Result<(), InvalidBufferSize> { - if buf.len() == self.output_size() { - FixedOutput::finalize_into(self, Output::::from_mut_slice(buf)); - Ok(()) - } else { - Err(InvalidBufferSize) - } + let buf = <&mut Output>::try_from(buf).map_err(|_| InvalidBufferSize)?; + FixedOutput::finalize_into(self, buf); + Ok(()) } fn finalize_into_reset(&mut self, buf: &mut [u8]) -> Result<(), InvalidBufferSize> { - if buf.len() == self.output_size() { - FixedOutputReset::finalize_into_reset(self, Output::::from_mut_slice(buf)); - Ok(()) - } else { - Err(InvalidBufferSize) - } + let buf = <&mut Output>::try_from(buf).map_err(|_| InvalidBufferSize)?; + FixedOutputReset::finalize_into_reset(self, buf); + Ok(()) } fn reset(&mut self) { diff --git a/digest/src/lib.rs b/digest/src/lib.rs index 04ea98931..911133d7e 100644 --- a/digest/src/lib.rs +++ b/digest/src/lib.rs @@ -65,7 +65,7 @@ pub use const_oid; pub use crypto_common; pub use crate::digest::{Digest, DynDigest, HashMarker}; -pub use crypto_common::{generic_array, typenum, typenum::consts, Output, OutputSizeUser, Reset}; +pub use crypto_common::{array, typenum, typenum::consts, Output, OutputSizeUser, Reset}; #[cfg(feature = "mac")] pub use crypto_common::{InnerInit, InvalidLength, Key, KeyInit}; #[cfg(feature = "mac")]