diff --git a/Cargo.lock b/Cargo.lock index ae3f266..3ade1ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,7 +24,7 @@ dependencies = [ [[package]] name = "belt-ctr" -version = "0.2.0" +version = "0.2.1" dependencies = [ "belt-block", "cipher", @@ -58,7 +58,7 @@ dependencies = [ [[package]] name = "cbc" -version = "0.2.0" +version = "0.2.1" dependencies = [ "aes", "cipher", @@ -67,7 +67,7 @@ dependencies = [ [[package]] name = "cfb-mode" -version = "0.9.0" +version = "0.9.1" dependencies = [ "aes", "belt-block", @@ -77,7 +77,7 @@ dependencies = [ [[package]] name = "cfb8" -version = "0.9.0" +version = "0.9.1" dependencies = [ "aes", "cipher", @@ -92,9 +92,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cipher" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" dependencies = [ "blobby", "block-buffer", @@ -105,9 +105,9 @@ dependencies = [ [[package]] name = "cpubits" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef0c543070d296ea414df2dd7625d1b24866ce206709d8a4a424f28377f5861" +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" [[package]] name = "cpufeatures" @@ -120,16 +120,16 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ "hybrid-array", ] [[package]] name = "ctr" -version = "0.10.0" +version = "0.10.1" dependencies = [ "aes", "cipher", @@ -156,9 +156,9 @@ checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1" [[package]] name = "hybrid-array" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" dependencies = [ "typenum", "zeroize", @@ -166,7 +166,7 @@ dependencies = [ [[package]] name = "ige" -version = "0.2.0" +version = "0.2.1" dependencies = [ "aes", "cipher", @@ -195,9 +195,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.184" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "magma" @@ -210,7 +210,7 @@ dependencies = [ [[package]] name = "ofb" -version = "0.7.0" +version = "0.7.1" dependencies = [ "aes", "cipher", @@ -219,7 +219,7 @@ dependencies = [ [[package]] name = "pcbc" -version = "0.2.0" +version = "0.2.1" dependencies = [ "aes", "cipher", @@ -228,9 +228,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "zeroize" diff --git a/belt-ctr/CHANGELOG.md b/belt-ctr/CHANGELOG.md index 5acaf15..02d0775 100644 --- a/belt-ctr/CHANGELOG.md +++ b/belt-ctr/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.2.1 (2026-05-20) +### Added +- Implementation of the `SetIvState` trait ([#114]) + +[#114]: https://github.com/RustCrypto/block-modes/pull/114 + ## 0.2.0 (2026-04-10) ## Added - `GenericBeltCtr` and `GenericBeltCtrCore` types ([#112]) diff --git a/belt-ctr/Cargo.toml b/belt-ctr/Cargo.toml index ac2d29a..a7ee804 100644 --- a/belt-ctr/Cargo.toml +++ b/belt-ctr/Cargo.toml @@ -1,24 +1,24 @@ [package] name = "belt-ctr" -version = "0.2.0" -description = "CTR block mode of operation specified by the BelT standard" +version = "0.2.1" authors = ["RustCrypto Developers"] -license = "MIT OR Apache-2.0" edition = "2024" rust-version = "1.85" -readme = "README.md" documentation = "https://docs.rs/belt-ctr" +readme = "README.md" repository = "https://github.com/RustCrypto/block-modes" +license = "MIT OR Apache-2.0" keywords = ["crypto", "block-mode", "stream-cipher", "ciphers", "belt"] categories = ["cryptography", "no-std"] +description = "CTR block mode of operation specified by the BelT standard" [dependencies] -cipher = { version = "0.5", features = ["stream-wrapper"] } +cipher = { version = "0.5.2", features = ["stream-wrapper"] } belt-block = "0.2" [dev-dependencies] +cipher = { version = "0.5.2", features = ["dev"] } hex-literal = "1" -cipher = { version = "0.5", features = ["dev"] } [features] alloc = ["cipher/alloc"] diff --git a/belt-ctr/src/lib.rs b/belt-ctr/src/lib.rs index 70b0ca6..807ac73 100644 --- a/belt-ctr/src/lib.rs +++ b/belt-ctr/src/lib.rs @@ -12,7 +12,7 @@ use belt_block::BeltBlock; use cipher::{ AlgorithmName, Block, BlockCipherDecrypt, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockSizeUser, InOut, InnerIvInit, Iv, IvSizeUser, IvState, ParBlocks, - ParBlocksSizeUser, StreamCipherBackend, StreamCipherClosure, StreamCipherCore, + ParBlocksSizeUser, SetIvState, StreamCipherBackend, StreamCipherClosure, StreamCipherCore, StreamCipherCoreWrapper, StreamCipherSeekCore, array::Array, common::InnerUser, consts::U16, }; use core::fmt; @@ -41,11 +41,13 @@ impl StreamCipherCore for GenericBeltCtrCore where C: BlockCipherEncrypt + BlockSizeUser, { + #[inline] fn remaining_blocks(&self) -> Option { let used = self.s.wrapping_sub(self.s_init); (u128::MAX - used).try_into().ok() } + #[inline] fn process_with_backend(&mut self, f: impl StreamCipherClosure) { struct Closure<'a, C: StreamCipherClosure> { s: &'a mut u128, @@ -75,10 +77,12 @@ where { type Counter = u128; + #[inline] fn get_block_pos(&self) -> Self::Counter { self.s.wrapping_sub(self.s_init) } + #[inline] fn set_block_pos(&mut self, pos: Self::Counter) { self.s = self.s_init.wrapping_add(pos); } @@ -126,6 +130,7 @@ impl IvState for GenericBeltCtrCore where C: BlockCipherEncrypt + BlockCipherDecrypt + BlockSizeUser, { + #[inline] fn iv_state(&self) -> Iv { let mut t = self.s.to_le_bytes().into(); self.cipher.decrypt_block(&mut t); @@ -133,10 +138,23 @@ where } } +impl SetIvState for GenericBeltCtrCore +where + C: BlockCipherEncrypt + BlockCipherDecrypt + BlockSizeUser, +{ + #[inline] + fn set_iv(&mut self, iv: &Iv) { + let mut iv = *iv; + self.cipher.encrypt_block(&mut iv); + self.s = u128::from_le_bytes(iv.0); + } +} + impl AlgorithmName for GenericBeltCtrCore where C: BlockCipherEncrypt + BlockSizeUser + AlgorithmName, { + #[inline] fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("BeltCtr<")?; ::write_alg_name(f)?; @@ -160,6 +178,7 @@ impl Drop for GenericBeltCtrCore where C: BlockCipherEncrypt + BlockSizeUser, { + #[inline] fn drop(&mut self) { #[cfg(feature = "zeroize")] { diff --git a/belt-ctr/tests/iv_state.rs b/belt-ctr/tests/iv_state.rs new file mode 100644 index 0000000..eefd835 --- /dev/null +++ b/belt-ctr/tests/iv_state.rs @@ -0,0 +1,33 @@ +//! Basic tests for `IvState` and `SetIvState` trait impls +use belt_ctr::BeltCtrCore; +use cipher::{IvState, KeyIvInit, SetIvState, StreamCipherCore}; + +cipher::iv_state_test!(belt_ctr_iv_state, BeltCtrCore, apply_ks); + +#[test] +fn belt_ctr_set_iv() { + let key = Default::default(); + let iv = Default::default(); + let mut mode = BeltCtrCore::new(&key, &iv); + + let mut blocks = [Default::default(); 16]; + + mode.apply_keystream_blocks(&mut blocks); + let iv = mode.iv_state(); + + let mut buf1 = blocks; + let mut buf2 = blocks; + + mode.peek(|m| m.apply_keystream_blocks(&mut buf1)); + assert_eq!(mode.iv_state(), iv); + + mode.apply_keystream_blocks(&mut blocks); + let iv2 = mode.iv_state(); + + mode.set_iv(&iv); + mode.apply_keystream_blocks(&mut buf2); + + assert_eq!(blocks, buf1); + assert_eq!(blocks, buf2); + assert_eq!(mode.iv_state(), iv2); +} diff --git a/belt-ctr/tests/mod.rs b/belt-ctr/tests/mod.rs index f4d50de..3cfe811 100644 --- a/belt-ctr/tests/mod.rs +++ b/belt-ctr/tests/mod.rs @@ -1,7 +1,6 @@ //! Test vectors from the BelT standard (tables A.15 and A.16): //! https://apmi.bsu.by/assets/files/std/belt-spec371.pdf -use belt_ctr::{BeltCtr, BeltCtrCore}; +use belt_ctr::BeltCtr; cipher::stream_cipher_test!(belt_ctr_core, "belt-ctr", BeltCtr); cipher::stream_cipher_seek_test!(belt_ctr_seek, BeltCtr); -cipher::iv_state_test!(belt_ctr_iv_state, BeltCtrCore, apply_ks); diff --git a/cbc/CHANGELOG.md b/cbc/CHANGELOG.md index 4d4bdb4..5393618 100644 --- a/cbc/CHANGELOG.md +++ b/cbc/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.2.1 (2026-05-20) +### Added +- Implementation of the `SetIvState` trait ([#114]) + +[#114]: https://github.com/RustCrypto/block-modes/pull/114 + ## 0.2.0 (2026-04-10) ### Removed - `std` feature ([#76]) diff --git a/cbc/Cargo.toml b/cbc/Cargo.toml index 446c20a..d052373 100644 --- a/cbc/Cargo.toml +++ b/cbc/Cargo.toml @@ -1,24 +1,24 @@ [package] name = "cbc" -version = "0.2.0" -description = "Cipher Block Chaining (CBC) block cipher mode of operation" +version = "0.2.1" authors = ["RustCrypto Developers"] -license = "MIT OR Apache-2.0" edition = "2024" rust-version = "1.85" -readme = "README.md" documentation = "https://docs.rs/cbc" +readme = "README.md" repository = "https://github.com/RustCrypto/block-modes" +license = "MIT OR Apache-2.0" keywords = ["crypto", "block-mode", "ciphers"] categories = ["cryptography", "no-std"] +description = "Cipher Block Chaining (CBC) block cipher mode of operation" [dependencies] -cipher = "0.5" +cipher = "0.5.2" [dev-dependencies] -aes = "0.9" -cipher = { version = "0.5", features = ["dev"] } +cipher = { version = "0.5.2", features = ["dev"] } hex-literal = "1" +aes = "0.9" [features] default = ["block-padding"] diff --git a/cbc/src/decrypt.rs b/cbc/src/decrypt.rs index 46cd727..17d05c2 100644 --- a/cbc/src/decrypt.rs +++ b/cbc/src/decrypt.rs @@ -2,7 +2,7 @@ use crate::xor; use cipher::{ AlgorithmName, Block, BlockCipherDecBackend, BlockCipherDecClosure, BlockCipherDecrypt, BlockModeDecBackend, BlockModeDecClosure, BlockModeDecrypt, BlockSizeUser, InnerIvInit, Iv, - IvState, ParBlocks, ParBlocksSizeUser, + IvState, ParBlocks, ParBlocksSizeUser, SetIvState, array::{Array, ArraySize}, common::{InnerUser, IvSizeUser}, inout::InOut, @@ -107,6 +107,16 @@ where } } +impl SetIvState for Decryptor +where + C: BlockCipherDecrypt, +{ + #[inline] + fn set_iv(&mut self, iv: &Iv) { + self.iv = iv.clone(); + } +} + impl AlgorithmName for Decryptor where C: BlockCipherDecrypt + AlgorithmName, diff --git a/cbc/src/encrypt.rs b/cbc/src/encrypt.rs index 475c1f9..ae02db7 100644 --- a/cbc/src/encrypt.rs +++ b/cbc/src/encrypt.rs @@ -2,7 +2,8 @@ use crate::xor; use cipher::{ AlgorithmName, Array, Block, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockModeEncBackend, BlockModeEncClosure, BlockModeEncrypt, BlockSizeUser, InOut, InnerIvInit, - Iv, IvSizeUser, IvState, ParBlocksSizeUser, array::ArraySize, common::InnerUser, consts::U1, + Iv, IvSizeUser, IvState, ParBlocksSizeUser, SetIvState, array::ArraySize, common::InnerUser, + consts::U1, }; use core::fmt; @@ -62,6 +63,16 @@ where } } +impl SetIvState for Encryptor +where + C: BlockCipherEncrypt, +{ + #[inline] + fn set_iv(&mut self, iv: &Iv) { + self.iv = iv.clone(); + } +} + impl BlockModeEncrypt for Encryptor where C: BlockCipherEncrypt, diff --git a/cbc/tests/iv_state.rs b/cbc/tests/iv_state.rs index ce550f6..dfb533e 100644 --- a/cbc/tests/iv_state.rs +++ b/cbc/tests/iv_state.rs @@ -2,7 +2,7 @@ use aes::*; use cbc::{Decryptor, Encryptor}; -use cipher::iv_state_test; +use cipher::{BlockModeDecrypt, BlockModeEncrypt, IvState, KeyIvInit, SetIvState, iv_state_test}; iv_state_test!(aes128_cbc_enc_iv_state, Encryptor, encrypt); iv_state_test!(aes128_cbc_dec_iv_state, Decryptor, decrypt); @@ -10,3 +10,59 @@ iv_state_test!(aes192_cbc_enc_iv_state, Encryptor, encrypt); iv_state_test!(aes192_cbc_dec_iv_state, Decryptor, decrypt); iv_state_test!(aes256_cbc_enc_iv_state, Encryptor, encrypt); iv_state_test!(aes256_cbc_dec_iv_state, Decryptor, decrypt); + +#[test] +fn aes128_cbc_encrypt_set_iv() { + let key = Default::default(); + let iv = Default::default(); + let mut mode = Encryptor::::new(&key, &iv); + + let mut blocks = [Default::default(); 16]; + + mode.encrypt_blocks(&mut blocks); + let iv = mode.iv_state(); + + let mut buf1 = blocks; + let mut buf2 = blocks; + + mode.peek(|m| m.encrypt_blocks(&mut buf1)); + assert_eq!(mode.iv_state(), iv); + + mode.encrypt_blocks(&mut blocks); + let iv2 = mode.iv_state(); + + mode.set_iv(&iv); + mode.encrypt_blocks(&mut buf2); + + assert_eq!(blocks, buf1); + assert_eq!(blocks, buf2); + assert_eq!(mode.iv_state(), iv2); +} + +#[test] +fn aes128_cbc_decrypt_set_iv() { + let key = Default::default(); + let iv = Default::default(); + let mut mode = Decryptor::::new(&key, &iv); + + let mut blocks = [Default::default(); 16]; + + mode.decrypt_blocks(&mut blocks); + let iv = mode.iv_state(); + + let mut buf1 = blocks; + let mut buf2 = blocks; + + mode.peek(|m| m.decrypt_blocks(&mut buf1)); + assert_eq!(mode.iv_state(), iv); + + mode.decrypt_blocks(&mut blocks); + let iv2 = mode.iv_state(); + + mode.set_iv(&iv); + mode.decrypt_blocks(&mut buf2); + + assert_eq!(blocks, buf1); + assert_eq!(blocks, buf2); + assert_eq!(mode.iv_state(), iv2); +} diff --git a/cfb-mode/CHANGELOG.md b/cfb-mode/CHANGELOG.md index 9642598..70803c8 100644 --- a/cfb-mode/CHANGELOG.md +++ b/cfb-mode/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.9.1 (2026-05-20) +### Added +- Implementation of the `SetIvState` trait ([#114]) + +[#114]: https://github.com/RustCrypto/block-modes/pull/114 + ## 0.9.0 (2026-04-10) ### Removed - `std` feature ([#76]) diff --git a/cfb-mode/Cargo.toml b/cfb-mode/Cargo.toml index b61eec6..23197df 100644 --- a/cfb-mode/Cargo.toml +++ b/cfb-mode/Cargo.toml @@ -1,25 +1,25 @@ [package] name = "cfb-mode" -version = "0.9.0" -description = "Cipher Feedback (CFB) block cipher mode of operation" +version = "0.9.1" authors = ["RustCrypto Developers"] -license = "MIT OR Apache-2.0" edition = "2024" rust-version = "1.85" -readme = "README.md" documentation = "https://docs.rs/cfb-mode" +readme = "README.md" repository = "https://github.com/RustCrypto/block-modes" +license = "MIT OR Apache-2.0" keywords = ["crypto", "block-mode", "stream-cipher", "ciphers"] categories = ["cryptography", "no-std"] +description = "Cipher Feedback (CFB) block cipher mode of operation" [dependencies] -cipher = "0.5" +cipher = "0.5.2" [dev-dependencies] +cipher = { version = "0.5.2", features = ["dev"] } +hex-literal = "1" aes = "0.9" belt-block = "0.2" -cipher = { version = "0.5", features = ["dev"] } -hex-literal = "1" [features] alloc = ["cipher/alloc"] diff --git a/cfb-mode/src/decrypt.rs b/cfb-mode/src/decrypt.rs index 9f5ac6c..9a5334c 100644 --- a/cfb-mode/src/decrypt.rs +++ b/cfb-mode/src/decrypt.rs @@ -1,7 +1,7 @@ use cipher::{ AlgorithmName, Array, Block, BlockCipherDecrypt, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockModeDecBackend, BlockModeDecClosure, BlockModeDecrypt, BlockSizeUser, - InnerIvInit, Iv, IvSizeUser, IvState, ParBlocks, ParBlocksSizeUser, + InnerIvInit, Iv, IvSizeUser, IvState, ParBlocks, ParBlocksSizeUser, SetIvState, array::ArraySize, common::InnerUser, inout::{InOut, InOutBuf, NotEqualError}, @@ -256,6 +256,17 @@ where } } +impl SetIvState for Decryptor +where + C: BlockCipherEncrypt + BlockCipherDecrypt, +{ + #[inline] + fn set_iv(&mut self, iv: &Iv) { + self.iv = iv.clone(); + self.cipher.encrypt_block(&mut self.iv); + } +} + impl AlgorithmName for Decryptor where C: BlockCipherEncrypt + AlgorithmName, diff --git a/cfb-mode/src/encrypt.rs b/cfb-mode/src/encrypt.rs index ad43064..5b05de7 100644 --- a/cfb-mode/src/encrypt.rs +++ b/cfb-mode/src/encrypt.rs @@ -1,7 +1,7 @@ use cipher::{ AlgorithmName, Block, BlockCipherDecrypt, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockModeEncBackend, BlockModeEncClosure, BlockModeEncrypt, BlockSizeUser, - InnerIvInit, Iv, IvSizeUser, IvState, ParBlocksSizeUser, + InnerIvInit, Iv, IvSizeUser, IvState, ParBlocksSizeUser, SetIvState, array::{Array, ArraySize}, common::InnerUser, consts::U1, @@ -144,6 +144,17 @@ where } } +impl SetIvState for Encryptor +where + C: BlockCipherEncrypt + BlockCipherDecrypt, +{ + #[inline] + fn set_iv(&mut self, iv: &Iv) { + self.iv = iv.clone(); + self.cipher.encrypt_block(&mut self.iv); + } +} + impl AlgorithmName for Encryptor where C: BlockCipherEncrypt + AlgorithmName, diff --git a/cfb-mode/tests/iv_state.rs b/cfb-mode/tests/iv_state.rs index 6ee6fb4..9649b5a 100644 --- a/cfb-mode/tests/iv_state.rs +++ b/cfb-mode/tests/iv_state.rs @@ -2,7 +2,7 @@ use aes::*; use cfb_mode::{Decryptor, Encryptor}; -use cipher::iv_state_test; +use cipher::{BlockModeDecrypt, BlockModeEncrypt, IvState, KeyIvInit, SetIvState, iv_state_test}; iv_state_test!(aes128_cfb_enc_iv_state, Encryptor, encrypt); iv_state_test!(aes128_cfb_dec_iv_state, Decryptor, decrypt); @@ -10,3 +10,59 @@ iv_state_test!(aes192_cfb_enc_iv_state, Encryptor, encrypt); iv_state_test!(aes192_cfb_dec_iv_state, Decryptor, decrypt); iv_state_test!(aes256_cfb_enc_iv_state, Encryptor, encrypt); iv_state_test!(aes256_cfb_dec_iv_state, Decryptor, decrypt); + +#[test] +fn aes128_cfb_encrypt_set_iv() { + let key = Default::default(); + let iv = Default::default(); + let mut mode = Encryptor::::new(&key, &iv); + + let mut blocks = [Default::default(); 16]; + + mode.encrypt_blocks(&mut blocks); + let iv = mode.iv_state(); + + let mut buf1 = blocks; + let mut buf2 = blocks; + + mode.peek(|m| m.encrypt_blocks(&mut buf1)); + assert_eq!(mode.iv_state(), iv); + + mode.encrypt_blocks(&mut blocks); + let iv2 = mode.iv_state(); + + mode.set_iv(&iv); + mode.encrypt_blocks(&mut buf2); + + assert_eq!(blocks, buf1); + assert_eq!(blocks, buf2); + assert_eq!(mode.iv_state(), iv2); +} + +#[test] +fn aes128_cfb_decrypt_set_iv() { + let key = Default::default(); + let iv = Default::default(); + let mut mode = Decryptor::::new(&key, &iv); + + let mut blocks = [Default::default(); 16]; + + mode.decrypt_blocks(&mut blocks); + let iv = mode.iv_state(); + + let mut buf1 = blocks; + let mut buf2 = blocks; + + mode.peek(|m| m.decrypt_blocks(&mut buf1)); + assert_eq!(mode.iv_state(), iv); + + mode.decrypt_blocks(&mut blocks); + let iv2 = mode.iv_state(); + + mode.set_iv(&iv); + mode.decrypt_blocks(&mut buf2); + + assert_eq!(blocks, buf1); + assert_eq!(blocks, buf2); + assert_eq!(mode.iv_state(), iv2); +} diff --git a/cfb8/CHANGELOG.md b/cfb8/CHANGELOG.md index d70dfd4..50c5117 100644 --- a/cfb8/CHANGELOG.md +++ b/cfb8/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.9.1 (2026-05-20) +### Added +- Implementation of the `SetIvState` trait ([#114]) + +[#114]: https://github.com/RustCrypto/block-modes/pull/114 + ## 0.9.0 (2026-04-10) ### Removed - `std` feature ([#76]) diff --git a/cfb8/Cargo.toml b/cfb8/Cargo.toml index 75d5906..e06982c 100644 --- a/cfb8/Cargo.toml +++ b/cfb8/Cargo.toml @@ -1,24 +1,24 @@ [package] name = "cfb8" -version = "0.9.0" -description = "Cipher Feedback with eight bit feedback (CFB-8) block cipher mode of operation" +version = "0.9.1" authors = ["RustCrypto Developers"] -license = "MIT OR Apache-2.0" edition = "2024" rust-version = "1.85" -readme = "README.md" documentation = "https://docs.rs/cfb8" +readme = "README.md" repository = "https://github.com/RustCrypto/block-modes" +license = "MIT OR Apache-2.0" keywords = ["crypto", "block-mode", "stream-cipher", "ciphers"] categories = ["cryptography", "no-std"] +description = "Cipher Feedback with eight bit feedback (CFB-8) block cipher mode of operation" [dependencies] -cipher = "0.5" +cipher = "0.5.2" [dev-dependencies] -aes = "0.9" -cipher = { version = "0.5", features = ["dev"] } +cipher = { version = "0.5.2", features = ["dev"] } hex-literal = "1" +aes = "0.9" [features] alloc = ["cipher/alloc"] diff --git a/cfb8/src/decrypt.rs b/cfb8/src/decrypt.rs index 372fc19..6b5caa5 100644 --- a/cfb8/src/decrypt.rs +++ b/cfb8/src/decrypt.rs @@ -1,7 +1,7 @@ use cipher::{ AlgorithmName, Block, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockModeDecBackend, BlockModeDecClosure, BlockModeDecrypt, BlockSizeUser, InnerIvInit, Iv, - IvState, ParBlocksSizeUser, + IvState, ParBlocksSizeUser, SetIvState, array::{Array, ArraySize}, common::{InnerUser, IvSizeUser}, consts::U1, @@ -129,6 +129,16 @@ where } } +impl SetIvState for Decryptor +where + C: BlockCipherEncrypt, +{ + #[inline] + fn set_iv(&mut self, iv: &Iv) { + self.iv = iv.clone(); + } +} + impl AlgorithmName for Decryptor where C: BlockCipherEncrypt + AlgorithmName, diff --git a/cfb8/src/encrypt.rs b/cfb8/src/encrypt.rs index f12ee6d..919d872 100644 --- a/cfb8/src/encrypt.rs +++ b/cfb8/src/encrypt.rs @@ -1,7 +1,7 @@ use cipher::{ AlgorithmName, Block, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockModeEncBackend, BlockModeEncClosure, BlockModeEncrypt, BlockSizeUser, InnerIvInit, Iv, - IvState, ParBlocksSizeUser, + IvState, ParBlocksSizeUser, SetIvState, array::{Array, ArraySize}, common::{InnerUser, IvSizeUser}, consts::U1, @@ -129,6 +129,16 @@ where } } +impl SetIvState for Encryptor +where + C: BlockCipherEncrypt, +{ + #[inline] + fn set_iv(&mut self, iv: &Iv) { + self.iv = iv.clone(); + } +} + impl AlgorithmName for Encryptor where C: BlockCipherEncrypt + AlgorithmName, diff --git a/cfb8/tests/iv_state.rs b/cfb8/tests/iv_state.rs index 2d43ebc..7f2121c 100644 --- a/cfb8/tests/iv_state.rs +++ b/cfb8/tests/iv_state.rs @@ -2,7 +2,7 @@ use aes::*; use cfb8::{Decryptor, Encryptor}; -use cipher::iv_state_test; +use cipher::{BlockModeDecrypt, BlockModeEncrypt, IvState, KeyIvInit, SetIvState, iv_state_test}; iv_state_test!(aes128_cfb8_enc_iv_state, Encryptor, encrypt); iv_state_test!(aes128_cfb8_dec_iv_state, Decryptor, decrypt); @@ -10,3 +10,59 @@ iv_state_test!(aes192_cfb8_enc_iv_state, Encryptor, encrypt); iv_state_test!(aes192_cfb8_dec_iv_state, Decryptor, decrypt); iv_state_test!(aes256_cfb8_enc_iv_state, Encryptor, encrypt); iv_state_test!(aes256_cfb8_dec_iv_state, Decryptor, decrypt); + +#[test] +fn aes128_cfb8_encrypt_set_iv() { + let key = Default::default(); + let iv = Default::default(); + let mut mode = Encryptor::::new(&key, &iv); + + let mut blocks = [Default::default(); 16]; + + mode.encrypt_blocks(&mut blocks); + let iv = mode.iv_state(); + + let mut buf1 = blocks; + let mut buf2 = blocks; + + mode.peek(|m| m.encrypt_blocks(&mut buf1)); + assert_eq!(mode.iv_state(), iv); + + mode.encrypt_blocks(&mut blocks); + let iv2 = mode.iv_state(); + + mode.set_iv(&iv); + mode.encrypt_blocks(&mut buf2); + + assert_eq!(blocks, buf1); + assert_eq!(blocks, buf2); + assert_eq!(mode.iv_state(), iv2); +} + +#[test] +fn aes128_cfb8_decrypt_set_iv() { + let key = Default::default(); + let iv = Default::default(); + let mut mode = Decryptor::::new(&key, &iv); + + let mut blocks = [Default::default(); 16]; + + mode.decrypt_blocks(&mut blocks); + let iv = mode.iv_state(); + + let mut buf1 = blocks; + let mut buf2 = blocks; + + mode.peek(|m| m.decrypt_blocks(&mut buf1)); + assert_eq!(mode.iv_state(), iv); + + mode.decrypt_blocks(&mut blocks); + let iv2 = mode.iv_state(); + + mode.set_iv(&iv); + mode.decrypt_blocks(&mut buf2); + + assert_eq!(blocks, buf1); + assert_eq!(blocks, buf2); + assert_eq!(mode.iv_state(), iv2); +} diff --git a/ctr/CHANGELOG.md b/ctr/CHANGELOG.md index 3154684..4c575a0 100644 --- a/ctr/CHANGELOG.md +++ b/ctr/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.10.1 (2026-05-20) +### Added +- Implementation of the `SetIvState` trait ([#114]) + +[#114]: https://github.com/RustCrypto/block-modes/pull/114 + ## 0.10.0 (2026-04-10) ### Removed - `std` feature ([#76]) diff --git a/ctr/Cargo.toml b/ctr/Cargo.toml index e4c021f..45702bf 100644 --- a/ctr/Cargo.toml +++ b/ctr/Cargo.toml @@ -1,26 +1,26 @@ [package] name = "ctr" -version = "0.10.0" -description = "CTR block modes of operation" +version = "0.10.1" authors = ["RustCrypto Developers"] -license = "MIT OR Apache-2.0" edition = "2024" rust-version = "1.85" -readme = "README.md" documentation = "https://docs.rs/ctr" +readme = "README.md" repository = "https://github.com/RustCrypto/block-modes" +license = "MIT OR Apache-2.0" keywords = ["crypto", "block-mode", "stream-cipher", "ciphers"] categories = ["cryptography", "no-std"] +description = "CTR block modes of operation" [dependencies] -cipher = { version = "0.5", features = ["stream-wrapper"] } +cipher = { version = "0.5.2", features = ["stream-wrapper"] } [dev-dependencies] +cipher = { version = "0.5.2", features = ["dev"] } +hex-literal = "1" aes = "0.9" magma = "0.10" kuznyechik = "0.9" -cipher = { version = "0.5", features = ["dev"] } -hex-literal = "1" [features] alloc = ["cipher/alloc"] diff --git a/ctr/src/ctr_core.rs b/ctr/src/ctr_core.rs index 11a63d0..46bf80c 100644 --- a/ctr/src/ctr_core.rs +++ b/ctr/src/ctr_core.rs @@ -1,8 +1,8 @@ use crate::CtrFlavor; use cipher::{ AlgorithmName, Block, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, - BlockSizeUser, InnerIvInit, Iv, IvState, ParBlocks, ParBlocksSizeUser, StreamCipherBackend, - StreamCipherClosure, StreamCipherCore, StreamCipherSeekCore, + BlockSizeUser, InnerIvInit, Iv, IvState, ParBlocks, ParBlocksSizeUser, SetIvState, + StreamCipherBackend, StreamCipherClosure, StreamCipherCore, StreamCipherSeekCore, array::ArraySize, common::{InnerUser, IvSizeUser}, }; @@ -137,6 +137,17 @@ where } } +impl SetIvState for CtrCore +where + C: BlockCipherEncrypt, + F: CtrFlavor, +{ + #[inline] + fn set_iv(&mut self, iv: &Iv) { + self.ctr_nonce = F::from_nonce(iv); + } +} + impl AlgorithmName for CtrCore where C: BlockCipherEncrypt + AlgorithmName, diff --git a/ctr/tests/ctr128/mod.rs b/ctr/tests/ctr128/mod.rs index 8bc0a77..70f9275 100644 --- a/ctr/tests/ctr128/mod.rs +++ b/ctr/tests/ctr128/mod.rs @@ -10,3 +10,33 @@ cipher::iv_state_test!( CtrCore, apply_ks, ); + +#[test] +fn set_iv() { + use ctr::cipher::{IvState, KeyIvInit, SetIvState, StreamCipherCore}; + + let key = Default::default(); + let iv = Default::default(); + let mut mode = CtrCore::::new(&key, &iv); + + let mut blocks = [Default::default(); 16]; + + mode.apply_keystream_blocks(&mut blocks); + let iv = mode.iv_state(); + + let mut buf1 = blocks; + let mut buf2 = blocks; + + mode.peek(|m| m.apply_keystream_blocks(&mut buf1)); + assert_eq!(mode.iv_state(), iv); + + mode.apply_keystream_blocks(&mut blocks); + let iv2 = mode.iv_state(); + + mode.set_iv(&iv); + mode.apply_keystream_blocks(&mut buf2); + + assert_eq!(blocks, buf1); + assert_eq!(blocks, buf2); + assert_eq!(mode.iv_state(), iv2); +} diff --git a/ctr/tests/ctr32/be.rs b/ctr/tests/ctr32/be.rs index 98b1302..fffa9e9 100644 --- a/ctr/tests/ctr32/be.rs +++ b/ctr/tests/ctr32/be.rs @@ -84,3 +84,33 @@ cipher::iv_state_test!( ctr::CtrCore, apply_ks, ); + +#[test] +fn set_iv() { + use ctr::cipher::{IvState, SetIvState, StreamCipherCore}; + + let key = Default::default(); + let iv = Default::default(); + let mut mode = ctr::CtrCore::::new(&key, &iv); + + let mut blocks = [Default::default(); 16]; + + mode.apply_keystream_blocks(&mut blocks); + let iv = mode.iv_state(); + + let mut buf1 = blocks; + let mut buf2 = blocks; + + mode.peek(|m| m.apply_keystream_blocks(&mut buf1)); + assert_eq!(mode.iv_state(), iv); + + mode.apply_keystream_blocks(&mut blocks); + let iv2 = mode.iv_state(); + + mode.set_iv(&iv); + mode.apply_keystream_blocks(&mut buf2); + + assert_eq!(blocks, buf1); + assert_eq!(blocks, buf2); + assert_eq!(mode.iv_state(), iv2); +} diff --git a/ctr/tests/ctr32/le.rs b/ctr/tests/ctr32/le.rs index a270dbd..92bf9ac 100644 --- a/ctr/tests/ctr32/le.rs +++ b/ctr/tests/ctr32/le.rs @@ -93,3 +93,33 @@ cipher::iv_state_test!( ctr::CtrCore, apply_ks, ); + +#[test] +fn set_iv() { + use ctr::cipher::{IvState, SetIvState, StreamCipherCore}; + + let key = Default::default(); + let iv = Default::default(); + let mut mode = ctr::CtrCore::::new(&key, &iv); + + let mut blocks = [Default::default(); 16]; + + mode.apply_keystream_blocks(&mut blocks); + let iv = mode.iv_state(); + + let mut buf1 = blocks; + let mut buf2 = blocks; + + mode.peek(|m| m.apply_keystream_blocks(&mut buf1)); + assert_eq!(mode.iv_state(), iv); + + mode.apply_keystream_blocks(&mut blocks); + let iv2 = mode.iv_state(); + + mode.set_iv(&iv); + mode.apply_keystream_blocks(&mut buf2); + + assert_eq!(blocks, buf1); + assert_eq!(blocks, buf2); + assert_eq!(mode.iv_state(), iv2); +} diff --git a/cts/Cargo.toml b/cts/Cargo.toml index b078915..e0bb6cf 100644 --- a/cts/Cargo.toml +++ b/cts/Cargo.toml @@ -1,16 +1,16 @@ [package] name = "cts" version = "0.7.0" -description = "Generic implementation of the ciphertext stealing block modes of operation" authors = ["RustCrypto Developers"] -license = "MIT OR Apache-2.0" edition = "2024" rust-version = "1.85" -readme = "README.md" documentation = "https://docs.rs/cts" +readme = "README.md" repository = "https://github.com/RustCrypto/block-modes" +license = "MIT OR Apache-2.0" keywords = ["crypto", "block-mode", "ciphers"] categories = ["cryptography", "no-std"] +description = "Generic implementation of the ciphertext stealing block modes of operation" [dependencies] cipher = "0.5" diff --git a/ige/CHANGELOG.md b/ige/CHANGELOG.md index e9cdd28..84a3cd0 100644 --- a/ige/CHANGELOG.md +++ b/ige/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.2.1 (2026-05-20) +### Added +- Implementation of the `SetIvState` trait ([#114]) + +[#114]: https://github.com/RustCrypto/block-modes/pull/114 + ## 0.2.0 (2026-04-10) ### Removed - `std` feature ([#76]) diff --git a/ige/Cargo.toml b/ige/Cargo.toml index 892ca5d..265bc20 100644 --- a/ige/Cargo.toml +++ b/ige/Cargo.toml @@ -1,24 +1,24 @@ [package] name = "ige" -version = "0.2.0" -description = "Infinite Garble Extension (IGE) block cipher mode of operation" +version = "0.2.1" authors = ["RustCrypto Developers"] -license = "MIT OR Apache-2.0" edition = "2024" rust-version = "1.85" -readme = "README.md" documentation = "https://docs.rs/ige" +readme = "README.md" repository = "https://github.com/RustCrypto/block-modes" +license = "MIT OR Apache-2.0" keywords = ["crypto", "block-mode", "ciphers"] categories = ["cryptography", "no-std"] +description = "Infinite Garble Extension (IGE) block cipher mode of operation" [dependencies] -cipher = "0.5" +cipher = "0.5.2" [dev-dependencies] -aes = "0.9" -cipher = { version = "0.5", features = ["dev"] } +cipher = { version = "0.5.2", features = ["dev"] } hex-literal = "1" +aes = "0.9" [features] default = ["block-padding"] diff --git a/ige/src/decrypt.rs b/ige/src/decrypt.rs index 85007d0..ac37081 100644 --- a/ige/src/decrypt.rs +++ b/ige/src/decrypt.rs @@ -2,7 +2,7 @@ use crate::{IgeIvSize, xor}; use cipher::{ AlgorithmName, Block, BlockCipherDecBackend, BlockCipherDecClosure, BlockCipherDecrypt, BlockModeDecBackend, BlockModeDecClosure, BlockModeDecrypt, BlockSizeUser, InnerIvInit, Iv, - IvState, ParBlocksSizeUser, + IvState, ParBlocksSizeUser, SetIvState, array::{Array, ArraySize}, common::{InnerUser, IvSizeUser}, inout::InOut, @@ -109,9 +109,9 @@ where { #[inline] fn inner_iv_init(cipher: C, iv: &Iv) -> Self { - let n = C::BlockSize::USIZE; - let y = iv[..n].try_into().expect("should be the correct size"); - let x = iv[n..].try_into().expect("should be the correct size"); + let (y, x) = iv.split_at(C::BlockSize::USIZE); + let x = x.try_into().expect("x has correct size"); + let y = y.try_into().expect("y has correct size"); Self { cipher, x, y } } } @@ -128,6 +128,20 @@ where } } +impl SetIvState for Decryptor +where + C: BlockCipherDecrypt, + C::BlockSize: Add, + IgeIvSize: ArraySize, +{ + #[inline] + fn set_iv(&mut self, iv: &Iv) { + let (y, x) = iv.split_at(C::BlockSize::USIZE); + self.x = x.try_into().expect("x has correct size"); + self.y = y.try_into().expect("y has correct size"); + } +} + impl AlgorithmName for Decryptor where C: BlockCipherDecrypt + AlgorithmName, diff --git a/ige/src/encrypt.rs b/ige/src/encrypt.rs index 5a90272..207c10d 100644 --- a/ige/src/encrypt.rs +++ b/ige/src/encrypt.rs @@ -2,7 +2,7 @@ use crate::{IgeIvSize, xor}; use cipher::{ AlgorithmName, Block, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockModeEncBackend, BlockModeEncClosure, BlockModeEncrypt, BlockSizeUser, InnerIvInit, Iv, - IvState, ParBlocksSizeUser, + IvState, ParBlocksSizeUser, SetIvState, array::{Array, ArraySize}, common::{InnerUser, IvSizeUser}, inout::InOut, @@ -109,9 +109,9 @@ where { #[inline] fn inner_iv_init(cipher: C, iv: &Iv) -> Self { - let n = C::BlockSize::USIZE; - let y = iv[..n].try_into().expect("should be the correct size"); - let x = iv[n..].try_into().expect("should be the correct size"); + let (y, x) = iv.split_at(C::BlockSize::USIZE); + let x = x.try_into().expect("x has correct size"); + let y = y.try_into().expect("y has correct size"); Self { cipher, x, y } } } @@ -128,6 +128,20 @@ where } } +impl SetIvState for Encryptor +where + C: BlockCipherEncrypt, + C::BlockSize: Add, + IgeIvSize: ArraySize, +{ + #[inline] + fn set_iv(&mut self, iv: &Iv) { + let (y, x) = iv.split_at(C::BlockSize::USIZE); + self.x = x.try_into().expect("x has correct size"); + self.y = y.try_into().expect("y has correct size"); + } +} + impl AlgorithmName for Encryptor where C: BlockCipherEncrypt + AlgorithmName, diff --git a/ige/tests/iv_state.rs b/ige/tests/iv_state.rs index 750a4f8..86d1dbc 100644 --- a/ige/tests/iv_state.rs +++ b/ige/tests/iv_state.rs @@ -1,8 +1,64 @@ //! IV state tests. use aes::Aes128; -use cipher::iv_state_test; +use cipher::{BlockModeDecrypt, BlockModeEncrypt, IvState, KeyIvInit, SetIvState, iv_state_test}; use ige::{Decryptor, Encryptor}; iv_state_test!(aes128_ige_enc_iv_state, Encryptor, encrypt); iv_state_test!(aes128_ige_dec_iv_state, Decryptor, decrypt); + +#[test] +fn aes128_ige_encrypt_set_iv() { + let key = Default::default(); + let iv = Default::default(); + let mut mode = Encryptor::::new(&key, &iv); + + let mut blocks = [Default::default(); 16]; + + mode.encrypt_blocks(&mut blocks); + let iv = mode.iv_state(); + + let mut buf1 = blocks; + let mut buf2 = blocks; + + mode.peek(|m| m.encrypt_blocks(&mut buf1)); + assert_eq!(mode.iv_state(), iv); + + mode.encrypt_blocks(&mut blocks); + let iv2 = mode.iv_state(); + + mode.set_iv(&iv); + mode.encrypt_blocks(&mut buf2); + + assert_eq!(blocks, buf1); + assert_eq!(blocks, buf2); + assert_eq!(mode.iv_state(), iv2); +} + +#[test] +fn aes128_ige_decrypt_set_iv() { + let key = Default::default(); + let iv = Default::default(); + let mut mode = Decryptor::::new(&key, &iv); + + let mut blocks = [Default::default(); 16]; + + mode.decrypt_blocks(&mut blocks); + let iv = mode.iv_state(); + + let mut buf1 = blocks; + let mut buf2 = blocks; + + mode.peek(|m| m.decrypt_blocks(&mut buf1)); + assert_eq!(mode.iv_state(), iv); + + mode.decrypt_blocks(&mut blocks); + let iv2 = mode.iv_state(); + + mode.set_iv(&iv); + mode.decrypt_blocks(&mut buf2); + + assert_eq!(blocks, buf1); + assert_eq!(blocks, buf2); + assert_eq!(mode.iv_state(), iv2); +} diff --git a/ofb/CHANGELOG.md b/ofb/CHANGELOG.md index a845d2e..5d6ec4d 100644 --- a/ofb/CHANGELOG.md +++ b/ofb/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.7.1 (2026-05-20) +### Added +- Implementation of the `SetIvState` trait ([#114]) + +[#114]: https://github.com/RustCrypto/block-modes/pull/114 + ## 0.7.0 (2026-04-10) ### Removed - `std` feature ([#76]) diff --git a/ofb/Cargo.toml b/ofb/Cargo.toml index b02a63a..a101585 100644 --- a/ofb/Cargo.toml +++ b/ofb/Cargo.toml @@ -1,24 +1,24 @@ [package] name = "ofb" -version = "0.7.0" -description = "Output Feedback (OFB) block cipher mode of operation" +version = "0.7.1" authors = ["RustCrypto Developers"] -license = "MIT OR Apache-2.0" edition = "2024" rust-version = "1.85" -readme = "README.md" documentation = "https://docs.rs/ofb" +readme = "README.md" repository = "https://github.com/RustCrypto/block-modes" +license = "MIT OR Apache-2.0" keywords = ["crypto", "block-mode", "stream-cipher", "ciphers"] categories = ["cryptography", "no-std"] +description = "Output Feedback (OFB) block cipher mode of operation" [dependencies] -cipher = { version = "0.5", features = ["stream-wrapper"] } +cipher = { version = "0.5.2", features = ["stream-wrapper"] } [dev-dependencies] -aes = "0.9" -cipher = { version = "0.5", features = ["dev"] } +cipher = { version = "0.5.2", features = ["dev"] } hex-literal = "1" +aes = "0.9" [features] alloc = ["cipher/alloc"] diff --git a/ofb/src/lib.rs b/ofb/src/lib.rs index 44baf26..1d385ef 100644 --- a/ofb/src/lib.rs +++ b/ofb/src/lib.rs @@ -69,7 +69,7 @@ use cipher::{ AlgorithmName, Array, Block, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockModeDecBackend, BlockModeDecClosure, BlockModeDecrypt, BlockModeEncBackend, BlockModeEncClosure, BlockModeEncrypt, BlockSizeUser, InOut, InnerIvInit, Iv, IvState, - ParBlocksSizeUser, StreamCipherBackend, StreamCipherClosure, StreamCipherCore, + ParBlocksSizeUser, SetIvState, StreamCipherBackend, StreamCipherClosure, StreamCipherCore, StreamCipherCoreWrapper, array::ArraySize, common::{InnerUser, IvSizeUser}, @@ -138,6 +138,16 @@ where } } +impl SetIvState for OfbCore +where + C: BlockCipherEncrypt, +{ + #[inline] + fn set_iv(&mut self, iv: &Iv) { + self.iv = iv.clone(); + } +} + impl StreamCipherCore for OfbCore where C: BlockCipherEncrypt, diff --git a/ofb/tests/iv_state.rs b/ofb/tests/iv_state.rs index 158d64b..71142b5 100644 --- a/ofb/tests/iv_state.rs +++ b/ofb/tests/iv_state.rs @@ -1,7 +1,10 @@ //! IV state tests. -use aes::*; -use cipher::iv_state_test; +use aes::{Aes128, Aes192, Aes256}; +use cipher::{ + BlockModeDecrypt, BlockModeEncrypt, IvState, KeyIvInit, SetIvState, StreamCipherCore, + iv_state_test, +}; use ofb::OfbCore; iv_state_test!(aes128_ofb_enc_iv_state, OfbCore, encrypt); @@ -13,3 +16,87 @@ iv_state_test!(aes192_ofb_apply_ks_iv_state, OfbCore, apply_ks); iv_state_test!(aes256_ofb_enc_iv_state, OfbCore, encrypt); iv_state_test!(aes256_ofb_dec_iv_state, OfbCore, decrypt); iv_state_test!(aes256_ofb_apply_ks_iv_state, OfbCore, apply_ks); + +#[test] +fn aes128_ofb_set_iv() { + let key = Default::default(); + let iv = Default::default(); + let mut mode = OfbCore::::new(&key, &iv); + + let mut blocks = [Default::default(); 16]; + + mode.apply_keystream_blocks(&mut blocks); + let iv = mode.iv_state(); + + let mut buf1 = blocks; + let mut buf2 = blocks; + + mode.peek(|m| m.apply_keystream_blocks(&mut buf1)); + assert_eq!(mode.iv_state(), iv); + + mode.apply_keystream_blocks(&mut blocks); + let iv2 = mode.iv_state(); + + mode.set_iv(&iv); + mode.apply_keystream_blocks(&mut buf2); + + assert_eq!(blocks, buf1); + assert_eq!(blocks, buf2); + assert_eq!(mode.iv_state(), iv2); +} + +#[test] +fn aes128_ofb_encrypt_set_iv() { + let key = Default::default(); + let iv = Default::default(); + let mut mode = OfbCore::::new(&key, &iv); + + let mut blocks = [Default::default(); 16]; + + mode.encrypt_blocks(&mut blocks); + let iv = mode.iv_state(); + + let mut buf1 = blocks; + let mut buf2 = blocks; + + mode.peek(|m| m.encrypt_blocks(&mut buf1)); + assert_eq!(mode.iv_state(), iv); + + mode.encrypt_blocks(&mut blocks); + let iv2 = mode.iv_state(); + + mode.set_iv(&iv); + mode.encrypt_blocks(&mut buf2); + + assert_eq!(blocks, buf1); + assert_eq!(blocks, buf2); + assert_eq!(mode.iv_state(), iv2); +} + +#[test] +fn aes128_ofb_decrypt_set_iv() { + let key = Default::default(); + let iv = Default::default(); + let mut mode = OfbCore::::new(&key, &iv); + + let mut blocks = [Default::default(); 16]; + + mode.decrypt_blocks(&mut blocks); + let iv = mode.iv_state(); + + let mut buf1 = blocks; + let mut buf2 = blocks; + + mode.peek(|m| m.decrypt_blocks(&mut buf1)); + assert_eq!(mode.iv_state(), iv); + + mode.decrypt_blocks(&mut blocks); + let iv2 = mode.iv_state(); + + mode.set_iv(&iv); + mode.decrypt_blocks(&mut buf2); + + assert_eq!(blocks, buf1); + assert_eq!(blocks, buf2); + assert_eq!(mode.iv_state(), iv2); +} diff --git a/pcbc/CHANGELOG.md b/pcbc/CHANGELOG.md index 1240633..f100153 100644 --- a/pcbc/CHANGELOG.md +++ b/pcbc/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.2.1 (2026-05-20) +### Added +- Implementation of the `SetIvState` trait ([#114]) + +[#114]: https://github.com/RustCrypto/block-modes/pull/114 + ## 0.2.0 (2026-04-10) ### Removed - `std` feature ([#76]) diff --git a/pcbc/Cargo.toml b/pcbc/Cargo.toml index 6285880..209e78c 100644 --- a/pcbc/Cargo.toml +++ b/pcbc/Cargo.toml @@ -1,24 +1,24 @@ [package] name = "pcbc" -version = "0.2.0" -description = "Propagating Cipher Block Chaining (PCBC) block cipher mode of operation" +version = "0.2.1" authors = ["RustCrypto Developers"] -license = "MIT OR Apache-2.0" edition = "2024" rust-version = "1.85" -readme = "README.md" documentation = "https://docs.rs/pcbc" +readme = "README.md" repository = "https://github.com/RustCrypto/block-modes" +license = "MIT OR Apache-2.0" keywords = ["crypto", "block-mode", "ciphers"] categories = ["cryptography", "no-std"] +description = "Propagating Cipher Block Chaining (PCBC) block cipher mode of operation" [dependencies] -cipher = "0.5" +cipher = "0.5.2" [dev-dependencies] -aes = "0.9" -cipher = { version = "0.5", features = ["dev"] } +cipher = { version = "0.5.2", features = ["dev"] } hex-literal = "1" +aes = "0.9" [features] default = ["block-padding"] diff --git a/pcbc/src/decrypt.rs b/pcbc/src/decrypt.rs index 3570c9f..8a4a542 100644 --- a/pcbc/src/decrypt.rs +++ b/pcbc/src/decrypt.rs @@ -2,7 +2,7 @@ use crate::xor; use cipher::{ AlgorithmName, Block, BlockCipherDecBackend, BlockCipherDecClosure, BlockCipherDecrypt, BlockModeDecBackend, BlockModeDecClosure, BlockModeDecrypt, BlockSizeUser, InnerIvInit, Iv, - IvState, ParBlocksSizeUser, + IvState, ParBlocksSizeUser, SetIvState, array::{Array, ArraySize}, common::{InnerUser, IvSizeUser}, consts::U1, @@ -110,6 +110,16 @@ where } } +impl SetIvState for Decryptor +where + C: BlockCipherDecrypt, +{ + #[inline] + fn set_iv(&mut self, iv: &Iv) { + self.iv = iv.clone(); + } +} + impl AlgorithmName for Decryptor where C: BlockCipherDecrypt + AlgorithmName, diff --git a/pcbc/src/encrypt.rs b/pcbc/src/encrypt.rs index ea3c45c..4d6cc70 100644 --- a/pcbc/src/encrypt.rs +++ b/pcbc/src/encrypt.rs @@ -2,7 +2,7 @@ use crate::xor; use cipher::{ AlgorithmName, Block, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockModeEncBackend, BlockModeEncClosure, BlockModeEncrypt, BlockSizeUser, InnerIvInit, Iv, - IvState, ParBlocksSizeUser, + IvState, ParBlocksSizeUser, SetIvState, array::{Array, ArraySize}, common::{InnerUser, IvSizeUser}, consts::U1, @@ -107,6 +107,16 @@ where } } +impl SetIvState for Encryptor +where + C: BlockCipherEncrypt, +{ + #[inline] + fn set_iv(&mut self, iv: &Iv) { + self.iv = iv.clone(); + } +} + impl AlgorithmName for Encryptor where C: BlockCipherEncrypt + AlgorithmName, diff --git a/pcbc/tests/iv_state.rs b/pcbc/tests/iv_state.rs index c47f2d6..41ec75a 100644 --- a/pcbc/tests/iv_state.rs +++ b/pcbc/tests/iv_state.rs @@ -1,8 +1,64 @@ //! IV state tests. use aes::Aes128; -use cipher::iv_state_test; +use cipher::{BlockModeDecrypt, BlockModeEncrypt, IvState, KeyIvInit, SetIvState, iv_state_test}; use pcbc::{Decryptor, Encryptor}; iv_state_test!(aes128_pcbc_enc_iv_state, Encryptor, encrypt); iv_state_test!(aes128_pcbc_dec_iv_state, Decryptor, decrypt); + +#[test] +fn aes128_pcbc_encrypt_set_iv() { + let key = Default::default(); + let iv = Default::default(); + let mut mode = Encryptor::::new(&key, &iv); + + let mut blocks = [Default::default(); 16]; + + mode.encrypt_blocks(&mut blocks); + let iv = mode.iv_state(); + + let mut buf1 = blocks; + let mut buf2 = blocks; + + mode.peek(|m| m.encrypt_blocks(&mut buf1)); + assert_eq!(mode.iv_state(), iv); + + mode.encrypt_blocks(&mut blocks); + let iv2 = mode.iv_state(); + + mode.set_iv(&iv); + mode.encrypt_blocks(&mut buf2); + + assert_eq!(blocks, buf1); + assert_eq!(blocks, buf2); + assert_eq!(mode.iv_state(), iv2); +} + +#[test] +fn aes128_pcbc_decrypt_set_iv() { + let key = Default::default(); + let iv = Default::default(); + let mut mode = Decryptor::::new(&key, &iv); + + let mut blocks = [Default::default(); 16]; + + mode.decrypt_blocks(&mut blocks); + let iv = mode.iv_state(); + + let mut buf1 = blocks; + let mut buf2 = blocks; + + mode.peek(|m| m.decrypt_blocks(&mut buf1)); + assert_eq!(mode.iv_state(), iv); + + mode.decrypt_blocks(&mut blocks); + let iv2 = mode.iv_state(); + + mode.set_iv(&iv); + mode.decrypt_blocks(&mut buf2); + + assert_eq!(blocks, buf1); + assert_eq!(blocks, buf2); + assert_eq!(mode.iv_state(), iv2); +}