aws_lc_rs/aead/
poly1305.rs

1// Copyright 2015-2016 Brian Smith.
2// SPDX-License-Identifier: ISC
3// Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4// SPDX-License-Identifier: Apache-2.0 OR ISC
5
6// TODO: enforce maximum input length.
7
8use super::{Tag, TAG_LEN};
9use crate::aws_lc::{CRYPTO_poly1305_finish, CRYPTO_poly1305_init, CRYPTO_poly1305_update};
10use crate::cipher::block::BLOCK_LEN;
11use core::mem::MaybeUninit;
12
13/// A Poly1305 key.
14pub(super) struct Key {
15    pub(super) key_and_nonce: [u8; KEY_LEN],
16}
17
18const KEY_LEN: usize = 2 * BLOCK_LEN;
19
20impl Key {
21    #[inline]
22    #[allow(dead_code)]
23    pub(super) fn new(key_and_nonce: [u8; KEY_LEN]) -> Self {
24        Self { key_and_nonce }
25    }
26}
27
28pub struct Context {
29    state: poly1305_state,
30}
31
32// Keep in sync with `poly1305_state` in GFp/poly1305.h.
33//
34// The C code, in particular the way the `poly1305_aligned_state` functions
35// are used, is only correct when the state buffer is 64-byte aligned.
36#[repr(C, align(64))]
37#[allow(non_camel_case_types)]
38struct poly1305_state(aws_lc::poly1305_state);
39
40impl Context {
41    #[inline]
42    pub(super) fn from_key(Key { key_and_nonce }: Key) -> Self {
43        unsafe {
44            // Use `zeroed()` instead of `uninit()` for MSAN compatibility.
45            // `CRYPTO_poly1305_init` may not write all 512 bytes of the opaque
46            // `poly1305_state` buffer, so MSAN would flag `assume_init()` as
47            // reading uninitialized memory. Zeroing is safe since the underlying
48            // type is `[u8; 512]`.
49            let mut state = MaybeUninit::<poly1305_state>::zeroed();
50            CRYPTO_poly1305_init(state.as_mut_ptr().cast(), key_and_nonce.as_ptr());
51            Self {
52                state: state.assume_init(),
53            }
54        }
55    }
56
57    #[inline]
58    pub fn update(&mut self, input: &[u8]) {
59        unsafe {
60            CRYPTO_poly1305_update(
61                self.state.0.as_mut_ptr().cast(),
62                input.as_ptr(),
63                input.len(),
64            );
65        }
66    }
67
68    #[inline]
69    pub(super) fn finish(mut self) -> Tag {
70        unsafe {
71            let mut tag = MaybeUninit::<[u8; TAG_LEN]>::uninit();
72            CRYPTO_poly1305_finish(self.state.0.as_mut_ptr().cast(), tag.as_mut_ptr().cast());
73            crate::fips::set_fips_service_status_unapproved();
74            Tag(tag.assume_init(), TAG_LEN)
75        }
76    }
77}
78
79/// Implements the original, non-IETF padding semantics.
80///
81/// This is used by `chacha20_poly1305_openssh` and the standalone
82/// poly1305 test vectors.
83#[inline]
84pub(super) fn sign(key: Key, input: &[u8]) -> Tag {
85    let mut ctx = Context::from_key(key);
86    ctx.update(input);
87    ctx.finish()
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93    use crate::{test, test_file};
94
95    // Adapted from BoringSSL's crypto/poly1305/poly1305_test.cc.
96    #[test]
97    pub fn test_poly1305() {
98        test::run(
99            test_file!("data/poly1305_test.txt"),
100            |section, test_case| {
101                assert_eq!(section, "");
102                let key = test_case.consume_bytes("Key");
103                let key: &[u8; BLOCK_LEN * 2] = key.as_slice().try_into().unwrap();
104                let input = test_case.consume_bytes("Input");
105                let expected_mac = test_case.consume_bytes("MAC");
106                let key = Key::new(*key);
107                let Tag(actual_mac, _) = sign(key, &input);
108                assert_eq!(expected_mac, actual_mac.as_ref());
109
110                Ok(())
111            },
112        );
113    }
114}