From 61b6e6916abfc91b00e5ac3a54848e1d77acfec8 Mon Sep 17 00:00:00 2001 From: Christian Bundy Date: Tue, 22 Sep 2020 05:47:36 -0700 Subject: [PATCH] Add crypto_box_easy (#29) * Fix backward crypto_secretbox_detached bug * Add crypto_box_easy --- crypto_box.js | 102 ++++++++++++++++++++++++++++++++++++++++++-- crypto_secretbox.js | 8 ++-- crypto_stream.js | 1 + 3 files changed, 104 insertions(+), 7 deletions(-) diff --git a/crypto_box.js b/crypto_box.js index 6479e10..21c9052 100644 --- a/crypto_box.js +++ b/crypto_box.js @@ -3,7 +3,13 @@ const { crypto_hash_sha512 } = require('./crypto_hash') const { crypto_scalarmult, crypto_scalarmult_base } = require('./crypto_scalarmult') const { randombytes } = require('./randombytes') const { crypto_generichash_batch } = require('./crypto_generichash') -const { crypto_secretbox_open_easy, crypto_secretbox_easy } = require('./crypto_secretbox') +const { crypto_stream_xsalsa20_MESSAGEBYTES_MAX } = require('./crypto_stream') +const { + crypto_secretbox_open_easy, + crypto_secretbox_easy, + crypto_secretbox_detached, + crypto_secretbox_open_detached +} = require('./crypto_secretbox') const xsalsa20 = require('xsalsa20') const assert = require('nanoassert') @@ -15,8 +21,17 @@ const crypto_box_BOXZEROBYTES = 16 const crypto_box_SEALBYTES = 48 const crypto_box_SEEDBYTES = 32 const crypto_box_BEFORENMBYTES = 32 +const crypto_box_MACBYTES = 16 + +const crypto_box_curve25519xsalsa20poly1305_MACBYTES = 16 + +const crypto_box_MESSAGEBYTES_MAX = + crypto_stream_xsalsa20_MESSAGEBYTES_MAX - + crypto_box_curve25519xsalsa20poly1305_MACBYTES module.exports = { + crypto_box_easy, + crypto_box_open_easy, crypto_box_keypair, crypto_box_seed_keypair, crypto_box_seal, @@ -28,7 +43,8 @@ module.exports = { crypto_box_BOXZEROBYTES, crypto_box_SEALBYTES, crypto_box_SEEDBYTES, - crypto_box_BEFORENMBYTES + crypto_box_BEFORENMBYTES, + crypto_box_MACBYTES } function crypto_box_keypair (pk, sk) { @@ -37,7 +53,6 @@ function crypto_box_keypair (pk, sk) { randombytes(sk, 32) return crypto_scalarmult_base(pk, sk) } - function crypto_box_seed_keypair (pk, sk, seed) { assert(pk.byteLength === crypto_box_PUBLICKEYBYTES, "pk should be 'crypto_box_PUBLICKEYBYTES' bytes") assert(sk.byteLength === crypto_box_SECRETKEYBYTES, "sk should be 'crypto_box_SECRETKEYBYTES' bytes") @@ -95,6 +110,87 @@ function crypto_box_seal_open (m, c, pk, sk) { return crypto_secretbox_open_easy(m, c.subarray(epk.length), n, k) } +function crypto_box_beforenm (k, pk, sk) { + const zero = new Uint8Array(16) + const s = new Uint8Array(32) + + assert(crypto_scalarmult(s, sk, pk) === 0) + + xsalsa20.core_hsalsa20(k, zero, s, xsalsa20.SIGMA) + + return true +} + +function crypto_box_detached_afternm (c, mac, m, n, k) { + return crypto_secretbox_detached(c, mac, m, n, k) +} + +function crypto_box_detached (c, mac, m, n, pk, sk) { + check(mac, crypto_box_MACBYTES) + check(n, crypto_box_NONCEBYTES) + check(pk, crypto_box_PUBLICKEYBYTES) + check(sk, crypto_box_SECRETKEYBYTES) + + const k = new Uint8Array(crypto_box_BEFORENMBYTES) + + assert(crypto_box_beforenm(k, pk, sk)) + + const ret = crypto_box_detached_afternm(c, mac, m, n, k) + cleanup(k) + + return ret +} + +function crypto_box_easy (c, m, n, pk, sk) { + assert( + c.length >= m.length + crypto_box_MACBYTES, + "c should be at least 'm.length + crypto_box_MACBYTES' bytes" + ) + assert( + m.length <= crypto_box_MESSAGEBYTES_MAX, + "m should be at most 'crypto_box_MESSAGEBYTES_MAX' bytes" + ) + + return crypto_box_detached( + c.subarray(crypto_box_MACBYTES, m.length + crypto_box_MACBYTES), + c.subarray(0, crypto_box_MACBYTES), + m, + n, + pk, + sk + ) +} + +function crypto_box_open_detached_afternm (m, c, mac, n, k) { + return crypto_secretbox_open_detached(m, c, mac, n, k) +} + +function crypto_box_open_detached (m, c, mac, n, pk, sk) { + const k = new Uint8Array(crypto_box_BEFORENMBYTES) + assert(crypto_box_beforenm(k, pk, sk)) + + const ret = crypto_box_open_detached_afternm(m, c, mac, n, k) + cleanup(k) + + return ret +} + +function crypto_box_open_easy (m, c, n, pk, sk) { + assert( + c.length >= m.length + crypto_box_MACBYTES, + "c should be at least 'm.length + crypto_box_MACBYTES' bytes" + ) + + return crypto_box_open_detached( + m, + c.subarray(crypto_box_MACBYTES, m.length + crypto_box_MACBYTES), + c.subarray(0, crypto_box_MACBYTES), + n, + pk, + sk + ) +} + function check (buf, len) { if (!buf || (len && buf.length < len)) throw new Error('Argument must be a buffer' + (len ? ' of length ' + len : '')) } diff --git a/crypto_secretbox.js b/crypto_secretbox.js index 63d66e8..9634d84 100644 --- a/crypto_secretbox.js +++ b/crypto_secretbox.js @@ -68,8 +68,8 @@ function crypto_secretbox_detached (o, mac, msg, n, k) { const tmp = new Uint8Array(msg.byteLength + mac.byteLength) crypto_secretbox_easy(tmp, msg, n, k) - o.set(tmp.subarray(0, msg.byteLength)) - mac.set(tmp.subarray(msg.byteLength)) + mac.set(tmp.subarray(0, mac.byteLength)) + o.set(tmp.subarray(mac.byteLength)) return true } @@ -80,8 +80,8 @@ function crypto_secretbox_open_detached (msg, o, mac, n, k) { assert(k.byteLength === crypto_secretbox_KEYBYTES, "k must be 'crypto_secretbox_KEYBYTES' bytes") const tmp = new Uint8Array(o.byteLength + mac.byteLength) - tmp.set(o) - tmp.set(mac, msg.byteLength) + tmp.set(mac) + tmp.set(o, mac.byteLength) return crypto_secretbox_open_easy(msg, tmp, n, k) } diff --git a/crypto_stream.js b/crypto_stream.js index 4bdc8b5..01af63c 100644 --- a/crypto_stream.js +++ b/crypto_stream.js @@ -6,6 +6,7 @@ if (new Uint16Array([1])[0] !== 1) throw new Error('Big endian architecture is n exports.crypto_stream_KEYBYTES = 32 exports.crypto_stream_NONCEBYTES = 24 exports.crypto_stream_PRIMITIVE = 'xsalsa20' +exports.crypto_stream_xsalsa20_MESSAGEBYTES_MAX = Number.MAX_SAFE_INTEGER exports.crypto_stream = function (c, nonce, key) { c.fill(0)