Skip to content

Commit bfc9171

Browse files
committed
Draft of streaming API
Expand API; improve test structure
1 parent c1181b3 commit bfc9171

File tree

4 files changed

+379
-7
lines changed

4 files changed

+379
-7
lines changed

aws-lc-rs/src/cipher.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ pub(crate) mod block;
142142
pub(crate) mod chacha;
143143
pub(crate) mod key;
144144
mod padded;
145+
mod stream;
145146

146147
pub use padded::{PaddedBlockDecryptingKey, PaddedBlockEncryptingKey};
147148

aws-lc-rs/src/cipher/stream.rs

Lines changed: 371 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,371 @@
1+
use crate::cipher::{
2+
Algorithm, DecryptionContext, EncryptionContext, OperatingMode, UnboundCipherKey,
3+
};
4+
use crate::error::Unspecified;
5+
use crate::ptr::{LcPtr, Pointer};
6+
use aws_lc::{
7+
EVP_CIPHER_CTX_new, EVP_CIPHER_iv_length, EVP_CIPHER_key_length, EVP_DecryptFinal_ex,
8+
EVP_DecryptInit_ex, EVP_DecryptUpdate, EVP_EncryptFinal_ex, EVP_EncryptInit_ex,
9+
EVP_EncryptUpdate, EVP_CIPHER_CTX,
10+
};
11+
use std::ptr::null_mut;
12+
13+
pub struct StreamingEncryptingKey {
14+
algorithm: &'static Algorithm,
15+
mode: OperatingMode,
16+
cipher_ctx: LcPtr<EVP_CIPHER_CTX>,
17+
context: EncryptionContext,
18+
}
19+
20+
impl StreamingEncryptingKey {
21+
fn new(
22+
key: UnboundCipherKey,
23+
mode: OperatingMode,
24+
context: EncryptionContext,
25+
) -> Result<Self, Unspecified> {
26+
let algorithm = key.algorithm();
27+
let cipher_ctx = LcPtr::new(unsafe { EVP_CIPHER_CTX_new() })?;
28+
let cipher = mode.evp_cipher(key.algorithm);
29+
let key_bytes = key.key_bytes.as_ref();
30+
debug_assert_eq!(
31+
key_bytes.len(),
32+
<usize>::try_from(unsafe { EVP_CIPHER_key_length(*cipher) }).unwrap()
33+
);
34+
let iv = <&[u8]>::try_from(&context)?;
35+
debug_assert_eq!(
36+
iv.len(),
37+
<usize>::try_from(unsafe { EVP_CIPHER_iv_length(*cipher) }).unwrap()
38+
);
39+
40+
if 1 != unsafe {
41+
EVP_EncryptInit_ex(
42+
cipher_ctx.as_mut_ptr(),
43+
*cipher,
44+
null_mut(),
45+
key_bytes.as_ptr(),
46+
iv.as_ptr(),
47+
)
48+
} {
49+
return Err(Unspecified);
50+
}
51+
52+
Ok(StreamingEncryptingKey {
53+
algorithm,
54+
mode,
55+
cipher_ctx,
56+
context,
57+
})
58+
}
59+
60+
pub fn cbc_pkcs7(key: UnboundCipherKey) -> Result<Self, Unspecified> {
61+
let context = key.algorithm().new_encryption_context(OperatingMode::CBC)?;
62+
Self::less_safe_cbc_pkcs7(key, context)
63+
}
64+
65+
pub fn less_safe_cbc_pkcs7(
66+
key: UnboundCipherKey,
67+
context: EncryptionContext,
68+
) -> Result<Self, Unspecified> {
69+
StreamingEncryptingKey::new(key, OperatingMode::CBC, context)
70+
}
71+
72+
pub fn ctr(key: UnboundCipherKey) -> Result<Self, Unspecified> {
73+
let context = key.algorithm().new_encryption_context(OperatingMode::CTR)?;
74+
Self::less_safe_ctr(key, context)
75+
}
76+
77+
pub fn less_safe_ctr(
78+
key: UnboundCipherKey,
79+
context: EncryptionContext,
80+
) -> Result<Self, Unspecified> {
81+
StreamingEncryptingKey::new(key, OperatingMode::CTR, context)
82+
}
83+
84+
/// Returns the cipher operating mode.
85+
#[must_use]
86+
pub fn mode(&self) -> OperatingMode {
87+
self.mode
88+
}
89+
90+
#[must_use]
91+
pub fn algorithm(&self) -> &'static Algorithm {
92+
self.algorithm
93+
}
94+
95+
pub fn update<'a>(&self, input: &[u8], output: &'a mut [u8]) -> Result<usize, Unspecified> {
96+
if output.len() < (input.len() + self.algorithm.block_len) {
97+
return Err(Unspecified);
98+
}
99+
100+
let mut outlen: i32 = output.len().try_into()?;
101+
let inlen: i32 = input.len().try_into()?;
102+
if 1 != unsafe {
103+
EVP_EncryptUpdate(
104+
*self.cipher_ctx,
105+
output.as_mut_ptr(),
106+
&mut outlen,
107+
input.as_ptr(),
108+
inlen,
109+
)
110+
} {
111+
return Err(Unspecified);
112+
}
113+
let outlen: usize = outlen.try_into()?;
114+
Ok(outlen)
115+
}
116+
117+
pub fn finish(self, output: &mut [u8]) -> Result<(DecryptionContext, usize), Unspecified> {
118+
if output.len() < self.algorithm.block_len {
119+
return Err(Unspecified);
120+
}
121+
let mut outlen: i32 = output.len().try_into()?;
122+
if 1 != unsafe { EVP_EncryptFinal_ex(*self.cipher_ctx, output.as_mut_ptr(), &mut outlen) } {
123+
return Err(Unspecified);
124+
}
125+
let outlen: usize = outlen.try_into()?;
126+
Ok((self.context.into(), outlen))
127+
}
128+
}
129+
130+
pub struct StreamingDecryptingKey {
131+
algorithm: &'static Algorithm,
132+
mode: OperatingMode,
133+
cipher_ctx: LcPtr<EVP_CIPHER_CTX>,
134+
}
135+
impl StreamingDecryptingKey {
136+
fn new(
137+
key: UnboundCipherKey,
138+
mode: OperatingMode,
139+
context: DecryptionContext,
140+
) -> Result<Self, Unspecified> {
141+
let cipher_ctx = LcPtr::new(unsafe { EVP_CIPHER_CTX_new() })?;
142+
let algorithm = key.algorithm();
143+
let cipher = mode.evp_cipher(key.algorithm);
144+
let key_bytes = key.key_bytes.as_ref();
145+
debug_assert_eq!(
146+
key_bytes.len(),
147+
<usize>::try_from(unsafe { EVP_CIPHER_key_length(*cipher) }).unwrap()
148+
);
149+
let iv = <&[u8]>::try_from(&context)?;
150+
debug_assert_eq!(
151+
iv.len(),
152+
<usize>::try_from(unsafe { EVP_CIPHER_iv_length(*cipher) }).unwrap()
153+
);
154+
155+
if 1 != unsafe {
156+
EVP_DecryptInit_ex(
157+
cipher_ctx.as_mut_ptr(),
158+
*cipher,
159+
null_mut(),
160+
key_bytes.as_ptr(),
161+
iv.as_ptr(),
162+
)
163+
} {
164+
return Err(Unspecified);
165+
}
166+
167+
Ok(StreamingDecryptingKey {
168+
algorithm,
169+
mode,
170+
cipher_ctx,
171+
})
172+
}
173+
174+
pub fn cbc_pkcs7(
175+
key: UnboundCipherKey,
176+
context: DecryptionContext,
177+
) -> Result<Self, Unspecified> {
178+
StreamingDecryptingKey::new(key, OperatingMode::CBC, context)
179+
}
180+
181+
pub fn ctr(key: UnboundCipherKey, context: DecryptionContext) -> Result<Self, Unspecified> {
182+
StreamingDecryptingKey::new(key, OperatingMode::CTR, context)
183+
}
184+
185+
#[must_use]
186+
pub fn algorithm(&self) -> &'static Algorithm {
187+
self.algorithm
188+
}
189+
190+
/// Returns the cipher operating mode.
191+
#[must_use]
192+
pub fn mode(&self) -> OperatingMode {
193+
self.mode
194+
}
195+
196+
pub fn update(&self, input: &[u8], output: &mut [u8]) -> Result<usize, Unspecified> {
197+
if output.len() < (input.len() + self.algorithm.block_len) {
198+
return Err(Unspecified);
199+
}
200+
201+
let mut outlen: i32 = output.len().try_into()?;
202+
let inlen: i32 = input.len().try_into()?;
203+
if 1 != unsafe {
204+
EVP_DecryptUpdate(
205+
*self.cipher_ctx,
206+
output.as_mut_ptr(),
207+
&mut outlen,
208+
input.as_ptr(),
209+
inlen,
210+
)
211+
} {
212+
return Err(Unspecified);
213+
}
214+
let outlen: usize = outlen.try_into()?;
215+
Ok(outlen)
216+
}
217+
218+
pub fn finish(self, output: &mut [u8]) -> Result<usize, Unspecified> {
219+
let mut outlen: i32 = output.len().try_into()?;
220+
if 1 != unsafe { EVP_DecryptFinal_ex(*self.cipher_ctx, output.as_mut_ptr(), &mut outlen) } {
221+
return Err(Unspecified);
222+
}
223+
Ok(outlen.try_into()?)
224+
}
225+
}
226+
227+
#[cfg(test)]
228+
mod tests {
229+
use crate::cipher::stream::{StreamingDecryptingKey, StreamingEncryptingKey};
230+
use crate::cipher::{Algorithm, DecryptionContext, UnboundCipherKey, AES_256, AES_256_KEY_LEN};
231+
use crate::rand::{SecureRandom, SystemRandom};
232+
use paste::*;
233+
234+
fn step_encrypt(
235+
encrypting_key: StreamingEncryptingKey,
236+
plaintext: &[u8],
237+
step: usize,
238+
) -> (Box<[u8]>, DecryptionContext) {
239+
let alg = encrypting_key.algorithm();
240+
let mode = encrypting_key.mode();
241+
let n = plaintext.len();
242+
let mut ciphertext = vec![0u8; n + alg.block_len()];
243+
244+
let mut in_idx: usize = 0;
245+
let mut out_idx: usize = 0;
246+
loop {
247+
let mut in_end = in_idx + step;
248+
if in_end > n {
249+
in_end = n;
250+
}
251+
let out_end = out_idx + (in_end - in_idx) + alg.block_len();
252+
let outlen = encrypting_key
253+
.update(
254+
&plaintext[in_idx..in_end],
255+
&mut ciphertext[out_idx..out_end],
256+
)
257+
.unwrap();
258+
in_idx += step;
259+
out_idx += outlen;
260+
if in_idx >= n {
261+
break;
262+
}
263+
}
264+
let out_end = out_idx + alg.block_len();
265+
let (decrypt_iv, outlen) = encrypting_key
266+
.finish(&mut ciphertext[out_idx..out_end])
267+
.unwrap();
268+
269+
ciphertext.truncate(out_idx + outlen);
270+
(ciphertext.into_boxed_slice(), decrypt_iv)
271+
}
272+
273+
fn step_decrypt(
274+
decrypting_key: StreamingDecryptingKey,
275+
ciphertext: &[u8],
276+
step: usize,
277+
) -> Box<[u8]> {
278+
let alg = decrypting_key.algorithm();
279+
let n = ciphertext.len();
280+
let mut output = vec![0u8; n + alg.block_len()];
281+
282+
let mut in_idx: usize = 0;
283+
let mut out_idx: usize = 0;
284+
loop {
285+
let mut in_end = in_idx + step;
286+
if in_end > n {
287+
in_end = n;
288+
}
289+
let out_end = out_idx + (in_end - in_idx) + alg.block_len();
290+
let outlen = decrypting_key
291+
.update(&ciphertext[in_idx..in_end], &mut output[out_idx..out_end])
292+
.unwrap();
293+
in_idx += step;
294+
out_idx += outlen;
295+
if in_idx >= n {
296+
break;
297+
}
298+
}
299+
let out_end = out_idx + alg.block_len();
300+
let outlen = decrypting_key
301+
.finish(&mut output[out_idx..out_end])
302+
.unwrap();
303+
304+
output.truncate(out_idx + outlen);
305+
306+
output.into_boxed_slice()
307+
}
308+
309+
macro_rules! helper_stream_step_encrypt_test {
310+
($mode:ident) => {
311+
paste! {
312+
fn [<helper_test_ $mode _stream_encrypt_step_n_bytes>](
313+
key: &[u8],
314+
alg: &'static Algorithm,
315+
n: usize,
316+
step: usize,
317+
) {
318+
let mut input = vec![0u8; n];
319+
let random = SystemRandom::new();
320+
random.fill(&mut input).unwrap();
321+
322+
let cipher_key = UnboundCipherKey::new(alg, key).unwrap();
323+
let encrypting_key = StreamingEncryptingKey::$mode(cipher_key).unwrap();
324+
325+
let (ciphertext, decrypt_iv) = step_encrypt(encrypting_key, &input, step);
326+
327+
let cipher_key2 = UnboundCipherKey::new(alg, key).unwrap();
328+
let decrypting_key = StreamingDecryptingKey::$mode(cipher_key2, decrypt_iv).unwrap();
329+
330+
let plaintext = step_decrypt(decrypting_key, &ciphertext, step);
331+
332+
assert_eq!(input.as_slice(), &*plaintext);
333+
}
334+
}
335+
};
336+
}
337+
338+
helper_stream_step_encrypt_test!(cbc_pkcs7);
339+
helper_stream_step_encrypt_test!(ctr);
340+
341+
#[test]
342+
fn test_step_cbc() {
343+
let random = SystemRandom::new();
344+
let mut key = [0u8; AES_256_KEY_LEN];
345+
random.fill(&mut key).unwrap();
346+
347+
for i in 13..=21 {
348+
for j in 124..=131 {
349+
let _ = helper_test_cbc_pkcs7_stream_encrypt_step_n_bytes(&key, &AES_256, j, i);
350+
}
351+
for j in 124..=131 {
352+
let _ = helper_test_cbc_pkcs7_stream_encrypt_step_n_bytes(&key, &AES_256, j, j - i);
353+
}
354+
}
355+
}
356+
357+
#[test]
358+
fn test_step_ctr() {
359+
let random = SystemRandom::new();
360+
let mut key = [0u8; AES_256_KEY_LEN];
361+
random.fill(&mut key).unwrap();
362+
for i in 13..=21 {
363+
for j in 124..=131 {
364+
let _ = helper_test_ctr_stream_encrypt_step_n_bytes(&key, &AES_256, j, i);
365+
}
366+
for j in 124..=131 {
367+
let _ = helper_test_ctr_stream_encrypt_step_n_bytes(&key, &AES_256, j, j - i);
368+
}
369+
}
370+
}
371+
}

aws-lc-rs/src/iv.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,9 @@ impl<const L: usize> TryFrom<&[u8]> for FixedLength<L> {
7272
}
7373
}
7474

75-
impl<const L: usize> TryFrom<FixedLength<L>> for [u8; L] {
76-
type Error = Unspecified;
77-
78-
fn try_from(value: FixedLength<L>) -> Result<Self, Self::Error> {
79-
Ok(value.0)
75+
impl<const L: usize> From<FixedLength<L>> for [u8; L] {
76+
fn from(value: FixedLength<L>) -> Self {
77+
value.0
8078
}
8179
}
8280

0 commit comments

Comments
 (0)