2023-01-03 19:55:46 +00:00
|
|
|
// Copyright 2015 Signal Messenger, LLC
|
2020-10-30 20:34:04 +00:00
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
2021-04-16 23:13:13 +00:00
|
|
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
2015-04-01 20:08:09 +00:00
|
|
|
|
2024-07-22 19:27:09 +00:00
|
|
|
import { assert } from 'chai';
|
2022-11-30 00:53:39 +00:00
|
|
|
import { clone } from 'lodash';
|
2021-05-14 01:18:43 +00:00
|
|
|
import {
|
|
|
|
Direction,
|
2022-08-02 01:31:24 +00:00
|
|
|
IdentityKeyPair,
|
|
|
|
PrivateKey,
|
|
|
|
PublicKey,
|
2021-05-14 01:18:43 +00:00
|
|
|
SenderKeyRecord,
|
|
|
|
SessionRecord,
|
2022-08-02 01:31:24 +00:00
|
|
|
SignedPreKeyRecord,
|
2022-03-24 21:47:21 +00:00
|
|
|
} from '@signalapp/libsignal-client';
|
2023-08-10 16:43:33 +00:00
|
|
|
import { v4 as generateUuid } from 'uuid';
|
2021-04-16 23:13:13 +00:00
|
|
|
|
2024-07-22 18:16:33 +00:00
|
|
|
import { DataReader, DataWriter } from '../sql/Client';
|
2021-04-16 23:13:13 +00:00
|
|
|
import { signal } from '../protobuf/compiled';
|
2021-09-24 00:49:05 +00:00
|
|
|
import { sessionStructureToBytes } from '../util/sessionTranslation';
|
2021-12-14 01:25:44 +00:00
|
|
|
import * as durations from '../util/durations';
|
2023-10-05 00:39:09 +00:00
|
|
|
import { explodePromise } from '../util/explodePromise';
|
2021-05-19 21:25:56 +00:00
|
|
|
import { Zone } from '../util/Zone';
|
2021-04-16 23:13:13 +00:00
|
|
|
|
2022-08-02 01:31:24 +00:00
|
|
|
import * as Bytes from '../Bytes';
|
2021-09-24 00:49:05 +00:00
|
|
|
import { getRandomBytes, constantTimeEqual } from '../Crypto';
|
2022-07-28 16:35:29 +00:00
|
|
|
import {
|
|
|
|
clampPrivateKey,
|
|
|
|
setPublicKeyTypeByte,
|
2022-08-02 01:31:24 +00:00
|
|
|
generateSignedPreKey,
|
2022-07-28 16:35:29 +00:00
|
|
|
} from '../Curve';
|
2021-10-26 19:15:33 +00:00
|
|
|
import type { SignalProtocolStore } from '../SignalProtocolStore';
|
|
|
|
import { GLOBAL_ZONE } from '../SignalProtocolStore';
|
2021-09-10 02:38:11 +00:00
|
|
|
import { Address } from '../types/Address';
|
|
|
|
import { QualifiedAddress } from '../types/QualifiedAddress';
|
2023-08-10 16:43:33 +00:00
|
|
|
import { generateAci, generatePni } from '../types/ServiceId';
|
2021-10-26 19:15:33 +00:00
|
|
|
import type { IdentityKeyType, KeyPairType } from '../textsecure/Types.d';
|
2021-04-16 23:13:13 +00:00
|
|
|
|
2021-05-14 01:18:43 +00:00
|
|
|
const {
|
|
|
|
RecordStructure,
|
|
|
|
SessionStructure,
|
|
|
|
SenderKeyRecordStructure,
|
|
|
|
SenderKeyStateStructure,
|
|
|
|
} = signal.proto.storage;
|
2017-06-29 02:39:55 +00:00
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('SignalProtocolStore', () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const ourAci = generateAci();
|
2023-12-07 21:52:27 +00:00
|
|
|
const ourPni = generatePni();
|
2023-08-10 16:43:33 +00:00
|
|
|
const theirAci = generateAci();
|
2021-04-16 23:13:13 +00:00
|
|
|
let store: SignalProtocolStore;
|
|
|
|
let identityKey: KeyPairType;
|
|
|
|
let testKey: KeyPairType;
|
|
|
|
|
|
|
|
function getSessionRecord(isOpen?: boolean): SessionRecord {
|
|
|
|
const proto = new RecordStructure();
|
|
|
|
|
|
|
|
proto.previousSessions = [];
|
|
|
|
|
|
|
|
if (isOpen) {
|
|
|
|
proto.currentSession = new SessionStructure();
|
|
|
|
|
2021-09-24 00:49:05 +00:00
|
|
|
proto.currentSession.aliceBaseKey = getPublicKey();
|
|
|
|
proto.currentSession.localIdentityPublic = getPublicKey();
|
2021-04-16 23:13:13 +00:00
|
|
|
proto.currentSession.localRegistrationId = 435;
|
|
|
|
|
|
|
|
proto.currentSession.previousCounter = 1;
|
2021-09-24 00:49:05 +00:00
|
|
|
proto.currentSession.remoteIdentityPublic = getPublicKey();
|
2021-04-16 23:13:13 +00:00
|
|
|
proto.currentSession.remoteRegistrationId = 243;
|
|
|
|
|
2021-09-24 00:49:05 +00:00
|
|
|
proto.currentSession.rootKey = getPrivateKey();
|
2021-04-16 23:13:13 +00:00
|
|
|
proto.currentSession.sessionVersion = 3;
|
2023-10-20 23:55:35 +00:00
|
|
|
proto.currentSession.senderChain = {};
|
2021-04-16 23:13:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return SessionRecord.deserialize(
|
2021-09-24 00:49:05 +00:00
|
|
|
Buffer.from(sessionStructureToBytes(proto))
|
2021-04-16 23:13:13 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-05-14 01:18:43 +00:00
|
|
|
function getSenderKeyRecord(): SenderKeyRecord {
|
|
|
|
const proto = new SenderKeyRecordStructure();
|
|
|
|
|
|
|
|
const state = new SenderKeyStateStructure();
|
|
|
|
|
|
|
|
state.senderKeyId = 4;
|
|
|
|
|
|
|
|
const senderChainKey = new SenderKeyStateStructure.SenderChainKey();
|
|
|
|
|
|
|
|
senderChainKey.iteration = 10;
|
2021-09-24 00:49:05 +00:00
|
|
|
senderChainKey.seed = getPublicKey();
|
2021-05-14 01:18:43 +00:00
|
|
|
state.senderChainKey = senderChainKey;
|
|
|
|
|
|
|
|
const senderSigningKey = new SenderKeyStateStructure.SenderSigningKey();
|
2021-09-24 00:49:05 +00:00
|
|
|
senderSigningKey.public = getPublicKey();
|
|
|
|
senderSigningKey.private = getPrivateKey();
|
2021-05-14 01:18:43 +00:00
|
|
|
|
|
|
|
state.senderSigningKey = senderSigningKey;
|
|
|
|
|
|
|
|
state.senderMessageKeys = [];
|
|
|
|
const messageKey = new SenderKeyStateStructure.SenderMessageKey();
|
|
|
|
messageKey.iteration = 234;
|
2021-09-24 00:49:05 +00:00
|
|
|
messageKey.seed = getPublicKey();
|
2021-05-14 01:18:43 +00:00
|
|
|
state.senderMessageKeys.push(messageKey);
|
|
|
|
|
|
|
|
proto.senderKeyStates = [];
|
|
|
|
proto.senderKeyStates.push(state);
|
|
|
|
|
|
|
|
return SenderKeyRecord.deserialize(
|
|
|
|
Buffer.from(
|
|
|
|
signal.proto.storage.SenderKeyRecordStructure.encode(proto).finish()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-04-16 23:13:13 +00:00
|
|
|
function getPrivateKey() {
|
|
|
|
const key = getRandomBytes(32);
|
|
|
|
clampPrivateKey(key);
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
function getPublicKey() {
|
|
|
|
const key = getRandomBytes(33);
|
|
|
|
setPublicKeyTypeByte(key);
|
|
|
|
return key;
|
|
|
|
}
|
2017-07-25 18:33:02 +00:00
|
|
|
|
2020-03-05 21:14:58 +00:00
|
|
|
before(async () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
store = window.textsecure.storage.protocol;
|
2022-12-21 18:41:48 +00:00
|
|
|
await store.hydrateCaches();
|
2017-06-29 02:39:55 +00:00
|
|
|
identityKey = {
|
2021-04-16 23:13:13 +00:00
|
|
|
pubKey: getPublicKey(),
|
|
|
|
privKey: getPrivateKey(),
|
2017-06-29 02:39:55 +00:00
|
|
|
};
|
|
|
|
testKey = {
|
2021-04-16 23:13:13 +00:00
|
|
|
pubKey: getPublicKey(),
|
|
|
|
privKey: getPrivateKey(),
|
2017-06-29 02:39:55 +00:00
|
|
|
};
|
|
|
|
|
2021-04-16 23:13:13 +00:00
|
|
|
setPublicKeyTypeByte(identityKey.pubKey);
|
|
|
|
setPublicKeyTypeByte(testKey.pubKey);
|
|
|
|
|
|
|
|
clampPrivateKey(identityKey.privKey);
|
|
|
|
clampPrivateKey(testKey.privKey);
|
|
|
|
|
2022-12-21 18:41:48 +00:00
|
|
|
await window.storage.put('registrationIdMap', {
|
2023-08-10 16:43:33 +00:00
|
|
|
[ourAci]: 1337,
|
2022-12-21 18:41:48 +00:00
|
|
|
});
|
|
|
|
await window.storage.put('identityKeyMap', {
|
2023-08-10 16:43:33 +00:00
|
|
|
[ourAci]: {
|
2022-07-28 16:35:29 +00:00
|
|
|
privKey: identityKey.privKey,
|
|
|
|
pubKey: identityKey.pubKey,
|
2021-09-10 02:38:11 +00:00
|
|
|
},
|
|
|
|
});
|
2021-04-16 23:13:13 +00:00
|
|
|
await window.storage.fetch();
|
|
|
|
|
|
|
|
window.ConversationController.reset();
|
|
|
|
await window.ConversationController.load();
|
2023-08-10 16:43:33 +00:00
|
|
|
await window.ConversationController.getOrCreateAndWait(theirAci, 'private');
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
2017-06-29 02:39:55 +00:00
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('getLocalRegistrationId', () => {
|
|
|
|
it('retrieves my registration id', async () => {
|
2019-09-26 19:56:31 +00:00
|
|
|
await store.hydrateCaches();
|
2023-08-10 16:43:33 +00:00
|
|
|
const id = await store.getLocalRegistrationId(ourAci);
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.strictEqual(id, 1337);
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('getIdentityKeyPair', () => {
|
|
|
|
it('retrieves my identity key', async () => {
|
2019-09-26 19:56:31 +00:00
|
|
|
await store.hydrateCaches();
|
2023-08-10 16:43:33 +00:00
|
|
|
const key = store.getIdentityKeyPair(ourAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!key) {
|
|
|
|
throw new Error('Missing key!');
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.isTrue(constantTimeEqual(key.pubKey, identityKey.pubKey));
|
|
|
|
assert.isTrue(constantTimeEqual(key.privKey, identityKey.privKey));
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
|
|
|
});
|
2017-06-13 20:57:46 +00:00
|
|
|
|
2021-05-14 01:18:43 +00:00
|
|
|
describe('senderKeys', () => {
|
|
|
|
it('roundtrips in memory', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const distributionId = generateUuid();
|
2021-05-14 01:18:43 +00:00
|
|
|
const expected = getSenderKeyRecord();
|
|
|
|
|
|
|
|
const deviceId = 1;
|
2021-09-10 02:38:11 +00:00
|
|
|
const qualifiedAddress = new QualifiedAddress(
|
2023-08-10 16:43:33 +00:00
|
|
|
ourAci,
|
|
|
|
new Address(theirAci, deviceId)
|
2021-09-10 02:38:11 +00:00
|
|
|
);
|
2021-05-14 01:18:43 +00:00
|
|
|
|
2021-09-10 02:38:11 +00:00
|
|
|
await store.saveSenderKey(qualifiedAddress, distributionId, expected);
|
2021-05-14 01:18:43 +00:00
|
|
|
|
2021-09-10 02:38:11 +00:00
|
|
|
const actual = await store.getSenderKey(qualifiedAddress, distributionId);
|
2021-05-14 01:18:43 +00:00
|
|
|
if (!actual) {
|
|
|
|
throw new Error('getSenderKey returned nothing!');
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.isTrue(
|
2021-09-24 00:49:05 +00:00
|
|
|
constantTimeEqual(expected.serialize(), actual.serialize())
|
2021-05-14 01:18:43 +00:00
|
|
|
);
|
2021-05-25 22:40:04 +00:00
|
|
|
|
2021-09-10 02:38:11 +00:00
|
|
|
await store.removeSenderKey(qualifiedAddress, distributionId);
|
2021-05-25 22:40:04 +00:00
|
|
|
|
|
|
|
const postDeleteGet = await store.getSenderKey(
|
2021-09-10 02:38:11 +00:00
|
|
|
qualifiedAddress,
|
2021-05-25 22:40:04 +00:00
|
|
|
distributionId
|
|
|
|
);
|
|
|
|
assert.isUndefined(postDeleteGet);
|
2021-05-14 01:18:43 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
it('roundtrips through database', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const distributionId = generateUuid();
|
2021-05-14 01:18:43 +00:00
|
|
|
const expected = getSenderKeyRecord();
|
|
|
|
|
|
|
|
const deviceId = 1;
|
2021-09-10 02:38:11 +00:00
|
|
|
const qualifiedAddress = new QualifiedAddress(
|
2023-08-10 16:43:33 +00:00
|
|
|
ourAci,
|
|
|
|
new Address(theirAci, deviceId)
|
2021-09-10 02:38:11 +00:00
|
|
|
);
|
2021-05-14 01:18:43 +00:00
|
|
|
|
2021-09-10 02:38:11 +00:00
|
|
|
await store.saveSenderKey(qualifiedAddress, distributionId, expected);
|
2021-05-14 01:18:43 +00:00
|
|
|
|
|
|
|
// Re-fetch from the database to ensure we get the latest database value
|
|
|
|
await store.hydrateCaches();
|
|
|
|
|
2021-09-10 02:38:11 +00:00
|
|
|
const actual = await store.getSenderKey(qualifiedAddress, distributionId);
|
2021-05-14 01:18:43 +00:00
|
|
|
if (!actual) {
|
|
|
|
throw new Error('getSenderKey returned nothing!');
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.isTrue(
|
2021-09-24 00:49:05 +00:00
|
|
|
constantTimeEqual(expected.serialize(), actual.serialize())
|
2021-05-14 01:18:43 +00:00
|
|
|
);
|
2021-05-25 22:40:04 +00:00
|
|
|
|
2021-09-10 02:38:11 +00:00
|
|
|
await store.removeSenderKey(qualifiedAddress, distributionId);
|
2021-05-25 22:40:04 +00:00
|
|
|
|
|
|
|
// Re-fetch from the database to ensure we get the latest database value
|
|
|
|
await store.hydrateCaches();
|
|
|
|
|
|
|
|
const postDeleteGet = await store.getSenderKey(
|
2021-09-10 02:38:11 +00:00
|
|
|
qualifiedAddress,
|
2021-05-25 22:40:04 +00:00
|
|
|
distributionId
|
|
|
|
);
|
|
|
|
assert.isUndefined(postDeleteGet);
|
2021-05-14 01:18:43 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('saveIdentity', () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const identifier = new Address(theirAci, 1);
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
it('stores identity keys', async () => {
|
2018-10-18 01:01:21 +00:00
|
|
|
await store.saveIdentity(identifier, testKey.pubKey);
|
2023-08-10 16:43:33 +00:00
|
|
|
const key = await store.loadIdentityKey(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!key) {
|
|
|
|
throw new Error('Missing key!');
|
|
|
|
}
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2021-04-16 23:13:13 +00:00
|
|
|
assert.isTrue(constantTimeEqual(key, testKey.pubKey));
|
2015-07-22 19:48:08 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('allows key changes', async () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
const newIdentity = getPublicKey();
|
2018-10-18 01:01:21 +00:00
|
|
|
await store.saveIdentity(identifier, testKey.pubKey);
|
|
|
|
await store.saveIdentity(identifier, newIdentity);
|
2016-05-04 07:09:44 +00:00
|
|
|
});
|
2023-10-05 00:39:09 +00:00
|
|
|
it('should not deadlock', async () => {
|
|
|
|
const newIdentity = getPublicKey();
|
|
|
|
const zone = new Zone('zone', {
|
|
|
|
pendingSenderKeys: true,
|
|
|
|
pendingSessions: true,
|
|
|
|
pendingUnprocessed: true,
|
|
|
|
});
|
|
|
|
|
|
|
|
await store.saveIdentity(identifier, testKey.pubKey);
|
|
|
|
|
|
|
|
const { promise, resolve } = explodePromise<void>();
|
|
|
|
|
|
|
|
await Promise.all([
|
|
|
|
store.withZone(zone, 'test', async () => {
|
|
|
|
await promise;
|
|
|
|
return store.saveIdentity(identifier, newIdentity, false, { zone });
|
|
|
|
}),
|
|
|
|
store.saveIdentity(identifier, newIdentity, false, {
|
|
|
|
zone: GLOBAL_ZONE,
|
|
|
|
}),
|
|
|
|
resolve(),
|
|
|
|
]);
|
|
|
|
});
|
2017-05-31 01:04:03 +00:00
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('When there is no existing key (first use)', () => {
|
|
|
|
before(async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
await store.removeIdentityKey(theirAci);
|
2018-10-18 01:01:21 +00:00
|
|
|
await store.saveIdentity(identifier, testKey.pubKey);
|
2017-05-31 01:04:03 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('marks the key firstUse', async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
2018-10-18 01:01:21 +00:00
|
|
|
assert(identity.firstUse);
|
2017-05-31 01:04:03 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('sets the timestamp', async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
2018-10-18 01:01:21 +00:00
|
|
|
assert(identity.timestamp);
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('sets the verified status to DEFAULT', async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.strictEqual(identity.verified, store.VerifiedStatus.DEFAULT);
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('When there is a different existing key (non first use)', () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
const newIdentity = getPublicKey();
|
2018-10-18 01:01:21 +00:00
|
|
|
const oldTimestamp = Date.now();
|
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
before(async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
await DataWriter.createOrUpdateIdentityKey({
|
2023-08-10 16:43:33 +00:00
|
|
|
id: theirAci,
|
2018-10-18 01:01:21 +00:00
|
|
|
publicKey: testKey.pubKey,
|
|
|
|
firstUse: true,
|
|
|
|
timestamp: oldTimestamp,
|
|
|
|
nonblockingApproval: false,
|
|
|
|
verified: store.VerifiedStatus.DEFAULT,
|
|
|
|
});
|
|
|
|
|
2019-02-04 23:54:37 +00:00
|
|
|
await store.hydrateCaches();
|
2018-10-18 01:01:21 +00:00
|
|
|
await store.saveIdentity(identifier, newIdentity);
|
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('marks the key not firstUse', async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
2018-10-18 01:01:21 +00:00
|
|
|
assert(!identity.firstUse);
|
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('updates the timestamp', async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.notEqual(identity.timestamp, oldTimestamp);
|
|
|
|
});
|
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('The previous verified status was DEFAULT', () => {
|
|
|
|
before(async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
await DataWriter.createOrUpdateIdentityKey({
|
2023-08-10 16:43:33 +00:00
|
|
|
id: theirAci,
|
2018-04-27 21:25:04 +00:00
|
|
|
publicKey: testKey.pubKey,
|
|
|
|
firstUse: true,
|
|
|
|
timestamp: oldTimestamp,
|
|
|
|
nonblockingApproval: false,
|
|
|
|
verified: store.VerifiedStatus.DEFAULT,
|
2017-05-31 01:04:03 +00:00
|
|
|
});
|
2019-02-04 23:54:37 +00:00
|
|
|
await store.hydrateCaches();
|
2017-06-13 20:57:46 +00:00
|
|
|
|
2018-10-18 01:01:21 +00:00
|
|
|
await store.saveIdentity(identifier, newIdentity);
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('sets the new key to default', async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.strictEqual(identity.verified, store.VerifiedStatus.DEFAULT);
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('The previous verified status was VERIFIED', () => {
|
|
|
|
before(async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
await DataWriter.createOrUpdateIdentityKey({
|
2023-08-10 16:43:33 +00:00
|
|
|
id: theirAci,
|
2018-10-18 01:01:21 +00:00
|
|
|
publicKey: testKey.pubKey,
|
|
|
|
firstUse: true,
|
|
|
|
timestamp: oldTimestamp,
|
|
|
|
nonblockingApproval: false,
|
|
|
|
verified: store.VerifiedStatus.VERIFIED,
|
|
|
|
});
|
2019-02-04 23:54:37 +00:00
|
|
|
|
|
|
|
await store.hydrateCaches();
|
2018-10-18 01:01:21 +00:00
|
|
|
await store.saveIdentity(identifier, newIdentity);
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('sets the new key to unverified', async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
2018-04-27 21:25:04 +00:00
|
|
|
assert.strictEqual(
|
2018-10-18 01:01:21 +00:00
|
|
|
identity.verified,
|
2018-04-27 21:25:04 +00:00
|
|
|
store.VerifiedStatus.UNVERIFIED
|
|
|
|
);
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('The previous verified status was UNVERIFIED', () => {
|
|
|
|
before(async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
await DataWriter.createOrUpdateIdentityKey({
|
2023-08-10 16:43:33 +00:00
|
|
|
id: theirAci,
|
2018-10-18 01:01:21 +00:00
|
|
|
publicKey: testKey.pubKey,
|
|
|
|
firstUse: true,
|
|
|
|
timestamp: oldTimestamp,
|
|
|
|
nonblockingApproval: false,
|
|
|
|
verified: store.VerifiedStatus.UNVERIFIED,
|
|
|
|
});
|
|
|
|
|
2019-02-04 23:54:37 +00:00
|
|
|
await store.hydrateCaches();
|
2018-10-18 01:01:21 +00:00
|
|
|
await store.saveIdentity(identifier, newIdentity);
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('sets the new key to unverified', async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
2018-04-27 21:25:04 +00:00
|
|
|
assert.strictEqual(
|
2018-10-18 01:01:21 +00:00
|
|
|
identity.verified,
|
2018-04-27 21:25:04 +00:00
|
|
|
store.VerifiedStatus.UNVERIFIED
|
|
|
|
);
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
|
|
|
});
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('When the key has not changed', () => {
|
|
|
|
const oldTimestamp = Date.now();
|
|
|
|
before(async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
await DataWriter.createOrUpdateIdentityKey({
|
2023-08-10 16:43:33 +00:00
|
|
|
id: theirAci,
|
2018-10-18 01:01:21 +00:00
|
|
|
publicKey: testKey.pubKey,
|
|
|
|
timestamp: oldTimestamp,
|
|
|
|
nonblockingApproval: false,
|
2021-04-16 23:13:13 +00:00
|
|
|
firstUse: false,
|
2018-10-18 01:01:21 +00:00
|
|
|
verified: store.VerifiedStatus.DEFAULT,
|
|
|
|
});
|
2019-02-04 23:54:37 +00:00
|
|
|
await store.hydrateCaches();
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('If it is marked firstUse', () => {
|
|
|
|
before(async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
2018-10-18 01:01:21 +00:00
|
|
|
identity.firstUse = true;
|
2024-07-22 18:16:33 +00:00
|
|
|
await DataWriter.createOrUpdateIdentityKey(identity);
|
2019-02-04 23:54:37 +00:00
|
|
|
await store.hydrateCaches();
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('nothing changes', async () => {
|
2018-10-18 01:01:21 +00:00
|
|
|
await store.saveIdentity(identifier, testKey.pubKey, true);
|
|
|
|
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
2018-10-18 01:01:21 +00:00
|
|
|
assert(!identity.nonblockingApproval);
|
|
|
|
assert.strictEqual(identity.timestamp, oldTimestamp);
|
2017-05-31 01:04:03 +00:00
|
|
|
});
|
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('If it is not marked firstUse', () => {
|
|
|
|
before(async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
2018-10-18 01:01:21 +00:00
|
|
|
identity.firstUse = false;
|
2024-07-22 18:16:33 +00:00
|
|
|
await DataWriter.createOrUpdateIdentityKey(identity);
|
2019-02-04 23:54:37 +00:00
|
|
|
await store.hydrateCaches();
|
2017-05-31 01:04:03 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('If nonblocking approval is required', () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
let now: number;
|
2018-11-02 18:02:53 +00:00
|
|
|
before(async () => {
|
2017-07-03 22:16:52 +00:00
|
|
|
now = Date.now();
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
2018-10-18 01:01:21 +00:00
|
|
|
identity.timestamp = now;
|
2024-07-22 18:16:33 +00:00
|
|
|
await DataWriter.createOrUpdateIdentityKey(identity);
|
2019-02-04 23:54:37 +00:00
|
|
|
await store.hydrateCaches();
|
2017-06-17 22:41:42 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('sets non-blocking approval', async () => {
|
2018-10-18 01:01:21 +00:00
|
|
|
await store.saveIdentity(identifier, testKey.pubKey, true);
|
|
|
|
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
2018-10-18 01:01:21 +00:00
|
|
|
|
|
|
|
assert.strictEqual(identity.nonblockingApproval, true);
|
|
|
|
assert.strictEqual(identity.timestamp, now);
|
|
|
|
assert.strictEqual(identity.firstUse, false);
|
2017-05-31 01:04:03 +00:00
|
|
|
});
|
|
|
|
});
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('saveIdentityWithAttributes', () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
let now: number;
|
|
|
|
let validAttributes: IdentityKeyType;
|
2017-06-29 02:39:55 +00:00
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
before(async () => {
|
2017-06-29 02:39:55 +00:00
|
|
|
now = Date.now();
|
|
|
|
validAttributes = {
|
2023-08-10 16:43:33 +00:00
|
|
|
id: theirAci,
|
2018-04-27 21:25:04 +00:00
|
|
|
publicKey: testKey.pubKey,
|
|
|
|
firstUse: true,
|
|
|
|
timestamp: now,
|
|
|
|
verified: store.VerifiedStatus.VERIFIED,
|
|
|
|
nonblockingApproval: false,
|
2017-06-29 02:39:55 +00:00
|
|
|
};
|
|
|
|
|
2023-08-10 16:43:33 +00:00
|
|
|
await store.removeIdentityKey(theirAci);
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('with valid attributes', () => {
|
|
|
|
before(async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
await store.saveIdentityWithAttributes(theirAci, validAttributes);
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
it('publicKey is saved', async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
|
|
|
assert.isTrue(constantTimeEqual(identity.publicKey, testKey.pubKey));
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('firstUse is saved', async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.strictEqual(identity.firstUse, true);
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('timestamp is saved', async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.strictEqual(identity.timestamp, now);
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('verified is saved', async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.strictEqual(identity.verified, store.VerifiedStatus.VERIFIED);
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('nonblockingApproval is saved', async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.strictEqual(identity.nonblockingApproval, false);
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('with invalid attributes', () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
let attributes: IdentityKeyType;
|
2018-11-02 18:02:53 +00:00
|
|
|
beforeEach(() => {
|
2022-11-30 00:53:39 +00:00
|
|
|
attributes = clone(validAttributes);
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
|
|
|
|
2018-10-18 01:01:21 +00:00
|
|
|
async function testInvalidAttributes() {
|
|
|
|
try {
|
2023-08-10 16:43:33 +00:00
|
|
|
await store.saveIdentityWithAttributes(theirAci, attributes);
|
2018-10-18 01:01:21 +00:00
|
|
|
throw new Error('saveIdentityWithAttributes should have failed');
|
|
|
|
} catch (error) {
|
|
|
|
// good. we expect to fail with invalid attributes.
|
|
|
|
}
|
2017-06-13 20:57:46 +00:00
|
|
|
}
|
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
it('rejects an invalid publicKey', async () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
attributes.publicKey = 'a string' as any;
|
2018-10-18 01:01:21 +00:00
|
|
|
await testInvalidAttributes();
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('rejects invalid firstUse', async () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
attributes.firstUse = 0 as any;
|
2018-10-18 01:01:21 +00:00
|
|
|
await testInvalidAttributes();
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('rejects invalid timestamp', async () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
attributes.timestamp = NaN as any;
|
2018-10-18 01:01:21 +00:00
|
|
|
await testInvalidAttributes();
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('rejects invalid verified', async () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
attributes.verified = null as any;
|
2018-10-18 01:01:21 +00:00
|
|
|
await testInvalidAttributes();
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('rejects invalid nonblockingApproval', async () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
attributes.nonblockingApproval = 0 as any;
|
2018-10-18 01:01:21 +00:00
|
|
|
await testInvalidAttributes();
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('setApproval', () => {
|
|
|
|
it('sets nonblockingApproval', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
await store.setApproval(theirAci, true);
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
2018-10-18 01:01:21 +00:00
|
|
|
|
|
|
|
assert.strictEqual(identity.nonblockingApproval, true);
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('setVerified', () => {
|
2018-10-18 01:01:21 +00:00
|
|
|
async function saveRecordDefault() {
|
2024-07-22 18:16:33 +00:00
|
|
|
await DataWriter.createOrUpdateIdentityKey({
|
2023-08-10 16:43:33 +00:00
|
|
|
id: theirAci,
|
2018-04-27 21:25:04 +00:00
|
|
|
publicKey: testKey.pubKey,
|
|
|
|
firstUse: true,
|
|
|
|
timestamp: Date.now(),
|
|
|
|
verified: store.VerifiedStatus.DEFAULT,
|
|
|
|
nonblockingApproval: false,
|
2017-06-17 03:21:16 +00:00
|
|
|
});
|
2019-02-04 23:54:37 +00:00
|
|
|
await store.hydrateCaches();
|
2017-06-13 20:57:46 +00:00
|
|
|
}
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('with no public key argument', () => {
|
2017-06-17 03:21:16 +00:00
|
|
|
before(saveRecordDefault);
|
2018-11-02 18:02:53 +00:00
|
|
|
it('updates the verified status', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
await store.setVerified(theirAci, store.VerifiedStatus.VERIFIED);
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
|
|
|
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.strictEqual(identity.verified, store.VerifiedStatus.VERIFIED);
|
2021-04-16 23:13:13 +00:00
|
|
|
assert.isTrue(constantTimeEqual(identity.publicKey, testKey.pubKey));
|
2017-06-17 03:21:16 +00:00
|
|
|
});
|
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('with the current public key', () => {
|
2017-06-17 03:21:16 +00:00
|
|
|
before(saveRecordDefault);
|
2018-11-02 18:02:53 +00:00
|
|
|
it('updates the verified status', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
await store.setVerified(theirAci, store.VerifiedStatus.VERIFIED);
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
|
|
|
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.strictEqual(identity.verified, store.VerifiedStatus.VERIFIED);
|
2021-04-16 23:13:13 +00:00
|
|
|
assert.isTrue(constantTimeEqual(identity.publicKey, testKey.pubKey));
|
2017-06-17 03:21:16 +00:00
|
|
|
});
|
|
|
|
});
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
2022-11-07 23:21:12 +00:00
|
|
|
|
|
|
|
describe('updateIdentityAfterSync', () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
const newIdentity = getPublicKey();
|
|
|
|
let keychangeTriggered: number;
|
2017-06-29 02:39:55 +00:00
|
|
|
|
2022-11-07 23:21:12 +00:00
|
|
|
beforeEach(async () => {
|
2017-06-29 02:39:55 +00:00
|
|
|
keychangeTriggered = 0;
|
2022-10-20 19:16:37 +00:00
|
|
|
store.on('keychange', () => {
|
2018-11-02 18:02:53 +00:00
|
|
|
keychangeTriggered += 1;
|
2017-06-29 02:39:55 +00:00
|
|
|
});
|
2022-11-07 23:21:12 +00:00
|
|
|
|
2024-07-22 18:16:33 +00:00
|
|
|
await DataWriter.createOrUpdateIdentityKey({
|
2023-08-10 16:43:33 +00:00
|
|
|
id: theirAci,
|
2022-11-07 23:21:12 +00:00
|
|
|
publicKey: testKey.pubKey,
|
|
|
|
timestamp: Date.now() - 10 * 1000 * 60,
|
|
|
|
verified: store.VerifiedStatus.DEFAULT,
|
|
|
|
firstUse: false,
|
|
|
|
nonblockingApproval: false,
|
|
|
|
});
|
|
|
|
await store.hydrateCaches();
|
2017-06-29 02:39:55 +00:00
|
|
|
});
|
2022-11-07 23:21:12 +00:00
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
afterEach(() => {
|
2022-10-20 19:16:37 +00:00
|
|
|
store.removeAllListeners('keychange');
|
2017-06-29 02:39:55 +00:00
|
|
|
});
|
|
|
|
|
2022-11-07 23:21:12 +00:00
|
|
|
it('should create an identity and set verified to DEFAULT', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const newAci = generateAci();
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2022-11-07 23:21:12 +00:00
|
|
|
const needsNotification = await store.updateIdentityAfterSync(
|
2023-08-10 16:43:33 +00:00
|
|
|
newAci,
|
2022-11-07 23:21:12 +00:00
|
|
|
store.VerifiedStatus.DEFAULT,
|
|
|
|
newIdentity
|
|
|
|
);
|
|
|
|
assert.isFalse(needsNotification);
|
|
|
|
assert.strictEqual(keychangeTriggered, 0);
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(newAci);
|
2022-11-07 23:21:12 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
|
|
|
assert.strictEqual(identity.verified, store.VerifiedStatus.DEFAULT);
|
|
|
|
assert.isTrue(constantTimeEqual(identity.publicKey, newIdentity));
|
2017-06-29 02:39:55 +00:00
|
|
|
});
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2022-11-07 23:21:12 +00:00
|
|
|
it('should create an identity and set verified to VERIFIED', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const newAci = generateAci();
|
2017-06-29 02:39:55 +00:00
|
|
|
|
2022-11-07 23:21:12 +00:00
|
|
|
const needsNotification = await store.updateIdentityAfterSync(
|
2023-08-10 16:43:33 +00:00
|
|
|
newAci,
|
2022-11-07 23:21:12 +00:00
|
|
|
store.VerifiedStatus.VERIFIED,
|
|
|
|
newIdentity
|
|
|
|
);
|
|
|
|
assert.isTrue(needsNotification);
|
|
|
|
assert.strictEqual(keychangeTriggered, 0);
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(newAci);
|
2022-11-07 23:21:12 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
|
|
|
assert.strictEqual(identity.verified, store.VerifiedStatus.VERIFIED);
|
|
|
|
assert.isTrue(constantTimeEqual(identity.publicKey, newIdentity));
|
2017-06-17 03:56:06 +00:00
|
|
|
});
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2022-11-07 23:21:12 +00:00
|
|
|
it('should update public key without verified change', async () => {
|
|
|
|
const needsNotification = await store.updateIdentityAfterSync(
|
2023-08-10 16:43:33 +00:00
|
|
|
theirAci,
|
2022-11-07 23:21:12 +00:00
|
|
|
store.VerifiedStatus.DEFAULT,
|
|
|
|
newIdentity
|
|
|
|
);
|
|
|
|
assert.isFalse(needsNotification);
|
|
|
|
assert.strictEqual(keychangeTriggered, 1);
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2022-11-07 23:21:12 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
|
|
|
assert.strictEqual(identity.verified, store.VerifiedStatus.DEFAULT);
|
|
|
|
assert.isTrue(constantTimeEqual(identity.publicKey, newIdentity));
|
|
|
|
});
|
2017-06-29 02:39:55 +00:00
|
|
|
|
2022-11-07 23:21:12 +00:00
|
|
|
it('should update verified without public key change', async () => {
|
|
|
|
const needsNotification = await store.updateIdentityAfterSync(
|
2023-08-10 16:43:33 +00:00
|
|
|
theirAci,
|
2022-11-07 23:21:12 +00:00
|
|
|
store.VerifiedStatus.VERIFIED,
|
|
|
|
testKey.pubKey
|
|
|
|
);
|
|
|
|
assert.isTrue(needsNotification);
|
|
|
|
assert.strictEqual(keychangeTriggered, 0);
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2024-07-22 18:16:33 +00:00
|
|
|
const identity = await DataReader.getIdentityKeyById(theirAci);
|
2022-11-07 23:21:12 +00:00
|
|
|
if (!identity) {
|
|
|
|
throw new Error('Missing identity!');
|
|
|
|
}
|
|
|
|
assert.strictEqual(identity.verified, store.VerifiedStatus.VERIFIED);
|
|
|
|
assert.isTrue(constantTimeEqual(identity.publicKey, testKey.pubKey));
|
2017-06-17 03:56:06 +00:00
|
|
|
});
|
|
|
|
});
|
2017-07-25 18:33:02 +00:00
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('isUntrusted', () => {
|
|
|
|
it('returns false if identity key old enough', async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
await DataWriter.createOrUpdateIdentityKey({
|
2023-08-10 16:43:33 +00:00
|
|
|
id: theirAci,
|
2018-04-27 21:25:04 +00:00
|
|
|
publicKey: testKey.pubKey,
|
|
|
|
timestamp: Date.now() - 10 * 1000 * 60,
|
|
|
|
verified: store.VerifiedStatus.DEFAULT,
|
|
|
|
firstUse: false,
|
|
|
|
nonblockingApproval: false,
|
2017-07-25 18:33:02 +00:00
|
|
|
});
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2019-02-04 23:54:37 +00:00
|
|
|
await store.hydrateCaches();
|
2023-08-10 16:43:33 +00:00
|
|
|
const untrusted = await store.isUntrusted(theirAci);
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.strictEqual(untrusted, false);
|
2017-07-25 18:33:02 +00:00
|
|
|
});
|
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
it('returns false if new but nonblockingApproval is true', async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
await DataWriter.createOrUpdateIdentityKey({
|
2023-08-10 16:43:33 +00:00
|
|
|
id: theirAci,
|
2018-04-27 21:25:04 +00:00
|
|
|
publicKey: testKey.pubKey,
|
|
|
|
timestamp: Date.now(),
|
|
|
|
verified: store.VerifiedStatus.DEFAULT,
|
|
|
|
firstUse: false,
|
|
|
|
nonblockingApproval: true,
|
2017-07-25 18:33:02 +00:00
|
|
|
});
|
2019-02-04 23:54:37 +00:00
|
|
|
await store.hydrateCaches();
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2023-08-10 16:43:33 +00:00
|
|
|
const untrusted = await store.isUntrusted(theirAci);
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.strictEqual(untrusted, false);
|
2017-07-25 18:33:02 +00:00
|
|
|
});
|
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
it('returns false if new but firstUse is true', async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
await DataWriter.createOrUpdateIdentityKey({
|
2023-08-10 16:43:33 +00:00
|
|
|
id: theirAci,
|
2018-04-27 21:25:04 +00:00
|
|
|
publicKey: testKey.pubKey,
|
|
|
|
timestamp: Date.now(),
|
|
|
|
verified: store.VerifiedStatus.DEFAULT,
|
|
|
|
firstUse: true,
|
|
|
|
nonblockingApproval: false,
|
2017-07-25 18:33:02 +00:00
|
|
|
});
|
2019-02-04 23:54:37 +00:00
|
|
|
await store.hydrateCaches();
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2023-08-10 16:43:33 +00:00
|
|
|
const untrusted = await store.isUntrusted(theirAci);
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.strictEqual(untrusted, false);
|
2017-07-25 18:33:02 +00:00
|
|
|
});
|
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
it('returns true if new, and no flags are set', async () => {
|
2024-07-22 18:16:33 +00:00
|
|
|
await DataWriter.createOrUpdateIdentityKey({
|
2023-08-10 16:43:33 +00:00
|
|
|
id: theirAci,
|
2018-04-27 21:25:04 +00:00
|
|
|
publicKey: testKey.pubKey,
|
|
|
|
timestamp: Date.now(),
|
|
|
|
verified: store.VerifiedStatus.DEFAULT,
|
|
|
|
firstUse: false,
|
|
|
|
nonblockingApproval: false,
|
2017-07-25 18:33:02 +00:00
|
|
|
});
|
2019-02-04 23:54:37 +00:00
|
|
|
await store.hydrateCaches();
|
|
|
|
|
2023-08-10 16:43:33 +00:00
|
|
|
const untrusted = await store.isUntrusted(theirAci);
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.strictEqual(untrusted, true);
|
2017-07-25 18:33:02 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('getVerified', () => {
|
|
|
|
before(async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
await store.setVerified(theirAci, store.VerifiedStatus.VERIFIED);
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('resolves to the verified status', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const result = await store.getVerified(theirAci);
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.strictEqual(result, store.VerifiedStatus.VERIFIED);
|
2017-06-13 20:57:46 +00:00
|
|
|
});
|
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('isTrustedIdentity', () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const identifier = new Address(theirAci, 1);
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('When invalid direction is given', () => {
|
|
|
|
it('should fail', async () => {
|
2022-01-28 00:28:41 +00:00
|
|
|
await assert.isRejected(
|
|
|
|
store.isTrustedIdentity(identifier, testKey.pubKey, 'dir' as any)
|
|
|
|
);
|
2017-05-31 01:04:03 +00:00
|
|
|
});
|
2016-05-04 07:09:44 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('When direction is RECEIVING', () => {
|
|
|
|
it('always returns true', async () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
const newIdentity = getPublicKey();
|
2018-10-18 01:01:21 +00:00
|
|
|
await store.saveIdentity(identifier, testKey.pubKey);
|
|
|
|
|
|
|
|
const trusted = await store.isTrustedIdentity(
|
|
|
|
identifier,
|
|
|
|
newIdentity,
|
2021-04-16 23:13:13 +00:00
|
|
|
Direction.Receiving
|
2018-10-18 01:01:21 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if (!trusted) {
|
|
|
|
throw new Error('isTrusted returned false when receiving');
|
|
|
|
}
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('When direction is SENDING', () => {
|
|
|
|
describe('When there is no existing key (first use)', () => {
|
|
|
|
before(async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
await store.removeIdentityKey(theirAci);
|
2017-05-30 22:21:56 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('returns true', async () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
const newIdentity = getPublicKey();
|
2018-10-18 01:01:21 +00:00
|
|
|
const trusted = await store.isTrustedIdentity(
|
|
|
|
identifier,
|
|
|
|
newIdentity,
|
2021-04-16 23:13:13 +00:00
|
|
|
Direction.Sending
|
2018-10-18 01:01:21 +00:00
|
|
|
);
|
|
|
|
if (!trusted) {
|
|
|
|
throw new Error('isTrusted returned false on first use');
|
|
|
|
}
|
2017-05-30 22:21:56 +00:00
|
|
|
});
|
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('When there is an existing key', () => {
|
|
|
|
before(async () => {
|
2018-10-18 01:01:21 +00:00
|
|
|
await store.saveIdentity(identifier, testKey.pubKey);
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('When the existing key is different', () => {
|
|
|
|
it('returns false', async () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
const newIdentity = getPublicKey();
|
2018-10-18 01:01:21 +00:00
|
|
|
const trusted = await store.isTrustedIdentity(
|
|
|
|
identifier,
|
|
|
|
newIdentity,
|
2021-04-16 23:13:13 +00:00
|
|
|
Direction.Sending
|
2018-10-18 01:01:21 +00:00
|
|
|
);
|
|
|
|
if (trusted) {
|
|
|
|
throw new Error('isTrusted returned true on untrusted key');
|
|
|
|
}
|
2017-05-31 01:04:03 +00:00
|
|
|
});
|
2016-05-04 07:30:42 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('When the existing key matches the new key', () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
const newIdentity = getPublicKey();
|
2018-11-02 18:02:53 +00:00
|
|
|
before(async () => {
|
2018-10-18 01:01:21 +00:00
|
|
|
await store.saveIdentity(identifier, newIdentity);
|
2017-05-31 01:04:03 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('returns false if keys match but we just received this new identiy', async () => {
|
2018-10-18 01:01:21 +00:00
|
|
|
const trusted = await store.isTrustedIdentity(
|
|
|
|
identifier,
|
|
|
|
newIdentity,
|
2021-04-16 23:13:13 +00:00
|
|
|
Direction.Sending
|
2018-10-18 01:01:21 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if (trusted) {
|
|
|
|
throw new Error('isTrusted returned true on untrusted key');
|
|
|
|
}
|
2018-04-27 21:25:04 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('returns true if we have already approved identity', async () => {
|
2018-10-18 01:01:21 +00:00
|
|
|
await store.saveIdentity(identifier, newIdentity, true);
|
|
|
|
|
|
|
|
const trusted = await store.isTrustedIdentity(
|
|
|
|
identifier,
|
|
|
|
newIdentity,
|
2021-04-16 23:13:13 +00:00
|
|
|
Direction.Sending
|
2018-10-18 01:01:21 +00:00
|
|
|
);
|
|
|
|
if (!trusted) {
|
|
|
|
throw new Error('isTrusted returned false on an approved key');
|
|
|
|
}
|
2017-05-31 01:04:03 +00:00
|
|
|
});
|
2017-05-30 22:21:56 +00:00
|
|
|
});
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
2015-04-01 20:08:09 +00:00
|
|
|
});
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('storePreKey', () => {
|
|
|
|
it('stores prekeys', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
await store.storePreKeys(ourAci, [{ keyId: 1, keyPair: testKey }]);
|
|
|
|
const key = await store.loadPreKey(ourAci, 1);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!key) {
|
|
|
|
throw new Error('Missing key!');
|
|
|
|
}
|
|
|
|
|
|
|
|
const keyPair = {
|
2021-09-24 00:49:05 +00:00
|
|
|
pubKey: key.publicKey().serialize(),
|
|
|
|
privKey: key.privateKey().serialize(),
|
2021-04-16 23:13:13 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
assert.isTrue(constantTimeEqual(keyPair.pubKey, testKey.pubKey));
|
|
|
|
assert.isTrue(constantTimeEqual(keyPair.privKey, testKey.privKey));
|
2016-05-04 07:30:42 +00:00
|
|
|
});
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('removePreKey', () => {
|
|
|
|
before(async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
await store.storePreKeys(ourAci, [{ keyId: 2, keyPair: testKey }]);
|
2015-04-01 20:08:09 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('deletes prekeys', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
await store.removePreKeys(ourAci, [2]);
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2023-08-10 16:43:33 +00:00
|
|
|
const key = await store.loadPreKey(ourAci, 2);
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.isUndefined(key);
|
2015-04-01 20:08:09 +00:00
|
|
|
});
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('storeSignedPreKey', () => {
|
|
|
|
it('stores signed prekeys', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
await store.storeSignedPreKey(ourAci, 3, testKey);
|
|
|
|
const key = await store.loadSignedPreKey(ourAci, 3);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!key) {
|
|
|
|
throw new Error('Missing key!');
|
|
|
|
}
|
|
|
|
|
|
|
|
const keyPair = {
|
2021-09-24 00:49:05 +00:00
|
|
|
pubKey: key.publicKey().serialize(),
|
|
|
|
privKey: key.privateKey().serialize(),
|
2021-04-16 23:13:13 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
assert.isTrue(constantTimeEqual(keyPair.pubKey, testKey.pubKey));
|
|
|
|
assert.isTrue(constantTimeEqual(keyPair.privKey, testKey.privKey));
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('removeSignedPreKey', () => {
|
|
|
|
before(async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
await store.storeSignedPreKey(ourAci, 4, testKey);
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
it('deletes signed prekeys', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
await store.removeSignedPreKeys(ourAci, [4]);
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2023-08-10 16:43:33 +00:00
|
|
|
const key = await store.loadSignedPreKey(ourAci, 4);
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.isUndefined(key);
|
2015-04-01 20:08:09 +00:00
|
|
|
});
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('storeSession', () => {
|
|
|
|
it('stores sessions', async () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
const testRecord = getSessionRecord();
|
2023-08-10 16:43:33 +00:00
|
|
|
const id = new QualifiedAddress(ourAci, new Address(theirAci, 1));
|
2021-09-10 02:38:11 +00:00
|
|
|
await store.storeSession(id, testRecord);
|
|
|
|
const record = await store.loadSession(id);
|
2021-04-16 23:13:13 +00:00
|
|
|
if (!record) {
|
|
|
|
throw new Error('Missing record!');
|
|
|
|
}
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2021-04-16 23:13:13 +00:00
|
|
|
assert.equal(record, testRecord);
|
2016-05-04 07:30:42 +00:00
|
|
|
});
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
2023-08-10 16:43:33 +00:00
|
|
|
describe('removeSessionsByServiceId', () => {
|
2021-09-10 02:38:11 +00:00
|
|
|
it('removes all sessions for a uuid', async () => {
|
|
|
|
const devices = [1, 2, 3].map(
|
|
|
|
deviceId =>
|
2023-08-10 16:43:33 +00:00
|
|
|
new QualifiedAddress(ourAci, new Address(theirAci, deviceId))
|
2021-09-10 02:38:11 +00:00
|
|
|
);
|
2018-10-18 01:01:21 +00:00
|
|
|
|
|
|
|
await Promise.all(
|
2021-09-10 02:38:11 +00:00
|
|
|
devices.map(async encodedAddress => {
|
|
|
|
await store.storeSession(encodedAddress, getSessionRecord());
|
2018-04-27 21:25:04 +00:00
|
|
|
})
|
2018-10-18 01:01:21 +00:00
|
|
|
);
|
|
|
|
|
2022-12-12 22:06:16 +00:00
|
|
|
const records0 = await Promise.all(
|
|
|
|
devices.map(device => store.loadSession(device))
|
|
|
|
);
|
|
|
|
for (let i = 0, max = records0.length; i < max; i += 1) {
|
|
|
|
assert.exists(records0[i], 'before delete');
|
|
|
|
}
|
|
|
|
|
2023-08-10 16:43:33 +00:00
|
|
|
await store.removeSessionsByServiceId(theirAci);
|
2018-10-18 01:01:21 +00:00
|
|
|
|
|
|
|
const records = await Promise.all(
|
2021-05-17 18:03:42 +00:00
|
|
|
devices.map(device => store.loadSession(device))
|
2018-10-18 01:01:21 +00:00
|
|
|
);
|
2022-12-12 22:06:16 +00:00
|
|
|
for (let i = 0, max = records.length; i < max; i += 1) {
|
|
|
|
assert.isUndefined(records[i], 'in-memory');
|
|
|
|
}
|
|
|
|
|
|
|
|
await store.hydrateCaches();
|
|
|
|
|
|
|
|
const records2 = await Promise.all(
|
|
|
|
devices.map(device => store.loadSession(device))
|
|
|
|
);
|
|
|
|
for (let i = 0, max = records2.length; i < max; i += 1) {
|
|
|
|
assert.isUndefined(records2[i], 'from database');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('removeSessionsByConversation', () => {
|
|
|
|
it('removes all sessions for a uuid', async () => {
|
|
|
|
const devices = [1, 2, 3].map(
|
|
|
|
deviceId =>
|
2023-08-10 16:43:33 +00:00
|
|
|
new QualifiedAddress(ourAci, new Address(theirAci, deviceId))
|
2022-12-12 22:06:16 +00:00
|
|
|
);
|
|
|
|
const conversationId = window.ConversationController.getOrCreate(
|
2023-08-10 16:43:33 +00:00
|
|
|
theirAci,
|
2022-12-12 22:06:16 +00:00
|
|
|
'private'
|
|
|
|
).id;
|
|
|
|
|
|
|
|
await Promise.all(
|
|
|
|
devices.map(async encodedAddress => {
|
|
|
|
await store.storeSession(encodedAddress, getSessionRecord());
|
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
const records0 = await Promise.all(
|
|
|
|
devices.map(device => store.loadSession(device))
|
|
|
|
);
|
|
|
|
for (let i = 0, max = records0.length; i < max; i += 1) {
|
|
|
|
assert.exists(records0[i], 'before delete');
|
|
|
|
}
|
2018-11-02 18:02:53 +00:00
|
|
|
|
2022-12-12 22:06:16 +00:00
|
|
|
await store.removeSessionsByConversation(conversationId);
|
|
|
|
|
|
|
|
const records = await Promise.all(
|
|
|
|
devices.map(device => store.loadSession(device))
|
|
|
|
);
|
2018-11-02 18:02:53 +00:00
|
|
|
for (let i = 0, max = records.length; i < max; i += 1) {
|
2022-12-12 22:06:16 +00:00
|
|
|
assert.isUndefined(records[i], 'in-memory');
|
|
|
|
}
|
|
|
|
|
|
|
|
await store.hydrateCaches();
|
|
|
|
|
|
|
|
const records2 = await Promise.all(
|
|
|
|
devices.map(device => store.loadSession(device))
|
|
|
|
);
|
|
|
|
for (let i = 0, max = records2.length; i < max; i += 1) {
|
|
|
|
assert.isUndefined(records[i], 'from database');
|
2018-10-18 01:01:21 +00:00
|
|
|
}
|
2015-04-22 02:13:13 +00:00
|
|
|
});
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('clearSessionStore', () => {
|
|
|
|
it('clears the session store', async () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
const testRecord = getSessionRecord();
|
2023-08-10 16:43:33 +00:00
|
|
|
const id = new QualifiedAddress(ourAci, new Address(theirAci, 1));
|
2021-09-10 02:38:11 +00:00
|
|
|
await store.storeSession(id, testRecord);
|
2018-10-18 01:01:21 +00:00
|
|
|
await store.clearSessionStore();
|
|
|
|
|
2021-09-10 02:38:11 +00:00
|
|
|
const record = await store.loadSession(id);
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.isUndefined(record);
|
2016-05-04 07:30:42 +00:00
|
|
|
});
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('getDeviceIds', () => {
|
2021-09-10 02:38:11 +00:00
|
|
|
it('returns deviceIds for a uuid', async () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
const openRecord = getSessionRecord(true);
|
2021-09-10 02:38:11 +00:00
|
|
|
const openDevices = [1, 2, 3, 10].map(
|
|
|
|
deviceId =>
|
2023-08-10 16:43:33 +00:00
|
|
|
new QualifiedAddress(ourAci, new Address(theirAci, deviceId))
|
2021-09-10 02:38:11 +00:00
|
|
|
);
|
2018-10-18 01:01:21 +00:00
|
|
|
await Promise.all(
|
2021-09-10 02:38:11 +00:00
|
|
|
openDevices.map(async address => {
|
|
|
|
await store.storeSession(address, openRecord);
|
2018-04-27 21:25:04 +00:00
|
|
|
})
|
2018-10-18 01:01:21 +00:00
|
|
|
);
|
|
|
|
|
2021-04-16 23:13:13 +00:00
|
|
|
const closedRecord = getSessionRecord(false);
|
2021-09-10 02:38:11 +00:00
|
|
|
await store.storeSession(
|
2023-08-10 16:43:33 +00:00
|
|
|
new QualifiedAddress(ourAci, new Address(theirAci, 11)),
|
2021-09-10 02:38:11 +00:00
|
|
|
closedRecord
|
|
|
|
);
|
2020-12-09 22:05:11 +00:00
|
|
|
|
2021-09-10 02:38:11 +00:00
|
|
|
const deviceIds = await store.getDeviceIds({
|
2023-08-10 16:43:33 +00:00
|
|
|
ourServiceId: ourAci,
|
|
|
|
serviceId: theirAci,
|
2021-09-10 02:38:11 +00:00
|
|
|
});
|
2018-11-20 01:38:55 +00:00
|
|
|
assert.sameMembers(deviceIds, [1, 2, 3, 10]);
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
2021-04-16 23:13:13 +00:00
|
|
|
|
2021-09-10 02:38:11 +00:00
|
|
|
it('returns empty array for a uuid with no device ids', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const foo = generateAci();
|
2021-09-10 02:38:11 +00:00
|
|
|
const deviceIds = await store.getDeviceIds({
|
2023-08-10 16:43:33 +00:00
|
|
|
ourServiceId: ourAci,
|
|
|
|
serviceId: foo,
|
2021-09-10 02:38:11 +00:00
|
|
|
});
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.sameMembers(deviceIds, []);
|
2015-04-22 02:13:13 +00:00
|
|
|
});
|
2017-06-14 17:40:21 +00:00
|
|
|
});
|
2017-07-17 22:46:00 +00:00
|
|
|
|
2021-05-25 22:40:04 +00:00
|
|
|
describe('getOpenDevices', () => {
|
2021-09-10 02:38:11 +00:00
|
|
|
it('returns all open devices for a uuid', async () => {
|
2021-05-25 22:40:04 +00:00
|
|
|
const openRecord = getSessionRecord(true);
|
2021-09-10 02:38:11 +00:00
|
|
|
const openDevices = [1, 2, 3, 10].map(
|
|
|
|
deviceId =>
|
2023-08-10 16:43:33 +00:00
|
|
|
new QualifiedAddress(ourAci, new Address(theirAci, deviceId))
|
2021-09-10 02:38:11 +00:00
|
|
|
);
|
2021-05-25 22:40:04 +00:00
|
|
|
await Promise.all(
|
2021-09-10 02:38:11 +00:00
|
|
|
openDevices.map(async address => {
|
|
|
|
await store.storeSession(address, openRecord);
|
2021-05-25 22:40:04 +00:00
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
const closedRecord = getSessionRecord(false);
|
2021-09-10 02:38:11 +00:00
|
|
|
await store.storeSession(
|
2023-08-10 16:43:33 +00:00
|
|
|
new QualifiedAddress(ourAci, new Address(theirAci, 11)),
|
2021-09-10 02:38:11 +00:00
|
|
|
closedRecord
|
|
|
|
);
|
2021-05-25 22:40:04 +00:00
|
|
|
|
2023-08-10 16:43:33 +00:00
|
|
|
const blah = generateAci();
|
|
|
|
const blah2 = generateAci();
|
|
|
|
|
|
|
|
const result = await store.getOpenDevices(ourAci, [
|
|
|
|
theirAci,
|
|
|
|
blah,
|
|
|
|
blah2,
|
2021-09-10 02:38:11 +00:00
|
|
|
]);
|
|
|
|
assert.deepStrictEqual(
|
|
|
|
{
|
|
|
|
...result,
|
2023-08-10 16:43:33 +00:00
|
|
|
devices: result.devices.map(({ id, serviceId, registrationId }) => ({
|
2021-09-10 02:38:11 +00:00
|
|
|
id,
|
2023-08-10 16:43:33 +00:00
|
|
|
serviceId,
|
2021-09-10 02:38:11 +00:00
|
|
|
registrationId,
|
|
|
|
})),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
devices: [
|
|
|
|
{
|
|
|
|
id: 1,
|
2023-08-10 16:43:33 +00:00
|
|
|
serviceId: theirAci,
|
2021-09-10 02:38:11 +00:00
|
|
|
registrationId: 243,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 2,
|
2023-08-10 16:43:33 +00:00
|
|
|
serviceId: theirAci,
|
2021-09-10 02:38:11 +00:00
|
|
|
registrationId: 243,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 3,
|
2023-08-10 16:43:33 +00:00
|
|
|
serviceId: theirAci,
|
2021-09-10 02:38:11 +00:00
|
|
|
registrationId: 243,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 10,
|
2023-08-10 16:43:33 +00:00
|
|
|
serviceId: theirAci,
|
2021-09-10 02:38:11 +00:00
|
|
|
registrationId: 243,
|
|
|
|
},
|
|
|
|
],
|
2023-08-10 16:43:33 +00:00
|
|
|
emptyServiceIds: [blah, blah2],
|
2021-09-10 02:38:11 +00:00
|
|
|
}
|
|
|
|
);
|
2021-05-25 22:40:04 +00:00
|
|
|
});
|
|
|
|
|
2021-09-10 02:38:11 +00:00
|
|
|
it('returns empty array for a uuid with no device ids', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const foo = generateAci();
|
|
|
|
const result = await store.getOpenDevices(ourAci, [foo]);
|
2021-05-25 22:40:04 +00:00
|
|
|
assert.deepEqual(result, {
|
|
|
|
devices: [],
|
2023-08-10 16:43:33 +00:00
|
|
|
emptyServiceIds: [foo],
|
2021-05-25 22:40:04 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2021-05-19 21:25:56 +00:00
|
|
|
describe('zones', () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const distributionId = generateUuid();
|
2021-05-19 21:25:56 +00:00
|
|
|
const zone = new Zone('zone', {
|
2022-01-08 02:12:13 +00:00
|
|
|
pendingSenderKeys: true,
|
2021-05-19 21:25:56 +00:00
|
|
|
pendingSessions: true,
|
|
|
|
pendingUnprocessed: true,
|
|
|
|
});
|
|
|
|
|
2021-05-17 18:03:42 +00:00
|
|
|
beforeEach(async () => {
|
|
|
|
await store.removeAllUnprocessed();
|
2023-08-10 16:43:33 +00:00
|
|
|
await store.removeSessionsByServiceId(theirAci);
|
2022-01-08 02:12:13 +00:00
|
|
|
await store.removeAllSenderKeys();
|
2021-05-17 18:03:42 +00:00
|
|
|
});
|
|
|
|
|
2021-05-19 21:25:56 +00:00
|
|
|
it('should not store pending sessions in global zone', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const id = new QualifiedAddress(ourAci, new Address(theirAci, 1));
|
2021-05-19 21:25:56 +00:00
|
|
|
const testRecord = getSessionRecord();
|
|
|
|
|
|
|
|
await assert.isRejected(
|
|
|
|
store.withZone(GLOBAL_ZONE, 'test', async () => {
|
|
|
|
await store.storeSession(id, testRecord);
|
|
|
|
throw new Error('Failure');
|
|
|
|
}),
|
|
|
|
'Failure'
|
|
|
|
);
|
|
|
|
|
|
|
|
assert.equal(await store.loadSession(id), testRecord);
|
|
|
|
});
|
|
|
|
|
2022-01-08 02:12:13 +00:00
|
|
|
it('should not store pending sender keys in global zone', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const id = new QualifiedAddress(ourAci, new Address(theirAci, 1));
|
2022-01-08 02:12:13 +00:00
|
|
|
const testRecord = getSenderKeyRecord();
|
|
|
|
|
|
|
|
await assert.isRejected(
|
|
|
|
store.withZone(GLOBAL_ZONE, 'test', async () => {
|
|
|
|
await store.saveSenderKey(id, distributionId, testRecord);
|
|
|
|
throw new Error('Failure');
|
|
|
|
}),
|
|
|
|
'Failure'
|
|
|
|
);
|
|
|
|
|
|
|
|
assert.equal(await store.getSenderKey(id, distributionId), testRecord);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('commits sender keys, sessions and unprocessed on success', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const id = new QualifiedAddress(ourAci, new Address(theirAci, 1));
|
2022-01-08 02:12:13 +00:00
|
|
|
const testSession = getSessionRecord();
|
|
|
|
const testSenderKey = getSenderKeyRecord();
|
2021-05-17 18:03:42 +00:00
|
|
|
|
2021-05-19 21:25:56 +00:00
|
|
|
await store.withZone(zone, 'test', async () => {
|
2022-01-08 02:12:13 +00:00
|
|
|
await store.storeSession(id, testSession, { zone });
|
|
|
|
await store.saveSenderKey(id, distributionId, testSenderKey, { zone });
|
2021-05-17 18:03:42 +00:00
|
|
|
|
2021-05-19 21:25:56 +00:00
|
|
|
await store.addUnprocessed(
|
|
|
|
{
|
|
|
|
id: '2-two',
|
|
|
|
version: 2,
|
2022-07-05 22:20:30 +00:00
|
|
|
|
2021-05-19 21:25:56 +00:00
|
|
|
attempts: 0,
|
2022-07-05 22:20:30 +00:00
|
|
|
envelope: 'second',
|
|
|
|
receivedAtCounter: 0,
|
|
|
|
timestamp: Date.now() + 2,
|
|
|
|
urgent: true,
|
2021-05-19 21:25:56 +00:00
|
|
|
},
|
|
|
|
{ zone }
|
|
|
|
);
|
2022-01-08 02:12:13 +00:00
|
|
|
|
|
|
|
assert.equal(await store.loadSession(id, { zone }), testSession);
|
|
|
|
assert.equal(
|
|
|
|
await store.getSenderKey(id, distributionId, { zone }),
|
|
|
|
testSenderKey
|
|
|
|
);
|
2021-05-17 18:03:42 +00:00
|
|
|
});
|
|
|
|
|
2022-01-08 02:12:13 +00:00
|
|
|
assert.equal(await store.loadSession(id), testSession);
|
|
|
|
assert.equal(await store.getSenderKey(id, distributionId), testSenderKey);
|
2021-05-17 18:03:42 +00:00
|
|
|
|
2022-04-28 22:28:30 +00:00
|
|
|
const allUnprocessed =
|
2023-02-02 19:39:07 +00:00
|
|
|
await store.getUnprocessedByIdsAndIncrementAttempts(
|
|
|
|
await store.getAllUnprocessedIds()
|
|
|
|
);
|
|
|
|
|
2021-05-17 18:03:42 +00:00
|
|
|
assert.deepEqual(
|
|
|
|
allUnprocessed.map(({ envelope }) => envelope),
|
|
|
|
['second']
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2022-01-08 02:12:13 +00:00
|
|
|
it('reverts sender keys, sessions and unprocessed on error', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const id = new QualifiedAddress(ourAci, new Address(theirAci, 1));
|
2022-01-08 02:12:13 +00:00
|
|
|
const testSession = getSessionRecord();
|
|
|
|
const failedSession = getSessionRecord();
|
|
|
|
const testSenderKey = getSenderKeyRecord();
|
|
|
|
const failedSenderKey = getSenderKeyRecord();
|
2021-05-17 18:03:42 +00:00
|
|
|
|
2022-01-08 02:12:13 +00:00
|
|
|
await store.storeSession(id, testSession);
|
|
|
|
assert.equal(await store.loadSession(id), testSession);
|
|
|
|
|
|
|
|
await store.saveSenderKey(id, distributionId, testSenderKey);
|
|
|
|
assert.equal(await store.getSenderKey(id, distributionId), testSenderKey);
|
2021-05-17 18:03:42 +00:00
|
|
|
|
|
|
|
await assert.isRejected(
|
2021-05-19 21:25:56 +00:00
|
|
|
store.withZone(zone, 'test', async () => {
|
2022-01-08 02:12:13 +00:00
|
|
|
await store.storeSession(id, failedSession, { zone });
|
|
|
|
assert.equal(await store.loadSession(id, { zone }), failedSession);
|
|
|
|
|
|
|
|
await store.saveSenderKey(id, distributionId, failedSenderKey, {
|
|
|
|
zone,
|
|
|
|
});
|
|
|
|
assert.equal(
|
|
|
|
await store.getSenderKey(id, distributionId, { zone }),
|
|
|
|
failedSenderKey
|
|
|
|
);
|
2021-05-19 21:25:56 +00:00
|
|
|
|
|
|
|
await store.addUnprocessed(
|
|
|
|
{
|
|
|
|
id: '2-two',
|
|
|
|
version: 2,
|
2022-07-05 22:20:30 +00:00
|
|
|
|
2021-05-19 21:25:56 +00:00
|
|
|
attempts: 0,
|
2022-07-05 22:20:30 +00:00
|
|
|
envelope: 'second',
|
|
|
|
receivedAtCounter: 0,
|
|
|
|
timestamp: 2,
|
|
|
|
urgent: true,
|
2021-05-19 21:25:56 +00:00
|
|
|
},
|
|
|
|
{ zone }
|
|
|
|
);
|
2021-05-17 18:03:42 +00:00
|
|
|
|
|
|
|
throw new Error('Failure');
|
|
|
|
}),
|
|
|
|
'Failure'
|
|
|
|
);
|
|
|
|
|
2022-01-08 02:12:13 +00:00
|
|
|
assert.equal(await store.loadSession(id), testSession);
|
|
|
|
assert.equal(await store.getSenderKey(id, distributionId), testSenderKey);
|
2023-02-02 19:39:07 +00:00
|
|
|
assert.deepEqual(
|
|
|
|
await store.getUnprocessedByIdsAndIncrementAttempts(
|
|
|
|
await store.getAllUnprocessedIds()
|
|
|
|
),
|
|
|
|
[]
|
|
|
|
);
|
2021-05-17 18:03:42 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
it('can be re-entered', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const id = new QualifiedAddress(ourAci, new Address(theirAci, 1));
|
2021-05-17 18:03:42 +00:00
|
|
|
const testRecord = getSessionRecord();
|
|
|
|
|
2021-05-19 21:25:56 +00:00
|
|
|
await store.withZone(zone, 'test', async () => {
|
|
|
|
await store.withZone(zone, 'nested', async () => {
|
|
|
|
await store.storeSession(id, testRecord, { zone });
|
2021-05-17 18:03:42 +00:00
|
|
|
|
2021-05-19 21:25:56 +00:00
|
|
|
assert.equal(await store.loadSession(id, { zone }), testRecord);
|
|
|
|
});
|
2021-05-17 18:03:42 +00:00
|
|
|
|
2021-05-19 21:25:56 +00:00
|
|
|
assert.equal(await store.loadSession(id, { zone }), testRecord);
|
|
|
|
});
|
2021-05-17 18:03:42 +00:00
|
|
|
|
|
|
|
assert.equal(await store.loadSession(id), testRecord);
|
|
|
|
});
|
2021-05-24 22:59:36 +00:00
|
|
|
|
|
|
|
it('can be re-entered after waiting', async () => {
|
|
|
|
const a = new Zone('a');
|
|
|
|
const b = new Zone('b');
|
|
|
|
|
|
|
|
const order: Array<number> = [];
|
|
|
|
const promises: Array<Promise<unknown>> = [];
|
|
|
|
|
|
|
|
// What happens below is briefly following:
|
|
|
|
// 1. We enter zone "a"
|
|
|
|
// 2. We wait for zone "a" to be left to enter zone "b"
|
|
|
|
// 3. Skip few ticks to trigger leave of zone "a" and resolve the waiting
|
|
|
|
// queue promise for zone "b"
|
|
|
|
// 4. Enter zone "a" while resolution was the promise above is queued in
|
|
|
|
// microtasks queue.
|
|
|
|
|
|
|
|
promises.push(store.withZone(a, 'a', async () => order.push(1)));
|
|
|
|
promises.push(store.withZone(b, 'b', async () => order.push(2)));
|
|
|
|
await Promise.resolve();
|
|
|
|
await Promise.resolve();
|
|
|
|
promises.push(store.withZone(a, 'a again', async () => order.push(3)));
|
|
|
|
|
|
|
|
await Promise.all(promises);
|
|
|
|
|
|
|
|
assert.deepEqual(order, [1, 2, 3]);
|
|
|
|
});
|
2021-05-28 23:09:17 +00:00
|
|
|
|
|
|
|
it('should not deadlock in archiveSiblingSessions', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const id = new QualifiedAddress(ourAci, new Address(theirAci, 1));
|
|
|
|
const sibling = new QualifiedAddress(ourAci, new Address(theirAci, 2));
|
2021-05-28 23:09:17 +00:00
|
|
|
|
|
|
|
await store.storeSession(id, getSessionRecord(true));
|
|
|
|
await store.storeSession(sibling, getSessionRecord(true));
|
|
|
|
|
2021-09-10 02:38:11 +00:00
|
|
|
await store.archiveSiblingSessions(id.address, { zone });
|
2021-05-28 23:09:17 +00:00
|
|
|
});
|
2021-08-24 21:07:40 +00:00
|
|
|
|
2023-12-07 21:52:27 +00:00
|
|
|
it('should not throw in archiveSession on PNI', async () => {
|
|
|
|
const id = new QualifiedAddress(ourPni, new Address(theirAci, 1));
|
|
|
|
|
|
|
|
await store.storeSession(id, getSessionRecord(true));
|
|
|
|
|
|
|
|
await store.archiveSession(id);
|
|
|
|
|
|
|
|
const { devices, emptyServiceIds } = await store.getOpenDevices(ourPni, [
|
|
|
|
theirAci,
|
|
|
|
]);
|
|
|
|
|
|
|
|
assert.deepEqual(devices, []);
|
|
|
|
assert.deepEqual(emptyServiceIds, [theirAci]);
|
|
|
|
});
|
|
|
|
|
2021-08-24 21:07:40 +00:00
|
|
|
it('can be concurrently re-entered after waiting', async () => {
|
|
|
|
const a = new Zone('a');
|
|
|
|
const b = new Zone('b');
|
|
|
|
|
|
|
|
const order: Array<number> = [];
|
|
|
|
const promises: Array<Promise<unknown>> = [];
|
|
|
|
|
|
|
|
// 1. Enter zone "a"
|
|
|
|
// 2. Wait for zone "a" to be left to enter zone "b" twice
|
|
|
|
// 3. Verify that both zone "b" tasks ran in parallel
|
|
|
|
|
|
|
|
promises.push(store.withZone(a, 'a', async () => order.push(1)));
|
|
|
|
promises.push(
|
|
|
|
store.withZone(b, 'b', async () => {
|
|
|
|
order.push(2);
|
|
|
|
await Promise.resolve();
|
|
|
|
order.push(22);
|
|
|
|
})
|
|
|
|
);
|
|
|
|
promises.push(
|
|
|
|
store.withZone(b, 'b', async () => {
|
|
|
|
order.push(3);
|
|
|
|
await Promise.resolve();
|
|
|
|
order.push(33);
|
|
|
|
})
|
|
|
|
);
|
|
|
|
await Promise.resolve();
|
|
|
|
await Promise.resolve();
|
|
|
|
|
|
|
|
await Promise.all(promises);
|
|
|
|
|
|
|
|
assert.deepEqual(order, [1, 2, 3, 22, 33]);
|
|
|
|
});
|
2021-05-17 18:03:42 +00:00
|
|
|
});
|
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
describe('Not yet processed messages', () => {
|
2021-12-14 01:25:44 +00:00
|
|
|
const NOW = Date.now();
|
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
beforeEach(async () => {
|
2018-10-18 01:01:21 +00:00
|
|
|
await store.removeAllUnprocessed();
|
2023-02-02 19:39:07 +00:00
|
|
|
const items = await store.getUnprocessedByIdsAndIncrementAttempts(
|
|
|
|
await store.getAllUnprocessedIds()
|
|
|
|
);
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.strictEqual(items.length, 0);
|
2017-07-17 22:46:00 +00:00
|
|
|
});
|
|
|
|
|
2019-02-05 01:23:50 +00:00
|
|
|
it('adds three and gets them back', async () => {
|
2018-10-18 01:01:21 +00:00
|
|
|
await Promise.all([
|
2021-12-14 01:25:44 +00:00
|
|
|
store.addUnprocessed({
|
|
|
|
id: '0-dropped',
|
|
|
|
version: 2,
|
2022-07-05 22:20:30 +00:00
|
|
|
|
2021-12-14 01:25:44 +00:00
|
|
|
attempts: 0,
|
2022-07-05 22:20:30 +00:00
|
|
|
envelope: 'old envelope',
|
|
|
|
receivedAtCounter: -1,
|
|
|
|
timestamp: NOW - 2 * durations.MONTH,
|
|
|
|
urgent: true,
|
2021-12-14 01:25:44 +00:00
|
|
|
}),
|
2021-04-16 23:13:13 +00:00
|
|
|
store.addUnprocessed({
|
|
|
|
id: '2-two',
|
|
|
|
version: 2,
|
2022-07-05 22:20:30 +00:00
|
|
|
|
2021-04-16 23:13:13 +00:00
|
|
|
attempts: 0,
|
2022-07-05 22:20:30 +00:00
|
|
|
envelope: 'second',
|
|
|
|
receivedAtCounter: 1,
|
|
|
|
timestamp: NOW + 2,
|
|
|
|
urgent: true,
|
2021-04-16 23:13:13 +00:00
|
|
|
}),
|
|
|
|
store.addUnprocessed({
|
|
|
|
id: '3-three',
|
|
|
|
version: 2,
|
2022-07-05 22:20:30 +00:00
|
|
|
|
2021-04-16 23:13:13 +00:00
|
|
|
attempts: 0,
|
2022-07-05 22:20:30 +00:00
|
|
|
envelope: 'third',
|
|
|
|
receivedAtCounter: 2,
|
|
|
|
timestamp: NOW + 3,
|
|
|
|
urgent: true,
|
2021-04-16 23:13:13 +00:00
|
|
|
}),
|
|
|
|
store.addUnprocessed({
|
|
|
|
id: '1-one',
|
|
|
|
version: 2,
|
2022-07-05 22:20:30 +00:00
|
|
|
|
2021-04-16 23:13:13 +00:00
|
|
|
attempts: 0,
|
2022-07-05 22:20:30 +00:00
|
|
|
envelope: 'first',
|
|
|
|
receivedAtCounter: 0,
|
|
|
|
timestamp: NOW + 1,
|
|
|
|
urgent: true,
|
2021-04-16 23:13:13 +00:00
|
|
|
}),
|
2018-10-18 01:01:21 +00:00
|
|
|
]);
|
2017-07-17 22:46:00 +00:00
|
|
|
|
2023-02-02 19:39:07 +00:00
|
|
|
const items = await store.getUnprocessedByIdsAndIncrementAttempts(
|
|
|
|
await store.getAllUnprocessedIds()
|
|
|
|
);
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.strictEqual(items.length, 3);
|
|
|
|
|
2022-06-10 16:09:21 +00:00
|
|
|
// they are in the proper order because the collection comparator is
|
|
|
|
// 'receivedAtCounter'
|
2019-02-05 01:23:50 +00:00
|
|
|
assert.strictEqual(items[0].envelope, 'first');
|
|
|
|
assert.strictEqual(items[1].envelope, 'second');
|
|
|
|
assert.strictEqual(items[2].envelope, 'third');
|
2017-07-17 22:46:00 +00:00
|
|
|
});
|
|
|
|
|
2021-05-27 15:45:45 +00:00
|
|
|
it('can updates items', async () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
const id = '1-one';
|
|
|
|
await store.addUnprocessed({
|
|
|
|
id,
|
|
|
|
version: 2,
|
2022-07-05 22:20:30 +00:00
|
|
|
|
2021-04-16 23:13:13 +00:00
|
|
|
attempts: 0,
|
2022-07-05 22:20:30 +00:00
|
|
|
envelope: 'first',
|
|
|
|
receivedAtCounter: 0,
|
|
|
|
timestamp: NOW + 1,
|
|
|
|
urgent: false,
|
2021-04-16 23:13:13 +00:00
|
|
|
});
|
2019-02-05 01:23:50 +00:00
|
|
|
await store.updateUnprocessedWithData(id, { decrypted: 'updated' });
|
2018-10-18 01:01:21 +00:00
|
|
|
|
2023-02-02 19:39:07 +00:00
|
|
|
const items = await store.getUnprocessedByIdsAndIncrementAttempts(
|
|
|
|
await store.getAllUnprocessedIds()
|
|
|
|
);
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.strictEqual(items.length, 1);
|
2019-02-05 01:23:50 +00:00
|
|
|
assert.strictEqual(items[0].decrypted, 'updated');
|
2021-12-14 01:25:44 +00:00
|
|
|
assert.strictEqual(items[0].timestamp, NOW + 1);
|
2022-04-28 22:28:30 +00:00
|
|
|
assert.strictEqual(items[0].attempts, 1);
|
2022-07-05 22:20:30 +00:00
|
|
|
assert.strictEqual(items[0].urgent, false);
|
2017-07-17 22:46:00 +00:00
|
|
|
});
|
|
|
|
|
2018-11-02 18:02:53 +00:00
|
|
|
it('removeUnprocessed successfully deletes item', async () => {
|
2021-04-16 23:13:13 +00:00
|
|
|
const id = '1-one';
|
|
|
|
await store.addUnprocessed({
|
|
|
|
id,
|
|
|
|
version: 2,
|
2022-07-05 22:20:30 +00:00
|
|
|
|
2021-04-16 23:13:13 +00:00
|
|
|
attempts: 0,
|
2022-07-05 22:20:30 +00:00
|
|
|
envelope: 'first',
|
|
|
|
receivedAtCounter: 0,
|
|
|
|
timestamp: NOW + 1,
|
|
|
|
urgent: true,
|
2021-04-16 23:13:13 +00:00
|
|
|
});
|
2018-10-18 01:01:21 +00:00
|
|
|
await store.removeUnprocessed(id);
|
|
|
|
|
2023-02-02 19:39:07 +00:00
|
|
|
const items = await store.getUnprocessedByIdsAndIncrementAttempts(
|
|
|
|
await store.getAllUnprocessedIds()
|
|
|
|
);
|
2022-04-28 22:28:30 +00:00
|
|
|
assert.strictEqual(items.length, 0);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('getAllUnprocessedAndIncrementAttempts deletes items', async () => {
|
|
|
|
await store.addUnprocessed({
|
|
|
|
id: '1-one',
|
|
|
|
version: 2,
|
2022-07-05 22:20:30 +00:00
|
|
|
|
2023-06-14 20:51:49 +00:00
|
|
|
attempts: 10,
|
2022-07-05 22:20:30 +00:00
|
|
|
envelope: 'first',
|
|
|
|
receivedAtCounter: 0,
|
|
|
|
timestamp: NOW + 1,
|
|
|
|
urgent: true,
|
2022-04-28 22:28:30 +00:00
|
|
|
});
|
|
|
|
|
2023-02-02 19:39:07 +00:00
|
|
|
const items = await store.getUnprocessedByIdsAndIncrementAttempts(
|
|
|
|
await store.getAllUnprocessedIds()
|
|
|
|
);
|
2018-10-18 01:01:21 +00:00
|
|
|
assert.strictEqual(items.length, 0);
|
2017-07-17 22:46:00 +00:00
|
|
|
});
|
|
|
|
});
|
2022-07-28 16:35:29 +00:00
|
|
|
describe('removeOurOldPni/updateOurPniKeyMaterial', () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const oldPni = generatePni();
|
|
|
|
|
2022-07-28 16:35:29 +00:00
|
|
|
beforeEach(async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
await store.storePreKeys(oldPni, [{ keyId: 2, keyPair: testKey }]);
|
|
|
|
await store.storeSignedPreKey(oldPni, 3, testKey);
|
2022-07-28 16:35:29 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
it('removes old data and sets new', async () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const newPni = generatePni();
|
2022-07-28 16:35:29 +00:00
|
|
|
|
|
|
|
const newIdentity = IdentityKeyPair.generate();
|
|
|
|
|
|
|
|
const data = generateSignedPreKey(
|
|
|
|
{
|
|
|
|
pubKey: newIdentity.publicKey.serialize(),
|
|
|
|
privKey: newIdentity.privateKey.serialize(),
|
|
|
|
},
|
|
|
|
8201
|
|
|
|
);
|
|
|
|
const createdAt = Date.now() - 1241;
|
|
|
|
const signedPreKey = SignedPreKeyRecord.new(
|
|
|
|
data.keyId,
|
|
|
|
createdAt,
|
|
|
|
PublicKey.deserialize(Buffer.from(data.keyPair.pubKey)),
|
|
|
|
PrivateKey.deserialize(Buffer.from(data.keyPair.privKey)),
|
|
|
|
Buffer.from(data.signature)
|
|
|
|
);
|
|
|
|
|
|
|
|
await store.removeOurOldPni(oldPni);
|
|
|
|
await store.updateOurPniKeyMaterial(newPni, {
|
|
|
|
identityKeyPair: newIdentity.serialize(),
|
|
|
|
signedPreKey: signedPreKey.serialize(),
|
|
|
|
registrationId: 5231,
|
|
|
|
});
|
|
|
|
|
|
|
|
// Old data has to be removed
|
2022-08-15 21:53:33 +00:00
|
|
|
assert.isUndefined(store.getIdentityKeyPair(oldPni));
|
2022-07-28 16:35:29 +00:00
|
|
|
assert.isUndefined(await store.getLocalRegistrationId(oldPni));
|
|
|
|
assert.isUndefined(await store.loadPreKey(oldPni, 2));
|
|
|
|
assert.isUndefined(await store.loadSignedPreKey(oldPni, 3));
|
|
|
|
|
|
|
|
// New data has to be added
|
2022-08-15 21:53:33 +00:00
|
|
|
const storedIdentity = store.getIdentityKeyPair(newPni);
|
2022-07-28 16:35:29 +00:00
|
|
|
if (!storedIdentity) {
|
|
|
|
throw new Error('New identity not found');
|
|
|
|
}
|
|
|
|
assert.isTrue(
|
|
|
|
Bytes.areEqual(
|
|
|
|
storedIdentity.privKey,
|
|
|
|
newIdentity.privateKey.serialize()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
assert.isTrue(
|
|
|
|
Bytes.areEqual(storedIdentity.pubKey, newIdentity.publicKey.serialize())
|
|
|
|
);
|
|
|
|
|
|
|
|
const storedSignedPreKey = await store.loadSignedPreKey(newPni, 8201);
|
|
|
|
if (!storedSignedPreKey) {
|
|
|
|
throw new Error('New signed pre key not found');
|
|
|
|
}
|
|
|
|
assert.isTrue(
|
|
|
|
Bytes.areEqual(
|
|
|
|
storedSignedPreKey.publicKey().serialize(),
|
|
|
|
data.keyPair.pubKey
|
|
|
|
)
|
|
|
|
);
|
|
|
|
assert.isTrue(
|
|
|
|
Bytes.areEqual(
|
|
|
|
storedSignedPreKey.privateKey().serialize(),
|
|
|
|
data.keyPair.privKey
|
|
|
|
)
|
|
|
|
);
|
|
|
|
assert.strictEqual(storedSignedPreKey.timestamp(), createdAt);
|
|
|
|
// Note: signature is ignored.
|
|
|
|
});
|
|
|
|
});
|
2015-04-01 20:08:09 +00:00
|
|
|
});
|