diff --git a/Cargo.lock b/Cargo.lock index b4203945..d52e7e35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,13 +1,15 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "aes" version = "0.7.0-pre" dependencies = [ "cfg-if", - "cipher", + "cipher 0.3.0-pre.4 (git+https://github.com/RustCrypto/traits?branch=experimental_design)", "cpuid-bool", - "ctr", + "ctr 0.7.0-pre.3", "opaque-debug", ] @@ -22,23 +24,15 @@ name = "block-modes" version = "0.8.0-pre" dependencies = [ "aes", - "block-padding", - "cipher", - "hex-literal", + "cipher 0.3.0-pre.4 (git+https://github.com/RustCrypto/traits?branch=experimental_design)", ] -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - [[package]] name = "blowfish" version = "0.8.0-pre" dependencies = [ "byteorder", - "cipher", + "cipher 0.3.0-pre.4 (registry+https://github.com/rust-lang/crates.io-index)", "opaque-debug", ] @@ -53,17 +47,51 @@ name = "cast5" version = "0.10.0-pre" dependencies = [ "byteorder", - "cipher", + "cipher 0.3.0-pre.4 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal", "opaque-debug", ] +[[package]] +name = "cbc" +version = "0.1.0" +dependencies = [ + "aes", + "cipher 0.3.0-pre.4 (git+https://github.com/RustCrypto/traits?branch=experimental_design)", +] + +[[package]] +name = "cfb-mode" +version = "0.7.0" +dependencies = [ + "aes", + "cipher 0.3.0-pre.4 (git+https://github.com/RustCrypto/traits?branch=experimental_design)", +] + +[[package]] +name = "cfb8" +version = "0.7.0" +dependencies = [ + "aes", + "cipher 0.3.0-pre.4 (git+https://github.com/RustCrypto/traits?branch=experimental_design)", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cipher" +version = "0.3.0-pre.4" +source = "git+https://github.com/RustCrypto/traits?branch=experimental_design#7bff7d65873402bf59f1cd09f0d0e23449db8ed9" +dependencies = [ + "blobby", + "crypto-common", + "generic-array", +] + [[package]] name = "cipher" version = "0.3.0-pre.4" @@ -80,13 +108,29 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" +[[package]] +name = "crypto-common" +version = "0.1.0" +source = "git+https://github.com/RustCrypto/traits?branch=experimental_design#7bff7d65873402bf59f1cd09f0d0e23449db8ed9" +dependencies = [ + "generic-array", +] + [[package]] name = "ctr" version = "0.7.0-pre.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa78b1c3f6ce4edd238882705f9589c9f2443bf541dd98d7b75aaa7ca91d4ba6" dependencies = [ - "cipher", + "cipher 0.3.0-pre.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ctr" +version = "0.7.0" +dependencies = [ + "aes", + "cipher 0.3.0-pre.4 (git+https://github.com/RustCrypto/traits?branch=experimental_design)", ] [[package]] @@ -94,7 +138,7 @@ name = "des" version = "0.7.0-pre" dependencies = [ "byteorder", - "cipher", + "cipher 0.3.0-pre.4 (registry+https://github.com/rust-lang/crates.io-index)", "opaque-debug", ] @@ -113,7 +157,7 @@ name = "gost-modes" version = "0.5.0-pre" dependencies = [ "block-modes", - "cipher", + "cipher 0.3.0-pre.4 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array", "hex-literal", "kuznyechik", @@ -143,15 +187,23 @@ dependencies = [ name = "idea" version = "0.4.0-pre" dependencies = [ - "cipher", + "cipher 0.3.0-pre.4 (registry+https://github.com/rust-lang/crates.io-index)", "opaque-debug", ] +[[package]] +name = "ige" +version = "0.1.0" +dependencies = [ + "aes", + "cipher 0.3.0-pre.4 (git+https://github.com/RustCrypto/traits?branch=experimental_design)", +] + [[package]] name = "kuznyechik" version = "0.7.0-pre" dependencies = [ - "cipher", + "cipher 0.3.0-pre.4 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal", "opaque-debug", ] @@ -160,17 +212,33 @@ dependencies = [ name = "magma" version = "0.7.0-pre" dependencies = [ - "cipher", + "cipher 0.3.0-pre.4 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal", "opaque-debug", ] +[[package]] +name = "ofb" +version = "0.5.0" +dependencies = [ + "aes", + "cipher 0.3.0-pre.4 (git+https://github.com/RustCrypto/traits?branch=experimental_design)", +] + [[package]] name = "opaque-debug" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "pcbc" +version = "0.1.0" +dependencies = [ + "aes", + "cipher 0.3.0-pre.4 (git+https://github.com/RustCrypto/traits?branch=experimental_design)", +] + [[package]] name = "proc-macro-hack" version = "0.5.18" @@ -181,7 +249,7 @@ checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598" name = "rc2" version = "0.7.0-pre" dependencies = [ - "cipher", + "cipher 0.3.0-pre.4 (registry+https://github.com/rust-lang/crates.io-index)", "opaque-debug", ] @@ -190,7 +258,7 @@ name = "serpent" version = "0.4.0-pre" dependencies = [ "byteorder", - "cipher", + "cipher 0.3.0-pre.4 (registry+https://github.com/rust-lang/crates.io-index)", "opaque-debug", ] @@ -199,7 +267,7 @@ name = "sm4" version = "0.4.0-pre" dependencies = [ "byteorder", - "cipher", + "cipher 0.3.0-pre.4 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal", "opaque-debug", ] @@ -208,7 +276,7 @@ dependencies = [ name = "threefish" version = "0.4.0-pre" dependencies = [ - "cipher", + "cipher 0.3.0-pre.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -216,7 +284,7 @@ name = "twofish" version = "0.6.0-pre" dependencies = [ "byteorder", - "cipher", + "cipher 0.3.0-pre.4 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal", "opaque-debug", ] diff --git a/Cargo.toml b/Cargo.toml index 3cc9549c..5b691c1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,13 @@ members = [ "idea", "kuznyechik", "magma", + "modes/cbc", + "modes/cfb8", + "modes/cfb-mode", + "modes/ctr", + "modes/ige", + "modes/ofb", + "modes/pcbc", "rc2", "serpent", "sm4", diff --git a/aes/Cargo.toml b/aes/Cargo.toml index efbc4314..583fdb82 100644 --- a/aes/Cargo.toml +++ b/aes/Cargo.toml @@ -16,12 +16,12 @@ categories = ["cryptography", "no-std"] [dependencies] cfg-if = "1" -cipher = "=0.3.0-pre.4" +cipher = { git = "https://github.com/RustCrypto/traits", branch = "experimental_design" } ctr = { version = "=0.7.0-pre.3", optional = true } opaque-debug = "0.3" [dev-dependencies] -cipher = { version = "=0.3.0-pre.4", features = ["dev"] } +cipher = { git = "https://github.com/RustCrypto/traits", branch = "experimental_design", features = ["dev"] } [target.'cfg(any(target_arch = "x86_64", target_arch = "x86"))'.dependencies] cpuid-bool = "0.2" diff --git a/aes/src/autodetect.rs b/aes/src/autodetect.rs index 50e3f822..8f989874 100644 --- a/aes/src/autodetect.rs +++ b/aes/src/autodetect.rs @@ -1,12 +1,12 @@ //! Autodetection support for hardware accelerated AES backends with fallback //! to the fixsliced "soft" implementation. -use crate::{Block, ParBlocks}; use cipher::{ - consts::{U16, U24, U32, U8}, + consts::{U16, U24, U32}, generic_array::GenericArray, - BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher, + BlockCipher, BlockProcessing, BlockDecrypt, BlockEncrypt, KeyInit, InOutVal, InOutBuf, InResOutBuf, }; +use crate::Block; use core::mem::ManuallyDrop; cpuid_bool::new!(aes_cpuid, "aes"); @@ -33,7 +33,7 @@ macro_rules! define_aes_impl { } } - impl NewBlockCipher for $name { + impl KeyInit for $name { type KeySize = $key_size; #[inline] @@ -62,14 +62,13 @@ macro_rules! define_aes_impl { } } - impl BlockCipher for $name { + impl BlockProcessing for $name { type BlockSize = U16; - type ParBlocks = U8; } impl BlockEncrypt for $name { #[inline] - fn encrypt_block(&self, block: &mut Block) { + fn encrypt_block(&self, block: impl InOutVal) { if self.token.get() { unsafe { self.inner.ni.encrypt_block(block) } } else { @@ -78,18 +77,22 @@ macro_rules! define_aes_impl { } #[inline] - fn encrypt_par_blocks(&self, blocks: &mut ParBlocks) { + fn encrypt_blocks( + &self, + blocks: InOutBuf<'_, '_, Block>, + proc: impl FnMut(InResOutBuf<'_, '_, '_, Block>), + ) { if self.token.get() { - unsafe { self.inner.ni.encrypt_par_blocks(blocks) } + unsafe { self.inner.ni.encrypt_blocks(blocks, proc) } } else { - unsafe { self.inner.soft.encrypt_par_blocks(blocks) } + unsafe { self.inner.soft.encrypt_blocks(blocks, proc) } } } } impl BlockDecrypt for $name { #[inline] - fn decrypt_block(&self, block: &mut Block) { + fn decrypt_block(&self, block: impl InOutVal) { if self.token.get() { unsafe { self.inner.ni.decrypt_block(block) } } else { @@ -98,15 +101,21 @@ macro_rules! define_aes_impl { } #[inline] - fn decrypt_par_blocks(&self, blocks: &mut ParBlocks) { + fn decrypt_blocks( + &self, + blocks: InOutBuf<'_, '_, Block>, + proc: impl FnMut(InResOutBuf<'_, '_, '_, Block>), + ) { if self.token.get() { - unsafe { self.inner.ni.decrypt_par_blocks(blocks) } + unsafe { self.inner.ni.decrypt_blocks(blocks, proc) } } else { - unsafe { self.inner.soft.decrypt_par_blocks(blocks) } + unsafe { self.inner.soft.decrypt_blocks(blocks, proc) } } } } + impl BlockCipher for $name {} + opaque_debug::implement!($name); } } diff --git a/aes/src/lib.rs b/aes/src/lib.rs index 68a951d6..d7a58e86 100644 --- a/aes/src/lib.rs +++ b/aes/src/lib.rs @@ -19,13 +19,12 @@ //! ``` //! use aes::Aes128; //! use aes::cipher::{ -//! BlockCipher, BlockEncrypt, BlockDecrypt, NewBlockCipher, +//! BlockEncrypt, BlockDecrypt, KeyInit, //! generic_array::GenericArray, //! }; //! //! let key = GenericArray::from_slice(&[0u8; 16]); //! let mut block = GenericArray::clone_from_slice(&[0u8; 16]); -//! let mut block8 = GenericArray::clone_from_slice(&[block; 8]); //! // Initialize cipher //! let cipher = Aes128::new(&key); //! @@ -36,12 +35,11 @@ //! cipher.decrypt_block(&mut block); //! assert_eq!(block, block_copy); //! -//! // We can encrypt 8 blocks simultaneously using -//! // instruction-level parallelism -//! let block8_copy = block8.clone(); -//! cipher.encrypt_par_blocks(&mut block8); -//! cipher.decrypt_par_blocks(&mut block8); -//! assert_eq!(block8, block8_copy); +//! // It's also possible to process data in buffer-to-buffer fashion +//! let mut res = GenericArray::default(); +//! cipher.encrypt_block((&block, &mut res)); +//! cipher.decrypt_block((&res, &mut block)); +//! assert_eq!(block, block_copy); //! ``` //! //! For implementations of block cipher modes of operation see @@ -83,10 +81,10 @@ cfg_if! { } } -pub use cipher::{self, BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher}; +pub use cipher::{self, BlockDecrypt, BlockEncrypt, KeyInit}; -/// 128-bit AES block -pub type Block = cipher::generic_array::GenericArray; +use cipher::generic_array::{GenericArray, typenum::{U16, U8, U2}}; -/// 8 x 128-bit AES blocks to be processed in parallel -pub type ParBlocks = cipher::generic_array::GenericArray; +type Block = GenericArray; +type Block2 = GenericArray; +type Block8 = GenericArray; diff --git a/aes/src/ni/aes128.rs b/aes/src/ni/aes128.rs index 16f29343..486c4d6c 100644 --- a/aes/src/ni/aes128.rs +++ b/aes/src/ni/aes128.rs @@ -1,14 +1,15 @@ use super::{ arch::*, utils::{ - aesdec8, aesdeclast8, aesenc8, aesenclast8, load8, store8, xor8, Block128, Block128x8, - U128x8, + aesdec8, aesdeclast8, aesenc8, aesenclast8, load8, store8, xor8 }, }; +use crate::{Block, Block8}; use cipher::{ consts::{U16, U8}, generic_array::GenericArray, - BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher, + BlockCipher, BlockDecrypt, BlockEncrypt, KeyInit, BlockProcessing, + InOutVal, InOutBuf, InResOutBuf, }; mod expand; @@ -25,58 +26,13 @@ pub struct Aes128 { decrypt_keys: RoundKeys, } -impl Aes128 { - #[inline(always)] - pub(crate) fn encrypt8(&self, mut blocks: U128x8) -> U128x8 { - #[inline] - #[target_feature(enable = "aes")] - unsafe fn aesni128_encrypt8(keys: &RoundKeys, blocks: &mut U128x8) { - xor8(blocks, keys[0]); - aesenc8(blocks, keys[1]); - aesenc8(blocks, keys[2]); - aesenc8(blocks, keys[3]); - aesenc8(blocks, keys[4]); - aesenc8(blocks, keys[5]); - aesenc8(blocks, keys[6]); - aesenc8(blocks, keys[7]); - aesenc8(blocks, keys[8]); - aesenc8(blocks, keys[9]); - aesenclast8(blocks, keys[10]); - } - unsafe { aesni128_encrypt8(&self.encrypt_keys, &mut blocks) }; - blocks - } - - #[inline(always)] - pub(crate) fn encrypt(&self, block: __m128i) -> __m128i { - #[inline] - #[target_feature(enable = "aes")] - unsafe fn aesni128_encrypt1(keys: &RoundKeys, mut block: __m128i) -> __m128i { - block = _mm_xor_si128(block, keys[0]); - block = _mm_aesenc_si128(block, keys[1]); - block = _mm_aesenc_si128(block, keys[2]); - block = _mm_aesenc_si128(block, keys[3]); - block = _mm_aesenc_si128(block, keys[4]); - block = _mm_aesenc_si128(block, keys[5]); - block = _mm_aesenc_si128(block, keys[6]); - block = _mm_aesenc_si128(block, keys[7]); - block = _mm_aesenc_si128(block, keys[8]); - block = _mm_aesenc_si128(block, keys[9]); - _mm_aesenclast_si128(block, keys[10]) - } - unsafe { aesni128_encrypt1(&self.encrypt_keys, block) } - } -} - -impl NewBlockCipher for Aes128 { +impl KeyInit for Aes128 { type KeySize = U16; #[inline] fn new(key: &GenericArray) -> Self { let key = unsafe { &*(key as *const _ as *const [u8; 16]) }; - let (encrypt_keys, decrypt_keys) = expand::expand(key); - Self { encrypt_keys, decrypt_keys, @@ -84,82 +40,160 @@ impl NewBlockCipher for Aes128 { } } -impl BlockCipher for Aes128 { +impl BlockProcessing for Aes128 { type BlockSize = U16; - type ParBlocks = U8; } +impl BlockCipher for Aes128 {} + impl BlockEncrypt for Aes128 { - #[inline] - fn encrypt_block(&self, block: &mut Block128) { - // Safety: `loadu` and `storeu` support unaligned access - #[allow(clippy::cast_ptr_alignment)] + fn encrypt_block(&self, mut block: impl InOutVal) { + let in_ptr = block.get_in() as *const Block; + let out_ptr = block.get_out() as *mut Block; unsafe { - let b = _mm_loadu_si128(block.as_ptr() as *const __m128i); - let b = self.encrypt(b); - _mm_storeu_si128(block.as_mut_ptr() as *mut __m128i, b); + aes128_encrypt1(&self.encrypt_keys, in_ptr, out_ptr); } } - #[inline] - fn encrypt_par_blocks(&self, blocks: &mut Block128x8) { - let b = self.encrypt8(load8(blocks)); - store8(blocks, b); + fn encrypt_blocks( + &self, + mut blocks: InOutBuf<'_, '_, Block>, + proc: impl FnMut(InResOutBuf<'_, '_, '_, Block>), + ) { + blocks.chunks::( + &self.encrypt_keys, + |keys, inc, res| unsafe { + aes128_encrypt8( + keys, + inc as *const Block8, + res as *mut Block8, + ) + }, + |keys, inc, res| unsafe { + let n = inc.len(); + res[..n].copy_from_slice(inc); + aes128_encrypt8( + keys, + res as *const Block8, + res as *mut Block8, + ) + }, + proc, + ); } } impl BlockDecrypt for Aes128 { - #[inline] - fn decrypt_block(&self, block: &mut Block128) { - #[inline] - #[target_feature(enable = "aes")] - unsafe fn aes128_decrypt1(block: &mut Block128, keys: &RoundKeys) { - // Safety: `loadu` and `storeu` support unaligned access - #[allow(clippy::cast_ptr_alignment)] - let mut b = _mm_loadu_si128(block.as_ptr() as *const __m128i); - - b = _mm_xor_si128(b, keys[10]); - b = _mm_aesdec_si128(b, keys[9]); - b = _mm_aesdec_si128(b, keys[8]); - b = _mm_aesdec_si128(b, keys[7]); - b = _mm_aesdec_si128(b, keys[6]); - b = _mm_aesdec_si128(b, keys[5]); - b = _mm_aesdec_si128(b, keys[4]); - b = _mm_aesdec_si128(b, keys[3]); - b = _mm_aesdec_si128(b, keys[2]); - b = _mm_aesdec_si128(b, keys[1]); - b = _mm_aesdeclast_si128(b, keys[0]); - - // Safety: `loadu` and `storeu` support unaligned access - #[allow(clippy::cast_ptr_alignment)] - _mm_storeu_si128(block.as_mut_ptr() as *mut __m128i, b); + fn decrypt_block(&self, mut block: impl InOutVal) { + let in_ptr = block.get_in() as *const Block; + let out_ptr = block.get_out() as *mut Block; + unsafe { + aes128_decrypt1(&self.decrypt_keys, in_ptr, out_ptr); } - - unsafe { aes128_decrypt1(block, &self.decrypt_keys) } } - #[inline] - fn decrypt_par_blocks(&self, blocks: &mut Block128x8) { - #[inline] - #[target_feature(enable = "aes")] - unsafe fn aes128_decrypt8(blocks: &mut Block128x8, keys: &RoundKeys) { - let mut b = load8(blocks); - xor8(&mut b, keys[10]); - aesdec8(&mut b, keys[9]); - aesdec8(&mut b, keys[8]); - aesdec8(&mut b, keys[7]); - aesdec8(&mut b, keys[6]); - aesdec8(&mut b, keys[5]); - aesdec8(&mut b, keys[4]); - aesdec8(&mut b, keys[3]); - aesdec8(&mut b, keys[2]); - aesdec8(&mut b, keys[1]); - aesdeclast8(&mut b, keys[0]); - store8(blocks, b); - } - - unsafe { aes128_decrypt8(blocks, &self.decrypt_keys) } + fn decrypt_blocks( + &self, + mut blocks: InOutBuf<'_, '_, Block>, + proc: impl FnMut(InResOutBuf<'_, '_, '_, Block>), + ) { + blocks.chunks::( + &self.decrypt_keys, + |keys, inc, res| unsafe { + aes128_decrypt8( + keys, + inc as *const Block8, + res as *mut Block8, + ) + }, + |keys, inc, res| unsafe { + let n = inc.len(); + res[..n].copy_from_slice(inc); + aes128_decrypt8( + keys, + res as *const Block8, + res as *mut Block8, + ) + }, + proc, + ); } } opaque_debug::implement!(Aes128); + +#[inline] +#[target_feature(enable = "aes")] +pub(crate) unsafe fn aes128_encrypt1(keys: &RoundKeys, in_ptr: *const Block, out_ptr: *mut Block) { + // Safety: `loadu` and `storeu` support unaligned access + #[allow(clippy::cast_ptr_alignment)] + let mut block = _mm_loadu_si128(in_ptr as *const __m128i); + block = _mm_xor_si128(block, keys[0]); + block = _mm_aesenc_si128(block, keys[1]); + block = _mm_aesenc_si128(block, keys[2]); + block = _mm_aesenc_si128(block, keys[3]); + block = _mm_aesenc_si128(block, keys[4]); + block = _mm_aesenc_si128(block, keys[5]); + block = _mm_aesenc_si128(block, keys[6]); + block = _mm_aesenc_si128(block, keys[7]); + block = _mm_aesenc_si128(block, keys[8]); + block = _mm_aesenc_si128(block, keys[9]); + block = _mm_aesenclast_si128(block, keys[10]); + _mm_storeu_si128(out_ptr as *mut __m128i, block); +} + +#[inline] +#[target_feature(enable = "aes")] +pub(crate) unsafe fn aes128_encrypt8(keys: &RoundKeys, in_ptr: *const Block8, out_ptr: *mut Block8) { + let mut blocks = load8(in_ptr); + xor8(&mut blocks, keys[0]); + aesenc8(&mut blocks, keys[1]); + aesenc8(&mut blocks, keys[2]); + aesenc8(&mut blocks, keys[3]); + aesenc8(&mut blocks, keys[4]); + aesenc8(&mut blocks, keys[5]); + aesenc8(&mut blocks, keys[6]); + aesenc8(&mut blocks, keys[7]); + aesenc8(&mut blocks, keys[8]); + aesenc8(&mut blocks, keys[9]); + aesenclast8(&mut blocks, keys[10]); + store8(out_ptr, blocks); +} + +#[inline] +#[target_feature(enable = "aes")] +unsafe fn aes128_decrypt1(keys: &RoundKeys, in_ptr: *const Block, out_ptr: *mut Block) { + // Safety: `loadu` and `storeu` support unaligned access + #[allow(clippy::cast_ptr_alignment)] + let mut block = _mm_loadu_si128(in_ptr as *const __m128i); + block = _mm_xor_si128(block, keys[10]); + block = _mm_aesdec_si128(block, keys[9]); + block = _mm_aesdec_si128(block, keys[8]); + block = _mm_aesdec_si128(block, keys[7]); + block = _mm_aesdec_si128(block, keys[6]); + block = _mm_aesdec_si128(block, keys[5]); + block = _mm_aesdec_si128(block, keys[4]); + block = _mm_aesdec_si128(block, keys[3]); + block = _mm_aesdec_si128(block, keys[2]); + block = _mm_aesdec_si128(block, keys[1]); + block = _mm_aesdeclast_si128(block, keys[0]); + _mm_storeu_si128(out_ptr as *mut __m128i, block); +} + +#[inline] +#[target_feature(enable = "aes")] +pub(crate) unsafe fn aes128_decrypt8(keys: &RoundKeys, in_ptr: *const Block8, out_ptr: *mut Block8) { + let mut blocks = load8(in_ptr); + xor8(&mut blocks, keys[10]); + aesdec8(&mut blocks, keys[9]); + aesdec8(&mut blocks, keys[8]); + aesdec8(&mut blocks, keys[7]); + aesdec8(&mut blocks, keys[6]); + aesdec8(&mut blocks, keys[5]); + aesdec8(&mut blocks, keys[4]); + aesdec8(&mut blocks, keys[3]); + aesdec8(&mut blocks, keys[2]); + aesdec8(&mut blocks, keys[1]); + aesdeclast8(&mut blocks, keys[0]); + store8(out_ptr, blocks); +} diff --git a/aes/src/ni/aes192.rs b/aes/src/ni/aes192.rs index 6ca30f05..92437057 100644 --- a/aes/src/ni/aes192.rs +++ b/aes/src/ni/aes192.rs @@ -1,15 +1,15 @@ use super::{ arch::*, utils::{ - aesdec8, aesdeclast8, aesenc8, aesenclast8, load8, store8, xor8, Block128, Block128x8, - U128x8, + aesdec8, aesdeclast8, aesenc8, aesenclast8, load8, store8, xor8, }, }; use cipher::{ consts::{U16, U24, U8}, generic_array::GenericArray, - BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher, + BlockCipher, BlockProcessing, BlockDecrypt, BlockEncrypt, KeyInit, InOutVal, InOutBuf, InResOutBuf, }; +use crate::{Block, Block8}; mod expand; #[cfg(test)] @@ -25,54 +25,7 @@ pub struct Aes192 { decrypt_keys: RoundKeys, } -impl Aes192 { - #[inline(always)] - pub(crate) fn encrypt8(&self, mut blocks: U128x8) -> U128x8 { - #[inline] - #[target_feature(enable = "aes")] - unsafe fn aesni192_encrypt8(keys: &RoundKeys, blocks: &mut U128x8) { - xor8(blocks, keys[0]); - aesenc8(blocks, keys[1]); - aesenc8(blocks, keys[2]); - aesenc8(blocks, keys[3]); - aesenc8(blocks, keys[4]); - aesenc8(blocks, keys[5]); - aesenc8(blocks, keys[6]); - aesenc8(blocks, keys[7]); - aesenc8(blocks, keys[8]); - aesenc8(blocks, keys[9]); - aesenc8(blocks, keys[10]); - aesenc8(blocks, keys[11]); - aesenclast8(blocks, keys[12]); - } - unsafe { aesni192_encrypt8(&self.encrypt_keys, &mut blocks) }; - blocks - } - - #[inline(always)] - pub(crate) fn encrypt(&self, block: __m128i) -> __m128i { - #[inline] - #[target_feature(enable = "aes")] - unsafe fn aesni192_encrypt1(keys: &RoundKeys, mut block: __m128i) -> __m128i { - block = _mm_xor_si128(block, keys[0]); - block = _mm_aesenc_si128(block, keys[1]); - block = _mm_aesenc_si128(block, keys[2]); - block = _mm_aesenc_si128(block, keys[3]); - block = _mm_aesenc_si128(block, keys[4]); - block = _mm_aesenc_si128(block, keys[5]); - block = _mm_aesenc_si128(block, keys[6]); - block = _mm_aesenc_si128(block, keys[7]); - block = _mm_aesenc_si128(block, keys[8]); - block = _mm_aesenc_si128(block, keys[9]); - block = _mm_aesenc_si128(block, keys[10]); - block = _mm_aesenc_si128(block, keys[11]); - _mm_aesenclast_si128(block, keys[12]) - } - unsafe { aesni192_encrypt1(&self.encrypt_keys, block) } - } -} - -impl NewBlockCipher for Aes192 { +impl KeyInit for Aes192 { type KeySize = U24; #[inline] @@ -86,86 +39,168 @@ impl NewBlockCipher for Aes192 { } } -impl BlockCipher for Aes192 { +impl BlockProcessing for Aes192 { type BlockSize = U16; - type ParBlocks = U8; } +impl BlockCipher for Aes192 {} + impl BlockEncrypt for Aes192 { - #[inline] - fn encrypt_block(&self, block: &mut Block128) { - // Safety: `loadu` and `storeu` support unaligned access - #[allow(clippy::cast_ptr_alignment)] + fn encrypt_block(&self, mut block: impl InOutVal) { + let in_ptr = block.get_in() as *const Block; + let out_ptr = block.get_out() as *mut Block; unsafe { - let b = _mm_loadu_si128(block.as_ptr() as *const __m128i); - let b = self.encrypt(b); - _mm_storeu_si128(block.as_mut_ptr() as *mut __m128i, b); + aes192_encrypt1(&self.encrypt_keys, in_ptr, out_ptr); } } - #[inline] - fn encrypt_par_blocks(&self, blocks: &mut Block128x8) { - let b = self.encrypt8(load8(blocks)); - store8(blocks, b); + fn encrypt_blocks( + &self, + mut blocks: InOutBuf<'_, '_, Block>, + proc: impl FnMut(InResOutBuf<'_, '_, '_, Block>), + ) { + blocks.chunks::( + &self.encrypt_keys, + |keys, inc, res| unsafe { + aes192_encrypt8( + keys, + inc as *const Block8, + res as *mut Block8, + ) + }, + |keys, inc, res| unsafe { + let n = inc.len(); + res[..n].copy_from_slice(inc); + aes192_encrypt8( + keys, + res as *const Block8, + res as *mut Block8, + ) + }, + proc, + ); } } impl BlockDecrypt for Aes192 { - #[inline] - fn decrypt_block(&self, block: &mut Block128) { - #[inline] - #[target_feature(enable = "aes")] - unsafe fn aes192_decrypt1(block: &mut Block128, keys: &RoundKeys) { - // Safety: `loadu` and `storeu` support unaligned access - #[allow(clippy::cast_ptr_alignment)] - let mut b = _mm_loadu_si128(block.as_ptr() as *const __m128i); - - b = _mm_xor_si128(b, keys[12]); - b = _mm_aesdec_si128(b, keys[11]); - b = _mm_aesdec_si128(b, keys[10]); - b = _mm_aesdec_si128(b, keys[9]); - b = _mm_aesdec_si128(b, keys[8]); - b = _mm_aesdec_si128(b, keys[7]); - b = _mm_aesdec_si128(b, keys[6]); - b = _mm_aesdec_si128(b, keys[5]); - b = _mm_aesdec_si128(b, keys[4]); - b = _mm_aesdec_si128(b, keys[3]); - b = _mm_aesdec_si128(b, keys[2]); - b = _mm_aesdec_si128(b, keys[1]); - b = _mm_aesdeclast_si128(b, keys[0]); - - // Safety: `loadu` and `storeu` support unaligned access - #[allow(clippy::cast_ptr_alignment)] - _mm_storeu_si128(block.as_mut_ptr() as *mut __m128i, b); + fn decrypt_block(&self, mut block: impl InOutVal) { + let in_ptr = block.get_in() as *const Block; + let out_ptr = block.get_out() as *mut Block; + unsafe { + aes192_decrypt1(&self.decrypt_keys, in_ptr, out_ptr); } - - unsafe { aes192_decrypt1(block, &self.decrypt_keys) } } - #[inline] - fn decrypt_par_blocks(&self, blocks: &mut Block128x8) { - #[inline] - #[target_feature(enable = "aes")] - unsafe fn aes192_decrypt8(blocks: &mut Block128x8, keys: &RoundKeys) { - let mut b = load8(blocks); - xor8(&mut b, keys[12]); - aesdec8(&mut b, keys[11]); - aesdec8(&mut b, keys[10]); - aesdec8(&mut b, keys[9]); - aesdec8(&mut b, keys[8]); - aesdec8(&mut b, keys[7]); - aesdec8(&mut b, keys[6]); - aesdec8(&mut b, keys[5]); - aesdec8(&mut b, keys[4]); - aesdec8(&mut b, keys[3]); - aesdec8(&mut b, keys[2]); - aesdec8(&mut b, keys[1]); - aesdeclast8(&mut b, keys[0]); - store8(blocks, b); - } - - unsafe { aes192_decrypt8(blocks, &self.decrypt_keys) } + fn decrypt_blocks( + &self, + mut blocks: InOutBuf<'_, '_, Block>, + proc: impl FnMut(InResOutBuf<'_, '_, '_, Block>), + ) { + blocks.chunks::( + &self.decrypt_keys, + |keys, inc, res| unsafe { + aes192_decrypt8( + keys, + inc as *const Block8, + res as *mut Block8, + ) + }, + |keys, inc, res| unsafe { + let n = inc.len(); + res[..n].copy_from_slice(inc); + aes192_decrypt8( + keys, + res as *const Block8, + res as *mut Block8, + ) + }, + proc, + ); } } opaque_debug::implement!(Aes192); + +#[inline] +#[target_feature(enable = "aes")] +pub(crate) unsafe fn aes192_encrypt1(keys: &RoundKeys, in_ptr: *const Block, out_ptr: *mut Block) { + // Safety: `loadu` and `storeu` support unaligned access + #[allow(clippy::cast_ptr_alignment)] + let mut block = _mm_loadu_si128(in_ptr as *const __m128i); + block = _mm_xor_si128(block, keys[0]); + block = _mm_aesenc_si128(block, keys[1]); + block = _mm_aesenc_si128(block, keys[2]); + block = _mm_aesenc_si128(block, keys[3]); + block = _mm_aesenc_si128(block, keys[4]); + block = _mm_aesenc_si128(block, keys[5]); + block = _mm_aesenc_si128(block, keys[6]); + block = _mm_aesenc_si128(block, keys[7]); + block = _mm_aesenc_si128(block, keys[8]); + block = _mm_aesenc_si128(block, keys[9]); + block = _mm_aesenc_si128(block, keys[10]); + block = _mm_aesenc_si128(block, keys[11]); + block = _mm_aesenclast_si128(block, keys[12]); + _mm_storeu_si128(out_ptr as *mut __m128i, block); +} + +#[inline] +#[target_feature(enable = "aes")] +pub(crate) unsafe fn aes192_encrypt8(keys: &RoundKeys, in_ptr: *const Block8, out_ptr: *mut Block8) { + let mut blocks = load8(in_ptr); + xor8(&mut blocks, keys[0]); + aesenc8(&mut blocks, keys[1]); + aesenc8(&mut blocks, keys[2]); + aesenc8(&mut blocks, keys[3]); + aesenc8(&mut blocks, keys[4]); + aesenc8(&mut blocks, keys[5]); + aesenc8(&mut blocks, keys[6]); + aesenc8(&mut blocks, keys[7]); + aesenc8(&mut blocks, keys[8]); + aesenc8(&mut blocks, keys[9]); + aesenc8(&mut blocks, keys[10]); + aesenc8(&mut blocks, keys[11]); + aesenclast8(&mut blocks, keys[12]); + store8(out_ptr, blocks); +} + +#[inline] +#[target_feature(enable = "aes")] +unsafe fn aes192_decrypt1(keys: &RoundKeys, in_ptr: *const Block, out_ptr: *mut Block) { + // Safety: `loadu` and `storeu` support unaligned access + #[allow(clippy::cast_ptr_alignment)] + let mut block = _mm_loadu_si128(in_ptr as *const __m128i); + block = _mm_xor_si128(block, keys[12]); + block = _mm_aesdec_si128(block, keys[11]); + block = _mm_aesdec_si128(block, keys[10]); + block = _mm_aesdec_si128(block, keys[9]); + block = _mm_aesdec_si128(block, keys[8]); + block = _mm_aesdec_si128(block, keys[7]); + block = _mm_aesdec_si128(block, keys[6]); + block = _mm_aesdec_si128(block, keys[5]); + block = _mm_aesdec_si128(block, keys[4]); + block = _mm_aesdec_si128(block, keys[3]); + block = _mm_aesdec_si128(block, keys[2]); + block = _mm_aesdec_si128(block, keys[1]); + block = _mm_aesdeclast_si128(block, keys[0]); + _mm_storeu_si128(out_ptr as *mut __m128i, block); +} + +#[inline] +#[target_feature(enable = "aes")] +pub(crate) unsafe fn aes192_decrypt8(keys: &RoundKeys, in_ptr: *const Block8, out_ptr: *mut Block8) { + let mut blocks = load8(in_ptr); + xor8(&mut blocks, keys[12]); + aesdec8(&mut blocks, keys[11]); + aesdec8(&mut blocks, keys[10]); + aesdec8(&mut blocks, keys[9]); + aesdec8(&mut blocks, keys[8]); + aesdec8(&mut blocks, keys[7]); + aesdec8(&mut blocks, keys[6]); + aesdec8(&mut blocks, keys[5]); + aesdec8(&mut blocks, keys[4]); + aesdec8(&mut blocks, keys[3]); + aesdec8(&mut blocks, keys[2]); + aesdec8(&mut blocks, keys[1]); + aesdeclast8(&mut blocks, keys[0]); + store8(out_ptr, blocks); +} diff --git a/aes/src/ni/aes256.rs b/aes/src/ni/aes256.rs index a4170ad8..dddfbb62 100644 --- a/aes/src/ni/aes256.rs +++ b/aes/src/ni/aes256.rs @@ -1,15 +1,15 @@ use super::{ arch::*, utils::{ - aesdec8, aesdeclast8, aesenc8, aesenclast8, load8, store8, xor8, Block128, Block128x8, - U128x8, + aesdec8, aesdeclast8, aesenc8, aesenclast8, load8, store8, xor8, }, }; use cipher::{ consts::{U16, U32, U8}, generic_array::GenericArray, - BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher, + BlockCipher, BlockProcessing, BlockDecrypt, BlockEncrypt, KeyInit, InOutVal, InOutBuf, InResOutBuf, }; +use crate::{Block, Block8}; mod expand; #[cfg(test)] @@ -25,58 +25,7 @@ pub struct Aes256 { decrypt_keys: RoundKeys, } -impl Aes256 { - #[inline(always)] - pub(crate) fn encrypt8(&self, mut blocks: U128x8) -> U128x8 { - #[inline] - #[target_feature(enable = "aes")] - unsafe fn aesni256_encrypt8(keys: &RoundKeys, blocks: &mut U128x8) { - xor8(blocks, keys[0]); - aesenc8(blocks, keys[1]); - aesenc8(blocks, keys[2]); - aesenc8(blocks, keys[3]); - aesenc8(blocks, keys[4]); - aesenc8(blocks, keys[5]); - aesenc8(blocks, keys[6]); - aesenc8(blocks, keys[7]); - aesenc8(blocks, keys[8]); - aesenc8(blocks, keys[9]); - aesenc8(blocks, keys[10]); - aesenc8(blocks, keys[11]); - aesenc8(blocks, keys[12]); - aesenc8(blocks, keys[13]); - aesenclast8(blocks, keys[14]); - } - unsafe { aesni256_encrypt8(&self.encrypt_keys, &mut blocks) }; - blocks - } - - #[inline(always)] - pub(crate) fn encrypt(&self, block: __m128i) -> __m128i { - #[inline] - #[target_feature(enable = "aes")] - unsafe fn aesni256_encrypt1(keys: &RoundKeys, mut block: __m128i) -> __m128i { - block = _mm_xor_si128(block, keys[0]); - block = _mm_aesenc_si128(block, keys[1]); - block = _mm_aesenc_si128(block, keys[2]); - block = _mm_aesenc_si128(block, keys[3]); - block = _mm_aesenc_si128(block, keys[4]); - block = _mm_aesenc_si128(block, keys[5]); - block = _mm_aesenc_si128(block, keys[6]); - block = _mm_aesenc_si128(block, keys[7]); - block = _mm_aesenc_si128(block, keys[8]); - block = _mm_aesenc_si128(block, keys[9]); - block = _mm_aesenc_si128(block, keys[10]); - block = _mm_aesenc_si128(block, keys[11]); - block = _mm_aesenc_si128(block, keys[12]); - block = _mm_aesenc_si128(block, keys[13]); - _mm_aesenclast_si128(block, keys[14]) - } - unsafe { aesni256_encrypt1(&self.encrypt_keys, block) } - } -} - -impl NewBlockCipher for Aes256 { +impl KeyInit for Aes256 { type KeySize = U32; #[inline] @@ -90,90 +39,175 @@ impl NewBlockCipher for Aes256 { } } -impl BlockCipher for Aes256 { +impl BlockProcessing for Aes256 { type BlockSize = U16; - type ParBlocks = U8; } +impl BlockCipher for Aes256 {} + impl BlockEncrypt for Aes256 { - #[inline] - fn encrypt_block(&self, block: &mut Block128) { - // Safety: `loadu` and `storeu` support unaligned access - #[allow(clippy::cast_ptr_alignment)] + fn encrypt_block(&self, mut block: impl InOutVal) { + let in_ptr = block.get_in() as *const Block; + let out_ptr = block.get_out() as *mut Block; unsafe { - let b = _mm_loadu_si128(block.as_ptr() as *const __m128i); - let b = self.encrypt(b); - _mm_storeu_si128(block.as_mut_ptr() as *mut __m128i, b); + aes256_encrypt1(&self.encrypt_keys, in_ptr, out_ptr); } } - #[inline] - fn encrypt_par_blocks(&self, blocks: &mut Block128x8) { - let b = self.encrypt8(load8(blocks)); - store8(blocks, b); + fn encrypt_blocks( + &self, + mut blocks: InOutBuf<'_, '_, Block>, + proc: impl FnMut(InResOutBuf<'_, '_, '_, Block>), + ) { + blocks.chunks::( + &self.encrypt_keys, + |keys, inc, res| unsafe { + aes256_encrypt8( + keys, + inc as *const Block8, + res as *mut Block8, + ) + }, + |keys, inc, res| unsafe { + let n = inc.len(); + res[..n].copy_from_slice(inc); + aes256_encrypt8( + keys, + res as *const Block8, + res as *mut Block8, + ) + }, + proc, + ); } } impl BlockDecrypt for Aes256 { - #[inline] - fn decrypt_block(&self, block: &mut Block128) { - #[inline] - #[target_feature(enable = "aes")] - unsafe fn aes256_decrypt1(block: &mut Block128, keys: &RoundKeys) { - // Safety: `loadu` and `storeu` support unaligned access - #[allow(clippy::cast_ptr_alignment)] - let mut b = _mm_loadu_si128(block.as_ptr() as *const __m128i); - - b = _mm_xor_si128(b, keys[14]); - b = _mm_aesdec_si128(b, keys[13]); - b = _mm_aesdec_si128(b, keys[12]); - b = _mm_aesdec_si128(b, keys[11]); - b = _mm_aesdec_si128(b, keys[10]); - b = _mm_aesdec_si128(b, keys[9]); - b = _mm_aesdec_si128(b, keys[8]); - b = _mm_aesdec_si128(b, keys[7]); - b = _mm_aesdec_si128(b, keys[6]); - b = _mm_aesdec_si128(b, keys[5]); - b = _mm_aesdec_si128(b, keys[4]); - b = _mm_aesdec_si128(b, keys[3]); - b = _mm_aesdec_si128(b, keys[2]); - b = _mm_aesdec_si128(b, keys[1]); - b = _mm_aesdeclast_si128(b, keys[0]); - - // Safety: `loadu` and `storeu` support unaligned access - #[allow(clippy::cast_ptr_alignment)] - _mm_storeu_si128(block.as_mut_ptr() as *mut __m128i, b); + fn decrypt_block(&self, mut block: impl InOutVal) { + let in_ptr = block.get_in() as *const Block; + let out_ptr = block.get_out() as *mut Block; + unsafe { + aes256_decrypt1(&self.decrypt_keys, in_ptr, out_ptr); } - - unsafe { aes256_decrypt1(block, &self.decrypt_keys) } } - - #[inline] - fn decrypt_par_blocks(&self, blocks: &mut Block128x8) { - #[inline] - #[target_feature(enable = "aes")] - unsafe fn aes256_decrypt8(blocks: &mut Block128x8, keys: &RoundKeys) { - let mut b = load8(blocks); - xor8(&mut b, keys[14]); - aesdec8(&mut b, keys[13]); - aesdec8(&mut b, keys[12]); - aesdec8(&mut b, keys[11]); - aesdec8(&mut b, keys[10]); - aesdec8(&mut b, keys[9]); - aesdec8(&mut b, keys[8]); - aesdec8(&mut b, keys[7]); - aesdec8(&mut b, keys[6]); - aesdec8(&mut b, keys[5]); - aesdec8(&mut b, keys[4]); - aesdec8(&mut b, keys[3]); - aesdec8(&mut b, keys[2]); - aesdec8(&mut b, keys[1]); - aesdeclast8(&mut b, keys[0]); - store8(blocks, b); - } - - unsafe { aes256_decrypt8(blocks, &self.decrypt_keys) } + fn decrypt_blocks( + &self, + mut blocks: InOutBuf<'_, '_, Block>, + proc: impl FnMut(InResOutBuf<'_, '_, '_, Block>), + ) { + blocks.chunks::( + &self.decrypt_keys, + |keys, inc, res| unsafe { + aes256_decrypt8( + keys, + inc as *const Block8, + res as *mut Block8, + ) + }, + |keys, inc, res| unsafe { + let n = inc.len(); + res[..n].copy_from_slice(inc); + aes256_decrypt8( + keys, + res as *const Block8, + res as *mut Block8, + ) + }, + proc, + ); } } opaque_debug::implement!(Aes256); + +#[inline] +#[target_feature(enable = "aes")] +pub(crate) unsafe fn aes256_encrypt1(keys: &RoundKeys, in_ptr: *const Block, out_ptr: *mut Block) { + // Safety: `loadu` and `storeu` support unaligned access + #[allow(clippy::cast_ptr_alignment)] + let mut block = _mm_loadu_si128(in_ptr as *const __m128i); + block = _mm_xor_si128(block, keys[0]); + block = _mm_aesenc_si128(block, keys[1]); + block = _mm_aesenc_si128(block, keys[2]); + block = _mm_aesenc_si128(block, keys[3]); + block = _mm_aesenc_si128(block, keys[4]); + block = _mm_aesenc_si128(block, keys[5]); + block = _mm_aesenc_si128(block, keys[6]); + block = _mm_aesenc_si128(block, keys[7]); + block = _mm_aesenc_si128(block, keys[8]); + block = _mm_aesenc_si128(block, keys[9]); + block = _mm_aesenc_si128(block, keys[10]); + block = _mm_aesenc_si128(block, keys[11]); + block = _mm_aesenc_si128(block, keys[12]); + block = _mm_aesenc_si128(block, keys[13]); + block = _mm_aesenclast_si128(block, keys[14]); + _mm_storeu_si128(out_ptr as *mut __m128i, block); +} + +#[inline] +#[target_feature(enable = "aes")] +pub(crate) unsafe fn aes256_encrypt8(keys: &RoundKeys, in_ptr: *const Block8, out_ptr: *mut Block8) { + let mut blocks = load8(in_ptr); + xor8(&mut blocks, keys[0]); + aesenc8(&mut blocks, keys[1]); + aesenc8(&mut blocks, keys[2]); + aesenc8(&mut blocks, keys[3]); + aesenc8(&mut blocks, keys[4]); + aesenc8(&mut blocks, keys[5]); + aesenc8(&mut blocks, keys[6]); + aesenc8(&mut blocks, keys[7]); + aesenc8(&mut blocks, keys[8]); + aesenc8(&mut blocks, keys[9]); + aesenc8(&mut blocks, keys[10]); + aesenc8(&mut blocks, keys[11]); + aesenc8(&mut blocks, keys[12]); + aesenc8(&mut blocks, keys[13]); + aesenclast8(&mut blocks, keys[14]); + store8(out_ptr, blocks); +} + +#[inline] +#[target_feature(enable = "aes")] +unsafe fn aes256_decrypt1(keys: &RoundKeys, in_ptr: *const Block, out_ptr: *mut Block) { + // Safety: `loadu` and `storeu` support unaligned access + #[allow(clippy::cast_ptr_alignment)] + let mut block = _mm_loadu_si128(in_ptr as *const __m128i); + block = _mm_xor_si128(block, keys[14]); + block = _mm_aesdec_si128(block, keys[13]); + block = _mm_aesdec_si128(block, keys[12]); + block = _mm_aesdec_si128(block, keys[11]); + block = _mm_aesdec_si128(block, keys[10]); + block = _mm_aesdec_si128(block, keys[9]); + block = _mm_aesdec_si128(block, keys[8]); + block = _mm_aesdec_si128(block, keys[7]); + block = _mm_aesdec_si128(block, keys[6]); + block = _mm_aesdec_si128(block, keys[5]); + block = _mm_aesdec_si128(block, keys[4]); + block = _mm_aesdec_si128(block, keys[3]); + block = _mm_aesdec_si128(block, keys[2]); + block = _mm_aesdec_si128(block, keys[1]); + block = _mm_aesdeclast_si128(block, keys[0]); + _mm_storeu_si128(out_ptr as *mut __m128i, block); +} + +#[inline] +#[target_feature(enable = "aes")] +pub(crate) unsafe fn aes256_decrypt8(keys: &RoundKeys, in_ptr: *const Block8, out_ptr: *mut Block8) { + let mut blocks = load8(in_ptr); + xor8(&mut blocks, keys[14]); + aesdec8(&mut blocks, keys[13]); + aesdec8(&mut blocks, keys[12]); + aesdec8(&mut blocks, keys[11]); + aesdec8(&mut blocks, keys[10]); + aesdec8(&mut blocks, keys[9]); + aesdec8(&mut blocks, keys[8]); + aesdec8(&mut blocks, keys[7]); + aesdec8(&mut blocks, keys[6]); + aesdec8(&mut blocks, keys[5]); + aesdec8(&mut blocks, keys[4]); + aesdec8(&mut blocks, keys[3]); + aesdec8(&mut blocks, keys[2]); + aesdec8(&mut blocks, keys[1]); + aesdeclast8(&mut blocks, keys[0]); + store8(out_ptr, blocks); +} diff --git a/aes/src/ni/utils.rs b/aes/src/ni/utils.rs index c24d98ce..b7b1eb6d 100644 --- a/aes/src/ni/utils.rs +++ b/aes/src/ni/utils.rs @@ -4,13 +4,8 @@ #![allow(clippy::needless_range_loop)] use super::arch::*; -use cipher::{ - consts::{U16, U8}, - generic_array::GenericArray, -}; +use crate::{Block, Block8}; -pub type Block128 = GenericArray; -pub type Block128x8 = GenericArray, U8>; pub type U128x8 = [__m128i; 8]; #[cfg(test)] @@ -23,32 +18,34 @@ pub(crate) fn check(a: &[__m128i], b: &[[u64; 2]]) { } #[inline(always)] -pub(crate) fn load8(blocks: &Block128x8) -> U128x8 { +pub(crate) fn load8(blocks: *const Block8) -> U128x8 { unsafe { + let p = blocks as *const Block; [ - _mm_loadu_si128(blocks[0].as_ptr() as *const __m128i), - _mm_loadu_si128(blocks[1].as_ptr() as *const __m128i), - _mm_loadu_si128(blocks[2].as_ptr() as *const __m128i), - _mm_loadu_si128(blocks[3].as_ptr() as *const __m128i), - _mm_loadu_si128(blocks[4].as_ptr() as *const __m128i), - _mm_loadu_si128(blocks[5].as_ptr() as *const __m128i), - _mm_loadu_si128(blocks[6].as_ptr() as *const __m128i), - _mm_loadu_si128(blocks[7].as_ptr() as *const __m128i), + _mm_loadu_si128(p.add(0) as *const __m128i), + _mm_loadu_si128(p.add(1) as *const __m128i), + _mm_loadu_si128(p.add(2) as *const __m128i), + _mm_loadu_si128(p.add(3) as *const __m128i), + _mm_loadu_si128(p.add(4) as *const __m128i), + _mm_loadu_si128(p.add(5) as *const __m128i), + _mm_loadu_si128(p.add(6) as *const __m128i), + _mm_loadu_si128(p.add(7) as *const __m128i), ] } } #[inline(always)] -pub(crate) fn store8(blocks: &mut Block128x8, b: U128x8) { +pub(crate) fn store8(blocks: *mut Block8, b: U128x8) { unsafe { - _mm_storeu_si128(blocks[0].as_mut_ptr() as *mut __m128i, b[0]); - _mm_storeu_si128(blocks[1].as_mut_ptr() as *mut __m128i, b[1]); - _mm_storeu_si128(blocks[2].as_mut_ptr() as *mut __m128i, b[2]); - _mm_storeu_si128(blocks[3].as_mut_ptr() as *mut __m128i, b[3]); - _mm_storeu_si128(blocks[4].as_mut_ptr() as *mut __m128i, b[4]); - _mm_storeu_si128(blocks[5].as_mut_ptr() as *mut __m128i, b[5]); - _mm_storeu_si128(blocks[6].as_mut_ptr() as *mut __m128i, b[6]); - _mm_storeu_si128(blocks[7].as_mut_ptr() as *mut __m128i, b[7]); + let p = blocks as *const Block; + _mm_storeu_si128(p.add(0) as *mut __m128i, b[0]); + _mm_storeu_si128(p.add(1) as *mut __m128i, b[1]); + _mm_storeu_si128(p.add(2) as *mut __m128i, b[2]); + _mm_storeu_si128(p.add(3) as *mut __m128i, b[3]); + _mm_storeu_si128(p.add(4) as *mut __m128i, b[4]); + _mm_storeu_si128(p.add(5) as *mut __m128i, b[5]); + _mm_storeu_si128(p.add(6) as *mut __m128i, b[6]); + _mm_storeu_si128(p.add(7) as *mut __m128i, b[7]); } } diff --git a/aes/src/soft/fixslice64.rs b/aes/src/soft/fixslice64.rs index 802081e4..6b11f892 100644 --- a/aes/src/soft/fixslice64.rs +++ b/aes/src/soft/fixslice64.rs @@ -15,15 +15,12 @@ #![allow(clippy::unreadable_literal)] -use crate::Block; use cipher::{ consts::{U16, U24, U32}, generic_array::GenericArray, }; use core::convert::TryInto; - -/// AES block batch size for this implementation -pub(crate) const FIXSLICE_BLOCKS: usize = 2; +use crate::Block2; /// AES-128 round keys pub(crate) type FixsliceKeys128 = [u32; 88]; @@ -244,8 +241,7 @@ pub(crate) fn aes256_key_schedule(key: &GenericArray) -> FixsliceKeys25 /// Fully-fixsliced AES-128 decryption (the InvShiftRows is completely omitted). /// /// Decrypts four blocks in-place and in parallel. -pub(crate) fn aes128_decrypt(rkeys: &FixsliceKeys128, blocks: &mut [Block]) { - debug_assert_eq!(blocks.len(), FIXSLICE_BLOCKS); +pub(crate) fn aes128_decrypt(rkeys: &FixsliceKeys128, blocks: &Block2) -> Block2 { let mut state = State::default(); bitslice(&mut state, &blocks[0], &blocks[1]); @@ -295,14 +291,13 @@ pub(crate) fn aes128_decrypt(rkeys: &FixsliceKeys128, blocks: &mut [Block]) { add_round_key(&mut state, &rkeys[..8]); - inv_bitslice(&mut state, blocks); + inv_bitslice(&mut state) } /// Fully-fixsliced AES-128 encryption (the ShiftRows is completely omitted). /// /// Encrypts four blocks in-place and in parallel. -pub(crate) fn aes128_encrypt(rkeys: &FixsliceKeys128, blocks: &mut [Block]) { - debug_assert_eq!(blocks.len(), FIXSLICE_BLOCKS); +pub(crate) fn aes128_encrypt(rkeys: &FixsliceKeys128, blocks: &Block2) -> Block2 { let mut state = State::default(); bitslice(&mut state, &blocks[0], &blocks[1]); @@ -352,14 +347,13 @@ pub(crate) fn aes128_encrypt(rkeys: &FixsliceKeys128, blocks: &mut [Block]) { sub_bytes(&mut state); add_round_key(&mut state, &rkeys[80..]); - inv_bitslice(&mut state, blocks); + inv_bitslice(&mut state) } /// Fully-fixsliced AES-192 decryption (the InvShiftRows is completely omitted). /// /// Decrypts four blocks in-place and in parallel. -pub(crate) fn aes192_decrypt(rkeys: &FixsliceKeys192, blocks: &mut [Block]) { - debug_assert_eq!(blocks.len(), FIXSLICE_BLOCKS); +pub(crate) fn aes192_decrypt(rkeys: &FixsliceKeys192, blocks: &Block2) -> Block2 { let mut state = State::default(); bitslice(&mut state, &blocks[0], &blocks[1]); @@ -403,14 +397,13 @@ pub(crate) fn aes192_decrypt(rkeys: &FixsliceKeys192, blocks: &mut [Block]) { add_round_key(&mut state, &rkeys[..8]); - inv_bitslice(&mut state, blocks); + inv_bitslice(&mut state) } /// Fully-fixsliced AES-192 encryption (the ShiftRows is completely omitted). /// /// Encrypts four blocks in-place and in parallel. -pub(crate) fn aes192_encrypt(rkeys: &FixsliceKeys192, blocks: &mut [Block]) { - debug_assert_eq!(blocks.len(), FIXSLICE_BLOCKS); +pub(crate) fn aes192_encrypt(rkeys: &FixsliceKeys192, blocks: &Block2) -> Block2 { let mut state = State::default(); bitslice(&mut state, &blocks[0], &blocks[1]); @@ -454,14 +447,13 @@ pub(crate) fn aes192_encrypt(rkeys: &FixsliceKeys192, blocks: &mut [Block]) { sub_bytes(&mut state); add_round_key(&mut state, &rkeys[96..]); - inv_bitslice(&mut state, blocks); + inv_bitslice(&mut state) } /// Fully-fixsliced AES-256 decryption (the InvShiftRows is completely omitted). /// /// Decrypts four blocks in-place and in parallel. -pub(crate) fn aes256_decrypt(rkeys: &FixsliceKeys256, blocks: &mut [Block]) { - debug_assert_eq!(blocks.len(), FIXSLICE_BLOCKS); +pub(crate) fn aes256_decrypt(rkeys: &FixsliceKeys256, blocks: &Block2) -> Block2 { let mut state = State::default(); bitslice(&mut state, &blocks[0], &blocks[1]); @@ -511,14 +503,13 @@ pub(crate) fn aes256_decrypt(rkeys: &FixsliceKeys256, blocks: &mut [Block]) { add_round_key(&mut state, &rkeys[..8]); - inv_bitslice(&mut state, blocks); + inv_bitslice(&mut state) } /// Fully-fixsliced AES-256 encryption (the ShiftRows is completely omitted). /// /// Encrypts four blocks in-place and in parallel. -pub(crate) fn aes256_encrypt(rkeys: &FixsliceKeys256, blocks: &mut [Block]) { - debug_assert_eq!(blocks.len(), FIXSLICE_BLOCKS); +pub(crate) fn aes256_encrypt(rkeys: &FixsliceKeys256, blocks: &Block2) -> Block2 { let mut state = State::default(); bitslice(&mut state, &blocks[0], &blocks[1]); @@ -568,7 +559,7 @@ pub(crate) fn aes256_encrypt(rkeys: &FixsliceKeys256, blocks: &mut [Block]) { sub_bytes(&mut state); add_round_key(&mut state, &rkeys[112..]); - inv_bitslice(&mut state, blocks); + inv_bitslice(&mut state) } /// Note that the 4 bitwise NOT (^= 0xffffffff) are accounted for here so that it is a true @@ -1234,9 +1225,8 @@ fn bitslice(output: &mut [u32], input0: &[u8], input1: &[u8]) { } /// Un-bitslice a 256-bit internal state into two 128-bit blocks of output. -fn inv_bitslice(input: &mut [u32], output: &mut [Block]) { +fn inv_bitslice(input: &mut [u32]) -> Block2 { debug_assert_eq!(input.len(), 8); - debug_assert_eq!(output.len(), 2); // Unbitslicing is a bit index manipulation. 256 bits of data means each bit is positioned at // an 8-bit index. AES data is 2 blocks, each one a 4x4 column-major matrix of bytes, so the @@ -1283,6 +1273,7 @@ fn inv_bitslice(input: &mut [u32], output: &mut [Block]) { // De-interleave the columns on output (note the order of output) // c1 c0 b0 __ __ __ __ __ => b0 c1 c0 __ __ __ __ __ + let mut output = Block2::default(); output[0][0x00..0x04].copy_from_slice(&t0.to_le_bytes()); output[0][0x04..0x08].copy_from_slice(&t2.to_le_bytes()); output[0][0x08..0x0c].copy_from_slice(&t4.to_le_bytes()); @@ -1294,6 +1285,7 @@ fn inv_bitslice(input: &mut [u32], output: &mut [Block]) { // Final AES bit index, as desired: // b0 c1 c0 r1 r0 p2 p1 p0 + output } /// Copy 32-bytes within the provided slice to an 8-byte offset diff --git a/aes/src/soft/impls.rs b/aes/src/soft/impls.rs index 5b76926c..69c97cba 100644 --- a/aes/src/soft/impls.rs +++ b/aes/src/soft/impls.rs @@ -1,13 +1,13 @@ //! Macros for implementing `Aes*` structs and the `BlockCipher` interface use cipher::{ - consts::{U16, U24, U32, U8}, + consts::{U16, U24, U32, U2}, generic_array::GenericArray, - BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher, + BlockProcessing, BlockCipher, BlockDecrypt, BlockEncrypt, KeyInit, InOutVal, InOutBuf, InResOutBuf, }; +use crate::{Block, Block2}; -use super::fixslice::{self, FixsliceKeys128, FixsliceKeys192, FixsliceKeys256, FIXSLICE_BLOCKS}; -use crate::{Block, ParBlocks}; +use super::fixslice::{self, FixsliceKeys128, FixsliceKeys192, FixsliceKeys256}; macro_rules! define_aes_impl { ( @@ -25,7 +25,7 @@ macro_rules! define_aes_impl { keys: $fixslice_keys, } - impl NewBlockCipher for $name { + impl KeyInit for $name { type KeySize = $key_size; #[inline] @@ -34,42 +34,66 @@ macro_rules! define_aes_impl { } } - impl BlockCipher for $name { + impl BlockProcessing for $name { type BlockSize = U16; - type ParBlocks = U8; } + impl BlockCipher for $name {} + impl BlockEncrypt for $name { - #[inline] - fn encrypt_block(&self, block: &mut Block) { - let mut blocks = [Block::default(); FIXSLICE_BLOCKS]; - blocks[0].copy_from_slice(block); - $fixslice_encrypt(&self.keys, &mut blocks); - block.copy_from_slice(&blocks[0]); + fn encrypt_block(&self, mut block: impl InOutVal) { + let mut t = Block2::default(); + t[0] = *(block.get_in()); + *(block.get_out()) = $fixslice_encrypt(&self.keys, &t)[0]; } - #[inline] - fn encrypt_par_blocks(&self, blocks: &mut ParBlocks) { - for chunk in blocks.chunks_mut(FIXSLICE_BLOCKS) { - $fixslice_encrypt(&self.keys, chunk); - } + fn encrypt_blocks( + &self, + mut blocks: InOutBuf<'_, '_, Block>, + proc: impl FnMut(InResOutBuf<'_, '_, '_, Block>), + ) { + blocks.chunks::( + &self.keys, + |keys, inc, res| *res = $fixslice_encrypt(keys, inc), + |keys, inc, res| { + debug_assert_eq!(inc.len(), 1); + res[0] = inc[0]; + res[0] = $fixslice_encrypt( + keys, + &res + )[0]; + }, + proc, + ); } } impl BlockDecrypt for $name { #[inline] - fn decrypt_block(&self, block: &mut Block) { - let mut blocks = [Block::default(); FIXSLICE_BLOCKS]; - blocks[0].copy_from_slice(block); - $fixslice_decrypt(&self.keys, &mut blocks); - block.copy_from_slice(&blocks[0]); + fn decrypt_block(&self, mut block: impl InOutVal) { + let mut t = Block2::default(); + t[0] = *(block.get_in()); + *(block.get_out()) = $fixslice_decrypt(&self.keys, &t)[0]; } - #[inline] - fn decrypt_par_blocks(&self, blocks: &mut ParBlocks) { - for chunk in blocks.chunks_mut(FIXSLICE_BLOCKS) { - $fixslice_decrypt(&self.keys, chunk); - } + fn decrypt_blocks( + &self, + mut blocks: InOutBuf<'_, '_, Block>, + proc: impl FnMut(InResOutBuf<'_, '_, '_, Block>), + ) { + blocks.chunks::( + &self.keys, + |keys, inc, res| *res = $fixslice_decrypt(keys, inc), + |keys, inc, res| { + debug_assert_eq!(inc.len(), 1); + res[0] = inc[0]; + res[0] = $fixslice_decrypt( + keys, + &res + )[0]; + }, + proc, + ); } } diff --git a/block-modes/Cargo.toml b/block-modes/Cargo.toml index 90a604c8..aa251234 100644 --- a/block-modes/Cargo.toml +++ b/block-modes/Cargo.toml @@ -11,12 +11,11 @@ repository = "https://github.com/RustCrypto/block-ciphers" keywords = ["crypto", "block-cipher", "ciphers"] [dependencies] -block-padding = "0.2" -cipher = "=0.3.0-pre.4" +cipher = { git = "https://github.com/RustCrypto/traits", branch = "experimental_design" } [dev-dependencies] aes = { version = "=0.7.0-pre", path = "../aes", features = ["force-soft"] } -hex-literal = "0.2" +#hex-literal = "0.2" [features] default = ["std"] diff --git a/block-modes/src/cbc.rs b/block-modes/src/cbc.rs index c7455762..2336a005 100644 --- a/block-modes/src/cbc.rs +++ b/block-modes/src/cbc.rs @@ -1,94 +1,104 @@ -use crate::traits::{BlockMode, IvState}; -use crate::utils::{get_par_blocks, xor, Block, ParBlocks}; -use block_padding::Padding; -use cipher::generic_array::{typenum::Unsigned, GenericArray}; -use cipher::{BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher}; -use core::marker::PhantomData; +//! [Cipher Block Chaining][1] (CBC) mode. +//! +//! [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC +use crate::{xor, xor_ret}; +use cipher::{ + generic_array::GenericArray, Block, BlockCipher, BlockDecryptMut, BlockEncryptMut, + BlockProcessing, InOutBuf, InOutVal, InResOutBuf, InnerIvInit, IvState, +}; -/// [Cipher Block Chaining][1] (CBC) block cipher mode instance. -/// -/// [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC +/// CBC mode encryptor. #[derive(Clone)] -pub struct Cbc { +pub struct Encrypt { cipher: C, - iv: GenericArray, - _p: PhantomData

, + iv: Block, } -impl Cbc -where - C: BlockCipher + BlockEncrypt + BlockDecrypt + NewBlockCipher, - P: Padding, -{ - #[inline(always)] - fn single_blocks_decrypt(&mut self, blocks: &mut [Block]) { - let mut iv = self.iv.clone(); - for block in blocks { - let block_copy = block.clone(); - self.cipher.decrypt_block(block); - xor(block, iv.as_slice()); - iv = block_copy; - } - self.iv = iv; +impl BlockEncryptMut for Encrypt { + fn encrypt_block(&mut self, mut block: impl InOutVal>) { + let t = xor_ret(block.get_in(), &self.iv); + self.cipher.encrypt_block((&t, block.get_out())); + self.iv = block.get_out().clone(); } } -impl BlockMode for Cbc -where - C: BlockCipher + BlockEncrypt + BlockDecrypt + NewBlockCipher, - P: Padding, -{ +impl BlockProcessing for Encrypt { + type BlockSize = C::BlockSize; +} + +impl InnerIvInit for Encrypt { + type Inner = C; type IvSize = C::BlockSize; - fn new(cipher: C, iv: &Block) -> Self { + #[inline] + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { Self { cipher, iv: iv.clone(), - _p: Default::default(), } } +} - fn encrypt_blocks(&mut self, blocks: &mut [Block]) { - self.iv = { - let mut iv = &self.iv; - for block in blocks { - xor(block, &iv); - self.cipher.encrypt_block(block); - iv = block; - } - iv.clone() - }; +impl IvState for Encrypt { + #[inline] + fn iv_state(&self) -> GenericArray { + self.iv.clone() } +} + +/// CBC mode decryptor. +#[derive(Clone)] +pub struct Decrypt { + cipher: C, + iv: Block, +} - fn decrypt_blocks(&mut self, blocks: &mut [Block]) { - let pbn = C::ParBlocks::to_usize(); - if pbn != 1 { - let (par_blocks, leftover) = get_par_blocks::(blocks); - let mut iv_buf = ParBlocks::::default(); - iv_buf[0] = self.iv.clone(); - for pb in par_blocks { - iv_buf[1..].clone_from_slice(&pb[..pbn - 1]); - let next_iv = pb[pbn - 1].clone(); - self.cipher.decrypt_blocks(pb); - pb.iter_mut() - .zip(iv_buf.iter()) - .for_each(|(a, b)| xor(a, b)); - iv_buf[0] = next_iv; +impl BlockDecryptMut for Decrypt { + fn decrypt_block(&mut self, mut block: impl InOutVal>) { + let enc_block = block.get_in().clone(); + self.cipher.decrypt_block((&enc_block, block.get_out())); + xor(block.get_out(), &self.iv); + self.iv = enc_block; + } + + fn decrypt_blocks( + &mut self, + blocks: InOutBuf<'_, '_, Block>, + mut proc: impl FnMut(InResOutBuf<'_, '_, '_, Block>), + ) { + let iv = &mut self.iv; + self.cipher.decrypt_blocks(blocks, |mut buf| { + let len = buf.len(); + let (in_buf, res_buf) = buf.get_in_res(); + xor(&mut res_buf[0], iv); + for i in 1..len { + xor(&mut res_buf[i], &in_buf[i - 1]); } - self.iv = iv_buf[0].clone(); - self.single_blocks_decrypt(leftover); - } else { - self.single_blocks_decrypt(blocks); + *iv = in_buf[len - 1].clone(); + proc(buf); + }); + } +} + +impl BlockProcessing for Decrypt { + type BlockSize = C::BlockSize; +} + +impl InnerIvInit for Decrypt { + type Inner = C; + type IvSize = C::BlockSize; + + #[inline] + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { + Self { + cipher, + iv: iv.clone(), } } } -impl IvState for Cbc -where - C: BlockCipher + BlockEncrypt + BlockDecrypt + NewBlockCipher, - P: Padding, -{ - fn iv_state(&self) -> GenericArray>::IvSize> { +impl IvState for Decrypt { + fn iv_state(&self) -> GenericArray { self.iv.clone() } } diff --git a/block-modes/src/cfb.rs b/block-modes/src/cfb.rs index b51b213a..15350014 100644 --- a/block-modes/src/cfb.rs +++ b/block-modes/src/cfb.rs @@ -1,124 +1,110 @@ -use crate::{ - traits::{BlockMode, IvState}, - utils::{xor, Block, ParBlocks}, -}; -use block_padding::Padding; +//! [Cipher feedback][1] (CFB) mode with full block feedback. +//! +//! [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_(CFB) +use crate::xor; use cipher::{ - generic_array::{typenum::Unsigned, GenericArray}, - BlockCipher, BlockEncrypt, NewBlockCipher, + generic_array::GenericArray, AsyncStreamCipher, Block, BlockCipher, BlockDecryptMut, + BlockEncryptMut, BlockProcessing, InOutBuf, InOutVal, InResOutBuf, InnerIvInit, IvState, }; -use core::{marker::PhantomData, ptr}; -/// [Cipher feedback][1] (CFB) block mode instance with a full block feedback. -/// -/// [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_(CFB) +/// CFB mode encryptor. #[derive(Clone)] -pub struct Cfb { +pub struct Encrypt { cipher: C, - iv: GenericArray, - _p: PhantomData

, + iv: Block, +} + +impl BlockEncryptMut for Encrypt { + fn encrypt_block(&mut self, mut block: impl InOutVal>) { + self.cipher.encrypt_block(&mut self.iv); + xor(&mut self.iv, block.get_in()); + *block.get_out() = self.iv.clone(); + } +} + +impl BlockProcessing for Encrypt { + type BlockSize = C::BlockSize; } -impl BlockMode for Cfb -where - C: BlockCipher + BlockEncrypt + NewBlockCipher, - P: Padding, -{ +impl AsyncStreamCipher for Encrypt {} + +impl InnerIvInit for Encrypt { + type Inner = C; type IvSize = C::BlockSize; - fn new(cipher: C, iv: &Block) -> Self { + #[inline] + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { Self { cipher, iv: iv.clone(), - _p: Default::default(), } } +} - fn encrypt_blocks(&mut self, blocks: &mut [Block]) { - for block in blocks { - self.cipher.encrypt_block(&mut self.iv); - xor_set1(block, self.iv.as_mut_slice()); - } +impl IvState for Encrypt { + fn iv_state(&self) -> GenericArray { + self.iv.clone() } +} - fn decrypt_blocks(&mut self, mut blocks: &mut [Block]) { - let pb = C::ParBlocks::to_usize(); - - if blocks.len() > pb { - // SAFETY: we have checked that `blocks` has enough elements - #[allow(unsafe_code)] - let mut par_iv = read_par_block::(&blocks[..pb]); - - let (b, r) = { blocks }.split_at_mut(1); - blocks = r; - self.cipher.encrypt_block(&mut self.iv); - xor(&mut b[0], &self.iv); - - // Remember IV for trailing blocks - self.iv = blocks[blocks.len() - (blocks.len() % pb) - 1].clone(); - - while blocks.len() >= 2 * pb { - let next_par_iv = read_par_block::(&blocks[pb - 1..2 * pb - 1]); - self.cipher.encrypt_blocks(&mut par_iv); - let (par_block, r) = { blocks }.split_at_mut(pb); - blocks = r; - - for (a, b) in par_block.iter_mut().zip(par_iv.iter()) { - xor(a, b) - } - par_iv = next_par_iv; - } +/// CFB mode decryptor. +#[derive(Clone)] +pub struct Decrypt { + cipher: C, + iv: Block, +} - self.cipher.encrypt_blocks(&mut par_iv); - let (par_block, r) = { blocks }.split_at_mut(pb); - blocks = r; +impl BlockDecryptMut for Decrypt { + fn decrypt_block(&mut self, mut block: impl InOutVal>) { + let mut t = self.iv.clone(); + self.cipher.encrypt_block(&mut t); + xor(&mut t, block.get_in()); + self.iv = block.get_in().clone(); + *block.get_out() = t; + } - for (a, b) in par_block.iter_mut().zip(par_iv[..pb].iter()) { - xor(a, b) + fn decrypt_blocks( + &mut self, + blocks: InOutBuf<'_, '_, Block>, + mut proc: impl FnMut(InResOutBuf<'_, '_, '_, Block>), + ) { + let mut enc_iv = self.iv.clone(); + self.cipher.encrypt_block(&mut enc_iv); + let iv = &mut self.iv; + self.cipher.encrypt_blocks(blocks, |mut buf| { + let len = buf.len(); + let (in_buf, res_buf) = buf.get_in_res(); + for i in 0..len { + xor(&mut enc_iv, &in_buf[i]); + core::mem::swap(&mut res_buf[i], &mut enc_iv); } - } - - for block in blocks { - self.cipher.encrypt_block(&mut self.iv); - xor_set2(block, self.iv.as_mut_slice()); - } + *iv = in_buf[len - 1].clone(); + proc(buf); + }); } } -impl IvState for Cfb -where - C: BlockCipher + BlockEncrypt + NewBlockCipher, - P: Padding, -{ - fn iv_state(&self) -> GenericArray { - self.iv.clone() - } +impl BlockProcessing for Decrypt { + type BlockSize = C::BlockSize; } -#[inline(always)] -fn read_par_block(blocks: &[Block]) -> ParBlocks { - assert!(blocks.len() >= C::ParBlocks::to_usize()); - // SAFETY: assert checks that `blocks` is long enough - #[allow(unsafe_code)] - unsafe { - ptr::read(blocks.as_ptr() as *const ParBlocks) - } -} +impl AsyncStreamCipher for Decrypt {} + +impl InnerIvInit for Decrypt { + type Inner = C; + type IvSize = C::BlockSize; -#[inline(always)] -fn xor_set1(buf1: &mut [u8], buf2: &mut [u8]) { - for (a, b) in buf1.iter_mut().zip(buf2) { - let t = *a ^ *b; - *a = t; - *b = t; + #[inline] + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { + Self { + cipher, + iv: iv.clone(), + } } } -#[inline(always)] -fn xor_set2(buf1: &mut [u8], buf2: &mut [u8]) { - for (a, b) in buf1.iter_mut().zip(buf2) { - let t = *a; - *a ^= *b; - *b = t; +impl IvState for Decrypt { + fn iv_state(&self) -> GenericArray { + self.iv.clone() } } diff --git a/block-modes/src/cfb8.rs b/block-modes/src/cfb8.rs index d0e26c17..2240e7dc 100644 --- a/block-modes/src/cfb8.rs +++ b/block-modes/src/cfb8.rs @@ -1,74 +1,86 @@ -use crate::{ - traits::{BlockMode, IvState}, - utils::Block, +//! [Cipher feedback][1] (CFB) mode with eight bit feedback. +//! +//! [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_(CFB) +use cipher::{ + generic_array::{typenum::U1, GenericArray}, + AsyncStreamCipher, Block, BlockCipher, BlockDecryptMut, BlockEncryptMut, BlockProcessing, + InOutVal, InnerIvInit, }; -use block_padding::Padding; -use cipher::{generic_array::GenericArray, BlockCipher, BlockEncrypt, NewBlockCipher}; -use core::marker::PhantomData; -/// [Cipher feedback][1] (CFB) block mode instance with a full block feedback. -/// -/// [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_(CFB) +/// CFB-8 mode encryptor. #[derive(Clone)] -pub struct Cfb8 { +pub struct Encrypt { cipher: C, - iv: GenericArray, - _p: PhantomData

, + iv: Block, } -impl BlockMode for Cfb8 -where - C: BlockCipher + BlockEncrypt + NewBlockCipher, - P: Padding, -{ +impl BlockEncryptMut for Encrypt { + fn encrypt_block(&mut self, mut block: impl InOutVal>) { + let mut t = self.iv.clone(); + self.cipher.encrypt_block(&mut t); + let r = block.get_in()[0] ^ t[0]; + block.get_out()[0] = r; + let n = self.iv.len(); + for i in 0..n - 1 { + self.iv[i] = self.iv[i + 1]; + } + self.iv[n - 1] = r; + } +} + +impl BlockProcessing for Encrypt { + type BlockSize = U1; +} + +impl AsyncStreamCipher for Encrypt {} + +impl InnerIvInit for Encrypt { + type Inner = C; type IvSize = C::BlockSize; - fn new(cipher: C, iv: &Block) -> Self { + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { Self { cipher, iv: iv.clone(), - _p: Default::default(), } } +} - fn encrypt_blocks(&mut self, blocks: &mut [Block]) { - let mut iv = self.iv.clone(); - let n = iv.len(); - for block in blocks.iter_mut() { - for b in block.iter_mut() { - let iv_copy = iv.clone(); - self.cipher.encrypt_block(&mut iv); - *b ^= iv[0]; - iv[..n - 1].clone_from_slice(&iv_copy[1..]); - iv[n - 1] = *b; - } - } - self.iv = iv; - } +/// CFB-8 mode decryptor. +#[derive(Clone)] +pub struct Decrypt { + cipher: C, + iv: Block, +} - fn decrypt_blocks(&mut self, blocks: &mut [Block]) { - let mut iv = self.iv.clone(); - let n = iv.len(); - for block in blocks.iter_mut() { - for b in block.iter_mut() { - let iv_copy = iv.clone(); - self.cipher.encrypt_block(&mut iv); - let t = *b; - *b ^= iv[0]; - iv[..n - 1].clone_from_slice(&iv_copy[1..]); - iv[n - 1] = t; - } +impl BlockDecryptMut for Decrypt { + fn decrypt_block(&mut self, mut block: impl InOutVal>) { + let mut t = self.iv.clone(); + self.cipher.encrypt_block(&mut t); + let r = block.get_in()[0]; + block.get_out()[0] = r ^ t[0]; + let n = self.iv.len(); + for i in 0..n - 1 { + self.iv[i] = self.iv[i + 1]; } - self.iv = iv; + self.iv[n - 1] = r; } } -impl IvState for Cfb8 -where - C: BlockCipher + BlockEncrypt + NewBlockCipher, - P: Padding, -{ - fn iv_state(&self) -> GenericArray { - self.iv.clone() +impl BlockProcessing for Decrypt { + type BlockSize = U1; +} + +impl AsyncStreamCipher for Decrypt {} + +impl InnerIvInit for Decrypt { + type Inner = C; + type IvSize = C::BlockSize; + + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { + Self { + cipher, + iv: iv.clone(), + } } } diff --git a/block-modes/src/ecb.rs b/block-modes/src/ecb.rs deleted file mode 100644 index 39d9bd3f..00000000 --- a/block-modes/src/ecb.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::{ - errors::InvalidKeyIvLength, - traits::BlockMode, - utils::{get_par_blocks, Block}, -}; -use block_padding::Padding; -use cipher::{ - generic_array::{ - typenum::{Unsigned, U0}, - GenericArray, - }, - BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher, -}; -use core::marker::PhantomData; - -/// [Electronic Codebook][1] (ECB) block cipher mode instance. -/// -/// Note that `new` method ignores IV, so during initialization you can -/// just pass `Default::default()` instead. -/// -/// [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#ECB -#[derive(Clone)] -pub struct Ecb { - cipher: C, - _p: PhantomData

, -} - -impl BlockMode for Ecb -where - C: BlockCipher + BlockEncrypt + BlockDecrypt + NewBlockCipher, - P: Padding, -{ - type IvSize = U0; - - fn new(cipher: C, _iv: &GenericArray) -> Self { - Self { - cipher, - _p: Default::default(), - } - } - - fn new_from_slices(key: &[u8], _iv: &[u8]) -> Result { - let cipher = C::new_from_slice(key).map_err(|_| InvalidKeyIvLength)?; - Ok(Self { - cipher, - _p: Default::default(), - }) - } - - fn encrypt_blocks(&mut self, blocks: &mut [Block]) { - if C::ParBlocks::to_usize() != 1 { - let (par_blocks, blocks) = get_par_blocks::(blocks); - par_blocks - .iter_mut() - .for_each(|pb| self.cipher.encrypt_blocks(pb)); - blocks - .iter_mut() - .for_each(|pb| self.cipher.encrypt_block(pb)); - } else { - blocks - .iter_mut() - .for_each(|pb| self.cipher.encrypt_block(pb)); - } - } - - fn decrypt_blocks(&mut self, blocks: &mut [Block]) { - if C::ParBlocks::to_usize() != 1 { - let (par_blocks, blocks) = get_par_blocks::(blocks); - par_blocks - .iter_mut() - .for_each(|pb| self.cipher.decrypt_blocks(pb)); - blocks - .iter_mut() - .for_each(|pb| self.cipher.decrypt_block(pb)); - } else { - blocks - .iter_mut() - .for_each(|pb| self.cipher.decrypt_block(pb)); - } - } -} diff --git a/block-modes/src/errors.rs b/block-modes/src/errors.rs deleted file mode 100644 index 476beddc..00000000 --- a/block-modes/src/errors.rs +++ /dev/null @@ -1,33 +0,0 @@ -use core::fmt; -#[cfg(feature = "std")] -use std::error; - -/// Block mode error. -#[derive(Clone, Copy, Debug)] -pub struct BlockModeError; - -/// Invalid key or IV length error. -#[derive(Clone, Copy, Debug)] -pub struct InvalidKeyIvLength; - -impl fmt::Display for BlockModeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.write_str("BlockModeError") - } -} - -#[cfg(feature = "std")] -impl error::Error for BlockModeError { - fn description(&self) -> &str { - "block mode error" - } -} - -impl fmt::Display for InvalidKeyIvLength { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.write_str("InvalidKeyIvLength") - } -} - -#[cfg(feature = "std")] -impl error::Error for InvalidKeyIvLength {} diff --git a/block-modes/src/ige.rs b/block-modes/src/ige.rs index f87c3c39..17031fdb 100644 --- a/block-modes/src/ige.rs +++ b/block-modes/src/ige.rs @@ -1,84 +1,156 @@ -use crate::{ - traits::{BlockMode, IvState}, - utils::{xor, Block}, -}; -use block_padding::Padding; +//! [Infinite Garble Extension][1] (IGE) block cipher mode of operation. +//! +//! [1]: https://www.links.org/files/openssl-ige.pdf +use crate::{xor, xor_ret}; +use core::ops::Add; use cipher::{ generic_array::{ sequence::Concat, typenum::{Sum, Unsigned}, ArrayLength, GenericArray, }, - BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher, + Block, BlockCipher, BlockDecryptMut, BlockEncryptMut, + BlockProcessing, InOutVal, InnerIvInit, IvState, }; -use core::{marker::PhantomData, ops::Add}; -type IgeIvBlockSize = Sum<::BlockSize, ::BlockSize>; +type BlockSize = ::BlockSize; +type IgeIvSize = Sum, BlockSize>; -/// [Infinite Garble Extension][1] (IGE) block cipher mode instance. -/// -/// [1]: https://www.links.org/files/openssl-ige.pdf -pub struct Ige +/// IGE mode encryptor. +#[derive(Clone)] +pub struct Encrypt where - C: BlockCipher + NewBlockCipher + BlockEncrypt + BlockDecrypt, - P: Padding, + C: BlockEncryptMut + BlockCipher, C::BlockSize: Add, - IgeIvBlockSize: ArrayLength, + IgeIvSize: ArrayLength, { cipher: C, - x: GenericArray, - y: GenericArray, - _p: PhantomData

, + x: Block, + y: Block, +} + +impl BlockEncryptMut for Encrypt +where + C: BlockEncryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + fn encrypt_block(&mut self, mut block: impl InOutVal>) { + let new_x = block.get_in().clone(); + let mut t = xor_ret(block.get_in(), &self.y); + self.cipher.encrypt_block(&mut t); + xor(&mut t, &self.x); + *block.get_out() = t.clone(); + self.x = new_x; + self.y = t; + } +} + +impl BlockProcessing for Encrypt +where + C: BlockEncryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + type BlockSize = C::BlockSize; } -impl BlockMode for Ige +impl InnerIvInit for Encrypt where - C: BlockCipher + NewBlockCipher + BlockEncrypt + BlockDecrypt, - P: Padding, + C: BlockEncryptMut + BlockCipher, C::BlockSize: Add, - IgeIvBlockSize: ArrayLength, + IgeIvSize: ArrayLength, { - type IvSize = IgeIvBlockSize; + type Inner = C; + type IvSize = IgeIvSize; - fn new(cipher: C, iv: &GenericArray) -> Self { + #[inline] + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { let (y, x) = iv.split_at(C::BlockSize::to_usize()); - Ige { + Self { cipher, x: GenericArray::clone_from_slice(x), y: GenericArray::clone_from_slice(y), - _p: Default::default(), } } +} - fn encrypt_blocks(&mut self, blocks: &mut [Block]) { - for block in blocks { - let t = block.clone(); - xor(block, &self.y); - self.cipher.encrypt_block(block); - xor(block, &self.x); - self.x = t; - self.y = block.clone(); - } +impl IvState for Encrypt +where + C: BlockEncryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + #[inline] + fn iv_state(&self) -> GenericArray { + self.y.clone().concat(self.x.clone()) } +} - fn decrypt_blocks(&mut self, blocks: &mut [Block]) { - for block in blocks { - let t = block.clone(); - xor(block, &self.x); - self.cipher.decrypt_block(block); - xor(block, &self.y); - self.y = t; - self.x = block.clone(); +/// IGE mode decryptor. +#[derive(Clone)] +pub struct Decrypt +where + C: BlockDecryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + cipher: C, + x: Block, + y: Block, +} + +impl BlockDecryptMut for Decrypt +where + C: BlockDecryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + fn decrypt_block(&mut self, mut block: impl InOutVal>) { + let new_y = block.get_in().clone(); + let mut t = xor_ret(block.get_in(), &self.x); + self.cipher.decrypt_block(&mut t); + xor(&mut t, &self.y); + *block.get_out() = t.clone(); + self.y = new_y; + self.x = t; + } +} + +impl BlockProcessing for Decrypt +where + C: BlockDecryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + type BlockSize = C::BlockSize; +} + +impl InnerIvInit for Decrypt +where + C: BlockDecryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + type Inner = C; + type IvSize = IgeIvSize; + + #[inline] + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { + let (y, x) = iv.split_at(C::BlockSize::to_usize()); + Self { + cipher, + x: GenericArray::clone_from_slice(x), + y: GenericArray::clone_from_slice(y), } } } -impl IvState for Ige +impl IvState for Decrypt where - C: BlockCipher + NewBlockCipher + BlockEncrypt + BlockDecrypt, - P: Padding, + C: BlockDecryptMut + BlockCipher, C::BlockSize: Add, - IgeIvBlockSize: ArrayLength, + IgeIvSize: ArrayLength, { fn iv_state(&self) -> GenericArray { self.y.clone().concat(self.x.clone()) diff --git a/block-modes/src/lib.rs b/block-modes/src/lib.rs index 220e9f99..e2c6d289 100644 --- a/block-modes/src/lib.rs +++ b/block-modes/src/lib.rs @@ -78,34 +78,37 @@ #![deny(unsafe_code)] #![warn(missing_docs, rust_2018_idioms)] -#[cfg(feature = "alloc")] -extern crate alloc; -#[cfg(feature = "std")] -extern crate std; +pub use cipher; -mod errors; -mod traits; mod utils; -mod cbc; -mod cfb; -mod cfb8; -mod ecb; -mod ige; +pub mod cbc; +pub mod cfb; +pub mod cfb8; +pub mod pcbc; +pub mod ige; mod ofb; -mod pcbc; -pub use block_padding; -pub use cipher; +pub use ofb::Ofb; + + +use cipher::generic_array::{ArrayLength, GenericArray}; + +#[inline(always)] +fn xor>(out: &mut GenericArray, buf: &GenericArray) { + for (a, b) in out.iter_mut().zip(buf) { + *a ^= *b; + } +} -pub use crate::{ - cbc::Cbc, - cfb::Cfb, - cfb8::Cfb8, - ecb::Ecb, - errors::{BlockModeError, InvalidKeyIvLength}, - ige::Ige, - ofb::Ofb, - pcbc::Pcbc, - traits::{BlockMode, IvState}, -}; +#[inline(always)] +fn xor_ret>( + buf1: &GenericArray, + buf2: &GenericArray, +) -> GenericArray { + let mut res = GenericArray::::default(); + for i in 0..N::USIZE { + res[i] = buf1[i] ^ buf2[i]; + } + res +} diff --git a/block-modes/src/ofb.rs b/block-modes/src/ofb.rs index edade926..84f820fb 100644 --- a/block-modes/src/ofb.rs +++ b/block-modes/src/ofb.rs @@ -1,54 +1,62 @@ -use crate::{ - traits::{BlockMode, IvState}, - utils::{xor, Block}, +/// [Output feedback][1] (OFB) mode. +/// +/// [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_(OFB) +use cipher::{ + generic_array::GenericArray, Block, BlockCipher, StreamCipherCore, BlockEncryptMut, + BlockProcessing, InnerIvInit, IvState, InOutVal, BlockDecryptMut, errors::LoopError, }; -use block_padding::Padding; -use cipher::{generic_array::GenericArray, BlockCipher, BlockEncrypt, NewBlockCipher}; -use core::marker::PhantomData; +use crate::xor_ret; -/// [Output feedback][1] (OFB) block mode instance with a full block feedback. +/// [Output feedback][1] (OFB) mode. /// -/// [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_(CFB) +/// [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_(OFB) #[derive(Clone)] -pub struct Ofb { +pub struct Ofb { cipher: C, - iv: GenericArray, - _p: PhantomData

, + iv: Block, +} + +impl StreamCipherCore for Ofb { + fn gen_keystream_block(&mut self) -> Result, LoopError> { + self.cipher.encrypt_block(&mut self.iv); + Ok(self.iv.clone()) + } +} + +impl BlockProcessing for Ofb { + type BlockSize = C::BlockSize; } -impl BlockMode for Ofb -where - C: BlockCipher + BlockEncrypt + NewBlockCipher, - P: Padding, -{ +impl InnerIvInit for Ofb { + type Inner = C; type IvSize = C::BlockSize; - fn new(cipher: C, iv: &Block) -> Self { + #[inline] + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { Self { cipher, iv: iv.clone(), - _p: Default::default(), } } +} - fn encrypt_blocks(&mut self, blocks: &mut [Block]) { - for block in blocks.iter_mut() { - self.cipher.encrypt_block(&mut self.iv); - xor(block, &self.iv); - } +impl IvState for Ofb { + #[inline] + fn iv_state(&self) -> GenericArray { + self.iv.clone() } +} - fn decrypt_blocks(&mut self, blocks: &mut [Block]) { - self.encrypt_blocks(blocks) +impl BlockEncryptMut for Ofb { + fn encrypt_block(&mut self, mut block: impl InOutVal>) { + self.cipher.encrypt_block(&mut self.iv); + *block.get_out() = xor_ret(&self.iv, block.get_in()); } } -impl IvState for Ofb -where - C: BlockCipher + BlockEncrypt + NewBlockCipher, - P: Padding, -{ - fn iv_state(&self) -> GenericArray>::IvSize> { - self.iv.clone() +impl BlockDecryptMut for Ofb { + fn decrypt_block(&mut self, mut block: impl InOutVal>) { + self.cipher.encrypt_block(&mut self.iv); + *block.get_out() = xor_ret(&self.iv, block.get_in()); } } diff --git a/block-modes/src/pcbc.rs b/block-modes/src/pcbc.rs index e9ecaf2d..a38dc77c 100644 --- a/block-modes/src/pcbc.rs +++ b/block-modes/src/pcbc.rs @@ -1,79 +1,88 @@ -use crate::{ - traits::{BlockMode, IvState}, - utils::{xor, Block}, -}; -use block_padding::Padding; +//! [Propagating cipher block chaining][1] (PCBC) mode. +//! +//! [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Propagating_cipher_block_chaining_(PCBC) +use crate::{xor, xor_ret}; use cipher::{ - generic_array::GenericArray, BlockCipher, BlockDecrypt, BlockEncrypt, NewBlockCipher, + generic_array::GenericArray, Block, BlockCipher, BlockDecryptMut, BlockEncryptMut, + BlockProcessing, InOutVal, InnerIvInit, IvState, }; -use core::marker::PhantomData; -/// [Propagating Cipher Block Chaining][1] (PCBC) mode instance. -/// -/// [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#PCBC +/// PCBC mode encryptor. #[derive(Clone)] -pub struct Pcbc { +pub struct Encrypt { cipher: C, - iv: GenericArray, - _p: PhantomData

, + iv: Block, } -impl Pcbc -where - C: BlockCipher + BlockEncrypt + BlockDecrypt + NewBlockCipher, - P: Padding, -{ - /// Initialize PCBC - pub fn new(cipher: C, iv: &Block) -> Self { - Self { - cipher, - iv: iv.clone(), - _p: Default::default(), - } +impl BlockEncryptMut for Encrypt { + fn encrypt_block(&mut self, mut block: impl InOutVal>) { + let mut t = self.iv.clone(); + xor(&mut t, block.get_in()); + self.cipher.encrypt_block((&t, block.get_out())); + self.iv = xor_ret(&t, block.get_out()); } } -impl BlockMode for Pcbc -where - C: BlockCipher + BlockEncrypt + BlockDecrypt + NewBlockCipher, - P: Padding, -{ +impl BlockProcessing for Encrypt { + type BlockSize = C::BlockSize; +} + +impl InnerIvInit for Encrypt { + type Inner = C; type IvSize = C::BlockSize; - fn new(cipher: C, iv: &GenericArray) -> Self { + #[inline] + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { Self { cipher, iv: iv.clone(), - _p: Default::default(), } } +} - fn encrypt_blocks(&mut self, blocks: &mut [Block]) { - for block in blocks { - let plaintext = block.clone(); - xor(block, &self.iv); - self.cipher.encrypt_block(block); - self.iv = plaintext; - xor(&mut self.iv, block); - } +impl IvState for Encrypt { + #[inline] + fn iv_state(&self) -> GenericArray { + self.iv.clone() + } +} + +/// PCBC mode decryptor. +#[derive(Clone)] +pub struct Decrypt { + cipher: C, + iv: Block, +} + +impl BlockDecryptMut for Decrypt { + fn decrypt_block(&mut self, mut block: impl InOutVal>) { + let mut t = Default::default(); + self.cipher.decrypt_block((block.get_in(), &mut t)); + xor(&mut t, &self.iv); + self.iv.copy_from_slice(block.get_in()); + block.get_out().copy_from_slice(&t); + xor(&mut self.iv, &t); } +} - fn decrypt_blocks(&mut self, blocks: &mut [Block]) { - for block in blocks { - let ciphertext = block.clone(); - self.cipher.decrypt_block(block); - xor(block, &self.iv); - self.iv = ciphertext; - xor(&mut self.iv, block); +impl BlockProcessing for Decrypt { + type BlockSize = C::BlockSize; +} + +impl InnerIvInit for Decrypt { + type Inner = C; + type IvSize = C::BlockSize; + + #[inline] + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { + Self { + cipher, + iv: iv.clone(), } } } -impl IvState for Pcbc -where - C: BlockCipher + BlockEncrypt + BlockDecrypt + NewBlockCipher, - P: Padding, -{ +impl IvState for Decrypt { fn iv_state(&self) -> GenericArray { self.iv.clone() } diff --git a/block-modes/src/traits.rs b/block-modes/src/traits.rs deleted file mode 100644 index 58d44c92..00000000 --- a/block-modes/src/traits.rs +++ /dev/null @@ -1,126 +0,0 @@ -#[cfg(feature = "alloc")] -pub use alloc::vec::Vec; - -use crate::{ - errors::{BlockModeError, InvalidKeyIvLength}, - utils::{to_blocks, Block, Key}, -}; -use block_padding::Padding; -use cipher::{ - generic_array::{typenum::Unsigned, ArrayLength, GenericArray}, - BlockCipher, NewBlockCipher, -}; - -/// Trait for a block cipher mode of operation that is used to apply a block cipher -/// operation to input data to transform it into a variable-length output message. -pub trait BlockMode: Sized -where - C: BlockCipher + NewBlockCipher, - P: Padding, -{ - /// Initialization Vector size. - type IvSize: ArrayLength; - - /// Create a new block mode instance from initialized block cipher and IV. - fn new(cipher: C, iv: &GenericArray) -> Self; - - /// Create a new block mode instance from fixed sized key and IV. - fn new_fix(key: &Key, iv: &GenericArray) -> Self { - Self::new(C::new(key), iv) - } - - /// Create a new block mode instance from variable size key and IV. - /// - /// Returns an error if key or IV have unsupported length. - fn new_from_slices(key: &[u8], iv: &[u8]) -> Result { - if iv.len() != Self::IvSize::USIZE { - return Err(InvalidKeyIvLength); - } - let iv = GenericArray::from_slice(iv); - let cipher = C::new_from_slice(key).map_err(|_| InvalidKeyIvLength)?; - Ok(Self::new(cipher, iv)) - } - - /// Encrypt blocks of data - fn encrypt_blocks(&mut self, blocks: &mut [Block]); - - /// Decrypt blocks of data - fn decrypt_blocks(&mut self, blocks: &mut [Block]); - - /// Encrypt message in-place. - /// - /// `&buffer[..pos]` is used as a message and `&buffer[pos..]` as a reserved - /// space for padding. The padding space should be big enough for padding, - /// otherwise method will return `Err(BlockModeError)`. - fn encrypt(mut self, buffer: &mut [u8], pos: usize) -> Result<&[u8], BlockModeError> { - let bs = C::BlockSize::to_usize(); - let buf = P::pad(buffer, pos, bs).map_err(|_| BlockModeError)?; - self.encrypt_blocks(to_blocks(buf)); - Ok(buf) - } - - /// Decrypt message in-place. - /// - /// Returns an error if `buffer` length is not multiple of block size and - /// if after decoding message has malformed padding. - fn decrypt(mut self, buffer: &mut [u8]) -> Result<&[u8], BlockModeError> { - let bs = C::BlockSize::to_usize(); - if buffer.len() % bs != 0 { - return Err(BlockModeError); - } - self.decrypt_blocks(to_blocks(buffer)); - P::unpad(buffer).map_err(|_| BlockModeError) - } - - /// Encrypt message and store result in vector. - #[cfg(feature = "alloc")] - fn encrypt_vec(mut self, plaintext: &[u8]) -> Vec { - let bs = C::BlockSize::to_usize(); - let pos = plaintext.len(); - let n = pos + bs; - let mut buf = Vec::with_capacity(n); - buf.extend_from_slice(plaintext); - // prepare space for padding - let block: Block = Default::default(); - buf.extend_from_slice(&block[..n - pos]); - - let n = P::pad(&mut buf, pos, bs) - .expect("enough space for padding is allocated") - .len(); - buf.truncate(n); - self.encrypt_blocks(to_blocks(&mut buf)); - buf - } - - /// Encrypt message and store result in vector. - #[cfg(feature = "alloc")] - fn decrypt_vec(mut self, ciphertext: &[u8]) -> Result, BlockModeError> { - let bs = C::BlockSize::to_usize(); - if ciphertext.len() % bs != 0 { - return Err(BlockModeError); - } - let mut buf = ciphertext.to_vec(); - self.decrypt_blocks(to_blocks(&mut buf)); - let n = P::unpad(&buf).map_err(|_| BlockModeError)?.len(); - buf.truncate(n); - Ok(buf) - } -} - -/// Trait for a BlockMode, used to obtain the current state in the form of an IV -/// that can initialize a BlockMode later and resume the original operation. -/// -/// The IV value SHOULD be used for resuming operations only and MUST NOT be -/// exposed to attackers. Failing to comply with this requirement breaks -/// unpredictability and opens attack venues (see e.g. [1], sec. 3.6.2). -/// -/// [1]: https://www.cs.umd.edu/~jkatz/imc.html -pub trait IvState: BlockMode -where - C: BlockCipher + NewBlockCipher, - P: Padding, -{ - /// Returns the IV needed to process the following block. This value MUST - /// NOT be exposed to attackers. - fn iv_state(&self) -> GenericArray; -} diff --git a/block-modes/src/utils.rs b/block-modes/src/utils.rs index a16d2908..9e9a8afa 100644 --- a/block-modes/src/utils.rs +++ b/block-modes/src/utils.rs @@ -1,9 +1,3 @@ -use cipher::{ - generic_array::{typenum::Unsigned, ArrayLength, GenericArray}, - BlockCipher, NewBlockCipher, -}; -use core::slice; - #[inline(always)] pub fn xor(buf: &mut [u8], key: &[u8]) { debug_assert_eq!(buf.len(), key.len()); @@ -12,32 +6,28 @@ pub fn xor(buf: &mut [u8], key: &[u8]) { } } -pub(crate) type Key = GenericArray::KeySize>; -pub(crate) type Block = GenericArray::BlockSize>; -pub(crate) type ParBlocks = GenericArray, ::ParBlocks>; +// pub(crate) fn to_blocks(data: &mut [u8]) -> &mut [GenericArray] +// where +// N: ArrayLength, +// { +// let n = N::to_usize(); +// debug_assert!(data.len() % n == 0); -pub(crate) fn to_blocks(data: &mut [u8]) -> &mut [GenericArray] -where - N: ArrayLength, -{ - let n = N::to_usize(); - debug_assert!(data.len() % n == 0); +// #[allow(unsafe_code)] +// unsafe { +// slice::from_raw_parts_mut(data.as_ptr() as *mut GenericArray, data.len() / n) +// } +// } - #[allow(unsafe_code)] - unsafe { - slice::from_raw_parts_mut(data.as_ptr() as *mut GenericArray, data.len() / n) - } -} +// pub(crate) fn get_par_blocks( +// blocks: &mut [Block], +// ) -> (&mut [ParBlocks], &mut [Block]) { +// let pb = C::ParBlocks::to_usize(); +// let n_par = blocks.len() / pb; -pub(crate) fn get_par_blocks( - blocks: &mut [Block], -) -> (&mut [ParBlocks], &mut [Block]) { - let pb = C::ParBlocks::to_usize(); - let n_par = blocks.len() / pb; +// let (par, single) = blocks.split_at_mut(n_par * pb); - let (par, single) = blocks.split_at_mut(n_par * pb); - - #[allow(unsafe_code)] - let par = unsafe { slice::from_raw_parts_mut(par.as_ptr() as *mut ParBlocks, n_par) }; - (par, single) -} +// #[allow(unsafe_code)] +// let par = unsafe { slice::from_raw_parts_mut(par.as_ptr() as *mut ParBlocks, n_par) }; +// (par, single) +// } diff --git a/block-modes/tests/data/cfb8-aes128.ciphertext.bin b/block-modes/tests/data/cfb8-aes128.ciphertext.bin new file mode 100644 index 00000000..d99c1935 --- /dev/null +++ b/block-modes/tests/data/cfb8-aes128.ciphertext.bin @@ -0,0 +1,3 @@ +mÝ\{a9f8ܛ9t`!WU])=d꜈+[ǵPsȗI)"[8\r"潝f J^vh#%>/j MD2_? C~eyu2.-ˢ!*XV:oKMncY56X +O^Ÿ;ՁDG_ DinyBZ͸ +¹|`H\PX$Ď׀tա!^+PX ip { + #[test] + fn $name() { + use cipher::{ + copy_res2out, generic_array::GenericArray, BlockDecryptMut, BlockEncryptMut, + InnerIvInit, KeyInit, + }; + use {$cipher as Cipher, $decrypt as Decrypt, $encrypt as Encrypt}; + + let key = GenericArray::from_slice(include_bytes!($key_path)); + let iv = GenericArray::from_slice(include_bytes!($iv_path)); + let pt_blocks = to_blocks(include_bytes!($pt_path)); + let ct_blocks = to_blocks(include_bytes!($ct_path)); + assert_eq!(pt_blocks.len(), ct_blocks.len()); + + let cipher = Cipher::new(key); + + let mut enc = Encrypt::inner_iv_init(&cipher, iv); + for (ptb, ctb) in pt_blocks.iter().zip(ct_blocks.iter()) { + let mut t = Default::default(); + enc.encrypt_block((ptb, &mut t)); + assert_eq!(ctb, &t); + } + let mut enc = Encrypt::inner_iv_init(&cipher, iv); + for (ptb, ctb) in pt_blocks.iter().zip(ct_blocks.iter()) { + let mut t = *ptb; + enc.encrypt_block(&mut t); + assert_eq!(ctb, &t); + } + let mut dec = Decrypt::inner_iv_init(&cipher, iv); + for (ptb, ctb) in pt_blocks.iter().zip(ct_blocks.iter()) { + let mut t = Default::default(); + dec.decrypt_block((ctb, &mut t)); + assert_eq!(ptb, &t); + } + let mut dec = Decrypt::inner_iv_init(&cipher, iv); + for (ptb, ctb) in pt_blocks.iter().zip(ct_blocks.iter()) { + let mut t = *ctb; + dec.decrypt_block(&mut t); + assert_eq!(ptb, &t); + } + + for i in 0..pt_blocks.len() { + let mut enc = Encrypt::inner_iv_init(&cipher, iv); + let mut buf = pt_blocks.to_vec(); + for chunk in buf.chunks_mut(i + 1) { + enc.encrypt_blocks(chunk.into(), copy_res2out); + } + assert_eq!(buf, ct_blocks); + + let mut dec = Decrypt::inner_iv_init(&cipher, iv); + let mut buf = ct_blocks.to_vec(); + for chunk in buf.chunks_mut(i + 1) { + dec.decrypt_blocks(chunk.into(), copy_res2out); + } + assert_eq!(buf, pt_blocks); + } + } + }; +} + +// TODO: add PCBC test + +new_test!( + cbc_aes128, + aes::Aes128, + block_modes::cbc::Encrypt, + block_modes::cbc::Decrypt, + "data/aes128.key.bin", + "data/aes128.iv.bin", + "data/aes128.plaintext.bin", + "data/cbc-aes128.ciphertext.bin", +); + +new_test!( + cfb_aes128, + aes::Aes128, + block_modes::cfb::Encrypt, + block_modes::cfb::Decrypt, + "data/aes128.key.bin", + "data/aes128.iv.bin", + "data/aes128.plaintext.bin", + "data/cfb-aes128.ciphertext.bin", +); + +new_test!( + cfb8_aes128, + aes::Aes128, + block_modes::cfb8::Encrypt, + block_modes::cfb8::Decrypt, + "data/aes128.key.bin", + "data/aes128.iv.bin", + "data/aes128.plaintext.bin", + "data/cfb8-aes128.ciphertext.bin", +); + +new_test!( + ofb_aes128, + aes::Aes128, + block_modes::Ofb, + block_modes::Ofb, + "data/aes128.key.bin", + "data/aes128.iv.bin", + "data/aes128.plaintext.bin", + "data/ofb-aes128.ciphertext.bin", +); + +new_test!( + ige1_aes128, + aes::Aes128, + block_modes::ige::Encrypt, + block_modes::ige::Decrypt, + "data/ige-aes128-1.key.bin", + "data/ige-aes128-1.iv.bin", + "data/ige-aes128-1.plaintext.bin", + "data/ige-aes128-1.ciphertext.bin", +); + +new_test!( + ige2_aes128, + aes::Aes128, + block_modes::ige::Encrypt, + block_modes::ige::Decrypt, + "data/ige-aes128-2.key.bin", + "data/ige-aes128-2.iv.bin", + "data/ige-aes128-2.plaintext.bin", + "data/ige-aes128-2.ciphertext.bin", +); + +/* #[test] -fn ecb_aes128() { +fn cbc_aes128() { + use block_modes::{CbcEncrypt as Encrypt, CbcDecrypt as Decrypt}; let key = include_bytes!("data/aes128.key.bin"); + let iv = include_bytes!("data/aes128.iv.bin"); let plaintext = include_bytes!("data/aes128.plaintext.bin"); - let ciphertext = include_bytes!("data/ecb-aes128.ciphertext.bin"); - // ECB mode ignores IV - let iv = Default::default(); + let ciphertext = include_bytes!("data/cbc-aes128.ciphertext.bin"); - let mode = Ecb::::new_from_slices(key, iv).unwrap(); - assert_eq!(mode.encrypt_vec(plaintext), &ciphertext[..]); + let cipher = Aes128::new_from_slice(key).unwrap(); + let pt_blocks = &to_blocks(plaintext)[..15]; + let ct_blocks = &to_blocks(ciphertext)[..15]; + assert_eq!(pt_blocks.len(), ct_blocks.len()); + + let mut enc = Encrypt::inner_iv_slice_init(&cipher, iv).unwrap(); + let mut dec = Decrypt::inner_iv_slice_init(&cipher, iv).unwrap(); + for (ptb, ctb) in pt_blocks.iter().zip(ct_blocks.iter()) { + let mut t = Default::default(); + enc.encrypt_block((ptb, &mut t)); + assert_eq!(ctb, &t); + + let mut t = Default::default(); + dec.decrypt_block((ctb, &mut t)); + assert_eq!(ptb, &t); + } - let mode = Ecb::::new_from_slices(key, iv).unwrap(); - assert_eq!(mode.decrypt_vec(ciphertext).unwrap(), &plaintext[..]); + let mut enc = Encrypt::inner_iv_slice_init(&cipher, iv).unwrap(); + let mut dec = Decrypt::inner_iv_slice_init(&cipher, iv).unwrap(); + for (ptb, ctb) in pt_blocks.iter().zip(ct_blocks.iter()) { + let mut t = *ptb; + enc.encrypt_block(&mut t); + assert_eq!(ctb, &t); + + let mut t = *ctb; + dec.decrypt_block(&mut t); + assert_eq!(ptb, &t); + } + + for i in (0..pt_blocks.len()).rev() { + println!("{:?}", i); + let mut enc = Encrypt::inner_iv_slice_init(&cipher, iv).unwrap(); + let mut buf = pt_blocks.to_vec(); + for chunk in buf.chunks_mut(i + 1) { + enc.encrypt_blocks(chunk.into(), copy_res2out); + } + assert_eq!(buf, ct_blocks); + + let mut dec = Decrypt::inner_iv_slice_init(&cipher, iv).unwrap(); + let mut buf = ct_blocks.to_vec(); + for chunk in buf.chunks_mut(i + 1) { + dec.decrypt_blocks(chunk.into(), copy_res2out); + } + assert_eq!(buf, pt_blocks); + } } + #[test] -fn cbc_aes128() { - let key = include_bytes!("data/aes128.key.bin"); - let iv = include_bytes!("data/aes128.iv.bin"); +fn cfb_aes128() { + use block_modes::{CfbEncrypt as Encrypt, CfbDecrypt as Decrypt}; + let key = GenericArray::from_slice(include_bytes!("data/aes128.key.bin")); + let iv = GenericArray::from_slice(include_bytes!("data/aes128.iv.bin")); let plaintext = include_bytes!("data/aes128.plaintext.bin"); - let ciphertext = include_bytes!("data/cbc-aes128.ciphertext.bin"); + let ciphertext = include_bytes!("data/cfb-aes128.ciphertext.bin"); - let mode = Cbc::::new_from_slices(key, iv).unwrap(); - assert_eq!(mode.encrypt_vec(plaintext), &ciphertext[..]); + let cipher = Aes128::new(key); + let pt_blocks = to_blocks(plaintext); + let ct_blocks = to_blocks(ciphertext); + assert_eq!(pt_blocks.len(), ct_blocks.len()); - let mode = Cbc::::new_from_slices(key, iv).unwrap(); - assert_eq!(mode.decrypt_vec(ciphertext).unwrap(), &plaintext[..]); + let mut enc = Encrypt::inner_iv_init(&cipher, iv); + for (ptb, ctb) in pt_blocks.iter().zip(ct_blocks.iter()) { + let mut t = Default::default(); + enc.encrypt_block((ptb, &mut t)); + assert_eq!(ctb, &t); + } + let mut enc = Encrypt::inner_iv_init(&cipher, iv); + for (ptb, ctb) in pt_blocks.iter().zip(ct_blocks.iter()) { + let mut t = *ptb; + enc.encrypt_block(&mut t); + assert_eq!(ctb, &t); + } + let mut dec = Decrypt::inner_iv_init(&cipher, iv); + for (ptb, ctb) in pt_blocks.iter().zip(ct_blocks.iter()) { + let mut t = Default::default(); + dec.decrypt_block((ctb, &mut t)); + assert_eq!(ptb, &t); + } + let mut dec = Decrypt::inner_iv_init(&cipher, iv); + for (ptb, ctb) in pt_blocks.iter().zip(ct_blocks.iter()) { + let mut t = *ctb; + dec.decrypt_block(&mut t); + assert_eq!(ptb, &t); + } + + for i in 0..pt_blocks.len() { + let mut enc = Encrypt::inner_iv_init(&cipher, iv); + let mut buf = pt_blocks.to_vec(); + for chunk in buf.chunks_mut(i + 1) { + enc.encrypt_blocks(chunk.into(), copy_res2out); + } + assert_eq!(buf, ct_blocks); + + let mut dec = Decrypt::inner_iv_init(&cipher, iv); + let mut buf = ct_blocks.to_vec(); + for chunk in buf.chunks_mut(i + 1) { + dec.decrypt_blocks(chunk.into(), copy_res2out); + } + assert_eq!(buf, pt_blocks); + } } +*/ +/* #[test] fn cbc_aes128_continued() { type BlockSize = ::BlockSize; @@ -364,8 +583,9 @@ fn ige_aes256_2_continued() { assert_eq!(blocks, plaintext_blocks); } } +*/ -fn to_blocks(data: &mut [u8]) -> &mut [GenericArray] +fn to_blocks_mut(data: &mut [u8]) -> &mut [GenericArray] where N: ArrayLength, { @@ -378,3 +598,17 @@ where slice::from_raw_parts_mut(data.as_ptr() as *mut GenericArray, data.len() / n) } } + +fn to_blocks(data: &[u8]) -> &[GenericArray] +where + N: ArrayLength, +{ + use core::slice; + let n = N::to_usize(); + debug_assert!(data.len() % n == 0); + + #[allow(unsafe_code)] + unsafe { + slice::from_raw_parts(data.as_ptr() as *const GenericArray, data.len() / n) + } +} diff --git a/modes/cbc/CHANGELOG.md b/modes/cbc/CHANGELOG.md new file mode 100644 index 00000000..0c00eeb0 --- /dev/null +++ b/modes/cbc/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.1.0 (2018-04-25) +- Initial release. diff --git a/modes/cbc/Cargo.toml b/modes/cbc/Cargo.toml new file mode 100644 index 00000000..7ac0fb42 --- /dev/null +++ b/modes/cbc/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "cbc" +version = "0.1.0" +description = "Cipher Block Chaining (CBC) block cipher mode of operation" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +readme = "README.md" +edition = "2018" +documentation = "https://docs.rs/cbc" +repository = "https://github.com/RustCrypto/block-ciphers" +keywords = ["crypto", "block-cipher", "ciphers"] + +[dependencies] +cipher = { git = "https://github.com/RustCrypto/traits", branch = "experimental_design" } + +[dev-dependencies] +aes = { version = "=0.7.0-pre", path = "../../aes", features = ["force-soft"] } diff --git a/modes/cbc/README.md b/modes/cbc/README.md new file mode 100644 index 00000000..4c602898 --- /dev/null +++ b/modes/cbc/README.md @@ -0,0 +1,60 @@ +# RustCrypto: CBC + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] +[![Build Status][build-image]][build-link] + +Generic implementation of the [Cipher Block Chaining][CBC] (CBC) block cipher mode +of operation. + + + +See [documentation][cipher-doc] of the `cipher` crate for additional information. + +## Minimum Supported Rust Version + +Rust **1.41** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/cbc.svg +[crate-link]: https://crates.io/crates/cbc +[docs-image]: https://docs.rs/cbc/badge.svg +[docs-link]: https://docs.rs/cbc/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260039-block-ciphers +[build-image]: https://github.com/RustCrypto/block-ciphers/workflows/cbc/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/block-ciphers/actions?query=workflow%cbc + +[//]: # (general links) + +[CBC]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC +[cipher-doc]: https://docs.rs/cipher/ diff --git a/modes/cbc/src/lib.rs b/modes/cbc/src/lib.rs new file mode 100644 index 00000000..1c814793 --- /dev/null +++ b/modes/cbc/src/lib.rs @@ -0,0 +1,121 @@ +//! [Cipher Block Chaining][1] (CBC) mode. +//! +//! [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC + +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/master/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/master/logo.svg" +)] +#![deny(unsafe_code)] +#![warn(missing_docs, rust_2018_idioms)] + +use cipher::{ + generic_array::{ArrayLength, GenericArray}, + Block, BlockCipher, BlockDecryptMut, BlockEncryptMut, BlockProcessing, InOutBuf, InOutVal, + InResOutBuf, InnerIvInit, IvState, +}; + +/// CBC mode encryptor. +#[derive(Clone)] +pub struct Encrypt { + cipher: C, + iv: Block, +} + +impl BlockEncryptMut for Encrypt { + fn encrypt_block(&mut self, mut block: impl InOutVal>) { + let mut t = block.get_in().clone(); + xor(&mut t, &self.iv); + self.cipher.encrypt_block((&t, block.get_out())); + self.iv = block.get_out().clone(); + } +} + +impl BlockProcessing for Encrypt { + type BlockSize = C::BlockSize; +} + +impl InnerIvInit for Encrypt { + type Inner = C; + type IvSize = C::BlockSize; + + #[inline] + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { + Self { + cipher, + iv: iv.clone(), + } + } +} + +impl IvState for Encrypt { + #[inline] + fn iv_state(&self) -> GenericArray { + self.iv.clone() + } +} + +/// CBC mode decryptor. +#[derive(Clone)] +pub struct Decrypt { + cipher: C, + iv: Block, +} + +impl BlockDecryptMut for Decrypt { + fn decrypt_block(&mut self, mut block: impl InOutVal>) { + let enc_block = block.get_in().clone(); + self.cipher.decrypt_block((&enc_block, block.get_out())); + xor(block.get_out(), &self.iv); + self.iv = enc_block; + } + + fn decrypt_blocks( + &mut self, + blocks: InOutBuf<'_, '_, Block>, + mut proc: impl FnMut(InResOutBuf<'_, '_, '_, Block>), + ) { + let iv = &mut self.iv; + self.cipher.decrypt_blocks(blocks, |mut buf| { + let len = buf.len(); + let (in_buf, res_buf) = buf.get_in_res(); + xor(&mut res_buf[0], iv); + for i in 1..len { + xor(&mut res_buf[i], &in_buf[i - 1]); + } + *iv = in_buf[len - 1].clone(); + proc(buf); + }); + } +} + +impl BlockProcessing for Decrypt { + type BlockSize = C::BlockSize; +} + +impl InnerIvInit for Decrypt { + type Inner = C; + type IvSize = C::BlockSize; + + #[inline] + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { + Self { + cipher, + iv: iv.clone(), + } + } +} + +impl IvState for Decrypt { + fn iv_state(&self) -> GenericArray { + self.iv.clone() + } +} + +#[inline(always)] +fn xor>(out: &mut GenericArray, buf: &GenericArray) { + for (a, b) in out.iter_mut().zip(buf) { + *a ^= *b; + } +} diff --git a/modes/cfb-mode/CHANGELOG.md b/modes/cfb-mode/CHANGELOG.md new file mode 100644 index 00000000..8d79397f --- /dev/null +++ b/modes/cfb-mode/CHANGELOG.md @@ -0,0 +1,36 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.6.0 (2020-10-16) +### Changed +- Replace `block-cipher`/`stream-cipher` with `cipher` crate ([#177]) + +[#177]: https://github.com/RustCrypto/stream-ciphers/pull/177 + +## 0.5.0 (2020-08-25) +### Changed +- Bump `stream-cipher` dependency to v0.7, implement the `FromBlockCipher` trait ([#161], [#164]) + +[#161]: https://github.com/RustCrypto/stream-ciphers/pull/161 +[#164]: https://github.com/RustCrypto/stream-ciphers/pull/164 + +## 0.4.0 (2020-06-08) +### Changed +- Bump `stream-cipher` dependency to v0.4 ([#119]) +- Upgrade to Rust 2018 edition ([#119]) + +[#119]: https://github.com/RustCrypto/stream-ciphers/pull/119 + +## 0.3.2 (2019-03-11) + +## 0.3.1 (2018-11-01) + +## 0.3.0 (2018-11-01) + +## 0.2.0 (2018-10-13) + +## 0.1.0 (2018-10-01) diff --git a/modes/cfb-mode/Cargo.toml b/modes/cfb-mode/Cargo.toml new file mode 100644 index 00000000..e41deb98 --- /dev/null +++ b/modes/cfb-mode/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "cfb-mode" +version = "0.7.0" +description = "Cipher Feedback (CFB) block cipher mode of operation" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +readme = "README.md" +edition = "2018" +documentation = "https://docs.rs/cfb-mode" +repository = "https://github.com/RustCrypto/block-ciphers" +keywords = ["crypto", "block-cipher", "ciphers"] + +[dependencies] +cipher = { git = "https://github.com/RustCrypto/traits", branch = "experimental_design" } + +[dev-dependencies] +aes = { version = "=0.7.0-pre", path = "../../aes", features = ["force-soft"] } diff --git a/modes/cfb-mode/README.md b/modes/cfb-mode/README.md new file mode 100644 index 00000000..19bd34fe --- /dev/null +++ b/modes/cfb-mode/README.md @@ -0,0 +1,60 @@ +# RustCrypto: CFB + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] +[![Build Status][build-image]][build-link] + +Generic implementation of the [Cipher Feedback][CFB] (CFB) block cipher mode +of operation. + + + +See [documentation][cipher-doc] of the `cipher` crate for additional information. + +## Minimum Supported Rust Version + +Rust **1.41** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/cfb-mode.svg +[crate-link]: https://crates.io/crates/cfb-mode +[docs-image]: https://docs.rs/cfb-mode/badge.svg +[docs-link]: https://docs.rs/cfb-mode/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260039-block-ciphers +[build-image]: https://github.com/RustCrypto/block-ciphers/workflows/cfb-mode/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/block-ciphers/actions?query=workflow%cfb-mode + +[//]: # (general links) + +[CFB]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_(CFB) +[cipher-doc]: https://docs.rs/cipher/ diff --git a/modes/cfb-mode/src/lib.rs b/modes/cfb-mode/src/lib.rs new file mode 100644 index 00000000..05c7046a --- /dev/null +++ b/modes/cfb-mode/src/lib.rs @@ -0,0 +1,126 @@ +//! [Cipher feedback][1] (CFB) mode with full block feedback. +//! +//! [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_(CFB) + +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/master/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/master/logo.svg" +)] +#![deny(unsafe_code)] +#![warn(missing_docs, rust_2018_idioms)] + +use cipher::{ + generic_array::{ArrayLength, GenericArray}, + AsyncStreamCipher, Block, BlockCipher, BlockDecryptMut, BlockEncryptMut, BlockProcessing, + InOutBuf, InOutVal, InResOutBuf, InnerIvInit, IvState, +}; + +/// CFB mode encryptor. +#[derive(Clone)] +pub struct Encrypt { + cipher: C, + iv: Block, +} + +impl BlockEncryptMut for Encrypt { + fn encrypt_block(&mut self, mut block: impl InOutVal>) { + self.cipher.encrypt_block(&mut self.iv); + xor(&mut self.iv, block.get_in()); + *block.get_out() = self.iv.clone(); + } +} + +impl BlockProcessing for Encrypt { + type BlockSize = C::BlockSize; +} + +impl AsyncStreamCipher for Encrypt {} + +impl InnerIvInit for Encrypt { + type Inner = C; + type IvSize = C::BlockSize; + + #[inline] + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { + Self { + cipher, + iv: iv.clone(), + } + } +} + +impl IvState for Encrypt { + fn iv_state(&self) -> GenericArray { + self.iv.clone() + } +} + +/// CFB mode decryptor. +#[derive(Clone)] +pub struct Decrypt { + cipher: C, + iv: Block, +} + +impl BlockDecryptMut for Decrypt { + fn decrypt_block(&mut self, mut block: impl InOutVal>) { + let mut t = self.iv.clone(); + self.cipher.encrypt_block(&mut t); + xor(&mut t, block.get_in()); + self.iv = block.get_in().clone(); + *block.get_out() = t; + } + + fn decrypt_blocks( + &mut self, + blocks: InOutBuf<'_, '_, Block>, + mut proc: impl FnMut(InResOutBuf<'_, '_, '_, Block>), + ) { + let mut enc_iv = self.iv.clone(); + self.cipher.encrypt_block(&mut enc_iv); + let iv = &mut self.iv; + self.cipher.encrypt_blocks(blocks, |mut buf| { + let len = buf.len(); + let (in_buf, res_buf) = buf.get_in_res(); + for i in 0..len { + xor(&mut enc_iv, &in_buf[i]); + core::mem::swap(&mut res_buf[i], &mut enc_iv); + } + *iv = in_buf[len - 1].clone(); + proc(buf); + }); + } +} + +impl BlockProcessing for Decrypt { + type BlockSize = C::BlockSize; +} + +impl AsyncStreamCipher for Decrypt {} + +impl InnerIvInit for Decrypt { + type Inner = C; + type IvSize = C::BlockSize; + + #[inline] + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { + Self { + cipher, + iv: iv.clone(), + } + } +} + +impl IvState for Decrypt { + fn iv_state(&self) -> GenericArray { + self.iv.clone() + } +} + +#[inline(always)] +fn xor>(out: &mut GenericArray, buf: &GenericArray) { + for (a, b) in out.iter_mut().zip(buf) { + *a ^= *b; + } +} diff --git a/modes/cfb8/CHANGELOG.md b/modes/cfb8/CHANGELOG.md new file mode 100644 index 00000000..87bd6720 --- /dev/null +++ b/modes/cfb8/CHANGELOG.md @@ -0,0 +1,34 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.6.0 (2020-10-16) +### Changed +- Replace `block-cipher`/`stream-cipher` with `cipher` crate ([#177]) + +[#177]: https://github.com/RustCrypto/stream-ciphers/pull/177 + +## 0.5.0 (2020-08-25) +### Changed +- Bump `stream-cipher` dependency to v0.7, implement the `FromBlockCipher` trait ([#161], [#164]) + +[#161]: https://github.com/RustCrypto/stream-ciphers/pull/161 +[#164]: https://github.com/RustCrypto/stream-ciphers/pull/164 + +## 0.4.0 (2020-06-08) +### Changed +- Bump `stream-cipher` dependency to v0.4 ([#120]) +- Upgrade to Rust 2018 edition ([#120]) + +[#120]: https://github.com/RustCrypto/stream-ciphers/pull/120 + +## 0.3.2 (2019-03-11) + +## 0.3.1 (2018-11-01) + +## 0.3.0 (2018-11-01) + +## 0.1.0 (2018-11-01) diff --git a/modes/cfb8/Cargo.toml b/modes/cfb8/Cargo.toml new file mode 100644 index 00000000..cf6aef8b --- /dev/null +++ b/modes/cfb8/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "cfb8" +version = "0.7.0" +description = "Cipher Feedback with eight bit feedback (CFB-8) block cipher mode of operation" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +readme = "README.md" +edition = "2018" +documentation = "https://docs.rs/cfb8" +repository = "https://github.com/RustCrypto/block-ciphers" +keywords = ["crypto", "block-cipher", "ciphers"] + +[dependencies] +cipher = { git = "https://github.com/RustCrypto/traits", branch = "experimental_design" } + +[dev-dependencies] +aes = { version = "=0.7.0-pre", path = "../../aes", features = ["force-soft"] } diff --git a/modes/cfb8/README.md b/modes/cfb8/README.md new file mode 100644 index 00000000..46cdda5d --- /dev/null +++ b/modes/cfb8/README.md @@ -0,0 +1,60 @@ +# RustCrypto: CFB-8 + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] +[![Build Status][build-image]][build-link] + +Generic implementation of the [Cipher Feedback][CFB-8] with eight bit feedback (CFB-8) +block cipher mode of operation. + + + +See [documentation][cipher-doc] of the `cipher` crate for additional information. + +## Minimum Supported Rust Version + +Rust **1.41** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/cfb.svg +[crate-link]: https://crates.io/crates/cfb +[docs-image]: https://docs.rs/cfb/badge.svg +[docs-link]: https://docs.rs/cfb/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260039-block-ciphers +[build-image]: https://github.com/RustCrypto/block-ciphers/workflows/cfb/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/block-ciphers/actions?query=workflow%cfb + +[//]: # (general links) + +[CFB]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CFB-1,_CFB-8,_CFB-64,_CFB-128,_etc. +[cipher-doc]: https://docs.rs/cipher/ diff --git a/modes/cfb8/src/lib.rs b/modes/cfb8/src/lib.rs new file mode 100644 index 00000000..2defbd27 --- /dev/null +++ b/modes/cfb8/src/lib.rs @@ -0,0 +1,107 @@ +//! [Cipher Feedback with eight bit feedback][1] (CFB-8) mode. +//! +//! [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CFB-1,_CFB-8,_CFB-64,_CFB-128,_etc. + +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/master/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/master/logo.svg" +)] +#![deny(unsafe_code)] +#![warn(missing_docs, rust_2018_idioms)] + +use cipher::{ + generic_array::{typenum::U1, GenericArray}, + AsyncStreamCipher, Block, BlockCipher, BlockDecryptMut, BlockEncryptMut, BlockProcessing, + InOutVal, InnerIvInit, IvState, +}; + +/// CFB-8 mode encryptor. +#[derive(Clone)] +pub struct Encrypt { + cipher: C, + iv: Block, +} + +impl BlockEncryptMut for Encrypt { + fn encrypt_block(&mut self, mut block: impl InOutVal>) { + let mut t = self.iv.clone(); + self.cipher.encrypt_block(&mut t); + let r = block.get_in()[0] ^ t[0]; + block.get_out()[0] = r; + let n = self.iv.len(); + for i in 0..n - 1 { + self.iv[i] = self.iv[i + 1]; + } + self.iv[n - 1] = r; + } +} + +impl BlockProcessing for Encrypt { + type BlockSize = U1; +} + +impl AsyncStreamCipher for Encrypt {} + +impl InnerIvInit for Encrypt { + type Inner = C; + type IvSize = C::BlockSize; + + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { + Self { + cipher, + iv: iv.clone(), + } + } +} + +impl IvState for Encrypt { + fn iv_state(&self) -> GenericArray { + self.iv.clone() + } +} + +/// CFB-8 mode decryptor. +#[derive(Clone)] +pub struct Decrypt { + cipher: C, + iv: Block, +} + +impl BlockDecryptMut for Decrypt { + fn decrypt_block(&mut self, mut block: impl InOutVal>) { + let mut t = self.iv.clone(); + self.cipher.encrypt_block(&mut t); + let r = block.get_in()[0]; + block.get_out()[0] = r ^ t[0]; + let n = self.iv.len(); + for i in 0..n - 1 { + self.iv[i] = self.iv[i + 1]; + } + self.iv[n - 1] = r; + } +} + +impl BlockProcessing for Decrypt { + type BlockSize = U1; +} + +impl AsyncStreamCipher for Decrypt {} + +impl InnerIvInit for Decrypt { + type Inner = C; + type IvSize = C::BlockSize; + + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { + Self { + cipher, + iv: iv.clone(), + } + } +} + +impl IvState for Decrypt { + fn iv_state(&self) -> GenericArray { + self.iv.clone() + } +} diff --git a/modes/ctr/CHANGELOG.md b/modes/ctr/CHANGELOG.md new file mode 100644 index 00000000..cad9c7f7 --- /dev/null +++ b/modes/ctr/CHANGELOG.md @@ -0,0 +1,41 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.6.0 (2020-10-16) +### Added +- `Ctr32BE` and `Ctr32LE` ([#170]) + +### Changed +- Replace `block-cipher`/`stream-cipher` with `cipher` crate ([#177]) + +[#177]: https://github.com/RustCrypto/stream-ciphers/pull/177 +[#170]: https://github.com/RustCrypto/stream-ciphers/pull/170 + +## 0.5.0 (2020-08-26) +### Changed +- Bump `stream-cipher` dependency to v0.7, implement the `FromBlockCipher` trait ([#161], [#164]) + +[#161]: https://github.com/RustCrypto/stream-ciphers/pull/161 +[#164]: https://github.com/RustCrypto/stream-ciphers/pull/164 + +## 0.4.0 (2020-06-06) +### Changed +- Upgrade to the `stream-cipher` v0.4 crate ([#116], [#138]) +- Upgrade to Rust 2018 edition ([#116]) + +[#138]: https://github.com/RustCrypto/stream-ciphers/pull/138 +[#116]: https://github.com/RustCrypto/stream-ciphers/pull/121 + +## 0.3.2 (2019-03-11) + +## 0.3.0 (2018-11-01) + +## 0.2.0 (2018-10-13) + +## 0.1.1 (2018-10-13) + +## 0.1.0 (2018-07-30) diff --git a/modes/ctr/Cargo.toml b/modes/ctr/Cargo.toml new file mode 100644 index 00000000..ce27e473 --- /dev/null +++ b/modes/ctr/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "ctr" +version = "0.7.0" +description = "Counter (CTR) block cipher mode of operation" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +readme = "README.md" +edition = "2018" +documentation = "https://docs.rs/ctr" +repository = "https://github.com/RustCrypto/block-ciphers" +keywords = ["crypto", "block-cipher", "ciphers"] + +[dependencies] +cipher = { git = "https://github.com/RustCrypto/traits", branch = "experimental_design" } + +[dev-dependencies] +aes = { version = "=0.7.0-pre", path = "../../aes", features = ["force-soft"] } diff --git a/modes/ctr/README.md b/modes/ctr/README.md new file mode 100644 index 00000000..7a87fc22 --- /dev/null +++ b/modes/ctr/README.md @@ -0,0 +1,59 @@ +# RustCrypto: CTR + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] +[![Build Status][build-image]][build-link] + +Generic implementation of the [Counter][CTR] (CTR) block cipher mode of operation. + + + +See [documentation][cipher-doc] of the `cipher` crate for additional information. + +## Minimum Supported Rust Version + +Rust **1.41** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/ctr.svg +[crate-link]: https://crates.io/crates/ctr +[docs-image]: https://docs.rs/ctr/badge.svg +[docs-link]: https://docs.rs/ctr/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260039-block-ciphers +[build-image]: https://github.com/RustCrypto/block-ciphers/workflows/ctr/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/block-ciphers/actions?query=workflow%ctr + +[//]: # (general links) + +[CTR]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR) +[cipher-doc]: https://docs.rs/cipher/ diff --git a/modes/ctr/src/lib.rs b/modes/ctr/src/lib.rs new file mode 100644 index 00000000..e69de29b diff --git a/modes/ige/CHANGELOG.md b/modes/ige/CHANGELOG.md new file mode 100644 index 00000000..da01deb5 --- /dev/null +++ b/modes/ige/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.1.0 (2018-04-25) +- Initial release diff --git a/modes/ige/Cargo.toml b/modes/ige/Cargo.toml new file mode 100644 index 00000000..512f35aa --- /dev/null +++ b/modes/ige/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "ige" +version = "0.1.0" +description = "Infinite Garble Extension (IGE) block cipher mode of operation" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +readme = "README.md" +edition = "2018" +documentation = "https://docs.rs/ige" +repository = "https://github.com/RustCrypto/block-ciphers" +keywords = ["crypto", "block-cipher", "ciphers"] + +[dependencies] +cipher = { git = "https://github.com/RustCrypto/traits", branch = "experimental_design" } + +[dev-dependencies] +aes = { version = "=0.7.0-pre", path = "../../aes", features = ["force-soft"] } diff --git a/modes/ige/README.md b/modes/ige/README.md new file mode 100644 index 00000000..af76ed4e --- /dev/null +++ b/modes/ige/README.md @@ -0,0 +1,60 @@ +# RustCrypto: IGE + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] +[![Build Status][build-image]][build-link] + +Generic implementation of the [Infinite Garble Extension][IGE] (IGE) block cipher mode +of operation. + + + +See [documentation][cipher-doc] of the `cipher` crate for additional information. + +## Minimum Supported Rust Version + +Rust **1.41** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/ige.svg +[crate-link]: https://crates.io/crates/ige +[docs-image]: https://docs.rs/ige/badge.svg +[docs-link]: https://docs.rs/ige/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260039-block-ciphers +[build-image]: https://github.com/RustCrypto/block-ciphers/workflows/ige/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/block-ciphers/actions?query=workflow%ige + +[//]: # (general links) + +[CBC]: https://www.links.org/files/openssl-ige.pdf +[cipher-doc]: https://docs.rs/cipher/ diff --git a/modes/ige/src/lib.rs b/modes/ige/src/lib.rs new file mode 100644 index 00000000..ddbf77a1 --- /dev/null +++ b/modes/ige/src/lib.rs @@ -0,0 +1,175 @@ +//! [Infinite Garble Extension][1] (IGE) block cipher mode of operation. +//! +//! [1]: https://www.links.org/files/openssl-ige.pdf + +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/master/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/master/logo.svg" +)] +#![deny(unsafe_code)] +#![warn(missing_docs, rust_2018_idioms)] + +use cipher::{ + generic_array::{ + sequence::Concat, + typenum::{Sum, Unsigned}, + ArrayLength, GenericArray, + }, + Block, BlockCipher, BlockDecryptMut, BlockEncryptMut, BlockProcessing, InOutVal, InnerIvInit, + IvState, +}; +use core::ops::Add; + +type BlockSize = ::BlockSize; +type IgeIvSize = Sum, BlockSize>; + +/// IGE mode encryptor. +#[derive(Clone)] +pub struct Encrypt +where + C: BlockEncryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + cipher: C, + x: Block, + y: Block, +} + +impl BlockEncryptMut for Encrypt +where + C: BlockEncryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + fn encrypt_block(&mut self, mut block: impl InOutVal>) { + let new_x = block.get_in().clone(); + let mut t = block.get_in().clone(); + xor(&mut t, &self.y); + self.cipher.encrypt_block(&mut t); + xor(&mut t, &self.x); + *block.get_out() = t.clone(); + self.x = new_x; + self.y = t; + } +} + +impl BlockProcessing for Encrypt +where + C: BlockEncryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + type BlockSize = C::BlockSize; +} + +impl InnerIvInit for Encrypt +where + C: BlockEncryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + type Inner = C; + type IvSize = IgeIvSize; + + #[inline] + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { + let (y, x) = iv.split_at(C::BlockSize::to_usize()); + Self { + cipher, + x: GenericArray::clone_from_slice(x), + y: GenericArray::clone_from_slice(y), + } + } +} + +impl IvState for Encrypt +where + C: BlockEncryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + #[inline] + fn iv_state(&self) -> GenericArray { + self.y.clone().concat(self.x.clone()) + } +} + +/// IGE mode decryptor. +#[derive(Clone)] +pub struct Decrypt +where + C: BlockDecryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + cipher: C, + x: Block, + y: Block, +} + +impl BlockDecryptMut for Decrypt +where + C: BlockDecryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + fn decrypt_block(&mut self, mut block: impl InOutVal>) { + let new_y = block.get_in().clone(); + let mut t = block.get_in().clone(); + xor(&mut t, &self.x); + self.cipher.decrypt_block(&mut t); + xor(&mut t, &self.y); + *block.get_out() = t.clone(); + self.y = new_y; + self.x = t; + } +} + +impl BlockProcessing for Decrypt +where + C: BlockDecryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + type BlockSize = C::BlockSize; +} + +impl InnerIvInit for Decrypt +where + C: BlockDecryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + type Inner = C; + type IvSize = IgeIvSize; + + #[inline] + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { + let (y, x) = iv.split_at(C::BlockSize::to_usize()); + Self { + cipher, + x: GenericArray::clone_from_slice(x), + y: GenericArray::clone_from_slice(y), + } + } +} + +impl IvState for Decrypt +where + C: BlockDecryptMut + BlockCipher, + C::BlockSize: Add, + IgeIvSize: ArrayLength, +{ + fn iv_state(&self) -> GenericArray { + self.y.clone().concat(self.x.clone()) + } +} + +#[inline(always)] +fn xor>(out: &mut GenericArray, buf: &GenericArray) { + for (a, b) in out.iter_mut().zip(buf) { + *a ^= *b; + } +} diff --git a/modes/ofb/CHANGELOG.md b/modes/ofb/CHANGELOG.md new file mode 100644 index 00000000..46b84fbb --- /dev/null +++ b/modes/ofb/CHANGELOG.md @@ -0,0 +1,30 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.4.0 (2020-10-16) +### Changed +- Replace `block-cipher`/`stream-cipher` with `cipher` crate ([#177]) + +[#177]: https://github.com/RustCrypto/stream-ciphers/pull/177 + +## 0.3.0 (2020-08-25) +### Changed +- Bump `stream-cipher` dependency to v0.7, implement the `FromBlockCipher` trait ([#161], [#164]) + +[#161]: https://github.com/RustCrypto/stream-ciphers/pull/161 +[#164]: https://github.com/RustCrypto/stream-ciphers/pull/164 + +## 0.2.0 (2020-06-08) +### Changed +- Bump `stream-cipher` dependency to v0.4 ([#123]) +- Upgrade to Rust 2018 edition ([#123]) + +[#123]: https://github.com/RustCrypto/stream-ciphers/pull/123 + +## 0.1.1 (2019-03-11) + +## 0.1.0 (2018-12-26) diff --git a/modes/ofb/Cargo.toml b/modes/ofb/Cargo.toml new file mode 100644 index 00000000..32572f5a --- /dev/null +++ b/modes/ofb/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "ofb" +version = "0.5.0" +description = "Output Feedback][OFB] (OFB) block cipher mode of operation" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +readme = "README.md" +edition = "2018" +documentation = "https://docs.rs/ofb" +repository = "https://github.com/RustCrypto/block-ciphers" +keywords = ["crypto", "block-cipher", "ciphers"] + +[dependencies] +cipher = { git = "https://github.com/RustCrypto/traits", branch = "experimental_design" } + +[dev-dependencies] +aes = { version = "=0.7.0-pre", path = "../../aes", features = ["force-soft"] } diff --git a/modes/ofb/README.md b/modes/ofb/README.md new file mode 100644 index 00000000..cde9dd09 --- /dev/null +++ b/modes/ofb/README.md @@ -0,0 +1,60 @@ +# RustCrypto: OFB + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] +[![Build Status][build-image]][build-link] + +Generic implementation of the [Output Feedback][OFB] (OFB) block cipher mode +of operation. + + + +See [documentation][cipher-doc] of the `cipher` crate for additional information. + +## Minimum Supported Rust Version + +Rust **1.41** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/ofb.svg +[crate-link]: https://crates.io/crates/ofb +[docs-image]: https://docs.rs/ofb/badge.svg +[docs-link]: https://docs.rs/ofb/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260039-block-ciphers +[build-image]: https://github.com/RustCrypto/block-ciphers/workflows/ofb/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/block-ciphers/actions?query=workflow%ofb + +[//]: # (general links) + +[OFB]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_(OFB) +[cipher-doc]: https://docs.rs/cipher/ diff --git a/modes/ofb/src/lib.rs b/modes/ofb/src/lib.rs new file mode 100644 index 00000000..c9ca9020 --- /dev/null +++ b/modes/ofb/src/lib.rs @@ -0,0 +1,82 @@ +//! [Output feedback][1] (OFB) mode. +//! +//! [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_(OFB) + +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/master/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/master/logo.svg" +)] +#![deny(unsafe_code)] +#![warn(missing_docs, rust_2018_idioms)] + +use cipher::{ + errors::LoopError, + generic_array::{ArrayLength, GenericArray}, + Block, BlockCipher, BlockDecryptMut, BlockEncryptMut, BlockProcessing, InOutVal, InnerIvInit, + IvState, StreamCipherCore, +}; + +/// Output feedback (OFB) mode. +#[derive(Clone)] +pub struct Ofb { + cipher: C, + iv: Block, +} + +impl StreamCipherCore for Ofb { + fn gen_keystream_block(&mut self) -> Result, LoopError> { + self.cipher.encrypt_block(&mut self.iv); + Ok(self.iv.clone()) + } +} + +impl BlockProcessing for Ofb { + type BlockSize = C::BlockSize; +} + +impl InnerIvInit for Ofb { + type Inner = C; + type IvSize = C::BlockSize; + + #[inline] + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { + Self { + cipher, + iv: iv.clone(), + } + } +} + +impl IvState for Ofb { + #[inline] + fn iv_state(&self) -> GenericArray { + self.iv.clone() + } +} + +impl BlockEncryptMut for Ofb { + fn encrypt_block(&mut self, mut block: impl InOutVal>) { + self.cipher.encrypt_block(&mut self.iv); + *block.get_out() = xor_ret(&self.iv, block.get_in()); + } +} + +impl BlockDecryptMut for Ofb { + fn decrypt_block(&mut self, mut block: impl InOutVal>) { + self.cipher.encrypt_block(&mut self.iv); + *block.get_out() = xor_ret(&self.iv, block.get_in()); + } +} + +#[inline(always)] +fn xor_ret>( + buf1: &GenericArray, + buf2: &GenericArray, +) -> GenericArray { + let mut res = GenericArray::::default(); + for i in 0..N::USIZE { + res[i] = buf1[i] ^ buf2[i]; + } + res +} diff --git a/modes/pcbc/CHANGELOG.md b/modes/pcbc/CHANGELOG.md new file mode 100644 index 00000000..da01deb5 --- /dev/null +++ b/modes/pcbc/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.1.0 (2018-04-25) +- Initial release diff --git a/modes/pcbc/Cargo.toml b/modes/pcbc/Cargo.toml new file mode 100644 index 00000000..ba98ab56 --- /dev/null +++ b/modes/pcbc/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "pcbc" +version = "0.1.0" +description = "Propagating Cipher Block Chaining (PCBC) block cipher mode of operation" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +readme = "README.md" +edition = "2018" +documentation = "https://docs.rs/pcbc" +repository = "https://github.com/RustCrypto/block-ciphers" +keywords = ["crypto", "block-cipher", "ciphers"] + +[dependencies] +cipher = { git = "https://github.com/RustCrypto/traits", branch = "experimental_design" } + +[dev-dependencies] +aes = { version = "=0.7.0-pre", path = "../../aes", features = ["force-soft"] } diff --git a/modes/pcbc/README.md b/modes/pcbc/README.md new file mode 100644 index 00000000..dbf70203 --- /dev/null +++ b/modes/pcbc/README.md @@ -0,0 +1,60 @@ +# RustCrypto: PCBC + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] +[![Build Status][build-image]][build-link] + +Generic implementation of the [Propagating Cipher Block Chaining][PCBC] (PCBC) +block cipher mode of operation. + + + +See [documentation][cipher-doc] of the `cipher` crate for additional information. + +## Minimum Supported Rust Version + +Rust **1.41** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/pcbc.svg +[crate-link]: https://crates.io/crates/pcbc +[docs-image]: https://docs.rs/pcbc/badge.svg +[docs-link]: https://docs.rs/pcbc/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260039-block-ciphers +[build-image]: https://github.com/RustCrypto/block-ciphers/workflows/pcbc/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/block-ciphers/actions?query=workflow%pcbc + +[//]: # (general links) + +[PCBC]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Propagating_cipher_block_chaining_(PCBC) +[cipher-doc]: https://docs.rs/cipher/ diff --git a/modes/pcbc/src/lib.rs b/modes/pcbc/src/lib.rs new file mode 100644 index 00000000..4afbe47d --- /dev/null +++ b/modes/pcbc/src/lib.rs @@ -0,0 +1,106 @@ +//! [Propagating Cipher Block Chaining][1] (PCBC) mode. +//! +//! [1]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Propagating_cipher_block_chaining_(PCBC) + +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/master/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/master/logo.svg" +)] +#![deny(unsafe_code)] +#![warn(missing_docs, rust_2018_idioms)] + +use cipher::{ + generic_array::{ArrayLength, GenericArray}, + Block, BlockCipher, BlockDecryptMut, BlockEncryptMut, BlockProcessing, InOutVal, InnerIvInit, + IvState, +}; + +/// PCBC mode encryptor. +#[derive(Clone)] +pub struct Encrypt { + cipher: C, + iv: Block, +} + +impl BlockEncryptMut for Encrypt { + fn encrypt_block(&mut self, mut block: impl InOutVal>) { + let mut t = self.iv.clone(); + xor(&mut t, block.get_in()); + self.cipher.encrypt_block((&t, block.get_out())); + xor(&mut t, block.get_out()); + self.iv = t; + } +} + +impl BlockProcessing for Encrypt { + type BlockSize = C::BlockSize; +} + +impl InnerIvInit for Encrypt { + type Inner = C; + type IvSize = C::BlockSize; + + #[inline] + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { + Self { + cipher, + iv: iv.clone(), + } + } +} + +impl IvState for Encrypt { + #[inline] + fn iv_state(&self) -> GenericArray { + self.iv.clone() + } +} + +/// PCBC mode decryptor. +#[derive(Clone)] +pub struct Decrypt { + cipher: C, + iv: Block, +} + +impl BlockDecryptMut for Decrypt { + fn decrypt_block(&mut self, mut block: impl InOutVal>) { + let mut t = Default::default(); + self.cipher.decrypt_block((block.get_in(), &mut t)); + xor(&mut t, &self.iv); + self.iv.copy_from_slice(block.get_in()); + block.get_out().copy_from_slice(&t); + xor(&mut self.iv, &t); + } +} + +impl BlockProcessing for Decrypt { + type BlockSize = C::BlockSize; +} + +impl InnerIvInit for Decrypt { + type Inner = C; + type IvSize = C::BlockSize; + + #[inline] + fn inner_iv_init(cipher: C, iv: &GenericArray) -> Self { + Self { + cipher, + iv: iv.clone(), + } + } +} + +impl IvState for Decrypt { + fn iv_state(&self) -> GenericArray { + self.iv.clone() + } +} + +#[inline(always)] +fn xor>(out: &mut GenericArray, buf: &GenericArray) { + for (a, b) in out.iter_mut().zip(buf) { + *a ^= *b; + } +}