diff --git a/README.md b/README.md index 2a55bc2..953db89 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,11 @@ Calculate the required length for a `ciphertext` from `plaintext` Buffer. Number of bytes written into `ciphertext` at last call to `tx.encrypt` +### `tx.destroy()` + +Destroys the internal state and zero all memory. Can only be called once, +you may never call `encrypt` after and sets `.bytes` to `null`. + ### `var rx = secretstream.decrypt(header, key)` Create an decrypt instance with `key`, using `header` from `encrypt`. @@ -90,6 +95,11 @@ compared against one of the exported tags. Please review the [libsodium documentation](https://download.libsodium.org/doc/secret-key_cryptography/secretstream#usage) for how tags should be interpreted. +### `rx.destroy()` + +Destroys the internal state and zero all memory. Can only be called once, +you may never call `encrypt` after and sets `.bytes` and `.tag` to `null`. + ## Install ```sh diff --git a/example.js b/example.js index 8a6e938..2468b68 100644 --- a/example.js +++ b/example.js @@ -18,3 +18,6 @@ var rx = secretstream.decrypt(header, key) var plaintext = rx.decrypt(ciphertext) console.log(plaintext.equals(Buffer.from('Hello world!')), rx.decrypt.tag.equals(secretstream.TAG_MESSAGE)) + +rx.destroy() +tx.destroy() diff --git a/index.js b/index.js index a763458..fea1870 100644 --- a/index.js +++ b/index.js @@ -18,10 +18,13 @@ exports.encrypt = function (header, key) { assert(Buffer.isBuffer(key), 'key must be Buffer') assert(key.byteLength >= exports.KEYBYTES, 'key must be at least KEYBYTES (' + exports.KEYBYTES + ') long') + var destroyed = false var state = sodium.crypto_secretstream_xchacha20poly1305_state_new() sodium.crypto_secretstream_xchacha20poly1305_init_push(state, header, key) function encrypt (tag, plaintext, ad, ciphertext, offset) { + assert(destroyed === false, 'state already destroyed') + assert(Buffer.isBuffer(plaintext), 'plaintext must be Buffer') if (ciphertext == null) ciphertext = Buffer.alloc(encryptionLength(plaintext)) if (offset == null) offset = 0 @@ -33,12 +36,23 @@ exports.encrypt = function (header, key) { encrypt.bytes = 0 function encryptionLength (plaintext) { + assert(Buffer.isBuffer(plaintext), 'plaintext must be Buffer') + return plaintext.byteLength + exports.ABYTES } + function destroy () { + assert(destroyed === false, 'state already destroyed') + state = null // Should memzero when we have buffer trick in sodium-native + encrypt.bytes = null + + destroyed = true + } + return { encrypt, - encryptionLength + encryptionLength, + destroy } } @@ -49,10 +63,13 @@ exports.decrypt = function (header, key) { assert(Buffer.isBuffer(key), 'key must be Buffer') assert(key.byteLength >= exports.KEYBYTES, 'key must be at least KEYBYTES (' + exports.KEYBYTES + ') long') + var destroyed = false var state = sodium.crypto_secretstream_xchacha20poly1305_state_new() sodium.crypto_secretstream_xchacha20poly1305_init_pull(state, header, key) function decrypt (ciphertext, ad, plaintext, offset) { + assert(destroyed === false, 'state already destroyed') + assert(Buffer.isBuffer(ciphertext), 'ciphertext must be Buffer') if (plaintext == null) plaintext = Buffer.alloc(decryptionLength(ciphertext)) if (offset == null) offset = 0 @@ -65,11 +82,23 @@ exports.decrypt = function (header, key) { decrypt.bytes = 0 function decryptionLength (ciphertext) { + assert(Buffer.isBuffer(ciphertext), 'ciphertext must be Buffer') + return ciphertext.byteLength - exports.ABYTES } + function destroy () { + assert(destroyed === false, 'state already destroyed') + state = null // Should memzero when we have buffer trick in sodium-native + decrypt.tag = null + decrypt.bytes = null + + destroyed = true + } + return { decrypt, - decryptionLength + decryptionLength, + destroy } }