wolf_crypto/mac/poly1305/mod.rs
1//! The `Poly1305` Message Authentication Code
2//!
3//! ```
4//! use wolf_crypto::mac::{Poly1305, poly1305::Key};
5//!
6//! # fn main() -> Result<(), wolf_crypto::Unspecified> {
7//! let key = Key::new([0u8; 32]);
8//!
9//! let tag = Poly1305::new(key.as_ref())
10//! .aead_padding()
11//! .update(b"hello world")?
12//! .finalize();
13//!
14//! let o_tag = Poly1305::new(key)
15//! .mac(b"Different message", ()).unwrap();
16//!
17//! assert_eq!(
18//! tag, o_tag,
19//! "All of our coefficients are zero!"
20//! );
21//!
22//! let key = Key::new([42u8; 32]);
23//!
24//! let tag = Poly1305::new(key.as_ref())
25//! .aead_padding_ct()
26//! .update_ct(b"thankfully this ")
27//! .update_ct(b"is only the case ")
28//! .update_ct(b"with a key of all zeroes.")
29//! .finalize(/* errors are accumulated in constant-time, so we handle them here */)?;
30//!
31//! let o_tag = Poly1305::new(key.as_ref())
32//! .mac(b"thankfully this is only the case with a key of all zeroes.", ())?;
33//!
34//! assert_eq!(tag, o_tag);
35//!
36//! let bad_tag = Poly1305::new(key)
37//! .update(b"This tag will not be the same.")?
38//! .finalize();
39//!
40//! assert_ne!(bad_tag, tag);
41//! # Ok(()) }
42//! ```
43//!
44//! ## Note
45//!
46//! The first test may be concerning, it is not. `Poly1305` was originally designed to be
47//! [paired with `AES`][1], this example only would take place if the cipher it is paired with
48//! is fundamentally broken. More explicitly, the cipher would need to be an identity function for
49//! the first 32 bytes, meaning not encrypt the first 32 bytes in any way shape or form.
50//!
51//! The author of `Poly1305` ([Daniel J. Bernstein][2]) also created [`Salsa20` (`Snuffle 2005`)][3],
52//! and then [`ChaCha`][4], which `Poly1305` generally complements for authentication.
53//!
54//! ## Security
55//!
56//! `Poly1305` is meant to be used with a **one-time key**, key reuse in `Poly1305` can be
57//! devastating. When pairing with something like [`ChaCha20Poly1305`] this requirement is handled
58//! via the discreteness of the initialization vector (more reason to never reuse initialization
59//! vectors).
60//!
61//! If you are using `Poly1305` directly, each discrete message you authenticate must leverage
62//! fresh key material.
63//!
64//! [1]: https://cr.yp.to/mac/poly1305-20050329.pdf
65//! [2]: https://cr.yp.to/djb.html
66//! [3]: https://cr.yp.to/snuffle.html
67//! [4]: https://cr.yp.to/chacha/chacha-20080128.pdf
68//! [`ChaCha20Poly1305`]: crate::aead::ChaCha20Poly1305
69
70use wolf_crypto_sys::{
71 Poly1305 as wc_Poly1305,
72 wc_Poly1305SetKey, wc_Poly1305Update, wc_Poly1305Final,
73 wc_Poly1305_Pad, wc_Poly1305_EncodeSizes,
74 wc_Poly1305_MAC
75};
76
77mod key;
78pub mod state;
79
80pub use key::{GenericKey, Key, KeyRef, KEY_SIZE};
81use key::KEY_SIZE_U32;
82
83use core::mem::MaybeUninit;
84use core::ptr::addr_of_mut;
85use state::{Poly1305State, Init, Ready, Streaming};
86use crate::opaque_res::Res;
87use crate::{can_cast_u32, to_u32, Unspecified};
88use core::marker::PhantomData;
89use crate::aead::{Aad, Tag};
90use crate::ct;
91
92// INFALLIBILITY COMMENTARY
93//
94// — poly1305_blocks
95//
96// SRC: https://github.com/wolfSSL/wolfssl/blob/master/wolfcrypt/src/poly1305.c#L277
97//
98// Only can fail under SMALL_STACK /\ W64WRAPPER via OOM. We do not enable either of these
99// features, and both must be enabled for this to be fallible.
100//
101// — poly1305_block
102//
103// SRC: https://github.com/wolfSSL/wolfssl/blob/master/wolfcrypt/src/poly1305.c#L483
104//
105// Returns 0 OR returns the result of poly1305_blocks. Which again is infallible unless the
106// aforementioned features are both enabled.
107//
108// — wc_Poly1305SetKey
109//
110// SRC: https://github.com/wolfSSL/wolfssl/blob/master/wolfcrypt/src/poly1305.c#L496
111//
112// Only can fail if these preconditions are not met:
113//
114// key != null /\ KeySz == 32 /\ ctx != null
115//
116// Which our types guarantee this is satisfied.
117//
118// — wc_Poly1305Final
119//
120// SRC: https://github.com/wolfSSL/wolfssl/blob/master/wolfcrypt/src/poly1305.c#L584
121//
122// Only can fail if these preconditions are not met:
123//
124// ctx != null /\ mac != null
125//
126// With an implicit, unchecked precondition (observed in the wc_Poly1305_MAC function)
127//
128// macSz == 16
129//
130// Which, again, our types guarantee this precondition is satisfied.
131//
132// — wc_Poly1305Update
133//
134// SRC: https://github.com/wolfSSL/wolfssl/blob/master/wolfcrypt/src/poly1305.c#L794
135//
136// This depends on the infallibility of poly1305_blocks, which we have already showed
137// is infallible.
138//
139// So, this can only fail if the following preconditions are not met:
140//
141// ctx != null /\ (bytes > 0 -> bytes != null)
142//
143// Which again, our types guarantee this is satisfied.
144//
145// — wc_Poly1305_Pad (invoked in finalize)
146//
147// SRC: https://github.com/wolfSSL/wolfssl/blob/master/wolfcrypt/src/poly1305.c#L913
148//
149// This depends on the success of wc_Poly1305Update, which we have already shown to be
150// infallible.
151//
152// This is only fallible if the provided ctx is null, which again our types are not
153// able to represent.
154//
155// Regarding the wc_Poly1305Update invocation, this is clearly infallible.
156//
157// We have this:
158// paddingLen = (-(int)lenToPad) & (WC_POLY1305_PAD_SZ - 1);
159//
160// Where they are just computing the compliment of lenToPad, masking it against 15, which gives
161// us the offset to 16 byte alignment.
162// For example, let's say our length to pad is 13 (for ease of reading over a single octet):
163// 13 to -13 (00001101 to 11110011)
164// -13 & 15 to 3 (11110011 & 00001111 to 00000011)
165//
166// For all values, this will never exceed 15 bytes, which is what is the amount of padding living
167// on the stack. Again, this usage of wc_Poly1305Update is clearly infallible with our
168// configuration.
169//
170// — wc_Poly1305_EncodeSizes (invoked in finalize)
171//
172// SRC: https://github.com/wolfSSL/wolfssl/blob/master/wolfcrypt/src/poly1305.c#L941
173//
174// Similar to wc_Poly1305_Pad, this depends on the infallibility of wc_Poly1305Update. Which again,
175// in this circumstance is infallible. The usage within this function is infallible with our
176// configuration of wolfcrypt. The only precondition again is ctx being non-null, which again
177// is guaranteed via our types.
178//
179// — wc_Poly1305_MAC
180//
181// SRC: https://github.com/wolfSSL/wolfssl/blob/master/wolfcrypt/src/poly1305.c#L995
182//
183// Building on the above commentary, this is infallible as well.
184//
185// We have the precondition:
186//
187// ctx != null /\ input != null /\ tag != null /\ tagSz >= 16
188// /\ (additionalSz != 0 -> additional != null)
189//
190// Which again, our types ensure that this is satisfied.
191// #1 ctx must not be null as we would never be able to invoke this function otherwise.
192// #2 input must not be null, this is guaranteed via Rust's type system.
193// #3 Our Tag type is never null, and the size of Tag is always 16/
194// #4 Our Aad trait ensures that if the size is non-zero that the ptr method never returns a
195// null pointer.
196//
197// END COMMENTARY
198
199
200/// The `Poly1305` Message Authentication Code (MAC)
201///
202/// # Example
203///
204/// ```
205/// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
206///
207/// let key: Key = [7u8; 32].into();
208///
209/// let tag = Poly1305::new(key.as_ref())
210/// .aead_padding_ct()
211/// .update_ct(b"hello world")
212/// .update_ct(b", how are you")
213/// .finalize()
214/// .unwrap();
215///
216/// let o_tag = Poly1305::new(key.as_ref())
217/// .mac(b"hello world, how are you", b"")
218/// .unwrap();
219///
220/// assert_eq!(tag, o_tag);
221/// ```
222#[repr(transparent)]
223pub struct Poly1305<State: Poly1305State = Init> {
224 inner: wc_Poly1305,
225 _state: PhantomData<State>
226}
227
228impl<State: Poly1305State> From<Poly1305<State>> for Unspecified {
229 #[inline]
230 fn from(_value: Poly1305<State>) -> Self {
231 Self
232 }
233}
234
235opaque_dbg! { Poly1305 }
236
237impl Poly1305<Init> {
238 /// Creates a new `Poly1305` instance with the provided key.
239 ///
240 /// # Arguments
241 ///
242 /// * `key` - The secret key material used for MAC computation.
243 ///
244 /// # Example
245 ///
246 /// ```
247 /// use wolf_crypto::mac::{Poly1305, poly1305::Key};
248 ///
249 /// let key: Key = [42u8; 32].into();
250 /// let poly = Poly1305::new(key.as_ref());
251 /// ```
252 pub fn new<K: GenericKey>(key: K) -> Poly1305<Ready> {
253 let mut poly1305 = MaybeUninit::<wc_Poly1305>::uninit();
254
255 unsafe {
256 // infallible, see commentary at start of file.
257 let _res = wc_Poly1305SetKey(
258 poly1305.as_mut_ptr(),
259 key.ptr(),
260 KEY_SIZE_U32
261 );
262
263 debug_assert_eq!(_res, 0);
264
265 Poly1305::<Ready> {
266 inner: poly1305.assume_init(),
267 _state: PhantomData
268 }
269 }
270 }
271}
272
273impl<State: Poly1305State> Poly1305<State> {
274 /// Transitions the `Poly1305` instance to a new state.
275 ///
276 /// # Type Parameters
277 ///
278 /// * `N` - The new state type.
279 ///
280 /// # Returns
281 ///
282 /// A `Poly1305` instance in the new state.
283 #[inline]
284 const fn with_state<N: Poly1305State>(self) -> Poly1305<N> {
285 unsafe { core::mem::transmute(self) }
286 }
287
288 /// Updates the `Poly1305` instance with additional input without performing checks.
289 ///
290 /// # Safety
291 ///
292 /// The length of the input must not be truncated / overflow a `u32`.
293 ///
294 /// # Arguments
295 ///
296 /// * `input` - A byte slice representing the data to include in the MAC computation.
297 #[inline]
298 unsafe fn update_unchecked(&mut self, input: &[u8]) {
299 // infallible, see commentary at beginning of file.
300 let _res = wc_Poly1305Update(
301 addr_of_mut!(self.inner),
302 input.as_ptr(),
303 input.len() as u32
304 );
305
306 debug_assert_eq!(_res, 0);
307 }
308}
309
310impl Poly1305<Ready> {
311 /// Computes the MAC for the given input and additional data without performing input length checks.
312 ///
313 /// # Safety
314 ///
315 /// Both the `input` and `additional` arguments length must be less than `u32::MAX`.
316 ///
317 /// # Arguments
318 ///
319 /// * `input` - A byte slice representing the message to authenticate.
320 /// * `additional` - A byte slice representing optional additional authenticated data (AAD).
321 ///
322 /// # Returns
323 ///
324 /// The associated authentication tag.
325 unsafe fn mac_unchecked<A: Aad>(mut self, input: &[u8], aad: A) -> Tag {
326 debug_assert!(can_cast_u32(input.len()));
327 debug_assert!(aad.is_valid_size());
328
329 let mut tag = Tag::new_zeroed();
330
331 // Infallible, see final section of commentary at beginning of file.
332 let _res = wc_Poly1305_MAC(
333 addr_of_mut!(self.inner),
334 aad.ptr(),
335 aad.size(),
336 input.as_ptr(),
337 input.len() as u32,
338 tag.as_mut_ptr(),
339 Tag::SIZE
340 );
341
342 assert_eq!(_res, 0);
343
344 debug_assert_eq!(_res, 0);
345
346 tag
347 }
348
349 /// Computes the MAC for the given input and additional data. This uses the TLS AEAD padding
350 /// scheme. If this is undesirable, consider calling `update` followed by `finalize` manually.
351 ///
352 /// # Arguments
353 ///
354 /// * `input` - A byte slice representing the message to authenticate.
355 /// * `aad` - Any additional authenticated data.
356 ///
357 /// # Returns
358 ///
359 /// The associated authentication tag.
360 ///
361 /// # Errors
362 ///
363 /// - The length of the `aad` is greater than [`u32::MAX`].
364 /// - The length of the `input` is greater than [`u32::MAX`].
365 ///
366 /// # Example
367 ///
368 /// ```
369 /// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
370 ///
371 /// let key: Key = [42u8; 32].into();
372 /// let tag = Poly1305::new(key.as_ref())
373 /// .mac(b"message", b"aad")
374 /// .unwrap();
375 /// # assert_ne!(tag, Tag::new_zeroed());
376 /// ```
377 #[inline]
378 pub fn mac<A: Aad>(self, input: &[u8], aad: A) -> Result<Tag, Unspecified> {
379 if can_cast_u32(input.len()) && aad.is_valid_size() {
380 Ok(unsafe { self.mac_unchecked(input, aad) })
381 } else {
382 Err(Unspecified)
383 }
384 }
385
386 /// Transitions the `Poly1305` instance into the streaming state with the TLS AEAD padding
387 /// scheme.
388 ///
389 /// # Returns
390 ///
391 /// A [`StreamPoly1305Aead`] instance for continued updates.
392 ///
393 /// # Example
394 ///
395 /// ```
396 /// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
397 ///
398 /// # fn main() -> Result<(), wolf_crypto::Unspecified> {
399 /// let key: Key = [42u8; 32].into();
400 /// let stream = Poly1305::new(key.as_ref())
401 /// .aead_padding()
402 /// .update(b"chunk1")?
403 /// .update(b"chunk2")?;
404 /// # Ok(()) }
405 /// ```
406 pub const fn aead_padding(self) -> StreamPoly1305Aead {
407 StreamPoly1305Aead::from_parts(self.with_state(), 0)
408 }
409
410 /// Transitions the `Poly1305` instance into the streaming state.
411 ///
412 /// # Returns
413 ///
414 /// A [`StreamPoly1305`] instance for continued updates.
415 ///
416 /// # Example
417 ///
418 /// ```
419 /// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
420 ///
421 /// # fn main() -> Result<(), wolf_crypto::Unspecified> {
422 /// let key: Key = [42u8; 32].into();
423 /// let stream = Poly1305::new(key.as_ref()).normal()
424 /// .update(b"chunk1")?
425 /// .update(b"chunk2")?;
426 /// # Ok(()) }
427 /// ```
428 pub const fn normal(self) -> StreamPoly1305 {
429 StreamPoly1305::from_parts(self.with_state(), 0)
430 }
431
432 /// Transitions the `Poly1305` instance into the streaming state with the TLS AEAD padding
433 /// scheme.
434 ///
435 /// The distinction between this and the standard [`aead_padding`] is that this accumulates
436 /// errors up until the point of finalization in constant time.
437 ///
438 /// # Returns
439 ///
440 /// A [`CtPoly1305Aead`] instance for continued updates.
441 ///
442 /// # Example
443 ///
444 /// ```
445 /// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
446 ///
447 /// # fn main() -> Result<(), wolf_crypto::Unspecified> {
448 /// let key: Key = [42u8; 32].into();
449 /// let stream = Poly1305::new(key.as_ref())
450 /// .aead_padding_ct()
451 /// .update_ct(b"chunk1")
452 /// .update_ct(b"chunk2");
453 /// # Ok(()) }
454 /// ```
455 ///
456 /// [`aead_padding`]: Self::aead_padding
457 pub const fn aead_padding_ct(self) -> CtPoly1305Aead {
458 CtPoly1305Aead::from_parts(self.with_state(), Res::OK, 0)
459 }
460
461 /// Transitions the `Poly1305` instance into the streaming state.
462 ///
463 /// The distinction between this and the standard [`normal`] is that this accumulates
464 /// errors up until the point of finalization in constant time.
465 ///
466 /// # Returns
467 ///
468 /// A [`CtPoly1305`] instance for continued updates.
469 ///
470 /// # Example
471 ///
472 /// ```
473 /// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
474 ///
475 /// # fn main() -> Result<(), wolf_crypto::Unspecified> {
476 /// let key: Key = [42u8; 32].into();
477 /// let stream = Poly1305::new(key.as_ref()).normal_ct()
478 /// .update_ct(b"chunk1")
479 /// .update_ct(b"chunk2");
480 /// # Ok(()) }
481 /// ```
482 ///
483 /// [`normal`]: Self::normal
484 pub const fn normal_ct(self) -> CtPoly1305 {
485 CtPoly1305::from_parts(self.with_state(), Res::OK, 0)
486 }
487
488 /// Updates the `Poly1305` instance with additional input, transitioning it to a streaming
489 /// state.
490 ///
491 /// # Arguments
492 ///
493 /// * `input` - A byte slice representing the data to include in the MAC computation.
494 ///
495 /// # Returns
496 ///
497 /// A `StreamPoly1305` instance for continued updates.
498 ///
499 /// # Errors
500 ///
501 /// If the length of `input` is greater than [`u32::MAX`].
502 ///
503 /// # Example
504 ///
505 /// ```
506 /// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
507 ///
508 /// # fn main() -> Result<(), wolf_crypto::Unspecified> {
509 /// let key: Key = [42u8; 32].into();
510 /// let stream = Poly1305::new(key.as_ref())
511 /// .update(b"chunk1")?
512 /// .update(b"chunk2")?;
513 /// # Ok(()) }
514 /// ```
515 #[inline]
516 pub fn update(mut self, input: &[u8]) -> Result<StreamPoly1305, Unspecified> {
517 to_u32(input.len()).map_or(
518 Err(Unspecified),
519 |len| unsafe {
520 self.update_unchecked(input);
521 Ok(StreamPoly1305::from_parts(self.with_state(), len))
522 }
523 )
524 }
525
526 /// Updates the `Poly1305` instance with additional input in a constant-time manner.
527 ///
528 /// # Arguments
529 ///
530 /// * `input` - A byte slice representing the data to include in the MAC computation.
531 ///
532 /// # Returns
533 ///
534 /// A `CtPoly1305` instance containing the updated state.
535 ///
536 /// # Example
537 ///
538 /// ```
539 /// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
540 ///
541 /// let key: Key = [42u8; 32].into();
542 /// let tag = Poly1305::new(key.as_ref())
543 /// .update_ct(b"sensitive ")
544 /// .update_ct(b"chunks")
545 /// .finalize()
546 /// .unwrap();
547 ///
548 /// let o_tag = Poly1305::new(key.as_ref())
549 /// .update_ct(b"sensitive chunks")
550 /// .finalize().unwrap();
551 ///
552 /// assert_eq!(tag, o_tag);
553 /// ```
554 pub fn update_ct(mut self, input: &[u8]) -> CtPoly1305 {
555 let (adjusted, res) = adjust_slice(input);
556 unsafe { self.update_unchecked(adjusted) };
557 // adjusted length will always be below u32::MAX
558 CtPoly1305::from_parts(self.with_state(), res, adjusted.len() as u32)
559 }
560}
561
562/// Finalizes the `Poly1305` MAC computation using the TLS AEAD padding scheme.
563///
564/// # Arguments
565///
566/// * `poly` - The `Poly1305` instance.
567/// * `accum_len` - The accumulated length of the input data.
568///
569/// # Returns
570///
571/// The associated authentication [`Tag`].
572#[inline]
573fn finalize_aead<S: Poly1305State>(mut poly: Poly1305<S>, accum_len: u32) -> Tag {
574 // Regarding fallibility for all functions invoked, and debug_asserted to have succeeded,
575 // see the commentary at the beginning of the document.
576 unsafe {
577 let _res = wc_Poly1305_Pad(
578 addr_of_mut!(poly.inner),
579 accum_len
580 );
581
582 debug_assert_eq!(_res, 0);
583
584 let _res = wc_Poly1305_EncodeSizes(
585 addr_of_mut!(poly.inner),
586 0u32,
587 accum_len
588 );
589
590 debug_assert_eq!(_res, 0);
591
592 finalize_no_pad(poly)
593 }
594}
595
596/// Finalizes the `Poly1305` MAC computation with padding.
597///
598/// # Arguments
599///
600/// * `poly` - The `Poly1305` instance.
601/// * `to_pad` - Either the length of the input, or the accumulated output of [`update_to_pad`].
602///
603/// # Returns
604///
605/// The associated authentication [`Tag`].
606#[inline]
607fn finalize<S: Poly1305State>(mut poly: Poly1305<S>, to_pad: u32) -> Tag {
608 unsafe {
609 let _res = wc_Poly1305_Pad(
610 addr_of_mut!(poly.inner),
611 to_pad
612 );
613
614 debug_assert_eq!(_res, 0);
615
616 finalize_no_pad(poly)
617 }
618}
619
620/// Finalizes the `Poly1305` MAC computation without padding.
621///
622/// # Arguments
623///
624/// * `poly` - The `Poly1305` instance.
625///
626/// # Returns
627///
628/// The associated authentication [`Tag`].
629#[inline]
630fn finalize_no_pad<S: Poly1305State>(mut poly: Poly1305<S>) -> Tag {
631 unsafe {
632 let mut tag = Tag::new_zeroed();
633
634 let _res = wc_Poly1305Final(
635 addr_of_mut!(poly.inner),
636 tag.as_mut_ptr()
637 );
638
639 debug_assert_eq!(_res, 0);
640
641 tag
642 }
643}
644
645#[inline(always)]
646#[must_use]
647const fn update_to_pad(to_pad: u8, new_len: u32) -> u8 {
648 // this is the same as (num + num) & 15, where num is a number of any possible size, not bound
649 // to a concrete type like u32.
650
651 // With wolfcrypt's padding algorithm this will always result in the same output as providing
652 // the total length. See kani verification at the bottom of the file.
653 to_pad.wrapping_add((new_len & 15) as u8) & 15
654}
655
656/// Represents an ongoing streaming MAC computation, allowing incremental updates.
657///
658/// This uses the TLS AEAD padding scheme.
659///
660/// # Example
661///
662/// ```
663/// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
664///
665/// # fn main() -> Result<(), wolf_crypto::Unspecified> {
666/// let key: Key = [42u8; 32].into();
667/// let tag = Poly1305::new(key.as_ref())
668/// .aead_padding()
669/// .update(b"chunk1")?
670/// .update(b"chunk2")?
671/// .update(b"chunk3")?
672/// .finalize();
673/// # Ok(()) }
674/// ```
675pub struct StreamPoly1305Aead {
676 poly1305: Poly1305<Streaming>,
677 accum_len: u32
678}
679
680impl From<StreamPoly1305Aead> for Unspecified {
681 #[inline]
682 fn from(value: StreamPoly1305Aead) -> Self {
683 value.poly1305.into()
684 }
685}
686
687opaque_dbg! { StreamPoly1305Aead }
688
689impl StreamPoly1305Aead {
690 /// Creates a new `StreamPoly1305` instance from its parts.
691 ///
692 /// # Arguments
693 ///
694 /// * `poly1305` - The `Poly1305` instance in the `Streaming` state.
695 /// * `accum_len` - The accumulated length of the input data.
696 ///
697 /// # Returns
698 ///
699 /// A new `StreamPoly1305` instance.
700 const fn from_parts(poly1305: Poly1305<Streaming>, accum_len: u32) -> Self {
701 Self { poly1305, accum_len }
702 }
703
704 /// Increments the accumulated length with the provided length.
705 ///
706 /// # Arguments
707 ///
708 /// * `len` - The length to add to the accumulator.
709 ///
710 /// # Returns
711 ///
712 /// A `Res` indicating the success or failure of the operation.
713 #[inline(always)]
714 fn incr_accum(&mut self, len: u32) -> Res {
715 let (accum_len, res) = ct::add_no_wrap(self.accum_len, len);
716 self.accum_len = accum_len;
717 res
718 }
719
720 /// Updates the streaming MAC computation with additional input.
721 ///
722 /// # Arguments
723 ///
724 /// * `input` - A byte slice representing the additional data to include.
725 ///
726 /// # Errors
727 ///
728 /// - The length of the `input` was greater than [`u32::MAX`].
729 /// - The total length that has been processed is greater than [`u32::MAX`],
730 ///
731 /// # Example
732 ///
733 /// ```
734 /// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
735 ///
736 /// # fn main() -> Result<(), wolf_crypto::Unspecified> {
737 /// let key: Key = [42u8; 32].into();
738 ///
739 /// let tag = Poly1305::new(key.as_ref())
740 /// .aead_padding()
741 /// .update(b"chunk1")?
742 /// .update(b"chunk2")?
743 /// .update(b"chunk3")?
744 /// .finalize();
745 /// # Ok(()) }
746 /// ```
747 pub fn update(mut self, input: &[u8]) -> Result<Self, Self> {
748 if let Some(input_len) = to_u32(input.len()) {
749 into_result!(self.incr_accum(input_len),
750 ok => {
751 // We MUST only invoke this AFTER knowing that the incr_accum succeeded.
752 // incr_accum uses the ct_add_no_wrap function, which may sound like it performs
753 // some form of saturating addition, but it does not. If the operation would
754 // overflow, no addition would take place. So, we can return self under the
755 // error case, and the state will not be corrupted.
756 unsafe { self.poly1305.update_unchecked(input) };
757 self
758 },
759 err => self
760 )
761 } else {
762 Err(self)
763 }
764 }
765
766 /// Finalizes the streaming MAC computation and returns the resulting `Tag`.
767 ///
768 /// # Returns
769 ///
770 /// The associated authentication [`Tag`].
771 ///
772 /// # Example
773 ///
774 /// ```
775 /// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
776 ///
777 /// # fn main() -> Result<(), wolf_crypto::Unspecified> {
778 /// let key: Key = [42u8; 32].into();
779 ///
780 /// let tag = Poly1305::new(key.as_ref())
781 /// .aead_padding()
782 /// .update(b"chunk1")?
783 /// .update(b"chunk2")?
784 /// .update(b"chunk3")?
785 /// .finalize();
786 /// # Ok(()) }
787 /// ```
788 pub fn finalize(self) -> Tag {
789 finalize_aead(self.poly1305, self.accum_len)
790 }
791}
792
793/// Represents an ongoing streaming MAC computation, allowing incremental updates.
794///
795/// # Example
796///
797/// ```
798/// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
799///
800/// # fn main() -> Result<(), wolf_crypto::Unspecified> {
801/// let key: Key = [42u8; 32].into();
802/// let tag = Poly1305::new(key.as_ref())
803/// .update(b"chunk1")?
804/// .update(b"chunk2")?
805/// .update(b"chunk3")?
806/// .finalize();
807/// # Ok(()) }
808/// ```
809pub struct StreamPoly1305 {
810 poly1305: Poly1305<Streaming>,
811 to_pad: u8
812}
813
814impl From<StreamPoly1305> for Unspecified {
815 #[inline]
816 fn from(value: StreamPoly1305) -> Self {
817 value.poly1305.into()
818 }
819}
820
821opaque_dbg! { StreamPoly1305 }
822
823impl StreamPoly1305 {
824 /// Creates a new `StreamPoly1305` instance from its parts.
825 ///
826 /// # Arguments
827 ///
828 /// * `poly1305` - The `Poly1305` instance in the `Streaming` state.
829 /// * `accum_len` - The accumulated length of the input data.
830 ///
831 /// # Returns
832 ///
833 /// A new `StreamPoly1305` instance.
834 const fn from_parts(poly1305: Poly1305<Streaming>, len: u32) -> Self {
835 Self { poly1305, to_pad: update_to_pad(0, len) }
836 }
837
838 /// Updates the streaming MAC computation with additional input.
839 ///
840 /// # Arguments
841 ///
842 /// * `input` - A byte slice representing the additional data to include.
843 ///
844 /// # Errors
845 ///
846 /// The length of the `input` was greater than [`u32::MAX`].
847 ///
848 /// # Example
849 ///
850 /// ```
851 /// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
852 ///
853 /// # fn main() -> Result<(), wolf_crypto::Unspecified> {
854 /// let key: Key = [42u8; 32].into();
855 ///
856 /// let tag = Poly1305::new(key.as_ref())
857 /// .update(b"chunk1")?
858 /// .update(b"chunk2")?
859 /// .update(b"chunk3")?
860 /// .finalize();
861 /// # Ok(()) }
862 /// ```
863 pub fn update(mut self, input: &[u8]) -> Result<Self, Self> {
864 if let Some(len) = to_u32(input.len()) {
865 self.to_pad = update_to_pad(self.to_pad, len);
866 unsafe { self.poly1305.update_unchecked(input) };
867 Ok(self)
868 } else {
869 Err(self)
870 }
871 }
872
873 /// Finalizes the streaming MAC computation and returns the resulting `Tag`.
874 ///
875 /// # Returns
876 ///
877 /// The associated authentication [`Tag`].
878 ///
879 /// # Example
880 ///
881 /// ```
882 /// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
883 ///
884 /// # fn main() -> Result<(), wolf_crypto::Unspecified> {
885 /// let key: Key = [42u8; 32].into();
886 ///
887 /// let tag = Poly1305::new(key.as_ref())
888 /// .update(b"chunk1")?
889 /// .update(b"chunk2")?
890 /// .update(b"chunk3")?
891 /// .finalize();
892 /// # Ok(()) }
893 /// ```
894 #[inline]
895 pub fn finalize(self) -> Tag {
896 finalize(self.poly1305, self.to_pad as u32)
897 }
898
899 /// Finalizes the constant-time streaming MAC computation and returns the resulting `Tag`.
900 ///
901 /// # Note
902 ///
903 /// It is far more common in practice to use to pad the [`finalize`] method. This is only here
904 /// for `XSalsa20Poly1305`.
905 ///
906 /// # Returns
907 ///
908 /// The associated authentication [`Tag`] representing all updates and the total length of the
909 /// updates.
910 ///
911 /// # Example
912 ///
913 /// ```
914 /// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
915 ///
916 /// let key: Key = [42u8; 32].into();
917 /// let tag = Poly1305::new(key.as_ref())
918 /// .update(b"chunk1").unwrap()
919 /// .update(b"chunk2").unwrap()
920 /// .finalize_no_padding();
921 /// ```
922 ///
923 /// [`finalize`]: Self::finalize
924 #[inline]
925 pub fn finalize_no_padding(self) -> Tag {
926 finalize_no_pad(self.poly1305)
927 }
928}
929
930/// Provides a constant-time interface for updating the MAC computation, enhancing resistance
931/// against side-channel attacks.
932///
933/// This uses the recent TLS AEAD padding scheme on finalization, if this is not desired see
934/// [`CtPoly1305`].
935///
936/// # Example
937///
938/// ```
939/// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
940///
941/// let key: Key = [42u8; 32].into();
942/// let ct_poly = Poly1305::new(key.as_ref())
943/// .aead_padding_ct()
944/// .update_ct(b"constant time ")
945/// .update_ct(b"chunk")
946/// .finalize()
947/// .unwrap();
948/// ```
949#[must_use]
950pub struct CtPoly1305Aead {
951 poly1305: Poly1305<Streaming>,
952 result: Res,
953 accum_len: u32
954}
955
956opaque_dbg! { CtPoly1305Aead }
957
958/// Creates a mask based on the slice length for constant-time adjustments.
959///
960/// # Arguments
961///
962/// * `len` - The length of the slice.
963///
964/// # Returns
965///
966/// If the length of the slice is greater than [`u32::MAX`] this will return all zeroes,
967/// otherwise this will return all ones.
968#[inline(always)]
969#[must_use]
970const fn slice_len_mask(len: usize) -> usize {
971 (can_cast_u32(len) as usize).wrapping_neg()
972}
973
974/// Adjusts the input slice based on the mask to ensure constant-time operations.
975///
976/// # Arguments
977///
978/// * `slice` - The input byte slice to adjust.
979///
980/// # Returns
981///
982/// A tuple containing the adjusted slice and the resulting `Res` state.
983#[inline(always)]
984fn adjust_slice(slice: &[u8]) -> (&[u8], Res) {
985 let mask = slice_len_mask(slice.len());
986
987 // So, of course, this isn't pure constant time. It has variable timing for lengths,
988 // though so does poly1305, and practically everything. Only way to avoid this is masking
989 // the computation which will never be perfect.
990 //
991 // Though, it does allow us to not need any early exit, the position of this error across
992 // multiple updates is not leaked via timing.
993 //
994 // So, there is variance in timing under this error yes, but this is given as at some point
995 // (finalize) there must be some branch to ensure the operation was successful and that
996 // the authentication code genuinely corresponds to the provided messages. With this
997 // approach there is a **reduction** in timing leakage, not a complete elimination of it.
998 (&slice[..(slice.len() & mask)], Res(mask != 0))
999}
1000
1001impl CtPoly1305Aead {
1002 /// Creates a new `CtPoly1305` instance from its parts.
1003 ///
1004 /// # Arguments
1005 ///
1006 /// * `poly1305` - The `Poly1305` instance in the `Streaming` state.
1007 /// * `result` - The current `Res` state.
1008 /// * `accum_len` - The accumulated length of the input data.
1009 ///
1010 /// # Returns
1011 ///
1012 /// A new `CtPoly1305` instance.
1013 const fn from_parts(poly1305: Poly1305<Streaming>, result: Res, accum_len: u32) -> Self {
1014 Self {
1015 poly1305,
1016 result,
1017 accum_len
1018 }
1019 }
1020
1021 /// Increments the accumulated length with the provided length without wrapping on overflow.
1022 ///
1023 /// # Arguments
1024 ///
1025 /// * `len` - The length to add to the accumulator.
1026 ///
1027 /// # Returns
1028 ///
1029 /// `Res` indicating if the operation would have overflowed the internal length.
1030 #[inline(always)]
1031 fn incr_accum(&mut self, len: u32) -> Res {
1032 let (accum_len, res) = ct::add_no_wrap(self.accum_len, len);
1033 self.accum_len = accum_len;
1034 res
1035 }
1036
1037 /// Adds more data to the constant-time streaming MAC computation.
1038 ///
1039 /// # Arguments
1040 ///
1041 /// * `input` - A byte slice representing the additional data to include.
1042 ///
1043 /// # Returns
1044 ///
1045 /// The updated `CtPoly1305` instance.
1046 ///
1047 /// # Example
1048 ///
1049 /// ```
1050 /// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
1051 ///
1052 /// let key: Key = [42u8; 32].into();
1053 /// let ct_poly = Poly1305::new(key.as_ref())
1054 /// .aead_padding_ct()
1055 /// .update_ct(b"chunk1")
1056 /// .update_ct(b"chunk2")
1057 /// .finalize()
1058 /// .unwrap();
1059 /// ```
1060 pub fn update_ct(mut self, input: &[u8]) -> Self {
1061 let (adjusted, mut res) = adjust_slice(input);
1062 res.ensure(self.incr_accum(adjusted.len() as u32));
1063
1064 unsafe { self.poly1305.update_unchecked(adjusted) };
1065
1066 self.result.ensure(res);
1067 self
1068 }
1069
1070 /// Returns `true` if no errors have been encountered to this point.
1071 #[must_use]
1072 pub const fn is_ok(&self) -> bool {
1073 self.result.is_ok()
1074 }
1075
1076 /// Returns `true` if an error has been encountered at some point.
1077 #[must_use]
1078 pub const fn is_err(&self) -> bool {
1079 self.result.is_err()
1080 }
1081
1082 /// Finalizes the constant-time streaming MAC computation and returns the resulting `Tag`.
1083 ///
1084 /// # Returns
1085 ///
1086 /// The associated authentication tag representing all updates and the total length of the
1087 /// updates.
1088 ///
1089 /// # Errors
1090 ///
1091 /// The `CtPoly1305` instance accumulates errors throughout the updating process without
1092 /// branching. There are two ways that this will return an error based on prior updates:
1093 ///
1094 /// - One of the provided inputs had a length which was greater than [`u32::MAX`].
1095 /// - The total length, accumulated from all inputs, is greater than [`u32::MAX`].
1096 ///
1097 /// # Example
1098 ///
1099 /// ```
1100 /// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
1101 ///
1102 /// let key: Key = [42u8; 32].into();
1103 /// let tag = Poly1305::new(key.as_ref())
1104 /// .aead_padding_ct()
1105 /// .update_ct(b"chunk1")
1106 /// .update_ct(b"chunk2")
1107 /// .finalize()
1108 /// .unwrap();
1109 /// ```
1110 pub fn finalize(self) -> Result<Tag, Unspecified> {
1111 let tag = finalize_aead(self.poly1305, self.accum_len);
1112 self.result.unit_err(tag)
1113 }
1114}
1115
1116/// Provides a constant-time interface for updating the MAC computation, enhancing resistance
1117/// against side-channel attacks.
1118///
1119/// # Example
1120///
1121/// ```
1122/// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
1123///
1124/// let key: Key = [42u8; 32].into();
1125/// let ct_poly = Poly1305::new(key.as_ref())
1126/// .update_ct(b"constant time ")
1127/// .update_ct(b"chunk")
1128/// .finalize()
1129/// .unwrap();
1130/// ```
1131#[must_use]
1132pub struct CtPoly1305 {
1133 poly1305: Poly1305<Streaming>,
1134 result: Res,
1135 to_pad: u8
1136}
1137
1138opaque_dbg! { CtPoly1305 }
1139
1140impl CtPoly1305 {
1141 /// Creates a new `CtPoly1305` instance from its parts.
1142 ///
1143 /// # Arguments
1144 ///
1145 /// * `poly1305` - The `Poly1305` instance in the `Streaming` state.
1146 /// * `result` - The current `Res` state.
1147 /// * `len` - The length of the input data. Used to compute the amount needed for padding
1148 /// overtime.
1149 ///
1150 /// # Returns
1151 ///
1152 /// A new `CtPoly1305` instance.
1153 const fn from_parts(poly1305: Poly1305<Streaming>, result: Res, len: u32) -> Self {
1154 Self {
1155 poly1305,
1156 result,
1157 to_pad: update_to_pad(0, len)
1158 }
1159 }
1160
1161 /// Adds more data to the constant-time streaming MAC computation.
1162 ///
1163 /// # Arguments
1164 ///
1165 /// * `input` - A byte slice representing the additional data to include.
1166 ///
1167 /// # Returns
1168 ///
1169 /// The updated `CtPoly1305` instance.
1170 ///
1171 /// # Example
1172 ///
1173 /// ```
1174 /// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
1175 ///
1176 /// let key: Key = [42u8; 32].into();
1177 /// let ct_poly = Poly1305::new(key.as_ref())
1178 /// .update_ct(b"chunk1")
1179 /// .update_ct(b"chunk2")
1180 /// .finalize()
1181 /// .unwrap();
1182 /// ```
1183 pub fn update_ct(mut self, input: &[u8]) -> Self {
1184 let (adjusted, res) = adjust_slice(input);
1185
1186 // adjusted length will always be less than u32::MAX
1187 self.to_pad = update_to_pad(self.to_pad, adjusted.len() as u32);
1188
1189 unsafe { self.poly1305.update_unchecked(adjusted) };
1190
1191 self.result.ensure(res);
1192 self
1193 }
1194
1195 /// Returns `true` if no errors have been encountered to this point.
1196 #[must_use]
1197 pub const fn is_ok(&self) -> bool { self.result.is_ok() }
1198
1199 /// Returns `true` if an error has been encountered at some point.
1200 #[must_use]
1201 pub const fn is_err(&self) -> bool {
1202 self.result.is_err()
1203 }
1204
1205 /// Finalizes the constant-time streaming MAC computation and returns the resulting `Tag`.
1206 ///
1207 /// # Returns
1208 ///
1209 /// The associated authentication [`Tag`] representing all updates and the total length of the
1210 /// updates.
1211 ///
1212 /// # Errors
1213 ///
1214 /// The `CtPoly1305` instance accumulates errors throughout the updating process without
1215 /// branching. The only error which could occur:
1216 ///
1217 /// One of the provided inputs had a length which was greater than [`u32::MAX`].
1218 ///
1219 /// # Example
1220 ///
1221 /// ```
1222 /// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
1223 ///
1224 /// let key: Key = [42u8; 32].into();
1225 /// let tag = Poly1305::new(key.as_ref())
1226 /// .update_ct(b"chunk1")
1227 /// .update_ct(b"chunk2")
1228 /// .finalize()
1229 /// .unwrap();
1230 /// ```
1231 pub fn finalize(self) -> Result<Tag, Unspecified> {
1232 let tag = finalize(self.poly1305, self.to_pad as u32);
1233 self.result.unit_err(tag)
1234 }
1235
1236 /// Finalizes the constant-time streaming MAC computation and returns the resulting `Tag`.
1237 ///
1238 /// # Note
1239 ///
1240 /// It is far more common in practice to use to pad the [`finalize`] method. This is only here
1241 /// for `XSalsa20Poly1305`.
1242 ///
1243 /// # Returns
1244 ///
1245 /// The associated authentication [`Tag`] representing all updates and the total length of the
1246 /// updates.
1247 ///
1248 /// # Errors
1249 ///
1250 /// The `CtPoly1305` instance accumulates errors throughout the updating process without
1251 /// branching. The only error which could occur:
1252 ///
1253 /// One of the provided inputs had a length which was greater than [`u32::MAX`].
1254 ///
1255 /// # Example
1256 ///
1257 /// ```
1258 /// use wolf_crypto::{mac::{Poly1305, poly1305::Key}, aead::Tag};
1259 ///
1260 /// let key: Key = [42u8; 32].into();
1261 /// let tag = Poly1305::new(key.as_ref())
1262 /// .update_ct(b"chunk1")
1263 /// .update_ct(b"chunk2")
1264 /// .finalize_no_padding()
1265 /// .unwrap();
1266 /// ```
1267 ///
1268 /// [`finalize`]: Self::finalize
1269 pub fn finalize_no_padding(self) -> Result<Tag, Unspecified> {
1270 let tag = finalize_no_pad(self.poly1305);
1271 self.result.unit_err(tag)
1272 }
1273}
1274
1275#[cfg(test)]
1276use poly1305::universal_hash::generic_array::{GenericArray, ArrayLength};
1277
1278#[cfg(test)]
1279const fn rc_to_blocks<T, N: ArrayLength<T>>(data: &[T]) -> (&[GenericArray<T, N>], &[T]) {
1280 let nb = data.len() / N::USIZE;
1281 let (left, right) = data.split_at(nb * N::USIZE);
1282 let p = left.as_ptr().cast::<GenericArray<T, N>>();
1283 // SAFETY: we guarantee that `blocks` does not point outside `data`
1284 // and `p` is valid for reads
1285 #[allow(unsafe_code)]
1286 let blocks = unsafe { core::slice::from_raw_parts(p, nb) };
1287 (blocks, right)
1288}
1289
1290#[cfg(test)]
1291use poly1305::{Poly1305 as rc_Poly1305, universal_hash::{KeyInit, UniversalHash}};
1292
1293#[cfg(test)]
1294fn construct_polys(key: [u8; 32]) -> (rc_Poly1305, Poly1305<Ready>) {
1295 let rc_key = poly1305::Key::from_slice(key.as_slice());
1296 (rc_Poly1305::new(rc_key), Poly1305::new(KeyRef::new(&key)))
1297}
1298
1299#[cfg(test)]
1300mod tests {
1301 use super::*;
1302
1303 #[test]
1304 fn smoke() {
1305 let key: Key = [42u8; 32].into();
1306 let tag = Poly1305::new(key.as_ref())
1307 .aead_padding_ct()
1308 .update_ct(b"hello world")
1309 .update_ct(b"good day to you")
1310 .update_ct(b"mmm yes mm yes indeed mm")
1311 .update_ct(b"hmm...")
1312 .finalize()
1313 .unwrap();
1314
1315 let o_tag = Poly1305::new(key.as_ref())
1316 .mac(b"hello worldgood day to yoummm yes mm yes indeed mmhmm...", b"")
1317 .unwrap();
1318
1319 assert_eq!(tag, o_tag);
1320 }
1321
1322 #[test]
1323 fn rust_crypto_aligned() {
1324 let key = [42u8; 32];
1325 let (mut rc, wc) = construct_polys(key);
1326 let data = b"hello world we operate equivalen";
1327
1328 let (blocks, _rem) = rc_to_blocks(data);
1329
1330 rc.update(blocks);
1331 let rc_out = rc.finalize();
1332
1333 let wc_out = wc.update(data).unwrap().finalize();
1334
1335 assert_eq!(wc_out, rc_out.as_slice());
1336 }
1337
1338 #[test]
1339 fn rust_crypto_unaligned() {
1340 let key = [42u8; 32];
1341 let (mut rc, wc) = construct_polys(key);
1342 let data = b"hello world we operate equivalently";
1343
1344 rc.update_padded(data);
1345 let rc_tag = rc.finalize();
1346 let tag = wc.update(data).unwrap().finalize();
1347
1348 assert_eq!(tag, rc_tag.as_slice());
1349 }
1350}
1351
1352#[cfg(kani)]
1353const fn wc_to_pad(len_to_pad: u32) -> u32 {
1354 ((-(len_to_pad as isize)) & 15) as u32
1355}
1356
1357#[cfg(kani)]
1358const fn wc_to_pad_64(len_to_pad: u64) -> u32 {
1359 ((-(len_to_pad as i128)) & 15) as u32
1360}
1361
1362#[cfg(test)]
1363mod property_tests {
1364 use super::*;
1365 use proptest::prelude::*;
1366 use crate::aes::test_utils::{BoundList, AnyList};
1367
1368 proptest! {
1369 #![proptest_config(ProptestConfig::with_cases(5_000))]
1370
1371 #[test]
1372 fn wc_poly_is_eq_to_rc_poly(
1373 key in any::<[u8; 32]>(),
1374 data in any::<BoundList<1024>>()
1375 ) {
1376 let (mut rc, wc) = construct_polys(key);
1377
1378 rc.update_padded(data.as_slice());
1379
1380 let tag = wc.update(data.as_slice()).unwrap().finalize();
1381 let rc_tag = rc.finalize();
1382
1383 prop_assert_eq!(tag, rc_tag.as_slice());
1384 }
1385
1386 #[test]
1387 fn wc_poly_multi_updates_is_eq_to_rc_poly_oneshot(
1388 key in any::<[u8; 32]>(),
1389 data in any::<AnyList<32, BoundList<256>>>()
1390 ) {
1391 let (mut rc, wc) = construct_polys(key);
1392
1393 let mut wc = wc.normal();
1394
1395 for input in data.as_slice() {
1396 wc = wc.update(input.as_slice()).unwrap();
1397 }
1398
1399 let joined = data.join();
1400 rc.update_padded(joined.as_slice());
1401
1402 let tag = wc.finalize();
1403 let rc_tag = rc.finalize();
1404
1405 prop_assert_eq!(tag, rc_tag.as_slice());
1406 }
1407
1408 #[test]
1409 fn multi_updates_is_eq_to_oneshot_tls_aead_scheme(
1410 key in any::<Key>(),
1411 data in any::<AnyList<32, BoundList<256>>>()
1412 ) {
1413 let mut updates = Poly1305::new(key.as_ref()).aead_padding();
1414
1415 for input in data.as_slice() {
1416 updates = updates.update(input.as_slice()).unwrap();
1417 }
1418
1419 let tag = updates.finalize();
1420
1421 let joined = data.join();
1422 let m_tag = Poly1305::new(key).mac(joined.as_slice(), ()).unwrap();
1423
1424 prop_assert_eq!(tag, m_tag);
1425 }
1426
1427 #[test]
1428 fn multi_updates_ct_is_eq_to_normal(
1429 key in any::<Key>(),
1430 data in any::<AnyList<32, BoundList<256>>>()
1431 ) {
1432 let mut poly = Poly1305::new(key.as_ref()).normal();
1433 let mut poly_ct = Poly1305::new(key.as_ref()).normal_ct();
1434
1435 for input in data.as_slice() {
1436 poly = poly.update(input.as_slice()).unwrap();
1437 poly_ct = poly_ct.update_ct(input.as_slice());
1438 }
1439
1440 let tag = poly.finalize();
1441 let tag_ct = poly_ct.finalize().unwrap();
1442
1443 prop_assert_eq!(tag, tag_ct);
1444 }
1445
1446 #[test]
1447 fn multi_updates_is_eq_oneshot(
1448 key in any::<Key>(),
1449 data in any::<AnyList<32, BoundList<256>>>()
1450 ) {
1451 let mut poly = Poly1305::new(key.as_ref()).normal();
1452
1453 for input in data.as_slice() {
1454 poly = poly.update(input.as_slice()).unwrap();
1455 }
1456
1457 let tag = poly.finalize();
1458
1459 let joined = data.join();
1460 let o_tag = Poly1305::new(key)
1461 .update(joined.as_slice()).unwrap()
1462 .finalize();
1463
1464 prop_assert_eq!(tag, o_tag);
1465 }
1466
1467 #[test]
1468 fn multi_updates_ct_is_eq_oneshot(
1469 key in any::<Key>(),
1470 data in any::<AnyList<32, BoundList<256>>>()
1471 ) {
1472 let mut poly = Poly1305::new(key.as_ref()).normal_ct();
1473
1474 for input in data.as_slice() {
1475 poly = poly.update_ct(input.as_slice());
1476 }
1477
1478 let tag = poly.finalize().unwrap();
1479
1480 let joined = data.join();
1481 let o_tag = Poly1305::new(key)
1482 .update(joined.as_slice()).unwrap()
1483 .finalize();
1484
1485 prop_assert_eq!(tag, o_tag);
1486 }
1487 }
1488}
1489
1490#[cfg(kani)]
1491mod proofs {
1492 use kani::proof;
1493 use super::*;
1494
1495 #[proof]
1496 fn univ_update_to_pad_is_no_wrap_mask() {
1497 let existing: u32 = kani::any();
1498 let to_add: u32 = kani::any();
1499
1500 let utp = update_to_pad((existing & 15) as u8, to_add) as u64;
1501 let genuine = ((existing as u64) + (to_add as u64)) & 15;
1502
1503 kani::assert(
1504 utp == genuine,
1505 "The wrapping addition must always be equivalent to non-wrapping output"
1506 );
1507 }
1508
1509 #[proof]
1510 fn univ_update_to_pad_holds_for_wc_pad_algo() {
1511 let start: u32 = kani::any();
1512 let end: u32 = kani::any();
1513
1514 let utp = update_to_pad((start & 15) as u8, end);
1515
1516 kani::assert(
1517 wc_to_pad(utp as u32) == wc_to_pad_64((start as u64) + (end as u64)),
1518 "update_to_pad must be equivalent to the total length in the eyes of the wolfcrypt \
1519 padding algorithm."
1520 );
1521 }
1522
1523 #[proof]
1524 fn univ_mask_is_no_mask_to_wc_pad_algo() {
1525 let some_num: u64 = kani::any();
1526
1527 kani::assert(
1528 wc_to_pad_64(some_num & 15) == wc_to_pad_64(some_num),
1529 "wolfcrypt's to pad must result in the same output for the input mask 15 and raw input"
1530 )
1531 }
1532}