1 /**
2 * Derived from poly1305-donna-64.h by Andrew Moon <liquidsun@gmail.com>
3 * in https://github.com/floodyberry/poly1305-donna
4 * 
5 * Copyright:
6 * (C) 2014 Jack Lloyd
7 * (C) 2014 Andrew Moon
8 * (C) 2014-2015 Etienne Cimon
9 *
10 * License:
11 * Botan is released under the Simplified BSD License (see LICENSE.md)
12 */
13 module botan.mac.poly1305;
14 
15 import botan.constants;
16 static if (BOTAN_HAS_POLY1305):
17 import botan.mac.mac;
18 import botan.utils.mul128;
19 import botan.utils.donna128;
20 import botan.utils.types;
21 import botan.utils.mem_ops;
22 import botan.utils.loadstor;
23 
24 /**
25 * DJB's Poly1305
26 * Important note: each key can only be used once
27 */
28 class Poly1305 : MessageAuthenticationCode, BufferedComputation, SymmetricAlgorithm
29 {
30 public:
31 	/*
32     * Return the name of this type
33     */
34 	override @property string name() const
35 	{
36 		return "Poly1305";
37 	}
38 	
39 	override @property size_t outputLength() const { return 16; }
40 
41 	/*
42     * Return a clone of this object
43     */
44 	override MessageAuthenticationCode clone() const
45 	{
46 		return new Poly1305;
47 	}
48 	
49 	/*
50     * Clear memory of sensitive data
51     */
52 	override void clear() {
53 		zap(m_poly);
54 		zap(m_buf);
55 		m_buf_pos = 0;
56 	}
57 	
58 	override KeyLengthSpecification keySpec() const
59 	{
60 		return KeyLengthSpecification(32);
61 	}
62 	
63 protected:
64 	override void addData(const(ubyte)* input, size_t length) {
65 		assert(m_poly.length == 8, "Initialized");
66 		
67 		if(m_buf_pos)
68 		{
69 			bufferInsert(m_buf, m_buf_pos, input, length);
70 			
71 			if(m_buf_pos + length >= m_buf.length)
72 			{
73 				poly1305_blocks(m_poly.ptr, m_buf.ptr, 1);
74 				input += (m_buf.length - m_buf_pos);
75 				length -= (m_buf.length - m_buf_pos);
76 				m_buf_pos = 0;
77 			}
78 		}
79 		
80 		const size_t full_blocks = length / m_buf.length;
81 		const size_t remaining   = length % m_buf.length;
82 		
83 		if(full_blocks)
84 			poly1305_blocks(m_poly.ptr, input, full_blocks);
85 		
86 		bufferInsert(m_buf, m_buf_pos, input + full_blocks * m_buf.length, remaining);
87 		m_buf_pos += remaining;
88 	}
89 	
90 	override void finalResult(ubyte* output) {
91 		assert(m_poly.length == 8, "Initialized");
92 		
93 		if(m_buf_pos != 0)
94 		{
95 			m_buf[m_buf_pos] = 1;
96 			const auto len = m_buf.length - m_buf_pos - 1;
97 			if (len > 0) {
98 				clearMem(m_buf.ptr + m_buf_pos + 1, len);
99 			}
100 			poly1305_blocks(m_poly.ptr, m_buf.ptr, 1, true);
101 		}
102 		
103 		poly1305_finish(m_poly.ptr, output);
104 		
105 		m_poly.clear();
106 		m_buf_pos = 0;
107 	}
108 	
109 	
110 	override void keySchedule(const(ubyte)* key, size_t length) {	
111 		assert(length == 32);
112 		m_buf_pos = 0;
113 		m_buf.resize(16);
114 		m_poly.resize(8);
115 		poly1305_init(m_poly.ptr, key);
116 	}
117 	
118 	SecureVector!ulong m_poly;
119 	SecureVector!ubyte m_buf;
120 	size_t m_buf_pos;
121 };
122 
123 private:
124 
125 void poly1305_init(ulong* X, const ubyte* key)
126 {
127 	/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
128 	const ulong t0 = loadLittleEndian!ulong(key, 0);
129 	const ulong t1 = loadLittleEndian!ulong(key, 1);
130 	
131 	X[0] = ( t0                    ) & 0xffc0fffffff;
132 	X[1] = ((t0 >> 44) | (t1 << 20)) & 0xfffffc0ffff;
133 	X[2] = ((t1 >> 24)             ) & 0x00ffffffc0f;
134 	
135 	/* h = 0 */
136 	X[3] = 0;
137 	X[4] = 0;
138 	X[5] = 0;
139 	
140 	/* save pad for later */
141 	X[6] = loadLittleEndian!ulong(key, 2);
142 	X[7] = loadLittleEndian!ulong(key, 3);
143 }
144 
145 void poly1305_blocks(ulong* X, const(ubyte)* m, size_t blocks, bool is_final = false)
146 {
147 	alias uint128_t = donna128;
148 	
149 	const ulong hibit = is_final ? 0 : (1UL << 40); /* 1 << 128 */
150 	
151 	const ulong r0 = X[0];
152 	const ulong r1 = X[1];
153 	const ulong r2 = X[2];
154 	
155 	ulong h0 = X[3+0];
156 	ulong h1 = X[3+1];
157 	ulong h2 = X[3+2];
158 	
159 	const ulong s1 = r1 * (5 << 2);
160 	const ulong s2 = r2 * (5 << 2);
161 	
162 	while(blocks--)
163 	{
164 		/* h += m[i] */
165 		const ulong t0 = loadLittleEndian!ulong(m, 0);
166 		const ulong t1 = loadLittleEndian!ulong(m, 1);
167 		
168 		h0 += (( t0                    ) & 0xfffffffffff);
169 		h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff);
170 		h2 += (((t1 >> 24)             ) & 0x3ffffffffff) | hibit;
171 		
172 		/* h *= r */
173 		uint128_t d0 = uint128_t(h0) * r0 + uint128_t(h1) * s2 + uint128_t(h2) * s1;
174 		uint128_t d1 = uint128_t(h0) * r1 + uint128_t(h1) * r0 + uint128_t(h2) * s2;
175 		uint128_t d2 = uint128_t(h0) * r2 + uint128_t(h1) * r1 + uint128_t(h2) * r0;
176 		
177 		/* (partial) h %= p */
178 		        ulong c = carry_shift(d0, 44); h0 = d0 & 0xfffffffffff;
179 		d1 += c;      c = carry_shift(d1, 44); h1 = d1 & 0xfffffffffff;
180 		d2 += c;      c = carry_shift(d2, 42); h2 = d2 & 0x3ffffffffff;
181 		h0  += c * 5; c = carry_shift(uint128_t(h0), 44); h0 = h0 & 0xfffffffffff;
182 		h1  += c;
183 		
184 		m += 16;
185 	}
186 	
187 	X[3+0] = h0;
188 	X[3+1] = h1;
189 	X[3+2] = h2;
190 }
191 
192 void poly1305_finish(ulong* X, ubyte* mac)
193 {
194 	/* fully carry h */
195 	ulong h0 = X[3+0];
196 	ulong h1 = X[3+1];
197 	ulong h2 = X[3+2];
198 	
199 	ulong c;
200 	c = (h1 >> 44); h1 &= 0xfffffffffff;
201 	h2 += c;     c = (h2 >> 42); h2 &= 0x3ffffffffff;
202 	h0 += c * 5; c = (h0 >> 44); h0 &= 0xfffffffffff;
203 	h1 += c;     c = (h1 >> 44); h1 &= 0xfffffffffff;
204 	h2 += c;     c = (h2 >> 42); h2 &= 0x3ffffffffff;
205 	h0 += c * 5; c = (h0 >> 44); h0 &= 0xfffffffffff;
206 	h1 += c;
207 	
208 	/* compute h + -p */
209 	ulong g0 = h0 + 5; c = (g0 >> 44); g0 &= 0xfffffffffff;
210 	ulong g1 = h1 + c; c = (g1 >> 44); g1 &= 0xfffffffffff;
211 	ulong g2 = h2 + c - (1UL << 42);
212 	
213 	/* select h if h < p, or h + -p if h >= p */
214 	c = (g2 >> ((ulong.sizeof * 8) - 1)) - 1;
215 	g0 &= c;
216 	g1 &= c;
217 	g2 &= c;
218 	c = ~c;
219 	h0 = (h0 & c) | g0;
220 	h1 = (h1 & c) | g1;
221 	h2 = (h2 & c) | g2;
222 	
223 	/* h = (h + pad) */
224 	const ulong t0 = X[6];
225 	const ulong t1 = X[7];
226 	
227 	h0 += (( t0                    ) & 0xfffffffffff)    ; c = (h0 >> 44); h0 &= 0xfffffffffff;
228 	h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff) + c; c = (h1 >> 44); h1 &= 0xfffffffffff;
229 	h2 += (((t1 >> 24)             ) & 0x3ffffffffff) + c;                 h2 &= 0x3ffffffffff;
230 	
231 	/* mac = h % (2^128) */
232 	h0 = ((h0      ) | (h1 << 44));
233 	h1 = ((h1 >> 20) | (h2 << 24));
234 	
235 	storeLittleEndian(mac, h0, h1);
236 	
237 	/* zero out the state */
238 	clearMem(X, 8);
239 }