Skip to content

Commit a8d6711

Browse files
authored
aead: tweak dev module and add DummyAead tests (#1802)
Tests the inout methods and moves test vector code outside of the macro.
1 parent d2b4b59 commit a8d6711

File tree

5 files changed

+272
-55
lines changed

5 files changed

+272
-55
lines changed

aead/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ heapless = { version = "0.8", optional = true, default-features = false }
2828
[features]
2929
default = ["rand_core"]
3030
alloc = []
31-
dev = ["blobby"]
31+
dev = ["blobby", "alloc"]
3232
os_rng = ["crypto-common/os_rng", "rand_core"]
3333
rand_core = ["crypto-common/rand_core"]
3434

aead/src/dev.rs

Lines changed: 96 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,116 @@
11
//! Development-related functionality
2+
use crate::{
3+
Aead, AeadInOut, Nonce, Payload, Tag, TagPosition, array::typenum::Unsigned, inout::InOutBuf,
4+
};
25
pub use blobby;
36

7+
/// Run AEAD test for the provided passing test vector
8+
pub fn run_pass_test<C: AeadInOut>(
9+
cipher: &C,
10+
nonce: &Nonce<C>,
11+
aad: &[u8],
12+
pt: &[u8],
13+
ct: &[u8],
14+
) -> Result<(), &'static str> {
15+
let res = cipher
16+
.encrypt(nonce, Payload { aad, msg: pt })
17+
.map_err(|_| "encryption failure")?;
18+
if res != ct {
19+
return Err("encrypted data is different from target ciphertext");
20+
}
21+
22+
let res = cipher
23+
.decrypt(nonce, Payload { aad, msg: ct })
24+
.map_err(|_| "decryption failure")?;
25+
if res != pt {
26+
return Err("decrypted data is different from target plaintext");
27+
}
28+
29+
let (ct, tag) = match C::TAG_POSITION {
30+
TagPosition::Prefix => {
31+
let (tag, ct) = ct.split_at(C::TagSize::USIZE);
32+
(ct, tag)
33+
}
34+
TagPosition::Postfix => ct.split_at(pt.len()),
35+
};
36+
let tag: &Tag<C> = tag.try_into().expect("tag has correct length");
37+
38+
// Fill output buffer with "garbage" to test that its data does not get read during encryption
39+
let mut buf: alloc::vec::Vec<u8> = (0..pt.len()).map(|i| i as u8).collect();
40+
let inout_buf = InOutBuf::new(pt, &mut buf).expect("pt and buf have the same length");
41+
42+
let calc_tag = cipher
43+
.encrypt_inout_detached(nonce, aad, inout_buf)
44+
.map_err(|_| "encrypt_inout_detached: encryption failure")?;
45+
if tag != &calc_tag {
46+
return Err("encrypt_inout_detached: tag mismatch");
47+
}
48+
if ct != buf {
49+
return Err("encrypt_inout_detached: ciphertext mismatch");
50+
}
51+
52+
// Fill output buffer with "garbage"
53+
buf.iter_mut().enumerate().for_each(|(i, v)| *v = i as u8);
54+
55+
let inout_buf = InOutBuf::new(ct, &mut buf).expect("ct and buf have the same length");
56+
cipher
57+
.decrypt_inout_detached(nonce, aad, inout_buf, tag)
58+
.map_err(|_| "decrypt_inout_detached: decryption failure")?;
59+
if pt != buf {
60+
return Err("decrypt_inout_detached: plaintext mismatch");
61+
}
62+
63+
Ok(())
64+
}
65+
66+
/// Run AEAD test for the provided failing test vector
67+
pub fn run_fail_test<C: AeadInOut>(
68+
cipher: &C,
69+
nonce: &Nonce<C>,
70+
aad: &[u8],
71+
ct: &[u8],
72+
) -> Result<(), &'static str> {
73+
let res = cipher.decrypt(nonce, Payload { aad, msg: ct });
74+
if res.is_ok() {
75+
Err("decryption must return error")
76+
} else {
77+
Ok(())
78+
}
79+
}
80+
481
/// Define AEAD test
582
#[macro_export]
683
macro_rules! new_test {
784
($name:ident, $test_name:expr, $cipher:ty $(,)?) => {
885
#[test]
986
fn $name() {
10-
use aead::{
11-
Aead, KeyInit, Payload,
12-
array::{Array, typenum::Unsigned},
13-
dev::blobby::Blob6Iterator,
14-
};
15-
16-
fn run_test(
17-
key: &[u8],
18-
nonce: &[u8],
19-
aad: &[u8],
20-
pt: &[u8],
21-
ct: &[u8],
22-
pass: bool,
23-
) -> Result<(), &'static str> {
24-
let key = key.try_into().map_err(|_| "wrong key size")?;
25-
let cipher = <$cipher>::new(key);
26-
let nonce = nonce.try_into().map_err(|_| "wrong nonce size")?;
27-
28-
if !pass {
29-
let res = cipher.decrypt(nonce, Payload { aad: aad, msg: ct });
30-
if res.is_ok() {
31-
return Err("decryption must return error");
32-
}
33-
return Ok(());
34-
}
35-
36-
let res = cipher
37-
.encrypt(nonce, Payload { aad: aad, msg: pt })
38-
.map_err(|_| "encryption failure")?;
39-
if res != ct {
40-
return Err("encrypted data is different from target ciphertext");
41-
}
42-
let res = cipher
43-
.decrypt(nonce, Payload { aad: aad, msg: ct })
44-
.map_err(|_| "decryption failure")?;
45-
if res != pt {
46-
return Err("decrypted data is different from target plaintext");
47-
}
48-
Ok(())
49-
}
87+
use $crate::KeyInit;
88+
use $crate::dev::blobby::Blob6Iterator;
5089

5190
let data = include_bytes!(concat!("data/", $test_name, ".blb"));
5291
for (i, row) in Blob6Iterator::new(data).unwrap().enumerate() {
5392
let [key, nonce, aad, pt, ct, status] = row.unwrap();
54-
let pass = match status[0] {
55-
0 => false,
56-
1 => true,
93+
let key = key.try_into().expect("wrong key size");
94+
let nonce = nonce.try_into().expect("wrong nonce size");
95+
let cipher = <$cipher as KeyInit>::new(key);
96+
97+
let res = match status {
98+
[0] => $crate::dev::run_fail_test(&cipher, nonce, aad, ct),
99+
[1] => $crate::dev::run_pass_test(&cipher, nonce, aad, pt, ct),
57100
_ => panic!("invalid value for pass flag"),
58101
};
59-
if let Err(reason) = run_test(key, nonce, aad, pt, ct, pass) {
102+
let mut pass = status[0] == 1;
103+
if let Err(reason) = res {
60104
panic!(
61105
"\n\
62-
Failed test №{}\n\
63-
reason: \t{:?}\n\
64-
key:\t{:?}\n\
65-
nonce:\t{:?}\n\
66-
aad:\t{:?}\n\
67-
plaintext:\t{:?}\n\
68-
ciphertext:\t{:?}\n\
69-
pass:\t{}\n\
70-
",
71-
i, reason, key, nonce, aad, pt, ct, pass,
106+
Failed test #{i}\n\
107+
reason:\t{reason:?}\n\
108+
key:\t{key:?}\n\
109+
nonce:\t{nonce:?}\n\
110+
aad:\t{aad:?}\n\
111+
plaintext:\t{pt:?}\n\
112+
ciphertext:\t{ct:?}\n\
113+
pass:\t{pass}\n"
72114
);
73115
}
74116
}

aead/tests/data/postfix.blb

208 Bytes
Binary file not shown.

aead/tests/data/prefix.blb

208 Bytes
Binary file not shown.

aead/tests/dummy.rs

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
//! This module defines dummy (horribly insecure!) AEAD implementations
2+
//! to test implementation of the AEAD traits and helper macros in the `dev` module.
3+
use aead::{
4+
AeadCore, AeadInOut, Error, Key, KeyInit, KeySizeUser, Nonce, Result, Tag, TagPosition,
5+
array::Array, consts::U8,
6+
};
7+
use inout::InOutBuf;
8+
9+
struct DummyAead {
10+
key: [u8; 8],
11+
}
12+
13+
impl DummyAead {
14+
fn process_aad(&self, nonce: &[u8; 8], aad: &[u8]) -> u64 {
15+
let mut tag = u64::from_le_bytes(*nonce);
16+
let key = u64::from_le_bytes(self.key);
17+
18+
let mut aad_iter = aad.chunks_exact(8);
19+
for chunk in &mut aad_iter {
20+
tag ^= u64::from_le_bytes(chunk.try_into().unwrap());
21+
tag = tag.wrapping_add(key);
22+
}
23+
let aad_rem = aad_iter.remainder();
24+
if !aad_rem.is_empty() {
25+
let mut chunk = [0u8; 8];
26+
chunk[..aad_rem.len()].copy_from_slice(aad_rem);
27+
tag ^= u64::from_le_bytes(chunk);
28+
tag = tag.wrapping_add(key);
29+
}
30+
31+
tag
32+
}
33+
34+
fn encrypt_inner(
35+
&self,
36+
nonce: &[u8; 8],
37+
aad: &[u8],
38+
buffer: InOutBuf<'_, '_, u8>,
39+
) -> Result<[u8; 8]> {
40+
let mut tag = self.process_aad(nonce, aad);
41+
42+
let (blocks, mut rem) = buffer.into_chunks::<U8>();
43+
for mut block in blocks {
44+
block.xor_in2out(&self.key.into());
45+
tag ^= u64::from_be_bytes(block.get_out().0);
46+
}
47+
48+
if !rem.is_empty() {
49+
rem.xor_in2out(&self.key[..rem.len()]);
50+
51+
let out_rem = rem.get_out();
52+
let mut block = [0u8; 8];
53+
block[..out_rem.len()].copy_from_slice(out_rem);
54+
tag ^= u64::from_le_bytes(block);
55+
}
56+
57+
Ok(tag.to_le_bytes())
58+
}
59+
60+
fn decrypt_inner(
61+
&self,
62+
nonce: &[u8; 8],
63+
aad: &[u8],
64+
mut buffer: InOutBuf<'_, '_, u8>,
65+
tag: &[u8; 8],
66+
) -> Result<()> {
67+
let exp_tag = u64::from_le_bytes(*tag);
68+
let mut tag = self.process_aad(nonce, aad);
69+
70+
let (blocks, mut rem) = buffer.reborrow().into_chunks::<U8>();
71+
for mut block in blocks {
72+
tag ^= u64::from_be_bytes(block.get_in().0);
73+
block.xor_in2out(&self.key.into());
74+
}
75+
76+
if !rem.is_empty() {
77+
let in_rem = rem.get_in();
78+
let mut block = [0u8; 8];
79+
block[..in_rem.len()].copy_from_slice(in_rem);
80+
tag ^= u64::from_le_bytes(block);
81+
82+
rem.xor_in2out(&self.key[..rem.len()]);
83+
}
84+
85+
if tag == exp_tag {
86+
Ok(())
87+
} else {
88+
buffer.get_out().fill(0);
89+
Err(Error)
90+
}
91+
}
92+
}
93+
94+
struct PrefixDummyAead(DummyAead);
95+
96+
impl KeySizeUser for PrefixDummyAead {
97+
type KeySize = U8;
98+
}
99+
100+
impl KeyInit for PrefixDummyAead {
101+
fn new(key: &Key<Self>) -> Self {
102+
Self(DummyAead { key: key.0 })
103+
}
104+
}
105+
106+
impl AeadCore for PrefixDummyAead {
107+
type NonceSize = U8;
108+
type TagSize = U8;
109+
const TAG_POSITION: TagPosition = TagPosition::Prefix;
110+
}
111+
112+
impl AeadInOut for PrefixDummyAead {
113+
fn encrypt_inout_detached(
114+
&self,
115+
nonce: &Nonce<Self>,
116+
aad: &[u8],
117+
buffer: InOutBuf<'_, '_, u8>,
118+
) -> Result<Tag<Self>> {
119+
self.0.encrypt_inner(nonce.into(), aad, buffer).map(Array)
120+
}
121+
122+
fn decrypt_inout_detached(
123+
&self,
124+
nonce: &Nonce<Self>,
125+
aad: &[u8],
126+
buffer: InOutBuf<'_, '_, u8>,
127+
tag: &Tag<Self>,
128+
) -> Result<()> {
129+
self.0.decrypt_inner(nonce.into(), aad, buffer, tag.into())
130+
}
131+
}
132+
133+
struct PostfixDummyAead(DummyAead);
134+
135+
impl KeySizeUser for PostfixDummyAead {
136+
type KeySize = U8;
137+
}
138+
139+
impl KeyInit for PostfixDummyAead {
140+
fn new(key: &Key<Self>) -> Self {
141+
Self(DummyAead { key: key.0 })
142+
}
143+
}
144+
145+
impl AeadCore for PostfixDummyAead {
146+
type NonceSize = U8;
147+
type TagSize = U8;
148+
const TAG_POSITION: TagPosition = TagPosition::Postfix;
149+
}
150+
151+
impl AeadInOut for PostfixDummyAead {
152+
fn encrypt_inout_detached(
153+
&self,
154+
nonce: &Nonce<Self>,
155+
aad: &[u8],
156+
buffer: InOutBuf<'_, '_, u8>,
157+
) -> Result<Tag<Self>> {
158+
self.0.encrypt_inner(nonce.into(), aad, buffer).map(Array)
159+
}
160+
161+
fn decrypt_inout_detached(
162+
&self,
163+
nonce: &Nonce<Self>,
164+
aad: &[u8],
165+
buffer: InOutBuf<'_, '_, u8>,
166+
tag: &Tag<Self>,
167+
) -> Result<()> {
168+
self.0.decrypt_inner(nonce.into(), aad, buffer, tag.into())
169+
}
170+
}
171+
172+
#[cfg(feature = "dev")]
173+
aead::new_test!(dummy_prefix, "prefix", PrefixDummyAead);
174+
#[cfg(feature = "dev")]
175+
aead::new_test!(dummy_postfix, "postfix", PostfixDummyAead);

0 commit comments

Comments
 (0)