dcrypt_algorithms/mac/poly1305/
mod.rs

1//! Poly1305 message authentication code
2//! Pure-Rust limb arithmetic implementation, constant-time throughout.
3//!
4//! Implements the algorithm described in RFC 8439.
5
6#[cfg(not(feature = "std"))]
7use alloc::vec::Vec;
8
9use crate::error::{validate, Result};
10use crate::mac::{Mac, MacAlgorithm};
11use crate::types::Tag;
12use dcrypt_common::security::SecretBuffer;
13use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
14
15/// Size of the Poly1305 key in bytes (32 B)
16pub const POLY1305_KEY_SIZE: usize = 32;
17/// Size of the Poly1305 authentication tag in bytes (16 B)
18pub const POLY1305_TAG_SIZE: usize = 16;
19
20/// Marker for the Poly1305 algorithm (type-level)
21pub enum Poly1305Algorithm {}
22
23impl MacAlgorithm for Poly1305Algorithm {
24    const KEY_SIZE: usize = POLY1305_KEY_SIZE;
25    const TAG_SIZE: usize = POLY1305_TAG_SIZE;
26    const BLOCK_SIZE: usize = 16;
27
28    fn name() -> &'static str {
29        "Poly1305"
30    }
31}
32
33/// Poly1305 MAC (branch-free limb arithmetic)
34#[derive(Zeroize, ZeroizeOnDrop)]
35pub struct Poly1305 {
36    r: SecretBuffer<24>,      // 130-bit key r stored as 3 u64s (24 bytes)
37    s: SecretBuffer<16>,      // 128-bit key s stored as 2 u64s (16 bytes)
38    data: Zeroizing<Vec<u8>>, // buffered input
39}
40
41impl Poly1305 {
42    /* ------------------------------------------------------------------ */
43    /*                           INITIALISATION                           */
44    /* ------------------------------------------------------------------ */
45
46    /// Construct a new `Poly1305` context from a 32-byte key.
47    ///
48    /// The key is split into the clamped `r` portion (first 16 bytes) and the
49    /// `s` portion (last 16 bytes) exactly as specified in RFC 8439 §2.5.2.
50    pub fn new(key: &[u8]) -> Result<Self> {
51        validate::length("Poly1305 key", key.len(), POLY1305_KEY_SIZE)?;
52
53        // ---- split & clamp r -------------------------------------------
54        let mut r_bytes = [0u8; 16];
55        r_bytes.copy_from_slice(&key[..16]);
56        r_bytes[3] &= 15;
57        r_bytes[7] &= 15;
58        r_bytes[11] &= 15;
59        r_bytes[15] &= 15;
60        r_bytes[4] &= 252;
61        r_bytes[8] &= 252;
62        r_bytes[12] &= 252;
63
64        // Convert to 64-bit values with proper padding for storage
65        let r0 = u64::from_le_bytes(r_bytes[0..8].try_into().unwrap());
66        let r1 = u64::from_le_bytes(r_bytes[8..16].try_into().unwrap());
67        let r2 = 0u64;
68
69        // Create a 24-byte buffer for r (3 * 8 bytes)
70        let mut r_buf = [0u8; 24];
71        r_buf[0..8].copy_from_slice(&r0.to_le_bytes());
72        r_buf[8..16].copy_from_slice(&r1.to_le_bytes());
73        r_buf[16..24].copy_from_slice(&r2.to_le_bytes());
74
75        // ---- split s ---------------------------------------------------
76        let s0 = u64::from_le_bytes(key[16..24].try_into().unwrap());
77        let s1 = u64::from_le_bytes(key[24..32].try_into().unwrap());
78
79        // Create a 16-byte buffer for s (2 * 8 bytes)
80        let mut s_buf = [0u8; 16];
81        s_buf[0..8].copy_from_slice(&s0.to_le_bytes());
82        s_buf[8..16].copy_from_slice(&s1.to_le_bytes());
83
84        Ok(Self {
85            r: SecretBuffer::new(r_buf),
86            s: SecretBuffer::new(s_buf),
87            data: Zeroizing::new(Vec::new()),
88        })
89    }
90
91    /* ------------------------------------------------------------------ */
92    /*                           HELPER METHODS                           */
93    /* ------------------------------------------------------------------ */
94
95    /// Extract r values from the secure buffer
96    fn get_r(&self) -> [u64; 3] {
97        let bytes = self.r.as_ref();
98        [
99            u64::from_le_bytes(bytes[0..8].try_into().unwrap()),
100            u64::from_le_bytes(bytes[8..16].try_into().unwrap()),
101            u64::from_le_bytes(bytes[16..24].try_into().unwrap()),
102        ]
103    }
104
105    /// Extract s values from the secure buffer
106    fn get_s(&self) -> [u64; 2] {
107        let bytes = self.s.as_ref();
108        [
109            u64::from_le_bytes(bytes[0..8].try_into().unwrap()),
110            u64::from_le_bytes(bytes[8..16].try_into().unwrap()),
111        ]
112    }
113
114    /* ------------------------------------------------------------------ */
115    /*                                UPDATE                               */
116    /* ------------------------------------------------------------------ */
117
118    /// Absorb additional message data into the MAC state.
119    ///
120    /// This can be called zero or more times before [`Self::finalize`].  
121    /// Data is internally buffered in 16-byte blocks.  
122    /// Always returns `Ok(())` (provided for API symmetry).
123    pub fn update(&mut self, chunk: &[u8]) -> Result<()> {
124        if !chunk.is_empty() {
125            self.data.extend_from_slice(chunk);
126        }
127        Ok(())
128    }
129
130    /* ------------------------------------------------------------------ */
131    /*                               FINALISE                              */
132    /* ------------------------------------------------------------------ */
133
134    /// Consume the context and return the 16-byte authentication tag.
135    ///
136    /// After this call the `Poly1305` instance must be discarded because its
137    /// internal key material has been moved.
138    pub fn finalize(self) -> Tag<POLY1305_TAG_SIZE> {
139        // 1) polynomial evaluation h = Σ (block · r^i)
140        let mut h = [0u64; 3];
141        let r = self.get_r(); // Get r values from SecretBuffer
142
143        for block in self.data.chunks(16) {
144            let mut buf = [0u8; 16];
145            buf[..block.len()].copy_from_slice(block);
146            let n2 = if block.len() == 16 {
147                1
148            } else {
149                buf[block.len()] = 1;
150                0
151            };
152            let n0 = u64::from_le_bytes(buf[0..8].try_into().unwrap());
153            let n1 = u64::from_le_bytes(buf[8..16].try_into().unwrap());
154
155            // h += n (carry-prop)
156            let (h0, c0) = h[0].overflowing_add(n0);
157            let (h1a, c1a) = h[1].overflowing_add(n1);
158            let (h1, c1b) = h1a.overflowing_add(c0 as u64);
159            let c1 = (c1a || c1b) as u64;
160            let (h2a, _) = h[2].overflowing_add(n2);
161            let (h2, _) = h2a.overflowing_add(c1);
162
163            h = mul_reduce([h0, h1, h2], r);
164        }
165
166        // 2) final reduction mod p = 2^130 − 5 (branch-free)
167        const P0: u64 = 0xffff_ffff_ffff_fffb;
168        const P1: u64 = 0xffff_ffff_ffff_ffff;
169        const P2: u64 = 3;
170
171        let (g0, b0) = h[0].overflowing_sub(P0);
172        let (g1a, b1a) = h[1].overflowing_sub(P1);
173        let (g1, b1b) = g1a.overflowing_sub(b0 as u64);
174        let borrow1 = (b1a || b1b) as u64;
175        let (g2, borrow2_bool) = h[2].overflowing_sub(P2 + borrow1);
176
177        // mask = 0xFFFF… when borrow2 == 0, else 0x0
178        let mask = (borrow2_bool as u64).wrapping_sub(1);
179        h[0] = (h[0] & !mask) | (g0 & mask);
180        h[1] = (h[1] & !mask) | (g1 & mask);
181        h[2] = (h[2] & !mask) | (g2 & mask);
182
183        // 3) add s (mod 2^128)
184        let s = self.get_s(); // Get s values from SecretBuffer
185        let (t0, carry0) = h[0].overflowing_add(s[0]);
186        let (t1a, _) = h[1].overflowing_add(s[1]);
187        let (t1, _) = t1a.overflowing_add(carry0 as u64);
188
189        let mut out = [0u8; POLY1305_TAG_SIZE];
190        out[..8].copy_from_slice(&t0.to_le_bytes());
191        out[8..16].copy_from_slice(&t1.to_le_bytes());
192        Tag::new(out)
193    }
194}
195
196/* ---------------------------------------------------------------------- */
197/*                       TRAIT IMPLEMENTATIONS                            */
198/* ---------------------------------------------------------------------- */
199impl Mac for Poly1305 {
200    type Key = [u8; POLY1305_KEY_SIZE];
201    type Tag = Tag<POLY1305_TAG_SIZE>;
202
203    fn new(key: &[u8]) -> Result<Self> {
204        Self::new(key)
205    }
206    fn update(&mut self, data: &[u8]) -> Result<&mut Self> {
207        self.update(data)?;
208        Ok(self)
209    }
210    fn finalize(&mut self) -> Result<Self::Tag> {
211        Ok(self.clone().finalize())
212    }
213    fn reset(&mut self) -> Result<()> {
214        self.data.clear();
215        Ok(())
216    }
217}
218
219impl Clone for Poly1305 {
220    fn clone(&self) -> Self {
221        Self {
222            r: self.r.clone(),
223            s: self.s.clone(),
224            data: self.data.clone(),
225        }
226    }
227}
228
229/* ---------------------------------------------------------------------- */
230/*                SCHOOLBOOK MUL & REDUCE (2^130 − 5)                     */
231/* ---------------------------------------------------------------------- */
232fn mul_reduce(h: [u64; 3], r: [u64; 3]) -> [u64; 3] {
233    let (h0, h1, h2) = (h[0] as u128, h[1] as u128, h[2] as u128);
234    let (r0, r1, r2) = (r[0] as u128, r[1] as u128, r[2] as u128);
235
236    // schoolbook multiply
237    let mut t0 = h0 * r0;
238    let mut t1 = h0 * r1 + h1 * r0;
239    let mut t2 = h0 * r2 + h1 * r1 + h2 * r0;
240    let mut t3 = h1 * r2 + h2 * r1;
241    let mut t4 = h2 * r2;
242
243    // propagate carries
244    let c1 = (t0 >> 64) as u64;
245    t0 &= u128::from(u64::MAX);
246    t1 += c1 as u128;
247    let c2 = (t1 >> 64) as u64;
248    t1 &= u128::from(u64::MAX);
249    t2 += c2 as u128;
250    let c3 = (t2 >> 64) as u64;
251    t2 &= u128::from(u64::MAX);
252    t3 += c3 as u128;
253    let c4 = (t3 >> 64) as u64;
254    t3 &= u128::from(u64::MAX);
255    t4 += c4 as u128;
256    let _c5 = (t4 >> 64) as u64;
257    t4 &= u128::from(u64::MAX);
258
259    // fold bits ≥2^130 back in via 2^130 ≡ 5 (mod p)
260    let high = (t2 >> 2) + (t3 << 62) + (t4 << 126);
261    let low2 = t2 & 0x3;
262
263    // combine low limbs with folded carry
264    let mut m0 = t0 + high * 5;
265    let mut m1 = t1;
266    let mut m2 = low2;
267
268    // final carry
269    let f1 = (m0 >> 64) as u64;
270    m0 &= u128::from(u64::MAX);
271    m1 += f1 as u128;
272    let f2 = (m1 >> 64) as u64;
273    m1 &= u128::from(u64::MAX);
274    m2 += f2 as u128;
275
276    m2 &= 0x3fff_ffff_ffff_ffff;
277    [m0 as u64, m1 as u64, m2 as u64]
278}
279
280#[cfg(test)]
281mod tests;