Sender Key: Track registrationIds in senderKeyState
This commit is contained in:
parent
689542a9b4
commit
9fb8114691
5 changed files with 57 additions and 15 deletions
|
@ -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);
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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, [
|
||||
|
|
1
ts/textsecure/Types.d.ts
vendored
1
ts/textsecure/Types.d.ts
vendored
|
@ -32,6 +32,7 @@ export type WebAPICredentials = {
|
|||
export type DeviceType = {
|
||||
id: number;
|
||||
identifier: string;
|
||||
registrationId: number;
|
||||
};
|
||||
|
||||
// How the legacy APIs generate these types
|
||||
|
|
|
@ -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 &&
|
||||
|
|
Loading…
Reference in a new issue