ES2015 classes for LokiServer and FallBackSessionCipher

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

@ -4,25 +4,6 @@ const fetch = require('node-fetch');
const is = require('@sindresorhus/is'); const is = require('@sindresorhus/is');
const { fork } = require('child_process'); 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) { function getPoWNonce(timestamp, ttl, pubKey, data) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// Create forked node process to calculate PoW without blocking main process // 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 = { const options = {
url: `${url}/retrieve`, url: `${this.url}/store`,
type: 'GET', type: 'POST',
responseType: 'json', responseType: undefined,
timeout: undefined, timeout: undefined,
}; };
@ -65,7 +68,11 @@ function initialize({ url }) {
const fetchOptions = { const fetchOptions = {
method: options.type, method: options.type,
body: data64,
headers: { headers: {
'X-Loki-pow-nonce': nonce,
'X-Loki-timestamp': timestamp.toString(),
'X-Loki-ttl': ttl.toString(),
'X-Loki-recipient': pubKey, 'X-Loki-recipient': pubKey,
}, },
timeout: options.timeout, timeout: options.timeout,
@ -96,27 +103,14 @@ function initialize({ url }) {
return result; return result;
} }
log.error(options.type, options.url, response.status, 'Error'); log.error(options.type, options.url, response.status, 'Error');
throw HTTPError('retrieveMessages: error response', response.status, result); throw HTTPError('sendMessage: 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');
} }
async retrieveMessages(pubKey) {
const options = { const options = {
url: `${url}/store`, url: `${this.url}/retrieve`,
type: 'POST', type: 'GET',
responseType: undefined, responseType: 'json',
timeout: undefined, timeout: undefined,
}; };
@ -124,11 +118,7 @@ function initialize({ url }) {
const fetchOptions = { const fetchOptions = {
method: options.type, method: options.type,
body: data64,
headers: { headers: {
'X-Loki-pow-nonce': nonce,
'X-Loki-timestamp': timestamp.toString(),
'X-Loki-ttl': ttl.toString(),
'X-Loki-recipient': pubKey, 'X-Loki-recipient': pubKey,
}, },
timeout: options.timeout, timeout: options.timeout,
@ -159,8 +149,7 @@ function initialize({ url }) {
return result; return result;
} }
log.error(options.type, options.url, response.status, 'Error'); 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; return e;
} }
module.exports = {
LokiServer,
};

@ -8,54 +8,42 @@
const IV_LENGTH = 16; const IV_LENGTH = 16;
function FallBackSessionCipher(address) { class FallBackSessionCipher {
constructor(address) {
this.identityKeyString = address.getName(); this.identityKeyString = address.getName();
this.pubKey = StringView.hexToArrayBuffer(address.getName()); this.pubKey = StringView.hexToArrayBuffer(address.getName());
}
this.encrypt = async plaintext => { async encrypt(plaintext) {
const myKeyPair = await textsecure.storage.protocol.getIdentityKeyPair(); const myKeyPair = await textsecure.storage.protocol.getIdentityKeyPair();
const myPrivateKey = myKeyPair.privKey; const myPrivateKey = myKeyPair.privKey;
const symmetricKey = libsignal.Curve.calculateAgreement( const symmetricKey = libsignal.Curve.calculateAgreement(this.pubKey, myPrivateKey);
this.pubKey,
myPrivateKey
);
const iv = libsignal.crypto.getRandomBytes(IV_LENGTH); const iv = libsignal.crypto.getRandomBytes(IV_LENGTH);
const ciphertext = await libsignal.crypto.encrypt( const ciphertext = await libsignal.crypto.encrypt(symmetricKey, plaintext, iv);
symmetricKey, const ivAndCiphertext = new Uint8Array(iv.byteLength + ciphertext.byteLength);
plaintext,
iv
);
const ivAndCiphertext = new Uint8Array(
iv.byteLength + ciphertext.byteLength
);
ivAndCiphertext.set(new Uint8Array(iv)); ivAndCiphertext.set(new Uint8Array(iv));
ivAndCiphertext.set(new Uint8Array(ciphertext), iv.byteLength); ivAndCiphertext.set(new Uint8Array(ciphertext), iv.byteLength);
return { return {
type: textsecure.protobuf.Envelope.Type.FRIEND_REQUEST, // friend request type: textsecure.protobuf.Envelope.Type.FRIEND_REQUEST,
body: ivAndCiphertext, body: ivAndCiphertext,
registrationId: null, registrationId: null,
}; };
}; }
this.decrypt = async ivAndCiphertext => {
async decrypt(ivAndCiphertext) {
const iv = ivAndCiphertext.slice(0, IV_LENGTH); const iv = ivAndCiphertext.slice(0, IV_LENGTH);
const cipherText = ivAndCiphertext.slice(IV_LENGTH); const cipherText = ivAndCiphertext.slice(IV_LENGTH);
const myKeyPair = await textsecure.storage.protocol.getIdentityKeyPair(); const myKeyPair = await textsecure.storage.protocol.getIdentityKeyPair();
const myPrivateKey = myKeyPair.privKey; const myPrivateKey = myKeyPair.privKey;
const symmetricKey = libsignal.Curve.calculateAgreement( const symmetricKey = libsignal.Curve.calculateAgreement(this.pubKey, myPrivateKey);
this.pubKey,
myPrivateKey
);
try { try {
return await libsignal.crypto.decrypt(symmetricKey, cipherText, iv); 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) { async function getPreKeyBundleForNumber(pubKey) {
@ -78,13 +66,13 @@
// generate and store new prekey // generate and store new prekey
const preKeyId = textsecure.storage.get('maxPreKeyId', 1); const preKeyId = textsecure.storage.get('maxPreKeyId', 1);
textsecure.storage.put('maxPreKeyId', preKeyId + 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( await textsecure.storage.protocol.storePreKey(
preKey.keyId, newPreKey.keyId,
preKey.keyPair, newPreKey.keyPair,
pubKey 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.signalingKey = signalingKey;
this.username = username; this.username = username;
this.password = password; this.password = password;
this.lokiserver = window.LokiAPI.connect(); this.lokiserver = window.LokiAPI;
if (!options.serverTrustRoot) { if (!options.serverTrustRoot) {
throw new Error('Server trust root is required!'); throw new Error('Server trust root is required!');

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

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

Loading…
Cancel
Save