Alexander Afanasyev | 181a8b9 | 2013-02-28 13:28:53 -0800 | [diff] [blame] | 1 | /* |
| 2 | * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined |
| 3 | * in FIPS 180-2 |
| 4 | * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009. |
| 5 | * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet |
| 6 | * Distributed under the BSD License |
| 7 | * See http://pajhome.org.uk/crypt/md5 for details. |
| 8 | * Also http://anmar.eu.org/projects/jssha2/ |
| 9 | */ |
| 10 | |
| 11 | /* |
| 12 | * Configurable variables. You may need to tweak these to be compatible with |
| 13 | * the server-side, but the defaults work in most cases. |
| 14 | */ |
| 15 | var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ |
| 16 | var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ |
| 17 | |
| 18 | /* |
| 19 | * These are the functions you'll usually want to call |
| 20 | * They take string arguments and return either hex or base-64 encoded strings |
| 21 | */ |
| 22 | |
| 23 | //@author axelcdv |
| 24 | /** |
| 25 | * Computes the Sha-256 hash of the given byte array |
| 26 | * @param {byte[]} |
| 27 | * @return the hex string corresponding to the Sha-256 hash of the byte array |
| 28 | */ |
| 29 | function hex_sha256_from_bytes(byteArray){ |
| 30 | return rstr2hex(binb2rstr(binb_sha256( byteArray2binb(byteArray), byteArray.length * 8))); |
| 31 | } |
| 32 | |
| 33 | function hex_sha256(s) { return rstr2hex(rstr_sha256(str2rstr_utf8(s))); } |
| 34 | function b64_sha256(s) { return rstr2b64(rstr_sha256(str2rstr_utf8(s))); } |
| 35 | function any_sha256(s, e) { return rstr2any(rstr_sha256(str2rstr_utf8(s)), e); } |
| 36 | function hex_hmac_sha256(k, d) |
| 37 | { return rstr2hex(rstr_hmac_sha256(str2rstr_utf8(k), str2rstr_utf8(d))); } |
| 38 | function b64_hmac_sha256(k, d) |
| 39 | { return rstr2b64(rstr_hmac_sha256(str2rstr_utf8(k), str2rstr_utf8(d))); } |
| 40 | function any_hmac_sha256(k, d, e) |
| 41 | { return rstr2any(rstr_hmac_sha256(str2rstr_utf8(k), str2rstr_utf8(d)), e); } |
| 42 | |
| 43 | |
| 44 | /* |
| 45 | function hex_sha256(s) { return rstr2hex(rstr_sha256(s)); } |
| 46 | function b64_sha256(s) { return rstr2b64(rstr_sha256(s)); } |
| 47 | function any_sha256(s, e) { return rstr2any(rstr_sha256(s), e); } |
| 48 | function hex_hmac_sha256(k, d) |
| 49 | { return rstr2hex(rstr_hmac_sha256(str2rstr_utf8(k), d)); } |
| 50 | function b64_hmac_sha256(k, d) |
| 51 | { return rstr2b64(rstr_hmac_sha256(str2rstr_utf8(k), d)); } |
| 52 | function any_hmac_sha256(k, d, e) |
| 53 | { return rstr2any(rstr_hmac_sha256(str2rstr_utf8(k), d), e); } |
| 54 | */ |
| 55 | |
| 56 | /* |
| 57 | * Perform a simple self-test to see if the VM is working |
| 58 | */ |
| 59 | function sha256_vm_test() |
| 60 | { |
| 61 | return hex_sha256("abc").toLowerCase() == |
| 62 | "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"; |
| 63 | } |
| 64 | |
| 65 | /** |
| 66 | * Calculate the sha256 of a raw string |
| 67 | * @param s: the raw string |
| 68 | */ |
| 69 | function rstr_sha256(s) |
| 70 | { |
| 71 | return binb2rstr(binb_sha256(rstr2binb(s), s.length * 8)); |
| 72 | } |
| 73 | |
| 74 | /** |
| 75 | * Calculate the HMAC-sha256 of a key and some data (raw strings) |
| 76 | */ |
| 77 | function rstr_hmac_sha256(key, data) |
| 78 | { |
| 79 | var bkey = rstr2binb(key); |
| 80 | if(bkey.length > 16) bkey = binb_sha256(bkey, key.length * 8); |
| 81 | |
| 82 | var ipad = Array(16), opad = Array(16); |
| 83 | for(var i = 0; i < 16; i++) |
| 84 | { |
| 85 | ipad[i] = bkey[i] ^ 0x36363636; |
| 86 | opad[i] = bkey[i] ^ 0x5C5C5C5C; |
| 87 | } |
| 88 | |
| 89 | var hash = binb_sha256(ipad.concat(rstr2binb(data)), 512 + data.length * 8); |
| 90 | return binb2rstr(binb_sha256(opad.concat(hash), 512 + 256)); |
| 91 | } |
| 92 | |
| 93 | /** |
| 94 | * Convert a raw string to a hex string |
| 95 | */ |
| 96 | function rstr2hex(input) |
| 97 | { |
| 98 | try { hexcase } catch(e) { hexcase=0; } |
| 99 | var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; |
| 100 | var output = ""; |
| 101 | var x; |
| 102 | for(var i = 0; i < input.length; i++) |
| 103 | { |
| 104 | x = input.charCodeAt(i); |
| 105 | output += hex_tab.charAt((x >>> 4) & 0x0F) |
| 106 | + hex_tab.charAt( x & 0x0F); |
| 107 | } |
| 108 | return output; |
| 109 | } |
| 110 | |
| 111 | /* |
| 112 | * Convert a raw string to a base-64 string |
| 113 | */ |
| 114 | function rstr2b64(input) |
| 115 | { |
| 116 | try { b64pad } catch(e) { b64pad=''; } |
| 117 | var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
| 118 | var output = ""; |
| 119 | var len = input.length; |
| 120 | for(var i = 0; i < len; i += 3) |
| 121 | { |
| 122 | var triplet = (input.charCodeAt(i) << 16) |
| 123 | | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0) |
| 124 | | (i + 2 < len ? input.charCodeAt(i+2) : 0); |
| 125 | for(var j = 0; j < 4; j++) |
| 126 | { |
| 127 | if(i * 8 + j * 6 > input.length * 8) output += b64pad; |
| 128 | else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F); |
| 129 | } |
| 130 | } |
| 131 | return output; |
| 132 | } |
| 133 | |
| 134 | /* |
| 135 | * Convert a raw string to an arbitrary string encoding |
| 136 | */ |
| 137 | function rstr2any(input, encoding) |
| 138 | { |
| 139 | var divisor = encoding.length; |
| 140 | var remainders = Array(); |
| 141 | var i, q, x, quotient; |
| 142 | |
| 143 | /* Convert to an array of 16-bit big-endian values, forming the dividend */ |
| 144 | var dividend = Array(Math.ceil(input.length / 2)); |
| 145 | for(i = 0; i < dividend.length; i++) |
| 146 | { |
| 147 | dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1); |
| 148 | } |
| 149 | |
| 150 | /* |
| 151 | * Repeatedly perform a long division. The binary array forms the dividend, |
| 152 | * the length of the encoding is the divisor. Once computed, the quotient |
| 153 | * forms the dividend for the next step. We stop when the dividend is zero. |
| 154 | * All remainders are stored for later use. |
| 155 | */ |
| 156 | while(dividend.length > 0) |
| 157 | { |
| 158 | quotient = Array(); |
| 159 | x = 0; |
| 160 | for(i = 0; i < dividend.length; i++) |
| 161 | { |
| 162 | x = (x << 16) + dividend[i]; |
| 163 | q = Math.floor(x / divisor); |
| 164 | x -= q * divisor; |
| 165 | if(quotient.length > 0 || q > 0) |
| 166 | quotient[quotient.length] = q; |
| 167 | } |
| 168 | remainders[remainders.length] = x; |
| 169 | dividend = quotient; |
| 170 | } |
| 171 | |
| 172 | /* Convert the remainders to the output string */ |
| 173 | var output = ""; |
| 174 | for(i = remainders.length - 1; i >= 0; i--) |
| 175 | output += encoding.charAt(remainders[i]); |
| 176 | |
| 177 | /* Append leading zero equivalents */ |
| 178 | var full_length = Math.ceil(input.length * 8 / |
| 179 | (Math.log(encoding.length) / Math.log(2))) |
| 180 | for(i = output.length; i < full_length; i++) |
| 181 | output = encoding[0] + output; |
| 182 | |
| 183 | return output; |
| 184 | } |
| 185 | |
| 186 | /* |
| 187 | * Encode a string as utf-8. |
| 188 | * For efficiency, this assumes the input is valid utf-16. |
| 189 | */ |
| 190 | function str2rstr_utf8(input) |
| 191 | { |
| 192 | var output = ""; |
| 193 | var i = -1; |
| 194 | var x, y; |
| 195 | |
| 196 | while(++i < input.length) |
| 197 | { |
| 198 | /* Decode utf-16 surrogate pairs */ |
| 199 | x = input.charCodeAt(i); |
| 200 | y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0; |
| 201 | if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) |
| 202 | { |
| 203 | x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF); |
| 204 | i++; |
| 205 | } |
| 206 | |
| 207 | /* Encode output as utf-8 */ |
| 208 | if(x <= 0x7F) |
| 209 | output += String.fromCharCode(x); |
| 210 | else if(x <= 0x7FF) |
| 211 | output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F), |
| 212 | 0x80 | ( x & 0x3F)); |
| 213 | else if(x <= 0xFFFF) |
| 214 | output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), |
| 215 | 0x80 | ((x >>> 6 ) & 0x3F), |
| 216 | 0x80 | ( x & 0x3F)); |
| 217 | else if(x <= 0x1FFFFF) |
| 218 | output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), |
| 219 | 0x80 | ((x >>> 12) & 0x3F), |
| 220 | 0x80 | ((x >>> 6 ) & 0x3F), |
| 221 | 0x80 | ( x & 0x3F)); |
| 222 | } |
| 223 | return output; |
| 224 | } |
| 225 | |
| 226 | /* |
| 227 | * Encode a string as utf-16 |
| 228 | */ |
| 229 | function str2rstr_utf16le(input) |
| 230 | { |
| 231 | var output = ""; |
| 232 | for(var i = 0; i < input.length; i++) |
| 233 | output += String.fromCharCode( input.charCodeAt(i) & 0xFF, |
| 234 | (input.charCodeAt(i) >>> 8) & 0xFF); |
| 235 | return output; |
| 236 | } |
| 237 | |
| 238 | function str2rstr_utf16be(input) |
| 239 | { |
| 240 | var output = ""; |
| 241 | for(var i = 0; i < input.length; i++) |
| 242 | output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, |
| 243 | input.charCodeAt(i) & 0xFF); |
| 244 | return output; |
| 245 | } |
| 246 | |
| 247 | /** |
| 248 | * Convert a raw string to an array of big-endian words |
| 249 | * Characters >255 have their high-byte silently ignored. |
| 250 | */ |
| 251 | function rstr2binb(input) |
| 252 | { |
| 253 | //console.log('Raw string comming is '+input); |
| 254 | var output = Array(input.length >> 2); |
| 255 | /* JavaScript automatically zeroizes a new array. |
| 256 | for(var i = 0; i < output.length; i++) |
| 257 | output[i] = 0; |
| 258 | */ |
| 259 | for(var i = 0; i < input.length * 8; i += 8) |
| 260 | output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32); |
| 261 | return output; |
| 262 | } |
| 263 | |
| 264 | /** |
| 265 | * @author axelcdv |
| 266 | * Convert a byte array to an array of big-endian words |
| 267 | * @param {byte[]} input |
| 268 | * @return the array of big-endian words |
| 269 | */ |
| 270 | function byteArray2binb(input){ |
| 271 | //console.log("Byte array coming is " + input); |
| 272 | var output = Array(input.length >> 2); |
| 273 | /* JavaScript automatically zeroizes a new array. |
| 274 | for(var i = 0; i < output.length; i++) |
| 275 | output[i] = 0; |
| 276 | */ |
| 277 | for(var i = 0; i < input.length * 8; i += 8) |
| 278 | output[i>>5] |= (input[i / 8] & 0xFF) << (24 - i % 32); |
| 279 | return output; |
| 280 | } |
| 281 | |
| 282 | /* |
| 283 | * Convert an array of big-endian words to a string |
| 284 | */ |
| 285 | function binb2rstr(input) |
| 286 | { |
| 287 | var output = ""; |
| 288 | for(var i = 0; i < input.length * 32; i += 8) |
| 289 | output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF); |
| 290 | return output; |
| 291 | } |
| 292 | |
| 293 | /* |
| 294 | * Main sha256 function, with its support functions |
| 295 | */ |
| 296 | function sha256_S (X, n) {return ( X >>> n ) | (X << (32 - n));} |
| 297 | function sha256_R (X, n) {return ( X >>> n );} |
| 298 | function sha256_Ch(x, y, z) {return ((x & y) ^ ((~x) & z));} |
| 299 | function sha256_Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));} |
| 300 | function sha256_Sigma0256(x) {return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22));} |
| 301 | function sha256_Sigma1256(x) {return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25));} |
| 302 | function sha256_Gamma0256(x) {return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3));} |
| 303 | function sha256_Gamma1256(x) {return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10));} |
| 304 | function sha256_Sigma0512(x) {return (sha256_S(x, 28) ^ sha256_S(x, 34) ^ sha256_S(x, 39));} |
| 305 | function sha256_Sigma1512(x) {return (sha256_S(x, 14) ^ sha256_S(x, 18) ^ sha256_S(x, 41));} |
| 306 | function sha256_Gamma0512(x) {return (sha256_S(x, 1) ^ sha256_S(x, 8) ^ sha256_R(x, 7));} |
| 307 | function sha256_Gamma1512(x) {return (sha256_S(x, 19) ^ sha256_S(x, 61) ^ sha256_R(x, 6));} |
| 308 | |
| 309 | var sha256_K = new Array |
| 310 | ( |
| 311 | 1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993, |
| 312 | -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987, |
| 313 | 1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522, |
| 314 | 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, |
| 315 | -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585, |
| 316 | 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, |
| 317 | 1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885, |
| 318 | -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344, |
| 319 | 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, |
| 320 | 1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872, |
| 321 | -1866530822, -1538233109, -1090935817, -965641998 |
| 322 | ); |
| 323 | |
| 324 | function binb_sha256(m, l) |
| 325 | { |
| 326 | var HASH = new Array(1779033703, -1150833019, 1013904242, -1521486534, |
| 327 | 1359893119, -1694144372, 528734635, 1541459225); |
| 328 | var W = new Array(64); |
| 329 | |
| 330 | /* append padding */ |
| 331 | m[l >> 5] |= 0x80 << (24 - l % 32); |
| 332 | m[((l + 64 >> 9) << 4) + 15] = l; |
| 333 | |
| 334 | for(var offset = 0; offset < m.length; offset += 16) |
| 335 | processBlock_sha256(m, offset, HASH, W); |
| 336 | |
| 337 | return HASH; |
| 338 | } |
| 339 | |
| 340 | /* |
| 341 | * Process a block of 16 4-byte words in m starting at offset and update HASH. |
| 342 | * offset must be a multiple of 16 and less than m.length. W is a scratchpad Array(64). |
| 343 | */ |
| 344 | function processBlock_sha256(m, offset, HASH, W) { |
| 345 | var a, b, c, d, e, f, g, h; |
| 346 | var j, T1, T2; |
| 347 | |
| 348 | a = HASH[0]; |
| 349 | b = HASH[1]; |
| 350 | c = HASH[2]; |
| 351 | d = HASH[3]; |
| 352 | e = HASH[4]; |
| 353 | f = HASH[5]; |
| 354 | g = HASH[6]; |
| 355 | h = HASH[7]; |
| 356 | |
| 357 | for(j = 0; j < 64; j++) |
| 358 | { |
| 359 | if (j < 16) W[j] = m[j + offset]; |
| 360 | else W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]), |
| 361 | sha256_Gamma0256(W[j - 15])), W[j - 16]); |
| 362 | |
| 363 | T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)), |
| 364 | sha256_K[j]), W[j]); |
| 365 | T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c)); |
| 366 | h = g; |
| 367 | g = f; |
| 368 | f = e; |
| 369 | e = safe_add(d, T1); |
| 370 | d = c; |
| 371 | c = b; |
| 372 | b = a; |
| 373 | a = safe_add(T1, T2); |
| 374 | } |
| 375 | |
| 376 | HASH[0] = safe_add(a, HASH[0]); |
| 377 | HASH[1] = safe_add(b, HASH[1]); |
| 378 | HASH[2] = safe_add(c, HASH[2]); |
| 379 | HASH[3] = safe_add(d, HASH[3]); |
| 380 | HASH[4] = safe_add(e, HASH[4]); |
| 381 | HASH[5] = safe_add(f, HASH[5]); |
| 382 | HASH[6] = safe_add(g, HASH[6]); |
| 383 | HASH[7] = safe_add(h, HASH[7]); |
| 384 | } |
| 385 | |
| 386 | function safe_add (x, y) |
| 387 | { |
| 388 | var lsw = (x & 0xFFFF) + (y & 0xFFFF); |
| 389 | var msw = (x >> 16) + (y >> 16) + (lsw >> 16); |
| 390 | return (msw << 16) | (lsw & 0xFFFF); |
| 391 | } |
| 392 | |
| 393 | /* |
| 394 | * Create a Sha256, call update(data) multiple times, then call finalize(). |
| 395 | */ |
| 396 | var Sha256 = function Sha256() { |
| 397 | this.W = new Array(64); |
| 398 | this.hash = new Array(1779033703, -1150833019, 1013904242, -1521486534, |
| 399 | 1359893119, -1694144372, 528734635, 1541459225); |
| 400 | this.nTotalBytes = 0; |
| 401 | this.buffer = new Uint8Array(16 * 4); |
| 402 | this.nBufferBytes = 0; |
| 403 | } |
| 404 | |
| 405 | /* |
| 406 | * Update the hash with data, which is Uint8Array. |
| 407 | */ |
| 408 | Sha256.prototype.update = function(data) { |
| 409 | this.nTotalBytes += data.length; |
| 410 | |
| 411 | if (this.nBufferBytes > 0) { |
| 412 | // Fill up the buffer and process it first. |
| 413 | var bytesNeeded = this.buffer.length - this.nBufferBytes; |
| 414 | if (data.length < bytesNeeded) { |
| 415 | this.buffer.set(data, this.nBufferBytes); |
| 416 | this.nBufferBytes += data.length; |
| 417 | return; |
| 418 | } |
| 419 | else { |
| 420 | this.buffer.set(data.subarray(0, bytesNeeded), this.nBufferBytes); |
| 421 | processBlock_sha256(byteArray2binb(this.buffer), 0, this.hash, this.W); |
| 422 | this.nBufferBytes = 0; |
| 423 | // Consume the bytes from data. |
| 424 | data = data.subarray(bytesNeeded, data.length); |
| 425 | if (data.length == 0) |
| 426 | return; |
| 427 | } |
| 428 | } |
| 429 | |
| 430 | // 2^6 is 16 * 4. |
| 431 | var nBlocks = data.length >> 6; |
| 432 | if (nBlocks > 0) { |
| 433 | var nBytes = nBlocks * 16 * 4; |
| 434 | var m = byteArray2binb(data.subarray(0, nBytes)); |
| 435 | for(var offset = 0; offset < m.length; offset += 16) |
| 436 | processBlock_sha256(m, offset, this.hash, this.W); |
| 437 | |
| 438 | data = data.subarray(nBytes, data.length); |
| 439 | } |
| 440 | |
| 441 | if (data.length > 0) { |
| 442 | // Save the remainder in the buffer. |
| 443 | this.buffer.set(data); |
| 444 | this.nBufferBytes = data.length; |
| 445 | } |
| 446 | } |
| 447 | |
| 448 | /* |
| 449 | * Finalize the hash and return the result as Uint8Array. |
| 450 | * Only call this once. Return values on subsequent calls are undefined. |
| 451 | */ |
| 452 | Sha256.prototype.finalize = function() { |
| 453 | var m = byteArray2binb(this.buffer.subarray(0, this.nBufferBytes)); |
| 454 | /* append padding */ |
| 455 | var l = this.nBufferBytes * 8; |
| 456 | m[l >> 5] |= 0x80 << (24 - l % 32); |
| 457 | m[((l + 64 >> 9) << 4) + 15] = this.nTotalBytes * 8; |
| 458 | |
| 459 | for(var offset = 0; offset < m.length; offset += 16) |
| 460 | processBlock_sha256(m, offset, this.hash, this.W); |
| 461 | |
| 462 | return Sha256.binb2Uint8Array(this.hash); |
| 463 | } |
| 464 | |
| 465 | /* |
| 466 | * Convert an array of big-endian words to Uint8Array. |
| 467 | */ |
| 468 | Sha256.binb2Uint8Array = function(input) |
| 469 | { |
| 470 | var output = new Uint8Array(input.length * 4); |
| 471 | var iOutput = 0; |
| 472 | for (var i = 0; i < input.length * 32; i += 8) |
| 473 | output[iOutput++] = (input[i>>5] >>> (24 - i % 32)) & 0xFF; |
| 474 | return output; |
| 475 | } |