In binb_sha256, extract processBlock_sha256 into a new function. Use this in new class Sha256 where you can call multiple update(data) then finalize(). Added test to test-sh256.html.
diff --git a/js/securityLib/sha256.js b/js/securityLib/sha256.js
index 00ea8af..214ee4d 100644
--- a/js/securityLib/sha256.js
+++ b/js/securityLib/sha256.js
@@ -252,8 +252,10 @@
{
//console.log('Raw string comming is '+input);
var output = Array(input.length >> 2);
+ /* JavaScript automatically zeroizes a new array.
for(var i = 0; i < output.length; i++)
output[i] = 0;
+ */
for(var i = 0; i < input.length * 8; i += 8)
output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
return output;
@@ -268,8 +270,10 @@
function byteArray2binb(input){
//console.log("Byte array coming is " + input);
var output = Array(input.length >> 2);
+ /* JavaScript automatically zeroizes a new array.
for(var i = 0; i < output.length; i++)
output[i] = 0;
+ */
for(var i = 0; i < input.length * 8; i += 8)
output[i>>5] |= (input[i / 8] & 0xFF) << (24 - i % 32);
return output;
@@ -322,15 +326,25 @@
var HASH = new Array(1779033703, -1150833019, 1013904242, -1521486534,
1359893119, -1694144372, 528734635, 1541459225);
var W = new Array(64);
- var a, b, c, d, e, f, g, h;
- var i, j, T1, T2;
/* append padding */
m[l >> 5] |= 0x80 << (24 - l % 32);
m[((l + 64 >> 9) << 4) + 15] = l;
+
+ for(var offset = 0; offset < m.length; offset += 16)
+ processBlock_sha256(m, offset, HASH, W);
- for(i = 0; i < m.length; i += 16)
- {
+ return HASH;
+}
+
+/*
+ * Process a block of 16 4-byte words in m starting at offset and update HASH.
+ * offset must be a multiple of 16 and less than m.length. W is a scratchpad Array(64).
+ */
+function processBlock_sha256(m, offset, HASH, W) {
+ var a, b, c, d, e, f, g, h;
+ var j, T1, T2;
+
a = HASH[0];
b = HASH[1];
c = HASH[2];
@@ -342,7 +356,7 @@
for(j = 0; j < 64; j++)
{
- if (j < 16) W[j] = m[j + i];
+ if (j < 16) W[j] = m[j + offset];
else W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),
sha256_Gamma0256(W[j - 15])), W[j - 16]);
@@ -367,8 +381,6 @@
HASH[5] = safe_add(f, HASH[5]);
HASH[6] = safe_add(g, HASH[6]);
HASH[7] = safe_add(h, HASH[7]);
- }
- return HASH;
}
function safe_add (x, y)
@@ -377,3 +389,87 @@
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
+
+/*
+ * Create a Sha256, call update(data) multiple times, then call finalize().
+ */
+var Sha256 = function Sha256() {
+ this.W = new Array(64);
+ this.hash = new Array(1779033703, -1150833019, 1013904242, -1521486534,
+ 1359893119, -1694144372, 528734635, 1541459225);
+ this.nTotalBytes = 0;
+ this.buffer = new Uint8Array(16 * 4);
+ this.nBufferBytes = 0;
+}
+
+/*
+ * Update the hash with data, which is Uint8Array.
+ */
+Sha256.prototype.update = function(data) {
+ this.nTotalBytes += data.length;
+
+ if (this.nBufferBytes > 0) {
+ // Fill up the buffer and process it first.
+ var bytesNeeded = this.buffer.length - this.nBufferBytes;
+ if (data.length < bytesNeeded) {
+ this.buffer.set(data, this.nBufferBytes);
+ this.nBufferBytes += data.length;
+ return;
+ }
+ else {
+ this.buffer.set(data.subarray(0, bytesNeeded), this.nBufferBytes);
+ processBlock_sha256(byteArray2binb(this.buffer), 0, this.hash, this.W);
+ this.nBufferBytes = 0;
+ // Consume the bytes from data.
+ data = data.subarray(bytesNeeded, data.length);
+ if (data.length == 0)
+ return;
+ }
+ }
+
+ // 2^6 is 16 * 4.
+ var nBlocks = data.length >> 6;
+ if (nBlocks > 0) {
+ var nBytes = nBlocks * 16 * 4;
+ var m = byteArray2binb(data.subarray(0, nBytes));
+ for(var offset = 0; offset < m.length; offset += 16)
+ processBlock_sha256(m, offset, this.hash, this.W);
+
+ data = data.subarray(nBytes, data.length);
+ }
+
+ if (data.length > 0) {
+ // Save the remainder in the buffer.
+ this.buffer.set(data);
+ this.nBufferBytes = data.length;
+ }
+}
+
+/*
+ * Finalize the hash and return the result as Uint8Array.
+ * Only call this once. Return values on subsequent calls are undefined.
+ */
+Sha256.prototype.finalize = function() {
+ var m = byteArray2binb(this.buffer.subarray(0, this.nBufferBytes));
+ /* append padding */
+ var l = this.nBufferBytes * 8;
+ m[l >> 5] |= 0x80 << (24 - l % 32);
+ m[((l + 64 >> 9) << 4) + 15] = this.nTotalBytes * 8;
+
+ for(var offset = 0; offset < m.length; offset += 16)
+ processBlock_sha256(m, offset, this.hash, this.W);
+
+ return Sha256.binb2Uint8Array(this.hash);
+}
+
+/*
+ * Convert an array of big-endian words to Uint8Array.
+ */
+Sha256.binb2Uint8Array = function(input)
+{
+ var output = new Uint8Array(input.length * 4);
+ var iOutput = 0;
+ for (var i = 0; i < input.length * 32; i += 8)
+ output[iOutput++] = (input[i>>5] >>> (24 - i % 32)) & 0xFF;
+ return output;
+}
diff --git a/js/testing/test-sha256.html b/js/testing/test-sha256.html
index 4020f53..5980dac 100644
--- a/js/testing/test-sha256.html
+++ b/js/testing/test-sha256.html
@@ -11,11 +11,21 @@
<script src="http://crypto-js.googlecode.com/svn/tags/3.0.2/build/rollups/sha256.js"></script>
<script type="text/javascript">
- function hash(){
+ function hash(){
var input = document.getElementById('contentname').value;
var output = "from string- " + hex_sha256(input) + "<br/>";
- output += "from bytes-- " + hex_sha256_from_bytes(DataUtils.toNumbersFromString(input)) + "<br/>";
+
+ var sha256 = new Sha256();
+ var data = DataUtils.toNumbersFromString(input);
+ // Call update multiple times in small chunks to test the buffering.
+ var chunkSize = 3;
+ var nCalls = Math.floor(data / chunkSize);
+ for (var i = 0; i < nCalls; ++i)
+ sha256.update(data.subarray(i * chunkSize, chunkSize));
+ sha256.update(data.subarray(i * nCalls, data.length));
+ output += "from bytes-- " + DataUtils.toHex(sha256.finalize()) + "<br/>";
+
output += "reference---- " + CryptoJS.SHA256(input);
document.getElementById('result').innerHTML = output;