wolf_crypto/chacha/
mod.rs

1//! The `ChaCha20` Stream Cipher
2
3mod key;
4pub mod states;
5
6pub use key::{Key, KeyRef, GenericKey};
7use core::fmt;
8
9use wolf_crypto_sys::{
10    ChaCha,
11    wc_Chacha_SetKey, wc_Chacha_SetIV,
12    wc_Chacha_Process
13};
14
15use core::marker::PhantomData;
16use core::mem::MaybeUninit;
17use core::ptr::addr_of_mut;
18use crate::buf::{GenericIv, U12};
19use crate::{can_cast_u32, const_can_cast_u32, lte, Unspecified};
20use states::{State, CanProcess, Init, NeedsIv, Ready, Streaming};
21
22macro_rules! impl_fmt {
23    ($(#[$meta:meta])* $trait:ident for $state:ident) => {
24        impl fmt::$trait for ChaCha20<$state> {
25            $(#[$meta])*
26            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
27                f.write_str(concat!("ChaCha20<", stringify!($state), "> { ... }"))
28            }
29        }
30    };
31    ($state:ident) => {
32        impl_fmt! { Debug for $state }
33        impl_fmt! {
34            #[inline]
35            Display for $state
36        }
37    };
38}
39
40/// The `ChaCha20` Stream Cipher
41///
42/// # Warning
43///
44/// `ChaCha20` alone does not ensure that the ciphertext is authentic, unless you have a reason
45/// for using this directly, it is generally recommended to use `ChaCha20-Poly1305`.
46///
47/// # Generic `S`
48///
49/// This `ChaCha20` implementation is implemented as a state machine, this is to better enforce
50/// best practices such as avoiding initialization vector reuse. The generic `S` represents the
51/// current state.
52///
53/// The state machine takes the following form:
54///
55/// ```text
56///      +----------+
57///      |   Init   |
58///      +----------+
59///        |
60///        |
61///        v
62///      +----------+      `finalize()`
63///   +> | Needs IV | <----------------------+
64///   |  +----------+                        |
65///   |    |                                 |
66///   |    |                                 |
67///   |    v                                 |
68///   |  +--------------------------+      +-----------+
69///   |  |                          |      |           | ---+
70///   |  |          Ready           |      | Streaming |    |
71///   |  |                          | ---> |           | <--+
72///   |  +--------------------------+      +-----------+
73///   |    |           ^    |
74///   |    |           |    |
75///   |    v           |    v
76///   |  +----------+  |  +---------+
77///   +- | Encrypt  |  +- | Decrypt |
78///      +----------+     +---------+
79/// ```
80///
81/// # Example
82///
83/// ```
84/// use wolf_crypto::chacha::ChaCha20;
85///
86/// let (output, mut chacha) = ChaCha20::new(&[7u8; 32])
87///     .set_iv(&[3u8; 12])
88///     .encrypt_exact(b"hello world")
89///     .unwrap();
90///
91/// let plaintext = chacha.set_iv(&[3u8; 12])
92///     .decrypt_exact(&output)
93///     .unwrap();
94///
95/// assert_eq!(b"hello world", &plaintext);
96/// ```
97#[repr(transparent)]
98pub struct ChaCha20<S: State = Init> {
99    inner: ChaCha,
100    _state: PhantomData<S>
101}
102
103impl ChaCha20<Init> {
104    /// Create a new `ChaCha20` instance.
105    ///
106    /// # Arguments
107    ///
108    /// * `key` - The 128-bit or 256-bit key material.
109    ///
110    /// # Returns
111    ///
112    /// A new `ChaCha20` instance in the [`NeedsIv`] state.
113    pub fn new<K: GenericKey>(key: K) -> ChaCha20<NeedsIv> {
114        let mut inner = MaybeUninit::<ChaCha>::uninit();
115
116        unsafe {
117            // Infallible, the GenericKey is sealed and only supports 128 or 256-bit keys, which
118            // are valid sizes. According to the docs this is the only way that this function
119            // can fail. Debug assert to build further confidence. This has been confirmed post
120            // reviewing the source code.
121            //
122            // See for yourself:
123            // https://github.com/wolfSSL/wolfssl/blob/master/wolfcrypt/src/chacha.c#L154
124            let _res = wc_Chacha_SetKey(
125                inner.as_mut_ptr(),
126                key.slice().as_ptr(),
127                key.size()
128            );
129            debug_assert_eq!(_res, 0);
130
131            Self::new_with(inner.assume_init())
132        }
133    }
134}
135
136impl<S: State> ChaCha20<S> {
137    #[inline]
138    #[must_use]
139    const fn new_with<NS: State>(inner: ChaCha) -> ChaCha20<NS> {
140        ChaCha20::<NS> {
141            inner,
142            _state: PhantomData
143        }
144    }
145}
146
147impl ChaCha20<NeedsIv> {
148    /// Set the initialization vector to use for the next [`Ready`] state.
149    ///
150    /// # Arguments
151    ///
152    /// * `iv` - The 96-bit initialization vector.
153    /// * `counter` - The value at which the block counter should start, generally zero.
154    ///
155    /// # Returns
156    ///
157    /// The `ChaCha20` instance in the [`Ready`] state.
158    pub fn set_iv_with_ctr<IV>(mut self, iv: IV, counter: u32) -> ChaCha20<Ready>
159        where IV: GenericIv<Size = U12>
160    {
161        // Infallible, see source:
162        // /**
163        //   * Set up iv(nonce). Earlier versions used 64 bits instead of 96, this version
164        //   * uses the typical AEAD 96 bit nonce and can do record sizes of 256 GB.
165        //   */
166        // int wc_Chacha_SetIV(ChaCha* ctx, const byte* inIv, word32 counter)
167        // {
168        //     word32 temp[CHACHA_IV_WORDS];/* used for alignment of memory */
169        //
170        //
171        //     if (ctx == NULL || inIv == NULL)
172        //         return BAD_FUNC_ARG;
173        //
174        //     XMEMCPY(temp, inIv, CHACHA_IV_BYTES);
175        //
176        //     ctx->left = 0; /* resets state */
177        //     ctx->X[CHACHA_MATRIX_CNT_IV+0] = counter;           /* block counter */
178        //     ctx->X[CHACHA_MATRIX_CNT_IV+1] = LITTLE32(temp[0]); /* fixed variable from nonce */
179        //     ctx->X[CHACHA_MATRIX_CNT_IV+2] = LITTLE32(temp[1]); /* counter from nonce */
180        //     ctx->X[CHACHA_MATRIX_CNT_IV+3] = LITTLE32(temp[2]); /* counter from nonce */
181        //
182        //     return 0;
183        // }
184        //
185        // https://github.com/wolfSSL/wolfssl/blob/master/wolfcrypt/src/chacha.c#L127
186        unsafe {
187            let _res = wc_Chacha_SetIV(
188                addr_of_mut!(self.inner),
189                iv.as_slice().as_ptr(),
190                counter
191            );
192            debug_assert_eq!(_res, 0);
193        }
194
195        Self::new_with(self.inner)
196    }
197
198    /// Set the initialization vector to use for the next [`Ready`] state.
199    ///
200    /// # Arguments
201    ///
202    /// * `iv` - The 96-bit initialization vector.
203    ///
204    /// # Returns
205    ///
206    /// The `ChaCha20` instance in the [`Ready`] state.
207    #[inline]
208    pub fn set_iv<IV: GenericIv<Size = U12>>(self, iv: IV) -> ChaCha20<Ready> {
209        self.set_iv_with_ctr(iv, 0)
210    }
211}
212
213impl_fmt! { NeedsIv }
214
215impl<S: CanProcess> ChaCha20<S> {
216    #[inline]
217    #[must_use]
218    const fn predicate(input_len: usize, output_len: usize) -> bool {
219        input_len <= output_len && can_cast_u32(input_len)
220    }
221
222    #[inline]
223    #[must_use]
224    const fn const_predicate<const I: usize, const O: usize>() -> bool {
225        I <= O && const_can_cast_u32::<I>()
226    }
227
228    /// Processes the input into the output buffer without checking lengths.
229    ///
230    /// # Safety
231    ///
232    /// This function is unsafe because it does not check if the input and output
233    /// lengths are valid. The caller must ensure that:
234    /// - The input length is less than or equal to the output length.
235    /// - The input length can be cast to a `u32` without overflow.
236    ///
237    /// # Arguments
238    ///
239    /// * `input` - The input slice to process.
240    /// * `output` - The output buffer to write the processed data into.
241    #[inline]
242    unsafe fn process_unchecked(&mut self, input: &[u8], output: &mut [u8]) {
243        debug_assert!(
244            Self::predicate(input.len(), output.len()),
245            "Process unchecked precondition violated (debug assertion). The size of the input must \
246            be less than or equal to the size of the output. The size of the input must also be \
247            representable as a `u32` without overflowing."
248        );
249
250        // INFALLIBLE (with preconditions respected, but this function is unsafe so caller's
251        // responsibility)
252        //
253        // -- ACTUAL PROCESSING:
254        // https://github.com/wolfSSL/wolfssl/blob/master/wolfcrypt/src/chacha.c#L267
255        //
256        // Returns void, so of course, no fallibility here (which makes sense).
257        //
258        // -- PROCESS FUNCTION
259        // https://github.com/wolfSSL/wolfssl/blob/master/wolfcrypt/src/chacha.c#L317
260        //
261        // The null checks at the start of wc_ChaCha_Process are the only fallible aspects, with
262        // the length corresponding to the input size and the output size being greater or eq to
263        // this length implied.
264        let _res = wc_Chacha_Process(
265            addr_of_mut!(self.inner),
266            output.as_mut_ptr(),
267            input.as_ptr(),
268            input.len() as u32
269        );
270
271        debug_assert_eq!(_res, 0);
272    }
273
274    /// Processes the input into the output buffer without checking lengths.
275    ///
276    /// # Safety
277    ///
278    /// This function is unsafe because it does not check if the input and output
279    /// lengths are valid. The caller must ensure that the input length can be cast to a `u32`
280    /// without overflow.
281    ///
282    /// # Arguments
283    ///
284    /// * `in_out` - The buffer to process in-place.
285    #[inline]
286    unsafe fn process_in_place_unchecked<'io>(&mut self, in_out: &'io mut [u8]) -> &'io [u8] {
287        debug_assert!(
288            can_cast_u32(in_out.len()),
289            "Process unchecked precondition violated (debug assertion). The size of the input must \
290            be less than or equal to the size of the output. The size of the input must also be \
291            representable as a `u32` without overflowing."
292        );
293
294        // The soundness of passing the input ptr as the output ptr is implied by this being sound
295        // in ChaCha20Poly1305.
296        //
297        // The infallibility explanation can be seen in the process_unchecked implementation.
298        let _res = wc_Chacha_Process(
299            addr_of_mut!(self.inner),
300            in_out.as_ptr().cast_mut(),
301            in_out.as_ptr(),
302            in_out.len() as u32
303        );
304
305        debug_assert_eq!(_res, 0);
306
307        in_out
308    }
309
310    /// Processes the input into the output buffer, checking lengths.
311    ///
312    /// # Arguments
313    ///
314    /// * `input` - The input slice to process.
315    /// * `output` - The output buffer to write the processed data into.
316    ///
317    /// # Errors
318    ///
319    /// `!(input_len <= output_len && can_cast_u32(input_len))`
320    #[inline]
321    fn process(&mut self, input: &[u8], output: &mut [u8]) -> Result<(), Unspecified> {
322        if !Self::predicate(input.len(), output.len()) { return Err(Unspecified) }
323        unsafe { self.process_unchecked(input, output) };
324        Ok(())
325    }
326
327    /// Encrypt / Decrypt the data in-place
328    ///
329    /// # Arguments
330    ///
331    /// * `in_out` - The buffer to encrypt / decrypt in-place
332    ///
333    /// # Errors
334    ///
335    /// `!can_cast_u32(in_out.len())`
336    #[inline]
337    fn process_in_place<'io>(&mut self, in_out: &'io mut [u8]) -> Result<&'io [u8], Unspecified> {
338        if can_cast_u32(in_out.len()) {
339            Ok(unsafe { self.process_in_place_unchecked(in_out) })
340        } else {
341            Err(Unspecified)
342        }
343    }
344
345    /// Processes the input into the output buffer with exact sizes.
346    ///
347    /// # Arguments
348    ///
349    /// * `input` - The input array to process.
350    /// * `output` - The output array to write the processed data into.
351    ///
352    /// # Errors
353    ///
354    /// `!const_can_cast_u32::<C>()`
355    #[inline]
356    fn process_exact<const C: usize>(
357        &mut self,
358        input: &[u8; C],
359        output: &mut [u8; C]
360    ) -> Result<(), Unspecified> {
361        if !const_can_cast_u32::<C>() { return Err(Unspecified); }
362        unsafe { self.process_unchecked(input, output) };
363        Ok(())
364    }
365
366    /// Processes the input into the output buffer with compile-time size checking.
367    ///
368    /// # Type Parameters
369    ///
370    /// * `I` - The size of the input array.
371    /// * `O` - The size of the output array.
372    ///
373    /// # Arguments
374    ///
375    /// * `input` - The input array to process.
376    /// * `output` - The output array to write the processed data into.
377    ///
378    /// # Errors
379    ///
380    /// `!(I <= O && const_can_cast_u32::<I>())`
381    #[inline]
382    fn process_sized<const I: usize, const O: usize>(
383        &mut self,
384        input: &[u8; I],
385        output: &mut [u8; O]
386    ) -> Result<(), Unspecified> {
387        if !Self::const_predicate::<I, O>() { return Err(Unspecified) }
388        unsafe { self.process_unchecked(input, output) };
389        Ok(())
390    }
391
392    /// Encrypt / Decrypt the `in_out` buffer in-place, with safety checks performed at compilation
393    /// time.
394    ///
395    /// # Arguments
396    ///
397    /// * `in_out` - The buffer to encrypt/decrypt in-place
398    ///
399    /// # Errors
400    ///
401    /// `!const_can_cast_u32::<C>()`
402    #[inline]
403    fn process_in_place_sized<'io, const C: usize>(&mut self, in_out: &'io mut [u8; C]) -> Result<&'io [u8; C], Unspecified> {
404        if const_can_cast_u32::<C>() {
405            unsafe { self.process_in_place_unchecked(in_out) };
406            Ok(in_out)
407        } else {
408            Err(Unspecified)
409        }
410    }
411
412    /// Processes the input into the output buffer with a fixed-size output.
413    ///
414    /// # Type Parameters
415    ///
416    /// * `O` - The size of the output array.
417    ///
418    /// # Arguments
419    ///
420    /// * `input` - The input slice to process.
421    /// * `output` - The output array to write the processed data into.
422    ///
423    /// # Errors
424    ///
425    /// `!(lte::<O>(input.len()) && can_cast_u32(input.len()))`
426    #[inline]
427    fn process_sized_out<const O: usize>(
428        &mut self,
429        input: &[u8],
430        output: &mut [u8; O]
431    ) -> Result<(), Unspecified> {
432        if !(lte::<O>(input.len()) && can_cast_u32(input.len())) { return Err(Unspecified) }
433        unsafe { self.process_unchecked(input, output) };
434        Ok(())
435    }
436}
437
438impl ChaCha20<Ready> {
439    /// Encrypts the plaintext into the ciphertext buffer.
440    ///
441    /// # Arguments
442    ///
443    /// * `plain` - The plaintext to encrypt.
444    /// * `cipher` - The buffer to store the encrypted data.
445    ///
446    /// # Errors
447    ///
448    /// - The length of `plain` was greater than [`u32::MAX`]
449    /// - The length of `cipher` was less than the length of `plain`
450    #[inline]
451    pub fn encrypt_into(
452        mut self,
453        plain: &[u8],
454        cipher: &mut [u8]
455    ) -> Result<ChaCha20<NeedsIv>, Self> {
456        if self.process(plain, cipher).is_ok() {
457            Ok(Self::new_with(self.inner))
458        } else {
459            Err(self)
460        }
461    }
462
463    /// Encrypts the plaintext in-place.
464    ///
465    /// # Arguments
466    ///
467    /// * `in_out` - The buffer to encrypt in-place.
468    ///
469    /// # Errors
470    ///
471    /// The length of `in_out` was greater than [`u32::MAX`].
472    #[inline]
473    pub fn encrypt_in_place(mut self, in_out: &mut [u8]) -> Result<ChaCha20<NeedsIv>, Self> {
474        if self.process_in_place(in_out).is_ok() {
475            Ok(Self::new_with(self.inner))
476        } else {
477            Err(self)
478        }
479    }
480
481    /// Encrypts the plaintext into the ciphertext buffer with compile-time safety checks.
482    ///
483    /// # Arguments
484    ///
485    /// * `plain` - The plaintext array to encrypt.
486    /// * `cipher` - The array to store the encrypted data.
487    ///
488    /// # Returns
489    ///
490    /// `ChaCha20` instance in the `NeedsIv` state.
491    ///
492    /// # Errors
493    ///
494    /// - The length of `plain` was greater than [`u32::MAX`].
495    /// - The length of `cipher` was less than the length of `plain`.
496    #[inline]
497    pub fn encrypt_into_sized<const P: usize, const C: usize>(
498        mut self,
499        plain: &[u8; P],
500        cipher: &mut [u8; C]
501    ) -> Result<ChaCha20<NeedsIv>, Self> {
502        if self.process_sized(plain, cipher).is_ok() {
503            Ok(Self::new_with(self.inner))
504        } else {
505            Err(self)
506        }
507    }
508
509    /// Encrypts the plaintext in-place with compile-time safety checks.
510    ///
511    /// # Arguments
512    ///
513    /// * `in_out` - The plaintext to encrypt in-place.
514    ///
515    /// # Returns
516    ///
517    /// `ChaCha20` instance in the `NeedsIv` state.
518    ///
519    /// # Errors
520    ///
521    /// The length of `in_out` was greater than [`u32::MAX`].
522    #[inline]
523    pub fn encrypt_in_place_sized<const C: usize>(mut self, in_out: &mut [u8; C]) -> Result<ChaCha20<NeedsIv>, Self> {
524        if self.process_in_place_sized(in_out).is_ok() {
525            Ok(Self::new_with(self.inner))
526        } else {
527            Err(self)
528        }
529    }
530
531    /// Encrypts the plaintext into a fixed-size ciphertext buffer.
532    ///
533    /// # Arguments
534    ///
535    /// * `plain` - The plaintext to encrypt.
536    /// * `cipher` - The array to store the encrypted data.
537    ///
538    /// # Returns
539    ///
540    /// `ChaCha20` instance in the `NeedsIv` state.
541    ///
542    /// # Errors
543    ///
544    /// - The length of `plain` was greater than [`u32::MAX`].
545    /// - The length of `cipher` was less than the length of `plain`.
546    #[inline]
547    pub fn encrypt_into_sized_out<const C: usize>(
548        mut self,
549        plain: &[u8],
550        cipher: &mut [u8; C]
551    ) -> Result<ChaCha20<NeedsIv>, Self> {
552        if self.process_sized_out(plain, cipher).is_ok() {
553            Ok(Self::new_with(self.inner))
554        } else {
555            Err(self)
556        }
557    }
558
559    /// Encrypts the plaintext into the ciphertext buffer with exact sizes.
560    ///
561    /// # Arguments
562    ///
563    /// * `plain` - The plaintext array to encrypt.
564    /// * `cipher` - The array to store the encrypted data.
565    ///
566    /// # Returns
567    ///
568    /// `ChaCha20` instance in the `NeedsIv` state.
569    ///
570    /// # Errors
571    ///
572    /// If `C` (the length of `plain` and `cipher`) was greater than [`u32::MAX`].
573    #[inline]
574    pub fn encrypt_into_exact<const C: usize>(
575        mut self,
576        plain: &[u8; C],
577        cipher: &mut [u8; C]
578    ) -> Result<ChaCha20<NeedsIv>, Self> {
579        if self.process_exact(plain, cipher).is_ok() {
580            Ok(Self::new_with(self.inner))
581        } else {
582            Err(self)
583        }
584    }
585
586    alloc! {
587        /// Encrypts the plaintext and returns the ciphertext as a vector.
588        ///
589        /// # Arguments
590        ///
591        /// * `plain` - The plaintext to encrypt.
592        ///
593        /// # Returns
594        ///
595        /// `ChaCha20` instance in the `NeedsIv` state.
596        ///
597        /// # Errors
598        ///
599        /// The length of `plain` was greater than [`u32::MAX`].
600        pub fn encrypt(
601            self,
602            plain: &[u8]
603        ) -> Result<(alloc::vec::Vec<u8>, ChaCha20<NeedsIv>), Self> {
604            let mut output = alloc::vec![0u8; plain.len()];
605            self.encrypt_into(plain, output.as_mut_slice()).map(move |ni| (output, ni))
606        }
607    }
608
609    /// Encrypts the plaintext array and returns the ciphertext array.
610    ///
611    /// # Type Parameters
612    ///
613    /// * `I` - The size of the plaintext and ciphertext arrays.
614    ///
615    /// # Arguments
616    ///
617    /// * `plain` - The plaintext array to encrypt.
618    ///
619    /// # Returns
620    ///
621    /// `ChaCha20` instance in the `NeedsIv` state.
622    ///
623    /// # Errors
624    ///
625    /// The length of `plain` was greater than [`u32::MAX`].
626    #[inline]
627    pub fn encrypt_exact<const I: usize>(
628        self,
629        plain: &[u8; I]
630    ) -> Result<([u8; I], ChaCha20<NeedsIv>), Self> {
631        let mut output = [0u8; I];
632        self.encrypt_into_exact(plain, &mut output).map(move |ni| (output, ni))
633    }
634
635    /// Transitions the `ChaCha20` instance into the `Streaming` state for partial updates.
636    pub const fn stream(self) -> ChaCha20<Streaming> {
637        Self::new_with(self.inner)
638    }
639}
640
641impl_fmt! { Ready }
642
643impl<S: CanProcess> ChaCha20<S> {
644    /// Decrypts the ciphertext into the output buffer.
645    ///
646    /// # Arguments
647    ///
648    /// * `cipher` - The ciphertext to decrypt.
649    /// * `plain` - The buffer to store the decrypted data.
650    ///
651    /// # Errors
652    ///
653    /// - If the length of `cipher` is greater than [`u32::MAX`].
654    /// - If the length of `cipher` is greater than the length of `plain`.
655    #[inline]
656    pub fn decrypt_into(&mut self, cipher: &[u8], plain: &mut [u8]) -> Result<(), Unspecified> {
657        self.process(cipher, plain)
658    }
659
660    /// Decrypts the ciphertext in-place.
661    ///
662    /// # Arguments
663    ///
664    /// * `in_out` - The plaintext to decrypt in place.
665    ///
666    /// # Errors
667    ///
668    /// If the length of `in_out` is greater than [`u32::MAX`].
669    ///
670    /// # Returns
671    ///
672    /// The `in_out` argument, decrypted, for convenience. This can be ignored.
673    #[inline]
674    pub fn decrypt_in_place<'io>(&mut self, in_out: &'io mut [u8]) -> Result<&'io [u8], Unspecified> {
675        self.process_in_place(in_out)
676    }
677
678    /// Decrypts the ciphertext into the output buffer with compile-time safety checks.
679    ///
680    /// # Arguments
681    ///
682    /// * `cipher` - The ciphertext array to decrypt.
683    /// * `plain` - The array to store the decrypted data.
684    ///
685    /// # Errors
686    ///
687    /// - If the length of `cipher` is greater than [`u32::MAX`].
688    /// - If the length of `cipher` is greater than the length of `plain`.
689    #[inline]
690    pub fn decrypt_into_sized<const I: usize, const O: usize>(
691        &mut self,
692        cipher: &[u8; I],
693        plain: &mut [u8; O]
694    ) -> Result<(), Unspecified> {
695        self.process_sized(cipher, plain)
696    }
697
698    /// Decrypts the ciphertext in-place with compile-time safety checks.
699    ///
700    /// # Arguments
701    ///
702    /// * `in_out` - The ciphertext to decrypt in-place.
703    ///
704    /// # Returns
705    ///
706    /// `ChaCha20` instance in the `NeedsIv` state.
707    ///
708    /// # Errors
709    ///
710    /// - The length of `in_out` was greater than [`u32::MAX`].
711    #[inline]
712    pub fn decrypt_in_place_sized<'io, const C: usize>(
713        &mut self,
714        in_out: &'io mut [u8; C]
715    ) -> Result<&'io [u8; C], Unspecified> {
716        self.process_in_place_sized(in_out)
717    }
718
719    /// Decrypts the ciphertext into the output buffer with exact sizes.
720    ///
721    /// # Arguments
722    ///
723    /// * `cipher` - The ciphertext array to decrypt.
724    /// * `plain` - The array to store the decrypted data.
725    ///
726    /// # Errors
727    ///
728    /// If `C` (the length of `cipher` and `plain`) is greater than [`u32::MAX`].
729    #[inline]
730    pub fn decrypt_into_exact<const C: usize>(&mut self, cipher: &[u8; C], plain: &mut [u8; C]) -> Result<(), Unspecified> {
731        self.process_exact(cipher, plain)
732    }
733
734    alloc! {
735        /// Decrypts the ciphertext and returns the plaintext as a vector.
736        ///
737        /// # Arguments
738        ///
739        /// * `cipher` - The ciphertext to decrypt.
740        ///
741        /// # Errors
742        ///
743        /// If the length of `cipher` is greater than [`u32::MAX`].
744        ///
745        /// # Returns
746        ///
747        /// A newly allocated buffer, the same length as `cipher`, containing the decrypted
748        /// plaintext.
749        #[inline]
750        pub fn decrypt(&mut self, cipher: &[u8]) -> Result<alloc::vec::Vec<u8>, Unspecified> {
751            let mut output = alloc::vec![0u8; cipher.len()];
752            self.decrypt_into(cipher, output.as_mut_slice()).map(move |()| output)
753        }
754    }
755
756    /// Decrypts the ciphertext array and returns the plaintext array.
757    ///
758    /// # Arguments
759    ///
760    /// * `cipher` - The ciphertext array to decrypt.
761    ///
762    /// # Errors
763    ///
764    /// If `O` (the length of `cipher`) is greater than [`u32::MAX`].
765    #[inline]
766    pub fn decrypt_exact<const O: usize>(&mut self, cipher: &[u8; O]) -> Result<[u8; O], Unspecified> {
767        let mut output = [0u8; O];
768        self.decrypt_into_exact(cipher, &mut output).map(move |()| output)
769    }
770}
771
772impl ChaCha20<Streaming> {
773    /// Encrypts the input into the output buffer in streaming mode.
774    ///
775    /// # Arguments
776    ///
777    /// * `plain` - The input to encrypt.
778    /// * `cipher` - The buffer to store the encrypted data.
779    ///
780    /// # Errors
781    /// 
782    /// - The length of `cipher` is less than the length of `plain`.
783    /// - The length of `plain` is greater than [`u32::MAX`].
784    #[inline]
785    pub fn encrypt_into(&mut self, plain: &[u8], cipher: &mut [u8]) -> Result<(), Unspecified> {
786        self.process(plain, cipher)
787    }
788
789    /// Encrypts the input into the output buffer in streaming mode with compile-time size checking.
790    ///
791    /// # Arguments
792    ///
793    /// * `plain` - The input array to encrypt.
794    /// * `cipher` - The array to store the encrypted data.
795    ///
796    /// # Errors
797    ///
798    /// - The length of `plain` was greater than [`u32::MAX`].
799    /// - The length of `cipher` was less than the length of `plain`.
800    #[inline]
801    pub fn encrypt_into_sized<const I: usize, const O: usize>(
802        &mut self,
803        plain: &[u8; I],
804        cipher: &mut [u8; O]
805    ) -> Result<(), Unspecified> {
806        self.process_sized(plain, cipher)
807    }
808
809    /// Encrypts the input into the output buffer in streaming mode with exact sizes.
810    ///
811    /// # Type Parameters
812    ///
813    /// * `C` - The size of both the input and output arrays.
814    ///
815    /// # Arguments
816    ///
817    /// * `input` - The input array to encrypt.
818    /// * `output` - The array to store the encrypted data.
819    ///
820    /// # Errors
821    ///
822    /// `C` (the length of `input` and `output`) was greater than [`u32::MAX`].
823    #[inline]
824    pub fn encrypt_into_exact<const C: usize>(
825        &mut self,
826        input: &[u8; C],
827        output: &mut [u8; C]
828    ) -> Result<(), Unspecified> {
829        self.process_exact(input, output)
830    }
831
832    /// Finishes the streaming encryption and returns to the `NeedsIv` state.
833    ///
834    /// # Returns
835    ///
836    /// A `ChaCha20` instance in the `NeedsIv` state.
837    #[inline]
838    pub const fn finish(self) -> ChaCha20<NeedsIv> {
839        Self::new_with(self.inner)
840    }
841
842    std! {
843        /// Creates a new `Writer` for streaming encryption with a custom chunk size.
844        ///
845        /// # Type Parameters
846        ///
847        /// * `CHUNK` - The chunk size for processing. This is the size of the intermediary buffer
848        ///             stored on the stack in bytes for the `write_all` and `write`
849        ///             implementations.
850        ///
851        /// # Arguments
852        ///
853        /// * `writer` - The underlying writer to use.
854        ///
855        /// # Returns
856        ///
857        /// A new `Writer` instance.
858        ///
859        /// # Errors
860        ///
861        /// If the provided `CHUNK` constant is greater than U32 max this will return the provided
862        /// `writer`.
863        pub const fn writer<W: io::Write, const CHUNK: usize>(self, writer: W) -> Result<Writer<W, CHUNK>, W> {
864            Writer::new(self, writer)
865        }
866
867        /// Creates a new `Writer` for streaming encryption with a default chunk size of 128 bytes.
868        ///
869        /// # Arguments
870        ///
871        /// * `writer` - The underlying writer to use.
872        ///
873        /// # Returns
874        ///
875        /// A new `Writer` instance with a chunk size of 128 bytes.
876        pub const fn default_writer<W: io::Write>(self, writer: W) -> Writer<W, 128> {
877            // SAFETY: 128 is significantly less than u32::MAX
878            unsafe { Writer::<W, 128>::new_unchecked(self, writer) }
879        }
880    }
881}
882
883impl_fmt! { Streaming }
884
885std! {
886    use std::io;
887    use core::ops;
888
889    /// A wrapper for any implementor of `std::io::Write`.
890    ///
891    /// `Writer` implements `std::io::Write` and takes a child which also implements this trait.
892    /// This type can wrap any writer, and ensure all data passed to said writer is encrypted.
893    pub struct Writer<W, const CHUNK: usize> {
894        chacha: ChaCha20<Streaming>,
895        writer: W
896    }
897
898    impl<W, const CHUNK: usize> Writer<W, CHUNK> {
899        /// Creates a new `Writer` instance.
900        ///
901        /// # Arguments
902        ///
903        /// * `chacha` - The `ChaCha20` instance in streaming mode.
904        /// * `writer` - The underlying writer.
905        ///
906        /// # Returns
907        ///
908        /// A new `Writer` instance.
909        ///
910        /// # Errors
911        ///
912        /// If the size of `CHUNK` is greater than [`u32::MAX`]
913        pub const fn new(chacha: ChaCha20<Streaming>, writer: W) -> Result<Self, W> {
914            if const_can_cast_u32::<CHUNK>() {
915                Ok(Self {
916                    chacha,
917                    writer
918                })
919            } else {
920                Err(writer)
921            }
922        }
923
924        /// # Safety
925        ///
926        /// The size of `CHUNK` must not be greater than [`u32::MAX`]
927        const unsafe fn new_unchecked(chacha: ChaCha20<Streaming>, writer: W) -> Self {
928            Self {
929                chacha,
930                writer
931            }
932        }
933
934        /// Finishes the streaming encryption and returns to the `NeedsIv` state.
935        ///
936        /// # Returns
937        ///
938        /// A `ChaCha20` instance in the `NeedsIv` state.
939        #[inline]
940        pub fn finish(self) -> ChaCha20<NeedsIv> {
941            self.chacha.finish()
942        }
943    }
944
945    impl<W, const CHUNK: usize> ops::Deref for Writer<W, CHUNK> {
946        type Target = ChaCha20<Streaming>;
947
948        #[inline]
949        fn deref(&self) -> &Self::Target {
950            &self.chacha
951        }
952    }
953
954    impl<W, const CHUNK: usize> ops::DerefMut for Writer<W, CHUNK> {
955        #[inline]
956        fn deref_mut(&mut self) -> &mut Self::Target {
957            &mut self.chacha
958        }
959    }
960
961    impl<W: io::Write, const CHUNK: usize> io::Write for Writer<W, CHUNK> {
962        /// Encrypts and writes the given buffer.
963        ///
964        /// # Arguments
965        ///
966        /// * `buf` - The buffer to encrypt and write.
967        ///
968        /// # Returns
969        ///
970        /// The number of bytes written on success, or an `io::Error` on failure.
971        ///
972        /// # Note
973        ///
974        /// The maximum number of bytes this can write in a single invocation is capped by the
975        /// `CHUNK` size. If this is not desirable, please consider using the `write_all`
976        /// implementation.
977        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
978            let mut out = [0u8; CHUNK];
979            let to_write = core::cmp::min(CHUNK, buf.len());
980
981            // SAFETY: we cannot be constructed with a chunk size larger than u32::MAX
982            unsafe { self.process_unchecked(&buf[..to_write], &mut out[..to_write]) };
983            self.writer.write(&out[..to_write])
984        }
985
986        /// Encrypts and writes the entire buffer.
987        ///
988        /// # Arguments
989        ///
990        /// * `buf` - The buffer to encrypt and write.
991        ///
992        /// # Returns
993        ///
994        /// `Ok(())` on success, or an `io::Error` on failure.
995        fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
996            let mut out = [0u8; CHUNK];
997            let mut pos = 0usize;
998            let len = buf.len();
999
1000            while pos + CHUNK <= len {
1001                unsafe {
1002                    // SAFETY: we cannot be constructed with a chunk size larger than u32::MAX
1003                    self.process_unchecked(&buf[pos..pos + CHUNK], &mut out);
1004                    pos += CHUNK;
1005                    self.writer.write_all(&out)?;
1006                }
1007            }
1008
1009            let last = &buf[pos..];
1010            debug_assert!(last.len() <= CHUNK);
1011
1012            // SAFETY: We are handling less than the CHUNK size.
1013            unsafe { self.process_unchecked(last, &mut out) }
1014            self.writer.write_all(&out[..last.len()])
1015        }
1016
1017        /// Flushes the underlying writer.
1018        ///
1019        /// # Returns
1020        ///
1021        /// Propagates the result of invoking flush for the underlying writer
1022        #[inline]
1023        fn flush(&mut self) -> io::Result<()> {
1024            self.writer.flush()
1025        }
1026    }
1027}
1028
1029#[cfg(test)]
1030mod tests {
1031    use super::*;
1032
1033    #[test]
1034    fn smoke() {
1035        let (encrypted, chacha) = ChaCha20::new(&[0u8; 16])
1036            .set_iv([3u8; 12])
1037            .encrypt_exact(b"hello world!")
1038            .unwrap();
1039
1040        let plain = chacha
1041            .set_iv([3u8; 12])
1042            .decrypt_exact(&encrypted)
1043            .unwrap();
1044
1045        assert_eq!(plain, *b"hello world!");
1046    }
1047}
1048
1049#[cfg(test)]
1050mod property_tests {
1051    use crate::aes::test_utils::{BoundList, AnyList};
1052    use proptest::prelude::*;
1053    use crate::chacha::{ChaCha20, Key};
1054    use crate::buf::Nonce;
1055
1056    proptest! {
1057        #![proptest_config(ProptestConfig::with_cases(10_000))]
1058
1059        #[test]
1060        fn in_place_bijectivity(
1061            input in any::<BoundList<1024>>(),
1062            key in any::<Key>(),
1063            iv in any::<Nonce>()
1064        ) {
1065            let mut in_out = input;
1066
1067            ChaCha20::new(key.as_ref())
1068                .set_iv(iv.copy())
1069                .encrypt_in_place(in_out.as_mut_slice())
1070                .unwrap();
1071
1072            if in_out.len() >= 3 {
1073                prop_assert_ne!(in_out, input);
1074            }
1075
1076            ChaCha20::new(key)
1077                .set_iv(iv)
1078                .decrypt_in_place(in_out.as_mut_slice())
1079                .unwrap();
1080
1081            prop_assert_eq!(in_out, input);
1082        }
1083
1084        #[test]
1085        fn enc_into_dec_in_place(
1086            input in any::<BoundList<1024>>(),
1087            key in any::<Key>(),
1088            iv in any::<Nonce>()
1089        ) {
1090            let mut enc = input.create_self();
1091
1092            ChaCha20::new(key.as_ref()).set_iv(iv.copy())
1093                .encrypt_into(input.as_slice(), enc.as_mut_slice())
1094                .unwrap();
1095
1096            if enc.len() >= 3 {
1097                prop_assert_ne!(enc.as_slice(), input.as_slice());
1098            }
1099
1100            ChaCha20::new(key.as_ref()).set_iv(iv)
1101                .decrypt_in_place(enc.as_mut_slice())
1102                .unwrap();
1103
1104            prop_assert_eq!(enc, input);
1105        }
1106
1107        #[test]
1108        fn enc_in_place_dec_into(
1109            input in any::<BoundList<1024>>(),
1110            key in any::<Key>(),
1111            iv in any::<Nonce>()
1112        ) {
1113            let mut enc = input;
1114
1115            ChaCha20::new(key.as_ref()).set_iv(iv.copy())
1116                .encrypt_in_place(enc.as_mut_slice())
1117                .unwrap();
1118
1119            if enc.len() >= 3 {
1120                prop_assert_ne!(enc.as_slice(), input.as_slice());
1121            }
1122
1123            let mut dec = input.create_self();
1124
1125            ChaCha20::new(key.as_ref()).set_iv(iv)
1126                .decrypt_into(enc.as_slice(), dec.as_mut_slice())
1127                .unwrap();
1128
1129            prop_assert_eq!(dec, input);
1130        }
1131
1132        #[test]
1133        fn bijective_arb_updates(
1134            inputs in any::<AnyList<32, BoundList<512>>>(),
1135            key in any::<Key>(),
1136            iv in any::<[u8; 12]>()
1137        ) {
1138            let mut outputs = inputs.create_self();
1139
1140            let io_iter = inputs.as_slice().iter().zip(outputs.as_mut_slice());
1141            let mut chacha = ChaCha20::new(key.as_ref()).set_iv(&iv).stream();
1142
1143            for (i, o) in io_iter {
1144                chacha.encrypt_into(i, o).unwrap();
1145                if i.len() >= 3 { prop_assert_ne!(i.as_slice(), o.as_slice()); }
1146            }
1147
1148            let mut in_out = outputs.join();
1149            let expected = inputs.join();
1150
1151            ChaCha20::new(key).set_iv(iv)
1152                .decrypt_in_place(in_out.as_mut_slice())
1153                .unwrap();
1154
1155            prop_assert_eq!(in_out, expected);
1156        }
1157    }
1158}