Added CCM and CCM_8 cipher suites.
[BoarSSL] / Crypto / HMAC.cs
1 /*
2 * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25 using System;
26
27 namespace Crypto {
28
29 /*
30 * Implementation of HMAC (RFC 2104).
31 */
32
33 public sealed class HMAC {
34
35 IDigest h;
36 byte[] key;
37 int keyLen;
38 byte[] tmp, tmpCT;
39 ulong dataLen;
40
41 /*
42 * Create a new instance, using the provided hash function. The
43 * hash function instance will be used internally.
44 */
45 public HMAC(IDigest h) : this(h, true)
46 {
47 }
48
49 private HMAC(IDigest h, bool doReset)
50 {
51 this.h = h;
52 int n = h.BlockSize;
53 if (n < h.DigestSize) {
54 throw new ArgumentException(
55 "invalid hash function for HMAC");
56 }
57 key = new byte[n];
58 keyLen = 0;
59 tmp = new byte[h.DigestSize];
60 tmpCT = new byte[h.DigestSize];
61 if (doReset) {
62 Reset();
63 }
64 }
65
66 /*
67 * Duplicate this engine. The returned instance inherits the
68 * current state (key, data already processed...) but is
69 * thereafter independent.
70 */
71 public HMAC Dup()
72 {
73 HMAC hm = new HMAC(h.Dup(), false);
74 Array.Copy(key, 0, hm.key, 0, keyLen);
75 hm.keyLen = keyLen;
76 hm.dataLen = dataLen;
77 return hm;
78 }
79
80 /*
81 * Get the HMAC output size (in bytes).
82 */
83 public int MACSize {
84 get {
85 return h.DigestSize;
86 }
87 }
88
89 /*
90 * Set the HMAC key. This implies a call to Reset() (thus,
91 * the key must be set before data begins to be inserted).
92 */
93 public void SetKey(byte[] key)
94 {
95 SetKey(key, 0, key.Length);
96 }
97
98 /*
99 * Set the HMAC key ('len' bytes, starting at offset 'off' in key[]).
100 */
101 public void SetKey(byte[] key, int off, int len)
102 {
103 h.Reset();
104 if (len > h.BlockSize) {
105 h.Update(key, off, len);
106 h.DoFinal(this.key, 0);
107 keyLen = h.DigestSize;
108 } else {
109 Array.Copy(key, off, this.key, 0, len);
110 keyLen = len;
111 }
112 Reset();
113 }
114
115 /*
116 * Add one byte to the input to HMAC.
117 */
118 public void Update(byte b)
119 {
120 h.Update(b);
121 dataLen ++;
122 }
123
124 /*
125 * Add some bytes to the input to HMAC.
126 */
127 public void Update(byte[] buf)
128 {
129 h.Update(buf, 0, buf.Length);
130 }
131
132 /*
133 * Add some bytes to the input to HMAC ('len' bytes from buf[],
134 * starting at offset 'off').
135 */
136 public void Update(byte[] buf, int off, int len)
137 {
138 h.Update(buf, off, len);
139 dataLen += (ulong)len;
140 }
141
142 /*
143 * Compute the HMAC value; it is written in outBuf[] at
144 * offset 'off'. The engine is also reset (as if Reset()
145 * was called).
146 */
147 public void DoFinal(byte[] outBuf, int off)
148 {
149 h.DoFinal(tmp, 0);
150 ProcessKey(0x5C);
151 h.Update(tmp);
152 h.DoFinal(outBuf, off);
153 Reset();
154 }
155
156 /*
157 * Compute the HMAC value; it is written to a newly allocated
158 * array, which is returned. The engine is also reset (as if
159 * Reset() was called).
160 */
161 public byte[] DoFinal()
162 {
163 byte[] r = new byte[h.DigestSize];
164 DoFinal(r, 0);
165 return r;
166 }
167
168 /*
169 * Reset the HMAC engine. This forgets all previously input
170 * data, but reuses the currently set key.
171 */
172 public void Reset()
173 {
174 h.Reset();
175 ProcessKey(0x36);
176 dataLen = 0;
177 }
178
179 /*
180 * Process some bytes, then compute the output.
181 *
182 * This function is supposed to implement the processing in
183 * constant time (and thus constant memory access pattern) for
184 * all values of 'len' between 'minLen' and 'maxLen'
185 * (inclusive). This function works only for the supported
186 * underlying hash functions (MD5, SHA-1 and the SHA-2
187 * functions).
188 *
189 * The source array (buf[]) must contain at least maxLen bytes
190 * (starting at offset 'off'); they will all be read.
191 */
192 public void ComputeCT(byte[] buf, int off, int len,
193 int minLen, int maxLen, byte[] outBuf, int outOff)
194 {
195 /*
196 * Padding is 0x80, followed by 0 to 63 bytes of value
197 * 0x00 (up to 127 bytes for SHA-384 and SHA-512), then
198 * the input bit length expressed over 64 bits
199 * (little-endian for MD5, big-endian for
200 * the SHA-* functions)(for SHA-384 and SHA-512, this is
201 * 128 bits).
202 *
203 * Note that we only support bit lengths that fit on
204 * 64 bits, so we can handle SHA-384/SHA-512 padding
205 * almost as if it was the same as SHA-256; we just have
206 * to take care of the larger blocks (128 bytes instead
207 * of 64) and the larger minimal overhead (17 bytes
208 * instead of 9 bytes).
209 *
210 * be true for big-endian length encoding
211 * bs block size, in bytes (must be a power of 2)
212 * po padding overhead (0x80 byte and length encoding)
213 */
214 bool be;
215 int bs, po;
216 if (h is MD5) {
217 be = false;
218 bs = 64;
219 po = 9;
220 } else if ((h is SHA1) || (h is SHA2Small)) {
221 be = true;
222 bs = 64;
223 po = 9;
224 } else if (h is SHA2Big) {
225 be = true;
226 bs = 128;
227 po = 17;
228 } else {
229 throw new NotSupportedException();
230 }
231
232 /*
233 * Method implemented here is inspired from the one
234 * described there:
235 * https://www.imperialviolet.org/2013/02/04/luckythirteen.html
236 */
237
238 /*
239 * We compute the data bit length; let's not forget
240 * the initial first block (the one with the HMAC key).
241 */
242 ulong bitLen = ((ulong)bs + dataLen + (ulong)len) << 3;
243
244 /*
245 * All complete blocks before minLen can be processed
246 * efficiently.
247 */
248 ulong nDataLen = (dataLen + (ulong)minLen) & ~(ulong)(bs - 1);
249 if (nDataLen > dataLen) {
250 int zlen = (int)(nDataLen - dataLen);
251 h.Update(buf, off, (int)(nDataLen - dataLen));
252 dataLen = nDataLen;
253 off += zlen;
254 len -= zlen;
255 maxLen -= zlen;
256 }
257
258 /*
259 * At that point:
260 * -- dataLen contains the number of bytes already processed
261 * (in total, not counting the initial key block).
262 * -- We must input 'len' bytes, which may be up to 'maxLen'
263 * (inclusive).
264 *
265 * We compute kr, kl, kz and km:
266 * kr number of input bytes already in the current block
267 * km index of the first byte after the end of the last
268 * padding block, if 'len' is equal to 'maxLen'
269 * kz index of the last byte of the actual last padding
270 * block
271 * kl index of the start of the encoded length
272 */
273 int kr = (int)dataLen & (bs - 1);
274 int kz = ((kr + len + po + bs - 1) & ~(bs - 1)) - 1 - kr;
275 int kl = kz - 7;
276 int km = ((kr + maxLen + po + bs - 1) & ~(bs - 1)) - kr;
277
278 /*
279 * We must process km bytes. For index i from 0 to km-1:
280 * d is from data[] if i < maxLen, 0x00 otherwise
281 * e is an encoded length byte or 0x00, depending on i
282 * These tests do not depend on the actual length, so
283 * they need not be constant-time.
284 *
285 * Actual input byte is:
286 * d if i < len
287 * 0x80 if i == len
288 * 0x00 if i > len and i < kl
289 * e if i >= kl
290 *
291 * We extract hash state whenever we reach a full block;
292 * we keep it only if i == kz.
293 */
294 int hlen = h.DigestSize;
295 for (int k = 0; k < hlen; k ++) {
296 tmp[k] = 0;
297 }
298 for (int i = 0; i < km; i ++) {
299 int d = (i < maxLen) ? buf[off + i] : 0x00;
300 int e;
301 int j = (kr + i) & (bs - 1);
302 if (j >= (bs - 8)) {
303 int k = (j - (bs - 8)) << 3;
304 if (be) {
305 e = (int)(bitLen >> (56 - k));
306 } else {
307 e = (int)(bitLen >> k);
308 }
309 e &= 0xFF;
310 } else {
311 e = 0x00;
312 }
313
314 /*
315 * x0 is 0x80 if i == len; otherwise it is d.
316 */
317 int z = i - len;
318 int x0 = 0x80 ^ (((z | -z) >> 31) & (0x80 ^ d));
319
320 /*
321 * x1 is e if i >= kl; otherwise it is 0x00.
322 */
323 int x1 = e & ~((i - kl) >> 31);
324
325 /*
326 * We input x0 if i <= len, x1 otherwise.
327 */
328 h.Update((byte)(x0 ^ (((len - i) >> 31) & (x0 ^ x1))));
329
330 /*
331 * Get current state if we are at the end of a block,
332 * and keep it if i == kz.
333 */
334 if (j == (bs - 1)) {
335 h.CurrentState(tmpCT, 0);
336 z = i - kz;
337 z = ~((z | -z) >> 31);
338 for (int k = 0; k < hlen; k ++) {
339 tmp[k] |= (byte)(z & tmpCT[k]);
340 }
341 }
342 }
343
344 /*
345 * We got the hash output in tmp[]; we must complete
346 * the HMAC computation.
347 */
348 h.Reset();
349 ProcessKey(0x5C);
350 h.Update(tmp);
351 h.DoFinal(outBuf, outOff);
352 Reset();
353 }
354
355 void ProcessKey(byte pad)
356 {
357 for (int i = 0; i < keyLen; i ++) {
358 h.Update((byte)(key[i] ^ pad));
359 }
360 for (int i = h.BlockSize - keyLen; i > 0; i --) {
361 h.Update(pad);
362 }
363 }
364 }
365
366 }