You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			204 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			204 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			TypeScript
		
	
| import { SignalService } from '../protobuf';
 | |
| import { removeFromCache } from './cache';
 | |
| import { EnvelopePlus } from './types';
 | |
| import { MediumGroupResponseKeysMessage } from '../session/messages/outgoing';
 | |
| import { getMessageQueue } from '../session';
 | |
| import { PubKey } from '../session/types';
 | |
| import _ from 'lodash';
 | |
| 
 | |
| import * as SenderKeyAPI from '../session/medium_group';
 | |
| import { StringUtils } from '../session/utils';
 | |
| 
 | |
| async function handleSenderKeyRequest(
 | |
|   envelope: EnvelopePlus,
 | |
|   groupUpdate: any
 | |
| ) {
 | |
|   const { StringView, textsecure, log } = window;
 | |
| 
 | |
|   const senderIdentity = envelope.source;
 | |
|   const ourIdentity = await textsecure.storage.user.getNumber();
 | |
|   const { groupId } = groupUpdate;
 | |
| 
 | |
|   log.debug('[sender key] sender key request from:', senderIdentity);
 | |
| 
 | |
|   // We reuse the same message type for sender keys
 | |
|   const { chainKey, keyIdx } = await SenderKeyAPI.getChainKey(
 | |
|     groupId,
 | |
|     ourIdentity
 | |
|   );
 | |
| 
 | |
|   const chainKeyHex = StringView.arrayBufferToHex(chainKey);
 | |
|   const responseParams = {
 | |
|     timestamp: Date.now(),
 | |
|     groupId,
 | |
|     senderKey: {
 | |
|       chainKey: chainKeyHex,
 | |
|       keyIdx,
 | |
|       pubKey: ourIdentity,
 | |
|     },
 | |
|   };
 | |
| 
 | |
|   const keysResponseMessage = new MediumGroupResponseKeysMessage(
 | |
|     responseParams
 | |
|   );
 | |
| 
 | |
|   const senderPubKey = new PubKey(senderIdentity);
 | |
|   await getMessageQueue().send(senderPubKey, keysResponseMessage);
 | |
| 
 | |
|   await removeFromCache(envelope);
 | |
| }
 | |
| 
 | |
| async function handleSenderKey(envelope: EnvelopePlus, groupUpdate: any) {
 | |
|   const { log } = window;
 | |
|   const { groupId, senderKey } = groupUpdate;
 | |
|   const senderIdentity = envelope.source;
 | |
| 
 | |
|   log.debug('[sender key] got a new sender key from:', senderIdentity);
 | |
| 
 | |
|   await SenderKeyAPI.saveSenderKeys(
 | |
|     groupId,
 | |
|     PubKey.cast(senderIdentity),
 | |
|     senderKey.chainKey,
 | |
|     senderKey.keyIdx
 | |
|   );
 | |
| 
 | |
|   await removeFromCache(envelope);
 | |
| }
 | |
| 
 | |
