[go: up one dir, main page]

aead/
stream.rs

1//! Streaming AEAD support.
2//!
3//! Implementation of the STREAM online authenticated encryption construction
4//! as described in the paper
5//! [Online Authenticated-Encryption and its Nonce-Reuse Misuse-Resistance][1].
6//!
7//! ## About
8//!
9//! The STREAM construction supports encrypting/decrypting sequences of AEAD
10//! message segments, which is useful in cases where the overall message is too
11//! large to fit in a single buffer and needs to be processed incrementally.
12//!
13//! STREAM defends against reordering and truncation attacks which are common
14//! in naive schemes which attempt to provide these properties, and is proven
15//! to meet the security definition of "nonce-based online authenticated
16//! encryption" (nOAE) as given in the aforementioned paper.
17//!
18//! ## Diagram
19//!
20//! ![STREAM Diagram](https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/img/AEADs/rogaway-stream.svg)
21//!
22//! Legend:
23//!
24//! - 𝐄k: AEAD encryption under key `k`
25//! - 𝐌: message
26//! - 𝐍: nonce
27//! - 𝐀: additional associated data
28//! - 𝐂: ciphertext
29//! - 𝜏: MAC tag
30//!
31//! [1]: https://eprint.iacr.org/2015/189.pdf
32
33#![allow(clippy::upper_case_acronyms)]
34
35use crate::{AeadCore, AeadInPlace, Buffer, Error, Key, KeyInit, Result};
36use core::ops::{AddAssign, Sub};
37use generic_array::{
38    typenum::{Unsigned, U4, U5},
39    ArrayLength, GenericArray,
40};
41
42#[cfg(feature = "alloc")]
43use {crate::Payload, alloc::vec::Vec};
44
45/// Nonce as used by a given AEAD construction and STREAM primitive.
46pub type Nonce<A, S> = GenericArray<u8, NonceSize<A, S>>;
47
48/// Size of a nonce as used by a STREAM construction, sans the overhead of
49/// the STREAM protocol itself.
50pub type NonceSize<A, S> =
51    <<A as AeadCore>::NonceSize as Sub<<S as StreamPrimitive<A>>::NonceOverhead>>::Output;
52
53/// STREAM encryptor instantiated with [`StreamBE32`] as the underlying
54/// STREAM primitive.
55pub type EncryptorBE32<A> = Encryptor<A, StreamBE32<A>>;
56
57/// STREAM decryptor instantiated with [`StreamBE32`] as the underlying
58/// STREAM primitive.
59pub type DecryptorBE32<A> = Decryptor<A, StreamBE32<A>>;
60
61/// STREAM encryptor instantiated with [`StreamLE31`] as the underlying
62/// STREAM primitive.
63pub type EncryptorLE31<A> = Encryptor<A, StreamLE31<A>>;
64
65/// STREAM decryptor instantiated with [`StreamLE31`] as the underlying
66/// STREAM primitive.
67pub type DecryptorLE31<A> = Decryptor<A, StreamLE31<A>>;
68
69/// Create a new STREAM from the provided AEAD.
70pub trait NewStream<A>: StreamPrimitive<A>
71where
72    A: AeadInPlace,
73    A::NonceSize: Sub<Self::NonceOverhead>,
74    NonceSize<A, Self>: ArrayLength<u8>,
75{
76    /// Create a new STREAM with the given key and nonce.
77    fn new(key: &Key<A>, nonce: &Nonce<A, Self>) -> Self
78    where
79        A: KeyInit,
80        Self: Sized,
81    {
82        Self::from_aead(A::new(key), nonce)
83    }
84
85    /// Create a new STREAM from the given AEAD cipher.
86    fn from_aead(aead: A, nonce: &Nonce<A, Self>) -> Self;
87}
88
89/// Low-level STREAM implementation.
90///
91/// This trait provides a particular "flavor" of STREAM, as there are
92/// different ways the specifics of the construction can be implemented.
93///
94/// Deliberately immutable and stateless to permit parallel operation.
95pub trait StreamPrimitive<A>
96where
97    A: AeadInPlace,
98    A::NonceSize: Sub<Self::NonceOverhead>,
99    NonceSize<A, Self>: ArrayLength<u8>,
100{
101    /// Number of bytes this STREAM primitive requires from the nonce.
102    type NonceOverhead: ArrayLength<u8>;
103
104    /// Type used as the STREAM counter.
105    type Counter: AddAssign + Copy + Default + Eq;
106
107    /// Value to use when incrementing the STREAM counter (i.e. one)
108    const COUNTER_INCR: Self::Counter;
109
110    /// Maximum value of the STREAM counter.
111    const COUNTER_MAX: Self::Counter;
112
113    /// Encrypt an AEAD message in-place at the given position in the STREAM.
114    fn encrypt_in_place(
115        &self,
116        position: Self::Counter,
117        last_block: bool,
118        associated_data: &[u8],
119        buffer: &mut dyn Buffer,
120    ) -> Result<()>;
121
122    /// Decrypt an AEAD message in-place at the given position in the STREAM.
123    fn decrypt_in_place(
124        &self,
125        position: Self::Counter,
126        last_block: bool,
127        associated_data: &[u8],
128        buffer: &mut dyn Buffer,
129    ) -> Result<()>;
130
131    /// Encrypt the given plaintext payload, and return the resulting
132    /// ciphertext as a vector of bytes.
133    #[cfg(feature = "alloc")]
134    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
135    fn encrypt<'msg, 'aad>(
136        &self,
137        position: Self::Counter,
138        last_block: bool,
139        plaintext: impl Into<Payload<'msg, 'aad>>,
140    ) -> Result<Vec<u8>> {
141        let payload = plaintext.into();
142        let mut buffer = Vec::with_capacity(payload.msg.len() + A::TagSize::to_usize());
143        buffer.extend_from_slice(payload.msg);
144        self.encrypt_in_place(position, last_block, payload.aad, &mut buffer)?;
145        Ok(buffer)
146    }
147
148    /// Decrypt the given ciphertext slice, and return the resulting plaintext
149    /// as a vector of bytes.
150    #[cfg(feature = "alloc")]
151    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
152    fn decrypt<'msg, 'aad>(
153        &self,
154        position: Self::Counter,
155        last_block: bool,
156        ciphertext: impl Into<Payload<'msg, 'aad>>,
157    ) -> Result<Vec<u8>> {
158        let payload = ciphertext.into();
159        let mut buffer = Vec::from(payload.msg);
160        self.decrypt_in_place(position, last_block, payload.aad, &mut buffer)?;
161        Ok(buffer)
162    }
163
164    /// Obtain [`Encryptor`] for this [`StreamPrimitive`].
165    fn encryptor(self) -> Encryptor<A, Self>
166    where
167        Self: Sized,
168    {
169        Encryptor::from_stream_primitive(self)
170    }
171
172    /// Obtain [`Decryptor`] for this [`StreamPrimitive`].
173    fn decryptor(self) -> Decryptor<A, Self>
174    where
175        Self: Sized,
176    {
177        Decryptor::from_stream_primitive(self)
178    }
179}
180
181/// Implement a stateful STREAM object (i.e. encryptor or decryptor)
182macro_rules! impl_stream_object {
183    (
184        $name:ident,
185        $next_method:tt,
186        $next_in_place_method:tt,
187        $last_method:tt,
188        $last_in_place_method:tt,
189        $op:tt,
190        $in_place_op:tt,
191        $op_desc:expr,
192        $obj_desc:expr
193    ) => {
194        #[doc = "Stateful STREAM object which can"]
195        #[doc = $op_desc]
196        #[doc = "AEAD messages one-at-a-time."]
197        #[doc = ""]
198        #[doc = "This corresponds to the "]
199        #[doc = $obj_desc]
200        #[doc = "object as defined in the paper"]
201        #[doc = "[Online Authenticated-Encryption and its Nonce-Reuse Misuse-Resistance][1]."]
202        #[doc = ""]
203        #[doc = "[1]: https://eprint.iacr.org/2015/189.pdf"]
204        pub struct $name<A, S>
205        where
206            A: AeadInPlace,
207            S: StreamPrimitive<A>,
208            A::NonceSize: Sub<<S as StreamPrimitive<A>>::NonceOverhead>,
209            NonceSize<A, S>: ArrayLength<u8>,
210        {
211            /// Underlying STREAM primitive.
212            stream: S,
213
214            /// Current position in the STREAM.
215            position: S::Counter,
216        }
217
218        impl<A, S> $name<A, S>
219        where
220            A: AeadInPlace,
221            S: StreamPrimitive<A>,
222            A::NonceSize: Sub<<S as StreamPrimitive<A>>::NonceOverhead>,
223            NonceSize<A, S>: ArrayLength<u8>,
224        {
225            #[doc = "Create a"]
226            #[doc = $obj_desc]
227            #[doc = "object from the given AEAD key and nonce."]
228            pub fn new(key: &Key<A>, nonce: &Nonce<A, S>) -> Self
229            where
230                A: KeyInit,
231                S: NewStream<A>,
232            {
233                Self::from_stream_primitive(S::new(key, nonce))
234            }
235
236            #[doc = "Create a"]
237            #[doc = $obj_desc]
238            #[doc = "object from the given AEAD primitive."]
239            pub fn from_aead(aead: A, nonce: &Nonce<A, S>) -> Self
240            where
241                A: KeyInit,
242                S: NewStream<A>,
243            {
244                Self::from_stream_primitive(S::from_aead(aead, nonce))
245            }
246
247            #[doc = "Create a"]
248            #[doc = $obj_desc]
249            #[doc = "object from the given STREAM primitive."]
250            pub fn from_stream_primitive(stream: S) -> Self {
251                Self {
252                    stream,
253                    position: Default::default(),
254                }
255            }
256
257            #[doc = "Use the underlying AEAD to"]
258            #[doc = $op_desc]
259            #[doc = "the next AEAD message in this STREAM, returning the"]
260            #[doc = "result as a [`Vec`]."]
261            #[cfg(feature = "alloc")]
262            #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
263            pub fn $next_method<'msg, 'aad>(
264                &mut self,
265                payload: impl Into<Payload<'msg, 'aad>>,
266            ) -> Result<Vec<u8>> {
267                if self.position == S::COUNTER_MAX {
268                    // Counter overflow. Note that the maximum counter value is
269                    // deliberately disallowed, as it would preclude being able
270                    // to encrypt a last block (i.e. with `$last_in_place_method`)
271                    return Err(Error);
272                }
273
274                let result = self.stream.$op(self.position, false, payload)?;
275
276                // Note: overflow checked above
277                self.position += S::COUNTER_INCR;
278                Ok(result)
279            }
280
281            #[doc = "Use the underlying AEAD to"]
282            #[doc = $op_desc]
283            #[doc = "the next AEAD message in this STREAM in-place."]
284            pub fn $next_in_place_method(
285                &mut self,
286                associated_data: &[u8],
287                buffer: &mut dyn Buffer,
288            ) -> Result<()> {
289                if self.position == S::COUNTER_MAX {
290                    // Counter overflow. Note that the maximum counter value is
291                    // deliberately disallowed, as it would preclude being able
292                    // to encrypt a last block (i.e. with `$last_in_place_method`)
293                    return Err(Error);
294                }
295
296                self.stream
297                    .$in_place_op(self.position, false, associated_data, buffer)?;
298
299                // Note: overflow checked above
300                self.position += S::COUNTER_INCR;
301                Ok(())
302            }
303
304            #[doc = "Use the underlying AEAD to"]
305            #[doc = $op_desc]
306            #[doc = "the last AEAD message in this STREAM,"]
307            #[doc = "consuming the "]
308            #[doc = $obj_desc]
309            #[doc = "object in order to prevent further use."]
310            #[cfg(feature = "alloc")]
311            #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
312            pub fn $last_method<'msg, 'aad>(
313                self,
314                payload: impl Into<Payload<'msg, 'aad>>,
315            ) -> Result<Vec<u8>> {
316                self.stream.$op(self.position, true, payload)
317            }
318
319            #[doc = "Use the underlying AEAD to"]
320            #[doc = $op_desc]
321            #[doc = "the last AEAD message in this STREAM in-place,"]
322            #[doc = "consuming the "]
323            #[doc = $obj_desc]
324            #[doc = "object in order to prevent further use."]
325            pub fn $last_in_place_method(
326                self,
327                associated_data: &[u8],
328                buffer: &mut dyn Buffer,
329            ) -> Result<()> {
330                self.stream
331                    .$in_place_op(self.position, true, associated_data, buffer)
332            }
333        }
334    };
335}
336
337impl_stream_object!(
338    Encryptor,
339    encrypt_next,
340    encrypt_next_in_place,
341    encrypt_last,
342    encrypt_last_in_place,
343    encrypt,
344    encrypt_in_place,
345    "encrypt",
346    "ℰ STREAM encryptor"
347);
348
349impl_stream_object!(
350    Decryptor,
351    decrypt_next,
352    decrypt_next_in_place,
353    decrypt_last,
354    decrypt_last_in_place,
355    decrypt,
356    decrypt_in_place,
357    "decrypt",
358    "𝒟 STREAM decryptor"
359);
360
361/// The original "Rogaway-flavored" STREAM as described in the paper
362/// [Online Authenticated-Encryption and its Nonce-Reuse Misuse-Resistance][1].
363///
364/// Uses a 32-bit big endian counter and 1-byte "last block" flag stored as
365/// the last 5-bytes of the AEAD nonce.
366///
367/// [1]: https://eprint.iacr.org/2015/189.pdf
368pub struct StreamBE32<A>
369where
370    A: AeadInPlace,
371    A::NonceSize: Sub<U5>,
372    <<A as AeadCore>::NonceSize as Sub<U5>>::Output: ArrayLength<u8>,
373{
374    /// Underlying AEAD cipher
375    aead: A,
376
377    /// Nonce (sans STREAM overhead)
378    nonce: Nonce<A, Self>,
379}
380
381impl<A> NewStream<A> for StreamBE32<A>
382where
383    A: AeadInPlace,
384    A::NonceSize: Sub<U5>,
385    <<A as AeadCore>::NonceSize as Sub<U5>>::Output: ArrayLength<u8>,
386{
387    fn from_aead(aead: A, nonce: &Nonce<A, Self>) -> Self {
388        Self {
389            aead,
390            nonce: nonce.clone(),
391        }
392    }
393}
394
395impl<A> StreamPrimitive<A> for StreamBE32<A>
396where
397    A: AeadInPlace,
398    A::NonceSize: Sub<U5>,
399    <<A as AeadCore>::NonceSize as Sub<U5>>::Output: ArrayLength<u8>,
400{
401    type NonceOverhead = U5;
402    type Counter = u32;
403    const COUNTER_INCR: u32 = 1;
404    const COUNTER_MAX: u32 = core::u32::MAX;
405
406    fn encrypt_in_place(
407        &self,
408        position: u32,
409        last_block: bool,
410        associated_data: &[u8],
411        buffer: &mut dyn Buffer,
412    ) -> Result<()> {
413        let nonce = self.aead_nonce(position, last_block);
414        self.aead.encrypt_in_place(&nonce, associated_data, buffer)
415    }
416
417    fn decrypt_in_place(
418        &self,
419        position: Self::Counter,
420        last_block: bool,
421        associated_data: &[u8],
422        buffer: &mut dyn Buffer,
423    ) -> Result<()> {
424        let nonce = self.aead_nonce(position, last_block);
425        self.aead.decrypt_in_place(&nonce, associated_data, buffer)
426    }
427}
428
429impl<A> StreamBE32<A>
430where
431    A: AeadInPlace,
432    A::NonceSize: Sub<U5>,
433    <<A as AeadCore>::NonceSize as Sub<U5>>::Output: ArrayLength<u8>,
434{
435    /// Compute the full AEAD nonce including the STREAM counter and last
436    /// block flag.
437    fn aead_nonce(&self, position: u32, last_block: bool) -> crate::Nonce<A> {
438        let mut result = GenericArray::default();
439
440        // TODO(tarcieri): use `generic_array::sequence::Concat` (or const generics)
441        let (prefix, tail) = result.split_at_mut(NonceSize::<A, Self>::to_usize());
442        prefix.copy_from_slice(&self.nonce);
443
444        let (counter, flag) = tail.split_at_mut(4);
445        counter.copy_from_slice(&position.to_be_bytes());
446        flag[0] = last_block as u8;
447
448        result
449    }
450}
451
452/// STREAM as instantiated with a 31-bit little endian counter and 1-bit
453/// "last block" flag stored as the most significant bit of the counter
454/// when interpreted as a 32-bit integer.
455///
456/// The 31-bit + 1-bit value is stored as the last 4 bytes of the AEAD nonce.
457pub struct StreamLE31<A>
458where
459    A: AeadInPlace,
460    A::NonceSize: Sub<U4>,
461    <<A as AeadCore>::NonceSize as Sub<U4>>::Output: ArrayLength<u8>,
462{
463    /// Underlying AEAD cipher
464    aead: A,
465
466    /// Nonce (sans STREAM overhead)
467    nonce: Nonce<A, Self>,
468}
469
470impl<A> NewStream<A> for StreamLE31<A>
471where
472    A: AeadInPlace,
473    A::NonceSize: Sub<U4>,
474    <<A as AeadCore>::NonceSize as Sub<U4>>::Output: ArrayLength<u8>,
475{
476    fn from_aead(aead: A, nonce: &Nonce<A, Self>) -> Self {
477        Self {
478            aead,
479            nonce: nonce.clone(),
480        }
481    }
482}
483
484impl<A> StreamPrimitive<A> for StreamLE31<A>
485where
486    A: AeadInPlace,
487    A::NonceSize: Sub<U4>,
488    <<A as AeadCore>::NonceSize as Sub<U4>>::Output: ArrayLength<u8>,
489{
490    type NonceOverhead = U4;
491    type Counter = u32;
492    const COUNTER_INCR: u32 = 1;
493    const COUNTER_MAX: u32 = 0xfff_ffff;
494
495    fn encrypt_in_place(
496        &self,
497        position: u32,
498        last_block: bool,
499        associated_data: &[u8],
500        buffer: &mut dyn Buffer,
501    ) -> Result<()> {
502        let nonce = self.aead_nonce(position, last_block)?;
503        self.aead.encrypt_in_place(&nonce, associated_data, buffer)
504    }
505
506    fn decrypt_in_place(
507        &self,
508        position: Self::Counter,
509        last_block: bool,
510        associated_data: &[u8],
511        buffer: &mut dyn Buffer,
512    ) -> Result<()> {
513        let nonce = self.aead_nonce(position, last_block)?;
514        self.aead.decrypt_in_place(&nonce, associated_data, buffer)
515    }
516}
517
518impl<A> StreamLE31<A>
519where
520    A: AeadInPlace,
521    A::NonceSize: Sub<U4>,
522    <<A as AeadCore>::NonceSize as Sub<U4>>::Output: ArrayLength<u8>,
523{
524    /// Compute the full AEAD nonce including the STREAM counter and last
525    /// block flag.
526    fn aead_nonce(&self, position: u32, last_block: bool) -> Result<crate::Nonce<A>> {
527        if position > Self::COUNTER_MAX {
528            return Err(Error);
529        }
530
531        let mut result = GenericArray::default();
532
533        // TODO(tarcieri): use `generic_array::sequence::Concat` (or const generics)
534        let (prefix, tail) = result.split_at_mut(NonceSize::<A, Self>::to_usize());
535        prefix.copy_from_slice(&self.nonce);
536
537        let position_with_flag = position | ((last_block as u32) << 31);
538        tail.copy_from_slice(&position_with_flag.to_le_bytes());
539
540        Ok(result)
541    }
542}