Update to the latest Group and Backups protos

This commit is contained in:
Scott Nonnenberg
2026-01-31 02:37:46 +10:00
committed by GitHub
parent f5efbdc25f
commit a59c298aa1
5 changed files with 470 additions and 453 deletions

View File

@@ -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; // Servers 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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -150,7 +150,7 @@ export async function joinViaLink(value: string): Promise<void> {
decryptGroupTitle(dropNull(result.title), secretParams) ||
i18n('icu:unknownGroup');
const groupDescription = decryptGroupDescription(
dropNull(result.descriptionBytes),
dropNull(result.description),
secretParams
);

View File

@@ -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,
};
}

View File

@@ -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<GroupV2MemberType>;
}): 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, {