Sender Key: Track registrationIds in senderKeyState

This commit is contained in:
Scott Nonnenberg 2021-07-30 11:35:25 -07:00 committed by GitHub
parent 689542a9b4
commit 9fb8114691
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 57 additions and 15 deletions

View File

@ -967,13 +967,17 @@ export class SignalProtocolStore extends EventsMixin {
conversationIds.has(session.fromDB.conversationId)
);
const openEntries: Array<
SessionCacheEntry | undefined
| undefined
| {
entry: SessionCacheEntry;
record: SessionRecord;
}
> = await Promise.all(
entries.map(async entry => {
if (entry.hydrated) {
const record = entry.item;
if (record.hasCurrentState()) {
return entry;
return { record, entry };
}
return undefined;
@ -981,7 +985,7 @@ export class SignalProtocolStore extends EventsMixin {
const record = await this._maybeMigrateSession(entry.fromDB);
if (record.hasCurrentState()) {
return entry;
return { record, entry };
}
return undefined;
@ -989,10 +993,11 @@ export class SignalProtocolStore extends EventsMixin {
);
const devices = openEntries
.map(entry => {
if (!entry) {
.map(item => {
if (!item) {
return undefined;
}
const { entry, record } = item;
const { conversationId } = entry.fromDB;
conversationIds.delete(conversationId);
@ -1015,9 +1020,12 @@ export class SignalProtocolStore extends EventsMixin {
);
}
const registrationId = record.remoteRegistrationId();
return {
identifier,
id,
registrationId,
};
})
.filter(isNotNil);

View File

@ -1330,18 +1330,22 @@ describe('SignalProtocolStore', () => {
{
id: 1,
identifier: number,
registrationId: 243,
},
{
id: 2,
identifier: number,
registrationId: 243,
},
{
id: 3,
identifier: number,
registrationId: 243,
},
{
id: 10,
identifier: number,
registrationId: 243,
},
],
emptyIdentifiers: ['blah', 'blah2'],

View File

@ -14,14 +14,17 @@ describe('sendToGroup', () => {
{
identifier: 'ident-guid-one',
id: 1,
registrationId: 11,
},
{
identifier: 'ident-guid-one',
id: 2,
registrationId: 22,
},
{
identifier: 'ident-guid-two',
id: 2,
registrationId: 33,
},
];
}
@ -60,10 +63,12 @@ describe('sendToGroup', () => {
{
identifier: 'ident-guid-one',
id: 2,
registrationId: 22,
},
{
identifier: 'ident-guid-two',
id: 2,
registrationId: 33,
},
]);
assert.deepEqual(newToMemberUuids, ['ident-guid-one', 'ident-guid-two']);
@ -90,10 +95,12 @@ describe('sendToGroup', () => {
{
identifier: 'ident-guid-one',
id: 2,
registrationId: 22,
},
{
identifier: 'ident-guid-two',
id: 2,
registrationId: 33,
},
]);
assert.deepEqual(removedFromMemberUuids, [

View File

@ -32,6 +32,7 @@ export type WebAPICredentials = {
export type DeviceType = {
id: number;
identifier: string;
registrationId: number;
};
// How the legacy APIs generate these types

View File

@ -44,7 +44,7 @@ import {
} from '../textsecure/WebAPI';
import { SignalService as Proto } from '../protobuf';
import { assert } from './assert';
import { strictAssert } from './assert';
import { isGroupV2 } from './whatTypeOfConversation';
const ERROR_EXPIRED_OR_MISSING_DEVICES = 409;
@ -56,7 +56,7 @@ const DAY = 24 * HOUR;
const MAX_CONCURRENCY = 5;
// sendWithSenderKey is recursive, but we don't want to loop back too many times.
const MAX_RECURSION = 5;
const MAX_RECURSION = 10;
// TODO: remove once we move away from ArrayBuffers
const FIXMEU8 = Uint8Array;
@ -80,7 +80,7 @@ export async function sendToGroup({
sendOptions?: SendOptionsType;
sendType: SendTypesType;
}): Promise<CallbackResultType> {
assert(
strictAssert(
window.textsecure.messaging,
'sendToGroup: textsecure.messaging not available!'
);
@ -133,7 +133,7 @@ export async function sendContentMessageToGroup({
timestamp: number;
}): Promise<CallbackResultType> {
const logId = conversation.idForLogging();
assert(
strictAssert(
window.textsecure.messaging,
'sendContentMessageToGroup: textsecure.messaging not available!'
);
@ -247,7 +247,7 @@ export async function sendToGroupViaSenderKey(options: {
);
}
assert(
strictAssert(
window.textsecure.messaging,
'sendToGroupViaSenderKey: textsecure.messaging not available!'
);
@ -293,14 +293,14 @@ export async function sendToGroupViaSenderKey(options: {
) {
await fetchKeysForIdentifiers(emptyIdentifiers);
// Restart here to capture devices for accounts we just started sesions with
// Restart here to capture devices for accounts we just started sessions with
return sendToGroupViaSenderKey({
...options,
recursionCount: recursionCount + 1,
});
}
assert(
strictAssert(
attributes.senderKeyInfo,
`sendToGroupViaSenderKey/${logId}: expect senderKeyInfo`
);
@ -380,6 +380,13 @@ export async function sendToGroupViaSenderKey(options: {
),
{ messageIds: [], sendType: 'senderKeyDistributionMessage' }
);
// Restart here because we might have discovered new or dropped devices as part of
// distributing our sender key.
return sendToGroupViaSenderKey({
...options,
recursionCount: recursionCount + 1,
});
}
// 9. Update memberDevices with both adds and the removals which didn't require a reset.
@ -696,7 +703,7 @@ async function handle410Response(
// been re-registered or re-linked.
const senderKeyInfo = conversation.get('senderKeyInfo');
if (senderKeyInfo) {
const devicesToRemove: Array<DeviceType> = devices.staleDevices.map(
const devicesToRemove: Array<PartialDeviceType> = devices.staleDevices.map(
id => ({ id, identifier: uuid })
);
conversation.set({
@ -705,7 +712,7 @@ async function handle410Response(
memberDevices: differenceWith(
senderKeyInfo.memberDevices,
devicesToRemove,
deviceComparator
partialDeviceComparator
),
},
});
@ -732,7 +739,7 @@ function getXorOfAccessKeys(devices: Array<DeviceType>): Buffer {
const uuids = getUuidsFromDevices(devices);
const result = Buffer.alloc(ACCESS_KEY_LENGTH);
assert(
strictAssert(
result.length === ACCESS_KEY_LENGTH,
'getXorOfAccessKeys starting value'
);
@ -876,6 +883,21 @@ function isValidSenderKeyRecipient(
}
function deviceComparator(left?: DeviceType, right?: DeviceType): boolean {
return Boolean(
left &&
right &&
left.id === right.id &&
left.identifier === right.identifier &&
left.registrationId === right.registrationId
);
}
type PartialDeviceType = Omit<DeviceType, 'registrationId'>;
function partialDeviceComparator(
left?: PartialDeviceType,
right?: PartialDeviceType
): boolean {
return Boolean(
left &&
right &&