Remove GroupContext proto

Co-authored-by: Scott Nonnenberg <scott@signal.org>
This commit is contained in:
Josh Perez 2023-04-14 20:52:50 -04:00 committed by GitHub
parent 9bfbee464b
commit 68ae25f5cd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 74 additions and 713 deletions

View file

@ -47,7 +47,7 @@ import { parseIntOrThrow } from '../util/parseIntOrThrow';
import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary';
import { Zone } from '../util/Zone';
import { DurationInSeconds } from '../util/durations';
import { deriveMasterKeyFromGroupV1, bytesToUuid } from '../Crypto';
import { bytesToUuid } from '../Crypto';
import type { DownloadedAttachmentType } from '../types/Attachment';
import { Address } from '../types/Address';
import { QualifiedAddress } from '../types/QualifiedAddress';
@ -127,7 +127,6 @@ import { inspectUnknownFieldTags } from '../util/inspectProtobufs';
import { incrementMessageCounter } from '../util/incrementMessageCounter';
import { filterAndClean } from '../types/BodyRange';
const GROUPV1_ID_LENGTH = 16;
const GROUPV2_ID_LENGTH = 32;
const RETRY_TIMEOUT = 2 * 60 * 1000;
@ -2007,19 +2006,8 @@ export default class MessageReceiver
const message = this.processDecrypted(envelope, msg);
const groupId = this.getProcessedGroupId(message);
const isBlocked = groupId ? this.isGroupBlocked(groupId) : false;
const { source, sourceUuid } = envelope;
const ourE164 = this.storage.user.getNumber();
const ourUuid = this.storage.user.getCheckedUuid().toString();
const isMe =
(source && ourE164 && source === ourE164) ||
(sourceUuid && ourUuid && sourceUuid === ourUuid);
const isLeavingGroup = Boolean(
!message.groupV2 &&
message.group &&
message.group.type === Proto.GroupContext.Type.QUIT
);
if (groupId && isBlocked && !(isMe && isLeavingGroup)) {
if (groupId && isBlocked) {
log.warn(
`Message ${getEnvelopeId(envelope)} ignored; destined for blocked group`
);
@ -2282,19 +2270,8 @@ export default class MessageReceiver
const message = this.processDecrypted(envelope, msg.dataMessage);
const groupId = this.getProcessedGroupId(message);
const isBlocked = groupId ? this.isGroupBlocked(groupId) : false;
const { source, sourceUuid } = envelope;
const ourE164 = this.storage.user.getNumber();
const ourUuid = this.storage.user.getCheckedUuid().toString();
const isMe =
(source && ourE164 && source === ourE164) ||
(sourceUuid && ourUuid && sourceUuid === ourUuid);
const isLeavingGroup = Boolean(
!message.groupV2 &&
message.group &&
message.group.type === Proto.GroupContext.Type.QUIT
);
if (groupId && isBlocked && !(isMe && isLeavingGroup)) {
if (groupId && isBlocked) {
log.warn(
`Message ${getEnvelopeId(envelope)} ignored; destined for blocked group`
);
@ -2354,8 +2331,6 @@ export default class MessageReceiver
return undefined;
}
this.checkGroupV1Data(msg);
if (msg.flags && msg.flags & Proto.DataMessage.Flags.END_SESSION) {
p = this.handleEndSession(envelope, new UUID(destination));
}
@ -2393,8 +2368,6 @@ export default class MessageReceiver
msg.flags & Proto.DataMessage.Flags.EXPIRATION_TIMER_UPDATE
) {
type = 'expirationTimerUpdate';
} else if (msg.group) {
type = 'legacyGroupChange';
}
// Note: other data messages without any of these attributes will fall into the
// 'message' bucket - like stickers, gift badges, etc.
@ -2404,19 +2377,8 @@ export default class MessageReceiver
const message = this.processDecrypted(envelope, msg);
const groupId = this.getProcessedGroupId(message);
const isBlocked = groupId ? this.isGroupBlocked(groupId) : false;
const { source, sourceUuid } = envelope;
const ourE164 = this.storage.user.getNumber();
const ourUuid = this.storage.user.getCheckedUuid().toString();
const isMe =
(source && ourE164 && source === ourE164) ||
(sourceUuid && ourUuid && sourceUuid === ourUuid);
const isLeavingGroup = Boolean(
!message.groupV2 &&
message.group &&
message.group.type === Proto.GroupContext.Type.QUIT
);
if (groupId && isBlocked && !(isMe && isLeavingGroup)) {
if (groupId && isBlocked) {
log.warn(
`Message ${getEnvelopeId(envelope)} ignored; destined for blocked group`
);
@ -2780,17 +2742,11 @@ export default class MessageReceiver
const { groupId, timestamp, action } = typingMessage;
let groupIdString: string | undefined;
let groupV2IdString: string | undefined;
if (groupId && groupId.byteLength > 0) {
if (groupId.byteLength === GROUPV1_ID_LENGTH) {
groupIdString = Bytes.toBinary(groupId);
groupV2IdString = this.deriveGroupV2FromV1(groupId);
} else if (groupId.byteLength === GROUPV2_ID_LENGTH) {
groupV2IdString = Bytes.toBase64(groupId);
} else {
log.error('handleTypingMessage: Received invalid groupId value');
}
if (groupId && groupId.byteLength === GROUPV2_ID_LENGTH) {
groupV2IdString = Bytes.toBase64(groupId);
} else {
log.error('handleTypingMessage: Received invalid groupId value');
}
this.dispatchEvent(
@ -2799,13 +2755,11 @@ export default class MessageReceiver
senderUuid: envelope.sourceUuid,
senderDevice: envelope.sourceDevice,
typing: {
groupV2Id: groupV2IdString,
typingMessage,
timestamp: timestamp?.toNumber() ?? Date.now(),
started: action === Proto.TypingMessage.Action.STARTED,
stopped: action === Proto.TypingMessage.Action.STOPPED,
groupId: groupIdString,
groupV2Id: groupV2IdString,
},
})
);
@ -2823,22 +2777,7 @@ export default class MessageReceiver
message: Proto.IDataMessage,
envelope: ProcessedEnvelope
): boolean {
const { group, groupV2 } = message;
if (group) {
const { id } = group;
strictAssert(id, 'Group data has no id');
const isInvalid = id.byteLength !== GROUPV1_ID_LENGTH;
if (isInvalid) {
log.info(
'isInvalidGroupData: invalid GroupV1 message from',
getEnvelopeId(envelope)
);
}
return isInvalid;
}
const { groupV2 } = message;
if (groupV2) {
const { masterKey } = groupV2;
@ -2857,46 +2796,12 @@ export default class MessageReceiver
return false;
}
private deriveGroupV2FromV1(groupId: Uint8Array): string {
if (groupId.byteLength !== GROUPV1_ID_LENGTH) {
throw new Error(
`deriveGroupV2FromV1: had id with wrong byteLength: ${groupId.byteLength}`
);
}
const masterKey = deriveMasterKeyFromGroupV1(groupId);
const data = deriveGroupFields(masterKey);
return Bytes.toBase64(data.id);
}
private checkGroupV1Data(message: Readonly<Proto.IDataMessage>): void {
const { group } = message;
if (!group) {
return;
}
if (!group.id) {
throw new Error('deriveGroupV1Data: had falsey id');
}
const { id } = group;
if (id.byteLength !== GROUPV1_ID_LENGTH) {
throw new Error(
`deriveGroupV1Data: had id with wrong byteLength: ${id.byteLength}`
);
}
}
private getProcessedGroupId(
message: ProcessedDataMessage
): string | undefined {
if (message.groupV2) {
return message.groupV2.id;
}
if (message.group && message.group.id) {
return message.group.id;
}
return undefined;
}
@ -2906,9 +2811,6 @@ export default class MessageReceiver
const { id } = deriveGroupFields(message.groupV2.masterKey);
return Bytes.toBase64(id);
}
if (message.group && message.group.id) {
return Bytes.toBinary(message.group.id);
}
return undefined;
}
@ -2917,10 +2819,6 @@ export default class MessageReceiver
if (sentMessage.message && sentMessage.message.groupV2) {
return `groupv2(${this.getGroupId(sentMessage.message)})`;
}
if (sentMessage.message && sentMessage.message.group) {
strictAssert(sentMessage.message.group.id, 'group without id');
return `group(${this.getGroupId(sentMessage.message)})`;
}
return sentMessage.destination || sentMessage.destinationUuid;
}
@ -2998,8 +2896,6 @@ export default class MessageReceiver
return;
}
this.checkGroupV1Data(sentMessage.message);
strictAssert(sentMessage.timestamp, 'sent message without timestamp');
log.info(
@ -3192,19 +3088,13 @@ export default class MessageReceiver
const { groupId } = sync;
let groupIdString: string | undefined;
let groupV2IdString: string | undefined;
if (groupId && groupId.byteLength > 0) {
if (groupId.byteLength === GROUPV1_ID_LENGTH) {
groupIdString = Bytes.toBinary(groupId);
groupV2IdString = this.deriveGroupV2FromV1(groupId);
} else if (groupId.byteLength === GROUPV2_ID_LENGTH) {
groupV2IdString = Bytes.toBase64(groupId);
} else {
this.removeFromCache(envelope);
log.error('Received message request with invalid groupId');
return undefined;
}
if (groupId && groupId.byteLength === GROUPV2_ID_LENGTH) {
groupV2IdString = Bytes.toBase64(groupId);
} else {
this.removeFromCache(envelope);
log.error('Received message request with invalid groupId');
return undefined;
}
const ev = new MessageRequestResponseEvent(
@ -3217,7 +3107,6 @@ export default class MessageReceiver
)
: undefined,
messageRequestResponseType: sync.type,
groupId: groupIdString,
groupV2Id: groupV2IdString,
},
this.removeFromCache.bind(this, envelope)
@ -3583,14 +3472,10 @@ export default class MessageReceiver
if (blocked.groupIds) {
const previous = this.storage.get('blocked-groups', []);
const groupV1Ids: Array<string> = [];
const groupIds: Array<string> = [];
blocked.groupIds.forEach(groupId => {
if (groupId.byteLength === GROUPV1_ID_LENGTH) {
groupV1Ids.push(Bytes.toBinary(groupId));
groupIds.push(this.deriveGroupV2FromV1(groupId));
} else if (groupId.byteLength === GROUPV2_ID_LENGTH) {
if (groupId.byteLength === GROUPV2_ID_LENGTH) {
groupIds.push(Bytes.toBase64(groupId));
} else {
log.error('handleBlocked: Received invalid groupId value');
@ -3598,18 +3483,15 @@ export default class MessageReceiver
});
log.info(
'handleBlocked: Blocking these groups - v2:',
groupIds.map(groupId => `groupv2(${groupId})`),
'v1:',
groupV1Ids.map(groupId => `group(${groupId})`)
groupIds.map(groupId => `groupv2(${groupId})`)
);
const ids = [...groupIds, ...groupV1Ids];
await this.storage.put('blocked-groups', ids);
await this.storage.put('blocked-groups', groupIds);
if (!areArraysMatchingSets(previous, ids)) {
if (!areArraysMatchingSets(previous, groupIds)) {
changed = true;
allIdentifiers.push(...previous);
allIdentifiers.push(...ids);
allIdentifiers.push(...groupIds);
}
}

View file

@ -103,10 +103,6 @@ export type GroupV2InfoType = {
revision: number;
members: ReadonlyArray<string>;
};
export type GroupV1InfoType = {
id: string;
members: ReadonlyArray<string>;
};
type GroupCallUpdateType = {
eraId: string;
@ -207,7 +203,6 @@ export type GroupSendOptionsType = {
expireTimer?: DurationInSeconds;
flags?: number;
groupCallUpdate?: GroupCallUpdateType;
groupV1?: GroupV1InfoType;
groupV2?: GroupV2InfoType;
messageText?: string;
preview?: ReadonlyArray<LinkPreviewType>;
@ -381,10 +376,6 @@ class Message {
proto.groupV2.masterKey = this.groupV2.masterKey;
proto.groupV2.revision = this.groupV2.revision;
proto.groupV2.groupChange = this.groupV2.groupChange || null;
} else if (this.group) {
proto.group = new Proto.GroupContext();
proto.group.id = Bytes.fromString(this.group.id);
proto.group.type = this.group.type;
}
if (this.sticker) {
proto.sticker = new Proto.DataMessage.Sticker();
@ -1106,7 +1097,6 @@ export default class MessageSender {
expireTimer,
flags,
groupCallUpdate,
groupV1,
groupV2,
messageText,
preview,
@ -1118,16 +1108,16 @@ export default class MessageSender {
timestamp,
} = options;
if (!groupV1 && !groupV2) {
if (!groupV2) {
throw new Error(
'getAttrsFromGroupOptions: Neither group1 nor groupv2 information provided!'
'getAttrsFromGroupOptions: No groupv2 information provided!'
);
}
const myE164 = window.textsecure.storage.user.getNumber();
const myUuid = window.textsecure.storage.user.getUuid()?.toString();
const groupMembers = groupV2?.members || groupV1?.members || [];
const groupMembers = groupV2?.members || [];
// We should always have a UUID but have this check just in case we don't.
let isNotMe: (recipient: string) => boolean;
@ -1158,12 +1148,6 @@ export default class MessageSender {
flags,
groupCallUpdate,
groupV2,
group: groupV1
? {
id: groupV1.id,
type: Proto.GroupContext.Type.DELIVER,
}
: undefined,
preview,
profileKey,
quote,
@ -2424,50 +2408,6 @@ export default class MessageSender {
});
}
// GroupV1-only functions; not to be used in the future
async leaveGroup(
groupId: string,
groupIdentifiers: Array<string>,
options?: SendOptionsType
): Promise<CallbackResultType> {
const timestamp = Date.now();
const proto = new Proto.Content({
dataMessage: {
group: {
id: Bytes.fromString(groupId),
type: Proto.GroupContext.Type.QUIT,
},
},
});
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
const contentHint = ContentHint.RESENDABLE;
const sendLogCallback =
groupIdentifiers.length > 1
? this.makeSendLogCallback({
contentHint,
proto: Buffer.from(Proto.Content.encode(proto).finish()),
sendType: 'legacyGroupChange',
timestamp,
urgent: false,
hasPniSignatureMessage: false,
})
: undefined;
return this.sendGroupProto({
contentHint,
groupId: undefined, // only for GV2 ids
options,
proto,
recipients: groupIdentifiers,
sendLogCallback,
timestamp,
urgent: false,
});
}
// Simple pass-throughs
// Note: instead of updating these functions, or adding new ones, remove these and go

View file

@ -118,17 +118,6 @@ export type ProcessedAttachment = {
textAttachment?: Omit<TextAttachmentType, 'preview'>;
};
export type ProcessedGroupContext = {
id: string;
type: Proto.GroupContext.Type;
name?: string;
membersE164: ReadonlyArray<string>;
avatar?: ProcessedAttachment;
// Computed fields
derivedGroupV2Id: string;
};
export type ProcessedGroupV2Context = {
masterKey: string;
revision?: number;
@ -208,7 +197,6 @@ export type ProcessedGiftBadge = {
export type ProcessedDataMessage = {
body?: string;
attachments: ReadonlyArray<ProcessedAttachment>;
group?: ProcessedGroupContext;
groupV2?: ProcessedGroupV2Context;
flags: number;
expireTimer: DurationInSeconds;

View file

@ -10,12 +10,10 @@ import { dropNull, shallowDropNull } from '../util/dropNull';
import { SignalService as Proto } from '../protobuf';
import { deriveGroupFields } from '../groups';
import * as Bytes from '../Bytes';
import { deriveMasterKeyFromGroupV1 } from '../Crypto';
import type {
ProcessedAttachment,
ProcessedDataMessage,
ProcessedGroupContext,
ProcessedGroupV2Context,
ProcessedQuote,
ProcessedContact,
@ -25,7 +23,6 @@ import type {
ProcessedDelete,
ProcessedGiftBadge,
} from './Types.d';
import { WarnOnlyError } from './Errors';
import { GiftBadgeStates } from '../components/conversation/Message';
import { APPLICATION_OCTET_STREAM, stringToMIMEType } from '../types/MIME';
import { SECOND, DurationInSeconds } from '../util/durations';
@ -71,39 +68,6 @@ export function processAttachment(
};
}
function processGroupContext(
group?: Proto.IGroupContext | null
): ProcessedGroupContext | undefined {
if (!group) {
return undefined;
}
strictAssert(group.id, 'group context without id');
strictAssert(group.type != null, 'group context without type');
const masterKey = deriveMasterKeyFromGroupV1(group.id);
const data = deriveGroupFields(masterKey);
const derivedGroupV2Id = Bytes.toBase64(data.id);
const result: ProcessedGroupContext = {
id: Bytes.toBinary(group.id),
type: group.type,
name: dropNull(group.name),
membersE164: group.membersE164 ?? [],
avatar: processAttachment(group.avatar),
derivedGroupV2Id,
};
if (result.type === Proto.GroupContext.Type.DELIVER) {
result.name = undefined;
result.membersE164 = [];
result.avatar = undefined;
}
return result;
}
export function processGroupV2Context(
groupV2?: Proto.IGroupContextV2 | null
): ProcessedGroupV2Context | undefined {
@ -331,7 +295,6 @@ export function processDataMessage(
attachments: (message.attachments ?? []).map(
(attachment: Proto.IAttachmentPointer) => processAttachment(attachment)
),
group: processGroupContext(message.group),
groupV2: processGroupV2Context(message.groupV2),
flags: message.flags ?? 0,
expireTimer: DurationInSeconds.fromSeconds(message.expireTimer ?? 0),
@ -375,7 +338,6 @@ export function processDataMessage(
if (isEndSession) {
result.body = undefined;
result.attachments = [];
result.group = undefined;
return result;
}
@ -389,27 +351,6 @@ export function processDataMessage(
throw new Error(`Unknown flags in message: ${result.flags}`);
}
if (result.group) {
switch (result.group.type) {
case Proto.GroupContext.Type.UPDATE:
result.body = undefined;
result.attachments = [];
break;
case Proto.GroupContext.Type.QUIT:
result.body = undefined;
result.attachments = [];
break;
case Proto.GroupContext.Type.DELIVER:
// Cleaned up in `processGroupContext`
break;
default: {
throw new WarnOnlyError(
`Unknown group message type: ${result.group.type}`
);
}
}
}
const attachmentCount = result.attachments.length;
if (attachmentCount > ATTACHMENT_MAX) {
throw new Error(