From 360713c6896d18a95dd3ca541ea477bf44b98d0c Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 10 Mar 2024 22:38:21 +0000 Subject: add sodium-plus js crypto library --- library/sodium-plus/test/sodiumplus-test.js | 732 ++++++++++++++++++++++++++++ 1 file changed, 732 insertions(+) create mode 100644 library/sodium-plus/test/sodiumplus-test.js (limited to 'library/sodium-plus/test/sodiumplus-test.js') diff --git a/library/sodium-plus/test/sodiumplus-test.js b/library/sodium-plus/test/sodiumplus-test.js new file mode 100644 index 000000000..0009b252c --- /dev/null +++ b/library/sodium-plus/test/sodiumplus-test.js @@ -0,0 +1,732 @@ +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')); + } + }); +}); -- cgit v1.2.3