diff --git a/lib/crypto/siphash.js b/lib/crypto/siphash.js index 807e6ced..5b5e032c 100644 --- a/lib/crypto/siphash.js +++ b/lib/crypto/siphash.js @@ -18,166 +18,177 @@ */ 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 c0 = U64(0x736f6d65, 0x70736575); + var c1 = U64(0x646f7261, 0x6e646f6d); + var c2 = U64(0x6c796765, 0x6e657261); + var c3 = U64(0x74656462, 0x79746573); + var f0 = U64(blocks << 27, 0); + var f1 = U64(0, 0xff); var i, d, v0, v1, v2, v3; - k0 = read(k0, 0); - k1 = read(k1, 0); + k0 = U64.fromRaw(k0); + k1 = U64.fromRaw(k1); // Init - v0 = xor64(c0, k0); - v1 = xor64(c1, k1); - v2 = xor64(c2, k0); - v3 = xor64(c3, k1); + v0 = c0.xor(k0); + v1 = c1.xor(k1); + v2 = c2.xor(k0); + v3 = c3.xor(k1); // Blocks for (i = 0; i < blocks; i++) { - d = read(data, i * 8); - xor64(v3, d); + d = U64.fromRaw(data, i * 8); + v3.xor(d); sipround(v0, v1, v2, v3); sipround(v0, v1, v2, v3); - xor64(v0, d); + v0.xor(d); } // Finalization - xor64(v3, f0); + v3.xor(f0); sipround(v0, v1, v2, v3); sipround(v0, v1, v2, v3); - xor64(v0, f0); - xor64(v2, f1); + v0.xor(f0); + v2.xor(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); + v0.xor(v1); + v0.xor(v2); + v0.xor(v3); - write(out, v0, 0); - - return out; + return v0.toRaw(); } function sipround(v0, v1, v2, v3) { - sum64(v0, v1); - rotl64(v1, 13); - xor64(v1, v0); + v0.add(v1); + v1.rotl(13); + v1.xor(v0); - rotl64(v0, 32); + v0.rotl(32); - sum64(v2, v3); - rotl64(v3, 16); - xor64(v3, v2); + v2.add(v3); + v3.rotl(16); + v3.xor(v2); - sum64(v0, v3); - rotl64(v3, 21); - xor64(v3, v0); + v0.add(v3); + v3.rotl(21); + v3.xor(v0); - sum64(v2, v1); - rotl64(v1, 17); - xor64(v1, v2); + v2.add(v1); + v1.rotl(17); + v1.xor(v2); - rotl64(v2, 32); + v2.rotl(32); } /* * Helpers */ -function sum64(a, b) { - var r, carry; +function U64(hi, lo) { + if (!(this instanceof U64)) + return new U64(hi, lo); - 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; + this.hi = hi || 0; + this.lo = lo || 0; } -function rotl64(x, b) { +U64.prototype.add = function add(b) { + var r, carry; + + r = this.lo + b.lo; + carry = (r - (r % 0x100000000)) / 0x100000000; + this.hi = (this.hi + b.hi + carry) & 0xffffffff; + this.lo = r & 0xffffffff; + + if (this.hi < 0) + this.hi += 0x100000000; + + if (this.lo < 0) + this.lo += 0x100000000; + + return this; +}; + +U64.prototype.xor = function xor(b) { + this.hi ^= b.hi; + this.lo ^= b.lo; + + if (this.hi < 0) + this.hi += 0x100000000; + + if (this.lo < 0) + this.lo += 0x100000000; + + return this; +}; + +U64.prototype.rotl = function rotl(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 = this.hi << b; + c = this.lo >>> (32 - b); + l1 = this.lo << b; h1 |= c; } else { - h1 = x.lo << (b - 32); + h1 = this.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; + h2 = this.hi >>> b; + c = this.hi & (0xffffffff >>> (32 - b)); + l2 = this.lo >>> b; l2 |= c << (32 - b); } else { h2 = 0; - l2 = x.hi >>> (b - 32); + l2 = this.hi >>> (b - 32); } // v1 | v2 - x.hi = h1 | h2; - x.lo = l1 | l2; + this.hi = h1 | h2; + this.lo = l1 | l2; - if (x.hi < 0) - x.hi += 0x100000000; + if (this.hi < 0) + this.hi += 0x100000000; - if (x.lo < 0) - x.lo += 0x100000000; + if (this.lo < 0) + this.lo += 0x100000000; - return x; -} + return this; +}; -function xor64(a, b) { - a.hi ^= b.hi; - a.lo ^= b.lo; +U64.prototype.toRaw = function toRaw() { + var data = new Buffer(8); + data.writeUInt32LE(this.hi, 4, true); + data.writeUInt32LE(this.lo, 0, true); + return data; +}; - if (a.hi < 0) - a.hi += 0x100000000; +U64.fromRaw = function fromRaw(data, off) { + var hi, lo; - if (a.lo < 0) - a.lo += 0x100000000; + if (!off) + off = 0; - return a; -} + hi = data.readUInt32LE(off + 4, true); + lo = data.readUInt32LE(off, true); -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); -} + return new U64(hi, lo); +}; /* * Expose */ exports = siphash; -exports.write = write; -exports.read = read; +exports.U64 = U64; module.exports = exports; diff --git a/test/siphash-test.js b/test/siphash-test.js index a2f33d98..4989f27f 100644 --- a/test/siphash-test.js +++ b/test/siphash-test.js @@ -2,41 +2,26 @@ var assert = require('assert'); var siphash = require('../lib/crypto/siphash'); +var U64 = siphash.U64; 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: + var k0 = U64(0x07060504, 0x03020100).toRaw(); + var k1 = U64(0x0f0e0d0c, 0x0b0a0908).toRaw(); 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: + var k0 = U64(0x07060504, 0x03020100).toRaw(); + var k1 = U64(0x0f0e0d0c, 0x0b0a0908).toRaw(); + var data = U64(0x07060504, 0x03020100).toRaw(); 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 k0 = U64(0x07060504, 0x03020100).toRaw(); + var k1 = U64(0x0f0e0d0c, 0x0b0a0908).toRaw(); 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'); }); });