Support for loading, storing, and using kyber keys in decryption

This commit is contained in:
Scott Nonnenberg
2023-07-14 09:53:20 -07:00
committed by Fedor Indutnyy
parent c1580a5eb3
commit b6445a6af0
49 changed files with 2260 additions and 806 deletions

View File

@@ -930,7 +930,7 @@ describe('SignalProtocolStore', () => {
});
describe('storePreKey', () => {
it('stores prekeys', async () => {
await store.storePreKey(ourUuid, 1, testKey);
await store.storePreKeys(ourUuid, [{ keyId: 1, keyPair: testKey }]);
const key = await store.loadPreKey(ourUuid, 1);
if (!key) {
throw new Error('Missing key!');
@@ -947,10 +947,10 @@ describe('SignalProtocolStore', () => {
});
describe('removePreKey', () => {
before(async () => {
await store.storePreKey(ourUuid, 2, testKey);
await store.storePreKeys(ourUuid, [{ keyId: 2, keyPair: testKey }]);
});
it('deletes prekeys', async () => {
await store.removePreKey(ourUuid, 2);
await store.removePreKeys(ourUuid, [2]);
const key = await store.loadPreKey(ourUuid, 2);
assert.isUndefined(key);
@@ -978,7 +978,7 @@ describe('SignalProtocolStore', () => {
await store.storeSignedPreKey(ourUuid, 4, testKey);
});
it('deletes signed prekeys', async () => {
await store.removeSignedPreKey(ourUuid, 4);
await store.removeSignedPreKeys(ourUuid, [4]);
const key = await store.loadSignedPreKey(ourUuid, 4);
assert.isUndefined(key);
@@ -1557,7 +1557,7 @@ describe('SignalProtocolStore', () => {
});
describe('removeOurOldPni/updateOurPniKeyMaterial', () => {
beforeEach(async () => {
await store.storePreKey(ourUuid, 2, testKey);
await store.storePreKeys(ourUuid, [{ keyId: 2, keyPair: testKey }]);
await store.storeSignedPreKey(ourUuid, 3, testKey);
});

View File

@@ -2,84 +2,98 @@
// SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai';
import { range } from 'lodash';
import { getRandomBytes } from '../../Crypto';
import AccountManager from '../../textsecure/AccountManager';
import type { OuterSignedPrekeyType } from '../../textsecure/Types.d';
import type {
KyberPreKeyType,
OuterSignedPrekeyType,
PreKeyType,
} from '../../textsecure/Types.d';
import { UUID, UUIDKind } from '../../types/UUID';
import { DAY } from '../../util/durations';
/* eslint-disable @typescript-eslint/no-explicit-any */
describe('AccountManager', () => {
let accountManager: AccountManager;
const ourUuid = UUID.generate();
const identityKey = window.Signal.Curve.generateKeyPair();
const pubKey = getRandomBytes(33);
const privKey = getRandomBytes(32);
let originalGetIdentityKeyPair: any;
let originalGetUuid: any;
let originalGetCheckedUuid: any;
beforeEach(() => {
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;
});
describe('#cleanSignedPreKeys', () => {
let originalGetIdentityKeyPair: any;
afterEach(() => {
window.textsecure.storage.protocol.getIdentityKeyPair =
originalGetIdentityKeyPair;
window.textsecure.storage.user.getUuid = originalGetUuid;
window.textsecure.storage.user.getCheckedUuid = originalGetCheckedUuid;
});
describe('encrypted device name', () => {
it('roundtrips', async () => {
const deviceName = 'v2.5.0 on Ubunto 20.04';
const encrypted = accountManager.encryptDeviceName(
deviceName,
identityKey
);
if (!encrypted) {
throw new Error('failed to encrypt!');
}
assert.strictEqual(typeof encrypted, 'string');
const decrypted = await accountManager.decryptDeviceName(encrypted);
assert.strictEqual(decrypted, deviceName);
});
it('handles falsey deviceName', () => {
const encrypted = accountManager.encryptDeviceName('', identityKey);
assert.strictEqual(encrypted, null);
});
});
describe('#_cleanSignedPreKeys', () => {
let originalLoadSignedPreKeys: any;
let originalRemoveSignedPreKey: any;
let originalGetUuid: any;
let signedPreKeys: Array<OuterSignedPrekeyType>;
const DAY = 1000 * 60 * 60 * 24;
const pubKey = getRandomBytes(33);
const privKey = getRandomBytes(32);
const identityKey = window.Signal.Curve.generateKeyPair();
beforeEach(async () => {
const ourUuid = UUID.generate();
originalGetUuid = window.textsecure.storage.user.getUuid;
originalGetIdentityKeyPair =
window.textsecure.storage.protocol.getIdentityKeyPair;
originalLoadSignedPreKeys =
window.textsecure.storage.protocol.loadSignedPreKeys;
originalRemoveSignedPreKey =
window.textsecure.storage.protocol.removeSignedPreKey;
window.textsecure.storage.protocol.removeSignedPreKeys;
window.textsecure.storage.user.getUuid = () => ourUuid;
window.textsecure.storage.protocol.getIdentityKeyPair = () => identityKey;
window.textsecure.storage.protocol.loadSignedPreKeys = async () =>
window.textsecure.storage.protocol.loadSignedPreKeys = () =>
signedPreKeys;
// removeSignedPreKeys is updated per-test, below
});
afterEach(() => {
window.textsecure.storage.user.getUuid = originalGetUuid;
window.textsecure.storage.protocol.getIdentityKeyPair =
originalGetIdentityKeyPair;
window.textsecure.storage.protocol.loadSignedPreKeys =
originalLoadSignedPreKeys;
window.textsecure.storage.protocol.removeSignedPreKey =
window.textsecure.storage.protocol.removeSignedPreKeys =
originalRemoveSignedPreKey;
});
describe('encrypted device name', () => {
it('roundtrips', async () => {
const deviceName = 'v2.5.0 on Ubunto 20.04';
const encrypted = accountManager.encryptDeviceName(
deviceName,
identityKey
);
if (!encrypted) {
throw new Error('failed to encrypt!');
}
assert.strictEqual(typeof encrypted, 'string');
const decrypted = await accountManager.decryptDeviceName(encrypted);
assert.strictEqual(decrypted, deviceName);
});
it('handles falsey deviceName', () => {
const encrypted = accountManager.encryptDeviceName('', identityKey);
assert.strictEqual(encrypted, null);
});
});
it('keeps three confirmed keys even if over a month old', () => {
it('keeps no keys if five or less, even if over a month old', () => {
const now = Date.now();
signedPreKeys = [
{
@@ -103,10 +117,24 @@ describe('AccountManager', () => {
pubKey,
privKey,
},
{
keyId: 4,
created_at: now - DAY * 39,
confirmed: true,
pubKey,
privKey,
},
{
keyId: 5,
created_at: now - DAY * 40,
confirmed: false,
pubKey,
privKey,
},
];
// should be no calls to store.removeSignedPreKey, would cause crash
return accountManager.cleanSignedPreKeys(UUIDKind.ACI);
return accountManager._cleanSignedPreKeys(UUIDKind.ACI);
});
it('eliminates oldest keys, even if recent key is unconfirmed', async () => {
@@ -157,60 +185,430 @@ describe('AccountManager', () => {
},
];
let count = 0;
window.textsecure.storage.protocol.removeSignedPreKey = async (
let removedKeys: Array<number> = [];
window.textsecure.storage.protocol.removeSignedPreKeys = async (
_,
keyId
keyIds
) => {
if (keyId !== 4) {
throw new Error(`Wrong keys were eliminated! ${keyId}`);
}
count += 1;
removedKeys = removedKeys.concat(keyIds);
};
await accountManager.cleanSignedPreKeys(UUIDKind.ACI);
assert.strictEqual(count, 1);
await accountManager._cleanSignedPreKeys(UUIDKind.ACI);
assert.deepEqual(removedKeys, [4]);
});
});
describe('#_cleanLastResortKeys', () => {
let originalLoadKyberPreKeys: any;
let originalRemoveKyberPreKey: any;
let kyberPreKeys: Array<KyberPreKeyType>;
beforeEach(async () => {
originalLoadKyberPreKeys =
window.textsecure.storage.protocol.loadKyberPreKeys;
originalRemoveKyberPreKey =
window.textsecure.storage.protocol.removeKyberPreKeys;
window.textsecure.storage.protocol.loadKyberPreKeys = () => kyberPreKeys;
// removeKyberPreKeys is updated per-test, below
});
afterEach(() => {
window.textsecure.storage.protocol.loadKyberPreKeys =
originalLoadKyberPreKeys;
window.textsecure.storage.protocol.removeKyberPreKeys =
originalRemoveKyberPreKey;
});
it('Removes no keys if less than five', async () => {
it('keeps five keys even if over a month old', () => {
const now = Date.now();
signedPreKeys = [
kyberPreKeys = [
{
id: `${ourUuid.toString()}:1`,
createdAt: now - DAY * 32,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: true,
keyId: 1,
created_at: now - DAY * 32,
confirmed: true,
pubKey,
privKey,
ourUuid: ourUuid.toString(),
},
{
id: `${ourUuid.toString()}:2`,
createdAt: now - DAY * 34,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: true,
keyId: 2,
created_at: now - DAY * 44,
confirmed: true,
pubKey,
privKey,
ourUuid: ourUuid.toString(),
},
{
id: `${ourUuid.toString()}:3`,
createdAt: now - DAY * 38,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: true,
keyId: 3,
created_at: now - DAY * 36,
confirmed: false,
pubKey,
privKey,
ourUuid: ourUuid.toString(),
},
{
id: `${ourUuid.toString()}:4`,
createdAt: now - DAY * 39,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: false,
keyId: 4,
created_at: now - DAY * 20,
confirmed: false,
pubKey,
privKey,
ourUuid: ourUuid.toString(),
},
{
id: `${ourUuid.toString()}:5`,
createdAt: now - DAY * 40,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: false,
keyId: 5,
ourUuid: ourUuid.toString(),
},
];
window.textsecure.storage.protocol.removeSignedPreKey = async () => {
throw new Error('None should be removed!');
// should be no calls to store.removeKyberPreKey, would cause crash
return accountManager._cleanLastResortKeys(UUIDKind.ACI);
});
it('eliminates oldest keys, even if recent key is unconfirmed', async () => {
const now = Date.now();
kyberPreKeys = [
{
id: `${ourUuid.toString()}:1`,
createdAt: now - DAY * 32,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: true,
keyId: 1,
ourUuid: ourUuid.toString(),
},
{
id: `${ourUuid.toString()}:2`,
createdAt: now - DAY * 31,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: false,
keyId: 2,
ourUuid: ourUuid.toString(),
},
{
id: `${ourUuid.toString()}:3`,
createdAt: now - DAY * 24,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: true,
keyId: 3,
ourUuid: ourUuid.toString(),
},
{
// Oldest, should be dropped
id: `${ourUuid.toString()}:4`,
createdAt: now - DAY * 38,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: true,
keyId: 4,
ourUuid: ourUuid.toString(),
},
{
id: `${ourUuid.toString()}:5`,
createdAt: now - DAY * 5,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: true,
keyId: 5,
ourUuid: ourUuid.toString(),
},
{
id: `${ourUuid.toString()}:6`,
createdAt: now - DAY * 5,
data: getRandomBytes(32),
isLastResort: true,
isConfirmed: true,
keyId: 6,
ourUuid: ourUuid.toString(),
},
];
let removedKeys: Array<number> = [];
window.textsecure.storage.protocol.removeKyberPreKeys = async (
_,
keyIds
) => {
removedKeys = removedKeys.concat(keyIds);
};
await accountManager.cleanSignedPreKeys(UUIDKind.ACI);
await accountManager._cleanLastResortKeys(UUIDKind.ACI);
assert.deepEqual(removedKeys, [4]);
});
});
describe('#_cleanPreKeys', () => {
let originalLoadPreKeys: any;
let originalRemovePreKeys: any;
let preKeys: Array<PreKeyType>;
beforeEach(async () => {
originalLoadPreKeys = window.textsecure.storage.protocol.loadPreKeys;
originalRemovePreKeys = window.textsecure.storage.protocol.removePreKeys;
window.textsecure.storage.protocol.loadPreKeys = () => preKeys;
// removePreKeys is updated per-test, below
});
afterEach(() => {
window.textsecure.storage.protocol.loadPreKeys = originalLoadPreKeys;
window.textsecure.storage.protocol.removePreKeys = originalRemovePreKeys;
});
it('keeps five keys even if over 90 days old, but all latest batch', () => {
const now = Date.now();
preKeys = [
{
id: `${ourUuid.toString()}:1`,
createdAt: now - DAY * 92,
keyId: 1,
ourUuid: ourUuid.toString(),
privateKey: privKey,
publicKey: pubKey,
},
{
id: `${ourUuid.toString()}:2`,
createdAt: now - DAY * 93,
keyId: 2,
ourUuid: ourUuid.toString(),
privateKey: privKey,
publicKey: pubKey,
},
{
id: `${ourUuid.toString()}:3`,
createdAt: now - DAY * 93,
keyId: 3,
ourUuid: ourUuid.toString(),
privateKey: privKey,
publicKey: pubKey,
},
{
id: `${ourUuid.toString()}:4`,
createdAt: now - DAY * 93,
keyId: 4,
ourUuid: ourUuid.toString(),
privateKey: privKey,
publicKey: pubKey,
},
{
id: `${ourUuid.toString()}:5`,
createdAt: now - DAY * 94,
keyId: 5,
ourUuid: ourUuid.toString(),
privateKey: privKey,
publicKey: pubKey,
},
];
// should be no calls to store.removeKyberPreKey, would cause crash
return accountManager._cleanPreKeys(UUIDKind.ACI);
});
it('eliminates keys not in the 200 newest, over 90 days old', async () => {
const now = Date.now();
preKeys = [
// The latest batch
...range(0, 100).map(
(id): PreKeyType => ({
id: `${ourUuid.toString()}:${id}`,
createdAt: now - DAY,
keyId: 1,
ourUuid: ourUuid.toString(),
privateKey: privKey,
publicKey: pubKey,
})
),
// Second-oldest batch, won't be dropped
...range(100, 200).map(
(id): PreKeyType => ({
id: `${ourUuid.toString()}:${id}`,
createdAt: now - DAY * 40,
keyId: 1,
ourUuid: ourUuid.toString(),
privateKey: privKey,
publicKey: pubKey,
})
),
// Oldest batch, will be dropped
{
id: `${ourUuid.toString()}:6`,
createdAt: now - DAY * 92,
keyId: 6,
ourUuid: ourUuid.toString(),
privateKey: privKey,
publicKey: pubKey,
},
];
let removedKeys: Array<number> = [];
window.textsecure.storage.protocol.removePreKeys = async (_, keyIds) => {
removedKeys = removedKeys.concat(keyIds);
};
await accountManager._cleanPreKeys(UUIDKind.ACI);
assert.deepEqual(removedKeys, [6]);
});
});
describe('#_cleanKyberPreKeys', () => {
let originalLoadKyberPreKeys: any;
let originalRemoveKyberPreKeys: any;
let kyberPreKeys: Array<KyberPreKeyType>;
beforeEach(async () => {
originalLoadKyberPreKeys =
window.textsecure.storage.protocol.loadKyberPreKeys;
originalRemoveKyberPreKeys =
window.textsecure.storage.protocol.removeKyberPreKeys;
window.textsecure.storage.protocol.loadKyberPreKeys = () => kyberPreKeys;
// removeKyberPreKeys is updated per-test, below
});
afterEach(() => {
window.textsecure.storage.protocol.loadKyberPreKeys =
originalLoadKyberPreKeys;
window.textsecure.storage.protocol.removeKyberPreKeys =
originalRemoveKyberPreKeys;
});
it('keeps five keys even if over 90 days old', () => {
const now = Date.now();
kyberPreKeys = [
{
id: `${ourUuid.toString()}:1`,
createdAt: now - DAY * 93,
data: getRandomBytes(32),
isConfirmed: false,
isLastResort: false,
keyId: 1,
ourUuid: ourUuid.toString(),
},
{
id: `${ourUuid.toString()}:2`,
createdAt: now - DAY * 93,
data: getRandomBytes(32),
isConfirmed: false,
isLastResort: false,
keyId: 2,
ourUuid: ourUuid.toString(),
},
{
id: `${ourUuid.toString()}:3`,
createdAt: now - DAY * 93,
data: getRandomBytes(32),
isConfirmed: false,
isLastResort: false,
keyId: 3,
ourUuid: ourUuid.toString(),
},
{
id: `${ourUuid.toString()}:4`,
createdAt: now - DAY * 93,
data: getRandomBytes(32),
isConfirmed: false,
isLastResort: false,
keyId: 4,
ourUuid: ourUuid.toString(),
},
{
id: `${ourUuid.toString()}:5`,
createdAt: now - DAY * 93,
data: getRandomBytes(32),
isConfirmed: false,
isLastResort: false,
keyId: 5,
ourUuid: ourUuid.toString(),
},
];
// should be no calls to store.removeKyberPreKey, would cause crash
return accountManager._cleanKyberPreKeys(UUIDKind.ACI);
});
it('eliminates keys not in the newest 200, over 90 days old', async () => {
const now = Date.now();
kyberPreKeys = [
// The latest batch
...range(0, 100).map(
(id): KyberPreKeyType => ({
id: `${ourUuid.toString()}:${id}`,
createdAt: now - DAY,
data: getRandomBytes(32),
isConfirmed: false,
isLastResort: false,
keyId: 1,
ourUuid: ourUuid.toString(),
})
),
// Second-oldest batch, won't be dropped
...range(100, 200).map(
(id): KyberPreKeyType => ({
id: `${ourUuid.toString()}:${id}`,
createdAt: now - DAY * 45,
data: getRandomBytes(32),
isConfirmed: false,
isLastResort: false,
keyId: 4,
ourUuid: ourUuid.toString(),
})
),
// Oldest batch, will be dropped
{
id: `${ourUuid.toString()}:6`,
createdAt: now - DAY * 93,
data: getRandomBytes(32),
isConfirmed: false,
isLastResort: false,
keyId: 6,
ourUuid: ourUuid.toString(),
},
];
let removedKeys: Array<number> = [];
window.textsecure.storage.protocol.removeKyberPreKeys = async (
_,
keyIds
) => {
removedKeys = removedKeys.concat(keyIds);
};
await accountManager._cleanKyberPreKeys(UUIDKind.ACI);
assert.deepEqual(removedKeys, [6]);
});
});
});

View File

@@ -5,7 +5,7 @@ import { assert } from 'chai';
import { constantTimeEqual } from '../../Crypto';
import { generateKeyPair } from '../../Curve';
import type { GeneratedKeysType } from '../../textsecure/AccountManager';
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';
@@ -19,6 +19,7 @@ const assertEqualBuffers = (a: Uint8Array, b: Uint8Array) => {
describe('Key generation', function thisNeeded() {
const count = 10;
const ourUuid = new UUID('aaaaaaaa-bbbb-4ccc-9ddd-eeeeeeeeeeee');
let result: UploadKeysType;
this.timeout(count * 2000);
function itStoresPreKey(keyId: number): void {
@@ -30,6 +31,15 @@ describe('Key generation', function thisNeeded() {
assert(keyPair, `PreKey ${keyId} not found`);
});
}
function itStoresKyberPreKey(keyId: number): void {
it(`kyber pre key ${keyId} is valid`, async () => {
const key = await textsecure.storage.protocol.loadKyberPreKey(
ourUuid,
keyId
);
assert(key, `kyber pre key ${keyId} not found`);
});
}
function itStoresSignedPreKey(keyId: number): void {
it(`signed prekey ${keyId} is valid`, async () => {
const keyPair = await textsecure.storage.protocol.loadSignedPreKey(
@@ -39,7 +49,8 @@ describe('Key generation', function thisNeeded() {
assert(keyPair, `SignedPreKey ${keyId} not found`);
});
}
async function validateResultKey(
async function validateResultPreKey(
resultKey: Pick<PreKeyType, 'keyId' | 'publicKey'>
): Promise<void> {
const keyPair = await textsecure.storage.protocol.loadPreKey(
@@ -52,8 +63,11 @@ describe('Key generation', function thisNeeded() {
assertEqualBuffers(resultKey.publicKey, keyPair.publicKey().serialize());
}
async function validateResultSignedKey(
resultSignedKey: Pick<SignedPreKeyType, 'keyId' | 'publicKey'>
resultSignedKey?: Pick<SignedPreKeyType, 'keyId' | 'publicKey'>
) {
if (!resultSignedKey) {
throw new Error('validateResultSignedKey: No signed prekey provided!');
}
const keyPair = await textsecure.storage.protocol.loadSignedPreKey(
ourUuid,
resultSignedKey.keyId
@@ -68,120 +82,166 @@ describe('Key generation', function thisNeeded() {
}
before(async () => {
await textsecure.storage.protocol.clearPreKeyStore();
await textsecure.storage.protocol.clearKyberPreKeyStore();
await textsecure.storage.protocol.clearSignedPreKeysStore();
const keyPair = generateKeyPair();
await textsecure.storage.put('identityKeyMap', {
[ourUuid.toString()]: keyPair,
});
await textsecure.storage.user.setUuidAndDeviceId(ourUuid.toString(), 1);
await textsecure.storage.protocol.hydrateCaches();
});
after(async () => {
await textsecure.storage.protocol.clearPreKeyStore();
await textsecure.storage.protocol.clearKyberPreKeyStore();
await textsecure.storage.protocol.clearSignedPreKeysStore();
});
describe('the first time', () => {
let result: GeneratedKeysType;
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, UUIDKind.ACI);
});
for (let i = 1; i <= count; i += 1) {
itStoresPreKey(i);
}
itStoresSignedPreKey(1);
describe('generates the basics', () => {
for (let i = 1; i <= count; i += 1) {
itStoresPreKey(i);
}
for (let i = 1; i <= count + 1; i += 1) {
itStoresKyberPreKey(i);
}
itStoresSignedPreKey(1);
});
it(`result contains ${count} preKeys`, () => {
assert.isArray(result.preKeys);
assert.lengthOf(result.preKeys, count);
const preKeys = result.preKeys || [];
assert.isArray(preKeys);
assert.lengthOf(preKeys, count);
for (let i = 0; i < count; i += 1) {
assert.isObject(result.preKeys[i]);
assert.isObject(preKeys[i]);
}
});
it('result contains the correct keyIds', () => {
const preKeys = result.preKeys || [];
for (let i = 0; i < count; i += 1) {
assert.strictEqual(result.preKeys[i].keyId, i + 1);
assert.strictEqual(preKeys[i].keyId, i + 1);
}
});
it('result contains the correct public keys', async () => {
await Promise.all(result.preKeys.map(validateResultKey));
const preKeys = result.preKeys || [];
await Promise.all(preKeys.map(validateResultPreKey));
});
it('returns a signed prekey', () => {
assert.strictEqual(result.signedPreKey.keyId, 1);
assert.instanceOf(result.signedPreKey.signature, Uint8Array);
assert.strictEqual(result.signedPreKey?.keyId, 1);
assert.instanceOf(result.signedPreKey?.signature, Uint8Array);
return validateResultSignedKey(result.signedPreKey);
});
});
describe('the second time', () => {
let result: GeneratedKeysType;
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, UUIDKind.ACI);
});
describe('generates the basics', () => {
for (let i = 1; i <= 2 * count; i += 1) {
itStoresPreKey(i);
}
for (let i = 1; i <= 2 * count + 2; i += 1) {
itStoresKyberPreKey(i);
}
itStoresSignedPreKey(1);
itStoresSignedPreKey(2);
});
for (let i = 1; i <= 2 * count; i += 1) {
itStoresPreKey(i);
}
itStoresSignedPreKey(1);
itStoresSignedPreKey(2);
it(`result contains ${count} preKeys`, () => {
assert.isArray(result.preKeys);
assert.lengthOf(result.preKeys, count);
const preKeys = result.preKeys || [];
assert.isArray(preKeys);
assert.lengthOf(preKeys, count);
for (let i = 0; i < count; i += 1) {
assert.isObject(result.preKeys[i]);
assert.isObject(preKeys[i]);
}
});
it('result contains the correct keyIds', () => {
const preKeys = result.preKeys || [];
for (let i = 1; i <= count; i += 1) {
assert.strictEqual(result.preKeys[i - 1].keyId, i + count);
assert.strictEqual(preKeys[i - 1].keyId, i + count);
}
});
it('result contains the correct public keys', async () => {
await Promise.all(result.preKeys.map(validateResultKey));
const preKeys = result.preKeys || [];
await Promise.all(preKeys.map(validateResultPreKey));
});
it('returns a signed prekey', () => {
assert.strictEqual(result.signedPreKey.keyId, 2);
assert.instanceOf(result.signedPreKey.signature, Uint8Array);
assert.strictEqual(result.signedPreKey?.keyId, 2);
assert.instanceOf(result.signedPreKey?.signature, Uint8Array);
return validateResultSignedKey(result.signedPreKey);
});
});
describe('the third time', () => {
let result: GeneratedKeysType;
describe('the third time, after keys are confirmed', () => {
before(async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const accountManager = new AccountManager({} as any);
result = await accountManager.generateKeys(count, UUIDKind.ACI);
await accountManager._confirmKeys(result, UUIDKind.ACI);
result = await accountManager._generateKeys(count, UUIDKind.ACI);
});
describe('generates the basics', () => {
for (let i = 1; i <= 3 * count; i += 1) {
itStoresPreKey(i);
}
// Note: no new last resort kyber key generated
for (let i = 1; i <= 3 * count + 2; i += 1) {
itStoresKyberPreKey(i);
}
itStoresSignedPreKey(1);
itStoresSignedPreKey(2);
});
for (let i = 1; i <= 3 * count; i += 1) {
itStoresPreKey(i);
}
itStoresSignedPreKey(2);
itStoresSignedPreKey(3);
it(`result contains ${count} preKeys`, () => {
assert.isArray(result.preKeys);
assert.lengthOf(result.preKeys, count);
const preKeys = result.preKeys || [];
assert.isArray(preKeys);
assert.lengthOf(preKeys, count);
for (let i = 0; i < count; i += 1) {
assert.isObject(result.preKeys[i]);
assert.isObject(preKeys[i]);
}
});
it('result contains the correct keyIds', () => {
const preKeys = result.preKeys || [];
for (let i = 1; i <= count; i += 1) {
assert.strictEqual(result.preKeys[i - 1].keyId, i + 2 * count);
assert.strictEqual(preKeys[i - 1].keyId, i + 2 * count);
}
});
it('result contains the correct public keys', async () => {
await Promise.all(result.preKeys.map(validateResultKey));
const preKeys = result.preKeys || [];
await Promise.all(preKeys.map(validateResultPreKey));
});
it('result contains a signed prekey', () => {
assert.strictEqual(result.signedPreKey.keyId, 3);
assert.instanceOf(result.signedPreKey.signature, Uint8Array);
return validateResultSignedKey(result.signedPreKey);
it('does not generate a third last resort prekey', async () => {
const keyId = 3 * count + 3;
const key = await textsecure.storage.protocol.loadKyberPreKey(
ourUuid,
keyId
);
assert.isUndefined(key, `kyber pre key ${keyId} was unexpectedly found`);
});
it('does not generate a third signed prekey', async () => {
const keyId = 3;
const keyPair = await textsecure.storage.protocol.loadSignedPreKey(
ourUuid,
keyId
);
assert.isUndefined(
keyPair,
`SignedPreKey ${keyId} was unexpectedly found`
);
});
});
});