mirror of
https://github.com/tlsnotary/PageSigner.git
synced 2026-01-08 22:27:57 -05:00
- implement garbled row reduction GRR3 for 25% bandwidth saving - implement KOS15 OT extension - linting
598 lines
19 KiB
JavaScript
598 lines
19 KiB
JavaScript
// gcworker.js is a WebWorker which performs garbling and evaluation
|
|
// of garbled circuits.
|
|
// This is a fixed-key-cipher garbling method from BHKR13 https://eprint.iacr.org/2013/426.pdf
|
|
|
|
// eslint-disable-next-line no-undef
|
|
var parentPort_;
|
|
let circuit = null;
|
|
let truthTable = null;
|
|
|
|
// fixedKey is used by randomOracle(). We need a 32-byte key because we use Salsa20. The last 4
|
|
// bytes will be filled with the index of the circuit's wire.
|
|
const fixedKey = new Uint8Array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
|
|
25,26,27,28,0,0,0,0]);
|
|
// sigma is Salsa's constant "expand 32-byte k"
|
|
const sigma = new Uint8Array([101, 120, 112, 97, 110, 100, 32, 51, 50, 45, 98, 121, 116, 101, 32, 107]);
|
|
// randomPool will be filled with data from getRandom
|
|
let randomPool;
|
|
// randomPoolOffset will be moved after data was read from randomPool
|
|
let randomPoolOffset = 0;
|
|
let garbledAssigment;
|
|
var crypto_;
|
|
|
|
if (typeof(importScripts) !== 'undefined') {
|
|
crypto_ = self.crypto;
|
|
self.onmessage = function(event) {
|
|
processMessage(event.data);
|
|
};
|
|
} else {
|
|
// we are in nodejs
|
|
import('module').then((module) => {
|
|
// we cannot use the "import" keyword here because on first pass the browser unconditionaly
|
|
// parses this if clause and will error out if "import" is found
|
|
// using process.argv instead of import.meta.url to get the name of this script
|
|
const filePath = 'file://' + process.argv[1];
|
|
// this workaround allows to require() from ES6 modules, which is not allowed by default
|
|
const require = module.createRequire(filePath)
|
|
const { parentPort } = require('worker_threads');
|
|
parentPort_ = parentPort
|
|
const { Crypto } = require("@peculiar/webcrypto");
|
|
crypto_ = new Crypto();
|
|
const perf = {'now':function(){return 0;}}
|
|
global.performance = perf;
|
|
parentPort.on('message', msg => {
|
|
processMessage(msg);
|
|
})
|
|
})
|
|
}
|
|
|
|
function processMessage(obj){
|
|
const msg = obj.msg;
|
|
const data = obj.data;
|
|
if (msg === 'parse'){
|
|
circuit = data;
|
|
garbledAssigment = new Uint8Array(32*(circuit.wiresCount));
|
|
// no need to respond to this message
|
|
}
|
|
else if (msg === 'setTruthTable'){
|
|
assert(data.byteLength == circuit.andGateCount*48);
|
|
truthTable = new Uint8Array(data);
|
|
}
|
|
else if (msg === 'garble'){
|
|
if (circuit == null){
|
|
console.log('error: need to parse circuit before garble');
|
|
return;
|
|
}
|
|
console.time('garbling done in');
|
|
const reuseLabels = (data == undefined) ? undefined : data.reuseLabels;
|
|
const reuseIndexes = (data == undefined) ? undefined : data.reuseIndexes;
|
|
const reuseR = (data == undefined) ? undefined : data.reuseR;
|
|
|
|
const [truthTable, inputLabels, outputLabels, R] = garble(circuit, garbledAssigment, reuseLabels, reuseIndexes, reuseR);
|
|
assert (truthTable.length === circuit.andGateCount*48);
|
|
assert (inputLabels.length === circuit.clientInputSize*32 + circuit.notaryInputSize*32);
|
|
assert (outputLabels.length === circuit.outputSize*32);
|
|
const obj = {'tt': truthTable.buffer, 'il': inputLabels.buffer, 'ol': outputLabels.buffer, 'R': R};
|
|
console.timeEnd('garbling done in');
|
|
postMsg(obj, [truthTable.buffer, inputLabels.buffer, outputLabels.buffer]);
|
|
}
|
|
else if (msg === 'evaluate'){
|
|
if (circuit == null || truthTable == null){
|
|
console.log('error: need to parse circuit and set truth table before evaluate');
|
|
return;
|
|
}
|
|
const garbledAssigment = new Uint8Array(16*(circuit.wiresCount));
|
|
const inputLabels = new Uint8Array(data);
|
|
assert (inputLabels.length === circuit.clientInputSize*16 + circuit.notaryInputSize*16);
|
|
const outputLabels = evaluate(circuit, garbledAssigment, truthTable, inputLabels);
|
|
assert (outputLabels.length === circuit.outputSize*16);
|
|
postMsg(outputLabels.buffer);
|
|
}
|
|
else {
|
|
console.log('Error: unexpected message in worker');
|
|
}
|
|
}
|
|
|
|
|
|
function postMsg(value, transferList){
|
|
if (typeof importScripts !== 'function'){
|
|
parentPort_.postMessage({data:value}, transferList)
|
|
} else {
|
|
postMessage(value, transferList);
|
|
}
|
|
}
|
|
|
|
|
|
function newR(){
|
|
const R = getRandom(16);
|
|
// set the last bit of R to 1 for point-and-permute
|
|
// this guarantees that 2 labels of the same wire will have the opposite last bits
|
|
R[15] = R[15] | 0x01;
|
|
return R;
|
|
}
|
|
|
|
function generateInputLabels(count, R){
|
|
const newLabels = new Uint8Array(count*32);
|
|
for (let i=0; i < count; i++){
|
|
const label1 = getRandom(16);
|
|
const label2 = xor(label1, R);
|
|
newLabels.set(label1, i*32);
|
|
newLabels.set(label2, i*32+16);
|
|
}
|
|
return newLabels;
|
|
}
|
|
|
|
function garble(circuit, ga, reuseLabels = new Uint8Array(0) , reuseIndexes = [], R){
|
|
|
|
const inputCount = circuit.notaryInputSize + circuit.clientInputSize;
|
|
fillRandom((inputCount+1+circuit.andGateCount)*16);
|
|
R = R || newR();
|
|
|
|
// generate new labels
|
|
const newLabels = generateInputLabels(inputCount - reuseIndexes.length, R);
|
|
|
|
// set both new and reused labels into ga
|
|
let reusedCount = 0; // how many reused inputs were already put into ga
|
|
let newInputsCount = 0; // how many new inputs were already put into ga
|
|
|
|
for (let i = 0; i < inputCount; i++) {
|
|
if (reuseIndexes.includes(i)) {
|
|
ga.set(reuseLabels.subarray(reusedCount*32, reusedCount*32+32), i*32);
|
|
reusedCount += 1;
|
|
}
|
|
else {
|
|
ga.set(newLabels.subarray(newInputsCount*32, newInputsCount*32+32), i*32);
|
|
newInputsCount += 1;
|
|
}
|
|
}
|
|
|
|
const truthTable = new Uint8Array(circuit.andGateCount*48);
|
|
|
|
let andGateIdx = 0;
|
|
// garble gates
|
|
for (let i = 0; i < circuit.gatesCount; i++) {
|
|
const gateBlob = circuit.gatesBlob.subarray(i*10, i*10+10);
|
|
const op = ['XOR', 'AND', 'INV'][gateBlob[0]];
|
|
if (op === 'AND') {
|
|
garbleAnd(gateBlob, R, ga, truthTable, andGateIdx, i);
|
|
andGateIdx += 1;
|
|
} else if (op === 'XOR') {
|
|
garbleXor(gateBlob, R, ga);
|
|
} else if (op === 'NOT' || op === 'INV') {
|
|
garbleNot(gateBlob, ga);
|
|
} else {
|
|
throw new Error('Unrecognized gate: ' + op);
|
|
}
|
|
}
|
|
|
|
return [truthTable, ga.slice(0, inputCount*32), ga.slice(-circuit.outputSize*32), R];
|
|
|
|
}
|
|
|
|
|
|
const garbleAnd = function (gateBlob, R, ga, tt, andGateIdx, id) {
|
|
// get wire numbers
|
|
const in1 = threeBytesToInt(gateBlob.subarray(1,4));
|
|
const in2 = threeBytesToInt(gateBlob.subarray(4,7));
|
|
const out = threeBytesToInt(gateBlob.subarray(7,10));
|
|
|
|
// get labels of each wire
|
|
const in1_0 = gaGetIndexG(ga, in1, 0);
|
|
const in1_1 = gaGetIndexG(ga, in1, 1);
|
|
const in2_0 = gaGetIndexG(ga, in2, 0);
|
|
const in2_1 = gaGetIndexG(ga, in2, 1);
|
|
|
|
// rows is a truthtable if wire labels in a canonical order, the third
|
|
// item shows an index of output label
|
|
const rows = [
|
|
[in1_0, in2_0, 0],
|
|
[in1_0, in2_1, 0],
|
|
[in1_1, in2_0, 0],
|
|
[in1_1, in2_1, 1]
|
|
]
|
|
|
|
// GRR3: garbled row reduction
|
|
// We want to reduce a row where both labels' points are set to 1.
|
|
// We first need to encrypt those labels with a dummy all-zero output label. The
|
|
// result X will be the actual value of the output label that we need to set.
|
|
// After we set the output label to X and encrypt again, the result will be 0 (but
|
|
// we don't actually need to encrypt it again, we just know that the result will be 0)
|
|
|
|
let outLabels
|
|
// idxToReduce is the index of the row that will be reduced
|
|
let idxToReduce = -1;
|
|
for (let i=0; i < rows.length; i++){
|
|
if (getPoint(rows[i][0]) == 1 && getPoint(rows[i][1]) == 1){
|
|
const outWire = encrypt(rows[i][0], rows[i][1], id, new Uint8Array(16).fill(0));
|
|
if (i==3){
|
|
outLabels = [xor(outWire, R), outWire]
|
|
} else {
|
|
outLabels = [outWire, xor(outWire, R)]
|
|
}
|
|
idxToReduce = i;
|
|
break;
|
|
}
|
|
}
|
|
gaSetIndexG(ga, out, 0, outLabels[0]);
|
|
gaSetIndexG(ga, out, 1, outLabels[1]);
|
|
assert(idxToReduce != -1)
|
|
|
|
for (let i=0; i < rows.length; i++){
|
|
if (i == idxToReduce){
|
|
// not encrypting this row because we already know that its encryption is 0
|
|
// and the sum of its points is 3
|
|
continue;
|
|
}
|
|
const value = encrypt(rows[i][0], rows[i][1], id, outLabels[rows[i][2]]);
|
|
const point = 2 * getPoint(rows[i][0]) + getPoint(rows[i][1])
|
|
tt.set(value, andGateIdx*48+16*point);
|
|
}
|
|
};
|
|
|
|
|
|
const garbleXor = function (gateBlob, R, ga) {
|
|
const in1 = threeBytesToInt(gateBlob.subarray(1,4));
|
|
const in2 = threeBytesToInt(gateBlob.subarray(4,7));
|
|
const out = threeBytesToInt(gateBlob.subarray(7,10));
|
|
|
|
const in1_0 = gaGetIndexG(ga, in1, 0);
|
|
const in1_1 = gaGetIndexG(ga, in1, 1);
|
|
const in2_0 = gaGetIndexG(ga, in2, 0);
|
|
const in2_1 = gaGetIndexG(ga, in2, 1);
|
|
|
|
gaSetIndexG(ga, out, 0, xor(in1_0, in2_0));
|
|
gaSetIndexG(ga, out, 1, xor(xor(in1_1, in2_1), R, true));
|
|
};
|
|
|
|
|
|
const garbleNot = function (gateBlob, ga) {
|
|
const in1 = threeBytesToInt(gateBlob.subarray(1,4));
|
|
const out = threeBytesToInt(gateBlob.subarray(7,10));
|
|
|
|
const in1_0 = gaGetIndexG(ga, in1, 0);
|
|
const in1_1 = gaGetIndexG(ga, in1, 1);
|
|
// careful! don't put the reference back into ga, but a copy of it
|
|
gaSetIndexG(ga, out, 0, in1_1.slice());
|
|
gaSetIndexG(ga, out, 1, in1_0.slice());
|
|
};
|
|
|
|
function evaluate (circuit, ga, tt, inputLabels) {
|
|
// set input labels
|
|
ga.set(inputLabels);
|
|
|
|
// evaluate one gate at a time
|
|
let numberOfANDGates = 0;
|
|
const t0 = performance.now();
|
|
console.time('worker_evaluate');
|
|
for (let i = 0; i < circuit.gatesCount; i++) {
|
|
const gateBlob = circuit.gatesBlob.subarray(i*10, i*10+10);
|
|
const op = ['XOR', 'AND', 'INV'][gateBlob[0]];
|
|
if (op === 'AND') {
|
|
evaluateAnd(ga, tt, numberOfANDGates, gateBlob, i);
|
|
numberOfANDGates += 1;
|
|
} else if (op === 'XOR') {
|
|
evaluateXor(ga, gateBlob);
|
|
} else if (op === 'INV' || op === 'NOT') {
|
|
evaluateNot(ga, gateBlob);
|
|
} else {
|
|
throw new Error(`Unrecognized gate: ${op}`);
|
|
}
|
|
}
|
|
console.timeEnd('worker_evaluate');
|
|
const t1 = performance.now();
|
|
|
|
return ga.slice((circuit.wiresCount-circuit.outputSize)*16, circuit.wiresCount*16);
|
|
}
|
|
|
|
const evaluateAnd = function (ga, tt, andGateIdx, gateBlob, id) {
|
|
const in1 = threeBytesToInt(gateBlob.subarray(1,4));
|
|
const in2 = threeBytesToInt(gateBlob.subarray(4,7));
|
|
const out = threeBytesToInt(gateBlob.subarray(7,10));
|
|
|
|
const label1 = gaGetIndexE(ga, in1); // ga[in1];
|
|
const label2 = gaGetIndexE(ga, in2); // ga[in2];
|
|
|
|
let cipher
|
|
const point = 2 * getPoint(label1) + getPoint(label2);
|
|
if (point == 3){
|
|
// GRR3: all rows with point sum of 3 have been reduced
|
|
// their encryption is an all-zero bytestring
|
|
cipher = new Uint8Array(16).fill(0);
|
|
} else {
|
|
const offset = andGateIdx*48+16*point;
|
|
cipher = tt.subarray(offset, offset+16);
|
|
}
|
|
gaSetIndexE(ga, out, decrypt(label1, label2, id, cipher));
|
|
};
|
|
|
|
|
|
const evaluateXor = function (ga, gateBlob) {
|
|
const in1 = threeBytesToInt(gateBlob.subarray(1,4));
|
|
const in2 = threeBytesToInt(gateBlob.subarray(4,7));
|
|
const out = threeBytesToInt(gateBlob.subarray(7,10));
|
|
|
|
const v1 = gaGetIndexE(ga, in1);
|
|
const v2 = gaGetIndexE(ga, in2);
|
|
gaSetIndexE(ga, out, xor(v1, v2));
|
|
};
|
|
|
|
const evaluateNot = function (ga, gateBlob) {
|
|
const in1 = threeBytesToInt(gateBlob.subarray(1,4));
|
|
const out = threeBytesToInt(gateBlob.subarray(7,10));
|
|
gaSetIndexE(ga, out, gaGetIndexE(ga, in1));
|
|
};
|
|
|
|
// get value at index in the garbled assignment when evaluating
|
|
function gaGetIndexE(ga, idx){
|
|
return ga.subarray(idx*16, idx*16+16);
|
|
}
|
|
|
|
// set value at index in the garbled assignment when evaluating
|
|
function gaSetIndexE(ga, idx, value){
|
|
ga.set(value, idx*16);
|
|
}
|
|
|
|
// get value at index in the garbled assignment when garbling
|
|
// pos is index within idx (either 0 or 1)
|
|
function gaGetIndexG(ga, idx, pos){
|
|
return ga.subarray(idx*32+16*pos, idx*32+16*pos+16);
|
|
}
|
|
|
|
// set value at index and (position in index) in the garbled assignment when garbling
|
|
// values is an array of two 16-byte values
|
|
function gaSetIndexG(ga, idx, pos, value){
|
|
ga.set(value, idx*32+pos*16);
|
|
}
|
|
|
|
|
|
function xor(a, b, reuse) {
|
|
assert(a.length == b.length, 'a.length !== b.length')
|
|
let bytes;
|
|
if (reuse === true){
|
|
// in some cases the calling function will have no more use of "a"
|
|
// so we reuse it to return the value
|
|
// saving a few cycles on not allocating a new var
|
|
bytes = a;
|
|
}
|
|
else {
|
|
bytes = a.slice();
|
|
}
|
|
for (let i = 0; i < a.length; i++) {
|
|
bytes[i] = a[i] ^ b[i];
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
function getPoint(arr) {
|
|
return arr[15] & 0x01;
|
|
}
|
|
|
|
const decrypt = encrypt;
|
|
|
|
// Based on the the A4 method from Fig.1 and the D4 method in Fig6 of the BHKR13 paper
|
|
// (https://eprint.iacr.org/2013/426.pdf)
|
|
// Note that the paper doesn't prescribe a specific method to break the symmetry between A and B,
|
|
// so we choose a circular byte shift instead of a circular bitshift as in Fig6.
|
|
function encrypt(a, b, t, m) {
|
|
// double a
|
|
const a2 = a.slice();
|
|
const leastbyte = a2[0];
|
|
a2.copyWithin(0,1,15); // Logical left shift by 1 byte
|
|
a2[14] = leastbyte; // Restore old least byte as new greatest (non-pointer) byte
|
|
// quadruple b
|
|
const b4 = b.slice();
|
|
const leastbytes = [b4[0], b4[1]];
|
|
b4.copyWithin(0,2,15); // Logical left shift by 2 byte
|
|
[b4[13], b4[14]] = leastbytes; // Restore old least two bytes as new greatest bytes
|
|
|
|
const k = xor(a2, b4, true);
|
|
const ro = randomOracle(k, t);
|
|
const mXorK = xor(k, m, true);
|
|
return xor(ro, mXorK, true);
|
|
}
|
|
|
|
|
|
function randomOracle(m, t) {
|
|
// convert the integer t to a 4-byte big-endian array and append
|
|
// it to fixedKey in-place
|
|
for (let index = 0; index < 4; index++) {
|
|
const byte = t & 0xff;
|
|
fixedKey[31-index] = byte;
|
|
t = (t - byte) / 256;
|
|
}
|
|
return Salsa20(fixedKey, m);
|
|
}
|
|
|
|
|
|
function threeBytesToInt(b){
|
|
return b[2] + b[1]*256 + b[0]*65536;
|
|
}
|
|
|
|
|
|
function getRandom(count) {
|
|
const rand = randomPool.subarray(randomPoolOffset, randomPoolOffset+count);
|
|
randomPoolOffset += count;
|
|
return rand;
|
|
}
|
|
|
|
// to save time we fill the randomPool in one call and then take
|
|
// randomness from that pool. Instead of making 1000s of calls to getRandomValues()
|
|
function fillRandom(count){
|
|
// 65536 is the max that API supports
|
|
const randomChunks = [];
|
|
const chunkCount = Math.ceil(count/65536);
|
|
for (let i=0; i < chunkCount; i++){
|
|
randomChunks.push(crypto_.getRandomValues(new Uint8Array(65536)));
|
|
}
|
|
randomPool = concatTA(...randomChunks);
|
|
randomPoolOffset = 0;
|
|
}
|
|
|
|
|
|
function assert(condition, message) {
|
|
if (!condition) {
|
|
console.trace();
|
|
throw message || 'Assertion failed';
|
|
}
|
|
}
|
|
|
|
// concatenate an array of typed arrays (specifically Uint8Array)
|
|
function concatTA (...arr){
|
|
let newLen = 0;
|
|
for (const item of arr){
|
|
assert(item instanceof Uint8Array);
|
|
newLen += item.length;
|
|
}
|
|
const newArray = new Uint8Array(newLen);
|
|
let offset = 0;
|
|
for (const item of arr){
|
|
newArray.set(item, offset);
|
|
offset += item.length;
|
|
}
|
|
return newArray;
|
|
}
|
|
|
|
// use Salsa20 as a random permutator. Instead of the nonce, we feed the data that needs
|
|
// to be permuted.
|
|
function Salsa20(key, data){
|
|
const out = new Uint8Array(16);
|
|
core_salsa20(out, data, key, sigma)
|
|
return out;
|
|
}
|
|
|
|
// copied from https://github.com/dchest/tweetnacl-js/blob/master/nacl-fast.js
|
|
// and modified to output only 16 bytes
|
|
function core_salsa20(o, p, k, c) {
|
|
var j0 = c[ 0] & 0xff | (c[ 1] & 0xff)<<8 | (c[ 2] & 0xff)<<16 | (c[ 3] & 0xff)<<24,
|
|
j1 = k[ 0] & 0xff | (k[ 1] & 0xff)<<8 | (k[ 2] & 0xff)<<16 | (k[ 3] & 0xff)<<24,
|
|
j2 = k[ 4] & 0xff | (k[ 5] & 0xff)<<8 | (k[ 6] & 0xff)<<16 | (k[ 7] & 0xff)<<24,
|
|
j3 = k[ 8] & 0xff | (k[ 9] & 0xff)<<8 | (k[10] & 0xff)<<16 | (k[11] & 0xff)<<24,
|
|
j4 = k[12] & 0xff | (k[13] & 0xff)<<8 | (k[14] & 0xff)<<16 | (k[15] & 0xff)<<24,
|
|
j5 = c[ 4] & 0xff | (c[ 5] & 0xff)<<8 | (c[ 6] & 0xff)<<16 | (c[ 7] & 0xff)<<24,
|
|
j6 = p[ 0] & 0xff | (p[ 1] & 0xff)<<8 | (p[ 2] & 0xff)<<16 | (p[ 3] & 0xff)<<24,
|
|
j7 = p[ 4] & 0xff | (p[ 5] & 0xff)<<8 | (p[ 6] & 0xff)<<16 | (p[ 7] & 0xff)<<24,
|
|
j8 = p[ 8] & 0xff | (p[ 9] & 0xff)<<8 | (p[10] & 0xff)<<16 | (p[11] & 0xff)<<24,
|
|
j9 = p[12] & 0xff | (p[13] & 0xff)<<8 | (p[14] & 0xff)<<16 | (p[15] & 0xff)<<24,
|
|
j10 = c[ 8] & 0xff | (c[ 9] & 0xff)<<8 | (c[10] & 0xff)<<16 | (c[11] & 0xff)<<24,
|
|
j11 = k[16] & 0xff | (k[17] & 0xff)<<8 | (k[18] & 0xff)<<16 | (k[19] & 0xff)<<24,
|
|
j12 = k[20] & 0xff | (k[21] & 0xff)<<8 | (k[22] & 0xff)<<16 | (k[23] & 0xff)<<24,
|
|
j13 = k[24] & 0xff | (k[25] & 0xff)<<8 | (k[26] & 0xff)<<16 | (k[27] & 0xff)<<24,
|
|
j14 = k[28] & 0xff | (k[29] & 0xff)<<8 | (k[30] & 0xff)<<16 | (k[31] & 0xff)<<24,
|
|
j15 = c[12] & 0xff | (c[13] & 0xff)<<8 | (c[14] & 0xff)<<16 | (c[15] & 0xff)<<24;
|
|
|
|
var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7,
|
|
x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14,
|
|
x15 = j15, u;
|
|
|
|
for (var i = 0; i < 20; i += 2) {
|
|
u = x0 + x12 | 0;
|
|
x4 ^= u<<7 | u>>>(32-7);
|
|
u = x4 + x0 | 0;
|
|
x8 ^= u<<9 | u>>>(32-9);
|
|
u = x8 + x4 | 0;
|
|
x12 ^= u<<13 | u>>>(32-13);
|
|
u = x12 + x8 | 0;
|
|
x0 ^= u<<18 | u>>>(32-18);
|
|
|
|
u = x5 + x1 | 0;
|
|
x9 ^= u<<7 | u>>>(32-7);
|
|
u = x9 + x5 | 0;
|
|
x13 ^= u<<9 | u>>>(32-9);
|
|
u = x13 + x9 | 0;
|
|
x1 ^= u<<13 | u>>>(32-13);
|
|
u = x1 + x13 | 0;
|
|
x5 ^= u<<18 | u>>>(32-18);
|
|
|
|
u = x10 + x6 | 0;
|
|
x14 ^= u<<7 | u>>>(32-7);
|
|
u = x14 + x10 | 0;
|
|
x2 ^= u<<9 | u>>>(32-9);
|
|
u = x2 + x14 | 0;
|
|
x6 ^= u<<13 | u>>>(32-13);
|
|
u = x6 + x2 | 0;
|
|
x10 ^= u<<18 | u>>>(32-18);
|
|
|
|
u = x15 + x11 | 0;
|
|
x3 ^= u<<7 | u>>>(32-7);
|
|
u = x3 + x15 | 0;
|
|
x7 ^= u<<9 | u>>>(32-9);
|
|
u = x7 + x3 | 0;
|
|
x11 ^= u<<13 | u>>>(32-13);
|
|
u = x11 + x7 | 0;
|
|
x15 ^= u<<18 | u>>>(32-18);
|
|
|
|
u = x0 + x3 | 0;
|
|
x1 ^= u<<7 | u>>>(32-7);
|
|
u = x1 + x0 | 0;
|
|
x2 ^= u<<9 | u>>>(32-9);
|
|
u = x2 + x1 | 0;
|
|
x3 ^= u<<13 | u>>>(32-13);
|
|
u = x3 + x2 | 0;
|
|
x0 ^= u<<18 | u>>>(32-18);
|
|
|
|
u = x5 + x4 | 0;
|
|
x6 ^= u<<7 | u>>>(32-7);
|
|
u = x6 + x5 | 0;
|
|
x7 ^= u<<9 | u>>>(32-9);
|
|
u = x7 + x6 | 0;
|
|
x4 ^= u<<13 | u>>>(32-13);
|
|
u = x4 + x7 | 0;
|
|
x5 ^= u<<18 | u>>>(32-18);
|
|
|
|
u = x10 + x9 | 0;
|
|
x11 ^= u<<7 | u>>>(32-7);
|
|
u = x11 + x10 | 0;
|
|
x8 ^= u<<9 | u>>>(32-9);
|
|
u = x8 + x11 | 0;
|
|
x9 ^= u<<13 | u>>>(32-13);
|
|
u = x9 + x8 | 0;
|
|
x10 ^= u<<18 | u>>>(32-18);
|
|
|
|
u = x15 + x14 | 0;
|
|
x12 ^= u<<7 | u>>>(32-7);
|
|
u = x12 + x15 | 0;
|
|
x13 ^= u<<9 | u>>>(32-9);
|
|
u = x13 + x12 | 0;
|
|
x14 ^= u<<13 | u>>>(32-13);
|
|
u = x14 + x13 | 0;
|
|
x15 ^= u<<18 | u>>>(32-18);
|
|
}
|
|
x0 = x0 + j0 | 0;
|
|
x1 = x1 + j1 | 0;
|
|
x2 = x2 + j2 | 0;
|
|
x3 = x3 + j3 | 0;
|
|
x4 = x4 + j4 | 0;
|
|
x5 = x5 + j5 | 0;
|
|
x6 = x6 + j6 | 0;
|
|
x7 = x7 + j7 | 0;
|
|
x8 = x8 + j8 | 0;
|
|
x9 = x9 + j9 | 0;
|
|
x10 = x10 + j10 | 0;
|
|
x11 = x11 + j11 | 0;
|
|
x12 = x12 + j12 | 0;
|
|
x13 = x13 + j13 | 0;
|
|
x14 = x14 + j14 | 0;
|
|
x15 = x15 + j15 | 0;
|
|
|
|
o[ 0] = x0 >>> 0 & 0xff;
|
|
o[ 1] = x0 >>> 8 & 0xff;
|
|
o[ 2] = x0 >>> 16 & 0xff;
|
|
o[ 3] = x0 >>> 24 & 0xff;
|
|
|
|
o[ 4] = x1 >>> 0 & 0xff;
|
|
o[ 5] = x1 >>> 8 & 0xff;
|
|
o[ 6] = x1 >>> 16 & 0xff;
|
|
o[ 7] = x1 >>> 24 & 0xff;
|
|
|
|
o[ 8] = x2 >>> 0 & 0xff;
|
|
o[ 9] = x2 >>> 8 & 0xff;
|
|
o[10] = x2 >>> 16 & 0xff;
|
|
o[11] = x2 >>> 24 & 0xff;
|
|
|
|
o[12] = x3 >>> 0 & 0xff;
|
|
o[13] = x3 >>> 8 & 0xff;
|
|
o[14] = x3 >>> 16 & 0xff;
|
|
o[15] = x3 >>> 24 & 0xff;
|
|
// we only need 16 bytes of the output
|
|
}
|