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}