use channel encryption with storage server

pull/213/head
sachaaaaa 6 years ago
parent 0500bb6f4f
commit aa722590fa

@ -1,11 +1,13 @@
/* eslint-disable no-await-in-loop */
/* eslint-disable no-loop-func */
/* global log, dcodeIO, window, callWorker, lokiP2pAPI, lokiSnodeAPI */
/* global log, dcodeIO, window, callWorker, lokiP2pAPI, lokiSnodeAPI, libloki */
const nodeFetch = require('node-fetch');
const _ = require('lodash');
const { parse } = require('url');
const endpointBase = '/v1/storage_rpc';
const LOKI_EPHEMKEY_HEADER = 'X-Loki-EphemKey';
class HTTPError extends Error {
constructor(response) {
@ -27,6 +29,25 @@ const fetch = async (url, options = {}) => {
const timeout = options.timeout || 10000;
const method = options.method || 'GET';
const address = parse(url).hostname;
const doEncryptChannel =
address.endsWith('.snode') &&
options.headers &&
LOKI_EPHEMKEY_HEADER in options.headers;
if (doEncryptChannel) {
try {
// eslint-disable-next-line no-param-reassign
options.body = await libloki.crypto.snodeCipher.encrypt(
address,
options.body
);
// eslint-disable-next-line no-param-reassign
options.headers['Content-Type'] = 'text/plain';
} catch (e) {
log.warn(`Could not encrypt channel for ${address}: `, e);
}
}
try {
const response = await nodeFetch(url, {
...options,
@ -45,6 +66,18 @@ const fetch = async (url, options = {}) => {
result = await response.buffer();
} else {
result = await response.text();
if (doEncryptChannel) {
try {
result = await libloki.crypto.snodeCipher.decrypt(address, result);
} catch (e) {
log.warn(`Could not decrypt response from ${address}`, e);
}
try {
result = JSON.parse(result);
} catch(e) {
log.warn(`Could not parse string to json ${result}`, e);
}
}
}
return result;
@ -151,7 +184,7 @@ class LokiMessageAPI {
method: 'POST',
body: JSON.stringify(body),
headers: {
'X-Loki-EphemKey': 'not implemented yet',
[LOKI_EPHEMKEY_HEADER]: libloki.crypto.snodeCipher.getChannelPublicKeyHex(),
},
};
@ -247,7 +280,7 @@ class LokiMessageAPI {
},
};
const headers = {
'X-Loki-EphemKey': 'not implemented yet',
[LOKI_EPHEMKEY_HEADER]: libloki.crypto.snodeCipher.getChannelPublicKeyHex(),
};
const fetchOptions = {
method: 'POST',

@ -1,4 +1,13 @@
/* global window, libsignal, textsecure, StringView, Multibase */
/* global
window,
libsignal,
textsecure,
StringView,
Multibase,
TextEncoder,
TextDecoder,
dcodeIO
*/
// eslint-disable-next-line func-names
(function() {
@ -81,7 +90,7 @@
return ab;
}
function decodeSnodeAddressToBuffer(snodeAddress) {
function decodeSnodeAddressToPubKey(snodeAddress) {
const snodeAddressClean = snodeAddress
.replace('.snode', '')
.replace('http://', '');
@ -99,12 +108,16 @@
this._cache = {};
}
_getSymmetricKey(snodeAddress) {
async _getSymmetricKey(snodeAddress) {
if (snodeAddress in this._cache) {
return this._cache[snodeAddress];
}
const buffer = decodeSnodeAddressToBuffer(snodeAddress);
const snodePubKeyArrayBuffer = bufferToArrayBuffer(buffer);
const ed25519PubKey = decodeSnodeAddressToPubKey(snodeAddress);
const sodium = await window.getSodium();
const curve25519PubKey = sodium.crypto_sign_ed25519_pk_to_curve25519(
ed25519PubKey
);
const snodePubKeyArrayBuffer = bufferToArrayBuffer(curve25519PubKey);
const symmetricKey = libsignal.Curve.calculateAgreement(
snodePubKeyArrayBuffer,
this._ephemeralKeyPair.privKey
@ -117,18 +130,30 @@
return this._ephemeralPubKeyHex;
}
async decrypt(snodeAddress, ivAndCipherText) {
const symmetricKey = this._getSymmetricKey(snodeAddress);
async decrypt(snodeAddress, ivAndCipherTextBase64) {
const ivAndCipherText = dcodeIO.ByteBuffer.wrap(
ivAndCipherTextBase64,
'base64'
).toArrayBuffer();
const symmetricKey = await this._getSymmetricKey(snodeAddress);
try {
return await DHDecrypt(symmetricKey, ivAndCipherText);
const decrypted = await DHDecrypt(symmetricKey, ivAndCipherText);
const decoder = new TextDecoder();
return decoder.decode(decrypted);
} catch (e) {
return ivAndCipherText;
}
}
async encrypt(snodeAddress, plainText) {
const symmetricKey = this._getSymmetricKey(snodeAddress);
return DHEncrypt(symmetricKey, plainText);
if (typeof plainText === 'string') {
const textEncoder = new TextEncoder();
// eslint-disable-next-line no-param-reassign
plainText = textEncoder.encode(plainText);
}
const symmetricKey = await this._getSymmetricKey(snodeAddress);
const cipherText = await DHEncrypt(symmetricKey, plainText);
return dcodeIO.ByteBuffer.wrap(cipherText).toString('base64');
}
}
@ -142,6 +167,6 @@
snodeCipher,
// for testing
_LokiSnodeChannel: LokiSnodeChannel,
_decodeSnodeAddressToBuffer: decodeSnodeAddressToBuffer,
_decodeSnodeAddressToPubKey: decodeSnodeAddressToPubKey,
};
})();

@ -77,6 +77,7 @@
"jquery": "3.3.1",
"js-sha512": "0.8.0",
"jsbn": "1.1.0",
"libsodium-wrappers": "^0.7.4",
"linkify-it": "2.0.3",
"lodash": "4.17.11",
"mkdirp": "0.5.1",

@ -339,6 +339,13 @@ window.React = require('react');
window.ReactDOM = require('react-dom');
window.moment = require('moment');
const _sodium = require('libsodium-wrappers');
window.getSodium = async () => {
await _sodium.ready;
return _sodium;
};
window.clipboard = clipboard;
const Signal = require('./js/modules/signal');

Loading…
Cancel
Save