dcrypt_algorithms/mac/poly1305/
mod.rs1use crate::error::{validate, Result};
9use crate::mac::MacAlgorithm;
10use crate::types::Tag;
11use dcrypt_common::security::{SecretBuffer, SecretVec};
12use dcrypt_internal::zeroing::{Zeroize, ZeroizeOnDrop, Zeroizing};
13
14pub const POLY1305_KEY_SIZE: usize = 32;
16pub const POLY1305_TAG_SIZE: usize = 16;
18
19pub enum Poly1305Algorithm {}
21
22impl MacAlgorithm for Poly1305Algorithm {
23 const KEY_SIZE: usize = POLY1305_KEY_SIZE;
24 const TAG_SIZE: usize = POLY1305_TAG_SIZE;
25 const BLOCK_SIZE: usize = 16;
26
27 fn name() -> &'static str {
28 "Poly1305"
29 }
30}
31
32pub struct Poly1305 {
34 r: SecretBuffer<24>, s: SecretBuffer<16>, data: SecretVec, }
38
39impl Zeroize for Poly1305 {
40 fn zeroize(&mut self) {
41 self.r.zeroize();
42 self.s.zeroize();
43 self.data.zeroize();
44 }
45}
46
47impl Drop for Poly1305 {
48 fn drop(&mut self) {
49 self.zeroize();
50 }
51}
52
53impl ZeroizeOnDrop for Poly1305 {}
54
55impl Poly1305 {
56 pub fn new(key: &[u8]) -> Result<Self> {
65 validate::length("Poly1305 key", key.len(), POLY1305_KEY_SIZE)?;
66
67 let mut r_bytes = Zeroizing::new([0u8; 16]);
69 r_bytes.copy_from_slice(&key[..16]);
70 r_bytes[3] &= 15;
71 r_bytes[7] &= 15;
72 r_bytes[11] &= 15;
73 r_bytes[15] &= 15;
74 r_bytes[4] &= 252;
75 r_bytes[8] &= 252;
76 r_bytes[12] &= 252;
77
78 let mut r = SecretBuffer::<24>::zeroed();
80 r.as_mut()[..16].copy_from_slice(r_bytes.as_ref());
81
82 let mut s = SecretBuffer::<16>::zeroed();
84 s.as_mut().copy_from_slice(&key[16..32]);
85
86 Ok(Self {
87 r,
88 s,
89 data: SecretVec::empty(),
90 })
91 }
92
93 fn get_r(&self) -> Zeroizing<[u64; 3]> {
99 let bytes = self.r.as_ref();
100 let mut words = Zeroizing::new([0u64; 3]);
101 for (word_index, word) in words.iter_mut().enumerate() {
102 for byte_index in 0..8 {
103 *word |= u64::from(bytes[word_index * 8 + byte_index]) << (byte_index * 8);
104 }
105 }
106 words
107 }
108
109 fn get_s(&self) -> Zeroizing<[u64; 2]> {
111 let bytes = self.s.as_ref();
112 let mut words = Zeroizing::new([0u64; 2]);
113 for (word_index, word) in words.iter_mut().enumerate() {
114 for byte_index in 0..8 {
115 *word |= u64::from(bytes[word_index * 8 + byte_index]) << (byte_index * 8);
116 }
117 }
118 words
119 }
120
121 pub fn update(&mut self, chunk: &[u8]) -> Result<()> {
131 if !chunk.is_empty() {
132 self.data.extend_from_slice(chunk);
133 }
134 Ok(())
135 }
136
137 pub fn finalize(self) -> Tag<POLY1305_TAG_SIZE> {
146 let mut h = Zeroizing::new([0u64; 3]);
148 let r = self.get_r();
149
150 for block in self.data.chunks(16) {
151 let mut buf = Zeroizing::new([0u8; 16]);
152 buf[..block.len()].copy_from_slice(block);
153 let n2 = if block.len() == 16 {
154 1
155 } else {
156 buf[block.len()] = 1;
157 0
158 };
159 let mut n = Zeroizing::new([0u64; 2]);
160 for (word_index, word) in n.iter_mut().enumerate() {
161 for byte_index in 0..8 {
162 *word |= u64::from(buf[word_index * 8 + byte_index]) << (byte_index * 8);
163 }
164 }
165
166 let mut sum = Zeroizing::new(u128::from(h[0]) + u128::from(n[0]));
168 h[0] = *sum as u64;
169 *sum = u128::from(h[1]) + u128::from(n[1]) + (*sum >> 64);
170 h[1] = *sum as u64;
171 h[2] = h[2].wrapping_add(n2).wrapping_add((*sum >> 64) as u64);
172
173 let reduced = mul_reduce(&h, &r);
174 h.copy_from_slice(&*reduced);
175 }
176
177 const P0: u64 = 0xffff_ffff_ffff_fffb;
179 const P1: u64 = 0xffff_ffff_ffff_ffff;
180 const P2: u64 = 3;
181
182 let mut g = Zeroizing::new([0u64; 3]);
183 let mut borrow = Zeroizing::new(0u64);
184 let mut wide = Zeroizing::new((1u128 << 64) + u128::from(h[0]) - u128::from(P0));
185 g[0] = *wide as u64;
186 *borrow = 1 - (*wide >> 64) as u64;
187 *wide = (1u128 << 64) + u128::from(h[1]) - u128::from(P1) - u128::from(*borrow);
188 g[1] = *wide as u64;
189 *borrow = 1 - (*wide >> 64) as u64;
190 *wide = (1u128 << 64) + u128::from(h[2]) - u128::from(P2) - u128::from(*borrow);
191 g[2] = *wide as u64;
192 *borrow = 1 - (*wide >> 64) as u64;
193
194 let mask = Zeroizing::new(borrow.wrapping_sub(1));
196 h[0] = (h[0] & !*mask) | (g[0] & *mask);
197 h[1] = (h[1] & !*mask) | (g[1] & *mask);
198 h[2] = (h[2] & !*mask) | (g[2] & *mask);
199
200 let s = self.get_s();
202 *wide = u128::from(h[0]) + u128::from(s[0]);
203 let mut tag_words = Zeroizing::new([0u64; 2]);
204 tag_words[0] = *wide as u64;
205 *wide = u128::from(h[1]) + u128::from(s[1]) + (*wide >> 64);
206 tag_words[1] = *wide as u64;
207
208 let mut out = [0u8; POLY1305_TAG_SIZE];
209 for (word_index, word) in tag_words.iter().enumerate() {
210 for byte_index in 0..8 {
211 out[word_index * 8 + byte_index] = (word >> (byte_index * 8)) as u8;
212 }
213 }
214 Tag::new(out)
215 }
216}
217
218fn mul_reduce(h: &[u64; 3], r: &[u64; 3]) -> Zeroizing<[u64; 3]> {
222 let operands = Zeroizing::new([
223 u128::from(h[0]),
224 u128::from(h[1]),
225 u128::from(h[2]),
226 u128::from(r[0]),
227 u128::from(r[1]),
228 u128::from(r[2]),
229 ]);
230
231 let mut products = Zeroizing::new([0u128; 5]);
233 products[0] = operands[0] * operands[3];
234 products[1] = operands[0] * operands[4] + operands[1] * operands[3];
235 products[2] = operands[0] * operands[5] + operands[1] * operands[4] + operands[2] * operands[3];
236 products[3] = operands[1] * operands[5] + operands[2] * operands[4];
237 products[4] = operands[2] * operands[5];
238
239 for limb in 0..4 {
241 products[limb + 1] += products[limb] >> 64;
242 products[limb] &= u128::from(u64::MAX);
243 }
244 products[4] &= u128::from(u64::MAX);
245
246 let high = Zeroizing::new((products[2] >> 2) + (products[3] << 62) + (products[4] << 126));
248
249 let mut reduced = Zeroizing::new([0u128; 3]);
251 reduced[0] = products[0] + *high * 5;
252 reduced[1] = products[1];
253 reduced[2] = products[2] & 0x3;
254
255 reduced[1] += reduced[0] >> 64;
257 reduced[0] &= u128::from(u64::MAX);
258 reduced[2] += reduced[1] >> 64;
259 reduced[1] &= u128::from(u64::MAX);
260 reduced[2] &= 0x3fff_ffff_ffff_ffff;
261
262 let mut result = Zeroizing::new([0u64; 3]);
263 for (output, value) in result.iter_mut().zip(reduced.iter()) {
264 *output = *value as u64;
265 }
266 result
267}
268
269#[cfg(test)]
270mod tests;