Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions deoxys/src/modes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ where
fn decrypt_inout(
nonce: &Array<u8, Self::NonceSize>,
associated_data: &[u8],
buffer: InOutBuf<'_, '_, u8>,
mut buffer: InOutBuf<'_, '_, u8>,
tag: &Tag,
subkeys: &Array<DeoxysKey, B::SubkeysSize>,
) -> Result<(), aead::Error> {
Expand Down Expand Up @@ -208,7 +208,7 @@ where

tweak[0] = (tweak[0] & 0xf) | TWEAK_M;

let (data_blocks, mut tail) = buffer.into_chunks();
let (data_blocks, mut tail) = buffer.reborrow().into_chunks();
let mut data_blocks_len = data_blocks.len();

for (index, mut data) in data_blocks.into_iter().enumerate() {
Expand Down Expand Up @@ -271,6 +271,8 @@ where
if tag.ct_eq(&computed_tag).into() {
Ok(())
} else {
// Do not leave unauthenticated plaintext in the caller's buffer.
buffer.get_out().fill(0);
Err(aead::Error)
}
}
Expand Down Expand Up @@ -457,6 +459,8 @@ where
if tag.ct_eq(&computed_tag).into() {
Ok(())
} else {
// Do not leave unauthenticated plaintext in the caller's buffer.
buffer.get_out().fill(0);
Err(aead::Error)
}
}
Expand Down
83 changes: 83 additions & 0 deletions deoxys/tests/decrypt_failure.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//! Regression tests for clearing unauthenticated plaintext.

use deoxys::aead::{AeadInOut, Key, KeyInit, Nonce, inout::InOutBuf};
use deoxys::{DeoxysI128, DeoxysI256, DeoxysII128, DeoxysII256};

fn assert_failed_decrypt_clears<C>()
where
C: AeadInOut + KeyInit,
{
let key = Key::<C>::default();
let cipher = C::new(&key);
for len in [0, 1, 15, 16, 17, 31, 32, 33, 64, 65] {
let mut nonce = Nonce::<C>::default();
nonce[0] = len;
let mut ciphertext = [0x42; 65];
let ciphertext = &mut ciphertext[..usize::from(len)];
let tag = cipher
.encrypt_inout_detached(&nonce, b"associated data", ciphertext.into())
.expect("encryption failed");

for tamper in 0..4 {
if tamper == 3 && ciphertext.is_empty() {
continue;
}
let mut bad_tag = tag.clone();
let mut bad_nonce = nonce.clone();
let mut input = ciphertext.to_vec();
let aad: &[u8] = match tamper {
0 => {
bad_tag[0] ^= 1;
b"associated data"
}
1 => b"different data",
2 => {
bad_nonce[0] ^= 1;
b"associated data"
}
_ => {
input[0] ^= 1;
b"associated data"
}
};

let mut output = vec![0xa5; input.len()];
let buffer = InOutBuf::new(&input, &mut output).expect("equal buffer lengths");
cipher
.decrypt_inout_detached(&bad_nonce, aad, buffer, &bad_tag)
.expect_err("tampered message was accepted");
assert!(
output.iter().all(|&byte| byte == 0),
"len={len}, tamper={tamper}"
);

cipher
.decrypt_inout_detached(&bad_nonce, aad, input.as_mut_slice().into(), &bad_tag)
.expect_err("tampered message was accepted");
assert!(
input.iter().all(|&byte| byte == 0),
"len={len}, tamper={tamper}"
);
}
}
}

#[test]
fn deoxys_i_128_failed_decrypt_clears_output() {
assert_failed_decrypt_clears::<DeoxysI128>();
}

#[test]
fn deoxys_i_256_failed_decrypt_clears_output() {
assert_failed_decrypt_clears::<DeoxysI256>();
}

#[test]
fn deoxys_ii_128_failed_decrypt_clears_output() {
assert_failed_decrypt_clears::<DeoxysII128>();
}

#[test]
fn deoxys_ii_256_failed_decrypt_clears_output() {
assert_failed_decrypt_clears::<DeoxysII256>();
}
7 changes: 5 additions & 2 deletions ocb3/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,13 +258,16 @@ where
&self,
nonce: &Nonce<NonceSize>,
associated_data: &[u8],
buffer: InOutBuf<'_, '_, u8>,
mut buffer: InOutBuf<'_, '_, u8>,
tag: &aead::Tag<Self>,
) -> aead::Result<()> {
let expected_tag = self.decrypt_inout_return_tag(nonce, associated_data, buffer)?;
let expected_tag =
self.decrypt_inout_return_tag(nonce, associated_data, buffer.reborrow())?;
if expected_tag.ct_eq(tag).into() {
Ok(())
} else {
// Do not leave unauthenticated plaintext in the caller's buffer.
buffer.get_out().fill(0);
Err(Error)
}
}
Expand Down
79 changes: 79 additions & 0 deletions ocb3/tests/kats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,85 @@ type Aes256Ocb3Tag64 = Ocb3<Aes256, U12, U8>;
type Aes128Ocb3 = Ocb3<Aes128, U12>;
type Aes256Ocb3 = Ocb3<Aes256, U12>;

fn assert_failed_decrypt_clears<C>()
where
C: AeadInOut + KeyInit,
{
let key = aead::Key::<C>::default();
let cipher = C::new(&key);
for len in [0, 1, 15, 16, 17, 31, 32, 33, 64, 65] {
let mut nonce = aead::Nonce::<C>::default();
nonce[0] = len;
let mut ciphertext = [0x42; 65];
let ciphertext = &mut ciphertext[..usize::from(len)];
let tag = cipher
.encrypt_inout_detached(&nonce, b"associated data", ciphertext.into())
.unwrap();

for tamper in 0..4 {
if tamper == 3 && ciphertext.is_empty() {
continue;
}
let mut bad_tag = tag.clone();
let mut bad_nonce = nonce.clone();
let mut input = ciphertext.to_vec();
let aad: &[u8] = match tamper {
0 => {
bad_tag[0] ^= 1;
b"associated data"
}
1 => b"different data",
2 => {
bad_nonce[0] ^= 1;
b"associated data"
}
_ => {
input[0] ^= 1;
b"associated data"
}
};

let mut output = vec![0xa5; input.len()];
let buffer = aead::inout::InOutBuf::new(&input, &mut output).unwrap();
cipher
.decrypt_inout_detached(&bad_nonce, aad, buffer, &bad_tag)
.unwrap_err();
assert!(
output.iter().all(|&byte| byte == 0),
"len={len}, tamper={tamper}"
);

cipher
.decrypt_inout_detached(&bad_nonce, aad, input.as_mut_slice().into(), &bad_tag)
.unwrap_err();
assert!(
input.iter().all(|&byte| byte == 0),
"len={len}, tamper={tamper}"
);
}
}
}

#[test]
fn aes128_ocb3_failed_decrypt_clears_output() {
assert_failed_decrypt_clears::<Aes128Ocb3>();
}

#[test]
fn aes256_ocb3_failed_decrypt_clears_output() {
assert_failed_decrypt_clears::<Aes256Ocb3>();
}

#[test]
fn aes128_ocb3_tag96_failed_decrypt_clears_output() {
assert_failed_decrypt_clears::<Aes128Ocb3Tag96>();
}

#[test]
fn aes128_ocb3_tag64_failed_decrypt_clears_output() {
assert_failed_decrypt_clears::<Aes128Ocb3Tag64>();
}

/// Test vectors from Page 18 of https://www.rfc-editor.org/rfc/rfc7253.html#appendix-A
#[test]
fn rfc7253_more_sample_results() {
Expand Down
Loading