ES2015 classes for LokiServer and FallBackSessionCipher

pull/34/head
sachaaaaa 6 years ago
parent d6534e1bb8
commit ad1cf94526

@ -4,25 +4,6 @@ const fetch = require('node-fetch');
const is = require('@sindresorhus/is');
const { fork } = require('child_process');
module.exports = {
initialize,
};
function initialize({ url }) {
if (!is.string(url)) {
throw new Error('WebAPI.initialize: Invalid server url');
}
return {
connect,
};
function connect() {
return {
sendMessage,
retrieveMessages,
};
function getPoWNonce(timestamp, ttl, pubKey, data) {
return new Promise((resolve, reject) => {
// Create forked node process to calculate PoW without blocking main process
@ -53,11 +34,33 @@ function initialize({ url }) {
});
}
async function retrieveMessages(pubKey) {
class LokiServer {
constructor({ url }) {
if (!is.string(url)) {
throw new Error('WebAPI.initialize: Invalid server url');
}
this.url = url;
}
async sendMessage(pubKey, data, ttl) {
const data64 = dcodeIO.ByteBuffer.wrap(data).toString('base64');
const timestamp = Math.floor(Date.now() / 1000);
// Nonce is returned as a base64 string to include in header
let nonce;
try {
nonce = await getPoWNonce(timestamp, ttl, pubKey, data64);
} catch (err) {
// Something went horribly wrong
// TODO: Handle gracefully
log.error('Error computing PoW');
}
const options = {
url: `${url}/retrieve`,
type: 'GET',
responseType: 'json',
url: `${this.url}/store`,
type: 'POST',
responseType: undefined,
timeout: undefined,
};
@ -65,7 +68,11 @@ function initialize({ url }) {
const fetchOptions = {
method: options.type,
body: data64,
headers: {
'X-Loki-pow-nonce': nonce,
'X-Loki-timestamp': timestamp.toString(),
'X-Loki-ttl': ttl.toString(),
'X-Loki-recipient': pubKey,
},
timeout: options.timeout,
@ -96,27 +103,14 @@ function initialize({ url }) {
return result;
}
log.error(options.type, options.url, response.status, 'Error');
throw HTTPError('retrieveMessages: error response', response.status, result);
}
async function sendMessage(pubKey, data, ttl) {
const data64 = dcodeIO.ByteBuffer.wrap(data).toString('base64');
const timestamp = Math.floor(Date.now() / 1000);
// Nonce is returned as a base64 string to include in header
let nonce;
try {
nonce = await getPoWNonce(timestamp, ttl, pubKey, data64);
} catch (err) {
// Something went horribly wrong
// TODO: Handle gracefully
log.error('Error computing PoW');
throw HTTPError('sendMessage: error response', response.status, result);
}
async retrieveMessages(pubKey) {
const options = {
url: `${url}/store`,
type: 'POST',
responseType: undefined,
url: `${this.url}/retrieve`,
type: 'GET',
responseType: 'json',
timeout: undefined,
};
@ -124,11 +118,7 @@ function initialize({ url }) {
const fetchOptions = {
method: options.type,
body: data64,
headers: {
'X-Loki-pow-nonce': nonce,
'X-Loki-timestamp': timestamp.toString(),
'X-Loki-ttl': ttl.toString(),
'X-Loki-recipient': pubKey,
},
timeout: options.timeout,
@ -159,8 +149,7 @@ function initialize({ url }) {
return result;
}
log.error(options.type, options.url, response.status, 'Error');
throw HTTPError('sendMessage: error response', response.status, result);
}
throw HTTPError('retrieveMessages: error response', response.status, result);
}
}
@ -177,3 +166,7 @@ function HTTPError(message, providedCode, response, stack) {
}
return e;
}
module.exports = {
LokiServer,
};

@ -8,54 +8,42 @@
const IV_LENGTH = 16;
function FallBackSessionCipher(address) {
class FallBackSessionCipher {
constructor(address) {
this.identityKeyString = address.getName();
this.pubKey = StringView.hexToArrayBuffer(address.getName());
}
this.encrypt = async plaintext => {
async encrypt(plaintext) {
const myKeyPair = await textsecure.storage.protocol.getIdentityKeyPair();
const myPrivateKey = myKeyPair.privKey;
const symmetricKey = libsignal.Curve.calculateAgreement(
this.pubKey,
myPrivateKey
);
const symmetricKey = libsignal.Curve.calculateAgreement(this.pubKey, myPrivateKey);
const iv = libsignal.crypto.getRandomBytes(IV_LENGTH);
const ciphertext = await libsignal.crypto.encrypt(
symmetricKey,
plaintext,
iv
);
const ivAndCiphertext = new Uint8Array(
iv.byteLength + ciphertext.byteLength
);
const ciphertext = await libsignal.crypto.encrypt(symmetricKey, plaintext, iv);
const ivAndCiphertext = new Uint8Array(iv.byteLength + ciphertext.byteLength);
ivAndCiphertext.set(new Uint8Array(iv));
ivAndCiphertext.set(new Uint8Array(ciphertext), iv.byteLength);
return {
type: textsecure.protobuf.Envelope.Type.FRIEND_REQUEST, // friend request
type: textsecure.protobuf.Envelope.Type.FRIEND_REQUEST,
body: ivAndCiphertext,
registrationId: null,
};
};
this.decrypt = async ivAndCiphertext => {
}
async decrypt(ivAndCiphertext) {
const iv = ivAndCiphertext.slice(0, IV_LENGTH);
const cipherText = ivAndCiphertext.slice(IV_LENGTH);
const myKeyPair = await textsecure.storage.protocol.getIdentityKeyPair();
const myPrivateKey = myKeyPair.privKey;
const symmetricKey = libsignal.Curve.calculateAgreement(
this.pubKey,
myPrivateKey
);
const symmetricKey = libsignal.Curve.calculateAgreement(this.pubKey, myPrivateKey);
try {
return await libsignal.crypto.decrypt(symmetricKey, cipherText, iv);
} catch (e) {
throw new FallBackDecryptionError(
`Could not decrypt message from ${
this.identityKeyString
} using FallBack encryption.`
);
}
};
catch (e) {
throw new FallBackDecryptionError(`Could not decrypt message from ${this.identityKeyString} using FallBack encryption.`);
}
}
}
async function getPreKeyBundleForNumber(pubKey) {
@ -78,13 +66,13 @@
// generate and store new prekey
const preKeyId = textsecure.storage.get('maxPreKeyId', 1);
textsecure.storage.put('maxPreKeyId', preKeyId + 1);
const preKey = await libsignal.KeyHelper.generatePreKey(preKeyId);
const newPreKey = await libsignal.KeyHelper.generatePreKey(preKeyId);
await textsecure.storage.protocol.storePreKey(
preKey.keyId,
preKey.keyPair,
newPreKey.keyId,
newPreKey.keyPair,
pubKey
);
resolve({ pubKey: preKey.keyPair.pubKey, keyId: preKeyId });
resolve({ pubKey: newPreKey.keyPair.pubKey, keyId: preKeyId });
}
}),
]);

@ -121,7 +121,7 @@ function MessageReceiver(username, password, signalingKey, options = {}) {
this.signalingKey = signalingKey;
this.username = username;
this.password = password;
this.lokiserver = window.LokiAPI.connect();
this.lokiserver = window.LokiAPI;
if (!options.serverTrustRoot) {
throw new Error('Server trust root is required!');

@ -35,7 +35,7 @@ function OutgoingMessage(
this.callback = callback;
this.silent = silent;
this.lokiserver = window.LokiAPI.connect();
this.lokiserver = window.LokiAPI;
this.numbersCompleted = 0;
this.errors = [];

@ -201,11 +201,9 @@ window.WebAPI = initializeWebAPI({
proxyUrl: config.proxyUrl,
});
const {
initialize: initializeLokiAPI,
} = require('./js/modules/loki_message_api');
const { LokiServer } = require('./js/modules/loki_message_api');
window.LokiAPI = initializeLokiAPI({
window.LokiAPI = new LokiServer({
url: config.serverUrl,
});

Loading…
Cancel
Save