Merge clearnet

pull/1495/head
Audric Ackermann 4 years ago
parent ebf9714e49
commit 7b81c4213a
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 B

@ -287,7 +287,7 @@ const serverRequest = async (endpoint, options = {}) => {
txtResponse = await result.text(); txtResponse = await result.text();
// cloudflare timeouts (504s) will be html... // cloudflare timeouts (504s) will be html...
response = options.textResponse ? txtResponse : JSON.parse(txtResponse); response = options.noJson ? txtResponse : JSON.parse(txtResponse);
// result.status will always be 200 // result.status will always be 200
// emulate the correct http code if available // emulate the correct http code if available
@ -303,7 +303,7 @@ const serverRequest = async (endpoint, options = {}) => {
e.message, e.message,
`json: ${txtResponse}`, `json: ${txtResponse}`,
'attempting connection to', 'attempting connection to',
url url.toString()
); );
} else { } else {
log.error( log.error(
@ -311,7 +311,7 @@ const serverRequest = async (endpoint, options = {}) => {
e.code, e.code,
e.message, e.message,
'attempting connection to', 'attempting connection to',
url url.toString()
); );
} }

@ -61,6 +61,7 @@ window.lokiFeatureFlags = {
useFileOnionRequests: true, useFileOnionRequests: true,
useFileOnionRequestsV2: true, // more compact encoding of files in response useFileOnionRequestsV2: true, // more compact encoding of files in response
onionRequestHops: 3, onionRequestHops: 3,
useRequestEncryptionKeyPair: false,
}; };
if ( if (
@ -85,7 +86,7 @@ window.isBeforeVersion = (toCheck, baseVersion) => {
}; };
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
window.CONSTANTS = new (function() { window.CONSTANTS = new (function () {
this.MAX_GROUP_NAME_LENGTH = 64; this.MAX_GROUP_NAME_LENGTH = 64;
this.DEFAULT_PUBLIC_CHAT_URL = appConfig.get('defaultPublicChatServer'); this.DEFAULT_PUBLIC_CHAT_URL = appConfig.get('defaultPublicChatServer');
this.MAX_LINKED_DEVICES = 1; this.MAX_LINKED_DEVICES = 1;
@ -376,7 +377,7 @@ window.callWorker = (fnName, ...args) => utilWorker.callWorker(fnName, ...args);
// Linux seems to periodically let the event loop stop, so this is a global workaround // Linux seems to periodically let the event loop stop, so this is a global workaround
setInterval(() => { setInterval(() => {
window.nodeSetImmediate(() => {}); window.nodeSetImmediate(() => { });
}, 1000); }, 1000);
const { autoOrientImage } = require('./js/modules/auto_orient_image'); const { autoOrientImage } = require('./js/modules/auto_orient_image');
@ -455,9 +456,9 @@ if (process.env.USE_STUBBED_NETWORK) {
} }
// eslint-disable-next-line no-extend-native,func-names // eslint-disable-next-line no-extend-native,func-names
Promise.prototype.ignore = function() { Promise.prototype.ignore = function () {
// eslint-disable-next-line more/no-then // eslint-disable-next-line more/no-then
this.then(() => {}); this.then(() => { });
}; };
if ( if (
@ -484,6 +485,7 @@ if (config.environment.includes('test-integration')) {
useOnionRequests: false, useOnionRequests: false,
useFileOnionRequests: false, useFileOnionRequests: false,
useOnionRequestsV2: false, useOnionRequestsV2: false,
useRequestEncryptionKeyPair: false,
}; };
/* eslint-disable global-require, import/no-extraneous-dependencies */ /* eslint-disable global-require, import/no-extraneous-dependencies */
window.sinon = require('sinon'); window.sinon = require('sinon');

@ -33,6 +33,11 @@ import { MessageController } from '../session/messages';
import { ClosedGroupEncryptionPairReplyMessage } from '../session/messages/outgoing/content/data/group'; import { ClosedGroupEncryptionPairReplyMessage } from '../session/messages/outgoing/content/data/group';
import { queueAllCachedFromSource } from './receiver'; import { queueAllCachedFromSource } from './receiver';
export const distributingClosedGroupEncryptionKeyPairs = new Map<
string,
ECKeyPair
>();
export async function handleClosedGroupControlMessage( export async function handleClosedGroupControlMessage(
envelope: EnvelopePlus, envelope: EnvelopePlus,
groupUpdate: SignalService.DataMessage.ClosedGroupControlMessage groupUpdate: SignalService.DataMessage.ClosedGroupControlMessage
@ -456,6 +461,9 @@ async function handleClosedGroupEncryptionKeyPair(
); );
if (isKeyPairAlreadyHere) { if (isKeyPairAlreadyHere) {
const existingKeyPairs = await getAllEncryptionKeyPairsForGroup(
groupPublicKey
);
window.log.info('Dropping already saved keypair for group', groupPublicKey); window.log.info('Dropping already saved keypair for group', groupPublicKey);
await removeFromCache(envelope); await removeFromCache(envelope);
return; return;
@ -532,11 +540,18 @@ async function performIfValid(
} else if (groupUpdate.type === Type.MEMBER_LEFT) { } else if (groupUpdate.type === Type.MEMBER_LEFT) {
await handleClosedGroupMemberLeft(envelope, groupUpdate, convo); await handleClosedGroupMemberLeft(envelope, groupUpdate, convo);
} else if (groupUpdate.type === Type.ENCRYPTION_KEY_PAIR_REQUEST) { } else if (groupUpdate.type === Type.ENCRYPTION_KEY_PAIR_REQUEST) {
await handleClosedGroupEncryptionKeyPairRequest( if (window.lokiFeatureFlags.useRequestEncryptionKeyPair) {
envelope, await handleClosedGroupEncryptionKeyPairRequest(
groupUpdate, envelope,
convo groupUpdate,
); convo
);
} else {
window.log.warn(
'Received ENCRYPTION_KEY_PAIR_REQUEST message but it is not enabled for now.'
);
await removeFromCache(envelope);
}
// if you add a case here, remember to add it where performIfValid is called too. // if you add a case here, remember to add it where performIfValid is called too.
} }
@ -590,6 +605,15 @@ async function handleClosedGroupMembersAdded(
return; return;
} }
if (await areWeAdmin(convo)) {
await sendLatestKeyPairToUsers(
envelope,
convo,
convo.id,
membersNotAlreadyPresent
);
}
const members = [...oldMembers, ...membersNotAlreadyPresent]; const members = [...oldMembers, ...membersNotAlreadyPresent];
// Only add update message if we have something to show // Only add update message if we have something to show
@ -604,6 +628,16 @@ async function handleClosedGroupMembersAdded(
await removeFromCache(envelope); await removeFromCache(envelope);
} }
async function areWeAdmin(groupConvo: ConversationModel) {
if (!groupConvo) {
throw new Error('areWeAdmin needs a convo');
}
const groupAdmins = groupConvo.get('groupAdmins');
const ourNumber = UserUtils.getOurPubKeyStrFromCache();
return groupAdmins?.includes(ourNumber) || false;
}
async function handleClosedGroupMembersRemoved( async function handleClosedGroupMembersRemoved(
envelope: EnvelopePlus, envelope: EnvelopePlus,
groupUpdate: SignalService.DataMessage.ClosedGroupControlMessage, groupUpdate: SignalService.DataMessage.ClosedGroupControlMessage,
@ -650,8 +684,7 @@ async function handleClosedGroupMembersRemoved(
window.SwarmPolling.removePubkey(groupPubKey); window.SwarmPolling.removePubkey(groupPubKey);
} }
// Generate and distribute a new encryption key pair if needed // Generate and distribute a new encryption key pair if needed
const isCurrentUserAdmin = firstAdmin === ourPubKey.key; if (await areWeAdmin(convo)) {
if (isCurrentUserAdmin) {
try { try {
await ClosedGroup.generateAndSendNewEncryptionKeyPair( await ClosedGroup.generateAndSendNewEncryptionKeyPair(
groupPubKey, groupPubKey,
@ -731,58 +764,83 @@ async function handleClosedGroupMemberLeft(
await removeFromCache(envelope); await removeFromCache(envelope);
} }
async function handleClosedGroupEncryptionKeyPairRequest( async function sendLatestKeyPairToUsers(
envelope: EnvelopePlus, envelope: EnvelopePlus,
groupUpdate: SignalService.DataMessage.ClosedGroupControlMessage, groupConvo: ConversationModel,
convo: ConversationModel groupPubKey: string,
targetUsers: Array<string>
) { ) {
const sender = envelope.senderIdentity; // use the inMemory keypair if found
const groupPublicKey = envelope.source; const inMemoryKeyPair = distributingClosedGroupEncryptionKeyPairs.get(
// Guard against self-sends groupPubKey
if (UserUtils.isUsFromCache(sender)) { );
window.log.info(
'Dropping self send message of type ENCRYPTION_KEYPAIR_REQUEST'
);
await removeFromCache(envelope);
return;
}
// Get the latest encryption key pair // Get the latest encryption key pair
const latestKeyPair = await getLatestClosedGroupEncryptionKeyPair( const latestKeyPair = await getLatestClosedGroupEncryptionKeyPair(
groupPublicKey groupPubKey
); );
if (!latestKeyPair) { if (!inMemoryKeyPair && !latestKeyPair) {
window.log.info( window.log.info(
'We do not have the keypair ourself, so dropping this message.' 'We do not have the keypair ourself, so dropping this message.'
); );
await removeFromCache(envelope);
return; return;
} }
window.log.info( const expireTimer = groupConvo.get('expireTimer') || 0;
`Responding to closed group encryption key pair request from: ${sender}`
);
await ConversationController.getInstance().getOrCreateAndWait(
sender,
'private'
);
const wrappers = await ClosedGroup.buildEncryptionKeyPairWrappers( await Promise.all(
[sender], targetUsers.map(async member => {
ECKeyPair.fromHexKeyPair(latestKeyPair) window.log.info(
); `Sending latest closed group encryption key pair to: ${member}`
const expireTimer = convo.get('expireTimer') || 0; );
await ConversationController.getInstance().getOrCreateAndWait(
member,
'private'
);
const keypairsMessage = new ClosedGroupEncryptionPairReplyMessage({ const wrappers = await ClosedGroup.buildEncryptionKeyPairWrappers(
groupId: groupPublicKey, [member],
timestamp: Date.now(), inMemoryKeyPair || ECKeyPair.fromHexKeyPair(latestKeyPair)
encryptedKeyPairs: wrappers, );
expireTimer,
});
// the encryption keypair is sent using established channels const keypairsMessage = new ClosedGroupEncryptionPairReplyMessage({
await getMessageQueue().sendToPubKey(PubKey.cast(sender), keypairsMessage); groupId: groupPubKey,
timestamp: Date.now(),
encryptedKeyPairs: wrappers,
expireTimer,
});
// the encryption keypair is sent using established channels
await getMessageQueue().sendToPubKey(
PubKey.cast(member),
keypairsMessage
);
})
);
}
await removeFromCache(envelope); async function handleClosedGroupEncryptionKeyPairRequest(
envelope: EnvelopePlus,
groupUpdate: SignalService.DataMessage.ClosedGroupControlMessage,
groupConvo: ConversationModel
) {
if (!window.lokiFeatureFlags.useRequestEncryptionKeyPair) {
throw new Error('useRequestEncryptionKeyPair is disabled');
}
const sender = envelope.senderIdentity;
const groupPublicKey = envelope.source;
// Guard against self-sends
if (UserUtils.isUsFromCache(sender)) {
window.log.info(
'Dropping self send message of type ENCRYPTION_KEYPAIR_REQUEST'
);
await removeFromCache(envelope);
return;
}
await sendLatestKeyPairToUsers(envelope, groupConvo, groupPublicKey, [
sender,
]);
return removeFromCache(envelope);
} }
export async function createClosedGroup( export async function createClosedGroup(

@ -124,18 +124,23 @@ async function decryptForClosedGroup(
'decryptWithSessionProtocol for medium group message throw:', 'decryptWithSessionProtocol for medium group message throw:',
e e
); );
const keypairRequestManager = KeyPairRequestManager.getInstance();
const groupPubKey = PubKey.cast(envelope.source); const groupPubKey = PubKey.cast(envelope.source);
if (keypairRequestManager.canTriggerRequestWith(groupPubKey)) {
keypairRequestManager.markRequestSendFor(groupPubKey, Date.now()); // To enable back if we decide to enable encryption key pair request work again
await requestEncryptionKeyPair(groupPubKey); if (window.lokiFeatureFlags.useRequestEncryptionKeyPair) {
const keypairRequestManager = KeyPairRequestManager.getInstance();
if (keypairRequestManager.canTriggerRequestWith(groupPubKey)) {
keypairRequestManager.markRequestSendFor(groupPubKey, Date.now());
await requestEncryptionKeyPair(groupPubKey);
}
} }
// IMPORTANT do not remove the message from the cache just yet.
// We will try to decrypt it once we get the encryption keypair.
// for that to work, we need to throw an error just like here.
throw new Error( throw new Error(
`Waiting for an encryption keypair to be received for group ${groupPubKey.key}` `Waiting for an encryption keypair to be received for group ${groupPubKey.key}`
); );
// do not remove it from the cache yet. We will try to decrypt it once we get the encryption keypair
// TODO drop it if after some time we still don't get to decrypt it
// await removeFromCache(envelope);
return null; return null;
} }
} }

@ -32,6 +32,7 @@ import { ConversationModel } from '../../models/conversation';
import { MessageModel } from '../../models/message'; import { MessageModel } from '../../models/message';
import { MessageModelType } from '../../models/messageType'; import { MessageModelType } from '../../models/messageType';
import { MessageController } from '../messages'; import { MessageController } from '../messages';
import { distributingClosedGroupEncryptionKeyPairs } from '../../receiver/closedGroups';
export interface GroupInfo { export interface GroupInfo {
id: string; id: string;
@ -557,11 +558,15 @@ export async function generateAndSendNewEncryptionKeyPair(
expireTimer, expireTimer,
}); });
distributingClosedGroupEncryptionKeyPairs.set(toHex(groupId), newKeyPair);
const messageSentCallback = async () => { const messageSentCallback = async () => {
window.log.info( window.log.info(
`KeyPairMessage for ClosedGroup ${groupPublicKey} is sent. Saving the new encryptionKeyPair.` `KeyPairMessage for ClosedGroup ${groupPublicKey} is sent. Saving the new encryptionKeyPair.`
); );
distributingClosedGroupEncryptionKeyPairs.delete(toHex(groupId));
await addClosedGroupEncryptionKeyPair( await addClosedGroupEncryptionKeyPair(
toHex(groupId), toHex(groupId),
newKeyPair.toHexKeyPair() newKeyPair.toHexKeyPair()
@ -611,6 +616,10 @@ export async function buildEncryptionKeyPairWrappers(
export async function requestEncryptionKeyPair( export async function requestEncryptionKeyPair(
groupPublicKey: string | PubKey groupPublicKey: string | PubKey
) { ) {
if (!window.lokiFeatureFlags.useRequestEncryptionKeyPair) {
throw new Error('useRequestEncryptionKeyPair is disabled');
}
const groupConvo = ConversationController.getInstance().get( const groupConvo = ConversationController.getInstance().get(
PubKey.cast(groupPublicKey).key PubKey.cast(groupPublicKey).key
); );

1
ts/window.d.ts vendored

@ -64,6 +64,7 @@ declare global {
useFileOnionRequests: boolean; useFileOnionRequests: boolean;
useFileOnionRequestsV2: boolean; useFileOnionRequestsV2: boolean;
onionRequestHops: number; onionRequestHops: number;
useRequestEncryptionKeyPair: boolean;
}; };
lokiFileServerAPI: LokiFileServerInstance; lokiFileServerAPI: LokiFileServerInstance;
lokiMessageAPI: LokiMessageInterface; lokiMessageAPI: LokiMessageInterface;

Loading…
Cancel
Save