|
|
@ -86,18 +86,20 @@ OutgoingMessage.prototype = {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
reloadDevicesAndSend(number, recurse) {
|
|
|
|
reloadDevicesAndSend(number, recurse) {
|
|
|
|
return () =>
|
|
|
|
return () =>
|
|
|
|
textsecure.storage.protocol.getDeviceIds(number).then(deviceIds => {
|
|
|
|
libloki.storage
|
|
|
|
if (deviceIds.length === 0) {
|
|
|
|
.getAllDevicePubKeysForPrimaryPubKey(number)
|
|
|
|
// eslint-disable-next-line no-param-reassign
|
|
|
|
.then(devicesPubKeys => {
|
|
|
|
deviceIds = [1];
|
|
|
|
if (devicesPubKeys.length === 0) {
|
|
|
|
// return this.registerError(
|
|
|
|
// eslint-disable-next-line no-param-reassign
|
|
|
|
// number,
|
|
|
|
devicesPubKeys = [number];
|
|
|
|
// 'Got empty device list when loading device keys',
|
|
|
|
// return this.registerError(
|
|
|
|
// null
|
|
|
|
// number,
|
|
|
|
// );
|
|
|
|
// 'Got empty device list when loading device keys',
|
|
|
|
}
|
|
|
|
// null
|
|
|
|
return this.doSendMessage(number, deviceIds, recurse);
|
|
|
|
// );
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.doSendMessage(number, devicesPubKeys, recurse);
|
|
|
|
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
getKeysForNumber(number, updateDevices) {
|
|
|
|
getKeysForNumber(number, updateDevices) {
|
|
|
@ -257,9 +259,11 @@ OutgoingMessage.prototype = {
|
|
|
|
const bytes = new Uint8Array(websocketMessage.encode().toArrayBuffer());
|
|
|
|
const bytes = new Uint8Array(websocketMessage.encode().toArrayBuffer());
|
|
|
|
return bytes;
|
|
|
|
return bytes;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
doSendMessage(number, deviceIds, recurse) {
|
|
|
|
doSendMessage(number, devicesPubKeys, recurse) {
|
|
|
|
const ciphers = {};
|
|
|
|
const ciphers = {};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.numbers = devicesPubKeys;
|
|
|
|
|
|
|
|
|
|
|
|
/* Disabled because i'm not sure how senderCertificate works :thinking:
|
|
|
|
/* Disabled because i'm not sure how senderCertificate works :thinking:
|
|
|
|
const { numberInfo, senderCertificate } = this;
|
|
|
|
const { numberInfo, senderCertificate } = this;
|
|
|
|
const info = numberInfo && numberInfo[number] ? numberInfo[number] : {};
|
|
|
|
const info = numberInfo && numberInfo[number] ? numberInfo[number] : {};
|
|
|
@ -291,8 +295,14 @@ OutgoingMessage.prototype = {
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
return Promise.all(
|
|
|
|
return Promise.all(
|
|
|
|
deviceIds.map(async deviceId => {
|
|
|
|
devicesPubKeys.map(async devicePubKey => {
|
|
|
|
const address = new libsignal.SignalProtocolAddress(number, deviceId);
|
|
|
|
// Loki Messenger doesn't use the deviceId scheme, it's always 1.
|
|
|
|
|
|
|
|
// Instead, there are multiple device public keys.
|
|
|
|
|
|
|
|
const deviceId = 1;
|
|
|
|
|
|
|
|
const address = new libsignal.SignalProtocolAddress(
|
|
|
|
|
|
|
|
devicePubKey,
|
|
|
|
|
|
|
|
deviceId
|
|
|
|
|
|
|
|
);
|
|
|
|
const ourKey = textsecure.storage.user.getNumber();
|
|
|
|
const ourKey = textsecure.storage.user.getNumber();
|
|
|
|
const options = {};
|
|
|
|
const options = {};
|
|
|
|
const fallBackCipher = new libloki.crypto.FallBackSessionCipher(
|
|
|
|
const fallBackCipher = new libloki.crypto.FallBackSessionCipher(
|
|
|
@ -302,6 +312,23 @@ OutgoingMessage.prototype = {
|
|
|
|
// Check if we need to attach the preKeys
|
|
|
|
// Check if we need to attach the preKeys
|
|
|
|
let sessionCipher;
|
|
|
|
let sessionCipher;
|
|
|
|
const isFriendRequest = this.messageType === 'friend-request';
|
|
|
|
const isFriendRequest = this.messageType === 'friend-request';
|
|
|
|
|
|
|
|
const isSecondaryDevice = !!window.storage.get('isSecondaryDevice');
|
|
|
|
|
|
|
|
if (isFriendRequest && isSecondaryDevice) {
|
|
|
|
|
|
|
|
// Attach authorisation from primary device ONLY FOR FRIEND REQUEST
|
|
|
|
|
|
|
|
const ourPubKeyHex = textsecure.storage.user.getNumber();
|
|
|
|
|
|
|
|
const pairingAuthorisation = await libloki.storage.getGrantAuthorisationForSecondaryPubKey(
|
|
|
|
|
|
|
|
ourPubKeyHex
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
if (pairingAuthorisation) {
|
|
|
|
|
|
|
|
this.message.pairingAuthorisation = libloki.api.createPairingAuthorisationProtoMessage(
|
|
|
|
|
|
|
|
pairingAuthorisation
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
window.log.error(
|
|
|
|
|
|
|
|
'Could not find authorisation for our own pubkey while being secondary device.'
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
this.fallBackEncryption = this.fallBackEncryption || isFriendRequest;
|
|
|
|
this.fallBackEncryption = this.fallBackEncryption || isFriendRequest;
|
|
|
|
const flags = this.message.dataMessage
|
|
|
|
const flags = this.message.dataMessage
|
|
|
|
? this.message.dataMessage.get_flags()
|
|
|
|
? this.message.dataMessage.get_flags()
|
|
|
@ -362,20 +389,41 @@ OutgoingMessage.prototype = {
|
|
|
|
sourceDevice: 1,
|
|
|
|
sourceDevice: 1,
|
|
|
|
destinationRegistrationId: ciphertext.registrationId,
|
|
|
|
destinationRegistrationId: ciphertext.registrationId,
|
|
|
|
content: ciphertext.body,
|
|
|
|
content: ciphertext.body,
|
|
|
|
|
|
|
|
pubKey: devicePubKey,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
})
|
|
|
|
})
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.then(async outgoingObjects => {
|
|
|
|
.then(async outgoingObjects => {
|
|
|
|
// TODO: handle multiple devices/messages per transmit
|
|
|
|
// TODO: handle multiple devices/messages per transmit
|
|
|
|
const outgoingObject = outgoingObjects[0];
|
|
|
|
const promises = outgoingObjects.map(async outgoingObject => {
|
|
|
|
const socketMessage = await this.wrapInWebsocketMessage(outgoingObject);
|
|
|
|
const destination = outgoingObject.pubKey;
|
|
|
|
await this.transmitMessage(
|
|
|
|
try {
|
|
|
|
number,
|
|
|
|
const socketMessage = await this.wrapInWebsocketMessage(
|
|
|
|
socketMessage,
|
|
|
|
outgoingObject
|
|
|
|
this.timestamp,
|
|
|
|
);
|
|
|
|
outgoingObject.ttl
|
|
|
|
await this.transmitMessage(
|
|
|
|
);
|
|
|
|
destination,
|
|
|
|
this.successfulNumbers[this.successfulNumbers.length] = number;
|
|
|
|
socketMessage,
|
|
|
|
|
|
|
|
this.timestamp,
|
|
|
|
|
|
|
|
outgoingObject.ttl
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
this.successfulNumbers.push(destination);
|
|
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
|
|
e.number = destination;
|
|
|
|
|
|
|
|
this.errors.push(e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
await Promise.all(promises);
|
|
|
|
|
|
|
|
// TODO: the retrySend should only send to the devices
|
|
|
|
|
|
|
|
// for which the transmission failed.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ensure numberCompleted() will execute the callback
|
|
|
|
|
|
|
|
this.numbersCompleted +=
|
|
|
|
|
|
|
|
this.errors.length + this.successfulNumbers.length;
|
|
|
|
|
|
|
|
// Absorb errors if message sent to at least 1 device
|
|
|
|
|
|
|
|
if (this.successfulNumbers.length > 0) {
|
|
|
|
|
|
|
|
this.errors = [];
|
|
|
|
|
|
|
|
}
|
|
|
|
this.numberCompleted();
|
|
|
|
this.numberCompleted();
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.catch(error => {
|
|
|
|
.catch(error => {
|
|
|
@ -431,7 +479,7 @@ OutgoingMessage.prototype = {
|
|
|
|
window.log.error(
|
|
|
|
window.log.error(
|
|
|
|
'Got "key changed" error from encrypt - no identityKey for application layer',
|
|
|
|
'Got "key changed" error from encrypt - no identityKey for application layer',
|
|
|
|
number,
|
|
|
|
number,
|
|
|
|
deviceIds
|
|
|
|
devicesPubKeys
|
|
|
|
);
|
|
|
|
);
|
|
|
|
throw error;
|
|
|
|
throw error;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|