mirror of
https://github.com/tlsnotary/PageSigner.git
synced 2026-01-08 22:27:57 -05:00
77 lines
2.9 KiB
JavaScript
77 lines
2.9 KiB
JavaScript
/* This is free and unencumbered software released into the public domain. */
|
|
"use strict";
|
|
const CHACHA_ROUNDS = 20;
|
|
const CHACHA_KEYSIZE = 32;
|
|
const CHACHA_IVSIZE = 8;
|
|
|
|
/**
|
|
* Create a new ChaCha generator seeded from the given KEY and IV.
|
|
* Both KEY and IV must be ArrayBuffer objects of at least the
|
|
* appropriate byte lengths (CHACHA_KEYSIZE, CHACHA_IVSIZE). Bytes
|
|
* beyond the specified length are ignored.
|
|
*
|
|
* Returns a closure that takes no arguments. This closure, when
|
|
* invoked, returns a 64-byte ArrayBuffer of the next 64 bytes of
|
|
* output. Note: This buffer object is "owned" by the closure, and
|
|
* the same buffer object is always returned, each time filled with
|
|
* fresh output.
|
|
*/
|
|
function ChaCha(key, iv) {
|
|
if (key.byteLength < CHACHA_KEYSIZE)
|
|
throw new Error('key too short');
|
|
if (iv.byteLength < CHACHA_IVSIZE)
|
|
throw new Error('IV too short');
|
|
const state = new Uint32Array(16);
|
|
let view = new DataView(key);
|
|
state[ 0] = 0x61707865; // "expand 32-byte k"
|
|
state[ 1] = 0x3320646e; //
|
|
state[ 2] = 0x79622d32; //
|
|
state[ 3] = 0x6b206574; //
|
|
state[ 4] = view.getUint32( 0, true);
|
|
state[ 5] = view.getUint32( 4, true);
|
|
state[ 6] = view.getUint32( 8, true);
|
|
state[ 7] = view.getUint32(12, true);
|
|
state[ 8] = view.getUint32(16, true);
|
|
state[ 9] = view.getUint32(20, true);
|
|
state[10] = view.getUint32(24, true);
|
|
state[11] = view.getUint32(28, true);
|
|
view = new DataView(iv);
|
|
state[14] = view.getUint32( 0, true);
|
|
state[15] = view.getUint32( 4, true);
|
|
|
|
/* Generator */
|
|
const output = new Uint32Array(16);
|
|
const outview = new DataView(output.buffer);
|
|
return function() {
|
|
function quarterround(x, a, b, c, d) {
|
|
function rotate(v, n) { return (v << n) | (v >>> (32 - n)); }
|
|
x[a] += x[b]; x[d] = rotate(x[d] ^ x[a], 16);
|
|
x[c] += x[d]; x[b] = rotate(x[b] ^ x[c], 12);
|
|
x[a] += x[b]; x[d] = rotate(x[d] ^ x[a], 8);
|
|
x[c] += x[d]; x[b] = rotate(x[b] ^ x[c], 7);
|
|
}
|
|
output.set(state);
|
|
for (let i = 0; i < CHACHA_ROUNDS; i += 2) {
|
|
quarterround(output, 0, 4, 8, 12);
|
|
quarterround(output, 1, 5, 9, 13);
|
|
quarterround(output, 2, 6, 10, 14);
|
|
quarterround(output, 3, 7, 11, 15);
|
|
quarterround(output, 0, 5, 10, 15);
|
|
quarterround(output, 1, 6, 11, 12);
|
|
quarterround(output, 2, 7, 8, 13);
|
|
quarterround(output, 3, 4, 9, 14);
|
|
}
|
|
for (let i = 0; i < 16; i++)
|
|
outview.setUint32(i * 4, output[i] + state[i], true);
|
|
state[12]++;
|
|
if (state[12] == 0) {
|
|
state[13]++;
|
|
if (state[13] == 0) {
|
|
/* One zebibyte of output reached! */
|
|
throw new Error('output exhausted');
|
|
}
|
|
}
|
|
return output.buffer;
|
|
}
|
|
}
|