add siphash.

This commit is contained in:
Christopher Jeffrey 2016-06-07 17:55:28 -07:00
parent 5357fd8567
commit a5fc00d4a6
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
2 changed files with 224 additions and 0 deletions

184
lib/bcoin/siphash.js Normal file
View File

@ -0,0 +1,184 @@
/*!
* siphash.js - siphash for bcoin
* Copyright (c) 2016, Christopher Jeffrey (MIT License).
* https://github.com/indutny/bcoin
*
* Ported from:
* https://github.com/bitcoin/bitcoin/blob/master/src/hash.cpp
*/
/**
* 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 hi, lo, 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
hi = h1 | h2;
lo = l1 | l2;
x.hi = hi;
x.lo = lo;
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;

40
test/siphash-test.js Normal file
View File

@ -0,0 +1,40 @@
var assert = require('assert');
var siphash = require('../lib/bcoin/siphash');
describe('SipHash', function() {
it('should perform siphash with no data', function() {
var k0 = new Buffer(8);
var k1 = new Buffer(8);
siphash.write(k0, { hi: 0x07060504, lo: 0x03020100 }, 0);
siphash.write(k1, { hi: 0x0F0E0D0C, lo: 0x0B0A0908 }, 0);
// be:
// assert.equal(siphash(k0, k1, new Buffer(0)).toString('hex'), '726fdb47dd0e0e31');
// le:
assert.equal(siphash(new Buffer(0), k0, k1).toString('hex'), '310e0edd47db6f72');
});
it('should perform siphash with data', function() {
var k0 = new Buffer(8);
var k1 = new Buffer(8);
var data = new Buffer(8);
siphash.write(k0, { hi: 0x07060504, lo: 0x03020100 }, 0);
siphash.write(k1, { hi: 0x0F0E0D0C, lo: 0x0B0A0908 }, 0);
siphash.write(data, { hi: 0x07060504, lo: 0x03020100 }, 0);
// be:
// assert.equal(siphash(k0, k1, data).toString('hex'), '93f5f5799a932462');
// le:
assert.equal(siphash(data, k0, k1).toString('hex'), '6224939a79f5f593');
});
it('should perform siphash with uint256', function() {
var k0 = new Buffer(8);
var k1 = new Buffer(8);
siphash.write(k0, { hi: 0x07060504, lo: 0x03020100 }, 0);
siphash.write(k1, { hi: 0x0F0E0D0C, lo: 0x0B0A0908 }, 0);
var hash = new Buffer('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', 'hex');
// be:
// assert.equal(siphash(k0, k1, hash).toString('hex'), '7127512f72f27cce');
// le:
assert.equal(siphash(hash, k0, k1).toString('hex'), 'ce7cf2722f512771');
});
});