diff --git a/crypto_generichash.js b/crypto_generichash.js new file mode 100644 index 0000000..36b9111 --- /dev/null +++ b/crypto_generichash.js @@ -0,0 +1,34 @@ +var blake2b = require('blake2b') + +module.exports.crypto_generichash_PRIMITIVE = 'blake2b' +module.exports.crypto_generichash_BYTES_MIN = blake2b.BYTES_MIN +module.exports.crypto_generichash_BYTES_MAX = blake2b.BYTES_MAX +module.exports.crypto_generichash_BYTES = blake2b.BYTES +module.exports.crypto_generichash_KEYBYTES_MIN = blake2b.KEYBYTES_MIN +module.exports.crypto_generichash_KEYBYTES_MAX = blake2b.KEYBYTES_MAX +module.exports.crypto_generichash_KEYBYTES = blake2b.KEYBYTES +module.exports.crypto_generichash_WASM_SUPPORTED = blake2b.WASM_SUPPORTED +module.exports.crypto_generichash_WASM_LOADED = false + +module.exports.crypto_generichash = function (output, input, key) { + blake2b(output.length, key).update(input).final(output) +} + +module.exports.crypto_generichash_ready = blake2b.ready + +module.exports.crypto_generichash_batch = function (output, inputArray, key) { + var ctx = blake2b(output.length, key) + for (var i = 0; i < inputArray.length; i++) { + ctx.update(inputArray[i]) + } + ctx.final(output) +} + +module.exports.crypto_generichash_instance = function (key, outlen) { + if (outlen == null) outlen = module.exports.crypto_generichash_BYTES + return blake2b(outlen, key) +} + +blake2b.ready(function (err) { + module.exports.crypto_generichash_WASM_LOADED = blake2b.WASM_LOADED +}) diff --git a/crypto_kdf.js b/crypto_kdf.js new file mode 100644 index 0000000..4aa945b --- /dev/null +++ b/crypto_kdf.js @@ -0,0 +1,39 @@ +var assert = require('nanoassert') +var randombytes_buf = require('.').randombytes_buf +var blake2b = require('blake2b') + +module.exports.crypto_kdf_PRIMITIVE = 'blake2b' +module.exports.crypto_kdf_BYTES_MIN = 16 +module.exports.crypto_kdf_BYTES_MAX = 64 +module.exports.crypto_kdf_CONTEXTBYTES = 8 +module.exports.crypto_kdf_KEYBYTES = 64 + +function STORE64_LE(dest, int) { + var mul = 1 + var i = 0 + dest[0] = int & 0xFF + while (++i < 8 && (mul *= 0x100)) { + dest[i] = (int / mul) & 0xFF + } +} + +module.exports.crypto_kdf_derive_from_key = function crypto_kdf_derive_from_key (subkey, subkey_id, ctx, key) { + assert(subkey.length >= module.exports.crypto_kdf_BYTES_MIN, 'subkey must be at least crypto_kdf_BYTES_MIN') + assert(subkey_id >= 0 && subkey_id <= 0x1fffffffffffff, 'subkey_id must be safe integer') + assert(ctx.length >= module.exports.crypto_kdf_CONTEXTBYTES, 'context must be at least crypto_kdf_CONTEXTBYTES') + + var ctx_padded = new Uint8Array(blake2b.PERSONALBYTES) + var salt = new Uint8Array(blake2b.SALTBYTES) + + ctx_padded.set(ctx, 0, module.exports.crypto_kdf_CONTEXTBYTES) + STORE64_LE(salt, subkey_id) + + var outlen = Math.min(subkey.length, module.exports.crypto_kdf_BYTES_MAX) + blake2b(outlen, key, salt, ctx_padded, true) + .final(subkey) +} + +module.exports.crypto_kdf_keygen = function crypto_kdf_keygen (out) { + assert(out.length >= module.exports.crypto_kdf_KEYBYTES, 'out.length must be crypto_kdf_KEYBYTES') + randombytes_buf(out.subarray(0, module.exports.crypto_kdf_KEYBYTES)) +} diff --git a/crypto_shorthash.js b/crypto_shorthash.js new file mode 100644 index 0000000..5563f89 --- /dev/null +++ b/crypto_shorthash.js @@ -0,0 +1,17 @@ +var siphash = require('siphash24') + +exports.crypto_shorthash_PRIMITIVE = 'siphash24' +exports.crypto_shorthash_BYTES = siphash.BYTES +exports.crypto_shorthash_KEYBYTES = siphash.KEYBYTES +exports.crypto_shorthash_WASM_SUPPORTED = siphash.WASM_SUPPORTED +exports.crypto_shorthash_WASM_LOADED = siphash.WASM_LOADED +exports.crypto_shorthash_ready = siphash.ready +exports.crypto_shorthash = shorthash + +siphash.ready(function () { + exports.crypto_shorthash_WASM_LOADED = siphash.WASM_LOADED +}) + +function shorthash (out, data, key, noAssert) { + siphash(data, key, out, noAssert) +} diff --git a/index.js b/index.js index 8cc07dc..c4b8ce1 100644 --- a/index.js +++ b/index.js @@ -2162,21 +2162,6 @@ function crypto_secretbox_open_easy(msg, box, n, k) { return true } -var blake2b = require('blakejs/blake2b') - -function crypto_generichash (out, data, key) { - var tmp = blake2b.blake2b(data, key, out.length) - for (var i = 0; i < tmp.length; i++) out[i] = tmp[i] -} - -function crypto_generichash_batch (out, batch, key) { - var i = 0 - var ctx = blake2b.blake2bInit(out.length, key) - for (i = 0; i < batch.length; i++) blake2b.blake2bUpdate(ctx, batch[i]) - var tmp = blake2b.blake2bFinal(ctx) - for (var i = 0; i < tmp.length; i++) out[i] = tmp[i] -} - var crypto_secretbox_KEYBYTES = 32, crypto_secretbox_NONCEBYTES = 24, crypto_secretbox_ZEROBYTES = 32, @@ -2195,6 +2180,10 @@ var crypto_secretbox_KEYBYTES = 32, crypto_sign_SEEDBYTES = 32, crypto_hash_BYTES = 64; +sodium.memzero = function (len, offset) { + for (var i = offset; i < len; i++) arr[i] = 0; +} + sodium.randombytes_buf = randombytes_buf sodium.crypto_sign_BYTES = crypto_sign_BYTES @@ -2208,6 +2197,10 @@ sodium.crypto_sign_open = crypto_sign_open sodium.crypto_sign_detached = crypto_sign_detached 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 sodium.crypto_stream = crypto_stream_wrap @@ -2224,15 +2217,6 @@ sodium.crypto_secretbox_MACBYTES = 16 sodium.crypto_secretbox_easy = crypto_secretbox_easy sodium.crypto_secretbox_open_easy = crypto_secretbox_open_easy -sodium.crypto_generichash_BYTES_MIN = 16 -sodium.crypto_generichash_BYTES_MAX = 64 -sodium.crypto_generichash_BYTES = 32 -sodium.crypto_generichash_KEYBYTES_MIN = 16 -sodium.crypto_generichash_KEYBYTES_MAX = 64 -sodium.crypto_generichash_KEYBYTES = 32 -sodium.crypto_generichash = crypto_generichash -sodium.crypto_generichash_batch = crypto_generichash_batch - function cleanup(arr) { for (var i = 0; i < arr.length; i++) arr[i] = 0; } @@ -2241,6 +2225,12 @@ function check (buf, len) { if (!buf || (len && buf.length < len)) throw new Error('Argument must be a buffer' + (len ? ' of length ' + len : '')) } +function forward (submodule) { + Object.keys(submodule).forEach(function (prop) { + module.exports[prop] = submodule[prop] + }) +} + (function() { // Initialize PRNG if environment provides CSPRNG. // If not, methods calling randombytes will throw. diff --git a/package.json b/package.json index 7265f7d..6595b81 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,27 @@ { "name": "sodium-javascript", - "version": "0.0.1", + "version": "0.3.1", "description": "WIP - a pure javascript version of sodium-native", "main": "index.js", "dependencies": { - "blakejs": "^1.0.1" + "blake2b": "^2.1.1", + "nanoassert": "^1.0.0", + "siphash24": "^1.0.1" }, "devDependencies": { "browser-run": "^4.0.2", - "browserify": "^14.1.0" + "browserify": "^14.1.0", + "sodium-test": "^0.4.0" }, "scripts": { "browser": "browserify example.js | browser-run", - "browser-manual": "browserify example.js | browser-run -p 1234" + "browser-manual": "browserify example.js | browser-run -p 1234", + "test": " node test.js" + }, + "browserify": { + "transform": [ + "brfs" + ] }, "repository": { "type": "git", diff --git a/test.js b/test.js new file mode 100644 index 0000000..f40a397 --- /dev/null +++ b/test.js @@ -0,0 +1 @@ +require('sodium-test')(require('.'))