#![forbid(unsafe_code)]
use crate::deoxys_bc::{DeoxysBc256, BLOCK_SIZE};
use oxicrypto_core::{ct_eq, Aead, CryptoError};
const KEY_LEN: usize = 16;
const NONCE_LEN: usize = 16;
const TAG_LEN: usize = 16;
const NONCE_TWEAK_LEN: usize = 15;
const TWEAK_AD: u8 = 0x20;
const TWEAK_AD_LAST: u8 = 0x60;
const TWEAK_MSG: u8 = 0x00;
const TWEAK_MSG_LAST: u8 = 0x40;
const TWEAK_TAG: u8 = 0x10;
#[inline]
fn set_counter(tweak: &mut [u8; BLOCK_SIZE], index: u64) {
tweak[8..16].copy_from_slice(&index.to_be_bytes());
}
fn absorb_ad(bc: &DeoxysBc256, aad: &[u8], auth: &mut [u8; BLOCK_SIZE]) {
if aad.is_empty() {
return;
}
let mut tweak = [0u8; BLOCK_SIZE];
let full_blocks = aad.len() / BLOCK_SIZE;
let rem = aad.len() % BLOCK_SIZE;
for (index, chunk) in aad.chunks(BLOCK_SIZE).take(full_blocks).enumerate() {
tweak[0] = TWEAK_AD;
set_counter(&mut tweak, index as u64);
let mut block = [0u8; BLOCK_SIZE];
block.copy_from_slice(chunk);
let out = bc.encrypt_block(&tweak, &block);
xor_into(auth, &out);
}
if rem != 0 {
tweak[0] = TWEAK_AD_LAST;
set_counter(&mut tweak, full_blocks as u64);
let mut block = [0u8; BLOCK_SIZE];
block[..rem].copy_from_slice(&aad[full_blocks * BLOCK_SIZE..]);
block[rem] = 0x80; let out = bc.encrypt_block(&tweak, &block);
xor_into(auth, &out);
}
}
fn absorb_message(bc: &DeoxysBc256, msg: &[u8], auth: &mut [u8; BLOCK_SIZE]) {
if msg.is_empty() {
return;
}
let mut tweak = [0u8; BLOCK_SIZE];
let full_blocks = msg.len() / BLOCK_SIZE;
let rem = msg.len() % BLOCK_SIZE;
for (index, chunk) in msg.chunks(BLOCK_SIZE).take(full_blocks).enumerate() {
tweak[0] = TWEAK_MSG;
set_counter(&mut tweak, index as u64);
let mut block = [0u8; BLOCK_SIZE];
block.copy_from_slice(chunk);
let out = bc.encrypt_block(&tweak, &block);
xor_into(auth, &out);
}
if rem != 0 {
tweak[0] = TWEAK_MSG_LAST;
set_counter(&mut tweak, full_blocks as u64);
let mut block = [0u8; BLOCK_SIZE];
block[..rem].copy_from_slice(&msg[full_blocks * BLOCK_SIZE..]);
block[rem] = 0x80; let out = bc.encrypt_block(&tweak, &block);
xor_into(auth, &out);
}
}
fn compute_tag(bc: &DeoxysBc256, nonce: &[u8], aad: &[u8], msg: &[u8]) -> [u8; TAG_LEN] {
let mut auth = [0u8; BLOCK_SIZE];
absorb_ad(bc, aad, &mut auth);
absorb_message(bc, msg, &mut auth);
let mut tweak = [0u8; BLOCK_SIZE];
tweak[0] = TWEAK_TAG;
tweak[1..1 + NONCE_TWEAK_LEN].copy_from_slice(&nonce[..NONCE_TWEAK_LEN]);
bc.encrypt_block(&tweak, &auth)
}
fn apply_keystream(bc: &DeoxysBc256, nonce: &[u8], tag: &[u8; TAG_LEN], data: &mut [u8]) {
if data.is_empty() {
return;
}
let mut base = [0u8; BLOCK_SIZE];
base.copy_from_slice(tag);
base[0] |= 0x80;
let mut input = [0u8; BLOCK_SIZE];
input[1..1 + NONCE_TWEAK_LEN].copy_from_slice(&nonce[..NONCE_TWEAK_LEN]);
for (index, chunk) in data.chunks_mut(BLOCK_SIZE).enumerate() {
let mut tweak = base;
let ctr = (index as u64).to_be_bytes();
for (t, c) in tweak[8..16].iter_mut().zip(ctr.iter()) {
*t ^= c;
}
let keystream = bc.encrypt_block(&tweak, &input);
for (b, k) in chunk.iter_mut().zip(keystream.iter()) {
*b ^= k;
}
}
}
#[inline]
fn xor_into(acc: &mut [u8; BLOCK_SIZE], block: &[u8; BLOCK_SIZE]) {
for (a, b) in acc.iter_mut().zip(block.iter()) {
*a ^= b;
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct Deoxys2_128;
impl Deoxys2_128 {
fn seal_impl(
key: &[u8],
nonce: &[u8],
aad: &[u8],
pt: &[u8],
ct_out: &mut [u8],
) -> Result<usize, CryptoError> {
if key.len() != KEY_LEN {
return Err(CryptoError::InvalidKey);
}
if nonce.len() != NONCE_LEN {
return Err(CryptoError::InvalidNonce);
}
let required = pt.len().checked_add(TAG_LEN).ok_or(CryptoError::BadInput)?;
if ct_out.len() < required {
return Err(CryptoError::BufferTooSmall);
}
let mut key_arr = [0u8; KEY_LEN];
key_arr.copy_from_slice(key);
let bc = DeoxysBc256::new(&key_arr);
let tag = compute_tag(&bc, nonce, aad, pt);
ct_out[..pt.len()].copy_from_slice(pt);
apply_keystream(&bc, nonce, &tag, &mut ct_out[..pt.len()]);
ct_out[pt.len()..required].copy_from_slice(&tag);
Ok(required)
}
fn open_impl(
key: &[u8],
nonce: &[u8],
aad: &[u8],
ct: &[u8],
pt_out: &mut [u8],
) -> Result<usize, CryptoError> {
if key.len() != KEY_LEN {
return Err(CryptoError::InvalidKey);
}
if nonce.len() != NONCE_LEN {
return Err(CryptoError::InvalidNonce);
}
if ct.len() < TAG_LEN {
return Err(CryptoError::BadInput);
}
let pt_len = ct.len() - TAG_LEN;
if pt_out.len() < pt_len {
return Err(CryptoError::BufferTooSmall);
}
let mut key_arr = [0u8; KEY_LEN];
key_arr.copy_from_slice(key);
let bc = DeoxysBc256::new(&key_arr);
let (ciphertext, received_tag) = ct.split_at(pt_len);
let mut tag = [0u8; TAG_LEN];
tag.copy_from_slice(received_tag);
pt_out[..pt_len].copy_from_slice(ciphertext);
apply_keystream(&bc, nonce, &tag, &mut pt_out[..pt_len]);
let expected = compute_tag(&bc, nonce, aad, &pt_out[..pt_len]);
if ct_eq(&expected, &tag) {
Ok(pt_len)
} else {
for b in &mut pt_out[..pt_len] {
*b = 0;
}
Err(CryptoError::InvalidTag)
}
}
}
impl Aead for Deoxys2_128 {
fn name(&self) -> &'static str {
"Deoxys-II-128-128"
}
fn key_len(&self) -> usize {
KEY_LEN
}
fn nonce_len(&self) -> usize {
NONCE_LEN
}
fn tag_len(&self) -> usize {
TAG_LEN
}
fn seal(
&self,
key: &[u8],
nonce: &[u8],
aad: &[u8],
pt: &[u8],
ct_out: &mut [u8],
) -> Result<usize, CryptoError> {
Self::seal_impl(key, nonce, aad, pt, ct_out)
}
fn open(
&self,
key: &[u8],
nonce: &[u8],
aad: &[u8],
ct: &[u8],
pt_out: &mut [u8],
) -> Result<usize, CryptoError> {
Self::open_impl(key, nonce, aad, ct, pt_out)
}
}
#[cfg(test)]
mod tests {
use super::*;
const KEY: [u8; 16] = [
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
0x1f,
];
const NONCE: [u8; 16] = [
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e,
0x2f,
];
#[test]
fn round_trip_multiblock() {
let aead = Deoxys2_128;
let pt = b"The quick brown fox jumps over the lazy dog!! Deoxys-II misuse resistant.";
let aad = b"header-metadata";
let mut ct = vec![0u8; pt.len() + aead.tag_len()];
let written = aead.seal(&KEY, &NONCE, aad, pt, &mut ct).expect("seal");
assert_eq!(written, pt.len() + 16);
let mut dec = vec![0u8; pt.len()];
let n = aead
.open(&KEY, &NONCE, aad, &ct[..written], &mut dec)
.expect("open");
assert_eq!(&dec[..n], pt.as_ref());
}
#[test]
fn empty_message_empty_aad() {
let aead = Deoxys2_128;
let mut ct = vec![0u8; aead.tag_len()];
let written = aead.seal(&KEY, &NONCE, b"", b"", &mut ct).expect("seal");
assert_eq!(written, 16);
let mut dec = vec![0u8; 0];
assert_eq!(
aead.open(&KEY, &NONCE, b"", &ct[..written], &mut dec)
.expect("open"),
0
);
}
#[test]
fn wrong_key_rejected() {
let aead = Deoxys2_128;
let pt = b"secret";
let mut ct = vec![0u8; pt.len() + 16];
let written = aead.seal(&KEY, &NONCE, b"", pt, &mut ct).expect("seal");
let mut dec = vec![0u8; pt.len()];
assert_eq!(
aead.open(&[0u8; 16], &NONCE, b"", &ct[..written], &mut dec),
Err(CryptoError::InvalidTag)
);
}
#[test]
fn invalid_lengths() {
let aead = Deoxys2_128;
let mut ct = vec![0u8; 32];
assert_eq!(
aead.seal(&[0u8; 15], &NONCE, b"", b"x", &mut ct),
Err(CryptoError::InvalidKey)
);
assert_eq!(
aead.seal(&KEY, &[0u8; 12], b"", b"x", &mut ct),
Err(CryptoError::InvalidNonce)
);
}
}