From 92346d7cea370d43ef1d948b32c3345b5e4d7b75 Mon Sep 17 00:00:00 2001 From: Antonio Date: Wed, 23 Nov 2022 20:43:09 +0100 Subject: [PATCH 01/13] initial rc5 impl --- .github/workflows/rc5.yml | 63 ++++++++++++++++ Cargo.lock | 7 ++ Cargo.toml | 1 + rc5/CHANGELOG.md | 8 +++ rc5/Cargo.toml | 21 ++++++ rc5/LICENSE-APACHE | 13 ++++ rc5/LICENSE-MIT | 25 +++++++ rc5/README.md | 71 ++++++++++++++++++ rc5/benches/mod.rs | 8 +++ rc5/src/block_cipher.rs | 64 +++++++++++++++++ rc5/src/core/mod.rs | 144 +++++++++++++++++++++++++++++++++++++ rc5/src/core/primitives.rs | 79 ++++++++++++++++++++ rc5/src/lib.rs | 12 ++++ rc5/tests/mod.rs | 26 +++++++ 14 files changed, 542 insertions(+) create mode 100644 .github/workflows/rc5.yml create mode 100644 rc5/CHANGELOG.md create mode 100644 rc5/Cargo.toml create mode 100644 rc5/LICENSE-APACHE create mode 100644 rc5/LICENSE-MIT create mode 100644 rc5/README.md create mode 100644 rc5/benches/mod.rs create mode 100644 rc5/src/block_cipher.rs create mode 100644 rc5/src/core/mod.rs create mode 100644 rc5/src/core/primitives.rs create mode 100644 rc5/src/lib.rs create mode 100644 rc5/tests/mod.rs diff --git a/.github/workflows/rc5.yml b/.github/workflows/rc5.yml new file mode 100644 index 00000000..b725266e --- /dev/null +++ b/.github/workflows/rc5.yml @@ -0,0 +1,63 @@ +name: rc5 +on: + pull_request: + paths: + - "rc5/**" + - "Cargo.*" + push: + branches: master + +defaults: + run: + working-directory: rc5 + +env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Dwarnings" + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + target: + - thumbv7em-none-eabi + - wasm32-unknown-unknown + steps: + - uses: actions/checkout@v3 + - uses: RustCrypto/actions/cargo-cache@master + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + - run: cargo build --no-default-features --release --target ${{ matrix.target }} + + minimal-versions: + uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master + with: + working-directory: ${{ github.workflow }} + + test: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + steps: + - uses: actions/checkout@v3 + - uses: RustCrypto/actions/cargo-cache@master + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + override: true + profile: minimal + - run: cargo check --all-features + - run: cargo test --no-default-features + - run: cargo test + - run: cargo test --all-features diff --git a/Cargo.lock b/Cargo.lock index 58726ba0..94f68c27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -170,6 +170,13 @@ dependencies = [ "cipher", ] +[[package]] +name = "rc5" +version = "0.1.0" +dependencies = [ + "cipher", +] + [[package]] name = "serpent" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 4b45587f..441df7ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "sm4", "twofish", "threefish", + "rc5" ] [profile.dev] diff --git a/rc5/CHANGELOG.md b/rc5/CHANGELOG.md new file mode 100644 index 00000000..fa09b52d --- /dev/null +++ b/rc5/CHANGELOG.md @@ -0,0 +1,8 @@ +# 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 (2022-11-23) diff --git a/rc5/Cargo.toml b/rc5/Cargo.toml new file mode 100644 index 00000000..d6d790cc --- /dev/null +++ b/rc5/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "rc5" +version = "0.1.0" +description = "RC5 block cipher" +authors = ["RustCrypto Developers"] +edition = "2021" +license = "MIT OR Apache-2.0" +rust-version = "1.56" +readme = "README.md" +repository = "https://github.com/RustCrypto/block-ciphers" +keywords = ["crypto", "rc5", "block-cipher"] +categories = ["cryptography"] + +[dependencies] +cipher = { version = "0.4.3", features = ["zeroize"] } + +[dev-dependencies] +cipher = { version = "0.4.3", features = ["dev"] } + +[features] +zeroize = [] \ No newline at end of file diff --git a/rc5/LICENSE-APACHE b/rc5/LICENSE-APACHE new file mode 100644 index 00000000..cf59c06d --- /dev/null +++ b/rc5/LICENSE-APACHE @@ -0,0 +1,13 @@ +Copyright 2022 Antonio Dropulic + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/rc5/LICENSE-MIT b/rc5/LICENSE-MIT new file mode 100644 index 00000000..8dd9c4e4 --- /dev/null +++ b/rc5/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2022 Antonio Dropulic + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/rc5/README.md b/rc5/README.md new file mode 100644 index 00000000..2db5f57b --- /dev/null +++ b/rc5/README.md @@ -0,0 +1,71 @@ +# RustCrypto: RC2 Cipher + +[![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] +[![HAZMAT][hazmat-image]][hazmat-link] + +Pure Rust implementation of the [RC5 block cipher][1]. + +[Documentation][docs-link] + +## ⚠️ Security Warning: [Hazmat!][hazmat-link] + +This crate does not ensure ciphertexts are authentic (i.e. by using a MAC to +verify ciphertext integrity), which can lead to serious vulnerabilities +if used incorrectly! + +No security audits of this crate have ever been performed, and it has not been +thoroughly assessed to ensure its operation is constant-time on common CPU +architectures. + +USE AT YOUR OWN RISK! + +## Minimum Supported Rust Version + +Rust **1.56** 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/rc2.svg +[crate-link]: https://crates.io/crates/rc2 +[docs-image]: https://docs.rs/rc2/badge.svg +[docs-link]: https://docs.rs/rc2/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg +[hazmat-image]: https://img.shields.io/badge/crypto-hazmat%E2%9A%A0-red.svg +[hazmat-link]: https://github.com/RustCrypto/meta/blob/master/HAZMAT.md +[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/rc2/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/block-ciphers/actions?query=workflow%3Arc2 + +[//]: # (general links) + +[1]: https://en.wikipedia.org/wiki/RC5 diff --git a/rc5/benches/mod.rs b/rc5/benches/mod.rs new file mode 100644 index 00000000..2f4907dd --- /dev/null +++ b/rc5/benches/mod.rs @@ -0,0 +1,8 @@ +#![feature(test)] +extern crate test; + +use cipher::{block_decryptor_bench, block_encryptor_bench}; +use rc5::RC5_32_20_16; + +block_encryptor_bench!(Key: RC5_32_20_16, rc5_encrypt_block, rc5_encrypt_blocks,); +block_decryptor_bench!(Key: RC5_32_20_16, rc5_decrypt_block, rc5_decrypt_blocks,); diff --git a/rc5/src/block_cipher.rs b/rc5/src/block_cipher.rs new file mode 100644 index 00000000..d6c01b91 --- /dev/null +++ b/rc5/src/block_cipher.rs @@ -0,0 +1,64 @@ +use cipher::consts::{U16, U20, U8}; + +use crate::core::{ExpandedKeyTable, RC5}; +use cipher::{impl_simple_block_encdec, AlgorithmName, KeyInit}; +use cipher::{inout::InOut, Block, BlockCipher, KeySizeUser}; + +pub struct RC5_32_20_16 { + key_table: ExpandedKeyTable, +} + +impl RC5 for RC5_32_20_16 {} + +impl RC5_32_20_16 { + fn encrypt_block(&self, block: InOut<'_, '_, Block>) { + Self::encrypt(block, &self.key_table); + } + + fn decrypt_block(&self, block: InOut<'_, '_, Block>) { + Self::decrypt(block, &self.key_table); + } +} + +impl BlockCipher for RC5_32_20_16 {} + +impl KeySizeUser for RC5_32_20_16 { + type KeySize = U16; +} + +impl KeyInit for RC5_32_20_16 { + fn new(key: &cipher::Key) -> Self { + Self { + key_table: Self::substitute_key(key), + } + } +} + +impl AlgorithmName for RC5_32_20_16 { + fn write_alg_name(f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str("RC5-32/12/16") + } +} + +impl_simple_block_encdec!( + RC5_32_20_16, U8, cipher, block, + encrypt: { + cipher.encrypt_block(block); + } + decrypt: { + cipher.decrypt_block(block); + } +); + +#[cfg(feature = "zeroize")] +use cipher::zeroize::Zeroize; + +#[cfg(feature = "zeroize")] +impl cipher::zeroize::ZeroizeOnDrop for RC5_32_20_16 {} + +#[cfg(feature = "zeroize")] +impl Drop for RC5_32_20_16 { + fn drop(&mut self) { + self.key_table.zeroize() + } +} diff --git a/rc5/src/core/mod.rs b/rc5/src/core/mod.rs new file mode 100644 index 00000000..c644499f --- /dev/null +++ b/rc5/src/core/mod.rs @@ -0,0 +1,144 @@ +//! Implementation according to the [RC5 paper] +//! [RC5 paper]: https://www.grc.com/r&d/rc5.pdf + +mod primitives; +pub use primitives::*; + +use std::{ + cmp::max, + convert::TryInto, + ops::{Add, Div, Mul, Sub}, +}; + +use cipher::{ + generic_array::{sequence::GenericSequence, ArrayLength, GenericArray}, + inout::InOut, + typenum::{Diff, Sum, Unsigned, U1, U2}, +}; + +pub trait RC5 +where + // u16 or u32 or u64 + W: Word, + // Block size is 2 * W::Bytes + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds are an uint in the range 0-255 + R: Unsigned, + // expanded key table size = (R + 1) * 2 + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, + // key size is an uint in the range 0-255 + B: ArrayLength, + // key as words size = div_ceil(B, W::Bytes) + B: Add, + Sum: Sub, + Diff, U1>: Div, + KeyAsWordsSize: ArrayLength, +{ + fn encrypt(mut block: InOut<'_, '_, Block>, key: &ExpandedKeyTable) { + let (mut a, mut b) = Self::words_from_block(block.get_in()); + + a = a.wrapping_add(key[0]); + b = b.wrapping_add(key[1]); + + for i in 1..=R::USIZE { + a = a.bitxor(b).rotate_left(b).wrapping_add(key[2 * i]); + b = b.bitxor(a).rotate_left(a).wrapping_add(key[2 * i + 1]); + } + + Self::block_from_words(a, b, block.get_out()) + } + + fn decrypt(mut block: InOut<'_, '_, Block>, key: &ExpandedKeyTable) { + let (mut a, mut b) = Self::words_from_block(block.get_in()); + + for i in (1..=R::USIZE).rev() { + b = b.wrapping_sub(key[2 * i + 1]).rotate_right(a).bitxor(a); + a = a.wrapping_sub(key[2 * i]).rotate_right(b).bitxor(b); + } + + b = b.wrapping_sub(key[1]); + a = a.wrapping_sub(key[0]); + + Self::block_from_words(a, b, block.get_out()) + } + + fn substitute_key(key: &Key) -> ExpandedKeyTable { + let key_as_words = Self::key_into_words(key); + let expanded_key_table = Self::initialize_expanded_key_table(); + + Self::mix_in(expanded_key_table, key_as_words) + } + + fn words_from_block(block: &Block) -> (W, W) { + // Block size is 2 * word::BYTES so the unwrap is safe + let a = W::from_le_bytes(block[..W::Bytes::USIZE].try_into().unwrap()); + let b = W::from_le_bytes(block[W::Bytes::USIZE..].try_into().unwrap()); + + (a, b) + } + + fn block_from_words(a: W, b: W, out_block: &mut Block) { + let (left, right) = out_block.split_at_mut(W::Bytes::USIZE); + + left.copy_from_slice(&a.to_le_bytes()); + right.copy_from_slice(&b.to_le_bytes()); + } + + fn key_into_words(key: &Key) -> KeyAsWords { + // can be uninitialized + let mut key_as_words: GenericArray> = GenericArray::default(); + + for i in (0..B::USIZE).rev() { + key_as_words[i / W::Bytes::USIZE] = + key_as_words[i / W::Bytes::USIZE].rotate_left(W::EIGHT) + key[i].into(); + // no need for wrapping addition since we are adding a byte sized uint onto an uint with its lsb byte zeroed + } + + key_as_words + } + + fn initialize_expanded_key_table() -> ExpandedKeyTable { + // must be zero initialized + let mut expanded_key_table: GenericArray> = + GenericArray::generate(|_| W::ZERO); + + expanded_key_table[0] = W::P; + for i in 1..expanded_key_table.len() { + expanded_key_table[i] = expanded_key_table[i - 1].wrapping_add(W::Q); + } + + expanded_key_table + } + + fn mix_in( + mut key_table: ExpandedKeyTable, + mut key_as_words: KeyAsWords, + ) -> ExpandedKeyTable { + let (mut expanded_key_index, mut key_as_words_index) = (0, 0); + let (mut a, mut b) = (W::ZERO, W::ZERO); + + for _ in 0..3 * max(key_as_words.len(), key_table.len()) { + key_table[expanded_key_index] = key_table[expanded_key_index] + .wrapping_add(a) + .wrapping_add(b) + .rotate_left(W::THREE); + + a = key_table[expanded_key_index]; + + key_as_words[key_as_words_index] = key_as_words[key_as_words_index] + .wrapping_add(a) + .wrapping_add(b) + .rotate_left(a.wrapping_add(b)); + + b = key_as_words[key_as_words_index]; + + expanded_key_index = (expanded_key_index + 1) % key_table.len(); + key_as_words_index = (key_as_words_index + 1) % key_as_words.len(); + } + + key_table + } +} diff --git a/rc5/src/core/primitives.rs b/rc5/src/core/primitives.rs new file mode 100644 index 00000000..39d43f42 --- /dev/null +++ b/rc5/src/core/primitives.rs @@ -0,0 +1,79 @@ +use std::ops::{Add, BitXor}; + +use cipher::{ + generic_array::{ArrayLength, GenericArray}, + typenum::{Diff, Prod, Quot, Sum, U1, U2, U4}, + zeroize::Zeroize, +}; + +pub type BlockSize = Prod<::Bytes, U2>; +pub type Block = GenericArray>; + +pub type Key = GenericArray; + +pub type ExpandedKeyTable = GenericArray>; +pub type ExpandedKeyTableSize = Prod, U2>; + +pub type KeyAsWords = GenericArray>; +pub type KeyAsWordsSize = Quot::Bytes>, U1>, ::Bytes>; + +pub trait Word: Default + Copy + From + Add + Zeroize { + type Bytes: ArrayLength; + + const ZERO: Self; + const THREE: Self; + const EIGHT: Self; + + const P: Self; + const Q: Self; + + fn wrapping_add(self, rhs: Self) -> Self; + fn wrapping_sub(self, rhs: Self) -> Self; + + fn rotate_left(self, n: Self) -> Self; + fn rotate_right(self, n: Self) -> Self; + + fn from_le_bytes(bytes: &GenericArray) -> Self; + fn to_le_bytes(self) -> GenericArray; + + fn bitxor(self, other: Self) -> Self; +} + +impl Word for u32 { + type Bytes = U4; + + const ZERO: Self = 0; + const THREE: Self = 3; + const EIGHT: Self = 8; + + const P: Self = 0xb7e15163; + const Q: Self = 0x9e3779b9; + + fn wrapping_add(self, rhs: Self) -> Self { + u32::wrapping_add(self, rhs) + } + + fn wrapping_sub(self, rhs: Self) -> Self { + u32::wrapping_sub(self, rhs) + } + + fn rotate_left(self, n: Self) -> Self { + u32::rotate_left(self, n) + } + + fn rotate_right(self, n: Self) -> Self { + u32::rotate_right(self, n) + } + + fn from_le_bytes(bytes: &GenericArray) -> Self { + u32::from_le_bytes(bytes.to_owned().into()) + } + + fn to_le_bytes(self) -> GenericArray { + u32::to_le_bytes(self).into() + } + + fn bitxor(self, other: Self) -> Self { + ::bitxor(self, other) + } +} diff --git a/rc5/src/lib.rs b/rc5/src/lib.rs new file mode 100644 index 00000000..8f2c3a05 --- /dev/null +++ b/rc5/src/lib.rs @@ -0,0 +1,12 @@ +//! Implementation of the RC5-32/12/16 block cipher. Based on the [RC5 paper]. +//! +//! ## WARNING +//! This crate implements the low-level RC5 block function. It is intended for implementing +//! higher level constructions. It is not intended for direct use in applications. +//! +//! [RC5 paper]: https://www.grc.com/r&d/rc5.pdf + +mod block_cipher; +mod core; + +pub use block_cipher::*; diff --git a/rc5/tests/mod.rs b/rc5/tests/mod.rs new file mode 100644 index 00000000..ac963ce9 --- /dev/null +++ b/rc5/tests/mod.rs @@ -0,0 +1,26 @@ +#[cfg(test)] +mod tests { + use cipher::{generic_array::GenericArray, BlockDecrypt, BlockEncrypt, KeyInit}; + use rc5::RC5_32_20_16; + + #[test] + fn enc_dec_32_20_16() { + // https://www.ietf.org/archive/id/draft-krovetz-rc6-rc5-vectors-00.txt + let key = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, + ]; + let pt = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]; + let ct = [0x2A, 0x0E, 0xDC, 0x0E, 0x94, 0x31, 0xFF, 0x73]; + + let rc5 = ::new_from_slice(&key).unwrap(); + + let mut block = GenericArray::clone_from_slice(&pt); + rc5.encrypt_block(&mut block); + + assert_eq!(ct, block[..]); + + rc5.decrypt_block(&mut block); + assert_eq!(pt, block[..]); + } +} From 1b868114451f27cdb62d2a2c8dce777593702d04 Mon Sep 17 00:00:00 2001 From: Antonio Date: Wed, 23 Nov 2022 21:06:35 +0100 Subject: [PATCH 02/13] fix no std --- rc5/src/core/mod.rs | 2 +- rc5/src/core/primitives.rs | 4 ++-- rc5/src/lib.rs | 8 +------- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/rc5/src/core/mod.rs b/rc5/src/core/mod.rs index c644499f..2291cc6b 100644 --- a/rc5/src/core/mod.rs +++ b/rc5/src/core/mod.rs @@ -4,7 +4,7 @@ mod primitives; pub use primitives::*; -use std::{ +use core::{ cmp::max, convert::TryInto, ops::{Add, Div, Mul, Sub}, diff --git a/rc5/src/core/primitives.rs b/rc5/src/core/primitives.rs index 39d43f42..3b97c5fa 100644 --- a/rc5/src/core/primitives.rs +++ b/rc5/src/core/primitives.rs @@ -1,4 +1,4 @@ -use std::ops::{Add, BitXor}; +use core::ops::{Add, BitXor}; use cipher::{ generic_array::{ArrayLength, GenericArray}, @@ -66,7 +66,7 @@ impl Word for u32 { } fn from_le_bytes(bytes: &GenericArray) -> Self { - u32::from_le_bytes(bytes.to_owned().into()) + u32::from_le_bytes(bytes.as_slice().try_into().unwrap()) } fn to_le_bytes(self) -> GenericArray { diff --git a/rc5/src/lib.rs b/rc5/src/lib.rs index 8f2c3a05..b2d584b0 100644 --- a/rc5/src/lib.rs +++ b/rc5/src/lib.rs @@ -1,10 +1,4 @@ -//! Implementation of the RC5-32/12/16 block cipher. Based on the [RC5 paper]. -//! -//! ## WARNING -//! This crate implements the low-level RC5 block function. It is intended for implementing -//! higher level constructions. It is not intended for direct use in applications. -//! -//! [RC5 paper]: https://www.grc.com/r&d/rc5.pdf +#![no_std] mod block_cipher; mod core; From 530a905b1480770cbaab4c9f6df6aa695a658a3e Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 25 Nov 2022 17:21:11 +0100 Subject: [PATCH 03/13] fix typos --- rc5/README.md | 14 +++++++------- rc5/src/block_cipher.rs | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rc5/README.md b/rc5/README.md index 2db5f57b..647754e5 100644 --- a/rc5/README.md +++ b/rc5/README.md @@ -1,4 +1,4 @@ -# RustCrypto: RC2 Cipher +# RustCrypto: RC5 Cipher [![crate][crate-image]][crate-link] [![Docs][docs-image]][docs-link] @@ -53,18 +53,18 @@ dual licensed as above, without any additional terms or conditions. [//]: # (badges) -[crate-image]: https://img.shields.io/crates/v/rc2.svg -[crate-link]: https://crates.io/crates/rc2 -[docs-image]: https://docs.rs/rc2/badge.svg -[docs-link]: https://docs.rs/rc2/ +[crate-image]: https://img.shields.io/crates/v/rc5.svg +[crate-link]: https://crates.io/crates/rc5 +[docs-image]: https://docs.rs/rc5/badge.svg +[docs-link]: https://docs.rs/rc5/ [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg [rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg [hazmat-image]: https://img.shields.io/badge/crypto-hazmat%E2%9A%A0-red.svg [hazmat-link]: https://github.com/RustCrypto/meta/blob/master/HAZMAT.md [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/rc2/badge.svg?branch=master&event=push -[build-link]: https://github.com/RustCrypto/block-ciphers/actions?query=workflow%3Arc2 +[build-image]: https://github.com/RustCrypto/block-ciphers/workflows/rc5/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/block-ciphers/actions?query=workflow%3Arc5 [//]: # (general links) diff --git a/rc5/src/block_cipher.rs b/rc5/src/block_cipher.rs index d6c01b91..846bcaee 100644 --- a/rc5/src/block_cipher.rs +++ b/rc5/src/block_cipher.rs @@ -36,7 +36,7 @@ impl KeyInit for RC5_32_20_16 { impl AlgorithmName for RC5_32_20_16 { fn write_alg_name(f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.write_str("RC5-32/12/16") + f.write_str("RC5-32/20/16") } } From 6537488f8c2d58846fd4ee443a0f056328585f30 Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 2 Dec 2022 19:41:44 +0100 Subject: [PATCH 04/13] add macro for cipher impl --- rc5/src/block_cipher.rs | 100 +++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/rc5/src/block_cipher.rs b/rc5/src/block_cipher.rs index 846bcaee..ced996a0 100644 --- a/rc5/src/block_cipher.rs +++ b/rc5/src/block_cipher.rs @@ -1,64 +1,70 @@ -use cipher::consts::{U16, U20, U8}; +use cipher::consts::{U16, U20}; -use crate::core::{ExpandedKeyTable, RC5}; +use crate::core::{BlockSize, ExpandedKeyTable, RC5}; use cipher::{impl_simple_block_encdec, AlgorithmName, KeyInit}; use cipher::{inout::InOut, Block, BlockCipher, KeySizeUser}; -pub struct RC5_32_20_16 { - key_table: ExpandedKeyTable, -} +macro_rules! impl_rc5 { + (cipher: $cipher:ident, name: $name:expr, word: $word:ident, rounds: $rounds:ident, key_bytes: $key_bytes:ident) => { + pub struct $cipher { + key_table: ExpandedKeyTable<$word, $rounds>, + } -impl RC5 for RC5_32_20_16 {} + impl RC5<$word, $rounds, $key_bytes> for $cipher {} -impl RC5_32_20_16 { - fn encrypt_block(&self, block: InOut<'_, '_, Block>) { - Self::encrypt(block, &self.key_table); - } + impl $cipher { + fn encrypt_block(&self, block: InOut<'_, '_, Block>) { + Self::encrypt(block, &self.key_table); + } - fn decrypt_block(&self, block: InOut<'_, '_, Block>) { - Self::decrypt(block, &self.key_table); - } -} + fn decrypt_block(&self, block: InOut<'_, '_, Block>) { + Self::decrypt(block, &self.key_table); + } + } -impl BlockCipher for RC5_32_20_16 {} + impl BlockCipher for $cipher {} -impl KeySizeUser for RC5_32_20_16 { - type KeySize = U16; -} + impl KeySizeUser for $cipher { + type KeySize = $key_bytes; + } -impl KeyInit for RC5_32_20_16 { - fn new(key: &cipher::Key) -> Self { - Self { - key_table: Self::substitute_key(key), + impl KeyInit for $cipher { + fn new(key: &cipher::Key) -> Self { + Self { + key_table: Self::substitute_key(key), + } + } } - } -} -impl AlgorithmName for RC5_32_20_16 { - fn write_alg_name(f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.write_str("RC5-32/20/16") - } -} + impl AlgorithmName for $cipher { + fn write_alg_name(f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str(stringify!($name)) + } + } -impl_simple_block_encdec!( - RC5_32_20_16, U8, cipher, block, - encrypt: { - cipher.encrypt_block(block); - } - decrypt: { - cipher.decrypt_block(block); - } -); + impl_simple_block_encdec!( + $cipher, BlockSize<$word>, cipher, block, + encrypt: { + cipher.encrypt_block(block); + } + decrypt: { + cipher.decrypt_block(block); + } + ); -#[cfg(feature = "zeroize")] -use cipher::zeroize::Zeroize; + #[cfg(feature = "zeroize")] + use cipher::zeroize::Zeroize; -#[cfg(feature = "zeroize")] -impl cipher::zeroize::ZeroizeOnDrop for RC5_32_20_16 {} + #[cfg(feature = "zeroize")] + impl cipher::zeroize::ZeroizeOnDrop for $cipher {} -#[cfg(feature = "zeroize")] -impl Drop for RC5_32_20_16 { - fn drop(&mut self) { - self.key_table.zeroize() - } + #[cfg(feature = "zeroize")] + impl Drop for $cipher { + fn drop(&mut self) { + self.key_table.zeroize() + } + } + }; } + +impl_rc5!(cipher: RC5_32_20_16, name: "RC5 - 32/16/16", word: u32, rounds: U20, key_bytes: U16); From 02579f4283864fa8362857b6f70e27d309ec9f5a Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 2 Dec 2022 20:10:55 +0100 Subject: [PATCH 05/13] add common u32 impls --- rc5/src/block_cipher.rs | 5 +++-- rc5/tests/mod.rs | 32 +++++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/rc5/src/block_cipher.rs b/rc5/src/block_cipher.rs index ced996a0..6c1e97e1 100644 --- a/rc5/src/block_cipher.rs +++ b/rc5/src/block_cipher.rs @@ -1,4 +1,4 @@ -use cipher::consts::{U16, U20}; +use cipher::consts::{U12, U16}; use crate::core::{BlockSize, ExpandedKeyTable, RC5}; use cipher::{impl_simple_block_encdec, AlgorithmName, KeyInit}; @@ -67,4 +67,5 @@ macro_rules! impl_rc5 { }; } -impl_rc5!(cipher: RC5_32_20_16, name: "RC5 - 32/16/16", word: u32, rounds: U20, key_bytes: U16); +impl_rc5!(cipher: RC5_32_12_16, name: "RC5 - 32/12/16", word: u32, rounds: U12, key_bytes: U16); +impl_rc5!(cipher: RC5_32_16_16, name: "RC5 - 32/16/16", word: u32, rounds: U16, key_bytes: U16); diff --git a/rc5/tests/mod.rs b/rc5/tests/mod.rs index ac963ce9..39e0d64d 100644 --- a/rc5/tests/mod.rs +++ b/rc5/tests/mod.rs @@ -1,19 +1,41 @@ +/// generated using the code in: https://www.ietf.org/archive/id/draft-krovetz-rc6-rc5-vectors-00.txt #[cfg(test)] mod tests { use cipher::{generic_array::GenericArray, BlockDecrypt, BlockEncrypt, KeyInit}; - use rc5::RC5_32_20_16; + use rc5::{RC5_32_12_16, RC5_32_16_16}; #[test] - fn enc_dec_32_20_16() { - // https://www.ietf.org/archive/id/draft-krovetz-rc6-rc5-vectors-00.txt + fn enc_dec_32_12_16() { let key = [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, ]; + + let pt = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]; + let ct = [0xC8, 0xD3, 0xB3, 0xC4, 0x86, 0x70, 0x0C, 0xFA]; + + let rc5 = ::new_from_slice(&key).unwrap(); + + let mut block = GenericArray::clone_from_slice(&pt); + rc5.encrypt_block(&mut block); + + assert_eq!(ct, block[..]); + + rc5.decrypt_block(&mut block); + assert_eq!(pt, block[..]); + } + + #[test] + fn enc_dec_32_16_16() { + let key = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, + ]; + let pt = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]; - let ct = [0x2A, 0x0E, 0xDC, 0x0E, 0x94, 0x31, 0xFF, 0x73]; + let ct = [0x3E, 0x2E, 0x95, 0x35, 0x70, 0x27, 0xD8, 0x96]; - let rc5 = ::new_from_slice(&key).unwrap(); + let rc5 = ::new_from_slice(&key).unwrap(); let mut block = GenericArray::clone_from_slice(&pt); rc5.encrypt_block(&mut block); From 2c9c474bb6ba578c87ce9ac0fd4c12106802fe34 Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 2 Dec 2022 20:24:02 +0100 Subject: [PATCH 06/13] fix macro imports --- rc5/src/block_cipher.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rc5/src/block_cipher.rs b/rc5/src/block_cipher.rs index 6c1e97e1..a0350df2 100644 --- a/rc5/src/block_cipher.rs +++ b/rc5/src/block_cipher.rs @@ -52,16 +52,13 @@ macro_rules! impl_rc5 { } ); - #[cfg(feature = "zeroize")] - use cipher::zeroize::Zeroize; - #[cfg(feature = "zeroize")] impl cipher::zeroize::ZeroizeOnDrop for $cipher {} #[cfg(feature = "zeroize")] impl Drop for $cipher { fn drop(&mut self) { - self.key_table.zeroize() + cipher::zeroize::Zeroize::zeroize(&mut *self.key_table) } } }; From 91d650742ce474bb04638dcf435f009c76f87b86 Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 2 Dec 2022 20:28:47 +0100 Subject: [PATCH 07/13] fix benchmarks --- rc5/benches/mod.rs | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/rc5/benches/mod.rs b/rc5/benches/mod.rs index 2f4907dd..538db60b 100644 --- a/rc5/benches/mod.rs +++ b/rc5/benches/mod.rs @@ -2,7 +2,26 @@ extern crate test; use cipher::{block_decryptor_bench, block_encryptor_bench}; -use rc5::RC5_32_20_16; +use rc5::{RC5_32_12_16, RC5_32_16_16}; -block_encryptor_bench!(Key: RC5_32_20_16, rc5_encrypt_block, rc5_encrypt_blocks,); -block_decryptor_bench!(Key: RC5_32_20_16, rc5_decrypt_block, rc5_decrypt_blocks,); +block_encryptor_bench!( + Key: RC5_32_12_16, + rc5_32_12_16_encrypt_block, + rc5_32_12_16_encrypt_blocks, +); +block_decryptor_bench!( + Key: RC5_32_12_16, + rc5_32_12_16_decrypt_block, + rc5_32_12_16_decrypt_blocks, +); + +block_encryptor_bench!( + Key: RC5_32_16_16, + rc5_32_16_16_encrypt_block, + rc5_32_16_16_encrypt_blocks, +); +block_decryptor_bench!( + Key: RC5_32_16_16, + rc5_32_16_16_decrypt_block, + rc5_32_16_16_decrypt_blocks, +); From 294ad4d8dae2b46e064b31e5303dc8c5ce3a2afb Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 2 Dec 2022 23:04:55 +0100 Subject: [PATCH 08/13] add range bounds on rounds and key --- rc5/src/core/mod.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/rc5/src/core/mod.rs b/rc5/src/core/mod.rs index 2291cc6b..004eef4a 100644 --- a/rc5/src/core/mod.rs +++ b/rc5/src/core/mod.rs @@ -13,25 +13,28 @@ use core::{ use cipher::{ generic_array::{sequence::GenericSequence, ArrayLength, GenericArray}, inout::InOut, - typenum::{Diff, Sum, Unsigned, U1, U2}, + typenum::{Diff, IsLess, Le, NonZero, Sum, Unsigned, U1, U2, U256}, }; pub trait RC5 where - // u16 or u32 or u64 W: Word, - // Block size is 2 * W::Bytes + // Block size W::Bytes: Mul, BlockSize: ArrayLength, - // Rounds are an uint in the range 0-255 + // Rounds range R: Unsigned, - // expanded key table size = (R + 1) * 2 + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize R: Add, Sum: Mul, ExpandedKeyTableSize: ArrayLength, - // key size is an uint in the range 0-255 + // Key range B: ArrayLength, - // key as words size = div_ceil(B, W::Bytes) + B: IsLess, + Le: NonZero, + // KeyAsWordsSize B: Add, Sum: Sub, Diff, U1>: Div, From 5b84ad4029deb510ff2979dba50a24bebc1ec910 Mon Sep 17 00:00:00 2001 From: Antonio Date: Sat, 3 Dec 2022 00:44:27 +0100 Subject: [PATCH 09/13] generic rc5 --- rc5/src/block_cipher.rs | 483 ++++++++++++++++++++++++++++++++----- rc5/src/core/primitives.rs | 4 +- 2 files changed, 430 insertions(+), 57 deletions(-) diff --git a/rc5/src/block_cipher.rs b/rc5/src/block_cipher.rs index a0350df2..a4964097 100644 --- a/rc5/src/block_cipher.rs +++ b/rc5/src/block_cipher.rs @@ -1,68 +1,441 @@ -use cipher::consts::{U12, U16}; +use core::{ + marker::PhantomData, + ops::{Add, Div, Mul, Sub}, +}; -use crate::core::{BlockSize, ExpandedKeyTable, RC5}; -use cipher::{impl_simple_block_encdec, AlgorithmName, KeyInit}; -use cipher::{inout::InOut, Block, BlockCipher, KeySizeUser}; +use cipher::{ + generic_array::ArrayLength, + inout::InOut, + typenum::{Diff, IsLess, Le, NonZero, Sum, Unsigned, U1, U12, U16, U2, U256}, + AlgorithmName, Block, BlockBackend, BlockCipher, BlockDecrypt, BlockEncrypt, BlockSizeUser, + KeyInit, KeySizeUser, ParBlocksSizeUser, +}; -macro_rules! impl_rc5 { - (cipher: $cipher:ident, name: $name:expr, word: $word:ident, rounds: $rounds:ident, key_bytes: $key_bytes:ident) => { - pub struct $cipher { - key_table: ExpandedKeyTable<$word, $rounds>, - } +use crate::core::{ + BlockSize, ExpandedKeyTable, ExpandedKeyTableSize, KeyAsWordsSize, Word, RC5 as RC5Trait, +}; - impl RC5<$word, $rounds, $key_bytes> for $cipher {} +pub struct RC5 +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, +{ + key_table: ExpandedKeyTable, + _key_size: PhantomData, +} - impl $cipher { - fn encrypt_block(&self, block: InOut<'_, '_, Block>) { - Self::encrypt(block, &self.key_table); - } +impl RC5Trait for RC5 +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, + // Key range + B: ArrayLength, + B: IsLess, + Le: NonZero, + // KeyAsWordsSize + B: Add, + Sum: Sub, + Diff, U1>: Div, + KeyAsWordsSize: ArrayLength, +{ +} - fn decrypt_block(&self, block: InOut<'_, '_, Block>) { - Self::decrypt(block, &self.key_table); - } +impl KeyInit for RC5 +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, + // Key range + B: ArrayLength, + B: IsLess, + Le: NonZero, + // KeyAsWordsSize + B: Add, + Sum: Sub, + Diff, U1>: Div, + KeyAsWordsSize: ArrayLength, +{ + fn new(key: &cipher::Key) -> Self { + Self { + key_table: Self::substitute_key(key), + _key_size: PhantomData, } + } +} - impl BlockCipher for $cipher {} +impl KeySizeUser for RC5 +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, + B: ArrayLength, +{ + type KeySize = B; +} - impl KeySizeUser for $cipher { - type KeySize = $key_bytes; - } +impl BlockCipher for RC5 +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, +{ +} - impl KeyInit for $cipher { - fn new(key: &cipher::Key) -> Self { - Self { - key_table: Self::substitute_key(key), - } - } - } +impl BlockSizeUser for RC5 +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, +{ + type BlockSize = BlockSize; +} - impl AlgorithmName for $cipher { - fn write_alg_name(f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.write_str(stringify!($name)) - } - } +impl BlockEncrypt for RC5 +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, + // Key range + B: ArrayLength, + B: IsLess, + Le: NonZero, + // KeyAsWordsSize + B: Add, + Sum: Sub, + Diff, U1>: Div, + KeyAsWordsSize: ArrayLength, +{ + fn encrypt_with_backend(&self, f: impl cipher::BlockClosure) { + f.call(&mut RC5EncryptBackend { enc_dec: self }) + } +} - impl_simple_block_encdec!( - $cipher, BlockSize<$word>, cipher, block, - encrypt: { - cipher.encrypt_block(block); - } - decrypt: { - cipher.decrypt_block(block); - } - ); - - #[cfg(feature = "zeroize")] - impl cipher::zeroize::ZeroizeOnDrop for $cipher {} - - #[cfg(feature = "zeroize")] - impl Drop for $cipher { - fn drop(&mut self) { - cipher::zeroize::Zeroize::zeroize(&mut *self.key_table) - } - } - }; +struct RC5EncryptBackend<'a, W, R, B> +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, +{ + enc_dec: &'a RC5, +} +impl<'a, W, R, B> BlockSizeUser for RC5EncryptBackend<'a, W, R, B> +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, +{ + type BlockSize = BlockSize; +} + +impl<'a, W, R, B> ParBlocksSizeUser for RC5EncryptBackend<'a, W, R, B> +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, +{ + type ParBlocksSize = U1; +} + +impl<'a, W, R, B> BlockBackend for RC5EncryptBackend<'a, W, R, B> +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, + // Key range + B: ArrayLength, + B: IsLess, + Le: NonZero, + // KeyAsWordsSize + B: Add, + Sum: Sub, + Diff, U1>: Div, + KeyAsWordsSize: ArrayLength, +{ + #[inline(always)] + fn proc_block(&mut self, block: InOut<'_, '_, Block>) { + let backend = self.enc_dec; + RC5::::encrypt(block, &backend.key_table) + } +} + +impl BlockDecrypt for RC5 +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, + // Key range + B: ArrayLength, + B: IsLess, + Le: NonZero, + // KeyAsWordsSize + B: Add, + Sum: Sub, + Diff, U1>: Div, + KeyAsWordsSize: ArrayLength, +{ + fn decrypt_with_backend(&self, f: impl cipher::BlockClosure) { + f.call(&mut RC5DecryptBackend { enc_dec: self }) + } +} + +struct RC5DecryptBackend<'a, W, R, B> +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, +{ + enc_dec: &'a RC5, +} +impl<'a, W, R, B> BlockSizeUser for RC5DecryptBackend<'a, W, R, B> +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, +{ + type BlockSize = BlockSize; +} + +impl<'a, W, R, B> ParBlocksSizeUser for RC5DecryptBackend<'a, W, R, B> +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, +{ + type ParBlocksSize = U1; +} + +impl<'a, W, R, B> BlockBackend for RC5DecryptBackend<'a, W, R, B> +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, + // Key range + B: ArrayLength, + B: IsLess, + Le: NonZero, + // KeyAsWordsSize + B: Add, + Sum: Sub, + Diff, U1>: Div, + KeyAsWordsSize: ArrayLength, +{ + #[inline(always)] + fn proc_block(&mut self, block: InOut<'_, '_, Block>) { + let backend = self.enc_dec; + RC5::::decrypt(block, &backend.key_table) + } +} + +impl AlgorithmName for RC5 +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, +{ + fn write_alg_name(f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "RC5 - {}/{}/{}", + core::any::type_name::(), + ::to_u8(), + ::to_u8(), + ) + } +} + +#[cfg(feature = "zeroize")] +impl cipher::zeroize::ZeroizeOnDrop for RC5 +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, +{ +} + +#[cfg(feature = "zeroize")] +impl Drop for RC5 +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, +{ + fn drop(&mut self) { + cipher::zeroize::Zeroize::zeroize(&mut *self.key_table) + } } -impl_rc5!(cipher: RC5_32_12_16, name: "RC5 - 32/12/16", word: u32, rounds: U12, key_bytes: U16); -impl_rc5!(cipher: RC5_32_16_16, name: "RC5 - 32/16/16", word: u32, rounds: U16, key_bytes: U16); +pub type RC5_32_12_16 = RC5; +pub type RC5_32_16_16 = RC5; diff --git a/rc5/src/core/primitives.rs b/rc5/src/core/primitives.rs index 3b97c5fa..0812e8d1 100644 --- a/rc5/src/core/primitives.rs +++ b/rc5/src/core/primitives.rs @@ -3,7 +3,7 @@ use core::ops::{Add, BitXor}; use cipher::{ generic_array::{ArrayLength, GenericArray}, typenum::{Diff, Prod, Quot, Sum, U1, U2, U4}, - zeroize::Zeroize, + zeroize::DefaultIsZeroes, }; pub type BlockSize = Prod<::Bytes, U2>; @@ -17,7 +17,7 @@ pub type ExpandedKeyTableSize = Prod, U2>; pub type KeyAsWords = GenericArray>; pub type KeyAsWordsSize = Quot::Bytes>, U1>, ::Bytes>; -pub trait Word: Default + Copy + From + Add + Zeroize { +pub trait Word: Default + Copy + From + Add + DefaultIsZeroes + Default { type Bytes: ArrayLength; const ZERO: Self; From 649f26a763e81fa65dd39bd1a0d7921f45b73cbc Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 5 Dec 2022 21:19:04 +0100 Subject: [PATCH 10/13] inline Word fn after 5b84ad4: generic rc5 pefromance regression was noticed. after analizing the generated asm I noticed that while the generic top level generic impl does get inlined the actual algorithm impl differs in the fact that it does not inline Word functions. After adding inline annotations performance is once again compareable. --- rc5/src/core/primitives.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/rc5/src/core/primitives.rs b/rc5/src/core/primitives.rs index 0812e8d1..5e3e80b8 100644 --- a/rc5/src/core/primitives.rs +++ b/rc5/src/core/primitives.rs @@ -49,30 +49,36 @@ impl Word for u32 { const P: Self = 0xb7e15163; const Q: Self = 0x9e3779b9; + #[inline(always)] fn wrapping_add(self, rhs: Self) -> Self { u32::wrapping_add(self, rhs) } - + #[inline(always)] fn wrapping_sub(self, rhs: Self) -> Self { u32::wrapping_sub(self, rhs) } + #[inline(always)] fn rotate_left(self, n: Self) -> Self { u32::rotate_left(self, n) } + #[inline(always)] fn rotate_right(self, n: Self) -> Self { u32::rotate_right(self, n) } + #[inline(always)] fn from_le_bytes(bytes: &GenericArray) -> Self { u32::from_le_bytes(bytes.as_slice().try_into().unwrap()) } + #[inline(always)] fn to_le_bytes(self) -> GenericArray { u32::to_le_bytes(self).into() } + #[inline(always)] fn bitxor(self, other: Self) -> Self { ::bitxor(self, other) } From cd7cd32485a5c6b8229901fd2202589c6ec217bb Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 5 Dec 2022 22:18:56 +0100 Subject: [PATCH 11/13] clean up code organization --- rc5/src/block_cipher.rs | 102 +----------------- rc5/src/core/backend.rs | 229 ++++++++++++++++++++++++++++++++++++++++ rc5/src/core/mod.rs | 145 +------------------------ 3 files changed, 237 insertions(+), 239 deletions(-) create mode 100644 rc5/src/core/backend.rs diff --git a/rc5/src/block_cipher.rs b/rc5/src/block_cipher.rs index a4964097..5a723d88 100644 --- a/rc5/src/block_cipher.rs +++ b/rc5/src/block_cipher.rs @@ -1,7 +1,4 @@ -use core::{ - marker::PhantomData, - ops::{Add, Div, Mul, Sub}, -}; +use core::ops::{Add, Div, Mul, Sub}; use cipher::{ generic_array::ArrayLength, @@ -11,54 +8,7 @@ use cipher::{ KeyInit, KeySizeUser, ParBlocksSizeUser, }; -use crate::core::{ - BlockSize, ExpandedKeyTable, ExpandedKeyTableSize, KeyAsWordsSize, Word, RC5 as RC5Trait, -}; - -pub struct RC5 -where - W: Word, - // Block size - W::Bytes: Mul, - BlockSize: ArrayLength, - // Rounds range - R: Unsigned, - R: IsLess, - Le: NonZero, - // ExpandedKeyTableSize - R: Add, - Sum: Mul, - ExpandedKeyTableSize: ArrayLength, -{ - key_table: ExpandedKeyTable, - _key_size: PhantomData, -} - -impl RC5Trait for RC5 -where - W: Word, - // Block size - W::Bytes: Mul, - BlockSize: ArrayLength, - // Rounds range - R: Unsigned, - R: IsLess, - Le: NonZero, - // ExpandedKeyTableSize - R: Add, - Sum: Mul, - ExpandedKeyTableSize: ArrayLength, - // Key range - B: ArrayLength, - B: IsLess, - Le: NonZero, - // KeyAsWordsSize - B: Add, - Sum: Sub, - Diff, U1>: Div, - KeyAsWordsSize: ArrayLength, -{ -} +use crate::core::{BlockSize, ExpandedKeyTableSize, KeyAsWordsSize, Word, RC5}; impl KeyInit for RC5 where @@ -85,10 +35,7 @@ where KeyAsWordsSize: ArrayLength, { fn new(key: &cipher::Key) -> Self { - Self { - key_table: Self::substitute_key(key), - _key_size: PhantomData, - } + Self::new(key) } } @@ -255,7 +202,7 @@ where #[inline(always)] fn proc_block(&mut self, block: InOut<'_, '_, Block>) { let backend = self.enc_dec; - RC5::::encrypt(block, &backend.key_table) + backend.encrypt(block) } } @@ -368,7 +315,7 @@ where #[inline(always)] fn proc_block(&mut self, block: InOut<'_, '_, Block>) { let backend = self.enc_dec; - RC5::::decrypt(block, &backend.key_table) + backend.decrypt(block) } } @@ -398,44 +345,5 @@ where } } -#[cfg(feature = "zeroize")] -impl cipher::zeroize::ZeroizeOnDrop for RC5 -where - W: Word, - // Block size - W::Bytes: Mul, - BlockSize: ArrayLength, - // Rounds range - R: Unsigned, - R: IsLess, - Le: NonZero, - // ExpandedKeyTableSize - R: Add, - Sum: Mul, - ExpandedKeyTableSize: ArrayLength, -{ -} - -#[cfg(feature = "zeroize")] -impl Drop for RC5 -where - W: Word, - // Block size - W::Bytes: Mul, - BlockSize: ArrayLength, - // Rounds range - R: Unsigned, - R: IsLess, - Le: NonZero, - // ExpandedKeyTableSize - R: Add, - Sum: Mul, - ExpandedKeyTableSize: ArrayLength, -{ - fn drop(&mut self) { - cipher::zeroize::Zeroize::zeroize(&mut *self.key_table) - } -} - pub type RC5_32_12_16 = RC5; pub type RC5_32_16_16 = RC5; diff --git a/rc5/src/core/backend.rs b/rc5/src/core/backend.rs new file mode 100644 index 00000000..dea60da1 --- /dev/null +++ b/rc5/src/core/backend.rs @@ -0,0 +1,229 @@ +use core::{ + cmp::max, + marker::PhantomData, + ops::{Add, Div, Mul, Sub}, +}; + +use cipher::{ + generic_array::{sequence::GenericSequence, ArrayLength, GenericArray}, + inout::InOut, + typenum::{Diff, IsLess, Le, NonZero, Sum, U1, U2}, + typenum::{Unsigned, U256}, +}; + +use super::{ + Block, BlockSize, ExpandedKeyTable, ExpandedKeyTableSize, Key, KeyAsWords, KeyAsWordsSize, Word, +}; + +pub struct RC5 +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, +{ + key_table: ExpandedKeyTable, + _key_size: PhantomData, +} + +impl RC5 +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, + // Key range + B: ArrayLength, + B: IsLess, + Le: NonZero, + // KeyAsWordsSize + B: Add, + Sum: Sub, + Diff, U1>: Div, + KeyAsWordsSize: ArrayLength, +{ + pub fn new(key: &Key) -> RC5 { + Self { + key_table: Self::substitute_key(key), + _key_size: PhantomData, + } + } + + fn substitute_key(key: &Key) -> ExpandedKeyTable { + let key_as_words = Self::key_into_words(key); + let expanded_key_table = Self::initialize_expanded_key_table(); + + Self::mix_in(expanded_key_table, key_as_words) + } + + fn key_into_words(key: &Key) -> KeyAsWords { + // can be uninitialized + let mut key_as_words: GenericArray> = GenericArray::default(); + + for i in (0..B::USIZE).rev() { + key_as_words[i / W::Bytes::USIZE] = + key_as_words[i / W::Bytes::USIZE].rotate_left(W::EIGHT) + key[i].into(); + // no need for wrapping addition since we are adding a byte sized uint onto an uint with its lsb byte zeroed + } + + key_as_words + } + + fn initialize_expanded_key_table() -> ExpandedKeyTable { + // must be zero initialized + let mut expanded_key_table: GenericArray> = + GenericArray::generate(|_| W::ZERO); + + expanded_key_table[0] = W::P; + for i in 1..expanded_key_table.len() { + expanded_key_table[i] = expanded_key_table[i - 1].wrapping_add(W::Q); + } + + expanded_key_table + } + + fn mix_in( + mut key_table: ExpandedKeyTable, + mut key_as_words: KeyAsWords, + ) -> ExpandedKeyTable { + let (mut expanded_key_index, mut key_as_words_index) = (0, 0); + let (mut a, mut b) = (W::ZERO, W::ZERO); + + for _ in 0..3 * max(key_as_words.len(), key_table.len()) { + key_table[expanded_key_index] = key_table[expanded_key_index] + .wrapping_add(a) + .wrapping_add(b) + .rotate_left(W::THREE); + + a = key_table[expanded_key_index]; + + key_as_words[key_as_words_index] = key_as_words[key_as_words_index] + .wrapping_add(a) + .wrapping_add(b) + .rotate_left(a.wrapping_add(b)); + + b = key_as_words[key_as_words_index]; + + expanded_key_index = (expanded_key_index + 1) % key_table.len(); + key_as_words_index = (key_as_words_index + 1) % key_as_words.len(); + } + + key_table + } +} + +impl RC5 +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, +{ + pub fn encrypt(&self, mut block: InOut<'_, '_, Block>) { + let (mut a, mut b) = Self::words_from_block(block.get_in()); + let key = &self.key_table; + + a = a.wrapping_add(key[0]); + b = b.wrapping_add(key[1]); + + for i in 1..=R::USIZE { + a = a.bitxor(b).rotate_left(b).wrapping_add(key[2 * i]); + b = b.bitxor(a).rotate_left(a).wrapping_add(key[2 * i + 1]); + } + + Self::block_from_words(a, b, block.get_out()) + } + + pub fn decrypt(&self, mut block: InOut<'_, '_, Block>) { + let (mut a, mut b) = Self::words_from_block(block.get_in()); + let key = &self.key_table; + + for i in (1..=R::USIZE).rev() { + b = b.wrapping_sub(key[2 * i + 1]).rotate_right(a).bitxor(a); + a = a.wrapping_sub(key[2 * i]).rotate_right(b).bitxor(b); + } + + b = b.wrapping_sub(key[1]); + a = a.wrapping_sub(key[0]); + + Self::block_from_words(a, b, block.get_out()) + } + + fn words_from_block(block: &Block) -> (W, W) { + // Block size is 2 * word::BYTES so the unwrap is safe + let a = W::from_le_bytes(block[..W::Bytes::USIZE].try_into().unwrap()); + let b = W::from_le_bytes(block[W::Bytes::USIZE..].try_into().unwrap()); + + (a, b) + } + + fn block_from_words(a: W, b: W, out_block: &mut Block) { + let (left, right) = out_block.split_at_mut(W::Bytes::USIZE); + + left.copy_from_slice(&a.to_le_bytes()); + right.copy_from_slice(&b.to_le_bytes()); + } +} + +#[cfg(feature = "zeroize")] +impl cipher::zeroize::ZeroizeOnDrop for RC5 +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, +{ +} + +#[cfg(feature = "zeroize")] +impl Drop for RC5 +where + W: Word, + // Block size + W::Bytes: Mul, + BlockSize: ArrayLength, + // Rounds range + R: Unsigned, + R: IsLess, + Le: NonZero, + // ExpandedKeyTableSize + R: Add, + Sum: Mul, + ExpandedKeyTableSize: ArrayLength, +{ + fn drop(&mut self) { + cipher::zeroize::Zeroize::zeroize(&mut *self.key_table) + } +} diff --git a/rc5/src/core/mod.rs b/rc5/src/core/mod.rs index 004eef4a..770a1033 100644 --- a/rc5/src/core/mod.rs +++ b/rc5/src/core/mod.rs @@ -1,147 +1,8 @@ //! Implementation according to the [RC5 paper] //! [RC5 paper]: https://www.grc.com/r&d/rc5.pdf +mod backend; mod primitives; -pub use primitives::*; - -use core::{ - cmp::max, - convert::TryInto, - ops::{Add, Div, Mul, Sub}, -}; - -use cipher::{ - generic_array::{sequence::GenericSequence, ArrayLength, GenericArray}, - inout::InOut, - typenum::{Diff, IsLess, Le, NonZero, Sum, Unsigned, U1, U2, U256}, -}; - -pub trait RC5 -where - W: Word, - // Block size - W::Bytes: Mul, - BlockSize: ArrayLength, - // Rounds range - R: Unsigned, - R: IsLess, - Le: NonZero, - // ExpandedKeyTableSize - R: Add, - Sum: Mul, - ExpandedKeyTableSize: ArrayLength, - // Key range - B: ArrayLength, - B: IsLess, - Le: NonZero, - // KeyAsWordsSize - B: Add, - Sum: Sub, - Diff, U1>: Div, - KeyAsWordsSize: ArrayLength, -{ - fn encrypt(mut block: InOut<'_, '_, Block>, key: &ExpandedKeyTable) { - let (mut a, mut b) = Self::words_from_block(block.get_in()); - - a = a.wrapping_add(key[0]); - b = b.wrapping_add(key[1]); - - for i in 1..=R::USIZE { - a = a.bitxor(b).rotate_left(b).wrapping_add(key[2 * i]); - b = b.bitxor(a).rotate_left(a).wrapping_add(key[2 * i + 1]); - } - - Self::block_from_words(a, b, block.get_out()) - } - - fn decrypt(mut block: InOut<'_, '_, Block>, key: &ExpandedKeyTable) { - let (mut a, mut b) = Self::words_from_block(block.get_in()); - - for i in (1..=R::USIZE).rev() { - b = b.wrapping_sub(key[2 * i + 1]).rotate_right(a).bitxor(a); - a = a.wrapping_sub(key[2 * i]).rotate_right(b).bitxor(b); - } - - b = b.wrapping_sub(key[1]); - a = a.wrapping_sub(key[0]); - - Self::block_from_words(a, b, block.get_out()) - } - - fn substitute_key(key: &Key) -> ExpandedKeyTable { - let key_as_words = Self::key_into_words(key); - let expanded_key_table = Self::initialize_expanded_key_table(); - - Self::mix_in(expanded_key_table, key_as_words) - } - - fn words_from_block(block: &Block) -> (W, W) { - // Block size is 2 * word::BYTES so the unwrap is safe - let a = W::from_le_bytes(block[..W::Bytes::USIZE].try_into().unwrap()); - let b = W::from_le_bytes(block[W::Bytes::USIZE..].try_into().unwrap()); - - (a, b) - } - fn block_from_words(a: W, b: W, out_block: &mut Block) { - let (left, right) = out_block.split_at_mut(W::Bytes::USIZE); - - left.copy_from_slice(&a.to_le_bytes()); - right.copy_from_slice(&b.to_le_bytes()); - } - - fn key_into_words(key: &Key) -> KeyAsWords { - // can be uninitialized - let mut key_as_words: GenericArray> = GenericArray::default(); - - for i in (0..B::USIZE).rev() { - key_as_words[i / W::Bytes::USIZE] = - key_as_words[i / W::Bytes::USIZE].rotate_left(W::EIGHT) + key[i].into(); - // no need for wrapping addition since we are adding a byte sized uint onto an uint with its lsb byte zeroed - } - - key_as_words - } - - fn initialize_expanded_key_table() -> ExpandedKeyTable { - // must be zero initialized - let mut expanded_key_table: GenericArray> = - GenericArray::generate(|_| W::ZERO); - - expanded_key_table[0] = W::P; - for i in 1..expanded_key_table.len() { - expanded_key_table[i] = expanded_key_table[i - 1].wrapping_add(W::Q); - } - - expanded_key_table - } - - fn mix_in( - mut key_table: ExpandedKeyTable, - mut key_as_words: KeyAsWords, - ) -> ExpandedKeyTable { - let (mut expanded_key_index, mut key_as_words_index) = (0, 0); - let (mut a, mut b) = (W::ZERO, W::ZERO); - - for _ in 0..3 * max(key_as_words.len(), key_table.len()) { - key_table[expanded_key_index] = key_table[expanded_key_index] - .wrapping_add(a) - .wrapping_add(b) - .rotate_left(W::THREE); - - a = key_table[expanded_key_index]; - - key_as_words[key_as_words_index] = key_as_words[key_as_words_index] - .wrapping_add(a) - .wrapping_add(b) - .rotate_left(a.wrapping_add(b)); - - b = key_as_words[key_as_words_index]; - - expanded_key_index = (expanded_key_index + 1) % key_table.len(); - key_as_words_index = (key_as_words_index + 1) % key_as_words.len(); - } - - key_table - } -} +pub use backend::RC5; +pub use primitives::*; From 345590b1ddcec6eb140e20e6eb5abf2ff5c1e577 Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 5 Dec 2022 22:26:58 +0100 Subject: [PATCH 12/13] seal Word --- rc5/src/core/primitives.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/rc5/src/core/primitives.rs b/rc5/src/core/primitives.rs index 5e3e80b8..0b90d795 100644 --- a/rc5/src/core/primitives.rs +++ b/rc5/src/core/primitives.rs @@ -17,7 +17,9 @@ pub type ExpandedKeyTableSize = Prod, U2>; pub type KeyAsWords = GenericArray>; pub type KeyAsWordsSize = Quot::Bytes>, U1>, ::Bytes>; -pub trait Word: Default + Copy + From + Add + DefaultIsZeroes + Default { +pub trait Word: + Default + Copy + From + Add + DefaultIsZeroes + Default + private::Sealed +{ type Bytes: ArrayLength; const ZERO: Self; @@ -39,6 +41,12 @@ pub trait Word: Default + Copy + From + Add + DefaultIsZeroes fn bitxor(self, other: Self) -> Self; } +mod private { + pub trait Sealed {} + + impl Sealed for u32 {} +} + impl Word for u32 { type Bytes = U4; From 65edb3d6033893aee3c45f4bf5f97f017faacb49 Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 5 Dec 2022 23:02:04 +0100 Subject: [PATCH 13/13] add u16 and u64 word impls --- rc5/src/block_cipher.rs | 4 +- rc5/src/core/primitives.rs | 96 +++++++++++++++++++++++++++++++++++++- rc5/tests/mod.rs | 47 ++++++++++++++++++- 3 files changed, 144 insertions(+), 3 deletions(-) diff --git a/rc5/src/block_cipher.rs b/rc5/src/block_cipher.rs index 5a723d88..6bde8f40 100644 --- a/rc5/src/block_cipher.rs +++ b/rc5/src/block_cipher.rs @@ -3,7 +3,7 @@ use core::ops::{Add, Div, Mul, Sub}; use cipher::{ generic_array::ArrayLength, inout::InOut, - typenum::{Diff, IsLess, Le, NonZero, Sum, Unsigned, U1, U12, U16, U2, U256}, + typenum::{Diff, IsLess, Le, NonZero, Sum, Unsigned, U1, U12, U16, U2, U24, U256, U8}, AlgorithmName, Block, BlockBackend, BlockCipher, BlockDecrypt, BlockEncrypt, BlockSizeUser, KeyInit, KeySizeUser, ParBlocksSizeUser, }; @@ -347,3 +347,5 @@ where pub type RC5_32_12_16 = RC5; pub type RC5_32_16_16 = RC5; +pub type RC5_16_16_8 = RC5; +pub type RC5_64_24_24 = RC5; diff --git a/rc5/src/core/primitives.rs b/rc5/src/core/primitives.rs index 0b90d795..1f12cb33 100644 --- a/rc5/src/core/primitives.rs +++ b/rc5/src/core/primitives.rs @@ -2,7 +2,7 @@ use core::ops::{Add, BitXor}; use cipher::{ generic_array::{ArrayLength, GenericArray}, - typenum::{Diff, Prod, Quot, Sum, U1, U2, U4}, + typenum::{Diff, Prod, Quot, Sum, U1, U2, U4, U8}, zeroize::DefaultIsZeroes, }; @@ -45,6 +45,8 @@ mod private { pub trait Sealed {} impl Sealed for u32 {} + impl Sealed for u16 {} + impl Sealed for u64 {} } impl Word for u32 { @@ -91,3 +93,95 @@ impl Word for u32 { ::bitxor(self, other) } } + +impl Word for u16 { + type Bytes = U2; + + const ZERO: Self = 0; + const THREE: Self = 3; + const EIGHT: Self = 8; + + const P: Self = 0xb7e1; + const Q: Self = 0x9e37; + + #[inline(always)] + fn wrapping_add(self, rhs: Self) -> Self { + u16::wrapping_add(self, rhs) + } + #[inline(always)] + fn wrapping_sub(self, rhs: Self) -> Self { + u16::wrapping_sub(self, rhs) + } + + #[inline(always)] + fn rotate_left(self, n: Self) -> Self { + u16::rotate_left(self, n as u32) + } + + #[inline(always)] + fn rotate_right(self, n: Self) -> Self { + u16::rotate_right(self, n as u32) + } + + #[inline(always)] + fn from_le_bytes(bytes: &GenericArray) -> Self { + u16::from_le_bytes(bytes.as_slice().try_into().unwrap()) + } + + #[inline(always)] + fn to_le_bytes(self) -> GenericArray { + u16::to_le_bytes(self).into() + } + + #[inline(always)] + fn bitxor(self, other: Self) -> Self { + ::bitxor(self, other) + } +} + +impl Word for u64 { + type Bytes = U8; + + const ZERO: Self = 0; + const THREE: Self = 3; + const EIGHT: Self = 8; + + const P: Self = 0xb7e151628aed2a6b; + const Q: Self = 0x9e3779b97f4a7c15; + + #[inline(always)] + fn wrapping_add(self, rhs: Self) -> Self { + u64::wrapping_add(self, rhs) + } + #[inline(always)] + fn wrapping_sub(self, rhs: Self) -> Self { + u64::wrapping_sub(self, rhs) + } + + #[inline(always)] + fn rotate_left(self, n: Self) -> Self { + let size = Self::BITS; + u64::rotate_left(self, (n % size as u64) as u32) + } + + #[inline(always)] + fn rotate_right(self, n: Self) -> Self { + let size = Self::BITS; + u64::rotate_right(self, (n % size as u64) as u32) + } + + #[inline(always)] + fn from_le_bytes(bytes: &GenericArray) -> Self { + u64::from_le_bytes(bytes.as_slice().try_into().unwrap()) + } + + #[inline(always)] + fn to_le_bytes(self) -> GenericArray { + u64::to_le_bytes(self).into() + } + + #[inline(always)] + fn bitxor(self, other: Self) -> Self { + ::bitxor(self, other) + } +} diff --git a/rc5/tests/mod.rs b/rc5/tests/mod.rs index 39e0d64d..77fe3554 100644 --- a/rc5/tests/mod.rs +++ b/rc5/tests/mod.rs @@ -2,7 +2,25 @@ #[cfg(test)] mod tests { use cipher::{generic_array::GenericArray, BlockDecrypt, BlockEncrypt, KeyInit}; - use rc5::{RC5_32_12_16, RC5_32_16_16}; + use rc5::{RC5_16_16_8, RC5_32_12_16, RC5_32_16_16, RC5_64_24_24}; + + #[test] + fn enc_dec_16_16_8() { + let key = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]; + + let pt = [0x00, 0x01, 0x02, 0x03]; + let ct = [0x23, 0xA8, 0xD7, 0x2E]; + + let rc5 = ::new_from_slice(&key).unwrap(); + + let mut block = GenericArray::clone_from_slice(&pt); + rc5.encrypt_block(&mut block); + + assert_eq!(ct, block[..]); + + rc5.decrypt_block(&mut block); + assert_eq!(pt, block[..]); + } #[test] fn enc_dec_32_12_16() { @@ -45,4 +63,31 @@ mod tests { rc5.decrypt_block(&mut block); assert_eq!(pt, block[..]); } + + #[test] + fn enc_dec_64_24_24() { + let key = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + ]; + + let pt = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, + ]; + let ct = [ + 0xA4, 0x67, 0x72, 0x82, 0x0E, 0xDB, 0xCE, 0x02, 0x35, 0xAB, 0xEA, 0x32, 0xAE, 0x71, + 0x78, 0xDA, + ]; + + let rc5 = ::new_from_slice(&key).unwrap(); + + let mut block = GenericArray::clone_from_slice(&pt); + rc5.encrypt_block(&mut block); + + assert_eq!(ct, block[..]); + + rc5.decrypt_block(&mut block); + assert_eq!(pt, block[..]); + } }