| 
						
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -1,16 +1,21 @@
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				/* global libloki, Multibase, libsignal, StringView */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				/* global libloki, Multibase, libsignal, StringView, dcodeIO */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				'use strict';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				function generateSnodeKeysAndAddress() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const keyPair = libsignal.Curve.generateKeyPair();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  // Signal protocol prepends with "0x05"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  keyPair.pubKey = keyPair.pubKey.slice(1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				async function generateSnodeKeysAndAddress() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  // snode identitys is a ed25519 keypair
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const sodium = await window.getSodium();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const ed25519KeyPair = sodium.crypto_sign_keypair();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const keyPair = {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    pubKey: ed25519KeyPair.publicKey,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    privKey: ed25519KeyPair.privateKey,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  };
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  // snode address is the pubkey in base32z
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  let address = Multibase.encode(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    'base32z',
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    Multibase.Buffer.from(keyPair.pubKey)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  ).toString();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  // first letter is the encoding code
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  // remove first letter, which is the encoding code
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  address = address.substring(1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  return { keyPair, address };
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -25,11 +30,11 @@ describe('Snode Channel', () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  describe('#decodeSnodeAddressToBuffer', () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    it('should decode a base32z encoded .snode address', () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const { keyPair, address } = generateSnodeKeysAndAddress();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  describe('#decodeSnodeAddressToPubKey', () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    it('should decode a base32z encoded .snode address', async () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const { keyPair, address } = await generateSnodeKeysAndAddress();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const buffer = libloki.crypto._decodeSnodeAddressToBuffer(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const buffer = libloki.crypto._decodeSnodeAddressToPubKey(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        `http://${address}.snode`
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -55,15 +60,15 @@ describe('Snode Channel', () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      assert.strictEqual(channel.getChannelPublicKeyHex(), pubKeyHex);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    it('should cache something by snode address', () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const { address } = generateSnodeKeysAndAddress();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    it('should cache something by snode address', async () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const { address } = await generateSnodeKeysAndAddress();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const channel = new libloki.crypto._LokiSnodeChannel();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // cache should be empty
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      assert.strictEqual(Object.keys(channel._cache).length, 0);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // push to cache
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      channel._getSymmetricKey(address);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      await channel._getSymmetricKey(address);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      assert.strictEqual(Object.keys(channel._cache).length, 1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      assert.strictEqual(Object.keys(channel._cache)[0], address);
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -71,7 +76,7 @@ describe('Snode Channel', () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    it('should encrypt data correctly', async () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // message sent by Loki Messenger
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const snode = generateSnodeKeysAndAddress();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const snode = await generateSnodeKeysAndAddress();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const messageSent = 'I am Groot';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const textEncoder = new TextEncoder();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const data = textEncoder.encode(messageSent);
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -79,17 +84,22 @@ describe('Snode Channel', () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const channel = new libloki.crypto._LokiSnodeChannel();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const encrypted = await channel.encrypt(snode.address, data);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      assert.isTrue(encrypted instanceof Uint8Array);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      assert.strictEqual(typeof encrypted, 'string');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // message received by storage server
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const senderPubKey = StringView.hexToArrayBuffer(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        channel.getChannelPublicKeyHex()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const sodium = await window.getSodium();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const snodePrivKey = sodium.crypto_sign_ed25519_sk_to_curve25519(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        snode.keyPair.privKey
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      ).buffer;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const symmetricKey = libsignal.Curve.calculateAgreement(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        senderPubKey,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        snode.keyPair.privKey
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        snodePrivKey
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const decrypted = await libloki.crypto.DHDecrypt(symmetricKey, encrypted);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const encryptedArrayBuffer = dcodeIO.ByteBuffer.wrap(encrypted, 'base64').toArrayBuffer();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const decrypted = await libloki.crypto.DHDecrypt(symmetricKey, encryptedArrayBuffer);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const textDecoder = new TextDecoder();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const messageReceived = textDecoder.decode(decrypted);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      assert.strictEqual(messageSent, messageReceived);
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -98,24 +108,26 @@ describe('Snode Channel', () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    it('should decrypt data correctly', async () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const channel = new libloki.crypto._LokiSnodeChannel();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // message sent by storage server
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const snode = generateSnodeKeysAndAddress();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const snode = await generateSnodeKeysAndAddress();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const messageSent = 'You are Groot';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const textEncoder = new TextEncoder();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const data = textEncoder.encode(messageSent);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const senderPubKey = StringView.hexToArrayBuffer(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        channel.getChannelPublicKeyHex()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const sodium = await window.getSodium();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const snodePrivKey = sodium.crypto_sign_ed25519_sk_to_curve25519(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        snode.keyPair.privKey
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      ).buffer;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const symmetricKey = libsignal.Curve.calculateAgreement(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        senderPubKey,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        snode.keyPair.privKey
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        snodePrivKey
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const encrypted = await libloki.crypto.DHEncrypt(symmetricKey, data);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const encryptedBase64 = dcodeIO.ByteBuffer.wrap(encrypted).toString('base64');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // message received by Loki Messenger
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const decrypted = await channel.decrypt(snode.address, encrypted);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const textDecoder = new TextDecoder();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const messageReceived = textDecoder.decode(decrypted);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      assert.strictEqual(messageSent, messageReceived);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const decrypted = await channel.decrypt(snode.address, encryptedBase64);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      assert.strictEqual(messageSent, decrypted);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				});
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
					 | 
				
			
			 | 
			 | 
			
				
 
 |