use aes::Aes128;
use ccm::{
aead::{Aead, KeyInit, Payload},
consts::{U13, U16},
Ccm, Nonce,
};
use crate::error::{Error, Result};
type Aes128Ccm = Ccm<Aes128, U16, U13>;
pub const AEAD_KEY_LEN: usize = 16;
pub const AEAD_NONCE_LEN: usize = 13;
pub const AEAD_TAG_LEN: usize = 16;
pub fn encrypt(
key: &[u8; AEAD_KEY_LEN],
nonce: &[u8; AEAD_NONCE_LEN],
aad: &[u8],
plaintext: &[u8],
) -> Result<Vec<u8>> {
let cipher = Aes128Ccm::new_from_slice(key).map_err(|_| Error::EncryptionFailed)?;
let nonce_arr: Nonce<U13> = (*nonce).into();
cipher
.encrypt(
&nonce_arr,
Payload {
msg: plaintext,
aad,
},
)
.map_err(|_| Error::EncryptionFailed)
}
pub fn decrypt(
key: &[u8; AEAD_KEY_LEN],
nonce: &[u8; AEAD_NONCE_LEN],
aad: &[u8],
ciphertext: &[u8],
) -> Result<Vec<u8>> {
let cipher =
Aes128Ccm::new_from_slice(key).map_err(|_| Error::EncryptedBlobDecryptionFailed)?;
let nonce_arr: Nonce<U13> = (*nonce).into();
cipher
.decrypt(
&nonce_arr,
Payload {
msg: ciphertext,
aad,
},
)
.map_err(|_| Error::EncryptedBlobDecryptionFailed)
}
pub fn ctr_apply(
key: &[u8; AEAD_KEY_LEN],
nonce: &[u8; AEAD_NONCE_LEN],
data: &[u8],
) -> Result<Vec<u8>> {
let mut out = encrypt(key, nonce, &[], data)?;
out.truncate(data.len()); Ok(out)
}
#[cfg(test)]
#[allow(clippy::unwrap_used)] mod tests {
use super::*;
#[test]
fn ctr_apply_is_an_involution() {
let key = [0x42u8; AEAD_KEY_LEN];
let nonce = [0x17u8; AEAD_NONCE_LEN];
let data = b"obfuscate me please";
let once = ctr_apply(&key, &nonce, data).unwrap();
assert_ne!(&once[..], &data[..]);
let twice = ctr_apply(&key, &nonce, &once).unwrap();
assert_eq!(&twice[..], &data[..]);
}
#[test]
fn encrypt_decrypt_roundtrip() {
let key = [0x42u8; AEAD_KEY_LEN];
let nonce = [0x17u8; AEAD_NONCE_LEN];
let aad = b"matter aad";
let plaintext = b"the quick brown fox jumps over the lazy dog";
let ciphertext = encrypt(&key, &nonce, aad, plaintext).unwrap();
assert_eq!(ciphertext.len(), plaintext.len() + AEAD_TAG_LEN);
let decrypted = decrypt(&key, &nonce, aad, &ciphertext).unwrap();
assert_eq!(decrypted, plaintext);
}
#[test]
fn tampered_ciphertext_rejected() {
let key = [0x42u8; AEAD_KEY_LEN];
let nonce = [0x17u8; AEAD_NONCE_LEN];
let mut ciphertext = encrypt(&key, &nonce, b"", b"payload").unwrap();
ciphertext[0] ^= 1;
assert!(decrypt(&key, &nonce, b"", &ciphertext).is_err());
}
#[test]
fn wrong_key_rejected() {
let key = [0x42u8; AEAD_KEY_LEN];
let bad_key = [0x43u8; AEAD_KEY_LEN];
let nonce = [0x17u8; AEAD_NONCE_LEN];
let ciphertext = encrypt(&key, &nonce, b"", b"payload").unwrap();
assert!(decrypt(&bad_key, &nonce, b"", &ciphertext).is_err());
}
#[test]
fn wrong_aad_rejected() {
let key = [0x42u8; AEAD_KEY_LEN];
let nonce = [0x17u8; AEAD_NONCE_LEN];
let ciphertext = encrypt(&key, &nonce, b"good aad", b"payload").unwrap();
assert!(decrypt(&key, &nonce, b"bad aad", &ciphertext).is_err());
}
}