Skip to content

Commit ed13370

Browse files
authored
cipher: add dummy stream cipher tests (#2042)
1 parent d4d1818 commit ed13370

File tree

4 files changed

+178
-23
lines changed

4 files changed

+178
-23
lines changed

Cargo.lock

Lines changed: 15 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cipher/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ blobby = { version = "0.4.0-pre.1", optional = true }
2121
block-buffer = { version = "0.11.0-rc.5", optional = true }
2222
zeroize = { version = "1.8", optional = true, default-features = false }
2323

24+
[dev-dependencies]
25+
hex-literal = "1"
26+
2427
[features]
2528
alloc = []
2629
block-padding = ["inout/block-padding"]
84 Bytes
Binary file not shown.

cipher/tests/stream.rs

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
use cipher::{
2+
BlockSizeUser, IvSizeUser, KeyIvInit, KeySizeUser, ParBlocksSizeUser, StreamCipherBackend,
3+
StreamCipherClosure, StreamCipherCore, StreamCipherSeekCore,
4+
consts::{U1, U4, U16},
5+
};
6+
use hex_literal::hex;
7+
8+
const KEY: [u8; 4] = hex!("00010203");
9+
const IV: [u8; 4] = hex!("04050607");
10+
11+
/// Core of dummy insecure stream cipher.
12+
pub struct DummyStreamCipherCore {
13+
key_iv: u64,
14+
pos: u64,
15+
}
16+
17+
impl KeySizeUser for DummyStreamCipherCore {
18+
type KeySize = U4;
19+
}
20+
21+
impl IvSizeUser for DummyStreamCipherCore {
22+
type IvSize = U4;
23+
}
24+
25+
impl KeyIvInit for DummyStreamCipherCore {
26+
fn new(key: &cipher::Key<Self>, iv: &cipher::Iv<Self>) -> Self {
27+
let mut key_iv = [0u8; 8];
28+
key_iv[..4].copy_from_slice(key);
29+
key_iv[4..].copy_from_slice(iv);
30+
Self {
31+
key_iv: u64::from_le_bytes(key_iv),
32+
pos: 0,
33+
}
34+
}
35+
}
36+
37+
impl BlockSizeUser for DummyStreamCipherCore {
38+
type BlockSize = U16;
39+
}
40+
41+
impl StreamCipherCore for DummyStreamCipherCore {
42+
fn remaining_blocks(&self) -> Option<usize> {
43+
let rem = u64::MAX - self.pos;
44+
usize::try_from(rem).ok()
45+
}
46+
47+
fn process_with_backend(&mut self, f: impl StreamCipherClosure<BlockSize = U16>) {
48+
f.call(self);
49+
}
50+
}
51+
52+
impl ParBlocksSizeUser for DummyStreamCipherCore {
53+
type ParBlocksSize = U1;
54+
}
55+
56+
impl StreamCipherBackend for DummyStreamCipherCore {
57+
fn gen_ks_block(&mut self, block: &mut cipher::Block<Self>) {
58+
const C1: u64 = 0x87c3_7b91_1142_53d5;
59+
const C2: u64 = 0x4cf5_ad43_2745_937f;
60+
61+
let a = self.key_iv ^ C1;
62+
let b = self.pos ^ C2;
63+
let a = a.rotate_left(13).wrapping_mul(b);
64+
let b = b.rotate_left(13).wrapping_mul(a);
65+
66+
block[..8].copy_from_slice(&a.to_le_bytes());
67+
block[8..].copy_from_slice(&b.to_le_bytes());
68+
self.pos = self.pos.wrapping_add(1);
69+
}
70+
}
71+
72+
impl StreamCipherSeekCore for DummyStreamCipherCore {
73+
type Counter = u64;
74+
75+
fn get_block_pos(&self) -> Self::Counter {
76+
self.pos
77+
}
78+
79+
fn set_block_pos(&mut self, pos: Self::Counter) {
80+
self.pos = pos;
81+
}
82+
}
83+
84+
#[test]
85+
fn dummy_stream_cipher_core() {
86+
let mut cipher = DummyStreamCipherCore::new(&KEY.into(), &IV.into());
87+
assert_eq!(cipher.get_block_pos(), 0);
88+
89+
let mut block = [0u8; 16].into();
90+
cipher.write_keystream_block(&mut block);
91+
assert_eq!(block, hex!("e82393543cc96089305116003a417acc"));
92+
assert_eq!(cipher.get_block_pos(), 1);
93+
94+
cipher.set_block_pos(200);
95+
assert_eq!(cipher.get_block_pos(), 200);
96+
97+
cipher.write_keystream_block(&mut block);
98+
assert_eq!(block, hex!("28a96998fe4874ffb0ce9b046c6a9ddb"));
99+
assert_eq!(cipher.get_block_pos(), 201);
100+
}
101+
102+
#[cfg(feature = "stream-wrapper")]
103+
mod wrapper {
104+
use super::*;
105+
use cipher::{StreamCipher, StreamCipherCoreWrapper, StreamCipherSeek};
106+
107+
/// Dummy insecure stream cipher.
108+
pub type DummyStreamCipher = StreamCipherCoreWrapper<DummyStreamCipherCore>;
109+
110+
#[test]
111+
fn dummy_stream_cipher_basic() {
112+
let mut cipher = DummyStreamCipher::new(&KEY.into(), &IV.into());
113+
assert_eq!(cipher.current_pos::<u64>(), 0);
114+
115+
let mut buf = [0u8; 20];
116+
cipher.apply_keystream(&mut buf);
117+
assert_eq!(buf, hex!("e82393543cc96089305116003a417accd073384a"));
118+
assert_eq!(cipher.current_pos::<usize>(), buf.len());
119+
120+
const SEEK_POS: usize = 500;
121+
cipher.seek(SEEK_POS);
122+
cipher.apply_keystream(&mut buf);
123+
assert_eq!(buf, hex!("6b014c6a3c376b13c4720590d26147c5ebf334c5"));
124+
assert_eq!(cipher.current_pos::<usize>(), SEEK_POS + buf.len());
125+
}
126+
127+
#[test]
128+
fn dummy_stream_cipher_seek_limit() {
129+
let mut cipher = DummyStreamCipher::new(&KEY.into(), &IV.into());
130+
131+
let pos = ((u64::MAX as u128) << 4) - 20;
132+
cipher.try_seek(pos).unwrap();
133+
134+
let mut buf = [0u8; 30];
135+
let res = cipher.try_apply_keystream(&mut buf);
136+
assert!(res.is_err());
137+
let cur_pos: u128 = cipher.current_pos();
138+
assert_eq!(cur_pos, pos);
139+
140+
let res = cipher.try_apply_keystream(&mut buf[..19]);
141+
assert!(res.is_ok());
142+
let cur_pos: u128 = cipher.current_pos();
143+
assert_eq!(cur_pos, pos + 19);
144+
145+
cipher.try_seek(pos).unwrap();
146+
147+
// TODO: fix as part of https://github.com/RustCrypto/traits/issues/1808
148+
// let res = cipher.try_apply_keystream(&mut buf[..20]);
149+
// assert!(res.is_err());
150+
}
151+
152+
#[cfg(feature = "dev")]
153+
cipher::stream_cipher_test!(
154+
dummy_stream_cipher,
155+
"dummy_stream_cipher",
156+
DummyStreamCipher,
157+
);
158+
#[cfg(feature = "dev")]
159+
cipher::stream_cipher_seek_test!(dummy_stream_cipher_seek, DummyStreamCipher);
160+
}

0 commit comments

Comments
 (0)