| // Copyright 2015-2016 Brian Smith. |
| // Portions Copyright (c) 2014, 2015, Google Inc. |
| // |
| // Permission to use, copy, modify, and/or distribute this software for any |
| // purpose with or without fee is hereby granted, provided that the above |
| // copyright notice and this permission notice appear in all copies. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES |
| // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY |
| // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| |
| // TODO: enforce maximum input length. |
| |
| use super::{block::BLOCK_LEN, Tag, TAG_LEN}; |
| use crate::{c, cpu}; |
| |
| /// A Poly1305 key. |
| pub(super) struct Key { |
| key_and_nonce: [u8; KEY_LEN], |
| cpu_features: cpu::Features, |
| } |
| |
| const KEY_LEN: usize = 2 * BLOCK_LEN; |
| |
| impl Key { |
| #[inline] |
| pub(super) fn new(key_and_nonce: [u8; KEY_LEN], cpu_features: cpu::Features) -> Self { |
| Self { |
| key_and_nonce, |
| cpu_features, |
| } |
| } |
| } |
| |
| pub struct Context { |
| state: poly1305_state, |
| #[allow(dead_code)] |
| cpu_features: cpu::Features, |
| } |
| |
| // Keep in sync with `poly1305_state` in GFp/poly1305.h. |
| // |
| // The C code, in particular the way the `poly1305_aligned_state` functions |
| // are used, is only correct when the state buffer is 64-byte aligned. |
| #[repr(C, align(64))] |
| struct poly1305_state([u8; OPAQUE_LEN]); |
| const OPAQUE_LEN: usize = 512; |
| |
| // Abstracts the dispatching logic that chooses the NEON implementation if and |
| // only if it would work. |
| macro_rules! dispatch { |
| ( $features:expr => |
| ( $f:ident | $neon_f:ident ) |
| ( $( $p:ident : $t:ty ),+ ) |
| ( $( $a:expr ),+ ) ) => { |
| match () { |
| // Apple's 32-bit ARM ABI is incompatible with the assembly code. |
| #[cfg(all(target_arch = "arm", not(target_vendor = "apple")))] |
| () if cpu::arm::NEON.available($features) => { |
| extern "C" { |
| fn $neon_f( $( $p : $t ),+ ); |
| } |
| unsafe { $neon_f( $( $a ),+ ) } |
| } |
| () => { |
| extern "C" { |
| fn $f( $( $p : $t ),+ ); |
| } |
| unsafe { $f( $( $a ),+ ) } |
| } |
| } |
| } |
| } |
| |
| impl Context { |
| #[inline] |
| pub(super) fn from_key( |
| Key { |
| key_and_nonce, |
| cpu_features, |
| }: Key, |
| ) -> Self { |
| let mut ctx = Self { |
| state: poly1305_state([0u8; OPAQUE_LEN]), |
| cpu_features, |
| }; |
| |
| dispatch!( |
| cpu_features => |
| (GFp_poly1305_init | GFp_poly1305_init_neon) |
| (statep: &mut poly1305_state, key: &[u8; KEY_LEN]) |
| (&mut ctx.state, &key_and_nonce)); |
| |
| ctx |
| } |
| |
| #[inline(always)] |
| pub fn update(&mut self, input: &[u8]) { |
| dispatch!( |
| self.cpu_features => |
| (GFp_poly1305_update | GFp_poly1305_update_neon) |
| (statep: &mut poly1305_state, input: *const u8, in_len: c::size_t) |
| (&mut self.state, input.as_ptr(), input.len())); |
| } |
| |
| pub(super) fn finish(mut self) -> Tag { |
| let mut tag = Tag([0u8; TAG_LEN]); |
| dispatch!( |
| self.cpu_features => |
| (GFp_poly1305_finish | GFp_poly1305_finish_neon) |
| (statep: &mut poly1305_state, mac: &mut [u8; TAG_LEN]) |
| (&mut self.state, &mut tag.0)); |
| tag |
| } |
| } |
| |
| /// Implements the original, non-IETF padding semantics. |
| /// |
| /// This is used by chacha20_poly1305_openssh and the standalone |
| /// poly1305 test vectors. |
| pub(super) fn sign(key: Key, input: &[u8]) -> Tag { |
| let mut ctx = Context::from_key(key); |
| ctx.update(input); |
| ctx.finish() |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use crate::test; |
| use core::convert::TryInto; |
| |
| // Adapted from BoringSSL's crypto/poly1305/poly1305_test.cc. |
| #[test] |
| pub fn test_poly1305() { |
| let cpu_features = cpu::features(); |
| test::run(test_file!("poly1305_test.txt"), |section, test_case| { |
| assert_eq!(section, ""); |
| let key = test_case.consume_bytes("Key"); |
| let key: &[u8; BLOCK_LEN * 2] = key.as_slice().try_into().unwrap(); |
| let input = test_case.consume_bytes("Input"); |
| let expected_mac = test_case.consume_bytes("MAC"); |
| let key = Key::new(*key, cpu_features); |
| let Tag(actual_mac) = sign(key, &input); |
| assert_eq!(expected_mac, actual_mac.as_ref()); |
| |
| Ok(()) |
| }) |
| } |
| } |