diff --git a/crypto_aead.js b/crypto_aead.js new file mode 100644 index 0000000..33da38c --- /dev/null +++ b/crypto_aead.js @@ -0,0 +1,132 @@ +const { crypto_stream_chacha20_ietf, crypto_stream_chacha20_ietf_xor_ic } = require('./crypto_stream_chacha20') +const { crypto_verify_16 } = require('./crypto_verify') +const Poly1305 = require('./poly1305.js') +const assert = require('nanoassert') + +const crypto_aead_chacha20poly1305_ietf_KEYBYTES = 32 +const crypto_aead_chacha20poly1305_ietf_NSECBYTES = 0 +const crypto_aead_chacha20poly1305_ietf_NPUBBYTES = 12 +const crypto_aead_chacha20poly1305_ietf_ABYTES = 16 +const crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX = Number.MAX_SAFE_INTEGER + +const _pad0 = Buffer.alloc(16) + +function crypto_aead_chacha20poly1305_ietf_encrypt(c, m, ad, nsec, npub, k) { + var clen = 0 + var ret + + assert(m.length <= crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX, 'message is too large') + + ret = crypto_aead_chacha20poly1305_ietf_encrypt_detached(c, c.subarray(m.length), m, ad, nsec, npub, k); + + if (ret == 0) { + return m.length + crypto_aead_chacha20poly1305_ietf_ABYTES; + } + + return ret; +} + +function crypto_aead_chacha20poly1305_ietf_encrypt_detached(c, mac, m, ad, nsec, npub, k) { + const block0 = new Uint8Array(64) + var slen = Buffer.alloc(8) + + crypto_stream_chacha20_ietf(block0, npub, k) + const poly = new Poly1305(block0) + block0.fill(0) + + poly.update(ad, 0, ad.length) + poly.update(_pad0, 0, (0x10 - ad.length) & 0xf) + + crypto_stream_chacha20_ietf_xor_ic(c, m, npub, 1, k) + + poly.update(c, 0, m.length) + poly.update(_pad0, 0, (0x10 - m.length) & 0xf) + + write64LE(slen, ad.length) + poly.update(slen, 0, slen.length) + + write64LE(slen, m.length) + poly.update(slen, 0, slen.length) + + poly.finish(mac, 0) + slen.fill(0) + + return crypto_aead_chacha20poly1305_ietf_ABYTES +} + +function crypto_aead_chacha20poly1305_ietf_decrypt(m, nsec, c, ad, npub, k) { + var mlen = 0 + var ret = -1 + + if (c.length >= crypto_aead_chacha20poly1305_ietf_ABYTES) { + ret = crypto_aead_chacha20poly1305_ietf_decrypt_detached( + m, nsec, + c.subarray(0, c.length - crypto_aead_chacha20poly1305_ietf_ABYTES), + c.subarray(c.length - crypto_aead_chacha20poly1305_ietf_ABYTES), + ad, npub, k) + } + + if (ret == 0) { + return mlen = c.length - crypto_aead_chacha20poly1305_ietf_ABYTES + } + + return ret; +} + +function crypto_aead_chacha20poly1305_ietf_decrypt_detached(m, nsec, c, mac, ad, npub, k) { + const block0 = new Uint8Array(64) + const slen = Buffer.alloc(8) + const computed_mac = Buffer.alloc(crypto_aead_chacha20poly1305_ietf_ABYTES) + var mlen + var ret + + crypto_stream_chacha20_ietf(block0, npub, k) + const poly = new Poly1305(block0) + block0.fill(0) + + poly.update(ad, 0, ad.length) + poly.update(_pad0, 0, (0x10 - ad.length) & 0xf) + + mlen = c.length + poly.update(c, 0, mlen) + poly.update(_pad0, 0, (0x10 - mlen) & 0xf) + + write64LE(slen, ad.length) + poly.update(slen, 0, slen.length) + + write64LE(slen, mlen) + poly.update(slen, 0, slen.length) + + poly.finish(computed_mac, 0) + + assert(computed_mac.length == 16) + ret = crypto_verify_16(computed_mac, 0, mac, 0) + + computed_mac.fill(0) + slen.fill(0) + + if (ret != 0) { + m.fill(0) + return -1 + } + + crypto_stream_chacha20_ietf_xor_ic(m, c, npub, 1, k) + return 0 +} + +function write64LE (buf, int) { + buf.fill(0, 0, 8) + buf.writeUInt32LE(int & 0xffffffff) + buf.writeUInt32LE((int >> 32) & 0xffffffff) +} + +module.exports = { + crypto_aead_chacha20poly1305_ietf_encrypt, + crypto_aead_chacha20poly1305_ietf_encrypt_detached, + crypto_aead_chacha20poly1305_ietf_decrypt, + crypto_aead_chacha20poly1305_ietf_decrypt_detached, + crypto_aead_chacha20poly1305_ietf_ABYTES, + crypto_aead_chacha20poly1305_ietf_KEYBYTES, + crypto_aead_chacha20poly1305_ietf_NPUBBYTES, + crypto_aead_chacha20poly1305_ietf_NSECBYTES +} diff --git a/crypto_verify.js b/crypto_verify.js index 9eaa261..d4b219d 100644 --- a/crypto_verify.js +++ b/crypto_verify.js @@ -3,16 +3,16 @@ module.exports = { crypto_verify_32 } -function vn(x, xi, y, yi, n) { - var i,d = 0; - for (i = 0; i < n; i++) d |= x[xi+i]^y[yi+i]; - return (1 & ((d - 1) >>> 8)) - 1; +function vn (x, xi, y, yi, n) { + var i, d = 0 + for (i = 0; i < n; i++) d |= x[xi + i] ^ y[yi + i] + return (1 & ((d - 1) >>> 8)) - 1 } function crypto_verify_16(x, xi, y, yi) { - return vn(x,xi,y,yi,16); + return vn(x, xi, y, yi, 16) } function crypto_verify_32(x, xi, y, yi) { - return vn(x,xi,y,yi,32); + return vn(x, xi, y, yi, 32) } diff --git a/index.js b/index.js index fb9f27b..2d91394 100644 --- a/index.js +++ b/index.js @@ -25,6 +25,7 @@ forward(require('./crypto_generichash')) forward(require('./crypto_hash')) forward(require('./crypto_kdf')) forward(require('./crypto_kx')) +forward(require('./crypto_aead')) forward(require('./crypto_onetimeauth')) forward(require('./crypto_scalarmult')) forward(require('./crypto_secretbox'))