use aes::block_cipher_trait::BlockCipher;
use aes::Aes128;
use aead::generic_array::typenum::{U0, U10, U12, U13, U14, U16, U4, U6, U8};
use aead::generic_array::{ArrayLength, GenericArray};
use aead::{Aead, Error, NewAead};
use core::marker::PhantomData;
const NB: usize = 4;
const NK: usize = 4;
const AES_BLOCK_SIZE: usize = NB * NK;
const CCM_AAD_MAX_BYTES: usize = 0xFF00;
const CCM_PAYLOAD_MAX_BYTES: usize = 0x10000;
pub trait CcmTagSize: ArrayLength<u8> {}
impl CcmTagSize for U4 {}
impl CcmTagSize for U6 {}
impl CcmTagSize for U8 {}
impl CcmTagSize for U10 {}
impl CcmTagSize for U12 {}
impl CcmTagSize for U14 {}
impl CcmTagSize for U16 {}
pub struct AesCcm<TagSize>
where
TagSize: CcmTagSize,
{
cipher: Aes128,
tag_size: PhantomData<TagSize>,
}
impl<TagSize: CcmTagSize> From<&Aes128> for AesCcm<TagSize> {
fn from(cipher: &Aes128) -> Self {
AesCcm {
cipher: cipher.clone(),
tag_size: PhantomData,
}
}
}
impl<TagSize: CcmTagSize> From<Aes128> for AesCcm<TagSize> {
fn from(cipher: Aes128) -> Self {
AesCcm {
cipher,
tag_size: PhantomData,
}
}
}
impl<TagSize> NewAead for AesCcm<TagSize>
where
TagSize: CcmTagSize,
{
type KeySize = U16;
fn new(key: GenericArray<u8, U16>) -> Self {
AesCcm {
cipher: Aes128::new(&key),
tag_size: PhantomData,
}
}
}
impl<TagSize> Aead for AesCcm<TagSize>
where
TagSize: CcmTagSize,
{
type NonceSize = U13;
type TagSize = TagSize;
type CiphertextOverhead = U0;
fn encrypt_in_place_detached(
&self,
nonce: &GenericArray<u8, Self::NonceSize>,
associated_data: &[u8],
payload: &mut [u8],
) -> Result<GenericArray<u8, TagSize>, Error> {
let alen = associated_data.len();
let plen = payload.len();
let tlen = TagSize::to_usize();
if alen >= CCM_AAD_MAX_BYTES || plen >= CCM_PAYLOAD_MAX_BYTES {
return Err(Error);
}
let mut b = [0u8; AES_BLOCK_SIZE];
let mut tag = [0u8; AES_BLOCK_SIZE];
b[0] = if alen > 0 { 0x40 } else { 0 } | ((tlen as u8 - 2) / 2) << 3 | 1;
b[1..14].copy_from_slice(&nonce[..13]);
b[14] = (plen >> 8) as u8;
b[15] = plen as u8;
tag.copy_from_slice(&b);
self.cipher
.encrypt_block(GenericArray::from_mut_slice(&mut tag));
if alen > 0 {
ccm_cbc_mac(&mut tag, associated_data, true, &self.cipher);
}
if plen > 0 {
ccm_cbc_mac(&mut tag, payload, false, &self.cipher);
}
b[0] = 1;
b[14] = 0;
b[15] = 0;
ccm_ctr_mode(payload, &mut b, &self.cipher);
b[14] = 0;
b[15] = 0;
self.cipher
.encrypt_block(GenericArray::from_mut_slice(&mut b));
let mut t = GenericArray::default();
for i in 0..tlen {
t[i] = tag[i] ^ b[i];
}
Ok(t)
}
fn decrypt_in_place_detached(
&self,
nonce: &GenericArray<u8, Self::NonceSize>,
associated_data: &[u8],
payload: &mut [u8],
tag: &GenericArray<u8, TagSize>,
) -> Result<(), Error> {
let alen = associated_data.len();
let plen = payload.len();
let tlen = TagSize::to_usize();
if alen >= CCM_AAD_MAX_BYTES || plen >= CCM_PAYLOAD_MAX_BYTES {
return Err(Error);
}
let mut b = [0u8; AES_BLOCK_SIZE];
let mut t = [0u8; AES_BLOCK_SIZE];
b[0] = 1;
b[1..14].copy_from_slice(&nonce[..13]);
ccm_ctr_mode(payload, &mut b, &self.cipher);
b[14] = 0;
b[15] = 0;
self.cipher
.encrypt_block(GenericArray::from_mut_slice(&mut b));
for i in 0..tlen {
t[i] = tag[i] ^ b[i];
}
b[0] = if alen > 0 { 0x40 } else { 0 } | ((tlen as u8 - 2) / 2) << 3 | 1;
b[1..14].copy_from_slice(&nonce[..13]);
b[14] = (plen >> 8) as u8;
b[15] = plen as u8;
self.cipher
.encrypt_block(GenericArray::from_mut_slice(&mut b));
if alen > 0 {
ccm_cbc_mac(&mut b, associated_data, true, &self.cipher);
}
if plen > 0 {
ccm_cbc_mac(&mut b, payload, false, &self.cipher);
}
use subtle::ConstantTimeEq;
if b[..tlen].ct_eq(&t[..tlen]).unwrap_u8() == 0 {
payload.iter_mut().for_each(|e| *e = 0);
return Err(Error);
}
Ok(())
}
}
fn ccm_cbc_mac(t: &mut [u8; 16], data: &[u8], flag: bool, cipher: &Aes128) {
let mut dlen = data.len();
let mut i = if flag {
t[0] ^= (dlen >> 8) as u8;
t[1] ^= dlen as u8;
dlen += 2;
2
} else {
0
};
let dlen = dlen;
let mut data = data.iter();
while i < dlen {
t[i % AES_BLOCK_SIZE] ^= data.next().unwrap();
i += 1;
if i % AES_BLOCK_SIZE == 0 || dlen == i {
cipher.encrypt_block(GenericArray::from_mut_slice(t));
}
}
}
fn ccm_ctr_mode(payload: &mut [u8], ctr: &mut [u8], cipher: &Aes128) {
let plen = payload.len();
let mut buffer = [0u8; AES_BLOCK_SIZE];
let mut nonce = [0u8; AES_BLOCK_SIZE];
nonce.copy_from_slice(ctr);
let mut block_num = u16::from(nonce[14]) << 8 | u16::from(nonce[15]);
for i in 0..plen {
if i % AES_BLOCK_SIZE == 0 {
block_num += 1;
nonce[14] = (block_num >> 8) as u8;
nonce[15] = block_num as u8;
buffer.copy_from_slice(&nonce);
cipher.encrypt_block(GenericArray::from_mut_slice(&mut buffer));
}
payload[i] ^= buffer[i % AES_BLOCK_SIZE];
}
ctr[14] = nonce[14];
ctr[15] = nonce[15];
}