Introduce Service Id Types

Co-authored-by: Scott Nonnenberg <scott@signal.org>
This commit is contained in:
Fedor Indutny 2023-08-10 18:43:33 +02:00 committed by Jamie Kyle
parent 414c0a58d3
commit 366b875fd2
269 changed files with 5832 additions and 5550 deletions

View file

@ -3,6 +3,7 @@
import { assert } from 'chai';
import { range } from 'lodash';
import * as sinon from 'sinon';
import { getRandomBytes } from '../../Crypto';
import AccountManager from '../../textsecure/AccountManager';
@ -11,42 +12,40 @@ import type {
OuterSignedPrekeyType,
PreKeyType,
} from '../../textsecure/Types.d';
import { UUID, UUIDKind } from '../../types/UUID';
import { ServiceIdKind, generateAci, generatePni } from '../../types/ServiceId';
import { DAY } from '../../util/durations';
/* eslint-disable @typescript-eslint/no-explicit-any */
describe('AccountManager', () => {
let sandbox: sinon.SinonSandbox;
let accountManager: AccountManager;
const ourUuid = UUID.generate();
const ourAci = generateAci();
const ourPni = generatePni();
const identityKey = window.Signal.Curve.generateKeyPair();
const pubKey = getRandomBytes(33);
const privKey = getRandomBytes(32);
let originalGetIdentityKeyPair: any;
let originalGetUuid: any;
let originalGetCheckedUuid: any;
beforeEach(() => {
sandbox = sinon.createSandbox();
const server: any = {};
accountManager = new AccountManager(server);
originalGetIdentityKeyPair =
window.textsecure.storage.protocol.getIdentityKeyPair;
originalGetUuid = window.textsecure.storage.user.getUuid;
originalGetCheckedUuid = window.textsecure.storage.user.getCheckedUuid;
window.textsecure.storage.protocol.getIdentityKeyPair = () => identityKey;
window.textsecure.storage.user.getUuid = () => ourUuid;
window.textsecure.storage.user.getCheckedUuid = () => ourUuid;
const { storage } = window.textsecure;
sandbox.stub(storage.protocol, 'getIdentityKeyPair').returns(identityKey);
const { user } = storage;
sandbox.stub(user, 'getAci').returns(ourAci);
sandbox.stub(user, 'getPni').returns(ourPni);
sandbox.stub(user, 'getServiceId').returns(ourAci);
sandbox.stub(user, 'getCheckedAci').returns(ourAci);
sandbox.stub(user, 'getCheckedPni').returns(ourPni);
sandbox.stub(user, 'getCheckedServiceId').returns(ourAci);
});
afterEach(() => {
window.textsecure.storage.protocol.getIdentityKeyPair =
originalGetIdentityKeyPair;
window.textsecure.storage.user.getUuid = originalGetUuid;
window.textsecure.storage.user.getCheckedUuid = originalGetCheckedUuid;
sandbox.restore();
});
describe('encrypted device name', () => {
@ -134,7 +133,7 @@ describe('AccountManager', () => {
];
// should be no calls to store.removeSignedPreKey, would cause crash
return accountManager._cleanSignedPreKeys(UUIDKind.ACI);
return accountManager._cleanSignedPreKeys(ServiceIdKind.ACI);
});
it('eliminates oldest keys, even if recent key is unconfirmed', async () => {
@ -193,7 +192,7 @@ describe('AccountManager', () => {
removedKeys = removedKeys.concat(keyIds);
};
await accountManager._cleanSignedPreKeys(UUIDKind.ACI);
await accountManager._cleanSignedPreKeys(ServiceIdKind.ACI);
assert.deepEqual(removedKeys, [4]);
});
});
@ -223,124 +222,124 @@ describe('AccountManager', () => {
const now = Date.now();
kyberPreKeys = [
{
id: `${ourUuid.toString()}:1`,
id: `${ourAci}:1`,
createdAt: now - DAY * 32,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: true,
keyId: 1,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
},
{
id: `${ourUuid.toString()}:2`,
id: `${ourAci}:2`,
createdAt: now - DAY * 34,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: true,
keyId: 2,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
},
{
id: `${ourUuid.toString()}:3`,
id: `${ourAci}:3`,
createdAt: now - DAY * 38,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: true,
keyId: 3,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
},
{
id: `${ourUuid.toString()}:4`,
id: `${ourAci}:4`,
createdAt: now - DAY * 39,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: false,
keyId: 4,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
},
{
id: `${ourUuid.toString()}:5`,
id: `${ourAci}:5`,
createdAt: now - DAY * 40,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: false,
keyId: 5,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
},
];
// should be no calls to store.removeKyberPreKey, would cause crash
return accountManager._cleanLastResortKeys(UUIDKind.ACI);
return accountManager._cleanLastResortKeys(ServiceIdKind.ACI);
});
it('eliminates oldest keys, even if recent key is unconfirmed', async () => {
const now = Date.now();
kyberPreKeys = [
{
id: `${ourUuid.toString()}:1`,
id: `${ourAci}:1`,
createdAt: now - DAY * 32,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: true,
keyId: 1,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
},
{
id: `${ourUuid.toString()}:2`,
id: `${ourAci}:2`,
createdAt: now - DAY * 31,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: false,
keyId: 2,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
},
{
id: `${ourUuid.toString()}:3`,
id: `${ourAci}:3`,
createdAt: now - DAY * 24,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: true,
keyId: 3,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
},
{
// Oldest, should be dropped
id: `${ourUuid.toString()}:4`,
id: `${ourAci}:4`,
createdAt: now - DAY * 38,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: true,
keyId: 4,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
},
{
id: `${ourUuid.toString()}:5`,
id: `${ourAci}:5`,
createdAt: now - DAY * 5,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: true,
keyId: 5,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
},
{
id: `${ourUuid.toString()}:6`,
id: `${ourAci}:6`,
createdAt: now - DAY * 5,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: true,
keyId: 6,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
},
];
@ -352,7 +351,7 @@ describe('AccountManager', () => {
removedKeys = removedKeys.concat(keyIds);
};
await accountManager._cleanLastResortKeys(UUIDKind.ACI);
await accountManager._cleanLastResortKeys(ServiceIdKind.ACI);
assert.deepEqual(removedKeys, [4]);
});
});
@ -378,54 +377,54 @@ describe('AccountManager', () => {
const now = Date.now();
preKeys = [
{
id: `${ourUuid.toString()}:1`,
id: `${ourAci}:1`,
createdAt: now - DAY * 92,
keyId: 1,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
privateKey: privKey,
publicKey: pubKey,
},
{
id: `${ourUuid.toString()}:2`,
id: `${ourAci}:2`,
createdAt: now - DAY * 93,
keyId: 2,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
privateKey: privKey,
publicKey: pubKey,
},
{
id: `${ourUuid.toString()}:3`,
id: `${ourAci}:3`,
createdAt: now - DAY * 93,
keyId: 3,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
privateKey: privKey,
publicKey: pubKey,
},
{
id: `${ourUuid.toString()}:4`,
id: `${ourAci}:4`,
createdAt: now - DAY * 93,
keyId: 4,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
privateKey: privKey,
publicKey: pubKey,
},
{
id: `${ourUuid.toString()}:5`,
id: `${ourAci}:5`,
createdAt: now - DAY * 94,
keyId: 5,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
privateKey: privKey,
publicKey: pubKey,
},
];
// should be no calls to store.removeKyberPreKey, would cause crash
return accountManager._cleanPreKeys(UUIDKind.ACI);
return accountManager._cleanPreKeys(ServiceIdKind.ACI);
});
it('eliminates keys not in the 200 newest, over 90 days old', async () => {
@ -434,11 +433,11 @@ describe('AccountManager', () => {
// The latest batch
...range(0, 100).map(
(id): PreKeyType => ({
id: `${ourUuid.toString()}:${id}`,
id: `${ourAci}:${id}`,
createdAt: now - DAY,
keyId: 1,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
privateKey: privKey,
publicKey: pubKey,
})
@ -446,22 +445,22 @@ describe('AccountManager', () => {
// Second-oldest batch, won't be dropped
...range(100, 200).map(
(id): PreKeyType => ({
id: `${ourUuid.toString()}:${id}`,
id: `${ourAci}:${id}`,
createdAt: now - DAY * 40,
keyId: 1,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
privateKey: privKey,
publicKey: pubKey,
})
),
// Oldest batch, will be dropped
{
id: `${ourUuid.toString()}:6`,
id: `${ourAci}:6`,
createdAt: now - DAY * 92,
keyId: 6,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
privateKey: privKey,
publicKey: pubKey,
},
@ -472,7 +471,7 @@ describe('AccountManager', () => {
removedKeys = removedKeys.concat(keyIds);
};
await accountManager._cleanPreKeys(UUIDKind.ACI);
await accountManager._cleanPreKeys(ServiceIdKind.ACI);
assert.deepEqual(removedKeys, [6]);
});
});
@ -502,59 +501,59 @@ describe('AccountManager', () => {
const now = Date.now();
kyberPreKeys = [
{
id: `${ourUuid.toString()}:1`,
id: `${ourAci}:1`,
createdAt: now - DAY * 93,
data: getRandomBytes(32),
isConfirmed: false,
isLastResort: false,
keyId: 1,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
},
{
id: `${ourUuid.toString()}:2`,
id: `${ourAci}:2`,
createdAt: now - DAY * 93,
data: getRandomBytes(32),
isConfirmed: false,
isLastResort: false,
keyId: 2,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
},
{
id: `${ourUuid.toString()}:3`,
id: `${ourAci}:3`,
createdAt: now - DAY * 93,
data: getRandomBytes(32),
isConfirmed: false,
isLastResort: false,
keyId: 3,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
},
{
id: `${ourUuid.toString()}:4`,
id: `${ourAci}:4`,
createdAt: now - DAY * 93,
data: getRandomBytes(32),
isConfirmed: false,
isLastResort: false,
keyId: 4,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
},
{
id: `${ourUuid.toString()}:5`,
id: `${ourAci}:5`,
createdAt: now - DAY * 93,
data: getRandomBytes(32),
isConfirmed: false,
isLastResort: false,
keyId: 5,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
},
];
// should be no calls to store.removeKyberPreKey, would cause crash
return accountManager._cleanKyberPreKeys(UUIDKind.ACI);
return accountManager._cleanKyberPreKeys(ServiceIdKind.ACI);
});
it('eliminates keys not in the newest 200, over 90 days old', async () => {
@ -563,39 +562,39 @@ describe('AccountManager', () => {
// The latest batch
...range(0, 100).map(
(id): KyberPreKeyType => ({
id: `${ourUuid.toString()}:${id}`,
id: `${ourAci}:${id}`,
createdAt: now - DAY,
data: getRandomBytes(32),
isConfirmed: false,
isLastResort: false,
keyId: 1,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
})
),
// Second-oldest batch, won't be dropped
...range(100, 200).map(
(id): KyberPreKeyType => ({
id: `${ourUuid.toString()}:${id}`,
id: `${ourAci}:${id}`,
createdAt: now - DAY * 45,
data: getRandomBytes(32),
isConfirmed: false,
isLastResort: false,
keyId: 4,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
})
),
// Oldest batch, will be dropped
{
id: `${ourUuid.toString()}:6`,
id: `${ourAci}:6`,
createdAt: now - DAY * 93,
data: getRandomBytes(32),
isConfirmed: false,
isLastResort: false,
keyId: 6,
ourUuid: ourUuid.toString(),
ourUuid: ourAci,
},
];
@ -607,7 +606,7 @@ describe('AccountManager', () => {
removedKeys = removedKeys.concat(keyIds);
};
await accountManager._cleanKyberPreKeys(UUIDKind.ACI);
await accountManager._cleanKyberPreKeys(ServiceIdKind.ACI);
assert.deepEqual(removedKeys, [6]);
});
});

View file

@ -2,11 +2,10 @@
// SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai';
import { v4 as getGuid } from 'uuid';
import { getRandomBytes } from '../../Crypto';
import { Address } from '../../types/Address';
import { UUID } from '../../types/UUID';
import { generateAci } from '../../types/ServiceId';
import { explodePromise } from '../../util/explodePromise';
import { SignalProtocolStore } from '../../SignalProtocolStore';
import type { ConversationModel } from '../../models/conversations';
@ -17,9 +16,9 @@ describe('KeyChangeListener', () => {
let oldNumberId: string | undefined;
let oldUuidId: string | undefined;
const ourUuid = getGuid();
const uuidWithKeyChange = getGuid();
const address = Address.create(uuidWithKeyChange, 1);
const ourServiceId = generateAci();
const ourServiceIdWithKeyChange = generateAci();
const address = Address.create(ourServiceIdWithKeyChange, 1);
const oldKey = getRandomBytes(33);
const newKey = getRandomBytes(33);
let store: SignalProtocolStore;
@ -33,7 +32,7 @@ describe('KeyChangeListener', () => {
oldNumberId = storage.get('number_id');
oldUuidId = storage.get('uuid_id');
await storage.put('number_id', '+14155555556.2');
await storage.put('uuid_id', `${ourUuid}.2`);
await storage.put('uuid_id', `${ourServiceId}.2`);
});
after(async () => {
@ -57,7 +56,7 @@ describe('KeyChangeListener', () => {
await window.ConversationController.load();
convo = await window.ConversationController.getOrCreateAndWait(
uuidWithKeyChange,
ourServiceIdWithKeyChange,
'private'
);
@ -69,11 +68,11 @@ describe('KeyChangeListener', () => {
afterEach(async () => {
await window.Signal.Data.removeAllMessagesInConversation(convo.id, {
logId: uuidWithKeyChange,
logId: ourServiceIdWithKeyChange,
});
await window.Signal.Data.removeConversation(convo.id);
await store.removeIdentityKey(new UUID(uuidWithKeyChange));
await store.removeIdentityKey(ourServiceIdWithKeyChange);
});
describe('When we have a conversation with this contact', () => {
@ -99,14 +98,14 @@ describe('KeyChangeListener', () => {
),
'group',
{
members: [uuidWithKeyChange],
members: [ourServiceIdWithKeyChange],
}
);
});
afterEach(async () => {
await window.Signal.Data.removeAllMessagesInConversation(groupConvo.id, {
logId: uuidWithKeyChange,
logId: ourServiceIdWithKeyChange,
});
await window.Signal.Data.removeConversation(groupConvo.id);
});
@ -116,7 +115,7 @@ describe('KeyChangeListener', () => {
const { resolve, promise } = explodePromise<void>();
groupConvo.addKeyChange = async (_, keyChangedId) => {
assert.equal(uuidWithKeyChange, keyChangedId?.toString());
assert.equal(ourServiceIdWithKeyChange, keyChangedId);
groupConvo.addKeyChange = original;
resolve();
};

View file

@ -8,7 +8,7 @@ import { generateKeyPair } from '../../Curve';
import type { UploadKeysType } from '../../textsecure/WebAPI';
import AccountManager from '../../textsecure/AccountManager';
import type { PreKeyType, SignedPreKeyType } from '../../textsecure/Types.d';
import { UUID, UUIDKind } from '../../types/UUID';
import { ServiceIdKind, normalizeAci } from '../../types/ServiceId';
const { textsecure } = window;
@ -18,14 +18,17 @@ const assertEqualBuffers = (a: Uint8Array, b: Uint8Array) => {
describe('Key generation', function thisNeeded() {
const count = 10;
const ourUuid = new UUID('aaaaaaaa-bbbb-4ccc-9ddd-eeeeeeeeeeee');
const ourServiceId = normalizeAci(
'aaaaaaaa-bbbb-4ccc-9ddd-eeeeeeeeeeee',
'test'
);
let result: UploadKeysType;
this.timeout(count * 2000);
function itStoresPreKey(keyId: number): void {
it(`prekey ${keyId} is valid`, async () => {
const keyPair = await textsecure.storage.protocol.loadPreKey(
ourUuid,
ourServiceId,
keyId
);
assert(keyPair, `PreKey ${keyId} not found`);
@ -34,7 +37,7 @@ describe('Key generation', function thisNeeded() {
function itStoresKyberPreKey(keyId: number): void {
it(`kyber pre key ${keyId} is valid`, async () => {
const key = await textsecure.storage.protocol.loadKyberPreKey(
ourUuid,
ourServiceId,
keyId
);
assert(key, `kyber pre key ${keyId} not found`);
@ -43,7 +46,7 @@ describe('Key generation', function thisNeeded() {
function itStoresSignedPreKey(keyId: number): void {
it(`signed prekey ${keyId} is valid`, async () => {
const keyPair = await textsecure.storage.protocol.loadSignedPreKey(
ourUuid,
ourServiceId,
keyId
);
assert(keyPair, `SignedPreKey ${keyId} not found`);
@ -54,7 +57,7 @@ describe('Key generation', function thisNeeded() {
resultKey: Pick<PreKeyType, 'keyId' | 'publicKey'>
): Promise<void> {
const keyPair = await textsecure.storage.protocol.loadPreKey(
ourUuid,
ourServiceId,
resultKey.keyId
);
if (!keyPair) {
@ -69,7 +72,7 @@ describe('Key generation', function thisNeeded() {
throw new Error('validateResultSignedKey: No signed prekey provided!');
}
const keyPair = await textsecure.storage.protocol.loadSignedPreKey(
ourUuid,
ourServiceId,
resultSignedKey.keyId
);
if (!keyPair) {
@ -88,9 +91,9 @@ describe('Key generation', function thisNeeded() {
const keyPair = generateKeyPair();
await textsecure.storage.put('identityKeyMap', {
[ourUuid.toString()]: keyPair,
[ourServiceId]: keyPair,
});
await textsecure.storage.user.setUuidAndDeviceId(ourUuid.toString(), 1);
await textsecure.storage.user.setUuidAndDeviceId(ourServiceId, 1);
await textsecure.storage.protocol.hydrateCaches();
});
@ -105,7 +108,7 @@ describe('Key generation', function thisNeeded() {
before(async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const accountManager = new AccountManager({} as any);
result = await accountManager._generateKeys(count, UUIDKind.ACI);
result = await accountManager._generateKeys(count, ServiceIdKind.ACI);
});
describe('generates the basics', () => {
@ -146,7 +149,7 @@ describe('Key generation', function thisNeeded() {
before(async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const accountManager = new AccountManager({} as any);
result = await accountManager._generateKeys(count, UUIDKind.ACI);
result = await accountManager._generateKeys(count, ServiceIdKind.ACI);
});
describe('generates the basics', () => {
@ -189,9 +192,9 @@ describe('Key generation', function thisNeeded() {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const accountManager = new AccountManager({} as any);
await accountManager._confirmKeys(result, UUIDKind.ACI);
await accountManager._confirmKeys(result, ServiceIdKind.ACI);
result = await accountManager._generateKeys(count, UUIDKind.ACI);
result = await accountManager._generateKeys(count, ServiceIdKind.ACI);
});
describe('generates the basics', () => {
@ -227,7 +230,7 @@ describe('Key generation', function thisNeeded() {
it('does not generate a third last resort prekey', async () => {
const keyId = 3 * count + 3;
const key = await textsecure.storage.protocol.loadKyberPreKey(
ourUuid,
ourServiceId,
keyId
);
assert.isUndefined(key, `kyber pre key ${keyId} was unexpectedly found`);
@ -235,7 +238,7 @@ describe('Key generation', function thisNeeded() {
it('does not generate a third signed prekey', async () => {
const keyId = 3;
const keyPair = await textsecure.storage.protocol.loadSignedPreKey(
ourUuid,
ourServiceId,
keyId
);
assert.isUndefined(