1 | // Copyright 2015-2025 Brian Smith. |
2 | // |
3 | // Permission to use, copy, modify, and/or distribute this software for any |
4 | // purpose with or without fee is hereby granted, provided that the above |
5 | // copyright notice and this permission notice appear in all copies. |
6 | // |
7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES |
8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY |
10 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
12 | // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
13 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | |
15 | // TODO: enforce maximum input length. |
16 | |
17 | use super::{Tag, TAG_LEN}; |
18 | #[cfg (all(target_arch = "arm" , target_endian = "little" ))] |
19 | use crate::cpu::GetFeature as _; |
20 | use crate::{cpu, polyfill::slice::AsChunks}; |
21 | |
22 | mod ffi_arm_neon; |
23 | mod ffi_fallback; |
24 | |
25 | /// A Poly1305 key. |
26 | pub(super) struct Key { |
27 | key_and_nonce: [u8; KEY_LEN], |
28 | } |
29 | |
30 | pub(super) const BLOCK_LEN: usize = 16; |
31 | pub(super) const KEY_LEN: usize = 2 * BLOCK_LEN; |
32 | |
33 | impl Key { |
34 | #[inline ] |
35 | pub(super) fn new(key_and_nonce: [u8; KEY_LEN]) -> Self { |
36 | Self { key_and_nonce } |
37 | } |
38 | } |
39 | |
40 | pub(super) enum Context { |
41 | #[cfg (all(target_arch = "arm" , target_endian = "little" ))] |
42 | ArmNeon(ffi_arm_neon::State), |
43 | Fallback(ffi_fallback::State), |
44 | } |
45 | |
46 | impl Context { |
47 | #[inline ] |
48 | pub(super) fn from_key(key: Key, cpu: cpu::Features) -> Self { |
49 | #[cfg (all(target_arch = "arm" , target_endian = "little" ))] |
50 | if let Some(cpu) = cpu.get_feature() { |
51 | return ffi_arm_neon::State::new_context(key, cpu); |
52 | } |
53 | let _: cpu::Features = cpu; |
54 | ffi_fallback::State::new_context(key) |
55 | } |
56 | |
57 | pub fn update_block(&mut self, input: [u8; BLOCK_LEN]) { |
58 | self.update(AsChunks::from_ref(&input)) |
59 | } |
60 | |
61 | pub fn update(&mut self, input: AsChunks<u8, BLOCK_LEN>) { |
62 | self.update_internal(input.as_flattened()); |
63 | } |
64 | |
65 | fn update_internal(&mut self, input: &[u8]) { |
66 | match self { |
67 | #[cfg (all(target_arch = "arm" , target_endian = "little" ))] |
68 | Self::ArmNeon(state) => state.update_internal(input), |
69 | Self::Fallback(state) => state.update_internal(input), |
70 | } |
71 | } |
72 | |
73 | pub(super) fn finish(mut self, input: &[u8]) -> Tag { |
74 | self.update_internal(input); |
75 | match self { |
76 | #[cfg (all(target_arch = "arm" , target_endian = "little" ))] |
77 | Self::ArmNeon(state) => state.finish(), |
78 | Self::Fallback(state) => state.finish(), |
79 | } |
80 | } |
81 | } |
82 | |
83 | /// Implements the original, non-IETF padding semantics. |
84 | /// |
85 | /// This is used by chacha20_poly1305_openssh and the standalone |
86 | /// poly1305 test vectors. |
87 | pub(super) fn sign(key: Key, input: &[u8], cpu_features: cpu::Features) -> Tag { |
88 | let ctx: Context = Context::from_key(key, cpu_features); |
89 | ctx.finish(input) |
90 | } |
91 | |
92 | #[cfg (test)] |
93 | mod tests { |
94 | use super::*; |
95 | use crate::test; |
96 | |
97 | // Adapted from BoringSSL's crypto/poly1305/poly1305_test.cc. |
98 | #[test ] |
99 | pub fn test_poly1305() { |
100 | let cpu_features = cpu::features(); |
101 | test::run(test_file!("poly1305_test.txt" ), |section, test_case| { |
102 | assert_eq!(section, "" ); |
103 | let key = test_case .consume_bytes("Key" ); |
104 | let key: &[u8; KEY_LEN] = key.as_slice().try_into().unwrap(); |
105 | let input = test_case .consume_bytes("Input" ); |
106 | let expected_mac = test_case .consume_bytes("MAC" ); |
107 | let key = Key::new(*key); |
108 | let Tag(actual_mac) = sign(key, &input, cpu_features); |
109 | assert_eq!(expected_mac, actual_mac.as_ref()); |
110 | |
111 | Ok(()) |
112 | }) |
113 | } |
114 | } |
115 | |