2 * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
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:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
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
30 * Implementation of HMAC (RFC 2104).
33 public sealed class HMAC {
42 * Create a new instance, using the provided hash function. The
43 * hash function instance will be used internally.
45 public HMAC(IDigest h) : this(h, true)
49 private HMAC(IDigest h, bool doReset)
53 if (n < h.DigestSize) {
54 throw new ArgumentException(
55 "invalid hash function for HMAC");
59 tmp = new byte[h.DigestSize];
60 tmpCT = new byte[h.DigestSize];
67 * Duplicate this engine. The returned instance inherits the
68 * current state (key, data already processed...) but is
69 * thereafter independent.
73 HMAC hm = new HMAC(h.Dup(), false);
74 Array.Copy(key, 0, hm.key, 0, keyLen);
81 * Get the HMAC output size (in bytes).
90 * Set the HMAC key. This implies a call to Reset() (thus,
91 * the key must be set before data begins to be inserted).
93 public void SetKey(byte[] key)
95 SetKey(key, 0, key.Length);
99 * Set the HMAC key ('len' bytes, starting at offset 'off' in key[]).
101 public void SetKey(byte[] key, int off, int len)
104 if (len > h.BlockSize) {
105 h.Update(key, off, len);
106 h.DoFinal(this.key, 0);
107 keyLen = h.DigestSize;
109 Array.Copy(key, off, this.key, 0, len);
116 * Add one byte to the input to HMAC.
118 public void Update(byte b)
125 * Add some bytes to the input to HMAC.
127 public void Update(byte[] buf)
129 h.Update(buf, 0, buf.Length);
133 * Add some bytes to the input to HMAC ('len' bytes from buf[],
134 * starting at offset 'off').
136 public void Update(byte[] buf, int off, int len)
138 h.Update(buf, off, len);
139 dataLen += (ulong)len;
143 * Compute the HMAC value; it is written in outBuf[] at
144 * offset 'off'. The engine is also reset (as if Reset()
147 public void DoFinal(byte[] outBuf, int off)
152 h.DoFinal(outBuf, off);
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).
161 public byte[] DoFinal()
163 byte[] r = new byte[h.DigestSize];
169 * Reset the HMAC engine. This forgets all previously input
170 * data, but reuses the currently set key.
180 * Process some bytes, then compute the output.
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
189 * The source array (buf[]) must contain at least maxLen bytes
190 * (starting at offset 'off'); they will all be read.
192 public void ComputeCT(byte[] buf, int off, int len,
193 int minLen, int maxLen, byte[] outBuf, int outOff)
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
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).
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)
220 } else if ((h is SHA1) || (h is SHA2Small)) {
224 } else if (h is SHA2Big) {
229 throw new NotSupportedException();
233 * Method implemented here is inspired from the one
235 * https://www.imperialviolet.org/2013/02/04/luckythirteen.html
239 * We compute the data bit length; let's not forget
240 * the initial first block (the one with the HMAC key).
242 ulong bitLen = ((ulong)bs + dataLen + (ulong)len) << 3;
245 * All complete blocks before minLen can be processed
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));
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'
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
271 * kl index of the start of the encoded length
273 int kr = (int)dataLen & (bs - 1);
274 int kz = ((kr + len + po + bs - 1) & ~(bs - 1)) - 1 - kr;
276 int km = ((kr + maxLen + po + bs - 1) & ~(bs - 1)) - kr;
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.
285 * Actual input byte is:
288 * 0x00 if i > len and i < kl
291 * We extract hash state whenever we reach a full block;
292 * we keep it only if i == kz.
294 int hlen = h.DigestSize;
295 for (int k = 0; k < hlen; k ++) {
298 for (int i = 0; i < km; i ++) {
299 int d = (i < maxLen) ? buf[off + i] : 0x00;
301 int j = (kr + i) & (bs - 1);
303 int k = (j - (bs - 8)) << 3;
305 e = (int)(bitLen >> (56 - k));
307 e = (int)(bitLen >> k);
315 * x0 is 0x80 if i == len; otherwise it is d.
318 int x0 = 0x80 ^ (((z | -z) >> 31) & (0x80 ^ d));
321 * x1 is e if i >= kl; otherwise it is 0x00.
323 int x1 = e & ~((i - kl) >> 31);
326 * We input x0 if i <= len, x1 otherwise.
328 h.Update((byte)(x0 ^ (((len - i) >> 31) & (x0 ^ x1))));
331 * Get current state if we are at the end of a block,
332 * and keep it if i == kz.
335 h.CurrentState(tmpCT, 0);
337 z = ~((z | -z) >> 31);
338 for (int k = 0; k < hlen; k ++) {
339 tmp[k] |= (byte)(z & tmpCT[k]);
345 * We got the hash output in tmp[]; we must complete
346 * the HMAC computation.
351 h.DoFinal(outBuf, outOff);
355 void ProcessKey(byte pad)
357 for (int i = 0; i < keyLen; i ++) {
358 h.Update((byte)(key[i] ^ pad));
360 for (int i = h.BlockSize - keyLen; i > 0; i --) {