ring/aead/
poly1305.rs

1// Copyright 2015-2016 Brian Smith.
2// Portions Copyright (c) 2014, 2015, Google Inc.
3//
4// Permission to use, copy, modify, and/or distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
11// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
16// TODO: enforce maximum input length.
17
18use super::{block::BLOCK_LEN, Tag, TAG_LEN};
19use crate::{c, cpu};
20
21/// A Poly1305 key.
22pub(super) struct Key {
23    key_and_nonce: [u8; KEY_LEN],
24    cpu_features: cpu::Features,
25}
26
27const KEY_LEN: usize = 2 * BLOCK_LEN;
28
29impl Key {
30    #[inline]
31    pub(super) fn new(key_and_nonce: [u8; KEY_LEN], cpu_features: cpu::Features) -> Self {
32        Self {
33            key_and_nonce,
34            cpu_features,
35        }
36    }
37}
38
39pub struct Context {
40    state: poly1305_state,
41    #[allow(dead_code)]
42    cpu_features: cpu::Features,
43}
44
45// Keep in sync with `poly1305_state` in GFp/poly1305.h.
46//
47// The C code, in particular the way the `poly1305_aligned_state` functions
48// are used, is only correct when the state buffer is 64-byte aligned.
49#[repr(C, align(64))]
50struct poly1305_state([u8; OPAQUE_LEN]);
51const OPAQUE_LEN: usize = 512;
52
53// Abstracts the dispatching logic that chooses the NEON implementation if and
54// only if it would work.
55macro_rules! dispatch {
56    ( $features:expr =>
57      ( $f:ident | $neon_f:ident )
58      ( $( $p:ident : $t:ty ),+ )
59      ( $( $a:expr ),+ ) ) => {
60        match () {
61            // Apple's 32-bit ARM ABI is incompatible with the assembly code.
62            #[cfg(all(target_arch = "arm", not(target_vendor = "apple")))]
63            () if cpu::arm::NEON.available($features) => {
64                extern "C" {
65                    fn $neon_f( $( $p : $t ),+ );
66                }
67                unsafe { $neon_f( $( $a ),+ ) }
68            }
69            () => {
70                extern "C" {
71                    fn $f( $( $p : $t ),+ );
72                }
73                unsafe { $f( $( $a ),+ ) }
74            }
75        }
76    }
77}
78
79impl Context {
80    #[inline]
81    pub(super) fn from_key(
82        Key {
83            key_and_nonce,
84            cpu_features,
85        }: Key,
86    ) -> Self {
87        let mut ctx = Self {
88            state: poly1305_state([0u8; OPAQUE_LEN]),
89            cpu_features,
90        };
91
92        dispatch!(
93            cpu_features =>
94            (GFp_poly1305_init | GFp_poly1305_init_neon)
95            (statep: &mut poly1305_state, key: &[u8; KEY_LEN])
96            (&mut ctx.state, &key_and_nonce));
97
98        ctx
99    }
100
101    #[inline(always)]
102    pub fn update(&mut self, input: &[u8]) {
103        dispatch!(
104            self.cpu_features =>
105            (GFp_poly1305_update | GFp_poly1305_update_neon)
106            (statep: &mut poly1305_state, input: *const u8, in_len: c::size_t)
107            (&mut self.state, input.as_ptr(), input.len()));
108    }
109
110    pub(super) fn finish(mut self) -> Tag {
111        let mut tag = Tag([0u8; TAG_LEN]);
112        dispatch!(
113            self.cpu_features =>
114            (GFp_poly1305_finish | GFp_poly1305_finish_neon)
115            (statep: &mut poly1305_state, mac: &mut [u8; TAG_LEN])
116            (&mut self.state, &mut tag.0));
117        tag
118    }
119}
120
121/// Implements the original, non-IETF padding semantics.
122///
123/// This is used by chacha20_poly1305_openssh and the standalone
124/// poly1305 test vectors.
125pub(super) fn sign(key: Key, input: &[u8]) -> Tag {
126    let mut ctx = Context::from_key(key);
127    ctx.update(input);
128    ctx.finish()
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134    use crate::test;
135    use core::convert::TryInto;
136
137    // Adapted from BoringSSL's crypto/poly1305/poly1305_test.cc.
138    #[test]
139    pub fn test_poly1305() {
140        let cpu_features = cpu::features();
141        test::run(test_file!("poly1305_test.txt"), |section, test_case| {
142            assert_eq!(section, "");
143            let key = test_case.consume_bytes("Key");
144            let key: &[u8; BLOCK_LEN * 2] = key.as_slice().try_into().unwrap();
145            let input = test_case.consume_bytes("Input");
146            let expected_mac = test_case.consume_bytes("MAC");
147            let key = Key::new(*key, cpu_features);
148            let Tag(actual_mac) = sign(key, &input);
149            assert_eq!(expected_mac, actual_mac.as_ref());
150
151            Ok(())
152        })
153    }
154}