diff --git a/crypto_shorthash.js b/crypto_shorthash.js new file mode 100644 index 0000000..6699605 --- /dev/null +++ b/crypto_shorthash.js @@ -0,0 +1,169 @@ +var fs = require('fs') +var toUint8Array = require('base64-to-uint8array') +var assert = require('nanoassert') + +var WASM = toUint8Array(fs.readFileSync(__dirname + '/wasm/siphash.wasm', 'base64')) +var mod +var mem +var rdy + +var BYTES = exports.crypto_shorthash_BYTES = 8 +var KEYBYTES = exports.crypto_shorthash_KEYBYTES = 16 +exports.crypto_shorthash_PRIMITIVE = 'siphash24' +exports.crypto_shorthash_WASM_SUPPORTED = typeof WebAssembly !== 'undefined' +exports.crypto_shorthash_WASM_LOADED = false +exports.crypto_shorthash_ready = ready +exports.crypto_shorthash = shorthash + +ready(function (err) { + if (!err) exports.crypto_shorthash_WASM_LOADED = true +}) + +function ready (cb) { + if (!cb) cb = noop + if (!exports.crypto_shorthash_WASM_SUPPORTED) return cb(new Error('WebAssembly not supported')) + if (!rdy) rdy = WebAssembly.instantiate(WASM).then(setup) + return rdy.then(cb).catch(cb) +} + +function shorthash (out, data, key, noAssert) { + if (noAssert !== true) { + assert(out.length >= BYTES, 'output must be at least crypto_shorthash_BYTES') + assert(key.length >= KEYBYTES, 'output must be at least crypto_shorthash_KEYBYTES') + } + + if (mod) { + mem.set(key, 8) + mem.set(data, 24) + mod.siphash(24, data.length) + out.set(mem.subarray(0, 8)) + } else { + fallback(out, data, key) + } +} + +function noop () {} + +function setup (w) { + mod = w.instance.exports + mem = new Uint8Array(w.instance.exports.siphash_memory.buffer) +} + +function _add(a, b) { + var rl = a.l + b.l + var a2 = { + h: a.h + b.h + (rl / 2 >>> 31) >>> 0, + l: rl >>> 0 + } + a.h = a2.h + a.l = a2.l +} + +function _xor(a, b) { + a.h ^= b.h + a.h >>>= 0 + a.l ^= b.l + a.l >>>= 0 +} + +function _rotl(a, n) { + var a2 = { + h: a.h << n | a.l >>> (32 - n), + l: a.l << n | a.h >>> (32 - n) + } + a.h = a2.h + a.l = a2.l +} + +function _rotl32(a) { + var al = a.l + a.l = a.h + a.h = al +} + +function _compress(v0, v1, v2, v3) { + _add(v0, v1) + _add(v2, v3) + _rotl(v1, 13) + _rotl(v3, 16) + _xor(v1, v0) + _xor(v3, v2) + _rotl32(v0) + _add(v2, v1) + _add(v0, v3) + _rotl(v1, 17) + _rotl(v3, 21) + _xor(v1, v2) + _xor(v3, v0) + _rotl32(v2) +} + +function _get_int(a, offset) { + return (a[offset + 3] << 24) | (a[offset + 2] << 16) | (a[offset + 1] << 8) | a[offset] +} + +function fallback (out, m, key) { // modified from https://github.com/jedisct1/siphash-js to use uint8arrays + var k0 = {h: _get_int(key, 4), l: _get_int(key, 0)} + var k1 = {h: _get_int(key, 12), l: _get_int(key, 8)} + var v0 = {h: k0.h, l: k0.l} + var v2 = k0 + var v1 = {h: k1.h, l: k1.l} + var v3 = k1 + var mi + var mp = 0 + var ml = m.length + var ml7 = ml - 7 + var buf = new Uint8Array(new ArrayBuffer(8)) + + _xor(v0, {h: 0x736f6d65, l: 0x70736575}) + _xor(v1, {h: 0x646f7261, l: 0x6e646f6d}) + _xor(v2, {h: 0x6c796765, l: 0x6e657261}) + _xor(v3, {h: 0x74656462, l: 0x79746573}) + + while (mp < ml7) { + mi = {h: _get_int(m, mp + 4), l: _get_int(m, mp)} + _xor(v3, mi) + _compress(v0, v1, v2, v3) + _compress(v0, v1, v2, v3) + _xor(v0, mi) + mp += 8 + } + + buf[7] = ml + var ic = 0 + while (mp < ml) { + buf[ic++] = m[mp++] + } + while (ic < 7) { + buf[ic++] = 0 + } + + mi = { + h: buf[7] << 24 | buf[6] << 16 | buf[5] << 8 | buf[4], + l: buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0] + } + + _xor(v3, mi) + _compress(v0, v1, v2, v3) + _compress(v0, v1, v2, v3) + _xor(v0, mi) + _xor(v2, { h: 0, l: 0xff }) + _compress(v0, v1, v2, v3) + _compress(v0, v1, v2, v3) + _compress(v0, v1, v2, v3) + _compress(v0, v1, v2, v3) + + var h = v0 + _xor(h, v1) + _xor(h, v2) + _xor(h, v3) + + out[0] = h.l & 0xff + out[1] = (h.l >> 8) & 0xff + out[2] = (h.l >> 16) & 0xff + out[3] = (h.l >> 24) & 0xff + out[4] = h.h & 0xff + out[5] = (h.h >> 8) & 0xff + out[6] = (h.h >> 16) & 0xff + out[7] = (h.h >> 24) & 0xff +} diff --git a/index.js b/index.js index 98b818f..c4b8ce1 100644 --- a/index.js +++ b/index.js @@ -2199,6 +2199,7 @@ sodium.crypto_sign_verify_detached = crypto_sign_verify_detached forward(require('./crypto_generichash')) forward(require('./crypto_kdf')) +forward(require('./crypto_shorthash')) sodium.crypto_stream_KEYBYTES = 32 sodium.crypto_stream_NONCEBYTES = 24 diff --git a/package.json b/package.json index a286be0..8525996 100644 --- a/package.json +++ b/package.json @@ -4,15 +4,22 @@ "description": "WIP - a pure javascript version of sodium-native", "main": "index.js", "dependencies": { + "base64-to-uint8array": "^1.0.0", "blake2b": "^2.1.1", + "brfs": "^1.4.3", "nanoassert": "^1.0.0" }, "devDependencies": { - "sodium-test": "^0.3.0" + "sodium-test": "^0.4.0" }, "scripts": { "test": " node test.js" }, + "browserify": { + "transform": [ + "brfs" + ] + }, "repository": { "type": "git", "url": "https://github.com/mafintosh/sodium-javascript.git" diff --git a/wasm/siphash.wasm b/wasm/siphash.wasm new file mode 100644 index 0000000..c1c0cde Binary files /dev/null and b/wasm/siphash.wasm differ diff --git a/wasm/siphash.wat b/wasm/siphash.wat new file mode 100644 index 0000000..fb9b322 --- /dev/null +++ b/wasm/siphash.wat @@ -0,0 +1,348 @@ +(module + (memory (export "siphash_memory") 10 10) + + (func (export "siphash") (param $ptr i32) (param $ptr_len i32) + (local $v0 i64) + (local $v1 i64) + (local $v2 i64) + (local $v3 i64) + (local $b i64) + (local $k0 i64) + (local $k1 i64) + (local $m i64) + (local $end i32) + (local $left i32) + + (set_local $v0 (i64.const 0x736f6d6570736575)) + (set_local $v1 (i64.const 0x646f72616e646f6d)) + (set_local $v2 (i64.const 0x6c7967656e657261)) + (set_local $v3 (i64.const 0x7465646279746573)) + + (set_local $k0 (i64.load (i32.const 8))) + (set_local $k1 (i64.load (i32.const 16))) + + ;; b = ((uint64_t) inlen) << 56; + (set_local $b (i64.shl (i64.extend_u/i32 (get_local $ptr_len)) (i64.const 56))) + + ;; left = inlen & 7; + (set_local $left (i32.and (get_local $ptr_len) (i32.const 7))) + + ;; end = in + inlen - left; + (set_local $end (i32.sub (i32.add (get_local $ptr) (get_local $ptr_len)) (get_local $left))) + + ;; v3 ^= k1; + (set_local $v3 (i64.xor (get_local $v3) (get_local $k1))) + + ;; v2 ^= k0; + (set_local $v2 (i64.xor (get_local $v2) (get_local $k0))) + + ;; v1 ^= k1; + (set_local $v1 (i64.xor (get_local $v1) (get_local $k1))) + + ;; v0 ^= k0; + (set_local $v0 (i64.xor (get_local $v0) (get_local $k0))) + + (block $end_loop + (loop $start_loop + (br_if $end_loop (i32.eq (get_local $ptr) (get_local $end))) + + ;; m = LOAD64_LE(in); + (set_local $m (i64.load (get_local $ptr))) + + ;; v3 ^= m + (set_local $v3 (i64.xor (get_local $v3) (get_local $m))) + + ;; SIPROUND + ;; v0 += v1; + (set_local $v0 (i64.add (get_local $v0) (get_local $v1))) + ;; v1 = ROTL64(v1, 13); + (set_local $v1 (i64.rotl (get_local $v1) (i64.const 13))) + ;; v1 ^= v0; + (set_local $v1 (i64.xor (get_local $v1) (get_local $v0))) + ;; v0 = ROTL64(v0, 32) + (set_local $v0 (i64.rotl (get_local $v0) (i64.const 32))) + ;; v2 += v3; + (set_local $v2 (i64.add (get_local $v2) (get_local $v3))) + ;; v3 = ROTL64(v3, 16); + (set_local $v3 (i64.rotl (get_local $v3) (i64.const 16))) + ;; v3 ^= v2; + (set_local $v3 (i64.xor (get_local $v3) (get_local $v2))) + ;; v0 += v3; + (set_local $v0 (i64.add (get_local $v0) (get_local $v3))) + ;; v3 = ROTL64(v3, 21); + (set_local $v3 (i64.rotl (get_local $v3) (i64.const 21))) + ;; v3 ^= v0; + (set_local $v3 (i64.xor (get_local $v3) (get_local $v0))) + ;; v2 += v1; + (set_local $v2 (i64.add (get_local $v2) (get_local $v1))) + ;; v1 = ROTL64(v1, 17); + (set_local $v1 (i64.rotl (get_local $v1) (i64.const 17))) + ;; v1 ^= v2; + (set_local $v1 (i64.xor (get_local $v1) (get_local $v2))) + ;; v2 = ROTL64(v2, 32); + (set_local $v2 (i64.rotl (get_local $v2) (i64.const 32))) + + ;; SIPROUND + ;; v0 += v1; + (set_local $v0 (i64.add (get_local $v0) (get_local $v1))) + ;; v1 = ROTL64(v1, 13); + (set_local $v1 (i64.rotl (get_local $v1) (i64.const 13))) + ;; v1 ^= v0; + (set_local $v1 (i64.xor (get_local $v1) (get_local $v0))) + ;; v0 = ROTL64(v0, 32) + (set_local $v0 (i64.rotl (get_local $v0) (i64.const 32))) + ;; v2 += v3; + (set_local $v2 (i64.add (get_local $v2) (get_local $v3))) + ;; v3 = ROTL64(v3, 16); + (set_local $v3 (i64.rotl (get_local $v3) (i64.const 16))) + ;; v3 ^= v2; + (set_local $v3 (i64.xor (get_local $v3) (get_local $v2))) + ;; v0 += v3; + (set_local $v0 (i64.add (get_local $v0) (get_local $v3))) + ;; v3 = ROTL64(v3, 21); + (set_local $v3 (i64.rotl (get_local $v3) (i64.const 21))) + ;; v3 ^= v0; + (set_local $v3 (i64.xor (get_local $v3) (get_local $v0))) + ;; v2 += v1; + (set_local $v2 (i64.add (get_local $v2) (get_local $v1))) + ;; v1 = ROTL64(v1, 17); + (set_local $v1 (i64.rotl (get_local $v1) (i64.const 17))) + ;; v1 ^= v2; + (set_local $v1 (i64.xor (get_local $v1) (get_local $v2))) + ;; v2 = ROTL64(v2, 32); + (set_local $v2 (i64.rotl (get_local $v2) (i64.const 32))) + + ;; v0 ^= m; + (set_local $v0 (i64.xor (get_local $v0) (get_local $m))) + + ;; ptr += 8 + (set_local $ptr (i32.add (get_local $ptr) (i32.const 8))) + (br $start_loop) + ) + ) + + (block $0 + (block $1 + (block $2 + (block $3 + (block $4 + (block $5 + (block $6 + (block $7 + (br_table $0 $1 $2 $3 $4 $5 $6 $7 (get_local $left)) + ) + ;; b |= ((uint64_t) in[6]) << 48; + (set_local $b (i64.or (get_local $b) (i64.shl (i64.load8_u (i32.add (get_local $ptr) (i32.const 6))) (i64.const 48)))) + ) + ;; b |= ((uint64_t) in[5]) << 40; + (set_local $b (i64.or (get_local $b) (i64.shl (i64.load8_u (i32.add (get_local $ptr) (i32.const 5))) (i64.const 40)))) + ) + ;; b |= ((uint64_t) in[4]) << 32; + (set_local $b (i64.or (get_local $b) (i64.shl (i64.load8_u (i32.add (get_local $ptr) (i32.const 4))) (i64.const 32)))) + ) + ;; b |= ((uint64_t) in[3]) << 24; + (set_local $b (i64.or (get_local $b) (i64.shl (i64.load8_u (i32.add (get_local $ptr) (i32.const 3))) (i64.const 24)))) + ) + ;; b |= ((uint64_t) in[2]) << 16; + (set_local $b (i64.or (get_local $b) (i64.shl (i64.load8_u (i32.add (get_local $ptr) (i32.const 2))) (i64.const 16)))) + ) + ;; b |= ((uint64_t) in[1]) << 8; + (set_local $b (i64.or (get_local $b) (i64.shl (i64.load8_u (i32.add (get_local $ptr) (i32.const 1))) (i64.const 8)))) + ) + ;; b |= ((uint64_t) in[0]); + (set_local $b (i64.or (get_local $b) (i64.load8_u (get_local $ptr)))) + ) + + ;; v3 ^= b; + (set_local $v3 (i64.xor (get_local $v3) (get_local $b))) + + ;; SIPROUND + ;; v0 += v1; + (set_local $v0 (i64.add (get_local $v0) (get_local $v1))) + ;; v1 = ROTL64(v1, 13); + (set_local $v1 (i64.rotl (get_local $v1) (i64.const 13))) + ;; v1 ^= v0; + (set_local $v1 (i64.xor (get_local $v1) (get_local $v0))) + ;; v0 = ROTL64(v0, 32) + (set_local $v0 (i64.rotl (get_local $v0) (i64.const 32))) + ;; v2 += v3; + (set_local $v2 (i64.add (get_local $v2) (get_local $v3))) + ;; v3 = ROTL64(v3, 16); + (set_local $v3 (i64.rotl (get_local $v3) (i64.const 16))) + ;; v3 ^= v2; + (set_local $v3 (i64.xor (get_local $v3) (get_local $v2))) + ;; v0 += v3; + (set_local $v0 (i64.add (get_local $v0) (get_local $v3))) + ;; v3 = ROTL64(v3, 21); + (set_local $v3 (i64.rotl (get_local $v3) (i64.const 21))) + ;; v3 ^= v0; + (set_local $v3 (i64.xor (get_local $v3) (get_local $v0))) + ;; v2 += v1; + (set_local $v2 (i64.add (get_local $v2) (get_local $v1))) + ;; v1 = ROTL64(v1, 17); + (set_local $v1 (i64.rotl (get_local $v1) (i64.const 17))) + ;; v1 ^= v2; + (set_local $v1 (i64.xor (get_local $v1) (get_local $v2))) + ;; v2 = ROTL64(v2, 32); + (set_local $v2 (i64.rotl (get_local $v2) (i64.const 32))) + + ;; SIPROUND + ;; v0 += v1; + (set_local $v0 (i64.add (get_local $v0) (get_local $v1))) + ;; v1 = ROTL64(v1, 13); + (set_local $v1 (i64.rotl (get_local $v1) (i64.const 13))) + ;; v1 ^= v0; + (set_local $v1 (i64.xor (get_local $v1) (get_local $v0))) + ;; v0 = ROTL64(v0, 32) + (set_local $v0 (i64.rotl (get_local $v0) (i64.const 32))) + ;; v2 += v3; + (set_local $v2 (i64.add (get_local $v2) (get_local $v3))) + ;; v3 = ROTL64(v3, 16); + (set_local $v3 (i64.rotl (get_local $v3) (i64.const 16))) + ;; v3 ^= v2; + (set_local $v3 (i64.xor (get_local $v3) (get_local $v2))) + ;; v0 += v3; + (set_local $v0 (i64.add (get_local $v0) (get_local $v3))) + ;; v3 = ROTL64(v3, 21); + (set_local $v3 (i64.rotl (get_local $v3) (i64.const 21))) + ;; v3 ^= v0; + (set_local $v3 (i64.xor (get_local $v3) (get_local $v0))) + ;; v2 += v1; + (set_local $v2 (i64.add (get_local $v2) (get_local $v1))) + ;; v1 = ROTL64(v1, 17); + (set_local $v1 (i64.rotl (get_local $v1) (i64.const 17))) + ;; v1 ^= v2; + (set_local $v1 (i64.xor (get_local $v1) (get_local $v2))) + ;; v2 = ROTL64(v2, 32); + (set_local $v2 (i64.rotl (get_local $v2) (i64.const 32))) + + ;; v0 ^= b; + (set_local $v0 (i64.xor (get_local $v0) (get_local $b))) + + ;; v2 ^= 0xff; + (set_local $v2 (i64.xor (get_local $v2) (i64.const 0xff))) + + ;; SIPROUND + ;; v0 += v1; + (set_local $v0 (i64.add (get_local $v0) (get_local $v1))) + ;; v1 = ROTL64(v1, 13); + (set_local $v1 (i64.rotl (get_local $v1) (i64.const 13))) + ;; v1 ^= v0; + (set_local $v1 (i64.xor (get_local $v1) (get_local $v0))) + ;; v0 = ROTL64(v0, 32) + (set_local $v0 (i64.rotl (get_local $v0) (i64.const 32))) + ;; v2 += v3; + (set_local $v2 (i64.add (get_local $v2) (get_local $v3))) + ;; v3 = ROTL64(v3, 16); + (set_local $v3 (i64.rotl (get_local $v3) (i64.const 16))) + ;; v3 ^= v2; + (set_local $v3 (i64.xor (get_local $v3) (get_local $v2))) + ;; v0 += v3; + (set_local $v0 (i64.add (get_local $v0) (get_local $v3))) + ;; v3 = ROTL64(v3, 21); + (set_local $v3 (i64.rotl (get_local $v3) (i64.const 21))) + ;; v3 ^= v0; + (set_local $v3 (i64.xor (get_local $v3) (get_local $v0))) + ;; v2 += v1; + (set_local $v2 (i64.add (get_local $v2) (get_local $v1))) + ;; v1 = ROTL64(v1, 17); + (set_local $v1 (i64.rotl (get_local $v1) (i64.const 17))) + ;; v1 ^= v2; + (set_local $v1 (i64.xor (get_local $v1) (get_local $v2))) + ;; v2 = ROTL64(v2, 32); + (set_local $v2 (i64.rotl (get_local $v2) (i64.const 32))) + + ;; SIPROUND + ;; v0 += v1; + (set_local $v0 (i64.add (get_local $v0) (get_local $v1))) + ;; v1 = ROTL64(v1, 13); + (set_local $v1 (i64.rotl (get_local $v1) (i64.const 13))) + ;; v1 ^= v0; + (set_local $v1 (i64.xor (get_local $v1) (get_local $v0))) + ;; v0 = ROTL64(v0, 32) + (set_local $v0 (i64.rotl (get_local $v0) (i64.const 32))) + ;; v2 += v3; + (set_local $v2 (i64.add (get_local $v2) (get_local $v3))) + ;; v3 = ROTL64(v3, 16); + (set_local $v3 (i64.rotl (get_local $v3) (i64.const 16))) + ;; v3 ^= v2; + (set_local $v3 (i64.xor (get_local $v3) (get_local $v2))) + ;; v0 += v3; + (set_local $v0 (i64.add (get_local $v0) (get_local $v3))) + ;; v3 = ROTL64(v3, 21); + (set_local $v3 (i64.rotl (get_local $v3) (i64.const 21))) + ;; v3 ^= v0; + (set_local $v3 (i64.xor (get_local $v3) (get_local $v0))) + ;; v2 += v1; + (set_local $v2 (i64.add (get_local $v2) (get_local $v1))) + ;; v1 = ROTL64(v1, 17); + (set_local $v1 (i64.rotl (get_local $v1) (i64.const 17))) + ;; v1 ^= v2; + (set_local $v1 (i64.xor (get_local $v1) (get_local $v2))) + ;; v2 = ROTL64(v2, 32); + (set_local $v2 (i64.rotl (get_local $v2) (i64.const 32))) + + ;; SIPROUND + ;; v0 += v1; + (set_local $v0 (i64.add (get_local $v0) (get_local $v1))) + ;; v1 = ROTL64(v1, 13); + (set_local $v1 (i64.rotl (get_local $v1) (i64.const 13))) + ;; v1 ^= v0; + (set_local $v1 (i64.xor (get_local $v1) (get_local $v0))) + ;; v0 = ROTL64(v0, 32) + (set_local $v0 (i64.rotl (get_local $v0) (i64.const 32))) + ;; v2 += v3; + (set_local $v2 (i64.add (get_local $v2) (get_local $v3))) + ;; v3 = ROTL64(v3, 16); + (set_local $v3 (i64.rotl (get_local $v3) (i64.const 16))) + ;; v3 ^= v2; + (set_local $v3 (i64.xor (get_local $v3) (get_local $v2))) + ;; v0 += v3; + (set_local $v0 (i64.add (get_local $v0) (get_local $v3))) + ;; v3 = ROTL64(v3, 21); + (set_local $v3 (i64.rotl (get_local $v3) (i64.const 21))) + ;; v3 ^= v0; + (set_local $v3 (i64.xor (get_local $v3) (get_local $v0))) + ;; v2 += v1; + (set_local $v2 (i64.add (get_local $v2) (get_local $v1))) + ;; v1 = ROTL64(v1, 17); + (set_local $v1 (i64.rotl (get_local $v1) (i64.const 17))) + ;; v1 ^= v2; + (set_local $v1 (i64.xor (get_local $v1) (get_local $v2))) + ;; v2 = ROTL64(v2, 32); + (set_local $v2 (i64.rotl (get_local $v2) (i64.const 32))) + + ;; SIPROUND + ;; v0 += v1; + (set_local $v0 (i64.add (get_local $v0) (get_local $v1))) + ;; v1 = ROTL64(v1, 13); + (set_local $v1 (i64.rotl (get_local $v1) (i64.const 13))) + ;; v1 ^= v0; + (set_local $v1 (i64.xor (get_local $v1) (get_local $v0))) + ;; v0 = ROTL64(v0, 32) + (set_local $v0 (i64.rotl (get_local $v0) (i64.const 32))) + ;; v2 += v3; + (set_local $v2 (i64.add (get_local $v2) (get_local $v3))) + ;; v3 = ROTL64(v3, 16); + (set_local $v3 (i64.rotl (get_local $v3) (i64.const 16))) + ;; v3 ^= v2; + (set_local $v3 (i64.xor (get_local $v3) (get_local $v2))) + ;; v0 += v3; + (set_local $v0 (i64.add (get_local $v0) (get_local $v3))) + ;; v3 = ROTL64(v3, 21); + (set_local $v3 (i64.rotl (get_local $v3) (i64.const 21))) + ;; v3 ^= v0; + (set_local $v3 (i64.xor (get_local $v3) (get_local $v0))) + ;; v2 += v1; + (set_local $v2 (i64.add (get_local $v2) (get_local $v1))) + ;; v1 = ROTL64(v1, 17); + (set_local $v1 (i64.rotl (get_local $v1) (i64.const 17))) + ;; v1 ^= v2; + (set_local $v1 (i64.xor (get_local $v1) (get_local $v2))) + ;; v2 = ROTL64(v2, 32); + (set_local $v2 (i64.rotl (get_local $v2) (i64.const 32))) + + ;; b = v0 ^ v1 ^ v2 ^ v3; + (i64.store (i32.const 0) (i64.xor (get_local $v0) (i64.xor (get_local $v1) (i64.xor (get_local $v2) (get_local $v3))))) + ) +)