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