184 lines
3.1 KiB
JavaScript
184 lines
3.1 KiB
JavaScript
/*!
|
|
* siphash.js - siphash for bcoin
|
|
* Copyright (c) 2016, Christopher Jeffrey (MIT License).
|
|
* https://github.com/bcoin-org/bcoin
|
|
*
|
|
* Ported from:
|
|
* https://github.com/bitcoin/bitcoin/blob/master/src/hash.cpp
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* Javascript siphash implementation. Used for compact block relay.
|
|
* @param {Buffer} data - Blocks are uint64le's.
|
|
* @param {Buffer} k0 - Must be encoded as a uint64le.
|
|
* @param {Buffer} k1 - Must be encoded as a uint64le.
|
|
* @returns {Buffer} uint64le
|
|
*/
|
|
|
|
function siphash(data, k0, k1) {
|
|
var out = new Buffer(8);
|
|
var blocks = Math.ceil(data.length / 8);
|
|
var c0 = { hi: 0x736f6d65, lo: 0x70736575 };
|
|
var c1 = { hi: 0x646f7261, lo: 0x6e646f6d };
|
|
var c2 = { hi: 0x6c796765, lo: 0x6e657261 };
|
|
var c3 = { hi: 0x74656462, lo: 0x79746573 };
|
|
var f0 = { hi: blocks << 27, lo: 0 };
|
|
var f1 = { hi: 0, lo: 0xff };
|
|
var i, d, v0, v1, v2, v3;
|
|
|
|
k0 = read(k0, 0);
|
|
k1 = read(k1, 0);
|
|
|
|
// Init
|
|
v0 = xor64(c0, k0);
|
|
v1 = xor64(c1, k1);
|
|
v2 = xor64(c2, k0);
|
|
v3 = xor64(c3, k1);
|
|
|
|
// Blocks
|
|
for (i = 0; i < blocks; i++) {
|
|
d = read(data, i * 8);
|
|
xor64(v3, d);
|
|
sipround(v0, v1, v2, v3);
|
|
sipround(v0, v1, v2, v3);
|
|
xor64(v0, d);
|
|
}
|
|
|
|
// Finalization
|
|
xor64(v3, f0);
|
|
sipround(v0, v1, v2, v3);
|
|
sipround(v0, v1, v2, v3);
|
|
xor64(v0, f0);
|
|
xor64(v2, f1);
|
|
sipround(v0, v1, v2, v3);
|
|
sipround(v0, v1, v2, v3);
|
|
sipround(v0, v1, v2, v3);
|
|
sipround(v0, v1, v2, v3);
|
|
xor64(v0, v1);
|
|
xor64(v0, v2);
|
|
xor64(v0, v3);
|
|
|
|
write(out, v0, 0);
|
|
|
|
return out;
|
|
}
|
|
|
|
function sipround(v0, v1, v2, v3) {
|
|
sum64(v0, v1);
|
|
rotl64(v1, 13);
|
|
xor64(v1, v0);
|
|
|
|
rotl64(v0, 32);
|
|
|
|
sum64(v2, v3);
|
|
rotl64(v3, 16);
|
|
xor64(v3, v2);
|
|
|
|
sum64(v0, v3);
|
|
rotl64(v3, 21);
|
|
xor64(v3, v0);
|
|
|
|
sum64(v2, v1);
|
|
rotl64(v1, 17);
|
|
xor64(v1, v2);
|
|
|
|
rotl64(v2, 32);
|
|
}
|
|
|
|
/*
|
|
* Helpers
|
|
*/
|
|
|
|
function sum64(a, b) {
|
|
var r, carry;
|
|
|
|
r = a.lo + b.lo;
|
|
carry = (r - (r % 0x100000000)) / 0x100000000;
|
|
a.hi = (a.hi + b.hi + carry) & 0xffffffff;
|
|
a.lo = r & 0xffffffff;
|
|
|
|
if (a.hi < 0)
|
|
a.hi += 0x100000000;
|
|
|
|
if (a.lo < 0)
|
|
a.lo += 0x100000000;
|
|
|
|
return a;
|
|
}
|
|
|
|
function rotl64(x, b) {
|
|
var h1, l1, h2, l2, c;
|
|
|
|
// v1 = x << b
|
|
if (b < 32) {
|
|
h1 = x.hi << b;
|
|
c = x.lo >>> (32 - b);
|
|
l1 = x.lo << b;
|
|
h1 |= c;
|
|
} else {
|
|
h1 = x.lo << (b - 32);
|
|
l1 = 0;
|
|
}
|
|
|
|
// v2 = x >> (64 - b)
|
|
b = 64 - b;
|
|
if (b < 32) {
|
|
h2 = x.hi >>> b;
|
|
c = x.hi & (0xffffffff >>> (32 - b));
|
|
l2 = x.lo >>> b;
|
|
l2 |= c << (32 - b);
|
|
} else {
|
|
h2 = 0;
|
|
l2 = x.hi >>> (b - 32);
|
|
}
|
|
|
|
// v1 | v2
|
|
x.hi = h1 | h2;
|
|
x.lo = l1 | l2;
|
|
|
|
if (x.hi < 0)
|
|
x.hi += 0x100000000;
|
|
|
|
if (x.lo < 0)
|
|
x.lo += 0x100000000;
|
|
|
|
return x;
|
|
}
|
|
|
|
function xor64(a, b) {
|
|
a.hi ^= b.hi;
|
|
a.lo ^= b.lo;
|
|
|
|
if (a.hi < 0)
|
|
a.hi += 0x100000000;
|
|
|
|
if (a.lo < 0)
|
|
a.lo += 0x100000000;
|
|
|
|
return a;
|
|
}
|
|
|
|
function read(data, off) {
|
|
return {
|
|
hi: data.readUInt32LE(off + 4, true),
|
|
lo: data.readUInt32LE(off, true)
|
|
};
|
|
}
|
|
|
|
function write(data, value, off) {
|
|
data.writeUInt32LE(value.hi, off + 4, true);
|
|
data.writeUInt32LE(value.lo, off, true);
|
|
}
|
|
|
|
/*
|
|
* Expose
|
|
*/
|
|
|
|
exports = siphash;
|
|
exports.write = write;
|
|
exports.read = read;
|
|
|
|
module.exports = exports;
|