libkrypton/
poly1305.rs

1//! Implemented according to [IETF RFC 8439](https://datatracker.ietf.org/doc/html/rfc8439).
2
3use core::convert::TryInto;
4
5use crate::segmented_int::{SegmentedInt, SegmentedIntDescriptor};
6
7/// 130-bit integer type that subtracts out 2 ** 130 - 5 until results fit within the bit length.
8type Num = SegmentedInt<Poly1305Descriptor>;
9
10struct Poly1305Descriptor;
11
12impl SegmentedIntDescriptor for Poly1305Descriptor {
13	type SegmentType = u64;
14
15	const SEGMENT_SIZE: u16 = 26;
16	const CARRY_FACTOR: u64 = 5;
17	const SEGMENT_MASK: u64 = LOW_26_BITS;
18	const ZERO: u64 = 0;
19	const ONE: u64 = 1;
20}
21
22const LOW_26_BITS: u64 = 0x03ff_ffff;
23
24impl Num {
25	fn zero() -> Self {
26		Self {segments: [0; 5]}
27	}
28
29	fn from_16_le_bytes(bytes: [u8; 16]) -> Self {
30		let as_num = u128::from_le_bytes(bytes);
31		let mut segments = [0; 5];
32
33		for i in 0 .. 5 {
34			segments[i] = (as_num >> (26 * i)) as u64 & LOW_26_BITS;
35		}
36
37		Self {segments}
38	}
39
40	fn from_complete_chunk(chunk: &[u8; 16]) -> Self {
41		let mut out = Self::from_16_le_bytes(*chunk);
42
43		// set the top bit
44		out.segments[4] |= 1 << 24;
45
46		out
47	}
48
49	fn from_incomplete_chunk(chunk: &[u8]) -> Self {
50		debug_assert!(1 <= chunk.len() && chunk.len() < 16);
51
52		let mut bytes = [0; 16];
53		bytes[.. chunk.len()].copy_from_slice(chunk);
54
55		bytes[chunk.len()] = 1;
56
57		Self::from_16_le_bytes(bytes)
58	}
59
60	fn to_16_le_bytes(mut self) -> [u8; 16] {
61		self.full_modular_reduction();
62
63		let mut out = 0;
64
65		for i in 0 .. 5 {
66			out |= (self.segments[i] as u128) << (26 * i);
67		}
68
69		out.to_le_bytes()
70	}
71}
72
73fn clamp_radix(radix: &mut [u8; 16]) {
74	radix[3] &= 0x0f;
75	radix[7] &= 0x0f;
76	radix[11] &= 0x0f;
77	radix[15] &= 0x0f;
78	radix[4] &= 0xfc;
79	radix[8] &= 0xfc;
80	radix[12] &= 0xfc;
81}
82
83/// Generates a Poly1305 tag for a `message`. While `radix` may be reused, `nonce`
84/// *must* only be used once. Both `radix` and `nonce` *must* be kept secret.
85pub fn poly1305(mut message: &[u8], mut radix: [u8; 16], nonce: [u8; 16]) -> [u8; 16] {
86	clamp_radix(&mut radix);
87
88	let radix = Num::from_16_le_bytes(radix);
89
90	let mut accum = Num::zero();
91
92	while message.len() >= 16 {
93		let val = Num::from_complete_chunk(message[0 .. 16].try_into().unwrap());
94		accum += val;
95		accum *= radix;
96		message = &message[16 ..];
97	}
98
99	if message.len() >= 1 {
100		let val = Num::from_incomplete_chunk(message);
101		accum += val;
102		accum *= radix;
103	}
104
105	accum = accum + Num::from_16_le_bytes(nonce);
106
107	accum.to_16_le_bytes()
108}
109
110/// Verifies a Poly1305 `tag` given the original `message`, `radix`, and `nonce`
111/// that was used to generate it. Note that naive comparison of tags may result
112/// in timing attacks. It's strongly recommended to use this function to verify
113/// Poly1305 tags instead of using `==` on tags.
114pub fn poly1305_verify(message: &[u8], radix: [u8; 16], nonce: [u8; 16], tag: [u8; 16]) -> bool {
115	let correct_tag = poly1305(message, radix, nonce);
116	constant_time_compare(tag, correct_tag)
117}
118
119fn constant_time_compare(tag_a: [u8; 16], tag_b: [u8; 16]) -> bool {
120	let mut equal = true;
121
122	for i in 0 .. 16 {
123		equal &= tag_a[i] == tag_b[i];
124	}
125
126	equal
127}
128
129#[test]
130#[cfg(feature = "std")]
131fn rfc8439_main_test_vector() {
132	let message = b"Cryptographic Forum Research Group";
133	let radix = [
134		0x85, 0xd6, 0xbe, 0x78, 0x57, 0x55, 0x6d, 0x33,
135		0x7f, 0x44, 0x52, 0xfe, 0x42, 0xd5, 0x06, 0xa8,
136	];
137	let nonce = [
138		0x01, 0x03, 0x80, 0x8a, 0xfb, 0x0d, 0xb2, 0xfd,
139		0x4a, 0xbf, 0xf6, 0xaf, 0x41, 0x49, 0xf5, 0x1b,
140	];
141
142	let tag = poly1305(message, radix, nonce);
143
144	print!("tag: ");
145	for i in 0 .. 16 {
146		print!("{:>02x}", tag[i]);
147	}
148	println!("");
149
150	assert!(tag == [
151		0xa8, 0x06, 0x1d, 0xc1, 0x30, 0x51, 0x36, 0xc6,
152		0xc2, 0x2b, 0x8b, 0xaf, 0x0c, 0x01, 0x27, 0xa9,
153	]);
154}