|
1 |
| -/* Copyright (c) 2015 Mikael Ganehag Brorsson. See the file LICENSE for copying permission. */ |
2 |
| -/* |
3 |
| -Small module to add HMAC support. Depends on 'hashlib'. |
4 |
| -*/ |
5 |
| - |
6 |
| -function hmac(key, message, digestmod) { |
7 |
| - if (digestmod == null) { |
8 |
| - digestmod = hashlib.sha256; |
9 |
| - } |
10 |
| - |
11 |
| - this.finished = false; |
12 |
| - this.inner = digestmod(); |
13 |
| - this.outer = digestmod(); |
14 |
| - |
15 |
| - var i, pad = new Uint8Array(this.inner.block_size); |
16 |
| - |
17 |
| - if (key.length > this.inner.block_size) { |
18 |
| - var h = (new digestmod()).update(k), |
19 |
| - ah = E.toBufferArray(h.digest()); |
20 |
| - |
21 |
| - for(i = 0; i < ah.length; i++) { |
22 |
| - pad[i] = ah[i].charCodeAt(0); |
23 |
| - } |
24 |
| - } |
25 |
| - else { |
26 |
| - for (i = 0; i < key.length; i++) { |
27 |
| - pad[i] = key[i].charCodeAt(0); |
28 |
| - } |
29 |
| - } |
30 |
| - |
31 |
| - for (i = 0; i < pad.length; i++) { |
32 |
| - pad[i] ^= 0x36; |
33 |
| - } |
34 |
| - this.inner.update(String.fromCharCode.apply(null, pad)); |
35 |
| - |
36 |
| - for (i = 0; i < pad.length; i++) { |
37 |
| - pad[i] ^= 0x36 ^ 0x5c; |
38 |
| - } |
39 |
| - this.outer.update(String.fromCharCode.apply(null, pad)); |
40 |
| - |
41 |
| - for (i = 0; i < pad.length; i++) { |
42 |
| - pad[i] = 0; |
43 |
| - } |
| 1 | +/* Copyright (c) 2020 Dominik Enzinger. See the file LICENSE for copying permission. */ |
| 2 | +function bitwiseOr0x36(b) {"compiled"; return b ^ 0x36; } |
| 3 | + |
| 4 | +function bitwiseOr0x5c(b) {"compiled"; return b ^ 0x5c; } |
| 5 | + |
| 6 | +/// Returns an MAC instance using the given hash. Eg. HMAC(key, require('crypto').SHA1, 64, 20) |
| 7 | +exports.HMAC = function(key, hash, blockSize, outputSize) { |
| 8 | + if ( key.byteLength > blockSize ) |
| 9 | + key = hash(key); |
| 10 | + this.hash = hash; |
| 11 | + this.keyLength = Math.max(blockSize, key.byteLength); |
| 12 | + key = new Uint8Array(key, 0, this.keyLength); |
| 13 | + this.oBuf = new Uint8Array(this.keyLength + outputSize); |
| 14 | + this.oBuf.set(key.map(bitwiseOr0x5c).buffer, 0); |
| 15 | + this.iKeyPad = key.map(bitwiseOr0x36).buffer; |
| 16 | +}; |
44 | 17 |
|
45 |
| - if(message) { |
46 |
| - this.inner.update(message); |
47 |
| - } |
48 |
| -} |
49 | 18 |
|
50 |
| -hmac.prototype.update = function(m) { |
51 |
| - if(m) { |
52 |
| - this.finished = false; |
53 |
| - this.inner.update(m); |
54 |
| - } |
55 |
| - return this; |
| 19 | +/// Take a message as an arraybuffer or string, return an arraybuffer |
| 20 | +exports.HMAC.prototype.digest = function(message) { |
| 21 | + const iBuf = new Uint8Array(this.keyLength + message.byteLength); |
| 22 | + iBuf.set(this.iKeyPad, 0); |
| 23 | + iBuf.set(message, this.keyLength); |
| 24 | + this.oBuf.set(this.hash(iBuf), this.keyLength); |
| 25 | + return this.hash(this.oBuf); |
56 | 26 | };
|
57 | 27 |
|
58 |
| -hmac.prototype.digest = function() { |
59 |
| - if(!this.finished) { |
60 |
| - this.outer.update(this.inner.digest()) |
61 |
| - this.finished = true; |
62 |
| - } |
63 |
| - return this.outer.digest(); |
| 28 | +function FixedHMAC(key, messageSize, hash, blockSize, outputSize) { |
| 29 | + exports.HMAC.call(this, key, hash, blockSize, outputSize); |
| 30 | + this.iBuf = new Uint8Array(this.keyLength + messageSize); |
| 31 | + this.iBuf.set(this.iKeyPad, 0); |
| 32 | + delete this.ikeyPad; |
64 | 33 | };
|
65 | 34 |
|
66 |
| -hmac.prototype.hexdigest = function() { |
67 |
| - var i, v, s = "", h = this.digest(); |
68 |
| - for(i = 0; i < h.length; i++) |
69 |
| - s += (256+h.charCodeAt(i)).toString(16).substr(-2); |
70 |
| - return s; |
| 35 | +/// Take a message as an arraybuffer or string, return an arraybuffer |
| 36 | +FixedHMAC.prototype.digest = function(message) { |
| 37 | + this.iBuf.set(message, this.keyLength); |
| 38 | + this.oBuf.set(this.hash(this.iBuf), this.keyLength); |
| 39 | + return this.hash(this.oBuf); |
71 | 40 | };
|
72 | 41 |
|
73 |
| -exports.create = function(key, message, digestmod) { |
74 |
| - return new hmac(key, message, digestmod); |
75 |
| -} |
76 |
| - |
77 |
| -/** |
78 |
| - compare_digest(a, b) -> bool |
79 |
| - |
80 |
| - Return 'a == b'. This function uses an approach designed to prevent |
81 |
| - timing analysis, making it appropriate for cryptography. |
82 |
| - a and b must both be of the same type. |
83 |
| - |
84 |
| - Note: If a and b are of different lengths, or if an error occurs, |
85 |
| - a timing attack could theoretically reveal information about the |
86 |
| - types and lengths of a and b--but not their values. |
87 |
| -*/ |
88 |
| -exports.compare_digest = function(a, b) { |
89 |
| - var match, i; |
90 |
| - |
91 |
| - if(a.length != b.length) { |
92 |
| - return false; |
93 |
| - } |
94 |
| - |
95 |
| - match = 0; |
96 |
| - for(i = 0; i < a.length; i++) { |
97 |
| - match |= a.charCodeAt(i) ^ b.charCodeAt(i); |
98 |
| - } |
| 42 | +/// Create a basic HMAC using SHA1 |
| 43 | +exports.SHA1 = function(key) { |
| 44 | + return new exports.HMAC(key, require('crypto').SHA1, 64, 20); |
| 45 | +}; |
99 | 46 |
|
100 |
| - return match == 0; |
| 47 | +/// FixedSHA1 is faster than SHA1, but digested message must always be the same fixed length. |
| 48 | +exports.FixedSHA1 = function(key, messageSize) { |
| 49 | + return new FixedHMAC(key, messageSize, require('crypto').SHA1, 64, 20); |
101 | 50 | };
|
0 commit comments