Label errors better, make _sendToProxy use ephermal layering

pull/734/head
Ryan Tharp 5 years ago
parent 3969817a0a
commit 4198f30b99

@ -1,6 +1,6 @@
/* global log, textsecure, libloki, Signal, Whisper, Headers, ConversationController, /* global log, textsecure, libloki, Signal, Whisper, Headers, ConversationController,
clearTimeout, MessageController, libsignal, StringView, window, _, clearTimeout, MessageController, libsignal, StringView, window, _,
dcodeIO, Buffer */ dcodeIO, Buffer, lokiSnodeAPI */
const nodeFetch = require('node-fetch'); const nodeFetch = require('node-fetch');
const { URL, URLSearchParams } = require('url'); const { URL, URLSearchParams } = require('url');
const FormData = require('form-data'); const FormData = require('form-data');
@ -19,6 +19,9 @@ const MESSAGE_ATTACHMENT_TYPE = 'net.app.core.oembed';
const LOKI_ATTACHMENT_TYPE = 'attachment'; const LOKI_ATTACHMENT_TYPE = 'attachment';
const LOKI_PREVIEW_TYPE = 'preview'; const LOKI_PREVIEW_TYPE = 'preview';
// for onion routing
const IV_LENGTH = 16;
// the core ADN class that handles all communication with a specific server // the core ADN class that handles all communication with a specific server
class LokiAppDotNetServerAPI { class LokiAppDotNetServerAPI {
constructor(ourKey, url) { constructor(ourKey, url) {
@ -108,7 +111,7 @@ class LokiAppDotNetServerAPI {
// no big deal if it fails... // no big deal if it fails...
if (res.err || !res.response || !res.response.data) { if (res.err || !res.response || !res.response.data) {
if (res.err) { if (res.err) {
log.error(`Error ${res.err}`); log.error(`setProfileName Error ${res.err}`);
} }
return []; return [];
} }
@ -135,7 +138,7 @@ class LokiAppDotNetServerAPI {
if (res.err || !res.response || !res.response.data) { if (res.err || !res.response || !res.response.data) {
if (res.err) { if (res.err) {
log.error(`Error ${res.err}`); log.error(`setHomeServer Error ${res.err}`);
} }
return []; return [];
} }
@ -292,23 +295,67 @@ class LokiAppDotNetServerAPI {
} }
async _sendToProxy(fetchOptions, endpoint, method) { async _sendToProxy(fetchOptions, endpoint, method) {
const randSnode = await lokiSnodeAPI.getRandomSnodeAddress();
const url = `https://${randSnode.ip}:${randSnode.port}/file_proxy`;
// make temporary key for this request/response
const ephemeralKey = libsignal.Curve.generateKeyPair();
const rand_snode = await lokiSnodeAPI.getRandomSnodeAddress(); // some randomness
const url = `https://${rand_snode.ip}:${rand_snode.port}/file_proxy`; const iv = libsignal.crypto.getRandomBytes(IV_LENGTH);
const body = fetchOptions.body; // mix server pub key with our priv key
const symKey = libsignal.Curve.calculateAgreement(
this.pubKey, // server's pubkey
ephemeralKey.privKey // our privkey
);
const payloadObj = {
// I think this is a stream, we may need to collect it all?
body: fetchOptions.body, // might need to b64 if binary...
endpoint,
method,
headers: fetchOptions.headers,
};
// convert our payload to binary buffer
const payloadData = Buffer.from(
dcodeIO.ByteBuffer.wrap(JSON.stringify(payloadObj)).toArrayBuffer()
);
// encrypt payloadData with symmetric Key using iv
const cipherBody = await libsignal.crypto.encrypt(symKey, payloadData, iv);
// make final buffer for cipherText
const ivAndCiphertext = new Uint8Array(
iv.byteLength + cipherBody.byteLength
);
// add iv
ivAndCiphertext.set(new Uint8Array(iv));
// add ciphertext after iv position
ivAndCiphertext.set(new Uint8Array(cipherBody), iv.byteLength);
// convert final buffer to base64
const cipherText64 = dcodeIO.ByteBuffer.wrap(ivAndCiphertext).toString(
'base64'
);
const ephemeralPubKey64 = dcodeIO.ByteBuffer.wrap(
ephemeralKey.privKey
).toString('base64');
const firstHopOptions = { const firstHopOptions = {
method: 'POST', method: 'POST',
body, cipherText64,
headers: { headers: {
"X-Loki-File-Server-Target": `/${endpoint}`, 'X-Loki-File-Server-Target': `/loki/v1/secure_rpc`,
"X-Loki-File-Server-Verb": method, 'X-Loki-File-Server-Verb': 'POST',
"X-Loki-File-Server-Headers": JSON.stringify(fetchOptions.headers), 'X-Loki-File-Server-Headers': JSON.stringify({
} 'X-Loki-File-Server-Ephemeral-Key': ephemeralPubKey64,
} }),
},
};
return await nodeFetch(url, firstHopOptions); return nodeFetch(url, firstHopOptions);
} }
// make a request to the server // make a request to the server
@ -320,6 +367,7 @@ class LokiAppDotNetServerAPI {
objBody, objBody,
forceFreshToken = false, forceFreshToken = false,
} = options; } = options;
const url = new URL(`${this.baseServerUrl}/${endpoint}`); const url = new URL(`${this.baseServerUrl}/${endpoint}`);
if (params) { if (params) {
url.search = new URLSearchParams(params); url.search = new URLSearchParams(params);
@ -345,14 +393,22 @@ class LokiAppDotNetServerAPI {
} }
fetchOptions.headers = new Headers(headers); fetchOptions.headers = new Headers(headers);
if (window.lokiFeatureFlags.useSnodeProxy && this.baseServerUrl === "https://file.lokinet.org") { if (
log.info("Sending a proxy request to https://file.lokinet.org"); window.lokiFeatureFlags.useSnodeProxy &&
result = await this._sendToProxy({...fetchOptions, headers}, endpoint, method); (this.baseServerUrl === 'https://file-dev.lokinet.org' ||
this.baseServerUrl === 'https://file.lokinet.org')
) {
log.info('Sending a proxy request to', this.baseServerUrl);
result = await this._sendToProxy(
{ ...fetchOptions, headers },
endpoint,
method
);
} else { } else {
result = await nodeFetch(url, fetchOptions || undefined); result = await nodeFetch(url, fetchOptions || undefined);
} }
} catch (e) { } catch (e) {
log.info(`e ${e}`); log.info(`serverRequest nodeFetch/_sendToProxy error: ${e}`);
return { return {
err: e, err: e,
}; };
@ -404,7 +460,7 @@ class LokiAppDotNetServerAPI {
if (res.err || !res.response || !res.response.data) { if (res.err || !res.response || !res.response.data) {
if (res.err) { if (res.err) {
log.error(`Error ${res.err}`); log.error(`getUserAnnotations Error ${res.err}`);
} }
return []; return [];
} }
@ -523,7 +579,7 @@ class LokiAppDotNetServerAPI {
if (res.err || !res.response || !res.response.data) { if (res.err || !res.response || !res.response.data) {
if (res.err) { if (res.err) {
log.error(`Error ${res.err}`); log.error(`getSubscribers Error ${res.err}`);
} }
return []; return [];
} }
@ -553,7 +609,7 @@ class LokiAppDotNetServerAPI {
if (res.err || !res.response || !res.response.data) { if (res.err || !res.response || !res.response.data) {
if (res.err) { if (res.err) {
log.error(`Error ${res.err}`); log.error(`getUsers Error ${res.err}`);
} }
return []; return [];
} }
@ -708,7 +764,7 @@ class LokiPublicChannelAPI {
if (res.err || !res.response || !res.response.data) { if (res.err || !res.response || !res.response.data) {
if (res.err) { if (res.err) {
log.error(`Error ${res.err}`); log.error(`banUser Error ${res.err}`);
} }
return false; return false;
} }
@ -824,7 +880,7 @@ class LokiPublicChannelAPI {
); );
if (updateRes.err || !updateRes.response || !updateRes.response.data) { if (updateRes.err || !updateRes.response || !updateRes.response.data) {
if (updateRes.err) { if (updateRes.err) {
log.error(`Error ${updateRes.err}`); log.error(`setChannelSettings Error ${updateRes.err}`);
} }
return false; return false;
} }
@ -975,7 +1031,7 @@ class LokiPublicChannelAPI {
// if any problems, abort out // if any problems, abort out
if (res.err || !res.response) { if (res.err || !res.response) {
if (res.err) { if (res.err) {
log.error(`Error ${res.err}`); log.error(`pollOnceForDeletions Error ${res.err}`);
} }
break; break;
} }

Loading…
Cancel
Save