Last active
September 21, 2021 14:14
-
-
Save tresf/ca5442082657e349090591c534400614 to your computer and use it in GitHub Desktop.
A version of sha256 that won't break with non-UTF8 web pages
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* @overview SHA-256 implementation in JavaScript | |
* @copyright Copyright (c) Chris Veness 2002-2014 | |
* @license Licensed under MIT license | |
* See http://www.movable-type.co.uk/scripts/sha256.html | |
* | |
* WARNING: This library been modified from its original to work without UTF8 support | |
* If using with QZ Tray, please consider upgrading to qz-tray.js 2.1 instead. :) | |
*/ | |
//@formatter:off | |
"use strict"; | |
// polyfills | |
if (typeof String.prototype.utf8Encode == 'undefined') { | |
String.prototype.utf8Encode = function() { return unescape(encodeURIComponent(this)); }; | |
} | |
if (typeof String.prototype.utf8Decode == 'undefined') { | |
String.prototype.utf8Decode = function() { | |
try { return decodeURIComponent(escape(this)); } | |
catch(e) { return this; } // invalid UTF-8? return as-is | |
}; | |
} | |
var Sha256 = {}; | |
// Rotates right (circular right shift) value x by n positions | |
Sha256._rotr = function(n, x) { return (x >>> n) | (x << (32 - n)); }, | |
// logical functions | |
Sha256._sig0 = function(x) { return Sha256._rotr(2, x) ^ Sha256._rotr(13, x) ^ Sha256._rotr(22, x); }, | |
Sha256._sig1 = function(x) { return Sha256._rotr(6, x) ^ Sha256._rotr(11, x) ^ Sha256._rotr(25, x); }, | |
Sha256._dev0 = function(x) { return Sha256._rotr(7, x) ^ Sha256._rotr(18, x) ^ (x >>> 3); }, | |
Sha256._dev1 = function(x) { return Sha256._rotr(17, x) ^ Sha256._rotr(19, x) ^ (x >>> 10); }, | |
Sha256._ch = function(x, y, z) { return (x & y) ^ (~x & z); }, | |
Sha256._maj = function(x, y, z) { return (x & y) ^ (x & z) ^ (y & z); }, | |
// note can't use toString(16) as it is implementation-dependant, and in IE returns signed numbers when used on full words | |
Sha256._hexStr = function(n) { var s = "", v; for(var i = 7; i >= 0; i--) { v = (n >>> (i * 4)) & 0xf; s += v.toString(16); } return s; }, | |
//@formatter:on | |
Sha256.hash = function(msg) { | |
// add trailing '1' bit (+ 0's padding) to string [§5.1.1] | |
msg = msg.utf8Encode() + String.fromCharCode(0x80); | |
// constants [§4.2.2] | |
var K = [ | |
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, | |
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, | |
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, | |
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, | |
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, | |
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, | |
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, | |
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 | |
]; | |
// initial hash value [§5.3.1] | |
var H = [ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ]; | |
// convert string msg into 512-bit/16-integer blocks arrays of ints [§5.2.1] | |
var l = msg.length / 4 + 2; // length (in 32-bit integers) of msg + ‘1’ + appended length | |
var N = Math.ceil(l / 16); // number of 16-integer-blocks required to hold 'l' ints | |
var M = new Array(N); | |
for(var i = 0; i < N; i++) { | |
M[i] = new Array(16); | |
for(var j = 0; j < 16; j++) { // encode 4 chars per integer, big-endian encoding | |
M[i][j] = (msg.charCodeAt(i * 64 + j * 4) << 24) | (msg.charCodeAt(i * 64 + j * 4 + 1) << 16) | | |
(msg.charCodeAt(i * 64 + j * 4 + 2) << 8) | (msg.charCodeAt(i * 64 + j * 4 + 3)); | |
} // note running off the end of msg is ok 'cos bitwise ops on NaN return 0 | |
} | |
// add length (in bits) into final pair of 32-bit integers (big-endian) [§5.1.1] | |
// note: most significant word would be (len-1)*8 >>> 32, but since JS converts | |
// bitwise-op args to 32 bits, we need to simulate this by arithmetic operators | |
M[N-1][14] = ((msg.length - 1) * 8) / Math.pow(2, 32); | |
M[N-1][14] = Math.floor(M[N-1][14]); | |
M[N-1][15] = ((msg.length - 1) * 8) & 0xffffffff; | |
// HASH COMPUTATION [§6.1.2] | |
var W = new Array(64); var a, b, c, d, e, f, g, h; | |
for(var i = 0; i < N; i++) { | |
// 1 - prepare message schedule 'W' | |
for(var t = 0; t < 16; t++) { W[t] = M[i][t]; } | |
for(var t = 16; t < 64; t++) { W[t] = (Sha256._dev1(W[t-2]) + W[t-7] + Sha256._dev0(W[t-15]) + W[t-16]) & 0xffffffff; } | |
// 2 - initialise working variables a, b, c, d, e, f, g, h with previous hash value | |
a = H[0]; b = H[1]; c = H[2]; d = H[3]; e = H[4]; f = H[5]; g = H[6]; h = H[7]; | |
// 3 - main loop (note 'addition modulo 2^32') | |
for(var t = 0; t < 64; t++) { | |
var T1 = h + Sha256._sig1(e) + Sha256._ch(e, f, g) + K[t] + W[t]; | |
var T2 = Sha256._sig0(a) + Sha256._maj(a, b, c); | |
h = g; g = f; f = e; e = (d + T1) & 0xffffffff; | |
d = c; c = b; b = a; a = (T1 + T2) & 0xffffffff; | |
} | |
// 4 - compute the new intermediate hash value (note 'addition modulo 2^32') | |
H[0] = (H[0]+a) & 0xffffffff; H[1] = (H[1]+b) & 0xffffffff; H[2] = (H[2]+c) & 0xffffffff; H[3] = (H[3]+d) & 0xffffffff; | |
H[4] = (H[4]+e) & 0xffffffff; H[5] = (H[5]+f) & 0xffffffff; H[6] = (H[6]+g) & 0xffffffff; H[7] = (H[7]+h) & 0xffffffff; | |
} | |
return Sha256._hexStr(H[0]) + Sha256._hexStr(H[1]) + Sha256._hexStr(H[2]) + Sha256._hexStr(H[3]) + | |
Sha256._hexStr(H[4]) + Sha256._hexStr(H[5]) + Sha256._hexStr(H[6]) + Sha256._hexStr(H[7]); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment