aboutsummaryrefslogtreecommitdiffstats
path: root/library/sodium-plus/lib
diff options
context:
space:
mode:
authorMario <mario@mariovavti.com>2024-03-10 22:38:21 +0000
committerMario <mario@mariovavti.com>2024-03-10 22:38:21 +0000
commit360713c6896d18a95dd3ca541ea477bf44b98d0c (patch)
tree8acaa683b277023aa4842f25b04623ae196fa1ae /library/sodium-plus/lib
parentee8aba3221f995b265c3196505a1c7c26b76f116 (diff)
downloadvolse-hubzilla-360713c6896d18a95dd3ca541ea477bf44b98d0c.tar.gz
volse-hubzilla-360713c6896d18a95dd3ca541ea477bf44b98d0c.tar.bz2
volse-hubzilla-360713c6896d18a95dd3ca541ea477bf44b98d0c.zip
add sodium-plus js crypto library
Diffstat (limited to 'library/sodium-plus/lib')
-rw-r--r--library/sodium-plus/lib/backend.js32
-rw-r--r--library/sodium-plus/lib/backend/libsodium-wrappers.js782
-rw-r--r--library/sodium-plus/lib/backend/sodiumnative.js866
-rw-r--r--library/sodium-plus/lib/cryptography-key.js80
-rw-r--r--library/sodium-plus/lib/keytypes/ed25519pk.js28
-rw-r--r--library/sodium-plus/lib/keytypes/ed25519sk.js29
-rw-r--r--library/sodium-plus/lib/keytypes/x25519pk.js29
-rw-r--r--library/sodium-plus/lib/keytypes/x25519sk.js29
-rw-r--r--library/sodium-plus/lib/polyfill.js74
-rw-r--r--library/sodium-plus/lib/sodium-error.js1
-rw-r--r--library/sodium-plus/lib/sodiumplus.js1212
-rw-r--r--library/sodium-plus/lib/util.js133
12 files changed, 3295 insertions, 0 deletions
diff --git a/library/sodium-plus/lib/backend.js b/library/sodium-plus/lib/backend.js
new file mode 100644
index 000000000..84466bf25
--- /dev/null
+++ b/library/sodium-plus/lib/backend.js
@@ -0,0 +1,32 @@
+const CryptographyKey = require('./cryptography-key');
+/* istanbul ignore if */
+if (typeof (Buffer) === 'undefined') {
+ let Buffer = require('buffer/').Buffer;
+}
+
+module.exports = class Backend {
+ constructor() {
+ // NOP
+ this.backendName = 'UndefinedBackend';
+ }
+
+ /**
+ * @param {CryptographyKey} sKey
+ * @param {CryptographyKey} pKey
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_box_keypair_from_secretkey_and_publickey(sKey, pKey) {
+ /* istanbul ignore if */
+ if (sKey.getLength() !== 32) {
+ throw new Error('Secret key must be 32 bytes');
+ }
+ /* istanbul ignore if */
+ if (pKey.getLength() !== 32) {
+ throw new Error('Public key must be 32 bytes');
+ }
+ const keypair = Buffer.alloc(64);
+ sKey.getBuffer().copy(keypair, 0, 0, 32);
+ pKey.getBuffer().copy(keypair, 32, 0, 32);
+ return new CryptographyKey(Buffer.from(keypair));
+ }
+};
diff --git a/library/sodium-plus/lib/backend/libsodium-wrappers.js b/library/sodium-plus/lib/backend/libsodium-wrappers.js
new file mode 100644
index 000000000..130f2e514
--- /dev/null
+++ b/library/sodium-plus/lib/backend/libsodium-wrappers.js
@@ -0,0 +1,782 @@
+const _sodium = require('libsodium-wrappers');
+const Backend = require('../backend');
+const CryptographyKey = require('../cryptography-key');
+const Polyfill = require('../polyfill');
+const Util = require('../util');
+const SodiumError = require('../sodium-error');
+const toBuffer = require('typedarray-to-buffer');
+/* istanbul ignore if */
+if (typeof (Buffer) === 'undefined') {
+ let Buffer = require('buffer/').Buffer;
+}
+
+/* istanbul ignore next */
+module.exports = class LibsodiumWrappersBackend extends Backend {
+ constructor(lib) {
+ super(lib);
+ this.sodium = lib;
+ this.backendName = 'LibsodiumWrappersBackend';
+ }
+
+ static async init() {
+ await _sodium.ready;
+ return new LibsodiumWrappersBackend(_sodium);
+ }
+
+ /**
+ *
+ * @param {String|Buffer} ciphertext
+ * @param {String|Buffer} assocData
+ * @param {String|Buffer} nonce
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_aead_xchacha20poly1305_ietf_decrypt(ciphertext, assocData, nonce, key) {
+ return toBuffer(
+ this.sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(
+ null,
+ ciphertext,
+ assocData,
+ nonce,
+ key.getBuffer()
+ )
+ );
+ }
+
+ /**
+ *
+ * @param {String|Buffer} plaintext
+ * @param {String|Buffer} assocData
+ * @param {String|Buffer} nonce
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, assocData, nonce, key) {
+ return toBuffer(
+ this.sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(
+ plaintext,
+ assocData,
+ null,
+ nonce,
+ key.getBuffer()
+ )
+ );
+ }
+
+ /**
+ * @param {String|Buffer} message
+ * @param {CryptographyKey} key
+ * @return {Promise<buffer>}
+ */
+ async crypto_auth(message, key) {
+ return toBuffer(
+ this.sodium.crypto_auth(
+ message,
+ key.getBuffer()
+ )
+ );
+ }
+
+ /**
+ * @param {Buffer} mac
+ * @param {String|Buffer} message
+ * @param {CryptographyKey} key
+ * @return {Promise<boolean>}
+ */
+ async crypto_auth_verify(mac, message, key) {
+ return this.sodium.crypto_auth_verify(
+ mac,
+ message,
+ key.getBuffer()
+ );
+ }
+
+ /**
+ * @param {string|Buffer} plaintext
+ * @param {Buffer} nonce
+ * @param {CryptographyKey} sk
+ * @param {CryptographyKey} pk
+ * @return {Promise<Buffer>}
+ *
+ */
+ async crypto_box(plaintext, nonce, sk, pk) {
+ return Util.toBuffer(
+ await this.sodium.crypto_box_easy(
+ await Util.toBuffer(plaintext),
+ await Util.toBuffer(nonce),
+ pk.getBuffer(),
+ sk.getBuffer()
+ )
+ );
+ }
+
+ /**
+ * @param {Buffer} ciphertext
+ * @param {Buffer} nonce
+ * @param {CryptographyKey} sk
+ * @param {CryptographyKey} pk
+ * @return {Promise<Buffer>}
+ */
+ async crypto_box_open(ciphertext, nonce, sk, pk) {
+ return Util.toBuffer(
+ await this.sodium.crypto_box_open_easy(
+ await Util.toBuffer(ciphertext),
+ await Util.toBuffer(nonce),
+ pk.getBuffer(),
+ sk.getBuffer()
+ )
+ );
+ }
+
+ /**
+ * @param {string|Buffer} plaintext
+ * @param {CryptographyKey} pk
+ * @return {Promise<Buffer>}
+ *
+ */
+ async crypto_box_seal(plaintext, pk) {
+ return Util.toBuffer(
+ await this.sodium.crypto_box_seal(
+ await Util.toBuffer(plaintext),
+ pk.getBuffer()
+ )
+ );
+ }
+
+ /**
+ * @param {Buffer} ciphertext
+ * @param {CryptographyKey} pk
+ * @param {CryptographyKey} sk
+ * @return {Promise<Buffer>}
+ */
+ async crypto_box_seal_open(ciphertext, pk, sk) {
+ return Util.toBuffer(
+ await this.sodium.crypto_box_seal_open(
+ await Util.toBuffer(ciphertext),
+ pk.getBuffer(),
+ sk.getBuffer()
+ )
+ );
+ }
+
+ /**
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_box_keypair() {
+ const obj = this.sodium.crypto_box_keypair();
+ return new CryptographyKey(
+ Buffer.concat([
+ await Util.toBuffer(obj.privateKey),
+ await Util.toBuffer(obj.publicKey)
+ ])
+ );
+ }
+
+ /**
+ * @param {string|Buffer} message
+ * @param {CryptographyKey|null} key
+ * @param {number} outputLength
+ * @return {Promise<Buffer>}
+ */
+ async crypto_generichash(message, key = null, outputLength = 32) {
+ if (key) {
+ return Util.toBuffer(
+ this.sodium.crypto_generichash(
+ outputLength,
+ await Util.toBuffer(message),
+ key.getBuffer()
+ )
+ );
+ }
+ return Util.toBuffer(
+ this.sodium.crypto_generichash(
+ outputLength,
+ await Util.toBuffer(message)
+ )
+ );
+ }
+
+ /**
+ * @param {CryptographyKey|null} key
+ * @param {number} outputLength
+ * @return {Promise<Buffer>}
+ */
+ async crypto_generichash_init(key = null, outputLength = 32) {
+ if (key) {
+ return this.sodium.crypto_generichash_init(key.getBuffer(), outputLength);
+ }
+ return this.sodium.crypto_generichash_init(null, outputLength);
+ }
+
+ /**
+ * @param {*} state
+ * @param {string|Buffer} message
+ * @return {Promise<*>}
+ */
+ async crypto_generichash_update(state, message) {
+ return this.sodium.crypto_generichash_update(state, await Util.toBuffer(message));
+ }
+
+ /**
+ * @param {*} state
+ * @param {number} outputLength
+ * @return {Promise<Buffer>}
+ */
+ async crypto_generichash_final(state, outputLength = 32) {
+ return Util.toBuffer(
+ this.sodium.crypto_generichash_final(state, outputLength)
+ );
+ }
+
+ /**
+ * @param {X25519PublicKey} clientPublicKey
+ * @param {X25519SecretKey} clientSecretKey
+ * @param {X25519PublicKey} serverPublicKey
+ * @return {Promise<CryptographyKey[]>}
+ */
+ async crypto_kx_client_session_keys(clientPublicKey, clientSecretKey, serverPublicKey) {
+ const gen = this.sodium.crypto_kx_client_session_keys(
+ clientPublicKey.getBuffer(),
+ clientSecretKey.getBuffer(),
+ serverPublicKey.getBuffer(),
+ );
+ return [
+ new CryptographyKey(await Util.toBuffer(gen.sharedRx)),
+ new CryptographyKey(await Util.toBuffer(gen.sharedTx))
+ ];
+ }
+
+ /**
+ * @param {X25519PublicKey} serverPublicKey
+ * @param {X25519SecretKey} serverSecretKey
+ * @param {X25519PublicKey} clientPublicKey
+ * @return {Promise<CryptographyKey[]>}
+ */
+ async crypto_kx_server_session_keys(serverPublicKey, serverSecretKey, clientPublicKey) {
+ const gen = this.sodium.crypto_kx_server_session_keys(
+ serverPublicKey.getBuffer(),
+ serverSecretKey.getBuffer(),
+ clientPublicKey.getBuffer(),
+ );
+ return [
+ new CryptographyKey(await Util.toBuffer(gen.sharedRx)),
+ new CryptographyKey(await Util.toBuffer(gen.sharedTx))
+ ];
+ }
+
+ /**
+ * @param {number} length
+ * @param {number} subKeyId
+ * @param {string|Buffer} context
+ * @param {CryptographyKey} key
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_kdf_derive_from_key(length, subKeyId, context, key) {
+ return new CryptographyKey(
+ await Util.toBuffer(
+ this.sodium.crypto_kdf_derive_from_key(
+ length,
+ subKeyId | 0,
+ context,
+ key.getBuffer()
+ )
+ )
+ );
+ }
+
+ /**
+ * @param {string|Buffer} message
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_onetimeauth(message, key) {
+ if (typeof this.sodium.crypto_onetimeauth === 'undefined') {
+ return Polyfill.crypto_onetimeauth(
+ await Util.toBuffer(message),
+ key
+ );
+ }
+ return this.sodium.crypto_onetimeauth(
+ await Util.toBuffer(message),
+ key.getBuffer()
+ );
+ }
+
+ /**
+ * @param {string|Buffer} message
+ * @param {CryptographyKey} key
+ * @param {Buffer} tag
+ * @return {Promise<boolean>}
+ */
+ async crypto_onetimeauth_verify(message, key, tag) {
+ if (typeof this.sodium.crypto_onetimeauth_verify === 'undefined') {
+ return Polyfill.crypto_onetimeauth_verify(
+ await Util.toBuffer(message),
+ key,
+ tag
+ );
+ }
+ return this.sodium.crypto_onetimeauth_verify(
+ tag,
+ await Util.toBuffer(message),
+ key.getBuffer()
+ );
+ }
+
+ /**
+ * @param {number} length
+ * @param {string|Buffer} password
+ * @param {Buffer} salt
+ * @param {number} opslimit
+ * @param {number} memlimit
+ * @param {number} algorithm
+ * @return {Promise<Buffer>}
+ */
+ async crypto_pwhash(length, password, salt, opslimit, memlimit, algorithm) {
+ return Util.toBuffer(
+ this.sodium.crypto_pwhash(
+ length,
+ await Util.toBuffer(password),
+ await Util.toBuffer(salt),
+ opslimit,
+ memlimit,
+ algorithm
+ )
+ );
+ }
+
+ /**
+ * @param {string|Buffer} password
+ * @param {number} opslimit
+ * @param {number} memlimit
+ * @return {Promise<string>}
+ */
+ async crypto_pwhash_str(password, opslimit, memlimit) {
+ return (await Util.toBuffer(
+ this.sodium.crypto_pwhash_str(
+ await Util.toBuffer(password),
+ opslimit,
+ memlimit
+ ))
+ ).toString('utf-8');
+ }
+
+ /**
+ * @param {string|Buffer} password
+ * @param {string|Buffer} hash
+ * @return {Promise<boolean>}
+ */
+ async crypto_pwhash_str_verify(password, hash) {
+ return this.sodium.crypto_pwhash_str_verify(
+ hash.toString('utf-8'),
+ await Util.toBuffer(password)
+ );
+ }
+
+ /**
+ * @param {string|Buffer} hash
+ * @param {number} opslimit
+ * @param {number} memlimit
+ * @return {Promise<boolean>}
+ */
+ async crypto_pwhash_str_needs_rehash(hash, opslimit, memlimit) {
+ if (typeof (this.sodium.crypto_pwhash_str_needs_rehash) !== 'function') {
+ return await Polyfill.crypto_pwhash_str_needs_rehash(hash, opslimit, memlimit);
+ }
+ return this.sodium.crypto_pwhash_str_needs_rehash(hash, opslimit, memlimit);
+ }
+
+ /**
+ * @param {X25519SecretKey} secretKey
+ * @param {X25519PublicKey} publicKey
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_scalarmult(secretKey, publicKey) {
+ return new CryptographyKey(
+ await Util.toBuffer(
+ this.sodium.crypto_scalarmult(secretKey.getBuffer(), publicKey.getBuffer())
+ )
+ );
+ }
+
+ /**
+ * @param {string|Buffer} plaintext
+ * @param {Buffer} nonce
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_secretbox(plaintext, nonce, key) {
+ return Util.toBuffer(
+ this.sodium.crypto_secretbox_easy(
+ await Util.toBuffer(plaintext),
+ nonce,
+ key.getBuffer()
+ )
+ );
+ }
+
+ /**
+ * @param {Buffer} ciphertext
+ * @param {Buffer} nonce
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_secretbox_open(ciphertext, nonce, key) {
+ return Util.toBuffer(
+ this.sodium.crypto_secretbox_open_easy(
+ await Util.toBuffer(ciphertext),
+ nonce,
+ key.getBuffer()
+ )
+ );
+ }
+
+ /**
+ * @param {string|Buffer} message
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_shorthash(message, key) {
+ return Util.toBuffer(
+ this.sodium.crypto_shorthash(
+ await Util.toBuffer(message),
+ key.getBuffer()
+ )
+ );
+ }
+
+ /**
+ * @param {string|Buffer} message,
+ * @param {Ed25519SecretKey} secretKey
+ * @return {Promise<Buffer>}
+ */
+ async crypto_sign(message, secretKey) {
+ return Util.toBuffer(
+ this.sodium.crypto_sign(
+ await Util.toBuffer(message),
+ secretKey.getBuffer()
+ )
+ );
+ }
+
+ /**
+ * @param {string|Buffer} message,
+ * @param {Ed25519PublicKey} publicKey
+ * @return {Promise<Buffer>}
+ */
+ async crypto_sign_open(message, publicKey) {
+ return Util.toBuffer(
+ this.sodium.crypto_sign_open(
+ message,
+ publicKey.getBuffer()
+ )
+ );
+ }
+ /**
+ * @param {string|Buffer} message,
+ * @param {Ed25519SecretKey} secretKey
+ * @return {Promise<Buffer>}
+ */
+ async crypto_sign_detached(message, secretKey) {
+ return Util.toBuffer(
+ this.sodium.crypto_sign_detached(
+ await Util.toBuffer(message),
+ secretKey.getBuffer()
+ )
+ );
+ }
+
+ /**
+ * @param {string|Buffer} message,
+ * @param {Ed25519PublicKey} publicKey
+ * @param {Buffer} signature
+ * @return {Promise<Buffer>}
+ */
+ async crypto_sign_verify_detached(message, publicKey, signature) {
+ return this.sodium.crypto_sign_verify_detached(
+ signature,
+ await Util.toBuffer(message),
+ publicKey.getBuffer()
+ );
+ }
+
+ /**
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_sign_keypair() {
+ const obj = this.sodium.crypto_sign_keypair();
+ return new CryptographyKey(
+ Buffer.concat([
+ await Util.toBuffer(obj.privateKey),
+ await Util.toBuffer(obj.publicKey)
+ ])
+ );
+ }
+
+ /**
+ * @param {Buffer} seed
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_sign_seed_keypair(seed) {
+ const obj = this.sodium.crypto_sign_seed_keypair(seed);
+ return new CryptographyKey(
+ Buffer.concat([
+ await Util.toBuffer(obj.privateKey),
+ await Util.toBuffer(obj.publicKey)
+ ])
+ );
+ }
+
+ /**
+ * @param {Ed25519SecretKey} sk
+ * @return {Promise<Buffer>}
+ */
+ async crypto_sign_ed25519_sk_to_curve25519(sk) {
+ return Util.toBuffer(
+ this.sodium.crypto_sign_ed25519_sk_to_curve25519(sk.getBuffer())
+ );
+ }
+
+ /**
+ * @param {Ed25519PublicKey} pk
+ * @return {Promise<Buffer>}
+ */
+ async crypto_sign_ed25519_pk_to_curve25519(pk) {
+ return Util.toBuffer(
+ this.sodium.crypto_sign_ed25519_pk_to_curve25519(pk.getBuffer())
+ );
+ }
+
+
+ /**
+ * @param {number} length
+ * @param {Buffer} nonce
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_stream(length, nonce, key) {
+ if (typeof (this.sodium.crypto_stream_xor) === 'undefined') {
+ return Polyfill.crypto_stream_xor(
+ Buffer.alloc(length, 0),
+ await Util.toBuffer(nonce),
+ key
+ );
+ }
+ return this.sodium.crypto_stream(
+ length,
+ await Util.toBuffer(nonce),
+ key.getBuffer()
+ );
+ }
+
+ /**
+ * @param {string|Buffer} plaintext
+ * @param {Buffer} nonce
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_stream_xor(plaintext, nonce, key) {
+ if (typeof (this.sodium.crypto_stream_xor) === 'undefined') {
+ return Polyfill.crypto_stream_xor(
+ await Util.toBuffer(plaintext),
+ await Util.toBuffer(nonce),
+ key
+ )
+ }
+ return this.sodium.crypto_stream_xor(
+ await Util.toBuffer(plaintext),
+ await Util.toBuffer(nonce),
+ key.getBuffer()
+ );
+ }
+
+ /**
+ *
+ * @param {CryptographyKey} secretKey
+ * @return {Promise<Buffer>}
+ */
+ async crypto_scalarmult_base(secretKey) {
+ return Util.toBuffer(
+ this.sodium.crypto_scalarmult_base(secretKey.getBuffer())
+ );
+ }
+
+ /**
+ * @param {CryptographyKey} key
+ * @return {Promise<array>} [state, header]
+ */
+ async crypto_secretstream_xchacha20poly1305_init_push(key) {
+ const res = this.sodium.crypto_secretstream_xchacha20poly1305_init_push(key.getBuffer());
+ return [res.state, await Util.toBuffer(res.header)];
+ }
+
+ /**
+ * @param {Buffer} header
+ * @param {CryptographyKey} key
+ * @return {Promise<*>} Returns the opaque state object
+ */
+ async crypto_secretstream_xchacha20poly1305_init_pull(header, key) {
+ if (header.length !== this.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES) {
+ throw new SodiumError(`Header must be ${this.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES} bytes long`);
+ }
+ return this.sodium.crypto_secretstream_xchacha20poly1305_init_pull(header, key.getBuffer());
+ }
+
+ /**
+ * @param {*} state
+ * @param {string|Buffer} message
+ * @param {string|Buffer} ad
+ * @param {number} tag
+ * @return {Promise<Buffer>}
+ */
+ async crypto_secretstream_xchacha20poly1305_push(state, message, ad = '', tag = 0) {
+ return Util.toBuffer(
+ this.sodium.crypto_secretstream_xchacha20poly1305_push(
+ state,
+ await Util.toBuffer(message),
+ ad.length > 0 ? (await Util.toBuffer(ad)) : null,
+ tag
+ )
+ );
+ }
+
+ /**
+ * @param {*} state
+ * @param {Buffer} ciphertext
+ * @param {string|Buffer} ad
+ * @param {number} tag
+ * @return {Promise<Buffer>}
+ */
+ async crypto_secretstream_xchacha20poly1305_pull(state, ciphertext, ad = '', tag = 0) {
+ if (ciphertext.length < this.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES) {
+ throw new SodiumError('Invalid ciphertext size');
+ }
+ const out = this.sodium.crypto_secretstream_xchacha20poly1305_pull(
+ state,
+ await Util.toBuffer(ciphertext),
+ ad.length > 0 ? (await Util.toBuffer(ad)) : null,
+ tag
+ );
+ if (tag !== out.tag) {
+ throw new SodiumError(`Invalid tag (Given: ${tag}; Expected: ${out.tag})`);
+ }
+ return Util.toBuffer(out.message);
+ }
+
+ /**
+ * @param {*} state
+ * @return {Promise<void>}
+ */
+ async crypto_secretstream_xchacha20poly1305_rekey(state) {
+ this.sodium.crypto_secretstream_xchacha20poly1305_rekey(state);
+ }
+
+ /**
+ * @param {number} number
+ * @return {Promise<Buffer>}
+ */
+ async randombytes_buf(number) {
+ return Util.toBuffer(await this.sodium.randombytes_buf(number));
+ }
+
+ /**
+ * @param {number} upperBound
+ * @return {Promise<number>}
+ */
+ async randombytes_uniform(upperBound) {
+ return this.sodium.randombytes_uniform(upperBound);
+ }
+
+ /**
+ * @param {Uint8Array} val
+ * @param {Uint8Array} addv
+ * @return {Promise<Buffer>}
+ */
+ async sodium_add(val, addv) {
+ const buf = await Util.cloneBuffer(val);
+ this.sodium.add(buf, addv);
+ return buf;
+ }
+
+ /**
+ * @param {Buffer} buf
+ * @return {Promise<string>}
+ */
+ async sodium_bin2hex(buf) {
+ return this.sodium.to_hex(buf);
+ }
+
+ /**
+ * @param {Buffer} b1
+ * @param {Buffer} b2
+ * @return {Promise<number>}
+ */
+ async sodium_compare(b1, b2) {
+ return this.sodium.compare(b1, b2);
+ }
+
+ /**
+ * @param {Buffer|string} encoded
+ * @return {Promise<Buffer>}
+ */
+ async sodium_hex2bin(encoded) {
+ return Buffer.from(this.sodium.from_hex(encoded));
+ }
+
+ /**
+ * @param {Buffer} buf
+ * @return {Promise<Buffer>}
+ */
+ async sodium_increment(buf) {
+ return this.sodium.increment(buf);
+ }
+
+ /**
+ * @param {Buffer} buf
+ * @param {number} len
+ * @return {Promise<Buffer>}
+ */
+ async sodium_is_zero(buf, len) {
+ return this.sodium.is_zero(buf, len);
+ }
+
+ /**
+ * @param {Buffer} b1
+ * @param {Buffer} b2
+ * @return {Promise<boolean>}
+ */
+ async sodium_memcmp(b1, b2) {
+ return this.sodium.memcmp(b1, b2);
+ }
+
+ /**
+ * @param {Buffer} buf
+ * @return {Promise<void>}
+ */
+ async sodium_memzero(buf) {
+ this.sodium.memzero(buf);
+ }
+
+
+ /**
+ *
+ * @param {string|Buffer} buf
+ * @param {number} blockSize
+ * @return {Promise<Buffer>}
+ */
+ async sodium_pad(buf, blockSize) {
+ return Util.toBuffer(
+ this.sodium.pad(await Util.toBuffer(buf), blockSize)
+ );
+ }
+
+ /**
+ *
+ * @param {string|Buffer} buf
+ * @param {number} blockSize
+ * @return {Promise<Buffer>}
+ */
+ async sodium_unpad(buf, blockSize) {
+ return Util.toBuffer(this.sodium.unpad(buf, blockSize));
+ }
+};
diff --git a/library/sodium-plus/lib/backend/sodiumnative.js b/library/sodium-plus/lib/backend/sodiumnative.js
new file mode 100644
index 000000000..feea8b432
--- /dev/null
+++ b/library/sodium-plus/lib/backend/sodiumnative.js
@@ -0,0 +1,866 @@
+let loaded = false;
+let _sodium;
+/* istanbul ignore next */
+try {
+ _sodium = require('sodium-native');
+ loaded = true;
+} catch (e) {
+ _sodium = {};
+}
+const Backend = require('../backend');
+const CryptographyKey = require('../cryptography-key');
+const SodiumError = require('../sodium-error');
+const Util = require('../util');
+const toBuffer = require('typedarray-to-buffer');
+/* istanbul ignore if */
+if (typeof (Buffer) === 'undefined') {
+ let Buffer = require('buffer/').Buffer;
+}
+
+/* istanbul ignore next */
+module.exports = class SodiumNativeBackend extends Backend {
+ constructor(lib) {
+ super(lib);
+ this.sodium = lib;
+ this.backendName = 'SodiumNativeBackend';
+ }
+
+ static async init() {
+ if (!loaded) {
+ throw new SodiumError('sodium-native not installed');
+ }
+ return new SodiumNativeBackend(_sodium);
+ }
+
+ /**
+ *
+ * @param {String|Buffer} ciphertext
+ * @param {String|Buffer} assocData
+ * @param {String|Buffer} nonce
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_aead_xchacha20poly1305_ietf_decrypt(ciphertext, assocData, nonce, key) {
+ const plaintext = Buffer.alloc(ciphertext.length - 16, 0);
+ this.sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(
+ plaintext,
+ null,
+ await Util.toBuffer(ciphertext),
+ await Util.toBuffer(assocData),
+ await Util.toBuffer(nonce),
+ key.getBuffer()
+ );
+ return plaintext;
+ }
+
+ /**
+ *
+ * @param {String|Buffer} plaintext
+ * @param {String|Buffer} assocData
+ * @param {String|Buffer} nonce
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, assocData, nonce, key) {
+ const ciphertext = Buffer.alloc(plaintext.length + 16, 0);
+ this.sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(
+ ciphertext,
+ await Util.toBuffer(plaintext),
+ await Util.toBuffer(assocData),
+ null,
+ await Util.toBuffer(nonce),
+ key.getBuffer()
+ );
+ return ciphertext;
+ }
+
+ /**
+ * @param {String|Buffer} message
+ * @param {CryptographyKey} key
+ * @return {Promise<buffer>}
+ */
+ async crypto_auth(message, key) {
+ const output = Buffer.alloc(32);
+ this.sodium.crypto_auth(
+ output,
+ await Util.toBuffer(message),
+ key.getBuffer()
+ );
+ return toBuffer(output);
+ }
+
+ /**
+ * @param {Buffer} mac
+ * @param {String|Buffer} message
+ * @param {CryptographyKey} key
+ * @return {Promise<boolean>}
+ */
+ async crypto_auth_verify(mac, message, key) {
+ return this.sodium.crypto_auth_verify(
+ mac,
+ await Util.toBuffer(message),
+ key.getBuffer()
+ );
+ }
+
+ /**
+ * @param {string|Buffer} plaintext
+ * @param {Buffer} nonce
+ * @param {CryptographyKey} sk
+ * @param {CryptographyKey} pk
+ * @return {Promise<Buffer>}
+ *
+ */
+ async crypto_box(plaintext, nonce, sk, pk) {
+ const ciphertext = Buffer.alloc(plaintext.length + 16);
+ this.sodium.crypto_box_easy(
+ ciphertext,
+ await Util.toBuffer(plaintext),
+ nonce,
+ pk.getBuffer(),
+ sk.getBuffer()
+ );
+ return Util.toBuffer(ciphertext);
+ }
+
+ /**
+ * @param {Buffer} ciphertext
+ * @param {Buffer} nonce
+ * @param {CryptographyKey} sk
+ * @param {CryptographyKey} pk
+ * @return {Promise<Buffer>}
+ */
+ async crypto_box_open(ciphertext, nonce, sk, pk) {
+ const plaintext = Buffer.alloc(ciphertext.length - 16);
+ const success = this.sodium.crypto_box_open_easy(
+ plaintext,
+ ciphertext,
+ nonce,
+ pk.getBuffer(),
+ sk.getBuffer()
+ );
+ if (!success) {
+ throw new SodiumError('Decryption failed');
+ }
+ return Util.toBuffer(plaintext);
+ }
+
+ /**
+ * @param {string|Buffer} plaintext
+ * @param {CryptographyKey} pk
+ * @return {Promise<Buffer>}
+ *
+ */
+ async crypto_box_seal(plaintext, pk) {
+ const ciphertext = Buffer.alloc(plaintext.length + 48);
+ this.sodium.crypto_box_seal(
+ ciphertext,
+ await Util.toBuffer(plaintext),
+ pk.getBuffer()
+ );
+ return Util.toBuffer(ciphertext);
+ }
+
+ /**
+ * @param {Buffer} ciphertext
+ * @param {CryptographyKey} pk
+ * @param {CryptographyKey} sk
+ * @return {Promise<Buffer>}
+ */
+ async crypto_box_seal_open(ciphertext, pk, sk) {
+ const plaintext = Buffer.alloc(ciphertext.length - 48);
+ const success = this.sodium.crypto_box_seal_open(
+ plaintext,
+ await Util.toBuffer(ciphertext),
+ pk.getBuffer(),
+ sk.getBuffer()
+ );
+ if (!success) {
+ throw new SodiumError('Decryption failed');
+ }
+ return Util.toBuffer(plaintext);
+ }
+
+ /**
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_box_keypair() {
+ const sK = Buffer.alloc(32, 0);
+ const pK = Buffer.alloc(32, 0);
+ this.sodium.crypto_box_keypair(sK, pK);
+ return new CryptographyKey(
+ Buffer.concat([pK, sK])
+ );
+ }
+
+ /**
+ * @param {string|Buffer} message
+ * @param {CryptographyKey|null} key
+ * @param {number} outputLength
+ * @return {Promise<Buffer>}
+ */
+ async crypto_generichash(message, key = null, outputLength = 32) {
+ const hash = Buffer.alloc(outputLength);
+ if (key) {
+ this.sodium.crypto_generichash(hash, await Util.toBuffer(message), key.getBuffer());
+ } else {
+ this.sodium.crypto_generichash(hash, await Util.toBuffer(message));
+ }
+ return hash;
+ }
+
+ /**
+ * @param {CryptographyKey|null} key
+ * @param {number} outputLength
+ * @return {Promise<Buffer>}
+ */
+ async crypto_generichash_init(key = null, outputLength = 32) {
+ const state = Buffer.alloc(this.CRYPTO_GENERICHASH_STATEBYTES);
+ if (key) {
+ this.sodium.crypto_generichash_init(state, key.getBuffer(), outputLength);
+ } else {
+ this.sodium.crypto_generichash_init(state, null, outputLength);
+ }
+ return state;
+ }
+
+ /**
+ * @param {*} state
+ * @param {string|Buffer} message
+ * @return {Promise<*>}
+ */
+ async crypto_generichash_update(state, message) {
+ this.sodium.crypto_generichash_update(state, await Util.toBuffer(message));
+ return state;
+ }
+
+ /**
+ * @param {*} state
+ * @param {number} outputLength
+ * @return {Promise<Buffer>}
+ */
+ async crypto_generichash_final(state, outputLength = 32) {
+ const output = Buffer.alloc(outputLength);
+ this.sodium.crypto_generichash_final(state, output);
+ return output;
+ }
+
+ /**
+ * @param {number} length
+ * @param {number} subKeyId
+ * @param {string|Buffer} context
+ * @param {CryptographyKey} key
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_kdf_derive_from_key(length, subKeyId, context, key) {
+ const subkey = Buffer.alloc(length, 0);
+ this.sodium.crypto_kdf_derive_from_key(
+ subkey,
+ subKeyId | 0,
+ await Util.toBuffer(context),
+ key.getBuffer()
+ );
+ return new CryptographyKey(subkey);
+ }
+
+ /**
+ * @param {X25519PublicKey} clientPublicKey
+ * @param {X25519SecretKey} clientSecretKey
+ * @param {X25519PublicKey} serverPublicKey
+ * @return {Promise<CryptographyKey[]>}
+ */
+ async crypto_kx_client_session_keys(clientPublicKey, clientSecretKey, serverPublicKey) {
+ const rx = Buffer.alloc(this.CRYPTO_KX_SESSIONKEYBYTES);
+ const tx = Buffer.alloc(this.CRYPTO_KX_SESSIONKEYBYTES);
+ this.sodium.crypto_kx_client_session_keys(
+ rx,
+ tx,
+ clientPublicKey.getBuffer(),
+ clientSecretKey.getBuffer(),
+ serverPublicKey.getBuffer(),
+ );
+ return [
+ new CryptographyKey(rx),
+ new CryptographyKey(tx)
+ ];
+ }
+
+ /**
+ * @param {X25519PublicKey} serverPublicKey
+ * @param {X25519SecretKey} serverSecretKey
+ * @param {X25519PublicKey} clientPublicKey
+ * @return {Promise<CryptographyKey[]>}
+ */
+ async crypto_kx_server_session_keys(serverPublicKey, serverSecretKey, clientPublicKey) {
+ const rx = Buffer.alloc(this.CRYPTO_KX_SESSIONKEYBYTES);
+ const tx = Buffer.alloc(this.CRYPTO_KX_SESSIONKEYBYTES);
+ this.sodium.crypto_kx_server_session_keys(
+ rx,
+ tx,
+ serverPublicKey.getBuffer(),
+ serverSecretKey.getBuffer(),
+ clientPublicKey.getBuffer(),
+ );
+ return [
+ new CryptographyKey(rx),
+ new CryptographyKey(tx)
+ ];
+ }
+
+ /**
+ * @param {string|Buffer} message
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_onetimeauth(message, key) {
+ const output = Buffer.alloc(16);
+ this.sodium.crypto_onetimeauth(
+ output,
+ await Util.toBuffer(message),
+ key.getBuffer()
+ );
+ return output;
+ }
+
+ /**
+ * @param {string|Buffer} message
+ * @param {CryptographyKey} key
+ * @param {Buffer} tag
+ * @return {Promise<boolean>}
+ */
+ async crypto_onetimeauth_verify(message, key, tag) {
+ return this.sodium.crypto_onetimeauth_verify(
+ tag,
+ await Util.toBuffer(message),
+ key.getBuffer()
+ );
+ }
+
+ /**
+ * @param {number} length
+ * @param {string|Buffer} password
+ * @param {Buffer} salt
+ * @param {number} opslimit
+ * @param {number} memlimit
+ * @param {number} algorithm
+ * @return {Promise<Buffer>}
+ */
+ async crypto_pwhash(length, password, salt, opslimit, memlimit, algorithm) {
+ const hashed = Buffer.alloc(length, 0);
+ const bufPass = await Util.toBuffer(password);
+ const bufSalt = await Util.toBuffer(salt);
+ await new Promise((resolve, reject) => {
+ this.sodium.crypto_pwhash_async(
+ hashed,
+ bufPass,
+ bufSalt,
+ opslimit,
+ memlimit,
+ algorithm,
+ (e, res) => {
+ if (e) return reject(e);
+ return resolve(res);
+ }
+ );
+ });
+ return hashed;
+ }
+
+ /**
+ * @param {string|Buffer} password
+ * @param {number} opslimit
+ * @param {number} memlimit
+ * @return {Promise<string>}
+ */
+ async crypto_pwhash_str(password, opslimit, memlimit) {
+ const hashed = Buffer.alloc(128, 0);
+ const bufPass = await Util.toBuffer(password);
+ await new Promise((resolve, reject) => {
+ this.sodium.crypto_pwhash_str_async(
+ hashed,
+ bufPass,
+ opslimit,
+ memlimit,
+ (e, res) => {
+ if (e) return reject(e);
+ return resolve(res);
+ }
+ );
+ });
+ return hashed.toString();
+
+ }
+
+ /**
+ * @param {string|Buffer} password
+ * @param {string|Buffer} hash
+ * @return {Promise<boolean>}
+ */
+ async crypto_pwhash_str_verify(password, hash) {
+ const allocated = Buffer.alloc(128, 0);
+ (await Util.toBuffer(hash)).copy(allocated, 0, 0);
+ const bufPass = await Util.toBuffer(password);
+ return new Promise((resolve, reject) => {
+ this.sodium.crypto_pwhash_str_verify_async(
+ allocated,
+ bufPass,
+ (e, res) => {
+ if (e) return reject(e);
+ return resolve(res);
+ }
+ );
+ });
+ }
+
+ /**
+ * @param {string|Buffer} hash
+ * @param {number} opslimit
+ * @param {number} memlimit
+ * @return {Promise<boolean>}
+ */
+ async crypto_pwhash_str_needs_rehash(hash, opslimit, memlimit) {
+ const allocated = Buffer.alloc(128, 0);
+ (await Util.toBuffer(hash)).copy(allocated, 0, 0);
+ return this.sodium.crypto_pwhash_str_needs_rehash(
+ allocated,
+ opslimit,
+ memlimit
+ );
+ }
+
+ /**
+ * @param {X25519SecretKey} secretKey
+ * @param {X25519PublicKey} publicKey
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_scalarmult(secretKey, publicKey) {
+ const shared = Buffer.alloc(32);
+ this.sodium.crypto_scalarmult(shared, secretKey.getBuffer(), publicKey.getBuffer());
+ return new CryptographyKey(
+ await Util.toBuffer(shared)
+ );
+ }
+
+ /**
+ *
+ * @param {CryptographyKey} secretKey
+ * @return {Promise<Buffer>}
+ */
+ async crypto_scalarmult_base(secretKey) {
+ const buf = Buffer.alloc(32);
+ this.sodium.crypto_scalarmult_base(buf, secretKey.getBuffer());
+ return buf;
+ }
+
+
+ /**
+ * @param {string|Buffer} plaintext
+ * @param {Buffer} nonce
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_secretbox(plaintext, nonce, key) {
+ const encrypted = Buffer.alloc(plaintext.length + 16);
+ this.sodium.crypto_secretbox_easy(
+ encrypted,
+ await Util.toBuffer(plaintext),
+ nonce,
+ key.getBuffer()
+ );
+ return encrypted;
+ }
+
+ /**
+ * @param {string|Buffer} message
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_shorthash(message, key) {
+ const output = Buffer.alloc(8);
+ this.sodium.crypto_shorthash(
+ output,
+ await Util.toBuffer(message),
+ key.getBuffer()
+ );
+ return output;
+ }
+
+ /**
+ * @param {Buffer} ciphertext
+ * @param {Buffer} nonce
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_secretbox_open(ciphertext, nonce, key) {
+ const decrypted = Buffer.alloc(ciphertext.length - 16);
+ if (!this.sodium.crypto_secretbox_open_easy(
+ decrypted,
+ ciphertext,
+ nonce,
+ key.getBuffer()
+ )) {
+ throw new SodiumError('Decryption failure');
+ }
+ return decrypted;
+ }
+
+ /**
+ * @param {CryptographyKey} key
+ * @return {Promise<array>} [state, header]
+ */
+ async crypto_secretstream_xchacha20poly1305_init_push(key) {
+ const state = Buffer.alloc(this.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_STATEBYTES);
+ const header = Buffer.alloc(this.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES);
+ this.sodium.randombytes_buf(header);
+ this.sodium.crypto_secretstream_xchacha20poly1305_init_push(state, header, key.getBuffer());
+ return [state, header];
+ }
+
+ /**
+ * @param {Buffer} header
+ * @param {CryptographyKey} key
+ * @return {Promise<*>} Returns the opaque state object
+ */
+ async crypto_secretstream_xchacha20poly1305_init_pull(header, key) {
+ if (header.length !== this.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES) {
+ throw new SodiumError(`Header must be ${this.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES} bytes long`);
+ }
+ const state = Buffer.alloc(this.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_STATEBYTES);
+ this.sodium.crypto_secretstream_xchacha20poly1305_init_pull(state, header, key.getBuffer());
+ return state;
+ }
+
+ /**
+ * @param {*} state
+ * @param {string|Buffer} message
+ * @param {string|Buffer} ad
+ * @param {number} tag
+ * @return {Promise<Buffer>}
+ */
+ async crypto_secretstream_xchacha20poly1305_push(state, message, ad = '', tag = 0) {
+ const ciphertext = Buffer.alloc(message.length + this.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES);
+ this.sodium.crypto_secretstream_xchacha20poly1305_push(
+ state,
+ ciphertext,
+ await Util.toBuffer(message),
+ ad.length > 0 ? (await Util.toBuffer(ad)) : null,
+ Buffer.from([tag])
+ );
+ return ciphertext;
+ }
+
+ /**
+ * @param {*} state
+ * @param {Buffer} ciphertext
+ * @param {string|Buffer} ad
+ * @param {number} tag
+ * @return {Promise<Buffer>}
+ */
+ async crypto_secretstream_xchacha20poly1305_pull(state, ciphertext, ad = '', tag = 0) {
+ if (ciphertext.length < this.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES) {
+ throw new SodiumError('Invalid ciphertext size');
+ }
+ const plaintext = Buffer.alloc(ciphertext.length - this.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES);
+ this.sodium.crypto_secretstream_xchacha20poly1305_pull(
+ state,
+ plaintext,
+ Buffer.from([tag]),
+ ciphertext,
+ ad.length > 0 ? (await Util.toBuffer(ad)) : null
+ );
+ return plaintext;
+ }
+
+ /**
+ * @param {*} state
+ * @return {Promise<void>}
+ */
+ async crypto_secretstream_xchacha20poly1305_rekey(state) {
+ this.sodium.crypto_secretstream_xchacha20poly1305_rekey(state);
+ }
+
+ /**
+ * @param {string|Buffer} message,
+ * @param {Ed25519SecretKey} secretKey
+ * @return {Promise<Buffer>}
+ */
+ async crypto_sign(message, secretKey) {
+ const signed = Buffer.alloc(message.length + 64);
+ this.sodium.crypto_sign(signed, await Util.toBuffer(message), secretKey.getBuffer());
+ return signed;
+ }
+
+ /**
+ * @param {Buffer} signedMessage,
+ * @param {Ed25519PublicKey} publicKey
+ * @return {Promise<Buffer>}
+ */
+ async crypto_sign_open(signedMessage, publicKey) {
+ const original = Buffer.alloc(signedMessage.length - 64);
+ this.sodium.crypto_sign_open(original, await Util.toBuffer(signedMessage), publicKey.getBuffer());
+ return original;
+ }
+
+ /**
+ * @param {string|Buffer} message,
+ * @param {Ed25519SecretKey} secretKey
+ * @return {Promise<Buffer>}
+ */
+ async crypto_sign_detached(message, secretKey) {
+ const signature = Buffer.alloc(64);
+ this.sodium.crypto_sign_detached(signature, await Util.toBuffer(message), secretKey.getBuffer());
+ return signature;
+ }
+
+ /**
+ * @param {string|Buffer} message,
+ * @param {Ed25519PublicKey} publicKey
+ * @param {Buffer} signature
+ * @return {Promise<Buffer>}
+ */
+ async crypto_sign_verify_detached(message, publicKey, signature) {
+ return this.sodium.crypto_sign_verify_detached(
+ signature,
+ await Util.toBuffer(message),
+ publicKey.getBuffer()
+ );
+ }
+
+ /**
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_sign_keypair() {
+ const sK = Buffer.alloc(64, 0);
+ const pK = Buffer.alloc(32, 0);
+ this.sodium.crypto_sign_keypair(pK, sK);
+ return new CryptographyKey(
+ Buffer.concat([sK, pK])
+ );
+ }
+
+ /**
+ * @param {Buffer} seed
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_sign_seed_keypair(seed) {
+ const sK = Buffer.alloc(64, 0);
+ const pK = Buffer.alloc(32, 0);
+ this.sodium.crypto_sign_seed_keypair(pK, sK, seed);
+ return new CryptographyKey(
+ Buffer.concat([sK, pK])
+ );
+ }
+
+ /**
+ * @param {Ed25519SecretKey} sk
+ * @return {Promise<Buffer>}
+ */
+ async crypto_sign_ed25519_sk_to_curve25519(sk) {
+ const xsk = Buffer.alloc(32);
+ this.sodium.crypto_sign_ed25519_sk_to_curve25519(xsk, sk.getBuffer());
+ return xsk;
+ }
+
+ /**
+ * @param {Ed25519PublicKey} pk
+ * @return {Promise<Buffer>}
+ */
+ async crypto_sign_ed25519_pk_to_curve25519(pk) {
+ const xpk = Buffer.alloc(32);
+ this.sodium.crypto_sign_ed25519_pk_to_curve25519(xpk, pk.getBuffer());
+ return xpk;
+ }
+
+ /**
+ * @param {number} length
+ * @param {Buffer} nonce
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_stream(length, nonce, key) {
+ const output = Buffer.alloc(length);
+ this.sodium.crypto_stream(
+ output,
+ await Util.toBuffer(nonce),
+ key.getBuffer()
+ );
+ return output;
+ }
+
+ /**
+ * @param {string|Buffer} plaintext
+ * @param {Buffer} nonce
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_stream_xor(plaintext, nonce, key) {
+ const output = Buffer.alloc(plaintext.length);
+ this.sodium.crypto_stream_xor(
+ output,
+ await Util.toBuffer(plaintext),
+ await Util.toBuffer(nonce),
+ key.getBuffer()
+ );
+ return output;
+ }
+
+ /**
+ * @param {number} number
+ * @return {Promise<Buffer>}
+ */
+ async randombytes_buf(number) {
+ let buf = Buffer.alloc(number);
+ this.sodium.randombytes_buf(buf);
+ return buf;
+ }
+
+ /**
+ * @param {number} upperBound
+ * @return {Promise<number>}
+ */
+ async randombytes_uniform(upperBound) {
+ return this.sodium.randombytes_uniform(upperBound);
+ }
+
+ /**
+ * @param {Uint8Array} val
+ * @param {Uint8Array} addv
+ * @return {Promise<Buffer>}
+ */
+ async sodium_add(val, addv) {
+ const buf = await Util.cloneBuffer(val);
+ this.sodium.sodium_add(buf, addv);
+ return buf;
+ }
+
+ /**
+ * @param {Buffer} input
+ * @return {Promise<string>}
+ */
+ async sodium_bin2hex(input) {
+ let str = "", b, c, x;
+ for (let i = 0; i < input.length; i++) {
+ c = input[i] & 0xf;
+ b = input[i] >>> 4;
+ x =
+ ((87 + c + (((c - 10) >> 8) & ~38)) << 8) |
+ (87 + b + (((b - 10) >> 8) & ~38));
+ str += String.fromCharCode(x & 0xff) + String.fromCharCode(x >>> 8);
+ }
+ return str;
+ }
+
+ /**
+ * @param {Buffer} b1
+ * @param {Buffer} b2
+ * @return {Promise<number>}
+ */
+ async sodium_compare(b1, b2) {
+ return this.sodium.sodium_compare(b1, b2);
+ }
+
+ /**
+ * @param {Buffer|string} hex
+ * @param {string|null} ignore
+ * @return {Promise<Buffer>}
+ */
+ async sodium_hex2bin(hex, ignore = null) {
+ let bin_pos = 0,
+ hex_pos = 0,
+ c = 0,
+ c_acc = 0,
+ c_alpha0 = 0,
+ c_alpha = 0,
+ c_num0 = 0,
+ c_num = 0,
+ c_val = 0,
+ state = 0;
+ const bin = Buffer.alloc(hex.length >> 1, 0);
+
+ while (hex_pos < hex.length) {
+ c = hex.charCodeAt(hex_pos);
+ c_num = c ^ 48;
+ c_num0 = (c_num - 10) >> 8;
+ c_alpha = (c & ~32) - 55;
+ c_alpha0 = ((c_alpha - 10) ^ (c_alpha - 16)) >> 8;
+ if ((c_num0 | c_alpha0) === 0) {
+ if (ignore && state === 0 && ignore.indexOf(c) >= 0) {
+ hex_pos++;
+ continue;
+ }
+ break;
+ }
+ c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha);
+ if (state === 0) {
+ c_acc = c_val * 16;
+ } else {
+ bin[bin_pos++] = c_acc | c_val;
+ }
+ state = ~state;
+ hex_pos++;
+ }
+ return bin;
+ }
+
+ /**
+ * @param {Buffer} buf
+ * @return {Promise<Buffer>}
+ */
+ async sodium_increment(buf) {
+ return this.sodium.sodium_increment(buf);
+ }
+
+ /**
+ * @param {Buffer} buf
+ * @param {number} len
+ * @return {Promise<Buffer>}
+ */
+ async sodium_is_zero(buf, len) {
+ return this.sodium.sodium_is_zero(buf, len);
+ }
+
+ /**
+ * @param {Buffer} b1
+ * @param {Buffer} b2
+ * @return {Promise<boolean>}
+ */
+ async sodium_memcmp(b1, b2) {
+ return this.sodium.sodium_memcmp(b1, b2);
+ }
+
+ /**
+ * @param {Buffer} buf
+ * @return {Promise<void>}
+ */
+ async sodium_memzero(buf) {
+ this.sodium.sodium_memzero(buf);
+ }
+
+ /**
+ * @param {string|Buffer} buf
+ * @param {number} blockSize
+ * @return {Promise<Buffer>}
+ */
+ async sodium_pad(buf, blockSize) {
+ buf = await Util.toBuffer(buf);
+ let length = buf.length + (buf.length % blockSize);
+ if (length < blockSize) {
+ length += blockSize;
+ }
+ const padded = Buffer.alloc(length + 100);
+ buf.copy(padded, 0, 0);
+ const sliceto = this.sodium.sodium_pad(padded, buf.length, blockSize);
+ return padded.slice(0, sliceto);
+ }
+
+ /**
+ *
+ * @param {string|Buffer} buf
+ * @param {number} blockSize
+ * @return {Promise<Buffer>}
+ */
+ async sodium_unpad(buf, blockSize) {
+ const outlen = this.sodium.sodium_unpad(buf, buf.length, blockSize);
+ return buf.slice(0, outlen);
+ }
+};
diff --git a/library/sodium-plus/lib/cryptography-key.js b/library/sodium-plus/lib/cryptography-key.js
new file mode 100644
index 000000000..8aed3a786
--- /dev/null
+++ b/library/sodium-plus/lib/cryptography-key.js
@@ -0,0 +1,80 @@
+"use strict";
+
+/* istanbul ignore if */
+if (typeof (Buffer) === 'undefined') {
+ let Buffer = require('buffer/').Buffer;
+}
+module.exports = class CryptographyKey {
+ /**
+ * Note: We use Object.defineProperty() to hide the buffer inside of the
+ * CryptographyKey object to prevent accidental leaks.
+ *
+ * @param {Buffer} buf
+ */
+ constructor(buf) {
+ if (!Buffer.isBuffer(buf)) {
+ throw new TypeError('Argument 1 must be an instance of Buffer.');
+ }
+ Object.defineProperty(this, 'buffer', {
+ enumerable: false,
+ value: buf.slice()
+ });
+ }
+
+ /**
+ * @return {CryptographyKey}
+ */
+ static from() {
+ return new CryptographyKey(Buffer.from(...arguments));
+ }
+
+ /**
+ * @return {boolean}
+ */
+ isEd25519Key() {
+ return false;
+ }
+
+ /**
+ * @return {boolean}
+ */
+ isX25519Key() {
+ return false;
+ }
+
+ /**
+ * @return {boolean}
+ */
+ isPublicKey() {
+ return false;
+ }
+
+ /**
+ * @return {Number}
+ */
+ getLength() {
+ return this.buffer.length;
+ }
+
+ /**
+ * @return {Buffer}
+ */
+ getBuffer() {
+ return this.buffer;
+ }
+
+ /**
+ * @param {string} encoding
+ */
+ toString(encoding = 'utf-8') {
+ /* istanbul ignore if */
+ return this.getBuffer().toString(encoding);
+ }
+
+ /**
+ * @return {Buffer}
+ */
+ slice() {
+ return this.buffer.slice(...arguments);
+ }
+};
diff --git a/library/sodium-plus/lib/keytypes/ed25519pk.js b/library/sodium-plus/lib/keytypes/ed25519pk.js
new file mode 100644
index 000000000..4c6dd911e
--- /dev/null
+++ b/library/sodium-plus/lib/keytypes/ed25519pk.js
@@ -0,0 +1,28 @@
+const CryptographyKey = require('../cryptography-key');
+
+class Ed25519PublicKey extends CryptographyKey {
+ constructor(buf) {
+ if (buf.length !== 32) {
+ throw new Error('Ed25519 public keys must be 32 bytes long');
+ }
+ super(buf);
+ this.keyType = 'ed25519';
+ this.publicKey = true;
+ }
+ /**
+ * @return {Ed25519PublicKey}
+ */
+ static from() {
+ return new Ed25519PublicKey(Buffer.from(...arguments));
+ }
+
+ isEd25519Key() {
+ return true;
+ }
+
+ isPublicKey() {
+ return true;
+ }
+}
+
+module.exports = Ed25519PublicKey;
diff --git a/library/sodium-plus/lib/keytypes/ed25519sk.js b/library/sodium-plus/lib/keytypes/ed25519sk.js
new file mode 100644
index 000000000..3c67286c2
--- /dev/null
+++ b/library/sodium-plus/lib/keytypes/ed25519sk.js
@@ -0,0 +1,29 @@
+const CryptographyKey = require('../cryptography-key');
+
+class Ed25519SecretKey extends CryptographyKey {
+ constructor(buf) {
+ if (buf.length !== 64) {
+ throw new Error('Ed25519 secret keys must be 64 bytes long');
+ }
+ super(buf);
+ this.keyType = 'ed25519';
+ this.publicKey = false;
+ }
+
+ /**
+ * @return {Ed25519SecretKey}
+ */
+ static from() {
+ return new Ed25519SecretKey(Buffer.from(...arguments));
+ }
+
+ isEd25519Key() {
+ return true;
+ }
+
+ isPublicKey() {
+ return false;
+ }
+}
+
+module.exports = Ed25519SecretKey; \ No newline at end of file
diff --git a/library/sodium-plus/lib/keytypes/x25519pk.js b/library/sodium-plus/lib/keytypes/x25519pk.js
new file mode 100644
index 000000000..322c899f8
--- /dev/null
+++ b/library/sodium-plus/lib/keytypes/x25519pk.js
@@ -0,0 +1,29 @@
+const CryptographyKey = require('../cryptography-key');
+
+class X25519PublicKey extends CryptographyKey {
+ constructor(buf) {
+ if (buf.length !== 32) {
+ throw new Error('X25519 public keys must be 32 bytes long');
+ }
+ super(buf);
+ this.keyType = 'x25519';
+ this.publicKey = true;
+ }
+
+ /**
+ * @return {X25519PublicKey}
+ */
+ static from() {
+ return new X25519PublicKey(Buffer.from(...arguments));
+ }
+
+ isX25519Key() {
+ return true;
+ }
+
+ isPublicKey() {
+ return true;
+ }
+}
+
+module.exports = X25519PublicKey;
diff --git a/library/sodium-plus/lib/keytypes/x25519sk.js b/library/sodium-plus/lib/keytypes/x25519sk.js
new file mode 100644
index 000000000..8b8016f83
--- /dev/null
+++ b/library/sodium-plus/lib/keytypes/x25519sk.js
@@ -0,0 +1,29 @@
+const CryptographyKey = require('../cryptography-key');
+
+class X25519SecretKey extends CryptographyKey {
+ constructor(buf) {
+ if (buf.length !== 32) {
+ throw new Error('X25519 secret keys must be 32 bytes long');
+ }
+ super(buf);
+ this.keyType = 'x25519';
+ this.publicKey = false;
+ }
+
+ /**
+ * @return {X25519SecretKey}
+ */
+ static from() {
+ return new X25519SecretKey(Buffer.from(...arguments));
+ }
+
+ isX25519Key() {
+ return true;
+ }
+
+ isPublicKey() {
+ return false;
+ }
+}
+
+module.exports = X25519SecretKey;
diff --git a/library/sodium-plus/lib/polyfill.js b/library/sodium-plus/lib/polyfill.js
new file mode 100644
index 000000000..faf6d75e5
--- /dev/null
+++ b/library/sodium-plus/lib/polyfill.js
@@ -0,0 +1,74 @@
+"use strict";
+const crypto = require('crypto');
+const Poly1305 = require('poly1305-js');
+const Util = require('./util');
+const XSalsa20 = require('xsalsa20');
+
+/* istanbul ignore if */
+if (typeof (Buffer) === 'undefined') {
+ let Buffer = require('buffer/').Buffer;
+}
+
+module.exports = class SodiumPolyfill {
+
+ /**
+ * @param {string|Buffer} message
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ static async crypto_onetimeauth(message, key) {
+ return Poly1305.onetimeauth(
+ await Util.toBuffer(message),
+ key.getBuffer()
+ );
+ }
+
+ /**
+ * @param {string|Buffer} message
+ * @param {CryptographyKey} key
+ * @param {Buffer} tag
+ * @return {Promise<boolean>}
+ */
+ static async crypto_onetimeauth_verify(message, key, tag) {
+ return Poly1305.onetimeauth_verify(
+ await Util.toBuffer(message),
+ key.getBuffer(),
+ await Util.toBuffer(tag)
+ );
+ }
+
+ /**
+ * @param {string|Buffer} plaintext
+ * @param {Buffer} nonce
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ static async crypto_stream_xor(plaintext, nonce, key) {
+ const stream = XSalsa20(nonce, key.getBuffer());
+ const output = stream.update(plaintext);
+ stream.finalize();
+ return Util.toBuffer(output);
+ }
+
+ /**
+ * Polyfill crypto_pwhash_str_needs_rehash() for bindings that don't
+ * include this (somewhat new) helper function.
+ *
+ * @param {string|Buffer} hash
+ * @param {number} opslimit
+ * @param {number} memlimit
+ * @return {Promise<boolean>}
+ */
+ static async crypto_pwhash_str_needs_rehash(hash, opslimit, memlimit) {
+ const pwhash = (await Util.toBuffer(hash)).toString('utf-8');
+ const pieces = pwhash.split('$');
+ const expect = 'm=' + (memlimit >> 10) + ',t=' + opslimit + ',p=1';
+ if (expect.length !== pieces[3].length) {
+ return true;
+ }
+ return !crypto.timingSafeEqual(
+ await Util.toBuffer(expect),
+ await Util.toBuffer(pieces[3])
+ );
+ }
+};
diff --git a/library/sodium-plus/lib/sodium-error.js b/library/sodium-plus/lib/sodium-error.js
new file mode 100644
index 000000000..a21b13b71
--- /dev/null
+++ b/library/sodium-plus/lib/sodium-error.js
@@ -0,0 +1 @@
+module.exports = class SodiumError extends Error {};
diff --git a/library/sodium-plus/lib/sodiumplus.js b/library/sodium-plus/lib/sodiumplus.js
new file mode 100644
index 000000000..7f5bc4c08
--- /dev/null
+++ b/library/sodium-plus/lib/sodiumplus.js
@@ -0,0 +1,1212 @@
+const Backend = require('./backend');
+const CryptographyKey = require('./cryptography-key');
+const Ed25519SecretKey = require('./keytypes/ed25519sk');
+const Ed25519PublicKey = require('./keytypes/ed25519pk');
+const LibsodiumWrappersBackend = require('./backend/libsodium-wrappers');
+const SodiumError = require('./sodium-error');
+const SodiumNativeBackend = require('./backend/sodiumnative');
+const X25519PublicKey = require('./keytypes/x25519pk');
+const X25519SecretKey = require('./keytypes/x25519sk');
+const Util = require('./util');
+
+/* istanbul ignore if */
+if (typeof (Buffer) === 'undefined') {
+ let Buffer = require('buffer/').Buffer;
+}
+
+class SodiumPlus {
+ constructor(backend) {
+ /* istanbul ignore if */
+ if (!(backend instanceof Backend)) {
+ throw new TypeError('Backend object must implement the backend function');
+ }
+ this.backend = backend;
+ Util.populateConstants(this);
+ }
+
+ /**
+ * Returns the name of the current active backend.
+ * This method is NOT async.
+ *
+ * @return {string}
+ */
+ getBackendName() {
+ return this.backend.backendName;
+ }
+
+ /**
+ * Is this powered by sodium-native?
+ * This method is NOT async.
+ *
+ * @return {boolean}
+ */
+ isSodiumNative() {
+ return (this.backend instanceof SodiumNativeBackend);
+ }
+
+ /**
+ * Is this powered by libsodium-wrappers?
+ * This method is NOT async.
+ *
+ * @return {boolean}
+ */
+ isLibsodiumWrappers() {
+ return (this.backend instanceof LibsodiumWrappersBackend);
+ }
+
+ /**
+ * Automatically select a backend.
+ *
+ * @return {Promise<SodiumPlus>}
+ */
+ static async auto() {
+ let backend;
+ try {
+ backend = await SodiumNativeBackend.init();
+ } catch (e) {
+ backend = await LibsodiumWrappersBackend.init();
+ }
+ /* istanbul ignore if */
+ if (!backend) {
+ backend = await LibsodiumWrappersBackend.init();
+ }
+ Util.populateConstants(backend);
+ return new SodiumPlus(backend);
+ }
+
+ /**
+ * If our backend isn't defined, it will trigger an autoload.
+ *
+ * Mostly used internally. `await SodiumPlus.auto()` provides the same
+ * exact guarantee as this method.
+ *
+ * @return {Promise<void>}
+ */
+ async ensureLoaded() {
+ /* istanbul ignore if */
+ if (typeof (this.backend) === 'undefined') {
+ try {
+ await SodiumPlus.auto();
+ } catch (e) {
+ this.backend = await LibsodiumWrappersBackend.init();
+ }
+ }
+ }
+
+ /**
+ * Decrypt a message (and optional associated data) with XChaCha20-Poly1305
+ *
+ * @param {String|Buffer} ciphertext
+ * @param {String|Buffer} nonce
+ * @param {CryptographyKey} key
+ * @param {String|Buffer} assocData
+ * @return {Promise<Buffer>}
+ * @throws {SodiumError}
+ */
+ async crypto_aead_xchacha20poly1305_ietf_decrypt(ciphertext, nonce, key, assocData = '') {
+ await this.ensureLoaded();
+ if (nonce.length !== 24) {
+ throw new SodiumError('Argument 2 must be 24 bytes');
+ }
+ if (!(key instanceof CryptographyKey)) {
+ throw new TypeError('Argument 3 must be an instance of CryptographyKey');
+ }
+ return await this.backend.crypto_aead_xchacha20poly1305_ietf_decrypt(
+ await Util.toBuffer(ciphertext),
+ assocData.length > 0 ? await Util.toBuffer(assocData) : null,
+ await Util.toBuffer(nonce),
+ key
+ );
+ }
+
+ /**
+ * Encrypt a message (and optional associated data) with XChaCha20-Poly1305.
+ *
+ * Throws a SodiumError if an invalid ciphertext/AAD is provided for this
+ * nonce and key.
+ *
+ * @param {String|Buffer} plaintext
+ * @param {String|Buffer} nonce
+ * @param {CryptographyKey} key
+ * @param {String|Buffer} assocData
+ * @return {Promise<Buffer>}
+ * @throws {SodiumError}
+ */
+ async crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, nonce, key, assocData = '') {
+ await this.ensureLoaded();
+ if (nonce.length !== 24) {
+ throw new SodiumError('Argument 2 must be 24 bytes');
+ }
+ if (!(key instanceof CryptographyKey)) {
+ throw new TypeError('Argument 3 must be an instance of CryptographyKey');
+ }
+
+ return await this.backend.crypto_aead_xchacha20poly1305_ietf_encrypt(
+ await Util.toBuffer(plaintext),
+ assocData.length > 0 ? await Util.toBuffer(assocData) : null,
+ await Util.toBuffer(nonce),
+ key
+ );
+ }
+
+ /**
+ * Generate an XChaCha20-Poly1305 key.
+ *
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_aead_xchacha20poly1305_ietf_keygen() {
+ return new CryptographyKey(await this.backend.randombytes_buf(32));
+ }
+
+ /**
+ * Get an authenticator for a message for a given key.
+ *
+ * Algorithm: HMAC-SHA512 truncated to 32 bytes.
+ *
+ * @param {string|Buffer} message
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_auth(message, key) {
+ await this.ensureLoaded();
+ if (!(key instanceof CryptographyKey)) {
+ throw new TypeError('Argument 2 must be an instance of CryptographyKey');
+ }
+ await this.ensureLoaded();
+ return await this.backend.crypto_auth(
+ await Util.toBuffer(message),
+ key
+ );
+ }
+
+ /**
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_auth_keygen() {
+ return new CryptographyKey(await this.backend.randombytes_buf(32));
+ }
+
+ /**
+ * Verify an authenticator for a message for a given key.
+ *
+ * @param {string|Buffer} message
+ * @param {CryptographyKey} key
+ * @param {Buffer} mac
+ * @return {Promise<boolean>}
+ */
+ async crypto_auth_verify(message, key, mac) {
+ await this.ensureLoaded();
+ if (!(key instanceof CryptographyKey)) {
+ throw new TypeError('Argument 2 must be an instance of CryptographyKey');
+ }
+ await this.ensureLoaded();
+ return await this.backend.crypto_auth_verify(
+ await Util.toBuffer(mac),
+ await Util.toBuffer(message),
+ key
+ );
+ }
+
+ /**
+ * Public-key authenticated encryption.
+ *
+ * @param {string|Buffer} plaintext
+ * @param {Buffer} nonce
+ * @param {X25519SecretKey} myPrivateKey
+ * @param {X25519PublicKey} theirPublicKey
+ * @return {Promise<Buffer>}
+ */
+ async crypto_box(plaintext, nonce, myPrivateKey, theirPublicKey) {
+ await this.ensureLoaded();
+ if (!(myPrivateKey instanceof X25519SecretKey)) {
+ throw new TypeError('Argument 3 must be an instance of X25519SecretKey');
+ }
+ if (!(theirPublicKey instanceof X25519PublicKey)) {
+ throw new TypeError('Argument 4 must be an instance of X25519PublicKey');
+ }
+ nonce = await Util.toBuffer(nonce);
+ if (nonce.length !== 24) {
+ throw new SodiumError('Nonce must be a buffer of exactly 24 bytes');
+ }
+ return this.backend.crypto_box(
+ await Util.toBuffer(plaintext),
+ await Util.toBuffer(nonce),
+ myPrivateKey,
+ theirPublicKey
+ );
+ }
+
+ /**
+ * Public-key authenticated decryption.
+ *
+ * @param {Buffer} ciphertext
+ * @param {Buffer} nonce
+ * @param {X25519SecretKey} myPrivateKey
+ * @param {X25519PublicKey} theirPublicKey
+ * @return {Promise<Buffer>}
+ */
+ async crypto_box_open(ciphertext, nonce, myPrivateKey, theirPublicKey) {
+ await this.ensureLoaded();
+ if (!(myPrivateKey instanceof X25519SecretKey)) {
+ throw new TypeError('Argument 3 must be an instance of X25519SecretKey');
+ }
+ if (!(theirPublicKey instanceof X25519PublicKey)) {
+ throw new TypeError('Argument 4 must be an instance of X25519PublicKey');
+ }
+ ciphertext = await Util.toBuffer(ciphertext);
+ if (ciphertext.length < 16) {
+ throw new SodiumError('Ciphertext must be a buffer of at least 16 bytes');
+ }
+ nonce = await Util.toBuffer(nonce);
+ if (nonce.length !== 24) {
+ throw new SodiumError('Nonce must be a buffer of exactly 24 bytes');
+ }
+ return this.backend.crypto_box_open(
+ ciphertext,
+ nonce,
+ myPrivateKey,
+ theirPublicKey
+ );
+ }
+
+ /**
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_box_keypair() {
+ await this.ensureLoaded();
+ return this.backend.crypto_box_keypair();
+ }
+
+ /**
+ * Combine two X25519 keys (secret, public) into a keypair object.
+ *
+ * @param {X25519SecretKey} sKey
+ * @param {X25519PublicKey} pKey
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_box_keypair_from_secretkey_and_publickey(sKey, pKey) {
+ await this.ensureLoaded();
+ if (!(sKey instanceof X25519SecretKey)) {
+ throw new TypeError('Argument 1 must be an instance of X25519SecretKey');
+ }
+ if (!(pKey instanceof X25519PublicKey)) {
+ throw new TypeError('Argument 2 must be an instance of X25519PublicKey');
+ }
+ return await this.backend.crypto_box_keypair_from_secretkey_and_publickey(sKey, pKey);
+ }
+
+ /**
+ * Extract the secret key from an X25519 keypair object.
+ *
+ * @param {CryptographyKey} keypair
+ * @return {Promise<X25519SecretKey>}
+ */
+ async crypto_box_secretkey(keypair) {
+ if (keypair.getLength()!== 64) {
+ throw new SodiumError('Keypair must be 64 bytes');
+ }
+ return new X25519SecretKey(
+ Buffer.from(keypair.getBuffer().slice(0, 32))
+ );
+ }
+
+ /**
+ * Extract the public key from an X25519 keypair object.
+ *
+ * @param {CryptographyKey} keypair
+ * @return {Promise<X25519PublicKey>}
+ */
+ async crypto_box_publickey(keypair) {
+ if (keypair.getLength() !== 64) {
+ throw new SodiumError('Keypair must be 64 bytes');
+ }
+ return new X25519PublicKey(
+ Buffer.from(keypair.getBuffer().slice(32, 64))
+ );
+ }
+
+ /**
+ * Derive the public key from a given X25519 secret key.
+ *
+ * @param {X25519SecretKey} secretKey
+ * @return {Promise<X25519PublicKey>}
+ */
+ async crypto_box_publickey_from_secretkey(secretKey) {
+ await this.ensureLoaded();
+ if (!(secretKey instanceof X25519SecretKey)) {
+ throw new TypeError('Argument 1 must be an instance of X25519SecretKey');
+ }
+ return new X25519PublicKey(
+ await this.backend.crypto_scalarmult_base(secretKey)
+ );
+ }
+
+ /**
+ * Anonymous public-key encryption. (Message integrity is still assured.)
+ *
+ * @param {string|Buffer} plaintext
+ * @param {X25519PublicKey} publicKey
+ * @return {Promise<Buffer>}
+ */
+ async crypto_box_seal(plaintext, publicKey) {
+ await this.ensureLoaded();
+ if (!(publicKey instanceof X25519PublicKey)) {
+ throw new TypeError('Argument 2 must be an instance of X25519PublicKey');
+ }
+ return await this.backend.crypto_box_seal(plaintext, publicKey);
+ }
+
+ /**
+ * Anonymous public-key decryption. (Message integrity is still assured.)
+ *
+ * @param {Buffer} ciphertext
+ * @param {X25519PublicKey} publicKey
+ * @param {X25519SecretKey} secretKey
+ * @return {Promise<Buffer>}
+ */
+ async crypto_box_seal_open(ciphertext, publicKey, secretKey) {
+ await this.ensureLoaded();
+ if (!(publicKey instanceof X25519PublicKey)) {
+ throw new TypeError('Argument 2 must be an instance of X25519PublicKey');
+ }
+ if (!(secretKey instanceof X25519SecretKey)) {
+ throw new TypeError('Argument 3 must be an instance of X25519SecretKey');
+ }
+ return await this.backend.crypto_box_seal_open(
+ await Util.toBuffer(ciphertext),
+ publicKey,
+ secretKey
+ );
+ }
+
+ /**
+ * Generic-purpose cryptographic hash.
+ *
+ * @param {string|Buffer} message
+ * @param {CryptographyKey|null} key
+ * @param {number} outputLength
+ * @return {Promise<Buffer>}
+ */
+ async crypto_generichash(message, key = null, outputLength = 32) {
+ await this.ensureLoaded();
+ return await this.backend.crypto_generichash(message, key, outputLength);
+ }
+
+ /**
+ * Initialize a BLAKE2 hash context for stream hashing.
+ *
+ * @param {CryptographyKey|null} key
+ * @param {number} outputLength
+ * @return {Promise<Buffer>}
+ */
+ async crypto_generichash_init(key = null, outputLength = 32) {
+ await this.ensureLoaded();
+ return await this.backend.crypto_generichash_init(key, outputLength);
+ }
+
+
+ /**
+ * Update the BLAKE2 hash state with a block of data.
+ *
+ * @param {*} state
+ * @param {string|Buffer} message
+ * @return {Promise<*>}
+ */
+ async crypto_generichash_update(state, message) {
+ await this.ensureLoaded();
+ return await this.backend.crypto_generichash_update(state, message);
+ }
+
+ /**
+ * Obtain the final BLAKE2 hash output.
+ *
+ * @param {*} state
+ * @param {number} outputLength
+ * @return {Promise<Buffer>}
+ */
+ async crypto_generichash_final(state, outputLength = 32) {
+ await this.ensureLoaded();
+ return await this.backend.crypto_generichash_final(state, outputLength);
+ }
+
+ /**
+ * Generate a 256-bit random key for BLAKE2.
+ *
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_generichash_keygen() {
+ return new CryptographyKey(
+ await this.backend.randombytes_buf(this.CRYPTO_GENERICHASH_KEYBYTES)
+ );
+ }
+
+ /**
+ * Derive a subkey from a master key.
+ *
+ * @param {number} length
+ * @param {number} subKeyId
+ * @param {string|Buffer} context
+ * @param {CryptographyKey} key
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_kdf_derive_from_key(length, subKeyId, context, key) {
+ await this.ensureLoaded();
+ if (length < 1) {
+ throw new SodiumError('Length must be a positive integer.');
+ }
+ if (subKeyId < 0) {
+ throw new SodiumError('Key ID must be an unsigned integer');
+ }
+ return await this.backend.crypto_kdf_derive_from_key(
+ length,
+ subKeyId,
+ context,
+ key
+ );
+ }
+
+ /**
+ * Generate a 256-bit random key for our KDF.
+ *
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_kdf_keygen() {
+ return new CryptographyKey(
+ await this.backend.randombytes_buf(this.CRYPTO_KDF_KEYBYTES)
+ );
+ }
+
+ /**
+ * This is functionally identical to crypto_box_keypair().
+ *
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_kx_keypair() {
+ return this.crypto_box_keypair();
+ }
+
+ /**
+ * Generate an X25519 keypair from a seed.
+ *
+ * @param {string|Buffer} seed
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_kx_seed_keypair(seed) {
+ await this.ensureLoaded();
+ const sk = await this.backend.crypto_generichash(seed, null, this.CRYPTO_KX_SECRETKEYBYTES);
+ const pk = await this.backend.crypto_scalarmult_base(new CryptographyKey(sk));
+ return new CryptographyKey(Buffer.concat([sk, pk]));
+ }
+
+ /**
+ * Perform a key exchange from the client's perspective.
+ *
+ * Returns an array of two CryptographyKey objects.
+ *
+ * The first is meant for data sent from the server to the client (incoming decryption).
+ * The second is meant for data sent from the client to the server (outgoing encryption).
+ *
+ * @param {X25519PublicKey} clientPublicKey
+ * @param {X25519SecretKey} clientSecretKey
+ * @param {X25519PublicKey} serverPublicKey
+ * @return {Promise<CryptographyKey[]>}
+ */
+ async crypto_kx_client_session_keys(clientPublicKey, clientSecretKey, serverPublicKey) {
+ await this.ensureLoaded();
+ if (!(clientPublicKey instanceof X25519PublicKey)) {
+ throw new TypeError('Argument 1 must be an instance of X25519PublicKey');
+ }
+ if (!(clientSecretKey instanceof X25519SecretKey)) {
+ throw new TypeError('Argument 2 must be an instance of X25519SecretKey');
+ }
+ if (!(serverPublicKey instanceof X25519PublicKey)) {
+ throw new TypeError('Argument 3 must be an instance of X25519PublicKey');
+ }
+ return this.backend.crypto_kx_client_session_keys(clientPublicKey, clientSecretKey, serverPublicKey);
+ }
+
+ /**
+ * Perform a key exchange from the server's perspective.
+ *
+ * Returns an array of two CryptographyKey objects.
+ *
+ * The first is meant for data sent from the client to the server (incoming decryption).
+ * The second is meant for data sent from the server to the client (outgoing encryption).
+ *
+ * @param {X25519PublicKey} serverPublicKey
+ * @param {X25519SecretKey} serverSecretKey
+ * @param {X25519PublicKey} clientPublicKey
+ * @return {Promise<CryptographyKey[]>}
+ */
+ async crypto_kx_server_session_keys(serverPublicKey, serverSecretKey, clientPublicKey) {
+ await this.ensureLoaded();
+ if (!(serverPublicKey instanceof X25519PublicKey)) {
+ throw new TypeError('Argument 1 must be an instance of X25519PublicKey');
+ }
+ if (!(serverSecretKey instanceof X25519SecretKey)) {
+ throw new TypeError('Argument 2 must be an instance of X25519SecretKey');
+ }
+ if (!(clientPublicKey instanceof X25519PublicKey)) {
+ throw new TypeError('Argument 3 must be an instance of X25519PublicKey');
+ }
+ return this.backend.crypto_kx_server_session_keys(serverPublicKey, serverSecretKey, clientPublicKey);
+ }
+
+ /**
+ * @param {string|Buffer} message
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_onetimeauth(message, key) {
+ if (!(key instanceof CryptographyKey)) {
+ throw new TypeError('Argument 2 must be an instance of CryptographyKey');
+ }
+ return await this.backend.crypto_onetimeauth(await Util.toBuffer(message), key);
+ }
+
+ /**
+ * @param {string|Buffer} message
+ * @param {CryptographyKey} key
+ * @param {Buffer} tag
+ * @return {Promise<boolean>}
+ */
+ async crypto_onetimeauth_verify(message, key, tag) {
+ if (!(key instanceof CryptographyKey)) {
+ throw new TypeError('Argument 2 must be an instance of CryptographyKey');
+ }
+ return await this.backend.crypto_onetimeauth_verify(
+ await Util.toBuffer(message),
+ key,
+ await Util.toBuffer(tag)
+ );
+ }
+
+ /**
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_onetimeauth_keygen() {
+ return new CryptographyKey(
+ await this.backend.randombytes_buf(32)
+ );
+ }
+
+ /**
+ * Derive a cryptography key from a password and salt.
+ *
+ * @param {number} length
+ * @param {string|Buffer} password
+ * @param {Buffer} salt
+ * @param {number} opslimit
+ * @param {number} memlimit
+ * @param {number|null} algorithm
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_pwhash(length, password, salt, opslimit, memlimit, algorithm = null) {
+ await this.ensureLoaded();
+ /* istanbul ignore if */
+ if (!algorithm) {
+ algorithm = this.CRYPTO_PWHASH_ALG_DEFAULT;
+ }
+ return new CryptographyKey(
+ await this.backend.crypto_pwhash(
+ length,
+ await Util.toBuffer(password),
+ await Util.toBuffer(salt),
+ opslimit,
+ memlimit,
+ algorithm
+ )
+ );
+ }
+
+ /**
+ * Get a password hash (in a safe-for-storage format)
+ *
+ * @param {string|Buffer} password
+ * @param {number} opslimit
+ * @param {number} memlimit
+ * @return {Promise<string>}
+ */
+ async crypto_pwhash_str(password, opslimit, memlimit) {
+ await this.ensureLoaded();
+ return await this.backend.crypto_pwhash_str(password, opslimit, memlimit);
+ }
+
+ /**
+ * Verify a password against a known password hash
+ *
+ * @param {string|Buffer} password
+ * @param {string|Buffer} hash
+ * @return {Promise<boolean>}
+ */
+ async crypto_pwhash_str_verify(password, hash) {
+ await this.ensureLoaded();
+ return await this.backend.crypto_pwhash_str_verify(password, hash);
+ }
+
+ /**
+ * Does this password need to be rehashed?
+ *
+ * @param {string|Buffer} hash
+ * @param {number} opslimit
+ * @param {number} memlimit
+ * @return {Promise<boolean>}
+ */
+ async crypto_pwhash_str_needs_rehash(hash, opslimit, memlimit) {
+ await this.ensureLoaded();
+ return await this.backend.crypto_pwhash_str_needs_rehash(hash, opslimit, memlimit);
+ }
+
+ /**
+ * Elliptic Curve Diffie-Hellman key exchange
+ *
+ * @param {X25519SecretKey} secretKey
+ * @param {X25519PublicKey} publicKey
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_scalarmult(secretKey, publicKey) {
+ await this.ensureLoaded();
+ if (!(secretKey instanceof X25519SecretKey)) {
+ throw new TypeError('Argument 1 must be an instance of X25519SecretKey');
+ }
+ if (!(publicKey instanceof X25519PublicKey)) {
+ throw new TypeError('Argument 2 must be an instance of X25519PublicKey');
+ }
+ return await this.backend.crypto_scalarmult(secretKey, publicKey);
+ }
+
+ /**
+ * Generate an X25519PublicKey from an X25519SecretKey
+ *
+ * @param {X25519SecretKey} secretKey
+ * @return {Promise<X25519PublicKey>}
+ */
+ async crypto_scalarmult_base(secretKey) {
+ await this.ensureLoaded();
+ if (!(secretKey instanceof X25519SecretKey)) {
+ throw new TypeError('Argument 1 must be an instance of X25519SecretKey');
+ }
+ return new X25519PublicKey(
+ await this.backend.crypto_scalarmult_base(secretKey)
+ );
+ }
+
+ /**
+ * Shared-key authenticated encryption
+ *
+ * @param {string|Buffer} plaintext
+ * @param {Buffer} nonce
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_secretbox(plaintext, nonce, key) {
+ await this.ensureLoaded();
+ if (key.isEd25519Key() || key.isX25519Key()) {
+ throw new TypeError('Argument 3 must not be an asymmetric key');
+ }
+ nonce = await Util.toBuffer(nonce);
+ if (nonce.length !== 24) {
+ throw new SodiumError('Nonce must be a buffer of exactly 24 bytes');
+ }
+
+ return await this.backend.crypto_secretbox(
+ plaintext,
+ nonce,
+ key
+ );
+ }
+
+ /**
+ * Shared-key authenticated decryption
+ *
+ * @param {Buffer} ciphertext
+ * @param {Buffer} nonce
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_secretbox_open(ciphertext, nonce, key) {
+ await this.ensureLoaded();
+ if (key.isEd25519Key() || key.isX25519Key()) {
+ throw new TypeError('Argument 3 must not be an asymmetric key');
+ }
+ ciphertext = await Util.toBuffer(ciphertext);
+ if (ciphertext.length < 16) {
+ throw new SodiumError('Ciphertext must be a buffer of at least 16 bytes');
+ }
+ nonce = await Util.toBuffer(nonce);
+ if (nonce.length !== 24) {
+ throw new SodiumError('Nonce must be a buffer of exactly 24 bytes');
+ }
+ return await this.backend.crypto_secretbox_open(
+ ciphertext,
+ nonce,
+ key
+ );
+ }
+
+ /**
+ * Generate a key for shared-key authenticated encryption.
+ *
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_secretbox_keygen() {
+ return new CryptographyKey(
+ await this.backend.randombytes_buf(this.CRYPTO_SECRETBOX_KEYBYTES)
+ );
+ }
+
+ /**
+ * Internalize the internal state and a random header for stream encryption.
+ *
+ * @param {CryptographyKey} key
+ * @return {Promise<array>}
+ */
+ async crypto_secretstream_xchacha20poly1305_init_push(key) {
+ await this.ensureLoaded();
+ if (!(key instanceof CryptographyKey)) {
+ throw new TypeError('Key must be an instance of CryptographyKey');
+ }
+ if (key.getLength() !== 32) {
+ throw new SodiumError('crypto_secretstream keys must be 32 bytes long');
+ }
+ const [state, header] = await this.backend.crypto_secretstream_xchacha20poly1305_init_push(key);
+ return Object.freeze({
+ header: header,
+ push: this.crypto_secretstream_xchacha20poly1305_push.bind(this, state),
+ rekey: this.crypto_secretstream_xchacha20poly1305_rekey.bind(this, state)
+ });
+ }
+
+ /**
+ * Initialize the internal state for stream decryption.
+ *
+ * @param {Buffer} header
+ * @param {CryptographyKey} key
+ * @return {Promise<*>}
+ */
+ async crypto_secretstream_xchacha20poly1305_init_pull(key, header) {
+ await this.ensureLoaded();
+ header = await Util.toBuffer(header);
+ if (header.length !== 24) {
+ throw new SodiumError('crypto_secretstream headers must be 24 bytes long');
+ }
+ if (!(key instanceof CryptographyKey)) {
+ throw new TypeError('Key must be an instance of CryptographyKey');
+ }
+ if (key.getLength() !== 32) {
+ throw new SodiumError('crypto_secretstream keys must be 32 bytes long');
+ }
+ const state = await this.backend.crypto_secretstream_xchacha20poly1305_init_pull(header, key);
+ return Object.freeze({
+ pull: this.crypto_secretstream_xchacha20poly1305_pull.bind(this, state)
+ });
+ }
+
+ /**
+ * Stream encryption.
+ *
+ * @param {*} state
+ * @param {string|Buffer} message
+ * @param {string|Buffer} ad
+ * @param {number} tag
+ * @return {Promise<Buffer>}
+ */
+ async crypto_secretstream_xchacha20poly1305_push(state, message, ad = '', tag = 0) {
+ await this.ensureLoaded();
+ return this.backend.crypto_secretstream_xchacha20poly1305_push(state, message, ad, tag);
+ }
+
+ /**
+ * Stream decryption.
+ *
+ * @param {*} state
+ * @param {Buffer} ciphertext
+ * @param {string|Buffer} ad
+ * @param {number} tag
+ * @return {Promise<Buffer>}
+ */
+ async crypto_secretstream_xchacha20poly1305_pull(state, ciphertext, ad = '', tag = 0) {
+ await this.ensureLoaded();
+ return this.backend.crypto_secretstream_xchacha20poly1305_pull(state, ciphertext, ad, tag);
+ }
+
+ /**
+ * Deterministic rekeying.
+ *
+ * @param {*} state
+ * @return {Promise<void>}
+ */
+ async crypto_secretstream_xchacha20poly1305_rekey(state) {
+ await this.ensureLoaded();
+ await this.backend.crypto_secretstream_xchacha20poly1305_rekey(state);
+ }
+
+ /**
+ * Generate a key for shared-key authenticated encryption.
+ *
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_secretstream_xchacha20poly1305_keygen() {
+ return new CryptographyKey(
+ await this.backend.randombytes_buf(this.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES)
+ );
+ }
+
+ /**
+ * Calculate a fast hash for short inputs.
+ *
+ * Algorithm: SipHash-2-4
+ *
+ * @param {string|Buffer} message
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_shorthash(message, key) {
+ await this.ensureLoaded();
+ return await this.backend.crypto_shorthash(await Util.toBuffer(message), key);
+ }
+
+ /**
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_shorthash_keygen() {
+ return new CryptographyKey(
+ await this.backend.randombytes_buf(this.CRYPTO_SHORTHASH_KEYBYTES)
+ );
+ }
+
+ /**
+ * Returns a signed message.
+ *
+ * @param {string|Buffer} message,
+ * @param {Ed25519SecretKey} secretKey
+ * @return {Promise<Buffer>}
+ */
+ async crypto_sign(message, secretKey) {
+ await this.ensureLoaded();
+ if (!(secretKey instanceof Ed25519SecretKey)) {
+ throw new TypeError('Argument 2 must be an instance of Ed25519SecretKey');
+ }
+ return this.backend.crypto_sign(message, secretKey);
+ }
+
+ /**
+ * Given a signed message, verify the Ed25519 signature. If it matches, return the
+ * bare message (no signature).
+ *
+ * @param {string|Buffer} message,
+ * @param {Ed25519PublicKey} publicKey
+ * @return {Promise<Buffer>}
+ */
+ async crypto_sign_open(message, publicKey) {
+ await this.ensureLoaded();
+ if (!(publicKey instanceof Ed25519PublicKey)) {
+ throw new TypeError('Argument 2 must be an instance of Ed25519PublicKey');
+ }
+ return this.backend.crypto_sign_open(message, publicKey);
+ }
+
+ /**
+ * Returns the Ed25519 signature of the message, for the given secret key.
+ *
+ * @param {string|Buffer} message,
+ * @param {Ed25519SecretKey} secretKey
+ * @return {Promise<Buffer>}
+ */
+ async crypto_sign_detached(message, secretKey) {
+ await this.ensureLoaded();
+ if (!(secretKey instanceof Ed25519SecretKey)) {
+ throw new TypeError('Argument 2 must be an instance of Ed25519SecretKey');
+ }
+ return this.backend.crypto_sign_detached(message, secretKey);
+ }
+
+ /**
+ * Returns true if the Ed25519 signature is valid for a given message and public key.
+ *
+ * @param {string|Buffer} message,
+ * @param {Ed25519PublicKey} publicKey
+ * @param {Buffer} signature
+ * @return {Promise<boolean>}
+ */
+ async crypto_sign_verify_detached(message, publicKey, signature) {
+ await this.ensureLoaded();
+ if (!(publicKey instanceof Ed25519PublicKey)) {
+ throw new TypeError('Argument 2 must be an instance of Ed25519PublicKey');
+ }
+ return this.backend.crypto_sign_verify_detached(message, publicKey, signature);
+ }
+
+ /**
+ * Extract the secret key from an Ed25519 keypair object.
+ *
+ * @param {CryptographyKey} keypair
+ * @return {Promise<Ed25519SecretKey>}
+ */
+ async crypto_sign_secretkey(keypair) {
+ if (keypair.getLength() !== 96) {
+ throw new SodiumError('Keypair must be 96 bytes');
+ }
+ return new Ed25519SecretKey(
+ await Util.toBuffer(
+ keypair.getBuffer().slice(0, 64)
+ )
+ );
+ }
+
+ /**
+ * Extract the public key from an Ed25519 keypair object.
+ *
+ * @param {CryptographyKey} keypair
+ * @return {Promise<Ed25519PublicKey>}
+ */
+ async crypto_sign_publickey(keypair) {
+ if (keypair.getLength() !== 96) {
+ throw new SodiumError('Keypair must be 96 bytes');
+ }
+ return new Ed25519PublicKey(
+ keypair.getBuffer().slice(64, 96)
+ );
+ }
+
+ /**
+ * Generate an Ed25519 keypair object.
+ *
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_sign_keypair() {
+ await this.ensureLoaded();
+ return this.backend.crypto_sign_keypair();
+ }
+
+ /**
+ * Generate an Ed25519 keypair object from a seed.
+ *
+ * @param {Buffer} seed
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_sign_seed_keypair(seed) {
+ await this.ensureLoaded();
+ if (seed instanceof CryptographyKey) {
+ seed = seed.getBuffer();
+ }
+ seed = await Util.toBuffer(seed);
+ if (seed.length !== 32) {
+ throw new SodiumError(`Seed must be 32 bytes long; got ${seed.length}`);
+ }
+ return this.backend.crypto_sign_seed_keypair(seed);
+ }
+
+ /**
+ * Obtain a birationally equivalent X25519 secret key,
+ * given an Ed25519 secret key.
+ *
+ * @param {Ed25519SecretKey} sk
+ * @return {Promise<X25519SecretKey>}
+ */
+ async crypto_sign_ed25519_sk_to_curve25519(sk) {
+ await this.ensureLoaded();
+ return new X25519SecretKey(
+ await this.backend.crypto_sign_ed25519_sk_to_curve25519(sk)
+ );
+ }
+
+ /**
+ * Obtain a birationally equivalent X25519 public key,
+ * given an Ed25519 public key.
+ *
+ * @param {Ed25519PublicKey} pk
+ * @return {Promise<X25519PublicKey>}
+ */
+ async crypto_sign_ed25519_pk_to_curve25519(pk) {
+ await this.ensureLoaded();
+ return new X25519PublicKey(
+ await this.backend.crypto_sign_ed25519_pk_to_curve25519(pk)
+ );
+ }
+
+ /**
+ * Generate an arbitrary number of pseudorandom bytes from a given
+ * nonce and key.
+ *
+ * @param {number} length
+ * @param {Buffer} nonce
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_stream(length, nonce, key) {
+ await this.ensureLoaded();
+ return this.backend.crypto_stream(length, nonce, key);
+ }
+
+ /**
+ * Encrypts a string (without authentication).
+ *
+ * @param {string|Buffer} plaintext
+ * @param {Buffer} nonce
+ * @param {CryptographyKey} key
+ * @return {Promise<Buffer>}
+ */
+ async crypto_stream_xor(plaintext, nonce, key) {
+ await this.ensureLoaded();
+ return this.backend.crypto_stream_xor(plaintext, nonce, key);
+ }
+ /**
+ * Generate a key for stream ciphers.
+ *
+ * @return {Promise<CryptographyKey>}
+ */
+ async crypto_stream_keygen() {
+ return new CryptographyKey(
+ await this.backend.randombytes_buf(this.CRYPTO_STREAM_KEYBYTES)
+ );
+ }
+
+ /**
+ * Returns a buffer filled with random bytes.
+ *
+ * @param {number} num
+ * @return {Promise<Buffer>}
+ */
+ async randombytes_buf(num) {
+ await this.ensureLoaded();
+ return await this.backend.randombytes_buf(num);
+ }
+
+ /**
+ * Generate an integer between 0 and upperBound (non-inclusive).
+ *
+ * For example, randombytes_uniform(10) returns an integer between 0 and 9.
+ *
+ * @param {number} upperBound
+ * @return {Promise<number>}
+ */
+ async randombytes_uniform(upperBound) {
+ await this.ensureLoaded();
+ return this.backend.randombytes_uniform(upperBound);
+ }
+
+ /**
+ * Add two buffers (little-endian). Returns the value.
+ *
+ * @param {Buffer} val
+ * @param {Buffer} addv
+ * @return {Promise<Buffer>}
+ */
+ async sodium_add(val, addv) {
+ await this.ensureLoaded();
+ return await this.backend.sodium_add(
+ await Util.toBuffer(val),
+ await Util.toBuffer(addv)
+ );
+ }
+
+ /**
+ * Convert to hex.
+ *
+ * @param {Buffer} decoded
+ * @return {Promise<Buffer>}
+ */
+ async sodium_bin2hex(decoded) {
+ await this.ensureLoaded();
+ return this.backend.sodium_bin2hex(decoded);
+ }
+
+ /**
+ * Compare two buffers in constant time.
+ *
+ * Returns -1 if b1 is less than b2.
+ * Returns 1 if b1 is greater than b2.
+ * Returns 0 if b1 is equal to b2.
+ *
+ * @param {Buffer} b1
+ * @param {Buffer} b2
+ * @return {Promise<number>}
+ */
+ async sodium_compare(b1, b2) {
+ await this.ensureLoaded();
+ return this.backend.sodium_compare(b1, b2);
+ }
+ /**
+ * Convert to hex.
+ *
+ * @param {Buffer|string} encoded
+ * @return {Promise<string>}
+ */
+ async sodium_hex2bin(encoded) {
+ await this.ensureLoaded();
+ return this.backend.sodium_hex2bin(encoded);
+ }
+
+ /**
+ * Increment a buffer (little endian). Overwrites the buffer in-place.
+ *
+ * @param {Buffer} buf
+ * @return {Promise<Buffer>}
+ */
+ async sodium_increment(buf) {
+ await this.ensureLoaded();
+ return this.backend.sodium_increment(buf);
+ }
+
+ /**
+ * Returns true if the buffer is zero.
+ *
+ * @param {Buffer} buf
+ * @param {number} len
+ * @return {Promise<Buffer>}
+ */
+ async sodium_is_zero(buf, len) {
+ await this.ensureLoaded();
+ return this.backend.sodium_is_zero(buf, len);
+ }
+
+ /**
+ * Timing-safe buffer comparison.
+ *
+ * @param {Buffer} b1
+ * @param {Buffer} b2
+ * @return {Promise<boolean>}
+ */
+ async sodium_memcmp(b1, b2) {
+ await this.ensureLoaded();
+ return this.backend.sodium_memcmp(b1, b2);
+ }
+
+ /**
+ * Zero out a buffer. Overwrites the buffer in-place.
+ *
+ * @param {Buffer} buf
+ * @return {Promise<void>}
+ */
+ async sodium_memzero(buf) {
+ await this.ensureLoaded();
+ await this.backend.sodium_memzero(buf);
+ }
+
+ /**
+ * Pad a string.
+ *
+ * @param {string|Buffer} buf
+ * @param {number} blockSize
+ * @return {Promise<Buffer>}
+ */
+ async sodium_pad(buf, blockSize) {
+ await this.ensureLoaded();
+ return this.backend.sodium_pad(buf, blockSize);
+ }
+
+ /**
+ * Unpad a string.
+ *
+ * @param {string|Buffer} buf
+ * @param {number} blockSize
+ * @return {Promise<Buffer>}
+ */
+ async sodium_unpad(buf, blockSize) {
+ await this.ensureLoaded();
+ return this.backend.sodium_unpad(buf, blockSize);
+ }
+}
+
+module.exports = SodiumPlus;
diff --git a/library/sodium-plus/lib/util.js b/library/sodium-plus/lib/util.js
new file mode 100644
index 000000000..e19030d0e
--- /dev/null
+++ b/library/sodium-plus/lib/util.js
@@ -0,0 +1,133 @@
+"use strict";
+
+/* istanbul ignore if */
+if (typeof (Buffer) === 'undefined') {
+ let Buffer = require('buffer/').Buffer;
+}
+
+const arrayToBuffer = require('typedarray-to-buffer');
+
+module.exports = class Util
+{
+ static async cloneBuffer(buf) {
+ return Buffer.from(buf);
+ }
+
+ /**
+ * Define the sodium constants
+ *
+ * @param {object} anyobject
+ * @return {object}
+ */
+ static populateConstants(anyobject) {
+ anyobject.LIBRARY_VERSION_MAJOR = 10;
+ anyobject.LIBRARY_VERSION_MINOR = 2;
+ anyobject.VERSION_STRING = '1.0.17';
+ anyobject.BASE64_VARIANT_ORIGINAL = 1;
+ anyobject.BASE64_VARIANT_ORIGINAL_NO_PADDING = 3;
+ anyobject.BASE64_VARIANT_URLSAFE = 5;
+ anyobject.BASE64_VARIANT_URLSAFE_NO_PADDING = 7;
+ anyobject.CRYPTO_AEAD_AES256GCM_KEYBYTES = 32;
+ anyobject.CRYPTO_AEAD_AES256GCM_NSECBYTES = 0;
+ anyobject.CRYPTO_AEAD_AES256GCM_NPUBBYTES = 12;
+ anyobject.CRYPTO_AEAD_AES256GCM_ABYTES = 16;
+ anyobject.CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES = 32;
+ anyobject.CRYPTO_AEAD_CHACHA20POLY1305_NSECBYTES = 0;
+ anyobject.CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES = 8;
+ anyobject.CRYPTO_AEAD_CHACHA20POLY1305_ABYTES = 16;
+ anyobject.CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES = 32;
+ anyobject.CRYPTO_AEAD_CHACHA20POLY1305_IETF_NSECBYTES = 0;
+ anyobject.CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES = 12;
+ anyobject.CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES = 16;
+ anyobject.CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES = 32;
+ anyobject.CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NSECBYTES = 0;
+ anyobject.CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES = 24;
+ anyobject.CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES = 16;
+ anyobject.CRYPTO_AUTH_BYTES = 32;
+ anyobject.CRYPTO_AUTH_KEYBYTES = 32;
+ anyobject.CRYPTO_BOX_SEALBYTES = 16;
+ anyobject.CRYPTO_BOX_SECRETKEYBYTES = 32;
+ anyobject.CRYPTO_BOX_PUBLICKEYBYTES = 32;
+ anyobject.CRYPTO_BOX_KEYPAIRBYTES = 64;
+ anyobject.CRYPTO_BOX_MACBYTES = 16;
+ anyobject.CRYPTO_BOX_NONCEBYTES = 24;
+ anyobject.CRYPTO_BOX_SEEDBYTES = 32;
+ anyobject.CRYPTO_KDF_BYTES_MIN = 16;
+ anyobject.CRYPTO_KDF_BYTES_MAX = 64;
+ anyobject.CRYPTO_KDF_CONTEXTBYTES = 8;
+ anyobject.CRYPTO_KDF_KEYBYTES = 32;
+ anyobject.CRYPTO_KX_BYTES = 32;
+ anyobject.CRYPTO_KX_PRIMITIVE = 'x25519blake2b';
+ anyobject.CRYPTO_KX_SEEDBYTES = 32;
+ anyobject.CRYPTO_KX_KEYPAIRBYTES = 64;
+ anyobject.CRYPTO_KX_PUBLICKEYBYTES = 32;
+ anyobject.CRYPTO_KX_SECRETKEYBYTES = 32;
+ anyobject.CRYPTO_KX_SESSIONKEYBYTES = 32;
+ anyobject.CRYPTO_GENERICHASH_BYTES = 32;
+ anyobject.CRYPTO_GENERICHASH_BYTES_MIN = 16;
+ anyobject.CRYPTO_GENERICHASH_BYTES_MAX = 64;
+ anyobject.CRYPTO_GENERICHASH_KEYBYTES = 32;
+ anyobject.CRYPTO_GENERICHASH_KEYBYTES_MIN = 16;
+ anyobject.CRYPTO_GENERICHASH_KEYBYTES_MAX = 64;
+ anyobject.CRYPTO_GENERICHASH_STATEBYTES = 384;
+ anyobject.CRYPTO_PWHASH_SALTBYTES = 16;
+ anyobject.CRYPTO_PWHASH_STRPREFIX = '$argon2id$';
+ anyobject.CRYPTO_PWHASH_ALG_ARGON2I13 = 1;
+ anyobject.CRYPTO_PWHASH_ALG_ARGON2ID13 = 2;
+ anyobject.CRYPTO_PWHASH_ALG_DEFAULT = anyobject.CRYPTO_PWHASH_ALG_ARGON2ID13;
+ anyobject.CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE = 2;
+ anyobject.CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE = 67108864;
+ anyobject.CRYPTO_PWHASH_OPSLIMIT_MODERATE = 3;
+ anyobject.CRYPTO_PWHASH_MEMLIMIT_MODERATE = 268435456;
+ anyobject.CRYPTO_PWHASH_OPSLIMIT_SENSITIVE = 4;
+ anyobject.CRYPTO_PWHASH_MEMLIMIT_SENSITIVE = 1073741824;
+ anyobject.CRYPTO_PWHASH_SCRYPTSALSA208SHA256_SALTBYTES = 32;
+ anyobject.CRYPTO_SCALARMULT_BYTES = 32;
+ anyobject.CRYPTO_SCALARMULT_SCALARBYTES = 32;
+ anyobject.CRYPTO_SHORTHASH_BYTES = 8;
+ anyobject.CRYPTO_SHORTHASH_KEYBYTES = 16;
+ anyobject.CRYPTO_SECRETBOX_KEYBYTES = 32;
+ anyobject.CRYPTO_SECRETBOX_MACBYTES = 16;
+ anyobject.CRYPTO_SECRETBOX_NONCEBYTES = 24;
+ anyobject.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_STATEBYTES = 52;
+ anyobject.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES = 17;
+ anyobject.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES = 24;
+ anyobject.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES = 32;
+ anyobject.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH = 0;
+ anyobject.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PULL = 1;
+ anyobject.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY = 2;
+ anyobject.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL = 3;
+ anyobject.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX = 0x3fffffff80;
+ anyobject.CRYPTO_SIGN_BYTES = 64;
+ anyobject.CRYPTO_SIGN_SEEDBYTES = 32;
+ anyobject.CRYPTO_SIGN_PUBLICKEYBYTES = 32;
+ anyobject.CRYPTO_SIGN_SECRETKEYBYTES = 64;
+ anyobject.CRYPTO_SIGN_KEYPAIRBYTES = 96;
+ anyobject.CRYPTO_STREAM_KEYBYTES = 32;
+ anyobject.CRYPTO_STREAM_NONCEBYTES = 24;
+ return anyobject;
+ }
+
+ /**
+ * Coerce input to a Buffer, throwing a TypeError if it cannot be coerced.
+ *
+ * @param {string|Buffer|Uint8Array|Promise<Buffer>} stringOrBuffer
+ * @returns {Buffer}
+ */
+ static async toBuffer(stringOrBuffer)
+ {
+ if (Buffer.isBuffer(stringOrBuffer)) {
+ return stringOrBuffer;
+ } else if (stringOrBuffer === null) {
+ return null;
+ } else if (typeof(stringOrBuffer) === 'string') {
+ return Buffer.from(stringOrBuffer, 'binary');
+ } else if (stringOrBuffer instanceof Uint8Array) {
+ return arrayToBuffer(stringOrBuffer);
+ } else if (stringOrBuffer instanceof Promise) {
+ return await stringOrBuffer;
+ } else {
+ throw new TypeError('Invalid type; string or buffer expected');
+ }
+ }
+};