| 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 | |