diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 767fe46..a6f6f63 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: target: - - wasm32-wasi + - wasm32-wasip1 steps: - uses: actions/checkout@v3 @@ -27,7 +27,7 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@1.60.0 + - uses: dtolnay/rust-toolchain@1.85.0 id: toolchain - run: rustup override set ${{steps.toolchain.outputs.name}} - name: Run tests @@ -38,7 +38,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@1.60.0 + - uses: dtolnay/rust-toolchain@1.85.0 id: toolchain - run: rustup override set ${{steps.toolchain.outputs.name}} # Build benchmarks to prevent bitrot @@ -51,7 +51,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@1.60.0 + - uses: dtolnay/rust-toolchain@1.85.0 id: toolchain - run: rustup override set ${{steps.toolchain.outputs.name}} - run: rustup component add clippy diff --git a/CHANGELOG.md b/CHANGELOG.md index ba190c9..f04fbad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this library adheres to Rust's notion of [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed +- MSRV is now 1.85.0. +- Bumped dependencies to `cipher 0.5.1`, `cbc 0.2`. + - `aes 0.9` is now the minimum compatible crate version. ## [0.6.1] - 2023-04-13 ### Fixed diff --git a/Cargo.toml b/Cargo.toml index 87d83b8..f8d2f5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.6.1" authors = ["Jack Grigg "] license = "MIT/Apache-2.0" edition = "2021" -rust-version = "1.56" +rust-version = "1.85" description = "Format-preserving encryption" documentation = "https://docs.rs/fpe/" homepage = "https://github.com/str4d/fpe" @@ -13,8 +13,8 @@ keywords = ["ff1"] categories = ["cryptography", "no-std"] [dependencies] -cbc = { version = "0.1", default-features = false } -cipher = "0.4" +cbc = { version = "0.2", default-features = false } +cipher = "0.5.1" libm = "0.2" num-bigint = { version = "0.4", optional = true, default-features = false } @@ -22,7 +22,7 @@ num-integer = { version = "0.1", optional = true, default-features = false } num-traits = { version = "0.2", optional = true, default-features = false } [dev-dependencies] -aes = "0.8" +aes = "0.9" # Tests proptest = "1.1" diff --git a/README.md b/README.md index 327c76c..a2dc0e5 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ algorithms. The following algorithms are implemented: - FF1 (specified in [NIST Special Publication 800-38G](http://dx.doi.org/10.6028/NIST.SP.800-38G)). -This crate requires Rust version 1.56 or greater. +This crate requires Rust version 1.85 or greater. ## License diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 9355f33..e22c344 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.56.0" +channel = "1.85.0" components = ["clippy", "rustfmt"] diff --git a/src/ff1.rs b/src/ff1.rs index bf0500d..8d7dc1e 100644 --- a/src/ff1.rs +++ b/src/ff1.rs @@ -4,8 +4,7 @@ use core::cmp; use cipher::{ - generic_array::GenericArray, Block, BlockCipher, BlockEncrypt, BlockEncryptMut, InnerIvInit, - KeyInit, + common::IvState, Array, Block, BlockCipherEncrypt, BlockModeEncrypt, InnerIvInit, KeyInit, }; #[cfg(test)] @@ -64,7 +63,7 @@ impl Radix { let log_radix = 31 - radix.leading_zeros(); Radix::PowerTwo { radix, - min_len: cmp::max((MIN_RADIX_2_NS_LEN + log_radix - 1) / log_radix, MIN_NS_LEN), + min_len: cmp::max(MIN_RADIX_2_NS_LEN.div_ceil(log_radix), MIN_NS_LEN), log_radix: u8::try_from(log_radix).unwrap(), } } else { @@ -156,20 +155,29 @@ pub trait NumeralString: Sized { fn concat(a: Self::Ops, b: Self::Ops) -> Self; } -#[derive(Clone)] -struct Prf { +struct Prf { state: cbc::Encryptor, // Contains the output when offset = 0, and partial input otherwise buf: [Block; 1], offset: usize, } -impl Prf { +impl Prf { + fn fork(&self, ciph: &CIPH) -> Self { + Self { + state: cbc::Encryptor::inner_iv_init(ciph.clone(), &self.state.iv_state()), + buf: self.buf.clone(), + offset: self.offset, + } + } +} + +impl Prf { fn new(ciph: &CIPH) -> Self { let ciph = ciph.clone(); Prf { - state: cbc::Encryptor::inner_iv_init(ciph, GenericArray::from_slice(&[0; 16])), - buf: [Block::::default()], + state: cbc::Encryptor::inner_iv_init(ciph, &Array::from_fn(|_| 0)), + buf: [Block::::from_fn(|_| 0)], offset: 0, } } @@ -182,7 +190,7 @@ impl Prf { data = &data[to_read..]; if self.offset == self.buf[0].len() { - self.state.encrypt_blocks_mut(&mut self.buf); + self.state.encrypt_blocks(&mut self.buf); self.offset = 0; } } @@ -197,7 +205,7 @@ impl Prf { } } -fn generate_s<'a, CIPH: BlockEncrypt>( +fn generate_s<'a, CIPH: BlockCipherEncrypt>( ciph: &'a CIPH, r: &'a Block, d: usize, @@ -216,23 +224,25 @@ fn generate_s<'a, CIPH: BlockEncrypt>( } /// A struct for performing FF1 encryption and decryption operations. -pub struct FF1 { +pub struct FF1 { ciph: CIPH, radix: Radix, } -impl FF1 { +impl FF1 { /// Creates a new FF1 object for the given key and radix. /// /// Returns an error if the given radix is not in [2..2^16]. + /// + /// Panics if the key size does not match that expected by the cipher. pub fn new(key: &[u8], radix: u32) -> Result { - let ciph = CIPH::new(GenericArray::from_slice(key)); + let ciph = CIPH::new(key.try_into().expect("Invalid key length")); let radix = Radix::from_u32(radix)?; Ok(FF1 { ciph, radix }) } } -impl FF1 { +impl FF1 { /// Encrypts the given numeral string. /// /// Returns an error if the numeral string is not in the required radix. @@ -277,7 +287,7 @@ impl FF1 { prf.update(&[0]); } for i in 0..10 { - let mut prf = prf.clone(); + let mut prf = prf.fork(&self.ciph); prf.update(&[i]); prf.update(x_b.to_be_bytes(self.radix.to_u32(), b).as_ref()); let r = prf.output(); @@ -348,7 +358,7 @@ impl FF1 { } for i in 0..10 { let i = 9 - i; - let mut prf = prf.clone(); + let mut prf = prf.fork(&self.ciph); prf.update(&[i]); prf.update(x_a.to_be_bytes(self.radix.to_u32(), b).as_ref()); let r = prf.output(); diff --git a/src/ff1/alloc.rs b/src/ff1/alloc.rs index f932fc1..d873d8c 100644 --- a/src/ff1/alloc.rs +++ b/src/ff1/alloc.rs @@ -97,7 +97,7 @@ impl NumeralString for FlexibleNumeralString { type Ops = Self; fn is_valid(&self, radix: u32) -> bool { - self.0.iter().all(|n| (u32::from(*n) < radix)) + self.0.iter().all(|n| u32::from(*n) < radix) } fn numeral_count(&self) -> usize { @@ -171,7 +171,7 @@ impl BinaryNumeralString { BinaryNumeralString(s.to_vec()) } - /// Returns a Vec, with each byte written from the BinaryNumeralString + /// Returns a Vec, with each byte written from the BinaryNumeralString /// in little-endian bit order. pub fn to_bytes_le(&self) -> Vec { self.0.to_vec() @@ -297,7 +297,7 @@ impl NumeralString for BinaryNumeralString { // Simple case: no shifting necessary, just reversing and joining. b.data .into_iter() - .chain(a.data.into_iter()) + .chain(a.data) .map(|b| b.reverse_bits()) .rev() .collect() diff --git a/src/ff1/proptests.rs b/src/ff1/proptests.rs index f44da13..a6f820f 100644 --- a/src/ff1/proptests.rs +++ b/src/ff1/proptests.rs @@ -56,7 +56,7 @@ proptest! { key in prop::array::uniform32(prop::num::u8::ANY), (radix, _, _) in valid_radix(), ) { - assert!(matches!(FF1::::new(&key, radix), Ok(_))); + assert!(FF1::::new(&key, radix).is_ok()); } #[test]