1use core::convert::TryInto;
4
5use crate::segmented_int::{SegmentedInt, SegmentedIntDescriptor};
6
7type Num = SegmentedInt<Poly1305Descriptor>;
9
10struct Poly1305Descriptor;
11
12impl SegmentedIntDescriptor for Poly1305Descriptor {
13 type SegmentType = u64;
14
15 const SEGMENT_SIZE: u16 = 26;
16 const CARRY_FACTOR: u64 = 5;
17 const SEGMENT_MASK: u64 = LOW_26_BITS;
18 const ZERO: u64 = 0;
19 const ONE: u64 = 1;
20}
21
22const LOW_26_BITS: u64 = 0x03ff_ffff;
23
24impl Num {
25 fn zero() -> Self {
26 Self {segments: [0; 5]}
27 }
28
29 fn from_16_le_bytes(bytes: [u8; 16]) -> Self {
30 let as_num = u128::from_le_bytes(bytes);
31 let mut segments = [0; 5];
32
33 for i in 0 .. 5 {
34 segments[i] = (as_num >> (26 * i)) as u64 & LOW_26_BITS;
35 }
36
37 Self {segments}
38 }
39
40 fn from_complete_chunk(chunk: &[u8; 16]) -> Self {
41 let mut out = Self::from_16_le_bytes(*chunk);
42
43 out.segments[4] |= 1 << 24;
45
46 out
47 }
48
49 fn from_incomplete_chunk(chunk: &[u8]) -> Self {
50 debug_assert!(1 <= chunk.len() && chunk.len() < 16);
51
52 let mut bytes = [0; 16];
53 bytes[.. chunk.len()].copy_from_slice(chunk);
54
55 bytes[chunk.len()] = 1;
56
57 Self::from_16_le_bytes(bytes)
58 }
59
60 fn to_16_le_bytes(mut self) -> [u8; 16] {
61 self.full_modular_reduction();
62
63 let mut out = 0;
64
65 for i in 0 .. 5 {
66 out |= (self.segments[i] as u128) << (26 * i);
67 }
68
69 out.to_le_bytes()
70 }
71}
72
73fn clamp_radix(radix: &mut [u8; 16]) {
74 radix[3] &= 0x0f;
75 radix[7] &= 0x0f;
76 radix[11] &= 0x0f;
77 radix[15] &= 0x0f;
78 radix[4] &= 0xfc;
79 radix[8] &= 0xfc;
80 radix[12] &= 0xfc;
81}
82
83pub fn poly1305(mut message: &[u8], mut radix: [u8; 16], nonce: [u8; 16]) -> [u8; 16] {
86 clamp_radix(&mut radix);
87
88 let radix = Num::from_16_le_bytes(radix);
89
90 let mut accum = Num::zero();
91
92 while message.len() >= 16 {
93 let val = Num::from_complete_chunk(message[0 .. 16].try_into().unwrap());
94 accum += val;
95 accum *= radix;
96 message = &message[16 ..];
97 }
98
99 if message.len() >= 1 {
100 let val = Num::from_incomplete_chunk(message);
101 accum += val;
102 accum *= radix;
103 }
104
105 accum = accum + Num::from_16_le_bytes(nonce);
106
107 accum.to_16_le_bytes()
108}
109
110pub fn poly1305_verify(message: &[u8], radix: [u8; 16], nonce: [u8; 16], tag: [u8; 16]) -> bool {
115 let correct_tag = poly1305(message, radix, nonce);
116 constant_time_compare(tag, correct_tag)
117}
118
119fn constant_time_compare(tag_a: [u8; 16], tag_b: [u8; 16]) -> bool {
120 let mut equal = true;
121
122 for i in 0 .. 16 {
123 equal &= tag_a[i] == tag_b[i];
124 }
125
126 equal
127}
128
129#[test]
130#[cfg(feature = "std")]
131fn rfc8439_main_test_vector() {
132 let message = b"Cryptographic Forum Research Group";
133 let radix = [
134 0x85, 0xd6, 0xbe, 0x78, 0x57, 0x55, 0x6d, 0x33,
135 0x7f, 0x44, 0x52, 0xfe, 0x42, 0xd5, 0x06, 0xa8,
136 ];
137 let nonce = [
138 0x01, 0x03, 0x80, 0x8a, 0xfb, 0x0d, 0xb2, 0xfd,
139 0x4a, 0xbf, 0xf6, 0xaf, 0x41, 0x49, 0xf5, 0x1b,
140 ];
141
142 let tag = poly1305(message, radix, nonce);
143
144 print!("tag: ");
145 for i in 0 .. 16 {
146 print!("{:>02x}", tag[i]);
147 }
148 println!("");
149
150 assert!(tag == [
151 0xa8, 0x06, 0x1d, 0xc1, 0x30, 0x51, 0x36, 0xc6,
152 0xc2, 0x2b, 0x8b, 0xaf, 0x0c, 0x01, 0x27, 0xa9,
153 ]);
154}