const assert = require('assert');
const expectError = require('./async-helper');
const fsp = require('fs').promises;
const path = require('path');
const { describe, it } = require('mocha');
const { expect } = require('chai');
const { CryptographyKey, SodiumPlus, X25519PublicKey, X25519SecretKey } = require('../index');
const Util = require('../lib/util');
const VERBOSE = false;
let sodium;
(async () => {
if (!sodium) sodium = await SodiumPlus.auto();
if (VERBOSE) {
console.log({
'libsodium-wrappers': sodium.isLibsodiumWrappers(),
'sodium-native': sodium.isSodiumNative()
});
}
})();
describe('SodiumPlus', () => {
it('ensureLoaded', async () => {
if (!sodium) sodium = await SodiumPlus.auto();
await sodium.ensureLoaded();
expect('string').to.be.equal(typeof sodium.getBackendName());
expect('boolean').to.be.equal(typeof sodium.isSodiumNative());
expect('boolean').to.be.equal(typeof sodium.isLibsodiumWrappers());
});
it('index.js', async () => {
const indexFile = require('../index');
expect(typeof indexFile.getBackendObject()).to.be.equal('function');
expect(typeof indexFile.getBackendObject('SodiumNative')).to.be.equal('function');
expect(typeof indexFile.getBackendObject('LibsodiumWrappers')).to.be.equal('function');
expect(() => {
indexFile.getBackendObject('Sodium')
}).to.throw('Unrecognized backend type: Sodium');
});
it('SodiumPlus.CONSTANTS', async () => {
if (!sodium) sodium = await SodiumPlus.auto();
let dummy = Util.populateConstants({});
for (let val in dummy) {
expect(sodium.backend[val]).to.be.equals(dummy[val]);
expect(sodium[val]).to.be.equals(dummy[val]);
}
});
it('SodiumPlus.crypto_aead_xchacha20poly1305_ietf_*', async() => {
if (!sodium) sodium = await SodiumPlus.auto();
let plaintext = Buffer.from(
'4c616469657320616e642047656e746c656d656e206f662074686520636c6173' +
'73206f66202739393a204966204920636f756c64206f6666657220796f75206f' +
'6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73' +
'637265656e20776f756c642062652069742e',
'hex'
);
let assocData = Buffer.from('50515253c0c1c2c3c4c5c6c7', 'hex');
let nonce = Buffer.from('404142434445464748494a4b4c4d4e4f5051525354555657', 'hex');
let key = CryptographyKey.from('808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f', 'hex');
let ciphertext = await sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, nonce, key, assocData);
let expected = 'bd6d179d3e83d43b9576579493c0e939572a1700252bfaccbed2902c21396cbb' +
'731c7f1b0b4aa6440bf3a82f4eda7e39ae64c6708c54c216cb96b72e1213b452' +
'2f8c9ba40db5d945b11b69b982c1bb9e3f3fac2bc369488f76b2383565d3fff9' +
'21f9664c97637da9768812f615c68b13b52e' +
'c0875924c1c7987947deafd8780acf49';
expect(ciphertext.toString('hex')).to.be.equals(expected);
let decrypted = await sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(ciphertext, nonce, key, assocData);
expect(decrypted.toString('hex')).to.be.equals(plaintext.toString('hex'));
let randomKey = await sodium.crypto_aead_xchacha20poly1305_ietf_keygen();
assert(randomKey instanceof CryptographyKey);
let ciphertext2 = await sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, nonce, randomKey);
decrypted = await sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(ciphertext2, nonce, randomKey);
expect(decrypted.toString('hex')).to.be.equals(plaintext.toString('hex'));
expect(ciphertext.toString('hex')).to.not.equals(ciphertext2.toString('hex'));
await expectError(
sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(plaintext, nonce.slice(1), randomKey),
'Argument 2 must be 24 bytes'
);
await expectError(
sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(plaintext, nonce, Buffer.alloc(32)),
'Argument 3 must be an instance of CryptographyKey'
);
await expectError(
sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, nonce.slice(1), randomKey),
'Argument 2 must be 24 bytes'
);
await expectError(
sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, nonce, Buffer.alloc(32)),
'Argument 3 must be an instance of CryptographyKey'
);
});
it('SodiumPlus.crypto_auth', async() => {
if (!sodium) sodium = await SodiumPlus.auto();
let key = await sodium.crypto_auth_keygen();
let message = 'Science, math, technology, engineering, and compassion for others.';
let mac = await sodium.crypto_auth(message, key);
assert(await sodium.crypto_auth_verify(message, key, mac) === true);
await expectError(
sodium.crypto_auth(message, Buffer.alloc(32)),
'Argument 2 must be an instance of CryptographyKey'
);
await expectError(
sodium.crypto_auth_verify(message, Buffer.alloc(32), mac),
'Argument 2 must be an instance of CryptographyKey'
);
});
it('SodiumPlus.crypto_box', async() => {
if (!sodium) sodium = await SodiumPlus.auto();
let plaintext = 'Science, math, technology, engineering, and compassion for others.';
let aliceKeypair = await sodium.crypto_box_keypair();
let aliceSecret = await sodium.crypto_box_secretkey(aliceKeypair);
let alicePublic = await sodium.crypto_box_publickey(aliceKeypair);
let bobKeypair = await sodium.crypto_box_keypair();
let bobSecret = await sodium.crypto_box_secretkey(bobKeypair);
let bobPublic = await sodium.crypto_box_publickey(bobKeypair);
let nonce = await sodium.randombytes_buf(24);
let ciphertext = await sodium.crypto_box(plaintext, nonce, aliceSecret, bobPublic);
let decrypted = await sodium.crypto_box_open(ciphertext, nonce, bobSecret, alicePublic);
expect(decrypted.toString('hex')).to.be.equals(Buffer.from(plaintext).toString('hex'));
let derived = await sodium.crypto_box_publickey_from_secretkey(aliceSecret);
expect(alicePublic.getBuffer().toString('hex'))
.to.be.equal(derived.getBuffer().toString('hex'));
/* Unhappy path: */
await expectError(
sodium.crypto_box(plaintext, nonce, alicePublic, bobPublic),
'Argument 3 must be an instance of X25519SecretKey'
);
await expectError(
sodium.crypto_box(plaintext, nonce, bobSecret, aliceSecret),
'Argument 4 must be an instance of X25519PublicKey'
);
await expectError(
sodium.crypto_box(plaintext, nonce.slice(1), bobSecret, alicePublic),
'Nonce must be a buffer of exactly 24 bytes'
);
await expectError(
sodium.crypto_box_open(ciphertext, nonce, alicePublic, bobPublic),
'Argument 3 must be an instance of X25519SecretKey'
);
await expectError(
sodium.crypto_box_open(ciphertext, nonce, bobSecret, aliceSecret),
'Argument 4 must be an instance of X25519PublicKey'
);
await expectError(
sodium.crypto_box_open(ciphertext.slice(0, 14), nonce, bobSecret, alicePublic),
'Ciphertext must be a buffer of at least 16 bytes'
);
await expectError(
sodium.crypto_box_open(ciphertext, nonce.slice(1), bobSecret, alicePublic),
'Nonce must be a buffer of exactly 24 bytes'
);
await expectError(
sodium.crypto_box_keypair_from_secretkey_and_publickey(alicePublic, alicePublic),
'Argument 1 must be an instance of X25519SecretKey'
);
await expectError(
sodium.crypto_box_keypair_from_secretkey_and_publickey(aliceSecret, aliceSecret),
'Argument 2 must be an instance of X25519PublicKey'
);
await expectError(
sodium.crypto_box_secretkey(derived),
'Keypair must be 64 bytes'
);
await expectError(
sodium.crypto_box_publickey(derived),
'Keypair must be 64 bytes'
);
await expectError(
sodium.crypto_box_publickey_from_secretkey(derived),
'Argument 1 must be an instance of X25519SecretKey'
);
});
it('SodiumPlus.crypto_box_seal', async() => {
if (!sodium) sodium = await SodiumPlus.auto();
let plaintext = 'Science, math, technology, engineering, and compassion for others.';
let aliceKeypair = await sodium.crypto_box_keypair();
let aliceSecret = await sodium.crypto_box_secretkey(aliceKeypair);
let alicePublic = await sodium.crypto_box_publickey(aliceKeypair);
assert(aliceSecret instanceof X25519SecretKey);
assert(alicePublic instanceof X25519PublicKey);
let ciphertext = await sodium.crypto_box_seal(plaintext, alicePublic);
let decrypted = await sodium.crypto_box_seal_open(ciphertext, alicePublic, aliceSecret);
expect(decrypted.toString('hex')).to.be.equals(Buffer.from(plaintext).toString('hex'));
await expectError(
sodium.crypto_box_seal(plaintext, aliceSecret),
'Argument 2 must be an instance of X25519PublicKey'
);
await expectError(
sodium.crypto_box_seal_open(plaintext, aliceSecret, aliceSecret),
'Argument 2 must be an instance of X25519PublicKey'
);
await expectError(
sodium.crypto_box_seal_open(plaintext, alicePublic, alicePublic),
'Argument 3 must be an instance of X25519SecretKey'
);
});
it('SodiumPlus.crypto_generichash', async() => {
let message = 'Science, math, technology, engineering, and compassion for others.';
let piece1 = message.slice(0, 16);
let piece2 = message.slice(16);
let hash1 = await sodium.crypto_generichash(message);
expect(hash1.toString('hex')).to.be.equals('47c1fdbde32b30b9c54dd47cf88ba92d2d05df1265e342c9563ed56aee84ab02');
let state = await sodium.crypto_generichash_init();
await sodium.crypto_generichash_update(state, piece1);
await sodium.crypto_generichash_update(state, piece2);
let hash2 = await sodium.crypto_generichash_final(state);
expect(hash1.toString('hex')).to.be.equals(hash2.toString('hex'));
let key = await sodium.crypto_generichash_keygen();
hash1 = await sodium.crypto_generichash(message, key);
state = await sodium.crypto_generichash_init(key);
await sodium.crypto_generichash_update(state, piece1);
await sodium.crypto_generichash_update(state, piece2);
hash2 = await sodium.crypto_generichash_final(state);
expect(hash1.toString('hex')).to.be.equals(hash2.toString('hex'));
});
it('SodiumPlus.crypto_kdf', async function() {
if (!sodium) sodium = await SodiumPlus.auto();
let subkey, expected;
let key = CryptographyKey.from('808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f', 'hex');
let context = 'NaClTest';
subkey = await sodium.crypto_kdf_derive_from_key(32, 1, context, key);
expected = 'bce6fcf118cac2691bb23975a63dfac02282c1cd5de6ab9febcbb0ec4348181b';
expect(subkey.toString('hex')).to.be.equals(expected);
subkey = await sodium.crypto_kdf_derive_from_key(32, 2, context, key);
expected = '877cf1c1a2da9b900c79464acebc3731ed4ebe326a7951911639821d09dc6dda';
expect(subkey.toString('hex')).to.be.equals(expected);
let key2 = await sodium.crypto_kdf_keygen();
let subkey2 = await sodium.crypto_kdf_derive_from_key(32, 1, context, key2);
expect(subkey2.toString('hex')).to.not.equals(key2.toString('hex'));
expect(subkey2.toString('hex')).to.not.equals(subkey.toString('hex'));
await expectError(
sodium.crypto_kdf_derive_from_key(-32, 1, context, key2),
'Length must be a positive integer.'
);
await expectError(
sodium.crypto_kdf_derive_from_key(32, -1, context, key2),
'Key ID must be an unsigned integer'
);
});
it('SodiumPlus.crypto_kx', async function() {
if (!sodium) sodium = await SodiumPlus.auto();
let clientKeys = await sodium.crypto_kx_keypair();
let clientSecret = await sodium.crypto_box_secretkey(clientKeys);
let clientPublic = await sodium.crypto_box_publickey(clientKeys);
let seed = 'Unit test static key seed goes here. Nothing too complicated. No randomness needed, really.';
let serverKeys = await sodium.crypto_kx_seed_keypair(seed);
let serverSecret = await sodium.crypto_box_secretkey(serverKeys);
let serverPublic = await sodium.crypto_box_publickey(serverKeys);
let clientRx, clientTx, serverRx, serverTx;
[clientRx, clientTx] = await sodium.crypto_kx_client_session_keys(clientPublic, clientSecret, serverPublic);
[serverRx, serverTx] = await sodium.crypto_kx_server_session_keys(serverPublic, serverSecret, clientPublic);
expect(clientRx.toString('hex')).to.be.equals(serverTx.toString('hex'));
expect(clientTx.toString('hex')).to.be.equals(serverRx.toString('hex'));
await expectError(
sodium.crypto_kx_client_session_keys(clientSecret, clientSecret, serverPublic),
'Argument 1 must be an instance of X25519PublicKey'
);
await expectError(
sodium.crypto_kx_client_session_keys(clientPublic, clientPublic, serverPublic),
'Argument 2 must be an instance of X25519SecretKey'
);
await expectError(
sodium.crypto_kx_client_session_keys(clientPublic, clientSecret, serverSecret),
'Argument 3 must be an instance of X25519PublicKey'
);
await expectError(
sodium.crypto_kx_server_session_keys(serverSecret, serverSecret, clientPublic),
'Argument 1 must be an instance of X25519PublicKey'
);
await expectError(
sodium.crypto_kx_server_session_keys(serverPublic, serverPublic, clientPublic),
'Argument 2 must be an instance of X25519SecretKey'
);
await expectError(
sodium.crypto_kx_server_session_keys(serverPublic, serverSecret, clientSecret),
'Argument 3 must be an instance of X25519PublicKey'
);
});
it('SodiumPlus.crypto_onetimeauth', async() => {
if (!sodium) sodium = await SodiumPlus.auto();
let key = await sodium.crypto_onetimeauth_keygen();
let plaintext = 'Science, math, technology, engineering, and compassion for others.';
let tag = await sodium.crypto_onetimeauth(plaintext, key);
assert(await sodium.crypto_onetimeauth_verify(plaintext, key, tag));
assert((await sodium.crypto_onetimeauth_verify(plaintext + ' extra', key, tag)) === false);
let msg = Buffer.alloc(32, 0);
key = CryptographyKey.from('746869732069732033322d62797465206b657920666f7220506f6c7931333035', 'hex');
tag = await sodium.crypto_onetimeauth(msg, key);
expect(tag.toString('hex')).to.be.equals('49ec78090e481ec6c26b33b91ccc0307');
assert(await sodium.crypto_onetimeauth_verify(msg, key, tag));
await expectError(
sodium.crypto_onetimeauth(msg, Buffer.alloc(32)),
'Argument 2 must be an instance of CryptographyKey'
);
await expectError(
sodium.crypto_onetimeauth_verify(msg, Buffer.alloc(32), tag),
'Argument 2 must be an instance of CryptographyKey'
);
});
it('SodiumPlus.crypto_scalarmult', async() => {
let aliceKeypair = await sodium.crypto_box_keypair();
let aliceSecret = await sodium.crypto_box_secretkey(aliceKeypair);
let alicePublic = await sodium.crypto_box_publickey(aliceKeypair);
assert(aliceSecret instanceof X25519SecretKey);
assert(alicePublic instanceof X25519PublicKey);
// crypto_scalarmult_base test:
let testPublic = await sodium.crypto_scalarmult_base(aliceSecret);
expect(testPublic.getBuffer().toString('hex')).to.be.equals(alicePublic.getBuffer().toString('hex'));
// crypto_scalarmult test:
let bobKeypair = await sodium.crypto_box_keypair();
let bobSecret = await sodium.crypto_box_secretkey(bobKeypair);
let bobPublic = await sodium.crypto_box_publickey(bobKeypair);
expect(alicePublic.getBuffer().toString('hex')).to.be.equals(alicePublic.getBuffer().toString('hex'));
let ab = await sodium.crypto_scalarmult(aliceSecret, bobPublic);
expect(ab.toString('hex')).to.not.equals('0000000000000000000000000000000000000000000000000000000000000000');
let ba = await sodium.crypto_scalarmult(bobSecret, alicePublic);
expect(ba.toString('hex')).to.not.equals('0000000000000000000000000000000000000000000000000000000000000000');
expect(ab.toString('hex')).to.be.equals(ba.toString('hex'));
await expectError(
sodium.crypto_scalarmult(alicePublic, bobPublic),
'Argument 1 must be an instance of X25519SecretKey'
);
await expectError(
sodium.crypto_scalarmult(aliceSecret, bobSecret),
'Argument 2 must be an instance of X25519PublicKey'
);
await expectError(
sodium.crypto_scalarmult_base(alicePublic),
'Argument 1 must be an instance of X25519SecretKey'
);
});
it('SodiumPlus.crypto_secretbox', async() => {
if (!sodium) sodium = await SodiumPlus.auto();
let plaintext = 'Science, math, technology, engineering, and compassion for others.';
let key = await sodium.crypto_secretbox_keygen();
let nonce = await sodium.randombytes_buf(24);
let ciphertext = await sodium.crypto_secretbox(plaintext, nonce, key);
let decrypted = await sodium.crypto_secretbox_open(ciphertext, nonce, key);
expect(decrypted.toString('hex')).to.be.equals(Buffer.from(plaintext).toString('hex'));
// Unhappy path:
let ed25519key = await sodium.crypto_sign_secretkey(await sodium.crypto_sign_keypair());
await expectError(
sodium.crypto_secretbox(ciphertext.slice(0, 14), nonce, ed25519key),
'Argument 3 must not be an asymmetric key'
);
await expectError(
sodium.crypto_secretbox(ciphertext, nonce.slice(1), key),
'Nonce must be a buffer of exactly 24 bytes'
);
await expectError(
sodium.crypto_secretbox_open(ciphertext.slice(0, 14), nonce, ed25519key),
'Argument 3 must not be an asymmetric key'
);
await expectError(
sodium.crypto_secretbox_open(ciphertext.slice(0, 14), nonce, key),
'Ciphertext must be a buffer of at least 16 bytes'
);
await expectError(
sodium.crypto_secretbox_open(ciphertext, nonce.slice(1), key),
'Nonce must be a buffer of exactly 24 bytes'
);
});
it('SodiumPlus.crypto_secretstream_xchacha20poly1305', async() => {
if (!sodium) sodium = await SodiumPlus.auto();
let key = await sodium.crypto_secretstream_xchacha20poly1305_keygen();
let encryptor, decryptor;
encryptor = await sodium.crypto_secretstream_xchacha20poly1305_init_push(key);
decryptor = await sodium.crypto_secretstream_xchacha20poly1305_init_pull(key, encryptor.header);
await expectError(
sodium.crypto_secretstream_xchacha20poly1305_init_push(Buffer.alloc(31)),
'Key must be an instance of CryptographyKey'
);
await expectError(
sodium.crypto_secretstream_xchacha20poly1305_init_pull(Buffer.alloc(31), encryptor.header),
'Key must be an instance of CryptographyKey'
);
let invalidKey = new CryptographyKey(Buffer.alloc(31));
await expectError(
sodium.crypto_secretstream_xchacha20poly1305_init_push(invalidKey),
'crypto_secretstream keys must be 32 bytes long'
);
await expectError(
sodium.crypto_secretstream_xchacha20poly1305_init_pull(invalidKey, encryptor.header),
'crypto_secretstream keys must be 32 bytes long'
);
await expectError(
sodium.crypto_secretstream_xchacha20poly1305_init_pull(key, encryptor.header.slice(1)),
'crypto_secretstream headers must be 24 bytes long'
);
// Get a test input from the text file.
let longText = await fsp.readFile(path.join(__dirname, 'longtext.md'));
let chunk, readUntil;
let ciphertext = Buffer.concat([encryptor.header]);
// How big are our chunks going to be?
let PUSH_CHUNK_SIZE = await sodium.randombytes_uniform(longText.length - 32) + 32;
let PULL_CHUNK_SIZE = PUSH_CHUNK_SIZE + sodium.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES;
// Encrypt
for (let i = 0; i < longText.length; i += PUSH_CHUNK_SIZE) {
readUntil = (i + PUSH_CHUNK_SIZE) > longText.length ? longText.length : i + PUSH_CHUNK_SIZE;
chunk = await encryptor.push(
longText.slice(i, readUntil)
);
ciphertext = Buffer.concat([ciphertext, chunk]);
}
expect(ciphertext.slice(0, 24).toString('hex')).to.be
.equals(encryptor.header.toString('hex'));
// Decrypt, starting at 24 (after the header, which we already have)
let decrypted = Buffer.alloc(0);
for (let i = 24; i < ciphertext.length; i += PULL_CHUNK_SIZE) {
readUntil = (i + PULL_CHUNK_SIZE) > ciphertext.length ? ciphertext.length : i + PULL_CHUNK_SIZE;
chunk = await decryptor.pull(
ciphertext.slice(i, readUntil)
);
decrypted = Buffer.concat([decrypted, chunk]);
}
expect(decrypted.toString('hex')).to.be.equals(longText.toString('hex'));
await encryptor.rekey();
});
it('SodiumPlus.crypto_shorthash', async() => {
if (!sodium) sodium = await SodiumPlus.auto();
let key = CryptographyKey.from('808182838485868788898a8b8c8d8e8f', 'hex');
let message;
let hash;
message = 'This is short input0';
hash = await sodium.crypto_shorthash(message, key);
expect(hash.toString('hex')).to.be.equals('ef589fb9ef4196b3');
message = 'This is short input1';
hash = await sodium.crypto_shorthash(message, key);
expect(hash.toString('hex')).to.be.equals('5e8f01039bc53eb7');
let random = await sodium.crypto_shorthash_keygen();
expect(sodium.CRYPTO_SHORTHASH_KEYBYTES).to.be.equal(random.getLength());
});
it('SodiumPlus.crypto_sign_seed_keypair', async() => {
if (!sodium) sodium = await SodiumPlus.auto();
let aliceKeypair = await sodium.crypto_sign_seed_keypair(
await sodium.crypto_generichash('sodium-plus')
);
let alicePublic = await sodium.crypto_sign_publickey(aliceKeypair);
expect(alicePublic.getBuffer().toString('hex')).to.be.equals(
'292288efba3a33275d216f2e4d9014d330f3b2852d6b767de15e43839096d6e8'
);
await expectError(
sodium.crypto_sign_seed_keypair(Buffer.alloc(31)),
'Seed must be 32 bytes long; got 31'
);
// Should not throw:
await sodium.crypto_sign_seed_keypair(
new CryptographyKey(await sodium.crypto_generichash('sodium-plus'))
);
});
it('SodiumPlus.crypto_sign_{secret,public}key', async() => {
await expectError(
sodium.crypto_sign_secretkey(new CryptographyKey(Buffer.alloc(16))),
'Keypair must be 96 bytes'
);
await expectError(
sodium.crypto_sign_publickey(new CryptographyKey(Buffer.alloc(16))),
'Keypair must be 96 bytes'
);
});
it('SodiumPlus.crypto_sign', async() => {
if (!sodium) sodium = await SodiumPlus.auto();
let aliceKeypair = await sodium.crypto_sign_keypair();
let aliceSecret = await sodium.crypto_sign_secretkey(aliceKeypair);
let alicePublic = await sodium.crypto_sign_publickey(aliceKeypair);
let plaintext = 'Science, math, technology, engineering, and compassion for others.';
let signed = await sodium.crypto_sign(plaintext, aliceSecret);
let opened = await sodium.crypto_sign_open(signed, alicePublic);
expect(signed.slice(64).toString('hex')).to.be.equals(opened.toString('hex'));
expect(opened.toString()).to.be.equals(plaintext);
let signature = await sodium.crypto_sign_detached(plaintext, aliceSecret);
let valid = await sodium.crypto_sign_verify_detached(plaintext, alicePublic, signature);
expect(valid).to.be.equals(true);
let invalid = await sodium.crypto_sign_verify_detached(plaintext + ' extra', alicePublic, signature);
expect(invalid).to.be.equals(false);
await expectError(
sodium.crypto_sign(plaintext, alicePublic),
'Argument 2 must be an instance of Ed25519SecretKey'
);
await expectError(
sodium.crypto_sign_open(signed, aliceSecret),
'Argument 2 must be an instance of Ed25519PublicKey'
);
await expectError(
sodium.crypto_sign_detached(plaintext, alicePublic),
'Argument 2 must be an instance of Ed25519SecretKey'
);
await expectError(
sodium.crypto_sign_verify_detached(plaintext, aliceSecret, signature),
'Argument 2 must be an instance of Ed25519PublicKey'
);
});
it('SodiumPlus.crypto_sign_ed25519_to_curve25519', async function () {
this.timeout(0);
if (!sodium) sodium = await SodiumPlus.auto();
let aliceKeypair = CryptographyKey.from(
'411a2c2227d2a799ebae0ed94417d8e8ed1ca9b0a9d5f4cd743cc52d961e94e2' +
'da49154c9e700b754199df7974e9fa4ee4b6ebbc71f89d8d8938335ea4a1409d' +
'da49154c9e700b754199df7974e9fa4ee4b6ebbc71f89d8d8938335ea4a1409d', 'hex');
let aliceSecret = await sodium.crypto_sign_secretkey(aliceKeypair);
let alicePublic = await sodium.crypto_sign_publickey(aliceKeypair);
let ecdhSecret = await sodium.crypto_sign_ed25519_sk_to_curve25519(aliceSecret);
expect(ecdhSecret.toString('hex')).to.be
.equals('60c783b8d1674b7081b72a105b55872502825d4ec638028152e085b54705ad7e');
let ecdhPublic = await sodium.crypto_sign_ed25519_pk_to_curve25519(alicePublic);
expect(ecdhPublic.toString('hex')).to.be
.equals('5a791d07cfb39060c8e9b641b6a915a3126cd14ddc243a9928c490c8e1f59e7c');
});
it('SodiumPlus.crypto_stream', async function () {
if (!sodium) sodium = await SodiumPlus.auto();
let key = CryptographyKey.from('8000000000000000000000000000000000000000000000000000000000000000', 'hex');
let iv = Buffer.alloc(24, 0);
let output = await sodium.crypto_stream(256, iv, key);
let testVector = '93D88C085B8433B1FBAD2221FAD718078D96119F727D27F0547F9F3D29DE1358' +
'F3FE3D9EEACF59E894FA76E6507F567B4A0796DD00D8BFC736344A9906CB1F5D';
expect(output.slice(0, 64).toString('hex').toUpperCase()).to.be.equals(testVector);
testVector = '17FD2BD86D095016D8367E0DD47D3E4A18DAE7BB24F8B5E3E9F52C4A493BE982' +
'ECA8E89A4DEC78467E31087A1ACDA83754BEFB273AB27EB396EB4957F7166C25';
expect(output.slice(192, 256).toString('hex').toUpperCase()).to.be.equals(testVector);
key = CryptographyKey.from('8080808080808080808080808080808080808080808080808080808080808080', 'hex');
output = await sodium.crypto_stream_xor('Test message', iv, key);
expect(output.toString('hex')).to.be.equals('1071d0355cb22c4c4e00303f');
key = await sodium.crypto_stream_keygen();
iv = await sodium.randombytes_buf(24);
let plaintext = 'This is a secret message';
let ciphertext = await sodium.crypto_stream_xor(plaintext, iv, key);
let decrypted = await sodium.crypto_stream_xor(ciphertext, iv, key);
expect(decrypted.toString()).to.be.equals(plaintext);
});
it('SodiumPlus.randombytes_buf', async() => {
if (!sodium) sodium = await SodiumPlus.auto();
let a, b;
for (let i = 0; i < 100; i++) {
a = await sodium.randombytes_buf(64);
b = await sodium.randombytes_buf(64);
expect(a.toString('hex')).to.not.equals(b.toString('hex'));
}
});
it('SodiumPlus.randombytes_uniform', async() => {
if (!sodium) sodium = await SodiumPlus.auto();
let a, b;
for (let i = 0; i < 100; i++) {
a = await sodium.randombytes_uniform(0x3fffffff);
b = await sodium.randombytes_uniform(0x3fffffff);
expect(a).to.not.equals(b);
}
});
it('SodiumPlus.sodium_bin2hex', async () => {
if (!sodium) sodium = await SodiumPlus.auto();
let buf = await sodium.randombytes_buf(32);
expect(await sodium.sodium_bin2hex(buf)).to.be.equals(buf.toString('hex'));
});
it('SodiumPlus.sodium_add', async () => {
if (!sodium) sodium = await SodiumPlus.auto();
let foo = Buffer.from('ed000000', 'hex');
let bar = Buffer.from('01000000', 'hex');
let baz = await sodium.sodium_add(foo, bar);
expect(baz.toString('hex')).to.be.equals('ee000000');
bar = Buffer.from('ff000000', 'hex');
baz = await sodium.sodium_add(baz, bar);
expect(baz.toString('hex')).to.be.equals('ed010000');
foo = Buffer.from('ffffffff', 'hex');
bar = Buffer.from('01000000', 'hex');
baz = await sodium.sodium_add(foo, bar);
expect(baz.toString('hex')).to.be.equals('00000000');
bar = Buffer.from('02000000', 'hex');
baz = await sodium.sodium_add(foo, bar);
expect(baz.toString('hex')).to.be.equals('01000000');
});
it('SodiumPlus.sodium_compare', async() => {
if (!sodium) sodium = await SodiumPlus.auto();
let a = Buffer.from('80808080', 'hex');
let b = Buffer.from('81808080', 'hex');
let c = Buffer.from('80808081', 'hex');
expect(await sodium.sodium_compare(a, a)).to.be.equals(0);
expect(await sodium.sodium_compare(b, b)).to.be.equals(0);
expect(await sodium.sodium_compare(c, c)).to.be.equals(0);
expect(await sodium.sodium_compare(a, b)).to.be.below(0);
expect(await sodium.sodium_compare(b, a)).to.be.above(0);
expect(await sodium.sodium_compare(a, c)).to.be.below(0);
expect(await sodium.sodium_compare(c, a)).to.be.above(0);
expect(await sodium.sodium_compare(b, c)).to.be.below(0);
expect(await sodium.sodium_compare(c, b)).to.be.above(0);
});
it('SodiumPlus.sodium_hex2bin', async () => {
if (!sodium) sodium = await SodiumPlus.auto();
let buf = await sodium.randombytes_buf(32);
let hex = buf.toString('hex');
let bin = await sodium.sodium_hex2bin(hex);
expect(Buffer.isBuffer(bin)).to.be.equals(true);
expect(bin.toString('base64')).to.be.equals(buf.toString('base64'));
});
it('SodiumPlus.sodium_increment', async() => {
if (!sodium) sodium = await SodiumPlus.auto();
let a = Buffer.from('80808080', 'hex');
let b = Buffer.from('81808080', 'hex');
await sodium.sodium_increment(a);
expect(await sodium.sodium_compare(b, a)).to.be.equals(0);
a = Buffer.from('ffffffff', 'hex');
b = Buffer.from('00000000', 'hex');
await sodium.sodium_increment(a);
expect(await sodium.sodium_compare(b, a)).to.be.equals(0);
});
it('SodiumPlus.sodium_is_zero', async() => {
if (!sodium) sodium = await SodiumPlus.auto();
let buf;
buf = Buffer.from('00', 'hex');
expect(await sodium.sodium_is_zero(buf, 1)).to.be.equals(true);
buf = Buffer.from('01', 'hex');
expect(await sodium.sodium_is_zero(buf, 1)).to.be.equals(false);
});
it('SodiumPlus.sodium_memcmp', async() => {
if (!sodium) sodium = await SodiumPlus.auto();
let a, b, c;
a = await sodium.randombytes_buf(32);
b = await sodium.randombytes_buf(32);
c = await Util.cloneBuffer(b);
expect(await sodium.sodium_memcmp(a, b)).to.be.equals(false);
expect(await sodium.sodium_memcmp(a, c)).to.be.equals(false);
expect(await sodium.sodium_memcmp(b, c)).to.be.equals(true);
expect(await sodium.sodium_memcmp(c, b)).to.be.equals(true);
});
it('SodiumPlus.sodium_memzero', async() => {
if (!sodium) sodium = await SodiumPlus.auto();
let buf = await sodium.randombytes_buf(16);
expect(buf.toString('hex')).to.not.equals('00000000000000000000000000000000');
await sodium.sodium_memzero(buf);
expect(buf.toString('hex')).to.be.equals('00000000000000000000000000000000');
});
it('SodiumPlus.sodium_pad', async() => {
if (!sodium) sodium = await SodiumPlus.auto();
let buf, size, padded, unpadded;
for (let i = 0; i < 100; i++) {
buf = await sodium.randombytes_buf(
await sodium.randombytes_uniform(96) + 16
);
size = await sodium.randombytes_uniform(96) + 5;
padded = await sodium.sodium_pad(buf, size);
unpadded = await sodium.sodium_unpad(padded, size);
expect(unpadded.toString('hex')).to.be.equals(buf.toString('hex'));
}
});
});