blob: 5f09e3351c3489e34503de24129c91a769407e84 [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.
#![allow(
clippy::indexing_slicing,
clippy::unwrap_used,
clippy::panic,
clippy::expect_used
)]
extern crate alloc;
use crate::{
build_np_adv_decrypter_from_key_seed, salt_padder, LdtAdvDecryptError, LdtEncrypterXtsAes128,
LdtNpAdvDecrypterXtsAes128, LdtXtsAes128Decrypter, LegacySalt, LDT_XTS_AES_MAX_LEN,
NP_LEGACY_METADATA_KEY_LEN,
};
use alloc::vec::Vec;
use crypto_provider::CryptoProvider;
use crypto_provider_rustcrypto::RustCrypto;
use ldt::{DefaultPadder, LdtError, LdtKey, XorPadder};
use np_hkdf::NpKeySeedHkdf;
use rand_ext::{random_vec, seeded_rng};
#[test]
fn decrypt_matches_correct_ciphertext() {
let mut rng = seeded_rng();
for _ in 0..1_000 {
let test_state = make_test_components::<_, RustCrypto>(&mut rng);
let cipher = build_np_adv_decrypter_from_key_seed(&test_state.hkdf, test_state.hmac);
let decrypted = cipher
.decrypt_and_verify(&test_state.ciphertext, &test_state.padder)
.unwrap();
assert_eq!(&test_state.plaintext, decrypted.as_ref());
}
}
#[test]
fn decrypt_doesnt_match_when_ciphertext_mangled() {
let mut rng = seeded_rng();
for _ in 0..1_000 {
let mut test_state = make_test_components::<_, RustCrypto>(&mut rng);
// mangle the ciphertext
test_state.ciphertext[0] ^= 0xAA;
let cipher = build_np_adv_decrypter_from_key_seed(&test_state.hkdf, test_state.hmac);
assert_eq!(
Err(LdtAdvDecryptError::MacMismatch),
cipher.decrypt_and_verify(&test_state.ciphertext, &test_state.padder)
);
}
}
#[test]
fn decrypt_doesnt_match_when_plaintext_doesnt_match_mac() {
let mut rng = seeded_rng();
for _ in 0..1_000 {
let mut test_state = make_test_components::<_, RustCrypto>(&mut rng);
// mangle the mac
test_state.hmac[0] ^= 0xAA;
let cipher = build_np_adv_decrypter_from_key_seed(&test_state.hkdf, test_state.hmac);
assert_eq!(
Err(LdtAdvDecryptError::MacMismatch),
cipher.decrypt_and_verify(&test_state.ciphertext, &test_state.padder)
);
}
}
#[test]
#[allow(deprecated)]
fn encrypt_works() {
let mut rng = seeded_rng();
for _ in 0..1_000 {
let test_state = make_test_components::<_, RustCrypto>(&mut rng);
let cipher = test_state.ldt_enc;
let mut plaintext_copy = test_state.plaintext.clone();
cipher
.encrypt(&mut plaintext_copy[..], &test_state.padder)
.unwrap();
assert_eq!(test_state.ciphertext, plaintext_copy);
}
}
#[test]
#[allow(deprecated)]
fn encrypt_too_short_err() {
let ldt_enc = LdtEncrypterXtsAes128::<RustCrypto>::new(&LdtKey::from_concatenated(&[0; 64]));
let mut payload = [0; 7];
assert_eq!(
Err(LdtError::InvalidLength(7)),
ldt_enc.encrypt(&mut payload, &DefaultPadder::default())
);
}
#[test]
#[allow(deprecated)]
fn encrypt_too_long_err() {
let ldt_enc = LdtEncrypterXtsAes128::<RustCrypto>::new(&LdtKey::from_concatenated(&[0; 64]));
let mut payload = [0; 40];
assert_eq!(
Err(LdtError::InvalidLength(40)),
ldt_enc.encrypt(&mut payload, &DefaultPadder::default())
);
}
#[test]
fn decrypt_too_short_err() {
let adv_cipher = LdtNpAdvDecrypterXtsAes128 {
ldt_decrypter: LdtXtsAes128Decrypter::<RustCrypto>::new(&LdtKey::from_concatenated(
&[0; 64],
)),
metadata_key_tag: [0; 32],
metadata_key_hmac_key: np_hkdf::NpHmacSha256Key::<RustCrypto>::from([0; 32]),
};
let payload = [0; 7];
assert_eq!(
Err(LdtAdvDecryptError::InvalidLength(7)),
adv_cipher.decrypt_and_verify(&payload, &DefaultPadder::default())
);
}
#[test]
fn decrypt_too_long_err() {
let adv_cipher = LdtNpAdvDecrypterXtsAes128 {
ldt_decrypter: LdtXtsAes128Decrypter::<RustCrypto>::new(&LdtKey::from_concatenated(
&[0; 64],
)),
metadata_key_tag: [0; 32],
metadata_key_hmac_key: np_hkdf::NpHmacSha256Key::<RustCrypto>::from([0; 32]),
};
let payload = [0; 40];
assert_eq!(
Err(LdtAdvDecryptError::InvalidLength(40)),
adv_cipher.decrypt_and_verify(&payload, &DefaultPadder::default())
);
}
/// Returns (plaintext, ciphertext, padder, hmac key, MAC, ldt)
fn make_test_components<R: rand::Rng, C: crypto_provider::CryptoProvider>(
rng: &mut R,
) -> LdtAdvTestComponents<C> {
// [1, 2) blocks of XTS-AES
let payload_len = rng
.gen_range(crypto_provider::aes::BLOCK_SIZE..=(crypto_provider::aes::BLOCK_SIZE * 2 - 1));
let plaintext = random_vec(rng, payload_len);
let salt = LegacySalt { bytes: rng.gen() };
let padder = salt_padder::<16, C>(salt);
let key_seed: [u8; 32] = rng.gen();
let hkdf = np_hkdf::NpKeySeedHkdf::new(&key_seed);
let ldt_key = hkdf.legacy_ldt_key();
let hmac_key = hkdf.legacy_metadata_key_hmac_key();
let hmac: [u8; 32] = hmac_key.calculate_hmac(&plaintext[..NP_LEGACY_METADATA_KEY_LEN]);
let ldt_enc = LdtEncrypterXtsAes128::<C>::new(&ldt_key);
let mut ciphertext = [0_u8; LDT_XTS_AES_MAX_LEN];
ciphertext[..plaintext.len()].copy_from_slice(&plaintext);
ldt_enc
.encrypt(&mut ciphertext[..plaintext.len()], &padder)
.unwrap();
LdtAdvTestComponents {
plaintext,
ciphertext: ciphertext[..payload_len].to_vec(),
padder,
hmac,
ldt_enc,
hkdf,
}
}
struct LdtAdvTestComponents<C: CryptoProvider> {
plaintext: Vec<u8>,
ciphertext: Vec<u8>,
padder: XorPadder<{ crypto_provider::aes::BLOCK_SIZE }>,
hmac: [u8; 32],
ldt_enc: LdtEncrypterXtsAes128<C>,
hkdf: NpKeySeedHkdf<C>,
}