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)
|
conversationIds.has(session.fromDB.conversationId)
|
||||||
);
|
);
|
||||||
const openEntries: Array<
|
const openEntries: Array<
|
||||||
SessionCacheEntry | undefined
|
| undefined
|
||||||
|
| {
|
||||||
|
entry: SessionCacheEntry;
|
||||||
|
record: SessionRecord;
|
||||||
|
}
|
||||||
> = await Promise.all(
|
> = await Promise.all(
|
||||||
entries.map(async entry => {
|
entries.map(async entry => {
|
||||||
if (entry.hydrated) {
|
if (entry.hydrated) {
|
||||||
const record = entry.item;
|
const record = entry.item;
|
||||||
if (record.hasCurrentState()) {
|
if (record.hasCurrentState()) {
|
||||||
return entry;
|
return { record, entry };
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -981,7 +985,7 @@ export class SignalProtocolStore extends EventsMixin {
|
||||||
|
|
||||||
const record = await this._maybeMigrateSession(entry.fromDB);
|
const record = await this._maybeMigrateSession(entry.fromDB);
|
||||||
if (record.hasCurrentState()) {
|
if (record.hasCurrentState()) {
|
||||||
return entry;
|
return { record, entry };
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -989,10 +993,11 @@ export class SignalProtocolStore extends EventsMixin {
|
||||||
);
|
);
|
||||||
|
|
||||||
const devices = openEntries
|
const devices = openEntries
|
||||||
.map(entry => {
|
.map(item => {
|
||||||
if (!entry) {
|
if (!item) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
const { entry, record } = item;
|
||||||
|
|
||||||
const { conversationId } = entry.fromDB;
|
const { conversationId } = entry.fromDB;
|
||||||
conversationIds.delete(conversationId);
|
conversationIds.delete(conversationId);
|
||||||
|
@ -1015,9 +1020,12 @@ export class SignalProtocolStore extends EventsMixin {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const registrationId = record.remoteRegistrationId();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
identifier,
|
identifier,
|
||||||
id,
|
id,
|
||||||
|
registrationId,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter(isNotNil);
|
.filter(isNotNil);
|
||||||
|
|
|
@ -1330,18 +1330,22 @@ describe('SignalProtocolStore', () => {
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
identifier: number,
|
identifier: number,
|
||||||
|
registrationId: 243,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
identifier: number,
|
identifier: number,
|
||||||
|
registrationId: 243,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
identifier: number,
|
identifier: number,
|
||||||
|
registrationId: 243,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 10,
|
id: 10,
|
||||||
identifier: number,
|
identifier: number,
|
||||||
|
registrationId: 243,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
emptyIdentifiers: ['blah', 'blah2'],
|
emptyIdentifiers: ['blah', 'blah2'],
|
||||||
|
|
|
@ -14,14 +14,17 @@ describe('sendToGroup', () => {
|
||||||
{
|
{
|
||||||
identifier: 'ident-guid-one',
|
identifier: 'ident-guid-one',
|
||||||
id: 1,
|
id: 1,
|
||||||
|
registrationId: 11,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
identifier: 'ident-guid-one',
|
identifier: 'ident-guid-one',
|
||||||
id: 2,
|
id: 2,
|
||||||
|
registrationId: 22,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
identifier: 'ident-guid-two',
|
identifier: 'ident-guid-two',
|
||||||
id: 2,
|
id: 2,
|
||||||
|
registrationId: 33,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -60,10 +63,12 @@ describe('sendToGroup', () => {
|
||||||
{
|
{
|
||||||
identifier: 'ident-guid-one',
|
identifier: 'ident-guid-one',
|
||||||
id: 2,
|
id: 2,
|
||||||
|
registrationId: 22,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
identifier: 'ident-guid-two',
|
identifier: 'ident-guid-two',
|
||||||
id: 2,
|
id: 2,
|
||||||
|
registrationId: 33,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
assert.deepEqual(newToMemberUuids, ['ident-guid-one', 'ident-guid-two']);
|
assert.deepEqual(newToMemberUuids, ['ident-guid-one', 'ident-guid-two']);
|
||||||
|
@ -90,10 +95,12 @@ describe('sendToGroup', () => {
|
||||||
{
|
{
|
||||||
identifier: 'ident-guid-one',
|
identifier: 'ident-guid-one',
|
||||||
id: 2,
|
id: 2,
|
||||||
|
registrationId: 22,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
identifier: 'ident-guid-two',
|
identifier: 'ident-guid-two',
|
||||||
id: 2,
|
id: 2,
|
||||||
|
registrationId: 33,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
assert.deepEqual(removedFromMemberUuids, [
|
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 = {
|
export type DeviceType = {
|
||||||
id: number;
|
id: number;
|
||||||
identifier: string;
|
identifier: string;
|
||||||
|
registrationId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// How the legacy APIs generate these types
|
// How the legacy APIs generate these types
|
||||||
|
|
|
@ -44,7 +44,7 @@ import {
|
||||||
} from '../textsecure/WebAPI';
|
} from '../textsecure/WebAPI';
|
||||||
import { SignalService as Proto } from '../protobuf';
|
import { SignalService as Proto } from '../protobuf';
|
||||||
|
|
||||||
import { assert } from './assert';
|
import { strictAssert } from './assert';
|
||||||
import { isGroupV2 } from './whatTypeOfConversation';
|
import { isGroupV2 } from './whatTypeOfConversation';
|
||||||
|
|
||||||
const ERROR_EXPIRED_OR_MISSING_DEVICES = 409;
|
const ERROR_EXPIRED_OR_MISSING_DEVICES = 409;
|
||||||
|
@ -56,7 +56,7 @@ const DAY = 24 * HOUR;
|
||||||
const MAX_CONCURRENCY = 5;
|
const MAX_CONCURRENCY = 5;
|
||||||
|
|
||||||
// sendWithSenderKey is recursive, but we don't want to loop back too many times.
|
// 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
|
// TODO: remove once we move away from ArrayBuffers
|
||||||
const FIXMEU8 = Uint8Array;
|
const FIXMEU8 = Uint8Array;
|
||||||
|
@ -80,7 +80,7 @@ export async function sendToGroup({
|
||||||
sendOptions?: SendOptionsType;
|
sendOptions?: SendOptionsType;
|
||||||
sendType: SendTypesType;
|
sendType: SendTypesType;
|
||||||
}): Promise<CallbackResultType> {
|
}): Promise<CallbackResultType> {
|
||||||
assert(
|
strictAssert(
|
||||||
window.textsecure.messaging,
|
window.textsecure.messaging,
|
||||||
'sendToGroup: textsecure.messaging not available!'
|
'sendToGroup: textsecure.messaging not available!'
|
||||||
);
|
);
|
||||||
|
@ -133,7 +133,7 @@ export async function sendContentMessageToGroup({
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
}): Promise<CallbackResultType> {
|
}): Promise<CallbackResultType> {
|
||||||
const logId = conversation.idForLogging();
|
const logId = conversation.idForLogging();
|
||||||
assert(
|
strictAssert(
|
||||||
window.textsecure.messaging,
|
window.textsecure.messaging,
|
||||||
'sendContentMessageToGroup: textsecure.messaging not available!'
|
'sendContentMessageToGroup: textsecure.messaging not available!'
|
||||||
);
|
);
|
||||||
|
@ -247,7 +247,7 @@ export async function sendToGroupViaSenderKey(options: {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(
|
strictAssert(
|
||||||
window.textsecure.messaging,
|
window.textsecure.messaging,
|
||||||
'sendToGroupViaSenderKey: textsecure.messaging not available!'
|
'sendToGroupViaSenderKey: textsecure.messaging not available!'
|
||||||
);
|
);
|
||||||
|
@ -293,14 +293,14 @@ export async function sendToGroupViaSenderKey(options: {
|
||||||
) {
|
) {
|
||||||
await fetchKeysForIdentifiers(emptyIdentifiers);
|
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({
|
return sendToGroupViaSenderKey({
|
||||||
...options,
|
...options,
|
||||||
recursionCount: recursionCount + 1,
|
recursionCount: recursionCount + 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(
|
strictAssert(
|
||||||
attributes.senderKeyInfo,
|
attributes.senderKeyInfo,
|
||||||
`sendToGroupViaSenderKey/${logId}: expect senderKeyInfo`
|
`sendToGroupViaSenderKey/${logId}: expect senderKeyInfo`
|
||||||
);
|
);
|
||||||
|
@ -380,6 +380,13 @@ export async function sendToGroupViaSenderKey(options: {
|
||||||
),
|
),
|
||||||
{ messageIds: [], sendType: 'senderKeyDistributionMessage' }
|
{ 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.
|
// 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.
|
// been re-registered or re-linked.
|
||||||
const senderKeyInfo = conversation.get('senderKeyInfo');
|
const senderKeyInfo = conversation.get('senderKeyInfo');
|
||||||
if (senderKeyInfo) {
|
if (senderKeyInfo) {
|
||||||
const devicesToRemove: Array<DeviceType> = devices.staleDevices.map(
|
const devicesToRemove: Array<PartialDeviceType> = devices.staleDevices.map(
|
||||||
id => ({ id, identifier: uuid })
|
id => ({ id, identifier: uuid })
|
||||||
);
|
);
|
||||||
conversation.set({
|
conversation.set({
|
||||||
|
@ -705,7 +712,7 @@ async function handle410Response(
|
||||||
memberDevices: differenceWith(
|
memberDevices: differenceWith(
|
||||||
senderKeyInfo.memberDevices,
|
senderKeyInfo.memberDevices,
|
||||||
devicesToRemove,
|
devicesToRemove,
|
||||||
deviceComparator
|
partialDeviceComparator
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -732,7 +739,7 @@ function getXorOfAccessKeys(devices: Array<DeviceType>): Buffer {
|
||||||
const uuids = getUuidsFromDevices(devices);
|
const uuids = getUuidsFromDevices(devices);
|
||||||
|
|
||||||
const result = Buffer.alloc(ACCESS_KEY_LENGTH);
|
const result = Buffer.alloc(ACCESS_KEY_LENGTH);
|
||||||
assert(
|
strictAssert(
|
||||||
result.length === ACCESS_KEY_LENGTH,
|
result.length === ACCESS_KEY_LENGTH,
|
||||||
'getXorOfAccessKeys starting value'
|
'getXorOfAccessKeys starting value'
|
||||||
);
|
);
|
||||||
|
@ -876,6 +883,21 @@ function isValidSenderKeyRecipient(
|
||||||
}
|
}
|
||||||
|
|
||||||
function deviceComparator(left?: DeviceType, right?: DeviceType): boolean {
|
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(
|
return Boolean(
|
||||||
left &&
|
left &&
|
||||||
right &&
|
right &&
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue