blob: 09bf6295032a78d15b4397d6aad572cbefbeabd8 [file] [log] [blame]
// Copyright 2022 Google LLC
//
// 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.
use aes::{cipher, cipher::KeyInit as _};
use crypto_provider::{aes::*, CryptoProvider};
use crypto_provider_rustcrypto::RustCrypto;
use rand::{self, distributions, Rng as _};
use rand_ext::seeded_rng;
use xts_aes::*;
#[test]
fn roundtrip_self() {
let mut rng = seeded_rng();
for _ in 0..100_000 {
if rng.gen() {
let mut key = [0_u8; 32];
rng.fill(&mut key);
do_roundtrip(
build_xts_aes::<_, <RustCrypto as CryptoProvider>::Aes128>(&XtsAes128Key::from(
&key,
)),
&mut rng,
)
} else {
let mut key = [0_u8; 64];
rng.fill(&mut key);
do_roundtrip(
build_xts_aes::<_, <RustCrypto as CryptoProvider>::Aes256>(&XtsAes256Key::from(
&key,
)),
&mut rng,
)
};
}
fn do_roundtrip<A: Aes, R: rand::Rng>(xts: Xts<A>, rng: &mut R) {
let plaintext_len_range = distributions::Uniform::new_inclusive(BLOCK_SIZE, BLOCK_SIZE * 4);
let mut plaintext = Vec::<u8>::new();
plaintext.extend((0..rng.sample(plaintext_len_range)).map(|_| rng.gen::<u8>()));
let mut ciphertext = plaintext.clone();
let tweak: Tweak = rng.gen::<u128>().into();
xts.encrypt_data_unit(tweak.clone(), &mut ciphertext)
.unwrap();
assert_eq!(plaintext.len(), ciphertext.len());
assert_ne!(plaintext, ciphertext);
xts.decrypt_data_unit(tweak, &mut ciphertext).unwrap();
assert_eq!(plaintext, ciphertext);
}
}
#[test]
fn identical_to_xtsmode_crate() {
// xts-mode crate exists, but is tied to RustCrypto, which we can't necessarily use with
// whatever C/assembly we need to use on embedded platforms. Thus, we have our own, but
// we can compare our impl's results with theirs.
let mut rng = seeded_rng();
for _ in 0..100_000 {
if rng.gen() {
let mut key = [0; 32];
rng.fill(&mut key);
let xts = build_xts_aes::<_, <RustCrypto as CryptoProvider>::Aes128>(
&XtsAes128Key::from(&key),
);
let primary_cipher =
aes::Aes128::new(cipher::generic_array::GenericArray::from_slice(&key[0..16]));
let tweak_cipher =
aes::Aes128::new(cipher::generic_array::GenericArray::from_slice(&key[16..]));
let other_xts = xts_mode::Xts128::new(primary_cipher, tweak_cipher);
do_roundtrip(xts, other_xts, &mut rng)
} else {
let mut key = [0; 64];
rng.fill(&mut key);
let xts = build_xts_aes::<_, <RustCrypto as CryptoProvider>::Aes256>(
&XtsAes256Key::from(&key),
);
let primary_cipher =
aes::Aes256::new(cipher::generic_array::GenericArray::from_slice(&key[0..32]));
let tweak_cipher =
aes::Aes256::new(cipher::generic_array::GenericArray::from_slice(&key[32..]));
let other_xts = xts_mode::Xts128::new(primary_cipher, tweak_cipher);
do_roundtrip(xts, other_xts, &mut rng)
};
}
fn do_roundtrip<
A: Aes,
C: cipher::BlockEncrypt + cipher::BlockDecrypt + cipher::BlockCipher,
R: rand::Rng,
>(
xts: Xts<A>,
other_xts: xts_mode::Xts128<C>,
rng: &mut R,
) {
// 1-3 blocks
let plaintext_len_range = distributions::Uniform::new_inclusive(BLOCK_SIZE, BLOCK_SIZE * 4);
let mut plaintext = Vec::<u8>::new();
plaintext.extend((0..rng.sample(plaintext_len_range)).map(|_| rng.gen::<u8>()));
// encrypt with our impl
let mut ciphertext = plaintext.clone();
let tweak: Tweak = rng.gen::<u128>().into();
xts.encrypt_data_unit(tweak.clone(), &mut ciphertext)
.unwrap();
// encrypt with the other impl
let mut other_ciphertext = plaintext.clone();
let tweak_bytes = tweak.le_bytes();
other_xts.encrypt_sector(&mut other_ciphertext[..], tweak_bytes);
assert_eq!(ciphertext, other_ciphertext);
// decrypt ciphertext in place
xts.decrypt_data_unit(tweak, &mut ciphertext).unwrap();
assert_eq!(plaintext, ciphertext);
// and with the other impl
other_xts.decrypt_sector(&mut other_ciphertext[..], tweak_bytes);
assert_eq!(ciphertext, other_ciphertext);
}
}