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')); } }); });