diff --git a/Cargo.lock b/Cargo.lock index cd3013409..e9d2d6bfa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,9 +93,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cipher" @@ -104,6 +104,7 @@ dependencies = [ "blobby", "block-buffer", "crypto-common", + "hex-literal", "inout", "zeroize", ] @@ -136,9 +137,9 @@ dependencies = [ [[package]] name = "crypto-bigint" -version = "0.7.0-rc.8" +version = "0.7.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4113edbc9f68c0a64d5b911f803eb245d04bb812680fd56776411f69c670f3e0" +checksum = "7f4b0fda9462026d53a3ef37c5ec283639ee8494a1a5401109c0e2a3fb4d490c" dependencies = [ "hybrid-array", "num-traits", @@ -235,14 +236,14 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi", + "wasip2", ] [[package]] @@ -390,9 +391,9 @@ checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -467,7 +468,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.108", ] [[package]] @@ -517,9 +518,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" dependencies = [ "proc-macro2", "quote", @@ -534,15 +535,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06" [[package]] name = "universal-hash" @@ -552,15 +553,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "wasi" -version = "0.14.7+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" -dependencies = [ - "wasip2", -] - [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" diff --git a/cipher/Cargo.toml b/cipher/Cargo.toml index abb3bb548..083fa4797 100644 --- a/cipher/Cargo.toml +++ b/cipher/Cargo.toml @@ -21,6 +21,9 @@ blobby = { version = "0.4.0-pre.1", optional = true } block-buffer = { version = "0.11.0-rc.5", optional = true } zeroize = { version = "1.8", optional = true, default-features = false } +[dev-dependencies] +hex-literal = "1" + [features] alloc = [] block-padding = ["inout/block-padding"] diff --git a/cipher/tests/data/dummy_stream_cipher.blb b/cipher/tests/data/dummy_stream_cipher.blb new file mode 100644 index 000000000..62d7c158a Binary files /dev/null and b/cipher/tests/data/dummy_stream_cipher.blb differ diff --git a/cipher/tests/stream.rs b/cipher/tests/stream.rs new file mode 100644 index 000000000..359e0f7d4 --- /dev/null +++ b/cipher/tests/stream.rs @@ -0,0 +1,160 @@ +use cipher::{ + BlockSizeUser, IvSizeUser, KeyIvInit, KeySizeUser, ParBlocksSizeUser, StreamCipherBackend, + StreamCipherClosure, StreamCipherCore, StreamCipherSeekCore, + consts::{U1, U4, U16}, +}; +use hex_literal::hex; + +const KEY: [u8; 4] = hex!("00010203"); +const IV: [u8; 4] = hex!("04050607"); + +/// Core of dummy insecure stream cipher. +pub struct DummyStreamCipherCore { + key_iv: u64, + pos: u64, +} + +impl KeySizeUser for DummyStreamCipherCore { + type KeySize = U4; +} + +impl IvSizeUser for DummyStreamCipherCore { + type IvSize = U4; +} + +impl KeyIvInit for DummyStreamCipherCore { + fn new(key: &cipher::Key, iv: &cipher::Iv) -> Self { + let mut key_iv = [0u8; 8]; + key_iv[..4].copy_from_slice(key); + key_iv[4..].copy_from_slice(iv); + Self { + key_iv: u64::from_le_bytes(key_iv), + pos: 0, + } + } +} + +impl BlockSizeUser for DummyStreamCipherCore { + type BlockSize = U16; +} + +impl StreamCipherCore for DummyStreamCipherCore { + fn remaining_blocks(&self) -> Option { + let rem = u64::MAX - self.pos; + usize::try_from(rem).ok() + } + + fn process_with_backend(&mut self, f: impl StreamCipherClosure) { + f.call(self); + } +} + +impl ParBlocksSizeUser for DummyStreamCipherCore { + type ParBlocksSize = U1; +} + +impl StreamCipherBackend for DummyStreamCipherCore { + fn gen_ks_block(&mut self, block: &mut cipher::Block) { + const C1: u64 = 0x87c3_7b91_1142_53d5; + const C2: u64 = 0x4cf5_ad43_2745_937f; + + let a = self.key_iv ^ C1; + let b = self.pos ^ C2; + let a = a.rotate_left(13).wrapping_mul(b); + let b = b.rotate_left(13).wrapping_mul(a); + + block[..8].copy_from_slice(&a.to_le_bytes()); + block[8..].copy_from_slice(&b.to_le_bytes()); + self.pos = self.pos.wrapping_add(1); + } +} + +impl StreamCipherSeekCore for DummyStreamCipherCore { + type Counter = u64; + + fn get_block_pos(&self) -> Self::Counter { + self.pos + } + + fn set_block_pos(&mut self, pos: Self::Counter) { + self.pos = pos; + } +} + +#[test] +fn dummy_stream_cipher_core() { + let mut cipher = DummyStreamCipherCore::new(&KEY.into(), &IV.into()); + assert_eq!(cipher.get_block_pos(), 0); + + let mut block = [0u8; 16].into(); + cipher.write_keystream_block(&mut block); + assert_eq!(block, hex!("e82393543cc96089305116003a417acc")); + assert_eq!(cipher.get_block_pos(), 1); + + cipher.set_block_pos(200); + assert_eq!(cipher.get_block_pos(), 200); + + cipher.write_keystream_block(&mut block); + assert_eq!(block, hex!("28a96998fe4874ffb0ce9b046c6a9ddb")); + assert_eq!(cipher.get_block_pos(), 201); +} + +#[cfg(feature = "stream-wrapper")] +mod wrapper { + use super::*; + use cipher::{StreamCipher, StreamCipherCoreWrapper, StreamCipherSeek}; + + /// Dummy insecure stream cipher. + pub type DummyStreamCipher = StreamCipherCoreWrapper; + + #[test] + fn dummy_stream_cipher_basic() { + let mut cipher = DummyStreamCipher::new(&KEY.into(), &IV.into()); + assert_eq!(cipher.current_pos::(), 0); + + let mut buf = [0u8; 20]; + cipher.apply_keystream(&mut buf); + assert_eq!(buf, hex!("e82393543cc96089305116003a417accd073384a")); + assert_eq!(cipher.current_pos::(), buf.len()); + + const SEEK_POS: usize = 500; + cipher.seek(SEEK_POS); + cipher.apply_keystream(&mut buf); + assert_eq!(buf, hex!("6b014c6a3c376b13c4720590d26147c5ebf334c5")); + assert_eq!(cipher.current_pos::(), SEEK_POS + buf.len()); + } + + #[test] + fn dummy_stream_cipher_seek_limit() { + let mut cipher = DummyStreamCipher::new(&KEY.into(), &IV.into()); + + let pos = ((u64::MAX as u128) << 4) - 20; + cipher.try_seek(pos).unwrap(); + + let mut buf = [0u8; 30]; + let res = cipher.try_apply_keystream(&mut buf); + assert!(res.is_err()); + let cur_pos: u128 = cipher.current_pos(); + assert_eq!(cur_pos, pos); + + let res = cipher.try_apply_keystream(&mut buf[..19]); + assert!(res.is_ok()); + let cur_pos: u128 = cipher.current_pos(); + assert_eq!(cur_pos, pos + 19); + + cipher.try_seek(pos).unwrap(); + + // TODO: fix as part of https://github.com/RustCrypto/traits/issues/1808 + // let res = cipher.try_apply_keystream(&mut buf[..20]); + // assert!(res.is_err()); + } + + #[cfg(feature = "dev")] + cipher::stream_cipher_test!( + dummy_stream_cipher, + "dummy_stream_cipher", + DummyStreamCipher, + ); + #[cfg(feature = "dev")] + cipher::stream_cipher_seek_test!(dummy_stream_cipher_seek, DummyStreamCipher); +}