secured_cipher/algorithm/poly1305/mod.rs
1mod core;
2pub use core::{
3 apply_poly1305_mod_p, apply_poly1305_pad, calculate_poly1305_d_values,
4 calculate_poly1305_h_values, finalize_poly1305_hash, poly1305_hash_to_tag,
5};
6
7use crate::AEADAlgorithm;
8
9use super::{AlgorithmKeyInit, AlgorithmProcess};
10
11/// Define the Poly1305 struct for the Poly1305 MAC algorithm.
12#[derive(Default)]
13pub struct Poly1305 {
14 // r: 5-element array storing part of the key.
15 r: [u32; 5],
16 // h: 5-element array, the internal state for the hash calculation.
17 h: [u32; 5],
18 // pad: 4-element array storing the other part of the key.
19 pad: [u32; 4],
20}
21
22impl Poly1305 {
23 /// Creates a new instance of Poly1305.
24 ///
25 /// This constructor initializes a new Poly1305 instance with default values.
26 /// It is typically used to start a new message authentication code (MAC) computation.
27 ///
28 /// # Returns
29 /// A new instance of `Poly1305`.
30 pub fn new() -> Self {
31 Self::default()
32 }
33
34 /// Computes a block of data for the MAC.
35 ///
36 /// This method processes a 16-byte block of data and updates the internal state
37 /// (`h`) based on the block data and the Poly1305 algorithm. It handles both full
38 /// and partial blocks, where a partial block is less than 16 bytes.
39 ///
40 /// # Arguments
41 /// * `block` - A 16-byte array representing the data block to be processed.
42 /// * `partial` - A boolean indicating whether the block is a partial block.
43 ///
44 /// # Notes
45 /// Partial blocks are handled differently in the algorithm, as indicated by the `partial` flag.
46 fn compute_block(&mut self, block: [u8; 16], partial: bool) {
47 let hibit = if partial { 0 } else { 1 << 24 };
48
49 let (r0, r1, r2, r3, r4) = (self.r[0], self.r[1], self.r[2], self.r[3], self.r[4]);
50 let (s1, s2, s3, s4) = (r1 * 5, r2 * 5, r3 * 5, r4 * 5);
51
52 // h += m
53 let (h0, h1, h2, h3, h4) = calculate_poly1305_h_values(&block, hibit);
54
55 // h *= r
56 let (mut d0, mut d1, mut d2, mut d3, mut d4) =
57 calculate_poly1305_d_values(h0, h1, h2, h3, h4, r0, r1, r2, r3, r4, s1, s2, s3, s4);
58
59 // (partial) h %= p
60 apply_poly1305_mod_p(&mut self.h, &mut d0, &mut d1, &mut d2, &mut d3, &mut d4)
61 }
62
63 /// Finalizes the MAC computation and returns the resulting tag.
64 ///
65 /// This method completes the Poly1305 MAC computation by finalizing the hash calculation,
66 /// applying the pad, and then converting the final hash state into a 16-byte tag.
67 ///
68 /// It should be called after all blocks of data have been processed using `compute_block`.
69 ///
70 /// # Returns
71 /// A 16-byte array representing the final MAC tag.
72 fn finalize(&mut self) -> [u8; 16] {
73 // Finalize the hash calculation
74 finalize_poly1305_hash(&mut self.h);
75
76 // Apply the padding to the hash
77 apply_poly1305_pad(&mut self.h, self.pad);
78
79 // Convert the hash to a tag
80 poly1305_hash_to_tag(&self.h)
81 }
82}
83
84impl AlgorithmKeyInit for Poly1305 {
85 /// Initializes the Poly1305 state with the given key.
86 ///
87 /// This method sets up the Poly1305 state using a 32-byte key. The key is split
88 /// into two parts: the `r` array (for the algorithm's internal state) and the `pad`
89 /// (used in the final computation steps).
90 ///
91 /// # Arguments
92 /// * `key` - A byte slice containing the 32-byte key.
93 /// * `_iv` - An optional Initialization Vector, not used in Poly1305.
94 ///
95 /// # Returns
96 /// A mutable reference to the initialized Poly1305 instance.
97 ///
98 /// # Notes
99 /// The Initialization Vector (`_iv`) is not used in Poly1305 and can be passed as an empty slice.
100 fn init(&mut self, key: &[u8]) {
101 self.r[0] = u32::from_le_bytes([key[0], key[1], key[2], key[3]]) & 0x3ff_ffff;
102 self.r[1] = u32::from_le_bytes([key[3], key[4], key[5], key[6]]) & 0x3ff_ff03;
103 self.r[2] = u32::from_le_bytes([key[6], key[7], key[8], key[9]]) & 0x3ff_c0ff;
104 self.r[3] = u32::from_le_bytes([key[9], key[10], key[11], key[12]]) & 0x3f0_3fff;
105 self.r[4] = u32::from_le_bytes([key[12], key[13], key[14], key[15]]) & 0x00f_ffff;
106 self.pad[0] = u32::from_le_bytes([key[16], key[17], key[18], key[19]]);
107 self.pad[1] = u32::from_le_bytes([key[20], key[21], key[22], key[23]]);
108 self.pad[2] = u32::from_le_bytes([key[24], key[25], key[26], key[27]]);
109 self.pad[3] = u32::from_le_bytes([key[28], key[29], key[30], key[31]]);
110 }
111}
112
113impl AlgorithmProcess for Poly1305 {
114 /// Processes the given data and computes the MAC.
115 ///
116 /// This method processes the input data in 16-byte blocks to compute the
117 /// message authentication code (MAC). If the data does not divide evenly into
118 /// 16-byte blocks, the final block is padded as necessary.
119 ///
120 /// # Arguments
121 /// * `data` - A byte slice representing the data to be processed.
122 ///
123 /// # Returns
124 /// A vector of bytes (`Vec<u8>`) containing the computed MAC.
125 fn process(&mut self, data: &[u8]) -> Vec<u8> {
126 let blocks = data.chunks_exact(16);
127 let partial = blocks.remainder();
128
129 for block in blocks {
130 self.compute_block(block.try_into().unwrap(), false);
131 }
132
133 if !partial.is_empty() {
134 let mut block = [0u8; 16];
135 block[..partial.len()].copy_from_slice(partial);
136 // this must end with 1 or during conversions trailing zeros will be lost
137 block[partial.len()] = 1;
138 self.compute_block(block, true);
139 }
140
141 self.finalize().to_vec()
142 }
143}
144
145impl AEADAlgorithm for Poly1305 {}
146
147/// SignedEnvelope struct for handling data with its associated MAC.
148pub struct SignedEnvelope {
149 pub header: Vec<u8>,
150 pub data: Vec<u8>,
151 pub mac: Vec<u8>,
152}
153
154impl SignedEnvelope {
155 /// Constructs a SignedEnvelope from a vector of bytes.
156 ///
157 /// # Arguments
158 /// - `bytes`: Vec<u8> where the last 16 bytes are considered the MAC.
159 /// - `mac`: Vec<u8> representing the MAC.
160 ///
161 /// # Returns
162 /// Returns a new SignedEnvelope instance.
163 pub fn new(header: Vec<u8>, data: Vec<u8>, mac: Vec<u8>) -> Self {
164 Self { header, data, mac }
165 }
166}
167
168impl From<Vec<u8>> for SignedEnvelope {
169 fn from(bytes: Vec<u8>) -> Self {
170 let mut offset = 0;
171
172 // Deserialize header
173 let header_len = u32::from_be_bytes(bytes[offset..offset + 4].try_into().unwrap()) as usize;
174 offset += 4;
175 let header = bytes[offset..offset + header_len].to_vec();
176
177 // Deserialize data
178 offset += header_len;
179 let data_len = u32::from_be_bytes(bytes[offset..offset + 4].try_into().unwrap()) as usize;
180 offset += 4;
181 let data = bytes[offset..offset + data_len].to_vec();
182
183 // Deserialize MAC
184 offset += data_len;
185 let mac = bytes[offset..offset + 16].to_vec();
186
187 // If the MAC length is not 16, return an error
188 if mac.len() != 16 {
189 panic!("Unexpected bytes length");
190 }
191
192 Self::new(header, data, mac)
193 }
194}
195
196impl From<SignedEnvelope> for Vec<u8> {
197 fn from(envelope: SignedEnvelope) -> Self {
198 let mut bytes = Vec::new();
199
200 // Serialize the header length and data
201 // Network Byte Order is used
202 bytes.extend(&(envelope.header.len() as u32).to_be_bytes());
203 bytes.extend(&envelope.header);
204
205 // Serialize the data length and data
206 // Network Byte Order is used
207 bytes.extend(&(envelope.data.len() as u32).to_be_bytes());
208 bytes.extend(&envelope.data);
209
210 // Serialize the MAC
211 bytes.extend(&envelope.mac);
212
213 bytes
214 }
215}