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}