-
Notifications
You must be signed in to change notification settings - Fork 6
/
hmacSha1.js
159 lines (119 loc) · 7.68 KB
/
hmacSha1.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
var crypto;
var sha1;
try{
crypto = require('crypto');
sha1 = function(key,enc, format){
enc = enc || 'binary'; // Fix for node versions >=6.0.0, in which default encoding is
// changed to utf-8.
format = format || 'hex'; // Defaults to hex
var hash = crypto.createHash('sha1'); // Create instance of sha1
hash.update(key, enc); // Feed data to it, specify encoding
return hash.digest(format); // Return result specified format
}
}
catch(err){
console.log('core crypto lib is unavalable: '+ err);
}
function HmacSha1(format){ // Format of hmac result (defaults to 'hex', can be 'base64')
this.blocksize = 64; // 64 when using these hash functions: SHA-1, MD5, RIPEMD-128/160 .
var opad = 0x5c; // outer padding constant = (0x5c) . And 0x5c is just hexadecimal for backward slash "\"
var ipad = 0x36; // inner padding contant = (0x36). And 0x36 is hexadecimal for char "6".
// We made both constants private.
this.digest = function (key, baseString, enc){ // the actual digest function
var opad_key = ""; // outer padded key
var ipad_key = ""; // inner padded key
var kLen = (enc === 'latin-1' || enc === 'utf8') ? this.asciiOnly(key) : key.length; // Enforce ascii in
// key, only if non ascii
// encoding specified.
var diff;
var hashedKeyLen;
if(kLen < this.blocksize){
diff = this.blocksize - kLen; // diff is how much blocksize is bigger then the key
}
if(kLen > this.blocksize){
key = this.hexToString(sha1(key, enc)); // The hash of 40 hex chars(40bytes) convert to exact char
// mappings, each char has codepoint from 0x00 to 0xff.
// Produces string of 20 bytes.
hashedKeyLen = key.length; // Take the length of key
}
(function applyXor(){ // Reads one char, at the time, from key and applies XOR constants on it
// acording to the length of the key.
var o_zeroPaddedCode; // result from opading the zero byte
var i_zeroPaddedCode; // res from ipading the zero byte
var o_paddedCode; // res from opading the char from key
var i_paddedCode; // res from ipading the char from key
var code; // Numeric represantation of char
for(var j = 0; j < this.blocksize; j++){
if(diff && (j+diff) >= this.blocksize || j >= hashedKeyLen){ // if diff exists (key is shorter then
// blocksize) and if we are at boundry where we should
// be, apply XOR on zero byte and constants, result put
// in corresponding padding key. Or the key was too long
// and was hashed, then also we need to do same thing.
o_zeroPaddedCode = 0x00 ^ opad; // XOR the zero byte with outer padding constant
opad_key += String.fromCodePoint(o_zeroPaddedCode); // convert result back to string
// using ".fromCodePoint()" so it can
// correctly return codes from chars in
// higher unicode plains
i_zeroPaddedCode = 0x00 ^ ipad;
ipad_key += String.fromCodePoint(i_zeroPaddedCode);
}
else {
code = key.codePointAt(j); // get code (number) of that char
o_paddedCode = code ^ opad; // XOR the char code with outer padding constant (opad)
opad_key += String.fromCodePoint(o_paddedCode); // convert back code result to string
i_paddedCode = code ^ ipad; // XOR with the inner padding constant (ipad)
ipad_key += String.fromCodePoint(i_paddedCode);
}
}
// console.log("opad_key: ", "|"+opad_key+"|",' len: '+ opad_key.length, "\nipad_key: ", "|"+ipad_key+"|", " len: "+ipad_key.length); // Prints opad and ipad key, line can be deleted.
}.bind(this))() // binding "this" reference in applyXor to each "instance" of HmacSha1
var stringify = this.hexToString(sha1(ipad_key + baseString, enc));// convert sha1 hex to character string
if(format === 'base64') return sha1(opad_key + stringify, '', format); // pass format as third arg
else return sha1(opad_key + stringify);
}
}
HmacSha1.prototype.messages = {
nonAscii: 'Key must contain only ascii code.'
}
HmacSha1.prototype.asciiOnly = function (str){ // Checks for ascii code, if ok, returns number of characters
// in str
var len = str.length,
code,
i;
for(i = 0; i < len; i++){
code = str.codePointAt(i);
if(code > 0x7f){ // check non ascii code
throw new Error(this.messages.nonAscii +" Char outside range is: " + String.fromCodePoint(code))
return;
}
}
return len; // if all ok, return length (number of chars);
}
HmacSha1.prototype.hexToString = function (sha1Output){ // Converts every pair of hex CHARS to their character
// conterparts.
// example1: "4e" is converted to char "N"
// example2: "34" is converted to char "4"
var l; // char at "i" place, left
var lcode; // code parsed from left char
var shiftedL; // left character shifted to the left
var r; // char at "i+1" place, right
var rcode; // code parsed from right char
var bin; // code from bitwise OR operation
var char; // one character
var result = ""; // result string
for (var i = 0; i < sha1Output.length; i+=2){ // In steps by 2
l = sha1Output[i]; // Take "left" char
if(typeof l === "number") lcode = parseInt(l); // Parse the number
else if(typeof l === "string") lcode = parseInt(l,16); // Take the code if char is hex number
// in range (a...f);
shiftedL = lcode << 4 ; // Shift it to left 4 places, gets filled in with 4 zeroes from the right
r = sha1Output[i+1]; // Take next char
if(typeof r === "number") rcode = parseInt(r); // Parse the number
else if(typeof r === "string") rcode = parseInt(r,16); // Take the code
bin = shiftedL | rcode; // Bitwise OR. This is essantialy concatenation. One character from two.
char = String.fromCodePoint(bin); // convert it back to string
result += char; // append to result string
}
return result;
}
module.exports = HmacSha1; // returns function that can be used in constructor calls, with 'new'