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::{Tag, TAG_LEN};
19use crate::{c, cpu};
20
21/// A Poly1305 key.
22pub(super) struct Key {
23 key_and_nonce: [u8; KEY_LEN],
24}
25
26pub(super) const BLOCK_LEN: usize = 16;
27pub(super) const KEY_LEN: usize = 2 * BLOCK_LEN;
28
29impl Key {
30 #[inline]
31 pub(super) fn new(key_and_nonce: [u8; KEY_LEN]) -> Self {
32 Self { key_and_nonce }
33 }
34}
35
36pub struct Context {
37 state: poly1305_state,
38 #[allow(dead_code)]
39 cpu_features: cpu::Features,
40}
41
42// Keep in sync with `poly1305_state` in ring-core/poly1305.h.
43//
44// The C code, in particular the way the `poly1305_aligned_state` functions
45// are used, is only correct when the state buffer is 64-byte aligned.
46#[repr(C, align(64))]
47struct poly1305_state([u8; OPAQUE_LEN]);
48const OPAQUE_LEN: usize = 512;
49
50// Abstracts the dispatching logic that chooses the NEON implementation if and
51// only if it would work.
52macro_rules! dispatch {
53 ( $features:expr =>
54 ( $f:ident | $neon_f:ident )
55 ( $( $p:ident : $t:ty ),+ )
56 ( $( $a:expr ),+ ) ) => {
57 match () {
58 // Apple's 32-bit ARM ABI is incompatible with the assembly code.
59 #[cfg(all(target_arch = "arm", not(target_vendor = "apple")))]
60 () if cpu::arm::NEON.available($features) => {
61 prefixed_extern! {
62 fn $neon_f( $( $p : $t ),+ );
63 }
64 unsafe { $neon_f( $( $a ),+ ) }
65 }
66 () => {
67 prefixed_extern! {
68 fn $f( $( $p : $t ),+ );
69 }
70 unsafe { $f( $( $a ),+ ) }
71 }
72 }
73 }
74}
75
76impl Context {
77 #[inline]
78 pub(super) fn from_key(Key { key_and_nonce }: Key, cpu_features: cpu::Features) -> Self {
79 let mut ctx = Self {
80 state: poly1305_state([0u8; OPAQUE_LEN]),
81 cpu_features,
82 };
83
84 dispatch!(
85 cpu_features =>
86 (CRYPTO_poly1305_init | CRYPTO_poly1305_init_neon)
87 (statep: &mut poly1305_state, key: &[u8; KEY_LEN])
88 (&mut ctx.state, &key_and_nonce));
89
90 ctx
91 }
92
93 #[inline(always)]
94 pub fn update(&mut self, input: &[u8]) {
95 dispatch!(
96 self.cpu_features =>
97 (CRYPTO_poly1305_update | CRYPTO_poly1305_update_neon)
98 (statep: &mut poly1305_state, input: *const u8, in_len: c::size_t)
99 (&mut self.state, input.as_ptr(), input.len()));
100 }
101
102 pub(super) fn finish(mut self) -> Tag {
103 let mut tag = Tag([0u8; TAG_LEN]);
104 dispatch!(
105 self.cpu_features =>
106 (CRYPTO_poly1305_finish | CRYPTO_poly1305_finish_neon)
107 (statep: &mut poly1305_state, mac: &mut [u8; TAG_LEN])
108 (&mut self.state, &mut tag.0));
109 tag
110 }
111}
112
113/// Implements the original, non-IETF padding semantics.
114///
115/// This is used by chacha20_poly1305_openssh and the standalone
116/// poly1305 test vectors.
117pub(super) fn sign(key: Key, input: &[u8], cpu_features: cpu::Features) -> Tag {
118 let mut ctx: Context = Context::from_key(key, cpu_features);
119 ctx.update(input);
120 ctx.finish()
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use crate::test;
127
128 // Adapted from BoringSSL's crypto/poly1305/poly1305_test.cc.
129 #[test]
130 pub fn test_poly1305() {
131 let cpu_features = cpu::features();
132 test::run(test_file!("poly1305_test.txt"), |section, test_case| {
133 assert_eq!(section, "");
134 let key = test_case.consume_bytes("Key");
135 let key: &[u8; KEY_LEN] = key.as_slice().try_into().unwrap();
136 let input = test_case.consume_bytes("Input");
137 let expected_mac = test_case.consume_bytes("MAC");
138 let key = Key::new(*key);
139 let Tag(actual_mac) = sign(key, &input, cpu_features);
140 assert_eq!(expected_mac, actual_mac.as_ref());
141
142 Ok(())
143 })
144 }
145}
146