blob: a87f709a4d4a474e58cda03dd3e1eda88c6df70e [file] [log] [blame]
// 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(())
})
}
}