diff --git a/crypto_stream_chacha20.js b/crypto_stream_chacha20.js index 73b9b0b..2832d3a 100644 --- a/crypto_stream_chacha20.js +++ b/crypto_stream_chacha20.js @@ -1,6 +1,5 @@ const assert = require('nanoassert') - -const constant = [1634760805, 857760878, 2036477234, 1797285236] +const Chacha20 = require('chacha20-universal') exports.crypto_stream_chacha20_KEYBYTES = 32 exports.crypto_stream_chacha20_NONCEBYTES = 8 @@ -81,139 +80,3 @@ exports.crypto_stream_chacha20_ietf_xor_instance = function (n, k) { return new Chacha20(n, k) } - -function Chacha20 (n, k, counter) { - assert(k.byteLength === exports.crypto_stream_chacha20_ietf_KEYBYTES) - assert(n.byteLength === exports.crypto_stream_chacha20_NONCEBYTES || - n.byteLength === exports.crypto_stream_chacha20_ietf_NONCEBYTES) - - if (!counter) counter = 0 - assert(counter < Number.MAX_SAFE_INTEGER) - - this.finalized = false - this.pos = 0 - this.state = new Uint32Array(16) - - for (let i = 0; i < 4; i++) this.state[i] = constant[i] - for (let i = 0; i < 8; i++) this.state[4 + i] = readUInt32LE(k, 4 * i) - - this.state[12] = counter & 0xffffffff - - if (n.byteLength === 8) { - this.state[13] = (counter && 0xffffffff00000000) >> 32 - this.state[14] = readUInt32LE(n, 0) - this.state[15] = readUInt32LE(n, 4) - } else { - this.state[13] = readUInt32LE(n, 0) - this.state[14] = readUInt32LE(n, 4) - this.state[15] = readUInt32LE(n, 8) - } - - return this -} - -Chacha20.prototype.update = function (output, input) { - assert(!this.finalized, 'cipher finalized.') - assert(output.byteLength >= input.byteLength, - 'output cannot be shorter than input.') - - let len = input.length - let offset = this.pos % 64 - this.pos += len - - // input position - let j = 0 - - let keyStream = chacha20Block(this.state) - - // try to finsih the current block - while (offset > 0 && len > 0) { - output[j] = input[j++] ^ keyStream[offset] - offset = (offset + 1) & 0x3f - if (!offset) this.state[12]++ - len-- - } - - // encrypt rest block at a time - while (len > 0) { - keyStream = chacha20Block(this.state) - - // less than a full block remaining - if (len < 64) { - for (let i = 0; i < len; i++) { - output[j] = input[j++] ^ keyStream[offset++] - offset &= 0x3f - } - - return - } - - for (; offset < 64;) { - output[j] = input[j++] ^ keyStream[offset++] - } - - this.state[12]++ - offset = 0 - len -= 64 - } -} - -Chacha20.prototype.final = function () { - this.finalized = true -} - -function chacha20Block (state) { - // working state - const ws = new Uint32Array(16) - for (let i = 16; i--;) ws[i] = state[i] - - for (let i = 0; i < 20; i += 2) { - QR(ws, 0, 4, 8, 12) // column 0 - QR(ws, 1, 5, 9, 13) // column 1 - QR(ws, 2, 6, 10, 14) // column 2 - QR(ws, 3, 7, 11, 15) // column 3 - - QR(ws, 0, 5, 10, 15) // diagonal 1 (main diagonal) - QR(ws, 1, 6, 11, 12) // diagonal 2 - QR(ws, 2, 7, 8, 13) // diagonal 3 - QR(ws, 3, 4, 9, 14) // diagonal 4 - } - - for (let i = 0; i < 16; i++) { - ws[i] += state[i] - } - - return Buffer.from(ws.buffer, ws.byteOffset, ws.byteLength) -} - -function rotl (a, b) { - return ((a << b) | (a >>> (32 - b))) -} - -function QR (obj, a, b, c, d) { - obj[a] += obj[b] - obj[d] ^= obj[a] - obj[d] = rotl(obj[d], 16) - - obj[c] += obj[d] - obj[b] ^= obj[c] - obj[b] = rotl(obj[b], 12) - - obj[a] += obj[b] - obj[d] ^= obj[a] - obj[d] = rotl(obj[d], 8) - - obj[c] += obj[d] - obj[b] ^= obj[c] - obj[b] = rotl(obj[b], 7) -} - -function readUInt32LE (buf, offset) { - if (Buffer.isBuffer(buf)) return buf.readUInt32LE(offset) - else if (buf instanceof Uint8Array) { - var ret = 0 - for (let i = 0; i < 4; i++) ret |= buf[offset + i] << (8 * i) - return ret - } - assert(false, 'buf should be a Buffer or a Uint8Array') -} diff --git a/package.json b/package.json index 2919548..87fb2f9 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "index.js", "dependencies": { "blake2b": "^2.1.1", + "chacha20-universal": "^1.0.2", "nanoassert": "^1.0.0", "siphash24": "^1.0.1", "xsalsa20": "^1.0.0"