Created
March 30, 2020 22:30
-
-
Save emilbayes/cd9153c04cfb495e1a8bb8c5f862f486 to your computer and use it in GitHub Desktop.
Key encapsulation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const sodium = require('sodium-native') | |
const assert = require('nanoassert') | |
const priv = new WeakMap() | |
class Key { | |
static BYTES = sodium.crypto_secretbox_KEYBYTES | |
static NONCEBYTES = crypto.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES | |
static TAGBYTES = sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES | |
static create () { | |
const key = sodium.sodium_malloc(this.BYTES) | |
sodium.randombytes_buf(key) | |
return new this(key) | |
} | |
static import (key) { | |
return new this(key) | |
} | |
constructor (key) { | |
assert(key.byteLength === this.BYTES, 'key must be Key.BYTES long') | |
assert(key.secure, 'key must be secure buffer') | |
this.destroyed = false | |
priv.set(this, key) | |
} | |
encrypt (plaintext, buf) { | |
if (this.destroyed) throw new Error('Destroyed') | |
assert(Buffer.isBuffer(plaintext), 'plaintext must be (secure) buffer') | |
const outlen = this.encryptLength(plaintext) | |
if (buf == null) buf = Buffer.alloc(outlen) | |
assert(buf.byteLength === outlen, 'buf must be encryptLength(plaintext) long') | |
const nonce = buf.subarray(0, Key.NONCEBYTES) | |
const ciphertext = buf.subarray(Key.NONCEBYTES) | |
sodium.randombytes_buf(nonce) | |
sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(ciphertext, plaintext, null, null, nonce, priv.get(this)) | |
return buf | |
} | |
encryptLength (plaintext) { | |
assert(Buffer.isBuffer(plaintext), 'plaintext must be (secure) buffer') | |
return Key.NONCEBYTES + plaintext.byteLength + Key.TAGBYTES | |
} | |
decrypt (ciphertext, buf) { | |
if (this.destroyed) throw new Error('Destroyed') | |
assert(Buffer.isBuffer(ciphertext), 'ciphertext must be buffer') | |
const outlen = this.decryptLength(ciphertext) | |
if (buf == null) buf = sodium.sodium_malloc(outlen) | |
assert(buf.byteLength === outlen, 'buf must be decryptLength(ciphertext) long') | |
const nonce = buf.subarray(0, Key.NONCEBYTES) | |
const aead = buf.subarray(Key.NONCEBYTES) | |
sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(buf, null, aead, null, nonce, priv.get(this)) | |
return buf | |
} | |
decryptLength (ciphertext) { | |
assert(Buffer.isBuffer(ciphertext), 'ciphertext must be buffer') | |
return -Key.NONCEBYTES + ciphertext.byteLength - Key.TAGBYTES | |
} | |
export () { | |
if (this.destroyed) throw new Error('Destroyed') | |
return priv.get(this) | |
} | |
destroy () { | |
const key = priv.get(this) | |
sodium.sodium_memzero(key) | |
priv.delete(key) | |
this.destroyed = true | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment