From a59c298aa10cc8246f9e323e44e56811df059286 Mon Sep 17 00:00:00 2001 From: Scott Nonnenberg Date: Sat, 31 Jan 2026 02:37:46 +1000 Subject: [PATCH] Update to the latest Group and Backups protos --- protos/Groups.proto | 254 ++++----- ts/groups.preload.ts | 641 ++++++++++++----------- ts/groups/joinViaLink.preload.ts | 2 +- ts/textsecure/WebAPI.preload.ts | 8 +- ts/util/groupSendEndorsements.preload.ts | 18 +- 5 files changed, 470 insertions(+), 453 deletions(-) diff --git a/protos/Groups.proto b/protos/Groups.proto index 8d9fa914f7..80aa611607 100644 --- a/protos/Groups.proto +++ b/protos/Groups.proto @@ -9,85 +9,124 @@ option java_package = "org.whispersystems.signalservice.protos.groups"; option java_multiple_files = true; message AvatarUploadAttributes { - string key = 1; + string key = 1; string credential = 2; - string acl = 3; - string algorithm = 4; - string date = 5; - string policy = 6; - string signature = 7; + string acl = 3; + string algorithm = 4; + string date = 5; + string policy = 6; + string signature = 7; } +// Stored data + message Member { enum Role { - UNKNOWN = 0; - DEFAULT = 1; // Normal member - ADMINISTRATOR = 2; // Group admin + UNKNOWN = 0; + DEFAULT = 1; + ADMINISTRATOR = 2; } - bytes userId = 1; // The UuidCiphertext - Role role = 2; - bytes profileKey = 3; // The ProfileKeyCiphertext - bytes presentation = 4; // ProfileKeyCredentialPresentation - uint32 joinedAtVersion = 5; // The Group.version this member joined at + bytes userId = 1; + Role role = 2; + bytes profileKey = 3; + bytes presentation = 4; + uint32 joinedAtVersion = 5; } message MemberPendingProfileKey { - Member member = 1; // The “invited” member - bytes addedByUserId = 2; // The UID who invited this member - uint64 timestamp = 3; // The time the invitation occurred + Member member = 1; + bytes addedByUserId = 2; + uint64 timestamp = 3; // ms since epoch } message MemberPendingAdminApproval { - bytes userId = 1; - bytes profileKey = 2; - bytes presentation = 3; - uint64 timestamp = 4; + bytes userId = 1; + bytes profileKey = 2; + bytes presentation = 3; + uint64 timestamp = 4; // ms since epoch } message MemberBanned { - bytes userId = 1; - uint64 timestamp = 2; // ms since epoch + bytes userId = 1; + uint64 timestamp = 2; // ms since epoch } message AccessControl { enum AccessRequired { - UNKNOWN = 0; - ANY = 1; - MEMBER = 2; // Any group member can make the modification - ADMINISTRATOR = 3; // Only administrators can make the modification + UNKNOWN = 0; + ANY = 1; + MEMBER = 2; + ADMINISTRATOR = 3; UNSATISFIABLE = 4; } - AccessRequired attributes = 1; // Who can modify the group title, avatar, disappearing messages timer - AccessRequired members = 2; // Who can add people to the group + AccessRequired attributes = 1; + AccessRequired members = 2; AccessRequired addFromInviteLink = 3; } message Group { - bytes publicKey = 1; // GroupPublicParams - bytes title = 2; // Encrypted title - string avatar = 3; // Pointer to encrypted avatar (‘key’ from AvatarUploadAttributes) - bytes disappearingMessagesTimer = 4; // Encrypted timer - AccessControl accessControl = 5; - uint32 version = 6; // Current group version number - repeated Member members = 7; - repeated MemberPendingProfileKey membersPendingProfileKey = 8; + bytes publicKey = 1; + bytes title = 2; + bytes description = 11; + // The URL for this group's avatar. The content at this URL can be + // decrypted/deserialized into a `GroupAttributeBlob`. + string avatarUrl = 3; + bytes disappearingMessagesTimer = 4; + AccessControl accessControl = 5; + uint32 version = 6; + repeated Member members = 7; + repeated MemberPendingProfileKey membersPendingProfileKey = 8; repeated MemberPendingAdminApproval membersPendingAdminApproval = 9; - bytes inviteLinkPassword = 10; - bytes descriptionBytes = 11; - bool announcementsOnly = 12; - repeated MemberBanned membersBanned = 13; + bytes inviteLinkPassword = 10; + bool announcements_only = 12; + repeated MemberBanned members_banned = 13; // next: 14 } +message GroupAttributeBlob { + oneof content { + string title = 1; + bytes avatar = 2; + uint32 disappearingMessagesDuration = 3; + string descriptionText = 4; + } +} + +message GroupInviteLink { + message GroupInviteLinkContentsV1 { + bytes groupMasterKey = 1; + bytes inviteLinkPassword = 2; + } + + oneof contents { + GroupInviteLinkContentsV1 contentsV1 = 1; + } +} + +message GroupJoinInfo { + bytes publicKey = 1; + bytes title = 2; + bytes description = 8; + string avatar = 3; + uint32 memberCount = 4; + AccessControl.AccessRequired addFromInviteLink = 5; + uint32 version = 6; + bool pendingAdminApproval = 7; + bool pendingAdminApprovalFull = 9; + // next: 10 +} + +// Deltas + message GroupChange { message Actions { message AddMemberAction { - Member added = 1; - bool joinFromInviteLink = 2; + Member added = 1; + bool joinFromInviteLink = 2; } message DeleteMemberAction { @@ -95,8 +134,8 @@ message GroupChange { } message ModifyMemberRoleAction { - bytes userId = 1; - Member.Role role = 2; + bytes userId = 1; + Member.Role role = 2; } message ModifyMemberProfileKeyAction { @@ -135,8 +174,8 @@ message GroupChange { } message PromoteMemberPendingAdminApprovalAction { - bytes userId = 1; - Member.Role role = 2; + bytes userId = 1; + Member.Role role = 2; } message AddMemberBannedAction { @@ -151,11 +190,15 @@ message GroupChange { bytes title = 1; } + message ModifyDescriptionAction { + bytes description = 1; + } + message ModifyAvatarAction { string avatar = 1; } - message ModifyDisappearingMessagesTimerAction { + message ModifyDisappearingMessageTimerAction { bytes timer = 1; } @@ -163,64 +206,56 @@ message GroupChange { AccessControl.AccessRequired attributesAccess = 1; } - message ModifyAvatarAccessControlAction { - AccessControl.AccessRequired avatarAccess = 1; - } - message ModifyMembersAccessControlAction { AccessControl.AccessRequired membersAccess = 1; } message ModifyAddFromInviteLinkAccessControlAction { - AccessControl.AccessRequired addFromInviteLinkAccess = 1; + AccessControl.AccessRequired addFromInviteLinkAccess = 1; } message ModifyInviteLinkPasswordAction { - bytes inviteLinkPassword = 1; - } - - message ModifyDescriptionAction { - bytes descriptionBytes = 1; + bytes inviteLinkPassword = 1; } message ModifyAnnouncementsOnlyAction { - bool announcementsOnly = 1; + bool announcements_only = 1; } - - bytes sourceUserId = 1; // Who made the change - uint32 version = 2; // The change version number + bytes sourceUserId = 1; // clients should not provide this value; the server will provide it in the response buffer to ensure the signature is binding to a particular group // if clients set it during a request the server will respond with 400. - bytes groupId = 25; - repeated AddMemberAction addMembers = 3; // Members added - repeated DeleteMemberAction deleteMembers = 4; // Members deleted - repeated ModifyMemberRoleAction modifyMemberRoles = 5; // Modified member roles - repeated ModifyMemberProfileKeyAction modifyMemberProfileKeys = 6; // Modified member profile keys - repeated AddMemberPendingProfileKeyAction addPendingMembers = 7; // Pending members added - repeated DeleteMemberPendingProfileKeyAction deletePendingMembers = 8; // Pending members deleted - repeated PromoteMemberPendingProfileKeyAction promotePendingMembers = 9; // Pending invitations accepted - ModifyTitleAction modifyTitle = 10; // Changed title - ModifyAvatarAction modifyAvatar = 11; // Changed avatar - ModifyDisappearingMessagesTimerAction modifyDisappearingMessagesTimer = 12; // Changed timer - ModifyAttributesAccessControlAction modifyAttributesAccess = 13; // Changed attributes access control - ModifyMembersAccessControlAction modifyMemberAccess = 14; // Changed membership access control - ModifyAddFromInviteLinkAccessControlAction modifyAddFromInviteLinkAccess = 15; // change epoch = 1 - repeated AddMemberPendingAdminApprovalAction addMemberPendingAdminApprovals = 16; // change epoch = 1 - repeated DeleteMemberPendingAdminApprovalAction deleteMemberPendingAdminApprovals = 17; // change epoch = 1 - repeated PromoteMemberPendingAdminApprovalAction promoteMemberPendingAdminApprovals = 18; // change epoch = 1 - ModifyInviteLinkPasswordAction modifyInviteLinkPassword = 19; // change epoch = 1 - ModifyDescriptionAction modifyDescription = 20; // change epoch = 2 - ModifyAnnouncementsOnlyAction modifyAnnouncementsOnly = 21; // change epoch = 3 - repeated AddMemberBannedAction addMembersBanned = 22; // change epoch = 4 - repeated DeleteMemberBannedAction deleteMembersBanned = 23; // change epoch = 4 - repeated PromoteMemberPendingPniAciProfileKeyAction promoteMembersPendingPniAciProfileKey = 24; // change epoch = 5 - // next: 26 + bytes group_id = 25; + uint32 version = 2; + + repeated AddMemberAction addMembers = 3; + repeated DeleteMemberAction deleteMembers = 4; + repeated ModifyMemberRoleAction modifyMemberRoles = 5; + repeated ModifyMemberProfileKeyAction modifyMemberProfileKeys = 6; + repeated AddMemberPendingProfileKeyAction addMembersPendingProfileKey = 7; + repeated DeleteMemberPendingProfileKeyAction deleteMembersPendingProfileKey = 8; + repeated PromoteMemberPendingProfileKeyAction promoteMembersPendingProfileKey = 9; + ModifyTitleAction modifyTitle = 10; + ModifyAvatarAction modifyAvatar = 11; + ModifyDisappearingMessageTimerAction modifyDisappearingMessageTimer = 12; + ModifyAttributesAccessControlAction modifyAttributesAccess = 13; + ModifyMembersAccessControlAction modifyMemberAccess = 14; + ModifyAddFromInviteLinkAccessControlAction modifyAddFromInviteLinkAccess = 15; // change epoch = 1 + repeated AddMemberPendingAdminApprovalAction addMembersPendingAdminApproval = 16; // change epoch = 1 + repeated DeleteMemberPendingAdminApprovalAction deleteMembersPendingAdminApproval = 17; // change epoch = 1 + repeated PromoteMemberPendingAdminApprovalAction promoteMembersPendingAdminApproval = 18; // change epoch = 1 + ModifyInviteLinkPasswordAction modifyInviteLinkPassword = 19; // change epoch = 1 + ModifyDescriptionAction modifyDescription = 20; // change epoch = 2 + ModifyAnnouncementsOnlyAction modify_announcements_only = 21; // change epoch = 3 + repeated AddMemberBannedAction add_members_banned = 22; // change epoch = 4 + repeated DeleteMemberBannedAction delete_members_banned = 23; // change epoch = 4 + repeated PromoteMemberPendingPniAciProfileKeyAction promote_members_pending_pni_aci_profile_key = 24; // change epoch = 5 + // next: 27 } - bytes actions = 1; // The serialized actions - bytes serverSignature = 2; // Server’s signature over serialized actions - uint32 changeEpoch = 3; // Allows clients to decide whether their change logic can successfully apply this diff + bytes actions = 1; + bytes serverSignature = 2; + uint32 changeEpoch = 3; } // External credentials @@ -233,51 +268,20 @@ message ExternalGroupCredential { message GroupResponse { Group group = 1; - bytes groupSendEndorsementResponse = 2; + bytes group_send_endorsements_response = 2; } message GroupChanges { message GroupChangeState { GroupChange groupChange = 1; - Group groupState = 2; + Group groupState = 2; } repeated GroupChangeState groupChanges = 1; - bytes groupSendEndorsementResponse = 2; + bytes group_send_endorsements_response = 2; } message GroupChangeResponse { - GroupChange groupChange = 1; - bytes groupSendEndorsementResponse = 2; -} - -message GroupAttributeBlob { - oneof content { - string title = 1; - bytes avatar = 2; - uint32 disappearingMessagesDuration = 3; - string descriptionText = 4; - } -} - -message GroupInviteLink { - message GroupInviteLinkContentsV1 { - bytes groupMasterKey = 1; - bytes inviteLinkPassword = 2; - } - - oneof contents { - GroupInviteLinkContentsV1 v1Contents = 1; - } -} - -message GroupJoinInfo { - bytes publicKey = 1; - bytes title = 2; - string avatar = 3; - uint32 memberCount = 4; - AccessControl.AccessRequired addFromInviteLink = 5; - uint32 version = 6; - bool pendingAdminApproval = 7; - bytes descriptionBytes = 8; + GroupChange group_change = 1; + bytes group_send_endorsements_response = 2; } diff --git a/ts/groups.preload.ts b/ts/groups.preload.ts index f1ea36b080..1da7caf8bd 100644 --- a/ts/groups.preload.ts +++ b/ts/groups.preload.ts @@ -114,7 +114,7 @@ import { incrementMessageCounter } from './util/incrementMessageCounter.preload. import { sleep } from './util/sleep.std.js'; import { groupInvitesRoute } from './util/signalRoutes.std.js'; import { - decodeGroupSendEndorsementResponse, + decodeGroupSendEndorsementsResponse, validateGroupSendEndorsementsExpiration, } from './util/groupSendEndorsements.preload.js'; import { getProfile } from './util/getProfile.preload.js'; @@ -253,7 +253,7 @@ export function buildGroupLink( strictAssert(masterKey, 'buildGroupLink requires the master key!'); const bytes = Proto.GroupInviteLink.encode({ - v1Contents: { + contentsV1: { groupMasterKey: Bytes.fromBase64(masterKey), inviteLinkPassword: Bytes.fromBase64(groupInviteLinkPassword), }, @@ -273,11 +273,11 @@ export function parseGroupLink(value: string): { const inviteLinkProto = Proto.GroupInviteLink.decode(buffer); if ( - inviteLinkProto.contents !== 'v1Contents' || - !inviteLinkProto.v1Contents + inviteLinkProto.contents !== 'contentsV1' || + !inviteLinkProto.contentsV1 ) { const error = new Error( - 'parseGroupLink: Parsed proto is missing v1Contents' + 'parseGroupLink: Parsed proto is missing contentsV1' ); error.name = LINK_VERSION_ERROR; throw error; @@ -286,13 +286,13 @@ export function parseGroupLink(value: string): { const { groupMasterKey: groupMasterKeyRaw, inviteLinkPassword: inviteLinkPasswordRaw, - } = inviteLinkProto.v1Contents; + } = inviteLinkProto.contentsV1; if (!groupMasterKeyRaw || !groupMasterKeyRaw.length) { - throw new Error('v1Contents.groupMasterKey had no data!'); + throw new Error('contentsV1.groupMasterKey had no data!'); } if (!inviteLinkPasswordRaw || !inviteLinkPasswordRaw.length) { - throw new Error('v1Contents.inviteLinkPassword had no data!'); + throw new Error('contentsV1.inviteLinkPassword had no data!'); } const masterKey = Bytes.toBase64(groupMasterKeyRaw); @@ -434,7 +434,7 @@ function buildGroupProto( } if (attributes.avatarUrl) { - proto.avatar = attributes.avatarUrl; + proto.avatarUrl = attributes.avatarUrl; } if (attributes.expireTimer) { @@ -558,7 +558,7 @@ export async function buildAddMembersChange( const now = Date.now(); const addMembers: Array = []; - const addPendingMembers: Array = + const addMembersPendingProfileKey: Array = []; const actions = new Proto.GroupChange.Actions(); @@ -620,7 +620,7 @@ export async function buildAddMembersChange( new Proto.GroupChange.Actions.AddMemberPendingProfileKeyAction(); addPendingMemberAction.added = memberPendingProfileKey; - addPendingMembers.push(addPendingMemberAction); + addMembersPendingProfileKey.push(addPendingMemberAction); } const doesMemberNeedUnban = conversation.bannedMembersV2?.some( @@ -643,7 +643,7 @@ export async function buildAddMembersChange( }) ); - if (!addMembers.length && !addPendingMembers.length) { + if (!addMembers.length && !addMembersPendingProfileKey.length) { // This shouldn't happen. When these actions are passed to `modifyGroupV2`, a warning // will be logged. return undefined; @@ -651,8 +651,8 @@ export async function buildAddMembersChange( if (addMembers.length) { actions.addMembers = addMembers; } - if (addPendingMembers.length) { - actions.addPendingMembers = addPendingMembers; + if (addMembersPendingProfileKey.length) { + actions.addMembersPendingProfileKey = addMembersPendingProfileKey; } actions.version = newGroupVersion; @@ -731,7 +731,7 @@ export async function buildUpdateAttributesChange( actions.modifyDescription = new Proto.GroupChange.Actions.ModifyDescriptionAction(); - actions.modifyDescription.descriptionBytes = buildGroupDescriptionBuffer( + actions.modifyDescription.description = buildGroupDescriptionBuffer( clientZkGroupCipher, description ); @@ -771,11 +771,11 @@ export function buildDisappearingMessagesTimerChange({ const blobCipherText = encryptGroupBlob(clientZkGroupCipher, blobPlaintext); const timerAction = - new Proto.GroupChange.Actions.ModifyDisappearingMessagesTimerAction(); + new Proto.GroupChange.Actions.ModifyDisappearingMessageTimerAction(); timerAction.timer = blobCipherText; actions.version = (group.revision || 0) + 1; - actions.modifyDisappearingMessagesTimer = timerAction; + actions.modifyDisappearingMessageTimer = timerAction; return actions; } @@ -963,7 +963,7 @@ export function buildDeletePendingAdminApprovalMemberChange({ deleteMemberPendingAdminApproval.deletedUserId = uuidCipherTextBuffer; actions.version = (group.revision || 0) + 1; - actions.deleteMemberPendingAdminApprovals = [ + actions.deleteMembersPendingAdminApproval = [ deleteMemberPendingAdminApproval, ]; @@ -1019,7 +1019,7 @@ export function buildAddPendingAdminApprovalMemberChange({ addMemberPendingAdminApproval.added = added; actions.version = (group.revision || 0) + 1; - actions.addMemberPendingAdminApprovals = [addMemberPendingAdminApproval]; + actions.addMembersPendingAdminApproval = [addMemberPendingAdminApproval]; return actions; } @@ -1096,7 +1096,7 @@ export function buildDeletePendingMemberChange({ } const clientZkGroupCipher = getClientZkGroupCipher(group.secretParams); - const deletePendingMembers = serviceIds.map(serviceId => { + const deleteMembersPendingProfileKey = serviceIds.map(serviceId => { const uuidCipherTextBuffer = encryptServiceId( clientZkGroupCipher, serviceId @@ -1108,7 +1108,7 @@ export function buildDeletePendingMemberChange({ }); actions.version = (group.revision || 0) + 1; - actions.deletePendingMembers = deletePendingMembers; + actions.deleteMembersPendingProfileKey = deleteMembersPendingProfileKey; return actions; } @@ -1185,7 +1185,7 @@ export function buildAddBannedMemberChange({ deleteMemberPendingAdminApprovalAction.deletedUserId = userIdCipherText; - actions.deleteMemberPendingAdminApprovals = [ + actions.deleteMembersPendingAdminApproval = [ deleteMemberPendingAdminApprovalAction, ]; } @@ -1248,7 +1248,7 @@ export function buildPromotePendingAdminApprovalMemberChange({ promotePendingMember.role = MEMBER_ROLE_ENUM.DEFAULT; actions.version = (group.revision || 0) + 1; - actions.promoteMemberPendingAdminApprovals = [promotePendingMember]; + actions.promoteMembersPendingAdminApproval = [promotePendingMember]; return actions; } @@ -1293,7 +1293,7 @@ export function buildPromoteMemberChange({ }, ]; } else { - actions.promotePendingMembers = [ + actions.promoteMembersPendingProfileKey = [ { presentation, }, @@ -1425,7 +1425,7 @@ export async function modifyGroupV2({ groupSecretParamsBase64: secretParams, inviteLinkPassword, }); - const { groupChange, groupSendEndorsementResponse } = + const { groupChange, groupSendEndorsementsResponse } = groupChangeResponse; strictAssert(groupChange, 'modifyGroupV2: missing groupChange'); @@ -1463,13 +1463,13 @@ export async function modifyGroupV2({ strictAssert(membersV2, 'modifyGroupV2: missing membersV2'); // If we are no longer a member - endorsement won't be present - if (Bytes.isNotEmpty(groupSendEndorsementResponse)) { + if (Bytes.isNotEmpty(groupSendEndorsementsResponse)) { try { log.info(`modifyGroupV2/${logId}: Saving group endorsements`); - const groupEndorsementData = decodeGroupSendEndorsementResponse({ + const groupEndorsementData = decodeGroupSendEndorsementsResponse({ groupId, - groupSendEndorsementResponse, + groupSendEndorsementsResponse, groupSecretParamsBase64: secretParams, groupMembersV2: membersV2, }); @@ -1777,16 +1777,16 @@ export async function createGroupV2( request: requestOptions => createGroup(groupProto, requestOptions), }); - const { groupSendEndorsementResponse } = groupResponse; + const { groupSendEndorsementsResponse } = groupResponse; strictAssert( - Bytes.isNotEmpty(groupSendEndorsementResponse), - 'missing groupSendEndorsementResponse' + Bytes.isNotEmpty(groupSendEndorsementsResponse), + 'missing groupSendEndorsementsResponse' ); try { - const groupEndorsementData = decodeGroupSendEndorsementResponse({ + const groupEndorsementData = decodeGroupSendEndorsementsResponse({ groupId, - groupSendEndorsementResponse, + groupSendEndorsementsResponse, groupSecretParamsBase64: secretParams, groupMembersV2: membersV2, }); @@ -2294,7 +2294,7 @@ export async function initiateMigrationToGroupV2( avatarUrl: avatarAttribute?.url, }); - let groupSendEndorsementResponse: Uint8Array | null | undefined; + let groupSendEndorsementsResponse: Uint8Array | null | undefined; try { const groupResponse = await makeRequestWithCredentials({ logId: `initiateMigrationToGroupV2/${logId}`, @@ -2303,8 +2303,8 @@ export async function initiateMigrationToGroupV2( request: options => createGroup(groupProto, options), }); - groupSendEndorsementResponse = - groupResponse.groupSendEndorsementResponse; + groupSendEndorsementsResponse = + groupResponse.groupSendEndorsementsResponse; } catch (error) { log.error( `initiateMigrationToGroupV2/${logId}: Error creating group:`, @@ -2347,14 +2347,14 @@ export async function initiateMigrationToGroupV2( await updateConversation(conversation.attributes); strictAssert( - Bytes.isNotEmpty(groupSendEndorsementResponse), - 'missing groupSendEndorsementResponse' + Bytes.isNotEmpty(groupSendEndorsementsResponse), + 'missing groupSendEndorsementsResponse' ); try { - const groupEndorsementData = decodeGroupSendEndorsementResponse({ + const groupEndorsementData = decodeGroupSendEndorsementsResponse({ groupId, - groupSendEndorsementResponse, + groupSendEndorsementsResponse, groupSecretParamsBase64: secretParams, groupMembersV2: membersV2, }); @@ -2634,7 +2634,7 @@ export async function respondToGroupV2Migration({ }; let firstGroupState: Proto.IGroup | null | undefined; - let groupSendEndorsementResponse: Uint8Array | null | undefined; + let groupSendEndorsementsResponse: Uint8Array | null | undefined; try { const fetchedAt = Date.now(); @@ -2658,7 +2658,7 @@ export async function respondToGroupV2Migration({ // Attempt to start with the first group state, only later processing future updates firstGroupState = response?.changes?.groupChanges?.[0]?.groupState; - groupSendEndorsementResponse = response.groupSendEndorsementResponse; + groupSendEndorsementsResponse = response.groupSendEndorsementsResponse; } catch (error) { if (error.code === GROUP_ACCESS_DENIED_CODE) { log.info( @@ -2675,8 +2675,8 @@ export async function respondToGroupV2Migration({ setLastSuccessfulGroupFetch(conversation.id, fetchedAt); firstGroupState = groupResponse.group; - groupSendEndorsementResponse = - groupResponse.groupSendEndorsementResponse; + groupSendEndorsementsResponse = + groupResponse.groupSendEndorsementsResponse; } catch (secondError) { if (secondError.code === GROUP_ACCESS_DENIED_CODE) { log.info( @@ -2839,14 +2839,14 @@ export async function respondToGroupV2Migration({ sentAt, }); - if (Bytes.isNotEmpty(groupSendEndorsementResponse)) { + if (Bytes.isNotEmpty(groupSendEndorsementsResponse)) { try { const { membersV2 } = conversation.attributes; strictAssert(membersV2, 'missing membersV2'); - const groupEndorsementData = decodeGroupSendEndorsementResponse({ + const groupEndorsementData = decodeGroupSendEndorsementsResponse({ groupId, - groupSendEndorsementResponse, + groupSendEndorsementsResponse, groupSecretParamsBase64: secretParams, groupMembersV2: membersV2, }); @@ -3639,7 +3639,7 @@ async function updateGroupViaPreJoinInfo({ let newAttributes: ConversationAttributesType = { ...group, description: decryptGroupDescription( - dropNull(preJoinInfo.descriptionBytes), + dropNull(preJoinInfo.description), secretParams ), name: decryptGroupTitle(dropNull(preJoinInfo.title), secretParams), @@ -3699,7 +3699,7 @@ async function updateGroupViaState({ }); setLastSuccessfulGroupFetch(id, fetchedAt); - const { group: groupState, groupSendEndorsementResponse } = groupResponse; + const { group: groupState, groupSendEndorsementsResponse } = groupResponse; strictAssert(groupState, 'updateGroupViaState: Group state must be present'); const decryptedGroupState = decryptGroupState( @@ -3719,7 +3719,7 @@ async function updateGroupViaState({ }); // If we're not in the group, we won't receive endorsements - if (Bytes.isNotEmpty(groupSendEndorsementResponse)) { + if (Bytes.isNotEmpty(groupSendEndorsementsResponse)) { try { // Use the latest state of the group after applying changes const { groupId, membersV2 } = newAttributes; @@ -3728,9 +3728,9 @@ async function updateGroupViaState({ log.info(`getCurrentGroupState/${logId}: Saving group endorsements`); - const groupEndorsementData = decodeGroupSendEndorsementResponse({ + const groupEndorsementData = decodeGroupSendEndorsementsResponse({ groupId, - groupSendEndorsementResponse, + groupSendEndorsementsResponse, groupSecretParamsBase64: secretParams, groupMembersV2: membersV2, }); @@ -3918,7 +3918,7 @@ async function updateGroupViaLogs({ } let response: GroupLogResponseType; - let groupSendEndorsementResponse: Uint8Array | null = null; + let groupSendEndorsementsResponse: Uint8Array | null = null; const changes: Array = []; do { const fetchedAt = Date.now(); @@ -3957,8 +3957,8 @@ async function updateGroupViaLogs({ } // Note: We should only get this on the final page - if (response.groupSendEndorsementResponse != null) { - groupSendEndorsementResponse = response.groupSendEndorsementResponse; + if (response.groupSendEndorsementsResponse != null) { + groupSendEndorsementsResponse = response.groupSendEndorsementsResponse; } changes.push(response.changes); @@ -3988,7 +3988,7 @@ async function updateGroupViaLogs({ isNumber(currentVersion) && updates.newAttributes.revision === currentVersion; - if (isAtLatestVersion && Bytes.isNotEmpty(groupSendEndorsementResponse)) { + if (isAtLatestVersion && Bytes.isNotEmpty(groupSendEndorsementsResponse)) { try { log.info(`updateGroupViaLogs/${logId}: Saving group endorsements`); // Use the latest state of the group after applying changes @@ -3998,9 +3998,9 @@ async function updateGroupViaLogs({ 'updateGroupViaLogs: Group must have membersV2' ); - const groupEndorsementData = decodeGroupSendEndorsementResponse({ + const groupEndorsementData = decodeGroupSendEndorsementsResponse({ groupId, - groupSendEndorsementResponse, + groupSendEndorsementsResponse, groupMembersV2: membersV2, groupSecretParamsBase64: secretParams, }); @@ -5151,100 +5151,106 @@ async function applyGroupChange({ } }); - // addPendingMembers?: Array< + // addMembersPendingProfileKey?: Array< // GroupChange.Actions.AddMemberPendingProfileKeyAction // >; - (actions.addPendingMembers || []).forEach(addPendingMember => { - const { added } = addPendingMember; - if (!added || !added.member || !added.member.userId) { - throw new Error( - 'applyGroupChange: addPendingMembers had a missing value' - ); + (actions.addMembersPendingProfileKey || []).forEach( + addMemberPendingProfileKey => { + const { added } = addMemberPendingProfileKey; + if (!added || !added.member || !added.member.userId) { + throw new Error( + 'applyGroupChange: addMemberPendingProfileKey had a missing value' + ); + } + + const addedUserId = added.member.userId; + + if (isAciString(addedUserId) && members[addedUserId]) { + log.warn( + `applyGroupChange/${logId}: Attempt to addMemberPendingProfileKey failed; was already in members.` + ); + return; + } + if (pendingMembers[addedUserId]) { + log.warn( + `applyGroupChange/${logId}: Attempt to addMemberPendingProfileKey failed; was already in pendingMembers.` + ); + return; + } + + pendingMembers[addedUserId] = { + serviceId: addedUserId, + addedByUserId: added.addedByUserId, + timestamp: added.timestamp, + role: added.member.role || MEMBER_ROLE_ENUM.DEFAULT, + }; } + ); - const addedUserId = added.member.userId; - - if (isAciString(addedUserId) && members[addedUserId]) { - log.warn( - `applyGroupChange/${logId}: Attempt to add pendingMember failed; was already in members.` - ); - return; - } - if (pendingMembers[addedUserId]) { - log.warn( - `applyGroupChange/${logId}: Attempt to add pendingMember failed; was already in pendingMembers.` - ); - return; - } - - pendingMembers[addedUserId] = { - serviceId: addedUserId, - addedByUserId: added.addedByUserId, - timestamp: added.timestamp, - role: added.member.role || MEMBER_ROLE_ENUM.DEFAULT, - }; - }); - - // deletePendingMembers?: Array< + // deleteMembersPendingProfileKey?: Array< // GroupChange.Actions.DeleteMemberPendingProfileKeyAction // >; - (actions.deletePendingMembers || []).forEach(deletePendingMember => { - const { deletedUserId } = deletePendingMember; - if (!deletedUserId) { - throw new Error( - 'applyGroupChange: deletePendingMember.deletedUserId is null!' - ); - } + (actions.deleteMembersPendingProfileKey || []).forEach( + deleteMemberPendingProfileKey => { + const { deletedUserId } = deleteMemberPendingProfileKey; + if (!deletedUserId) { + throw new Error( + 'applyGroupChange: deleteMemberPendingProfileKey.deletedUserId is null!' + ); + } - if (pendingMembers[deletedUserId]) { - delete pendingMembers[deletedUserId]; - } else { - log.warn( - `applyGroupChange/${logId}: Attempt to remove pendingMember failed; was not in pendingMembers.` - ); + if (pendingMembers[deletedUserId]) { + delete pendingMembers[deletedUserId]; + } else { + log.warn( + `applyGroupChange/${logId}: Attempt to deleteMemberPendingProfileKey failed; was not in pendingMembers.` + ); + } } - }); + ); - // promotePendingMembers?: Array< + // promoteMembersPendingProfileKey?: Array< // GroupChange.Actions.PromoteMemberPendingProfileKeyAction // >; - (actions.promotePendingMembers || []).forEach(promotePendingMember => { - const { profileKey, aci } = promotePendingMember; - if (!profileKey || !aci) { - throw new Error( - 'applyGroupChange: promotePendingMember had a missing value' - ); + (actions.promoteMembersPendingProfileKey || []).forEach( + promoteMemberPendingProfileKey => { + const { profileKey, aci } = promoteMemberPendingProfileKey; + if (!profileKey || !aci) { + throw new Error( + 'applyGroupChange: promoteMemberPendingProfileKey had a missing value' + ); + } + + const previousRecord = pendingMembers[aci]; + + if (pendingMembers[aci]) { + delete pendingMembers[aci]; + } else { + log.warn( + `applyGroupChange/${logId}: Attempt to promoteMemberPendingProfileKey failed; was not in pendingMembers.` + ); + return; + } + + if (members[aci]) { + log.warn( + `applyGroupChange/${logId}: Attempt to promoteMemberPendingProfileKey failed; was already in members.` + ); + return; + } + + members[aci] = { + aci, + joinedAtVersion: version, + role: previousRecord.role || MEMBER_ROLE_ENUM.DEFAULT, + }; + + newProfileKeys.push({ + profileKey, + aci, + }); } - - const previousRecord = pendingMembers[aci]; - - if (pendingMembers[aci]) { - delete pendingMembers[aci]; - } else { - log.warn( - `applyGroupChange/${logId}: Attempt to promote pendingMember failed; was not in pendingMembers.` - ); - return; - } - - if (members[aci]) { - log.warn( - `applyGroupChange/${logId}: Attempt to promote pendingMember failed; was already in members.` - ); - return; - } - - members[aci] = { - aci, - joinedAtVersion: version, - role: previousRecord.role || MEMBER_ROLE_ENUM.DEFAULT, - }; - - newProfileKeys.push({ - profileKey, - aci, - }); - }); + ); // promoteMembersPendingPniAciProfileKey?: Array< // GroupChange.Actions.PromoteMemberPendingPniAciProfileKeyAction @@ -5316,11 +5322,11 @@ async function applyGroupChange({ }; } - // modifyDisappearingMessagesTimer?: - // GroupChange.Actions.ModifyDisappearingMessagesTimerAction; - if (actions.modifyDisappearingMessagesTimer) { + // modifyDisappearingMessageTimer?: + // GroupChange.Actions.ModifyDisappearingMessageTimerAction; + if (actions.modifyDisappearingMessageTimer) { const disappearingMessagesTimer: Proto.GroupAttributeBlob | undefined = - actions.modifyDisappearingMessagesTimer.timer; + actions.modifyDisappearingMessageTimer.timer; if ( disappearingMessagesTimer && disappearingMessagesTimer.content === 'disappearingMessagesDuration' @@ -5371,12 +5377,12 @@ async function applyGroupChange({ }; } - // addMemberPendingAdminApprovals?: Array< + // addMembersPendingAdminApproval?: Array< // GroupChange.Actions.AddMemberPendingAdminApprovalAction // >; - (actions.addMemberPendingAdminApprovals || []).forEach( - pendingAdminApproval => { - const { added } = pendingAdminApproval; + (actions.addMembersPendingAdminApproval || []).forEach( + addMemberPendingAdminApproval => { + const { added } = addMemberPendingAdminApproval; if (!added) { throw new Error( 'applyGroupChange: modifyMemberProfileKey had a missing value' @@ -5385,19 +5391,19 @@ async function applyGroupChange({ if (members[added.userId]) { log.warn( - `applyGroupChange/${logId}: Attempt to add pending admin approval failed; was already in members.` + `applyGroupChange/${logId}: Attempt to addMemberPendingAdminApproval failed; was already in members.` ); return; } if (pendingMembers[added.userId]) { log.warn( - `applyGroupChange/${logId}: Attempt to add pending admin approval failed; was already in pendingMembers.` + `applyGroupChange/${logId}: Attempt to addMemberPendingAdminApprovals failed; was already in pendingMembers.` ); return; } if (pendingAdminApprovalMembers[added.userId]) { log.warn( - `applyGroupChange/${logId}: Attempt to add pending admin approval failed; was already in pendingAdminApprovalMembers.` + `applyGroupChange/${logId}: Attempt to addMemberPendingAdminApprovals failed; was already in pendingAdminApprovalMembers.` ); return; } @@ -5416,15 +5422,15 @@ async function applyGroupChange({ } ); - // deleteMemberPendingAdminApprovals?: Array< + // deleteMembersPendingAdminApproval?: Array< // GroupChange.Actions.DeleteMemberPendingAdminApprovalAction // >; - (actions.deleteMemberPendingAdminApprovals || []).forEach( - deleteAdminApproval => { - const { deletedUserId } = deleteAdminApproval; + (actions.deleteMembersPendingAdminApproval || []).forEach( + deleteMemberPendingAdminApproval => { + const { deletedUserId } = deleteMemberPendingAdminApproval; if (!deletedUserId) { throw new Error( - 'applyGroupChange: deleteAdminApproval.deletedUserId is null!' + 'applyGroupChange: deleteMemberPendingAdminApproval.deletedUserId is null!' ); } @@ -5432,7 +5438,7 @@ async function applyGroupChange({ delete pendingAdminApprovalMembers[deletedUserId]; } else { log.warn( - `applyGroupChange/${logId}: Attempt to remove pendingAdminApproval failed; was not in pendingAdminApprovalMembers.` + `applyGroupChange/${logId}: Attempt to deleteMemberPendingAdminApproval failed; was not in pendingAdminApprovalMembers.` ); } } @@ -5441,12 +5447,12 @@ async function applyGroupChange({ // promoteMemberPendingAdminApprovals?: Array< // GroupChange.Actions.PromoteMemberPendingAdminApprovalAction // >; - (actions.promoteMemberPendingAdminApprovals || []).forEach( - promoteAdminApproval => { - const { userId, role } = promoteAdminApproval; + (actions.promoteMembersPendingAdminApproval || []).forEach( + promoteMemberPendingAdminApproval => { + const { userId, role } = promoteMemberPendingAdminApproval; if (!userId) { throw new Error( - 'applyGroupChange: promoteAdminApproval had a missing value' + 'applyGroupChange: promoteMemberPendingAdminApproval had a missing value' ); } @@ -5454,20 +5460,20 @@ async function applyGroupChange({ delete pendingAdminApprovalMembers[userId]; } else { log.warn( - `applyGroupChange/${logId}: Attempt to promote pendingAdminApproval failed; was not in pendingAdminApprovalMembers.` + `applyGroupChange/${logId}: Attempt to promoteMemberPendingAdminApproval failed; was not in pendingAdminApprovalMembers.` ); return; } if (pendingMembers[userId]) { delete pendingAdminApprovalMembers[userId]; log.warn( - `applyGroupChange/${logId}: Deleted pendingAdminApproval from pendingMembers.` + `applyGroupChange/${logId}: promoteMemberPendingAdminApproval removed from from pendingMembers.` ); } if (members[userId]) { log.warn( - `applyGroupChange/${logId}: Attempt to promote pendingMember failed; was already in members.` + `applyGroupChange/${logId}: Attempt to promoteMemberPendingAdminApproval failed; was already in members.` ); return; } @@ -5498,9 +5504,9 @@ async function applyGroupChange({ // modifyDescription?: GroupChange.Actions.ModifyDescriptionAction; if (actions.modifyDescription) { - const { descriptionBytes } = actions.modifyDescription; - if (descriptionBytes && descriptionBytes.content === 'descriptionText') { - result.description = dropNull(descriptionBytes.descriptionText)?.trim(); + const { description } = actions.modifyDescription; + if (description && description.content === 'descriptionText') { + result.description = dropNull(description.descriptionText)?.trim(); } else { log.warn( `applyGroupChange/${logId}: Clearing group description due to missing data.` @@ -5948,10 +5954,10 @@ async function applyGroupState({ result.groupInviteLinkPassword = undefined; } - // descriptionBytes - const { descriptionBytes } = groupState; - if (descriptionBytes && descriptionBytes.content === 'descriptionText') { - result.description = dropNull(descriptionBytes.descriptionText)?.trim(); + // description + const { description } = groupState; + if (description && description.content === 'descriptionText') { + result.description = dropNull(description.descriptionText)?.trim(); } else { result.description = undefined; } @@ -5979,7 +5985,7 @@ async function applyGroupState({ result = { ...result, ...(await applyNewAvatar({ - newAvatarUrl: dropNull(groupState.avatar), + newAvatarUrl: dropNull(groupState.avatarUrl), attributes: result, logId, })), @@ -6063,14 +6069,14 @@ type DecryptedGroupChangeActions = { profileKey: Uint8Array; aci: AciString; }>; - addPendingMembers?: ReadonlyArray<{ + addMembersPendingProfileKey?: ReadonlyArray<{ added: DecryptedMemberPendingProfileKey; }>; - deletePendingMembers?: ReadonlyArray<{ + deleteMembersPendingProfileKey?: ReadonlyArray<{ // This might be a PNI deletedUserId: ServiceIdString; }>; - promotePendingMembers?: ReadonlyArray<{ + promoteMembersPendingProfileKey?: ReadonlyArray<{ profileKey: Uint8Array; aci: AciString; }>; @@ -6082,16 +6088,16 @@ type DecryptedGroupChangeActions = { modifyTitle?: { title?: Proto.GroupAttributeBlob; }; - modifyDisappearingMessagesTimer?: { + modifyDisappearingMessageTimer?: { timer?: Proto.GroupAttributeBlob; }; - addMemberPendingAdminApprovals?: ReadonlyArray<{ + addMembersPendingAdminApproval?: ReadonlyArray<{ added: DecryptedMemberPendingAdminApproval; }>; - deleteMemberPendingAdminApprovals?: ReadonlyArray<{ + deleteMembersPendingAdminApproval?: ReadonlyArray<{ deletedUserId: AciString; }>; - promoteMemberPendingAdminApprovals?: ReadonlyArray<{ + promoteMembersPendingAdminApproval?: ReadonlyArray<{ userId: AciString; role: Proto.Member.Role; }>; @@ -6099,7 +6105,7 @@ type DecryptedGroupChangeActions = { inviteLinkPassword?: string; }; modifyDescription?: { - descriptionBytes?: Proto.GroupAttributeBlob; + description?: Proto.GroupAttributeBlob; }; modifyAnnouncementsOnly?: { announcementsOnly: boolean; @@ -6293,120 +6299,127 @@ function decryptGroupChange( // addPendingMembers?: Array< // GroupChange.Actions.AddMemberPendingProfileKeyAction // >; - result.addPendingMembers = compact( - (actions.addPendingMembers || []).map(addPendingMember => { - strictAssert( - addPendingMember.added, - 'decryptGroupChange: addPendingMember was missing added field!' - ); - const decrypted = decryptMemberPendingProfileKey( - clientZkGroupCipher, - addPendingMember.added, - logId - ); - if (!decrypted) { - return null; - } + result.addMembersPendingProfileKey = compact( + (actions.addMembersPendingProfileKey || []).map( + addMemberPendingProfileKey => { + strictAssert( + addMemberPendingProfileKey.added, + 'decryptGroupChange: addMemberPendingProfileKey was missing added field!' + ); + const decrypted = decryptMemberPendingProfileKey( + clientZkGroupCipher, + addMemberPendingProfileKey.added, + logId + ); + if (!decrypted) { + return null; + } - return { - added: decrypted, - }; - }) + return { + added: decrypted, + }; + } + ) ); // deletePendingMembers?: Array< // GroupChange.Actions.DeleteMemberPendingProfileKeyAction // >; - result.deletePendingMembers = compact( - (actions.deletePendingMembers || []).map(deletePendingMember => { - const { deletedUserId } = deletePendingMember; - strictAssert( - Bytes.isNotEmpty(deletedUserId), - 'decryptGroupChange: deletePendingMembers.deletedUserId was missing' - ); - let userId: ServiceIdString; - try { - userId = decryptServiceId(clientZkGroupCipher, deletedUserId); - } catch (error) { - log.warn( - `decryptGroupChange/${logId}: Unable to decrypt deletePendingMembers.deletedUserId. Dropping member.`, - Errors.toLogFormat(error) + result.deleteMembersPendingProfileKey = compact( + (actions.deleteMembersPendingProfileKey || []).map( + deleteMemberPendingProfileKey => { + const { deletedUserId } = deleteMemberPendingProfileKey; + strictAssert( + Bytes.isNotEmpty(deletedUserId), + 'decryptGroupChange: deleteMemberPendingProfileKey.deletedUserId was missing' ); - return null; + let userId: ServiceIdString; + try { + userId = decryptServiceId(clientZkGroupCipher, deletedUserId); + } catch (error) { + log.warn( + `decryptGroupChange/${logId}: Unable to decrypt deleteMemberPendingProfileKey.deletedUserId. Dropping member.`, + Errors.toLogFormat(error) + ); + return null; + } + + if (!isServiceIdString(userId)) { + log.warn( + `decryptGroupChange/${logId}: Dropping deleteMemberPendingProfileKey due to invalid deletedUserId` + ); + + return null; + } + + return { + deletedUserId: userId, + }; } - - if (!isServiceIdString(userId)) { - log.warn( - `decryptGroupChange/${logId}: Dropping deletePendingMember due to invalid deletedUserId` - ); - - return null; - } - - return { - deletedUserId: userId, - }; - }) + ) ); // promotePendingMembers?: Array< // GroupChange.Actions.PromoteMemberPendingProfileKeyAction // >; - result.promotePendingMembers = compact( - (actions.promotePendingMembers || []).map(promotePendingMember => { - let { userId, profileKey: encryptedProfileKey } = promotePendingMember; + result.promoteMembersPendingProfileKey = compact( + (actions.promoteMembersPendingProfileKey || []).map( + promoteMemberPendingProfileKey => { + let { userId, profileKey: encryptedProfileKey } = + promoteMemberPendingProfileKey; - // TODO: DESKTOP-3816 - if (Bytes.isEmpty(userId) || Bytes.isEmpty(encryptedProfileKey)) { - const { presentation } = promotePendingMember; + // TODO: DESKTOP-3816 + if (Bytes.isEmpty(userId) || Bytes.isEmpty(encryptedProfileKey)) { + const { presentation } = promoteMemberPendingProfileKey; + + strictAssert( + Bytes.isNotEmpty(presentation), + 'decryptGroupChange: promoteMemberPendingProfileKey.presentation was missing' + ); + + const decodedPresentation = + decodeProfileKeyCredentialPresentation(presentation); + + ({ userId, profileKey: encryptedProfileKey } = decodedPresentation); + } strictAssert( - Bytes.isNotEmpty(presentation), - 'decryptGroupChange: promotePendingMember.presentation was missing' + Bytes.isNotEmpty(userId), + 'decryptGroupChange: promoteMemberPendingProfileKey.userId was missing' + ); + strictAssert( + Bytes.isNotEmpty(encryptedProfileKey), + 'decryptGroupChange: promoteMemberPendingProfileKey.profileKey was missing' ); - const decodedPresentation = - decodeProfileKeyCredentialPresentation(presentation); + let aci: AciString; + let profileKey: Uint8Array; + try { + aci = decryptAci(clientZkGroupCipher, userId); - ({ userId, profileKey: encryptedProfileKey } = decodedPresentation); + profileKey = decryptProfileKey( + clientZkGroupCipher, + encryptedProfileKey, + aci + ); + } catch (error) { + log.warn( + `decryptGroupChange/${logId}: Unable to decrypt ` + + 'promoteMemberPendingProfileKey.userId/profileKey. Dropping member.', + Errors.toLogFormat(error) + ); + return null; + } + + if (!isValidProfileKey(profileKey)) { + throw new Error( + 'decryptGroupChange: promoteMemberPendingProfileKey had invalid profileKey' + ); + } + + return { aci, profileKey }; } - - strictAssert( - Bytes.isNotEmpty(userId), - 'decryptGroupChange: promotePendingMembers.userId was missing' - ); - strictAssert( - Bytes.isNotEmpty(encryptedProfileKey), - 'decryptGroupChange: promotePendingMembers.profileKey was missing' - ); - - let aci: AciString; - let profileKey: Uint8Array; - try { - aci = decryptAci(clientZkGroupCipher, userId); - - profileKey = decryptProfileKey( - clientZkGroupCipher, - encryptedProfileKey, - aci - ); - } catch (error) { - log.warn( - `decryptGroupChange/${logId}: Unable to decrypt ` + - 'promotePendingMembers.userId/profileKey. Dropping member.', - Errors.toLogFormat(error) - ); - return null; - } - - if (!isValidProfileKey(profileKey)) { - throw new Error( - 'decryptGroupChange: promotePendingMembers had invalid profileKey' - ); - } - - return { aci, profileKey }; - }) + ) ); // promoteMembersPendingPniAciProfileKey?: Array< @@ -6493,26 +6506,26 @@ function decryptGroupChange( // Note: decryption happens during application of the change, on download of the avatar result.modifyAvatar = actions.modifyAvatar; - // modifyDisappearingMessagesTimer?: - // GroupChange.Actions.ModifyDisappearingMessagesTimerAction; - if (actions.modifyDisappearingMessagesTimer) { - const { timer } = actions.modifyDisappearingMessagesTimer; + // modifyDisappearingMessageTimer?: + // GroupChange.Actions.ModifyDisappearingMessageTimerAction; + if (actions.modifyDisappearingMessageTimer) { + const { timer } = actions.modifyDisappearingMessageTimer; if (Bytes.isNotEmpty(timer)) { try { - result.modifyDisappearingMessagesTimer = { + result.modifyDisappearingMessageTimer = { timer: Proto.GroupAttributeBlob.decode( decryptGroupBlob(clientZkGroupCipher, timer) ), }; } catch (error) { log.warn( - `decryptGroupChange/${logId}: Unable to decrypt modifyDisappearingMessagesTimer.timer`, + `decryptGroupChange/${logId}: Unable to decrypt modifyDisappearingMessageTimer.timer`, Errors.toLogFormat(error) ); } } else { - result.modifyDisappearingMessagesTimer = {}; + result.modifyDisappearingMessageTimer = {}; } } @@ -6564,13 +6577,13 @@ function decryptGroupChange( // addMemberPendingAdminApprovals?: Array< // GroupChange.Actions.AddMemberPendingAdminApprovalAction // >; - result.addMemberPendingAdminApprovals = compact( - (actions.addMemberPendingAdminApprovals || []).map( - addPendingAdminApproval => { - const { added } = addPendingAdminApproval; + result.addMembersPendingAdminApproval = compact( + (actions.addMembersPendingAdminApproval || []).map( + addMemberPendingAdminApproval => { + const { added } = addMemberPendingAdminApproval; strictAssert( added, - 'decryptGroupChange: addPendingAdminApproval was missing added field!' + 'decryptGroupChange: addMemberPendingAdminApproval was missing added field!' ); const decrypted = decryptMemberPendingAdminApproval( @@ -6580,7 +6593,7 @@ function decryptGroupChange( ); if (!decrypted) { log.warn( - `decryptGroupChange/${logId}: Unable to decrypt addPendingAdminApproval.added. Dropping member.` + `decryptGroupChange/${logId}: Unable to decrypt addMemberPendingAdminApproval.added. Dropping member.` ); return null; } @@ -6593,13 +6606,13 @@ function decryptGroupChange( // deleteMemberPendingAdminApprovals?: Array< // GroupChange.Actions.DeleteMemberPendingAdminApprovalAction // >; - result.deleteMemberPendingAdminApprovals = compact( - (actions.deleteMemberPendingAdminApprovals || []).map( - deletePendingApproval => { - const { deletedUserId } = deletePendingApproval; + result.deleteMembersPendingAdminApproval = compact( + (actions.deleteMembersPendingAdminApproval || []).map( + deleteMemberPendingAdminApproval => { + const { deletedUserId } = deleteMemberPendingAdminApproval; strictAssert( Bytes.isNotEmpty(deletedUserId), - 'decryptGroupChange: deletePendingApproval.deletedUserId was missing' + 'decryptGroupChange: deleteMemberPendingAdminApproval.deletedUserId was missing' ); let aci: AciString; @@ -6607,7 +6620,7 @@ function decryptGroupChange( aci = decryptAci(clientZkGroupCipher, deletedUserId); } catch (error) { log.warn( - `decryptGroupChange/${logId}: Unable to decrypt deletePendingApproval.deletedUserId. Dropping member.`, + `decryptGroupChange/${logId}: Unable to decrypt deleteMemberPendingAdminApproval.deletedUserId. Dropping member.`, Errors.toLogFormat(error) ); return null; @@ -6621,13 +6634,13 @@ function decryptGroupChange( // promoteMemberPendingAdminApprovals?: Array< // GroupChange.Actions.PromoteMemberPendingAdminApprovalAction // >; - result.promoteMemberPendingAdminApprovals = compact( - (actions.promoteMemberPendingAdminApprovals || []).map( - promoteAdminApproval => { - const { userId } = promoteAdminApproval; + result.promoteMembersPendingAdminApproval = compact( + (actions.promoteMembersPendingAdminApproval || []).map( + promoteMemberPendingAdminApproval => { + const { userId } = promoteMemberPendingAdminApproval; strictAssert( Bytes.isNotEmpty(userId), - 'decryptGroupChange: promoteAdminApproval.userId was missing' + 'decryptGroupChange: promoteMemberPendingAdminApproval.userId was missing' ); let decryptedUserId: AciString; @@ -6635,16 +6648,16 @@ function decryptGroupChange( decryptedUserId = decryptAci(clientZkGroupCipher, userId); } catch (error) { log.warn( - `decryptGroupChange/${logId}: Unable to decrypt promoteAdminApproval.userId. Dropping member.`, + `decryptGroupChange/${logId}: Unable to decrypt promoteMemberPendingAdminApproval.userId. Dropping member.`, Errors.toLogFormat(error) ); return null; } - const role = dropNull(promoteAdminApproval.role); + const role = dropNull(promoteMemberPendingAdminApproval.role); if (!isValidRole(role)) { throw new Error( - `decryptGroupChange: promoteAdminApproval had invalid role ${promoteAdminApproval.role}` + `decryptGroupChange: promoteMemberPendingAdminApproval had invalid role ${promoteMemberPendingAdminApproval.role}` ); } @@ -6667,17 +6680,17 @@ function decryptGroupChange( // modifyDescription?: GroupChange.Actions.ModifyDescriptionAction; if (actions.modifyDescription) { - const { descriptionBytes } = actions.modifyDescription; - if (Bytes.isNotEmpty(descriptionBytes)) { + const { description } = actions.modifyDescription; + if (Bytes.isNotEmpty(description)) { try { result.modifyDescription = { - descriptionBytes: Proto.GroupAttributeBlob.decode( - decryptGroupBlob(clientZkGroupCipher, descriptionBytes) + description: Proto.GroupAttributeBlob.decode( + decryptGroupBlob(clientZkGroupCipher, description) ), }; } catch (error) { log.warn( - `decryptGroupChange/${logId}: Unable to decrypt modifyDescription.descriptionBytes`, + `decryptGroupChange/${logId}: Unable to decrypt modifyDescription.description`, Errors.toLogFormat(error) ); } @@ -6785,8 +6798,8 @@ type DecryptedGroupState = { membersPendingProfileKey?: ReadonlyArray; membersPendingAdminApproval?: ReadonlyArray; inviteLinkPassword?: string; - descriptionBytes?: Proto.GroupAttributeBlob; - avatar?: string; + description?: Proto.GroupAttributeBlob; + avatarUrl?: string; announcementsOnly?: boolean; membersBanned?: Array; }; @@ -6911,15 +6924,15 @@ function decryptGroupState( result.inviteLinkPassword = Bytes.toBase64(groupState.inviteLinkPassword); } - // descriptionBytes - if (Bytes.isNotEmpty(groupState.descriptionBytes)) { + // description + if (Bytes.isNotEmpty(groupState.description)) { try { - result.descriptionBytes = Proto.GroupAttributeBlob.decode( - decryptGroupBlob(clientZkGroupCipher, groupState.descriptionBytes) + result.description = Proto.GroupAttributeBlob.decode( + decryptGroupBlob(clientZkGroupCipher, groupState.description) ); } catch (error) { log.warn( - `decryptGroupState/${logId}: Unable to decrypt descriptionBytes. Clearing it.`, + `decryptGroupState/${logId}: Unable to decrypt description. Clearing it.`, Errors.toLogFormat(error) ); } @@ -6950,7 +6963,7 @@ function decryptGroupState( result.membersBanned = []; } - result.avatar = dropNull(groupState.avatar); + result.avatarUrl = dropNull(groupState.avatarUrl); return result; } diff --git a/ts/groups/joinViaLink.preload.ts b/ts/groups/joinViaLink.preload.ts index 870eb6696f..7d8d5a28cf 100644 --- a/ts/groups/joinViaLink.preload.ts +++ b/ts/groups/joinViaLink.preload.ts @@ -150,7 +150,7 @@ export async function joinViaLink(value: string): Promise { decryptGroupTitle(dropNull(result.title), secretParams) || i18n('icu:unknownGroup'); const groupDescription = decryptGroupDescription( - dropNull(result.descriptionBytes), + dropNull(result.description), secretParams ); diff --git a/ts/textsecure/WebAPI.preload.ts b/ts/textsecure/WebAPI.preload.ts index 6a5b5ac0be..b2857f4fe0 100644 --- a/ts/textsecure/WebAPI.preload.ts +++ b/ts/textsecure/WebAPI.preload.ts @@ -890,7 +890,7 @@ export type GetGroupLogOptionsType = Readonly<{ }>; export type GroupLogResponseType = { changes: Proto.GroupChanges; - groupSendEndorsementResponse: Uint8Array | null; + groupSendEndorsementsResponse: Uint8Array | null; } & ( | { paginated: false; @@ -4757,7 +4757,7 @@ export async function getGroupLog( }); const { data, response } = withDetails; const changes = Proto.GroupChanges.decode(data); - const { groupSendEndorsementResponse } = changes; + const { groupSendEndorsementsResponse } = changes; if (response && response.status === 206) { const range = response.headers.get('Content-Range'); @@ -4779,7 +4779,7 @@ export async function getGroupLog( start, end, currentRevision, - groupSendEndorsementResponse, + groupSendEndorsementsResponse, }; } } @@ -4787,7 +4787,7 @@ export async function getGroupLog( return { paginated: false, changes, - groupSendEndorsementResponse, + groupSendEndorsementsResponse, }; } diff --git a/ts/util/groupSendEndorsements.preload.ts b/ts/util/groupSendEndorsements.preload.ts index 13b2d5719c..60a75d37ec 100644 --- a/ts/util/groupSendEndorsements.preload.ts +++ b/ts/util/groupSendEndorsements.preload.ts @@ -39,31 +39,31 @@ const { throttle } = lodash; const log = createLogger('groupSendEndorsements'); -export function decodeGroupSendEndorsementResponse({ +export function decodeGroupSendEndorsementsResponse({ groupId, - groupSendEndorsementResponse, + groupSendEndorsementsResponse, groupSecretParamsBase64, groupMembersV2, }: { groupId: string; - groupSendEndorsementResponse: Uint8Array; + groupSendEndorsementsResponse: Uint8Array; groupSecretParamsBase64: string; groupMembersV2: ReadonlyArray; }): GroupSendEndorsementsData { const idForLogging = `groupv2(${groupId})`; strictAssert( - groupSendEndorsementResponse != null, - 'Missing groupSendEndorsementResponse' + groupSendEndorsementsResponse != null, + 'Missing groupSendEndorsementsResponse' ); strictAssert( - groupSendEndorsementResponse.byteLength > 0, - 'Received empty groupSendEndorsementResponse' + groupSendEndorsementsResponse.byteLength > 0, + 'Received empty groupSendEndorsementsResponse' ); const response = new GroupSendEndorsementsResponse( - groupSendEndorsementResponse + groupSendEndorsementsResponse ); const expiration = response.getExpiration().getTime() / 1000; @@ -99,7 +99,7 @@ export function decodeGroupSendEndorsementResponse({ ); log.info( - `decodeGroupSendEndorsementResponse: Received endorsements (group: ${idForLogging}, expiration: ${expiration}, members: ${groupMembers.length})` + `decodeGroupSendEndorsementsResponse: Received endorsements (group: ${idForLogging}, expiration: ${expiration}, members: ${groupMembers.length})` ); return parseStrict(groupSendEndorsementsDataSchema, {