secured_cipher/algorithm/poly1305/
core.rs

1//! Poly1305 Authentication Algorithm Implementation
2//!
3//! This module provides an implementation of the Poly1305 algorithm,
4//! as specified in RFC 7539 by the Internet Engineering Task Force (IETF).
5//!
6//! The constants and logic, as well as the test vectors used in this module,
7//! are based on and verifiable against the specifications detailed in the IETF paper:
8//! "ChaCha20 and Poly1305 for IETF Protocols" (RFC 7539).
9//! This can be accessed at https://datatracker.ietf.org/doc/html/rfc7539.
10//!
11//! The module is designed to be compliant with the RFC 7539 standard, ensuring reliability
12//! and correctness of the cryptographic operations as per the established IETF guidelines.
13
14/// Calculates the `h` values for Poly1305.
15///
16/// This function takes a block of data and the high bit (hibit) and calculates
17/// the `h` values as per the Poly1305 algorithm.
18///
19/// # Arguments
20/// * `block` - A reference to a 16-byte array representing the data block.
21/// * `hibit` - The high bit, used in the calculation of `h4`.
22///
23/// # Returns
24/// A tuple of five `u32` values representing the calculated `h` values.
25pub fn calculate_poly1305_h_values(block: &[u8; 16], hibit: u32) -> (u32, u32, u32, u32, u32) {
26  let h0 = u32::from_le_bytes(block[0..4].try_into().unwrap());
27  let h1 = u32::from_le_bytes(block[3..7].try_into().unwrap()) >> 2;
28  let h2 = u32::from_le_bytes(block[6..10].try_into().unwrap()) >> 4;
29  let h3 = u32::from_le_bytes(block[9..13].try_into().unwrap()) >> 6;
30  let h4 = u32::from_le_bytes(block[12..16].try_into().unwrap()) >> 8 | hibit;
31
32  (h0, h1, h2, h3, h4)
33}
34
35/// Calculates the `d` values for Poly1305.
36///
37/// This function computes the `d` values based on the `h` values and key-related
38/// values (`r` and `s` arrays). These calculations are part of the Poly1305 algorithm
39/// for message authentication.
40///
41/// # Arguments
42/// * `h0`, `h1`, `h2`, `h3`, `h4` - The `h` values from the Poly1305 state.
43/// * `r0`, `r1`, `r2`, `r3`, `r4` - The `r` values from the Poly1305 key.
44/// * `s1`, `s2`, `s3`, `s4` - The `s` values, which are derived from the `r` values.
45///
46/// # Returns
47/// A tuple of five `u64` values representing the calculated `d` values.
48pub fn calculate_poly1305_d_values(
49  h0: u32,
50  h1: u32,
51  h2: u32,
52  h3: u32,
53  h4: u32,
54  r0: u32,
55  r1: u32,
56  r2: u32,
57  r3: u32,
58  r4: u32,
59  s1: u32,
60  s2: u32,
61  s3: u32,
62  s4: u32,
63) -> (u64, u64, u64, u64, u64) {
64  let d0 = h0 as u64 * r0 as u64
65    + h1 as u64 * s4 as u64
66    + h2 as u64 * s3 as u64
67    + h3 as u64 * s2 as u64
68    + h4 as u64 * s1 as u64;
69
70  let d1 = h0 as u64 * r1 as u64
71    + h1 as u64 * r0 as u64
72    + h2 as u64 * s4 as u64
73    + h3 as u64 * s3 as u64
74    + h4 as u64 * s2 as u64;
75
76  let d2 = h0 as u64 * r2 as u64
77    + h1 as u64 * r1 as u64
78    + h2 as u64 * r0 as u64
79    + h3 as u64 * s4 as u64
80    + h4 as u64 * s3 as u64;
81
82  let d3 = h0 as u64 * r3 as u64
83    + h1 as u64 * r2 as u64
84    + h2 as u64 * r1 as u64
85    + h3 as u64 * r0 as u64
86    + h4 as u64 * s4 as u64;
87
88  let d4 = h0 as u64 * r4 as u64
89    + h1 as u64 * r3 as u64
90    + h2 as u64 * r2 as u64
91    + h3 as u64 * r1 as u64
92    + h4 as u64 * r0 as u64;
93
94  (d0, d1, d2, d3, d4)
95}
96
97/// Applies the modulo p reduction to the Poly1305 hash.
98///
99/// This function performs the modulo p reduction on the hash state, which is
100/// part of the Poly1305 algorithm. It modifies the hash state in-place.
101///
102/// # Arguments
103/// * `hash` - A mutable reference to the Poly1305 hash state.
104/// * `d0`, `d1`, `d2`, `d3`, `d4` - Mutable references to the `d` values.
105pub fn apply_poly1305_mod_p(
106  hash: &mut [u32; 5],
107  d0: &mut u64,
108  d1: &mut u64,
109  d2: &mut u64,
110  d3: &mut u64,
111  d4: &mut u64,
112) {
113  let mut c = (*d0 >> 26) as u32;
114  hash[0] = (*d0 as u32) & 0x3ff_ffff;
115  *d1 += c as u64;
116
117  c = (*d1 >> 26) as u32;
118  hash[1] = (*d1 as u32) & 0x3ff_ffff;
119  *d2 += c as u64;
120
121  c = (*d2 >> 26) as u32;
122  hash[2] = (*d2 as u32) & 0x3ff_ffff;
123  *d3 += c as u64;
124
125  c = (*d3 >> 26) as u32;
126  hash[3] = (*d3 as u32) & 0x3ff_ffff;
127  *d4 += c as u64;
128
129  c = (*d4 >> 26) as u32;
130  hash[4] = (*d4 as u32) & 0x3ff_ffff;
131  hash[0] += c * 5;
132
133  c = hash[0] >> 26;
134  hash[0] &= 0x3ff_ffff;
135  hash[1] += c;
136}
137
138/// Finalizes the Poly1305 hash computation.
139///
140/// This function finalizes the Poly1305 hash computation by performing the
141/// necessary adjustments and reductions on the internal state.
142///
143/// # Arguments
144/// * `hash` - A mutable reference to the Poly1305 hash state.
145pub fn finalize_poly1305_hash(hash: &mut [u32; 5]) {
146  let mut c = hash[1] >> 26;
147  hash[1] &= 0x3ff_ffff;
148  hash[2] += c;
149
150  c = hash[2] >> 26;
151  hash[2] &= 0x3ff_ffff;
152  hash[3] += c;
153
154  c = hash[3] >> 26;
155  hash[3] &= 0x3ff_ffff;
156  hash[4] += c;
157
158  c = hash[4] >> 26;
159  hash[4] &= 0x3ff_ffff;
160  hash[0] += c * 5;
161
162  c = hash[0] >> 26;
163  hash[0] &= 0x3ff_ffff;
164  hash[1] += c;
165
166  let mut g0 = hash[0].wrapping_add(5);
167  c = g0 >> 26;
168  g0 &= 0x3ff_ffff;
169
170  let mut g1 = hash[1].wrapping_add(c);
171  c = g1 >> 26;
172  g1 &= 0x3ff_ffff;
173
174  let mut g2 = hash[2].wrapping_add(c);
175  c = g2 >> 26;
176  g2 &= 0x3ff_ffff;
177
178  let mut g3 = hash[3].wrapping_add(c);
179  c = g3 >> 26;
180  g3 &= 0x3ff_ffff;
181
182  let mut g4 = hash[4].wrapping_add(c).wrapping_sub(1 << 26);
183
184  let mut mask = (g4 >> (31 - 1)).wrapping_sub(1);
185  g0 &= mask;
186  g1 &= mask;
187  g2 &= mask;
188  g3 &= mask;
189  g4 &= mask;
190  mask = !mask;
191  hash[0] = (hash[0] & mask) | g0;
192  hash[1] = (hash[1] & mask) | g1;
193  hash[2] = (hash[2] & mask) | g2;
194  hash[3] = (hash[3] & mask) | g3;
195  hash[4] = (hash[4] & mask) | g4;
196
197  hash[0] |= hash[1] << 26;
198  hash[1] = (hash[1] >> 6) | (hash[2] << 20);
199  hash[2] = (hash[2] >> 12) | (hash[3] << 14);
200  hash[3] = (hash[3] >> 18) | (hash[4] << 8);
201}
202
203/// Applies the pad to the Poly1305 hash.
204///
205/// This function applies the pad (part of the key) to the Poly1305 hash. It is
206/// called as part of the finalization process of the Poly1305 algorithm.
207///
208/// # Arguments
209/// * `hash` - A mutable reference to the Poly1305 hash state.
210/// * `pad` - The pad values from the Poly1305 key.
211pub fn apply_poly1305_pad(hash: &mut [u32; 5], pad: [u32; 4]) {
212  let mut f: u64 = hash[0] as u64 + pad[0] as u64;
213  hash[0] = f as u32;
214
215  f = hash[1] as u64 + pad[1] as u64 + (f >> 32);
216  hash[1] = f as u32;
217
218  f = hash[2] as u64 + pad[2] as u64 + (f >> 32);
219  hash[2] = f as u32;
220
221  f = hash[3] as u64 + pad[3] as u64 + (f >> 32);
222  hash[3] = f as u32;
223}
224
225/// Converts the Poly1305 hash into a tag.
226///
227/// This function converts the Poly1305 hash state into a 16-byte tag, which is
228/// the final output of the Poly1305 algorithm.
229///
230/// # Arguments
231/// * `hash` - A reference to the Poly1305 hash state.
232///
233/// # Returns
234/// A 16-byte array representing the Poly1305 tag.
235pub fn poly1305_hash_to_tag(hash: &[u32; 5]) -> [u8; 16] {
236  let mut tag = [0u8; 16];
237  tag[0..4].copy_from_slice(&hash[0].to_le_bytes());
238  tag[4..8].copy_from_slice(&hash[1].to_le_bytes());
239  tag[8..12].copy_from_slice(&hash[2].to_le_bytes());
240  tag[12..16].copy_from_slice(&hash[3].to_le_bytes());
241  tag
242}