#![forbid(unsafe_code)]
use aes::cipher::{BlockCipherEncrypt, KeyInit};
use aes::{Aes128, Aes256};
use chacha20::cipher::{KeyIvInit, StreamCipher, StreamCipherSeek};
use chacha20::ChaCha20;
use oxicrypto_core::CryptoError;
pub const AES_BLOCK_LEN: usize = 16;
pub const AES128_KEY_LEN: usize = 16;
pub const AES256_KEY_LEN: usize = 32;
pub fn aes128_encrypt_block(key: &[u8], block: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
if key.len() != AES128_KEY_LEN {
return Err(CryptoError::InvalidKey);
}
if block.len() != AES_BLOCK_LEN {
return Err(CryptoError::BadInput);
}
if out.len() < AES_BLOCK_LEN {
return Err(CryptoError::BufferTooSmall);
}
let cipher = Aes128::new_from_slice(key).map_err(|_| CryptoError::InvalidKey)?;
let mut buf: aes::Block = aes::Block::try_from(block).map_err(|_| CryptoError::BadInput)?;
cipher.encrypt_block(&mut buf);
out[..AES_BLOCK_LEN].copy_from_slice(&buf);
Ok(())
}
pub fn aes256_encrypt_block(key: &[u8], block: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
if key.len() != AES256_KEY_LEN {
return Err(CryptoError::InvalidKey);
}
if block.len() != AES_BLOCK_LEN {
return Err(CryptoError::BadInput);
}
if out.len() < AES_BLOCK_LEN {
return Err(CryptoError::BufferTooSmall);
}
let cipher = Aes256::new_from_slice(key).map_err(|_| CryptoError::InvalidKey)?;
let mut buf: aes::Block = aes::Block::try_from(block).map_err(|_| CryptoError::BadInput)?;
cipher.encrypt_block(&mut buf);
out[..AES_BLOCK_LEN].copy_from_slice(&buf);
Ok(())
}
pub const CHACHA20_KEY_LEN: usize = 32;
pub const CHACHA20_NONCE_LEN: usize = 12;
pub fn chacha20_keystream_block(
key: &[u8],
counter: u32,
nonce: &[u8],
out: &mut [u8],
) -> Result<(), CryptoError> {
if key.len() != CHACHA20_KEY_LEN {
return Err(CryptoError::InvalidKey);
}
if nonce.len() != CHACHA20_NONCE_LEN {
return Err(CryptoError::InvalidNonce);
}
if out.is_empty() {
return Err(CryptoError::BadInput);
}
let key_arr: chacha20::Key =
chacha20::Key::try_from(key).map_err(|_| CryptoError::InvalidKey)?;
let nonce_arr: chacha20::cipher::Iv<ChaCha20> =
chacha20::cipher::Iv::<ChaCha20>::try_from(nonce).map_err(|_| CryptoError::InvalidNonce)?;
let mut cipher = ChaCha20::new(&key_arr, &nonce_arr);
let byte_offset = u64::from(counter)
.checked_mul(64)
.ok_or(CryptoError::BadInput)?;
cipher.seek(byte_offset);
for b in out.iter_mut() {
*b = 0;
}
cipher.apply_keystream(out);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn hex_decode(s: &str) -> Vec<u8> {
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).expect("valid hex"))
.collect()
}
#[test]
fn aes128_fips197_appendix_b() {
let key = hex_decode("000102030405060708090a0b0c0d0e0f");
let pt = hex_decode("00112233445566778899aabbccddeeff");
let mut out = [0u8; 16];
aes128_encrypt_block(&key, &pt, &mut out).expect("aes128");
assert_eq!(out.to_vec(), hex_decode("69c4e0d86a7b0430d8cdb78070b4c55a"));
}
#[test]
fn aes256_fips197_appendix_c() {
let key = hex_decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
let pt = hex_decode("00112233445566778899aabbccddeeff");
let mut out = [0u8; 16];
aes256_encrypt_block(&key, &pt, &mut out).expect("aes256");
assert_eq!(out.to_vec(), hex_decode("8ea2b7ca516745bfeafc49904b496089"));
}
#[test]
fn rfc9001_a5_chacha20_header_mask() {
let hp = hex_decode("25a282b9e82f06f21f488917a4fc8f1b73573685608597d0efcb076b0ab7a7a4");
let sample = hex_decode("5e5cd55c41f69080575d7999c25a5bfb");
let counter = u32::from_le_bytes([sample[0], sample[1], sample[2], sample[3]]);
let nonce = &sample[4..16];
let mut mask = [0u8; 5];
chacha20_keystream_block(&hp, counter, nonce, &mut mask).expect("ks");
assert_eq!(
mask.to_vec(),
hex_decode("aefefe7d03"),
"RFC 9001 A.5 mask mismatch"
);
}
#[test]
fn aes_invalid_lengths() {
let mut out = [0u8; 16];
assert_eq!(
aes128_encrypt_block(&[0u8; 15], &[0u8; 16], &mut out),
Err(CryptoError::InvalidKey)
);
assert_eq!(
aes128_encrypt_block(&[0u8; 16], &[0u8; 15], &mut out),
Err(CryptoError::BadInput)
);
assert_eq!(
aes256_encrypt_block(&[0u8; 32], &[0u8; 16], &mut [0u8; 8]),
Err(CryptoError::BufferTooSmall)
);
}
#[test]
fn chacha20_invalid_lengths() {
let mut out = [0u8; 5];
assert_eq!(
chacha20_keystream_block(&[0u8; 31], 0, &[0u8; 12], &mut out),
Err(CryptoError::InvalidKey)
);
assert_eq!(
chacha20_keystream_block(&[0u8; 32], 0, &[0u8; 11], &mut out),
Err(CryptoError::InvalidNonce)
);
assert_eq!(
chacha20_keystream_block(&[0u8; 32], 0, &[0u8; 12], &mut []),
Err(CryptoError::BadInput)
);
}
#[test]
fn chacha20_keystream_deterministic() {
let key = [0x01u8; 32];
let nonce = [0x02u8; 12];
let mut a = [0u8; 16];
let mut b = [0u8; 16];
chacha20_keystream_block(&key, 1, &nonce, &mut a).expect("a");
chacha20_keystream_block(&key, 1, &nonce, &mut b).expect("b");
assert_eq!(a, b);
assert_ne!(a, [0u8; 16]);
}
}