From 357fd1640dd66880622876cbed7b083fb498e278 Mon Sep 17 00:00:00 2001 From: frankie-tech <26461046+frankie-tech@users.noreply.github.com> Date: Wed, 21 Oct 2020 13:35:47 -0400 Subject: [PATCH 1/6] adds form back to event interface needed for part of pubsub wrapper --- src/types.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types.d.ts b/src/types.d.ts index 91b719c..a9d842d 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -21,6 +21,7 @@ export { Verification, EncodedMessage, WorkerResponse }; interface eventInterface { event: string; + form?: HTMLFormElement; difficulty?: number | string; verification?: Verification[]; progress?: number; From 8c52848e3674ae8f00d886d8bb33906e78b5a253 Mon Sep 17 00:00:00 2001 From: frankie-tech <26461046+frankie-tech@users.noreply.github.com> Date: Wed, 21 Oct 2020 13:36:40 -0400 Subject: [PATCH 2/6] uses form object to store worker cleans up the global scope, and also makes things a little less wonky IMO --- src/index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/index.js b/src/index.js index d2fc60c..c83a24d 100644 --- a/src/index.js +++ b/src/index.js @@ -45,6 +45,7 @@ import getSettings from './includes/get-settings'; const eventDefault = { event: 'whc:Update#' + i, difficulty, + form, verification: [], progress: 0, done: false, @@ -68,13 +69,12 @@ import getSettings from './includes/get-settings'; } function verify() { - const time = +new Date(); - this.whcWorkers[i] = createWorker(worker); + form.__worker = createWorker(worker); - this.whcWorkers[i].addEventListener('message', workerHandler); - this.whcWorkers[i].postMessage({ + form.__worker.addEventListener('message', workerHandler); + form.__worker.postMessage({ difficulty, - time, + time: Date.now(), }); e.run( 'whc:Start#' + i, @@ -92,7 +92,7 @@ import getSettings from './includes/get-settings'; button.removeAttribute('disabled'); button.setAttribute('value', '' + finished); // @ts-ignore - w.whcWorkers[i].terminate(); + form.__worker.terminate(); } /** From 649f78dbd28dbc0a63fd862bb1e3c42e14e9e2ed Mon Sep 17 00:00:00 2001 From: frankie-tech <26461046+frankie-tech@users.noreply.github.com> Date: Wed, 21 Oct 2020 14:12:32 -0400 Subject: [PATCH 3/6] extracted hashing functions to a module --- src/includes/worker.js | 266 +---------------------------------------- 1 file changed, 2 insertions(+), 264 deletions(-) diff --git a/src/includes/worker.js b/src/includes/worker.js index 1420e91..881e48e 100644 --- a/src/includes/worker.js +++ b/src/includes/worker.js @@ -1,269 +1,5 @@ // @ts-check export default function () { - /* SHA-256 implementation in JavaScript | (c) Chris Veness 2002-2010 | www.movable-type.co.uk */ - /* - see http://csrc.nist.gov/groups/ST/toolkit/secure_sha256.html */ - /* http://csrc.nist.gov/groups/ST/toolkit/examples.html */ - - // 0xffffffff is an unsigned int, a constant which is not present in javascrript - - function utilities() {} - utilities.prototype = { - // constants [§4.2.2] - /** @const {number[]} K */ - 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] - /** @const {number[]} H */ - H: [ - 0x6a09e667, - 0xbb67ae85, - 0x3c6ef372, - 0xa54ff53a, - 0x510e527f, - 0x9b05688c, - 0x1f83d9ab, - 0x5be0cd19, - ], - - /** @param {number} n */ - _toHexString: function (n) { - var s = '', - v; - for (var i = 7; i >= 0; i--) { - v = (n >>> (i * 4)) & 0xf; - s += v.toString(16); - } - return s; - }, - /** - * @param {number} n - * @param {number} x - */ - _ROTR: function (n, x) { - return (x >>> n) | (x << (32 - n)); - }, - - /** @param {number} x */ - _Sigma0: function (x) { - return this._ROTR(2, x) ^ this._ROTR(13, x) ^ this._ROTR(22, x); - }, - /** @param {number} x */ - _Sigma1: function (x) { - return this._ROTR(6, x) ^ this._ROTR(11, x) ^ this._ROTR(25, x); - }, - /** @param {number} x */ - _sigma0: function (x) { - return this._ROTR(7, x) ^ this._ROTR(18, x) ^ (x >>> 3); - }, - /** @param {number} x */ - _sigma1: function (x) { - return this._ROTR(17, x) ^ this._ROTR(19, x) ^ (x >>> 10); - }, - - /** - * @param {number} x - * @param {number} y - * @param {number} z - */ - _Ch: function (x, y, z) { - return (x & y) ^ (~x & z); - }, - - /** - * @param {number} x - * @param {number} y - * @param {number} z - */ - _Maj: function (x, y, z) { - return (x & y) ^ (x & z) ^ (y & z); - }, - }; - /** - * Contains all the hashing functions for sha256 algorithm - * @namespace sha256 - */ - function sha256() { - const u = new utilities(); - /** - * @type { (msg: string) => import('../types').EncodedMessage} - * @memberof sha256 - */ - this._encodeMessage = function (msg) { - msg += String.fromCharCode(0x80); // add trailing '1' bit (+ 0's padding) to string [§5.1.1] - - // 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 - /** @type {number[][]} M - An Array of number arrays */ - 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; - - return { - M, - N, - }; - }; - - /** - * @type { (encodedMessage: import('../types').EncodedMessage, H:number[], K:number[]) => string[]} - * @memberof sha256 - * @this utilities - */ - this._computeHash = function ({ M, N }, H, K) { - 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] = - (u._sigma1(W[t - 2]) + - W[t - 7] + - u._sigma0(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 + u._Sigma1(e) + u._Ch(e, f, g) + K[t] + W[t]; - var T2 = u._Sigma0(a) + u._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; - } - const hashMap = H.map(hash => u._toHexString(hash)); - return hashMap; - }; - - /** - * @type {(msg: string) => string} - * @memberof sha256 - */ - this.hash = function (msg) { - const encodedMessage = this._encodeMessage(msg); - const intermediateHash = this._computeHash( - encodedMessage, - u.H, - u.K - ); - const hashedString = intermediateHash.join(''); - return hashedString; - }; - return this; - } - - sha256.prototype = Object.assign({}, utilities.prototype); - - /** @type {sha256} */ - var sha = new sha256(); - /** @type { (percentFor: number, percentOf: number) => number } */ var getWholePercent = (percentFor, percentOf) => { return Math.floor((percentFor / percentOf) * 100); @@ -294,6 +30,7 @@ export default function () { nonce: nonce, }; + // @ts-ignore var currentHash = sha.hash(JSON.stringify(verifyArray)); while (currentHash.substr(0, 4) !== '0000' || !isPrime(nonce)) { @@ -303,6 +40,7 @@ export default function () { time: time, nonce: nonce, }; + // @ts-ignore var currentHash = sha.hash(JSON.stringify(verifyArray)); } From d3c088d3eee27f937365853001c25bbd75d6f76e Mon Sep 17 00:00:00 2001 From: frankie-tech <26461046+frankie-tech@users.noreply.github.com> Date: Wed, 21 Oct 2020 14:12:51 -0400 Subject: [PATCH 4/6] separated hashing functions to own file --- src/includes/sha256.js | 265 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 src/includes/sha256.js diff --git a/src/includes/sha256.js b/src/includes/sha256.js new file mode 100644 index 0000000..effeb51 --- /dev/null +++ b/src/includes/sha256.js @@ -0,0 +1,265 @@ +/* SHA-256 implementation in JavaScript | (c) Chris Veness 2002-2010 | www.movable-type.co.uk */ +/* - see http://csrc.nist.gov/groups/ST/toolkit/secure_sha256.html */ +/* http://csrc.nist.gov/groups/ST/toolkit/examples.html */ + +// 0xffffffff is an unsigned int, a constant which is not present in javascrript + +function Utilities() {} +Utilities.prototype = { + // constants [§4.2.2] + /** @const {number[]} K */ + 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] + /** @const {number[]} H */ + H: [ + 0x6a09e667, + 0xbb67ae85, + 0x3c6ef372, + 0xa54ff53a, + 0x510e527f, + 0x9b05688c, + 0x1f83d9ab, + 0x5be0cd19, + ], + + /** @param {number} n */ + _toHexString: function (n) { + var s = '', + v; + for (var i = 7; i >= 0; i--) { + v = (n >>> (i * 4)) & 0xf; + s += v.toString(16); + } + return s; + }, + /** + * @param {number} n + * @param {number} x + */ + _ROTR: function (n, x) { + return (x >>> n) | (x << (32 - n)); + }, + + /** @param {number} x */ + _Sigma0: function (x) { + return this._ROTR(2, x) ^ this._ROTR(13, x) ^ this._ROTR(22, x); + }, + /** @param {number} x */ + _Sigma1: function (x) { + return this._ROTR(6, x) ^ this._ROTR(11, x) ^ this._ROTR(25, x); + }, + /** @param {number} x */ + _sigma0: function (x) { + return this._ROTR(7, x) ^ this._ROTR(18, x) ^ (x >>> 3); + }, + /** @param {number} x */ + _sigma1: function (x) { + return this._ROTR(17, x) ^ this._ROTR(19, x) ^ (x >>> 10); + }, + + /** + * @param {number} x + * @param {number} y + * @param {number} z + */ + _Ch: function (x, y, z) { + return (x & y) ^ (~x & z); + }, + + /** + * @param {number} x + * @param {number} y + * @param {number} z + */ + _Maj: function (x, y, z) { + return (x & y) ^ (x & z) ^ (y & z); + }, +}; +/** + * Contains all the hashing functions for sha256 algorithm + * @namespace sha256 + */ +function sha256() { + this.utilities = new Utilities(); + /** + * @type { (msg: string) => import('../types').EncodedMessage} + * @memberof sha256 + */ + this._encodeMessage = function (msg) { + msg += String.fromCharCode(0x80); // add trailing '1' bit (+ 0's padding) to string [§5.1.1] + + // 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 + /** @type {number[][]} M - An Array of number arrays */ + 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; + + return { + M, + N, + }; + }; + + /** + * @type { (encodedMessage: import('../types').EncodedMessage, H:number[], K:number[]) => string[]} + * @memberof sha256 + */ + this._computeHash = function ({ M, N }, H, K) { + 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] = + (this.utilities._sigma1(W[t - 2]) + + W[t - 7] + + this.utilities._sigma0(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 + + this.utilities._Sigma1(e) + + this.utilities._Ch(e, f, g) + + K[t] + + W[t]; + var T2 = + this.utilities._Sigma0(a) + this.utilities._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; + } + const hashMap = H.map(hash => this.utilities._toHexString(hash)); + return hashMap; + }; + + /** + * @type {(msg: string) => string} + * @memberof sha256 + */ + this.hash = function (msg) { + const encodedMessage = this._encodeMessage(msg); + const intermediateHash = this._computeHash( + encodedMessage, + this.utilities.H, + this.utilities.K + ); + const hashedString = intermediateHash.join(''); + return hashedString; + }; + return this; +} + +export { sha256, Utilities }; From 0753399b59011ca0bbef17244c5b82c0af21bb21 Mon Sep 17 00:00:00 2001 From: frankie-tech <26461046+frankie-tech@users.noreply.github.com> Date: Wed, 21 Oct 2020 14:14:00 -0400 Subject: [PATCH 5/6] fixes worker import to work with changes to hashing file Since the prototype method was used, I had to transfer all the prototype methods over as well. This was a little funky and quite honestly... I don't like it But it works --- src/index.js | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/index.js b/src/index.js index c83a24d..e4f785b 100644 --- a/src/index.js +++ b/src/index.js @@ -6,6 +6,7 @@ import emitter from './includes/emit'; import worker from './includes/worker'; +import { sha256, Utilities } from './includes/sha256'; import getSettings from './includes/get-settings'; (function (w) { @@ -54,13 +55,31 @@ import getSettings from './includes/get-settings'; /** @type { ( obj:import('./types').eventInterface ) => object } */ const merge = obj => Object.assign(eventDefault, obj); - /** @param {Function} fn */ - function createWorker(fn) { + function createWorker() { try { // generates a worker by converting into a string and then running that function as a worker - const blob = new Blob(['(' + fn.toString() + ')();'], { - type: 'application/javascript', - }); + const blob = new Blob( + [ + ` + ${Utilities}; + Utilities.prototype = ${JSON.stringify(Utilities.prototype)}; + Utilities.prototype._toHexString = ${Utilities.prototype._toHexString}; + Utilities.prototype._ROTR = ${Utilities.prototype._ROTR}; + Utilities.prototype._Sigma0 = ${Utilities.prototype._Sigma0}; + Utilities.prototype._Sigma1 = ${Utilities.prototype._Sigma1}; + Utilities.prototype._sigma0 = ${Utilities.prototype._sigma0}; + Utilities.prototype._sigma1 = ${Utilities.prototype._sigma1}; + Utilities.prototype._Ch = ${Utilities.prototype._Ch}; + Utilities.prototype._Maj = ${Utilities.prototype._Maj}; + ${sha256}; + sha256.prototype = Object.assign({}, Utilities.prototype); + const sha = new sha256(); + (${worker})()`.trim(), + ], + { + type: 'application/javascript', + } + ); const blobUrl = URL.createObjectURL(blob); return new Worker(blobUrl); } catch (e) { @@ -69,7 +88,7 @@ import getSettings from './includes/get-settings'; } function verify() { - form.__worker = createWorker(worker); + form.__worker = createWorker(); form.__worker.addEventListener('message', workerHandler); form.__worker.postMessage({ From 5f864ca168f15ff106b548f20e31a1a7b3aeaf96 Mon Sep 17 00:00:00 2001 From: frankie-tech <26461046+frankie-tech@users.noreply.github.com> Date: Wed, 21 Oct 2020 14:17:40 -0400 Subject: [PATCH 6/6] adds comments, removes worker on complete --- src/index.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/index.js b/src/index.js index e4f785b..c68d47b 100644 --- a/src/index.js +++ b/src/index.js @@ -15,15 +15,6 @@ import getSettings from './includes/get-settings'; /** @type {NodeListOf} */ const forms = document.querySelectorAll('[data-whc]'); - /** - * A weird bug in firefox leads to web workers with no "Active reference" to be garbage collected - * So we create a global array to push workers into so that they don't get collected - * once the workers complete their job, they are splice from the array - * and terminated - */ - // @ts-ignore - w.whcWorkers = []; - /** * @param {HTMLFormElement} form * @param {number} i @@ -57,7 +48,7 @@ import getSettings from './includes/get-settings'; function createWorker() { try { - // generates a worker by converting into a string and then running that function as a worker + // converts hashing prototypes into a string version and then exports it to a web worker const blob = new Blob( [ ` @@ -112,6 +103,7 @@ import getSettings from './includes/get-settings'; button.setAttribute('value', '' + finished); // @ts-ignore form.__worker.terminate(); + delete form.__worker; } /**