| async function handleNewGroup(
 | |
|   envelope: EnvelopePlus,
 | |
|   groupUpdate: SignalService.MediumGroupUpdate
 | |
| ) {
 | |
|   const { Whisper, log } = window;
 | |
| 
 | |
|   const senderIdentity = envelope.source;
 | |
| 
 | |
|   const {
 | |
|     name,
 | |
|     groupPublicKey,
 | |
|     groupPrivateKey,
 | |
|     members: membersBinary,
 | |
|     admins: adminsBinary,
 | |
|     senderKeys,
 | |
|   } = groupUpdate;
 | |
| 
 | |
|   const groupId = StringUtils.decode(groupPublicKey, 'hex');
 | |
|   const maybeConvo = await window.ConversationController.get(groupId);
 | |
| 
 | |
|   const members = membersBinary.map((pk: Uint8Array) =>
 | |
|     StringUtils.decode(pk, 'hex')
 | |
|   );
 | |
| 
 | |
|   const admins = adminsBinary.map((pk: Uint8Array) =>
 | |
|     StringUtils.decode(pk, 'hex')
 | |
|   );
 | |
| 
 | |
|   const groupExists = !!maybeConvo;
 | |
|   const convo = groupExists
 | |
|     ? maybeConvo
 | |
|     : await window.ConversationController.getOrCreateAndWait(groupId, 'group');
 | |
| 
 | |
|   {
 | |
|     // Add group update message
 | |
|     const now = Date.now();
 | |
|     const message = convo.messageCollection.add({
 | |
|       conversationId: convo.id,
 | |
|       type: 'incoming',
 | |
|       sent_at: now,
 | |
|       received_at: now,
 | |
|       group_update: {
 | |
|         name,
 | |
|         members,
 | |
|       },
 | |
|     });
 | |
| 
 | |
|     const messageId = await window.Signal.Data.saveMessage(message.attributes, {
 | |
|       Message: Whisper.Message,
 | |
|     });
 | |
|     message.set({ id: messageId });
 | |
|   }
 | |
| 
 | |
|   if (groupExists) {
 | |
|     // ***** Updating the group *****
 | |
|     log.info('Received a group update for medium group:', groupId);
 | |
| 
 | |
|     // Check that the sender is admin (make sure it words with multidevice)
 | |
|     const isAdmin = convo.get('groupAdmins').includes(senderIdentity);
 | |
| 
 | |
|     if (!isAdmin) {
 | |
|       log.warn('Rejected attempt to update a group by non-admin');
 | |
|       await removeFromCache(envelope);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     convo.set('name', name);
 | |
|     convo.set('members', members);
 | |
| 
 | |
|     // TODO: check that we are still in the group (when we enable deleting members)
 | |
|     convo.saveChangesToDB();
 | |
| 
 | |
|     // Update other fields. Add a corresponding "update" message to the conversation
 | |
|   } else {
 | |
|     // ***** Creating a new group *****
 | |
|     log.info('Received a new medium group:', groupId);
 | |
| 
 | |
|     // TODO: Check that we are even a part of this group?
 | |
| 
 | |
|     convo.set('is_medium_group', true);
 | |
|     convo.set('active_at', Date.now());
 | |
|     convo.set('name', name);
 | |
|     convo.set('groupAdmins', admins);
 | |
| 
 | |
|     const secretKeyHex = StringUtils.decode(groupPrivateKey, 'hex');
 | |
| 
 | |
|     await window.Signal.Data.createOrUpdateIdentityKey({
 | |
|       id: groupId,
 | |
|       secretKey: secretKeyHex,
 | |
|     });
 | |
| 
 | |
|     // Save everyone's ratchet key
 | |
|     await Promise.all(
 | |
|       senderKeys.map(async senderKey => {
 | |
|         // Note that keyIndex is a number and 0 is considered a valid value:
 | |
|         if (
 | |
|           senderKey.chainKey &&
 | |
|           senderKey.keyIndex !== undefined &&
 | |
|           senderKey.publicKey
 | |
|         ) {
 | |
|           const pubKey = StringUtils.decode(senderKey.publicKey, 'hex');
 | |
|           const chainKey = StringUtils.decode(senderKey.chainKey, 'hex');
 | |
|           const keyIndex = senderKey.keyIndex as number;
 | |
|           await SenderKeyAPI.saveSenderKeys(
 | |
|             groupId,
 | |
|             PubKey.cast(pubKey),
 | |
|             chainKey,
 | |
|             keyIndex
 | |
|           );
 | |
|         } else {
 | |
|           log.error('Received invalid sender key');
 | |
|         }
 | |
|       })
 | |
|     );
 | |
| 
 | |
|     window.SwarmPolling.addGroupId(PubKey.cast(groupId));
 | |
|   }
 | |
| 
 | |
|   await removeFromCache(envelope);
 | |
| }
 | |
| 
 | |
| export async function handleMediumGroupUpdate(
 | |
|   envelope: EnvelopePlus,
 | |
|   groupUpdate: any
 | |
| ) {
 | |
|   const { type } = groupUpdate;
 | |
|   const { Type } = SignalService.MediumGroupUpdate;
 | |
| 
 | |
|   if (type === Type.SENDER_KEY_REQUEST) {
 | |
|     await handleSenderKeyRequest(envelope, groupUpdate);
 | |
|   } else if (type === Type.SENDER_KEY) {
 | |
|     await handleSenderKey(envelope, groupUpdate);
 | |
|   } else if (type === Type.NEW) {
 | |
|     await handleNewGroup(envelope, groupUpdate);
 | |
|   }
 | |
| }
 |