Add cache support to Signal Protocol Store
This commit is contained in:
parent
1d2c3ae23c
commit
9c540ab977
7 changed files with 171 additions and 52 deletions
17
app/sql.js
17
app/sql.js
|
@ -29,12 +29,14 @@ module.exports = {
|
|||
bulkAddIdentityKeys,
|
||||
removeIdentityKeyById,
|
||||
removeAllIdentityKeys,
|
||||
getAllIdentityKeys,
|
||||
|
||||
createOrUpdatePreKey,
|
||||
getPreKeyById,
|
||||
bulkAddPreKeys,
|
||||
removePreKeyById,
|
||||
removeAllPreKeys,
|
||||
getAllPreKeys,
|
||||
|
||||
createOrUpdateSignedPreKey,
|
||||
getSignedPreKeyById,
|
||||
|
@ -57,6 +59,7 @@ module.exports = {
|
|||
removeSessionById,
|
||||
removeSessionsByNumber,
|
||||
removeAllSessions,
|
||||
getAllSessions,
|
||||
|
||||
getConversationCount,
|
||||
saveConversation,
|
||||
|
@ -701,6 +704,9 @@ async function removeIdentityKeyById(id) {
|
|||
async function removeAllIdentityKeys() {
|
||||
return removeAllFromTable(IDENTITY_KEYS_TABLE);
|
||||
}
|
||||
async function getAllIdentityKeys() {
|
||||
return getAllFromTable(IDENTITY_KEYS_TABLE);
|
||||
}
|
||||
|
||||
const PRE_KEYS_TABLE = 'preKeys';
|
||||
async function createOrUpdatePreKey(data) {
|
||||
|
@ -718,6 +724,9 @@ async function removePreKeyById(id) {
|
|||
async function removeAllPreKeys() {
|
||||
return removeAllFromTable(PRE_KEYS_TABLE);
|
||||
}
|
||||
async function getAllPreKeys() {
|
||||
return getAllFromTable(PRE_KEYS_TABLE);
|
||||
}
|
||||
|
||||
const SIGNED_PRE_KEYS_TABLE = 'signedPreKeys';
|
||||
async function createOrUpdateSignedPreKey(data) {
|
||||
|
@ -815,6 +824,9 @@ async function removeSessionsByNumber(number) {
|
|||
async function removeAllSessions() {
|
||||
return removeAllFromTable(SESSIONS_TABLE);
|
||||
}
|
||||
async function getAllSessions() {
|
||||
return getAllFromTable(SESSIONS_TABLE);
|
||||
}
|
||||
|
||||
async function createOrUpdate(table, data) {
|
||||
const { id } = data;
|
||||
|
@ -884,6 +896,11 @@ async function removeAllFromTable(table) {
|
|||
await db.run(`DELETE FROM ${table};`);
|
||||
}
|
||||
|
||||
async function getAllFromTable(table) {
|
||||
const rows = await db.all(`SELECT json FROM ${table};`);
|
||||
return rows.map(row => jsonToObject(row.json));
|
||||
}
|
||||
|
||||
// Conversations
|
||||
|
||||
async function getConversationCount() {
|
||||
|
|
|
@ -414,7 +414,10 @@
|
|||
window.Events.setThemeSetting(newThemeSetting);
|
||||
|
||||
try {
|
||||
await ConversationController.load();
|
||||
await Promise.all([
|
||||
ConversationController.load(),
|
||||
textsecure.storage.protocol.hydrateCaches(),
|
||||
]);
|
||||
} catch (error) {
|
||||
window.log.error(
|
||||
'background.js: ConversationController failed to load:',
|
||||
|
|
|
@ -60,12 +60,14 @@ module.exports = {
|
|||
bulkAddIdentityKeys,
|
||||
removeIdentityKeyById,
|
||||
removeAllIdentityKeys,
|
||||
getAllIdentityKeys,
|
||||
|
||||
createOrUpdatePreKey,
|
||||
getPreKeyById,
|
||||
bulkAddPreKeys,
|
||||
removePreKeyById,
|
||||
removeAllPreKeys,
|
||||
getAllPreKeys,
|
||||
|
||||
createOrUpdateSignedPreKey,
|
||||
getSignedPreKeyById,
|
||||
|
@ -88,6 +90,7 @@ module.exports = {
|
|||
removeSessionById,
|
||||
removeSessionsByNumber,
|
||||
removeAllSessions,
|
||||
getAllSessions,
|
||||
|
||||
getConversationCount,
|
||||
saveConversation,
|
||||
|
@ -440,6 +443,10 @@ async function removeIdentityKeyById(id) {
|
|||
async function removeAllIdentityKeys() {
|
||||
await channels.removeAllIdentityKeys();
|
||||
}
|
||||
async function getAllIdentityKeys() {
|
||||
const keys = await channels.getAllIdentityKeys();
|
||||
return keys.map(key => keysToArrayBuffer(IDENTITY_KEY_KEYS, key));
|
||||
}
|
||||
|
||||
// Pre Keys
|
||||
|
||||
|
@ -461,6 +468,10 @@ async function removePreKeyById(id) {
|
|||
async function removeAllPreKeys() {
|
||||
await channels.removeAllPreKeys();
|
||||
}
|
||||
async function getAllPreKeys() {
|
||||
const keys = await channels.getAllPreKeys();
|
||||
return keys.map(key => keysToArrayBuffer(PRE_KEY_KEYS, key));
|
||||
}
|
||||
|
||||
// Signed Pre Keys
|
||||
|
||||
|
@ -475,7 +486,7 @@ async function getSignedPreKeyById(id) {
|
|||
}
|
||||
async function getAllSignedPreKeys() {
|
||||
const keys = await channels.getAllSignedPreKeys();
|
||||
return keys;
|
||||
return keys.map(key => keysToArrayBuffer(PRE_KEY_KEYS, key));
|
||||
}
|
||||
async function bulkAddSignedPreKeys(array) {
|
||||
const updated = map(array, data => keysFromArrayBuffer(PRE_KEY_KEYS, data));
|
||||
|
@ -567,6 +578,10 @@ async function removeSessionsByNumber(number) {
|
|||
async function removeAllSessions(id) {
|
||||
await channels.removeAllSessions(id);
|
||||
}
|
||||
async function getAllSessions(id) {
|
||||
const sessions = await channels.getAllSessions(id);
|
||||
return sessions;
|
||||
}
|
||||
|
||||
// Conversation
|
||||
|
||||
|
|
|
@ -150,8 +150,51 @@
|
|||
|
||||
function SignalProtocolStore() {}
|
||||
|
||||
async function _hydrateCache(object, field, items, idField) {
|
||||
const cache = Object.create(null);
|
||||
for (let i = 0, max = items.length; i < max; i += 1) {
|
||||
const item = items[i];
|
||||
const id = item[idField];
|
||||
|
||||
cache[id] = item;
|
||||
}
|
||||
|
||||
window.log.info(`SignalProtocolStore: Finished caching ${field} data`);
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
object[field] = cache;
|
||||
}
|
||||
|
||||
SignalProtocolStore.prototype = {
|
||||
constructor: SignalProtocolStore,
|
||||
async hydrateCaches() {
|
||||
await Promise.all([
|
||||
_hydrateCache(
|
||||
this,
|
||||
'identityKeys',
|
||||
await window.Signal.Data.getAllIdentityKeys(),
|
||||
'id'
|
||||
),
|
||||
_hydrateCache(
|
||||
this,
|
||||
'sessions',
|
||||
await window.Signal.Data.getAllSessions(),
|
||||
'id'
|
||||
),
|
||||
_hydrateCache(
|
||||
this,
|
||||
'preKeys',
|
||||
await window.Signal.Data.getAllPreKeys(),
|
||||
'id'
|
||||
),
|
||||
_hydrateCache(
|
||||
this,
|
||||
'signedPreKeys',
|
||||
await window.Signal.Data.getAllSignedPreKeys(),
|
||||
'id'
|
||||
),
|
||||
]);
|
||||
},
|
||||
|
||||
async getIdentityKeyPair() {
|
||||
const item = await window.Signal.Data.getItemById('identityKey');
|
||||
if (item) {
|
||||
|
@ -169,9 +212,10 @@
|
|||
return undefined;
|
||||
},
|
||||
|
||||
/* Returns a prekeypair object or undefined */
|
||||
// PreKeys
|
||||
|
||||
async loadPreKey(keyId) {
|
||||
const key = await window.Signal.Data.getPreKeyById(keyId);
|
||||
const key = this.preKeys[keyId];
|
||||
if (key) {
|
||||
window.log.info('Successfully fetched prekey:', keyId);
|
||||
return {
|
||||
|
@ -190,6 +234,7 @@
|
|||
privateKey: keyPair.privKey,
|
||||
};
|
||||
|
||||
this.preKeys[keyId] = data;
|
||||
await window.Signal.Data.createOrUpdatePreKey(data);
|
||||
},
|
||||
async removePreKey(keyId) {
|
||||
|
@ -202,15 +247,18 @@
|
|||
);
|
||||
}
|
||||
|
||||
delete this.preKeys[keyId];
|
||||
await window.Signal.Data.removePreKeyById(keyId);
|
||||
},
|
||||
async clearPreKeyStore() {
|
||||
this.preKeys = Object.create(null);
|
||||
await window.Signal.Data.removeAllPreKeys();
|
||||
},
|
||||
|
||||
/* Returns a signed keypair object or undefined */
|
||||
// Signed PreKeys
|
||||
|
||||
async loadSignedPreKey(keyId) {
|
||||
const key = await window.Signal.Data.getSignedPreKeyById(keyId);
|
||||
const key = this.signedPreKeys[keyId];
|
||||
if (key) {
|
||||
window.log.info('Successfully fetched signed prekey:', key.id);
|
||||
return {
|
||||
|
@ -230,7 +278,7 @@
|
|||
throw new Error('loadSignedPreKeys takes no arguments');
|
||||
}
|
||||
|
||||
const keys = await window.Signal.Data.getAllSignedPreKeys();
|
||||
const keys = Object.values(this.signedPreKeys);
|
||||
return keys.map(prekey => ({
|
||||
pubKey: prekey.publicKey,
|
||||
privKey: prekey.privateKey,
|
||||
|
@ -240,28 +288,34 @@
|
|||
}));
|
||||
},
|
||||
async storeSignedPreKey(keyId, keyPair, confirmed) {
|
||||
const key = {
|
||||
const data = {
|
||||
id: keyId,
|
||||
publicKey: keyPair.pubKey,
|
||||
privateKey: keyPair.privKey,
|
||||
created_at: Date.now(),
|
||||
confirmed: Boolean(confirmed),
|
||||
};
|
||||
await window.Signal.Data.createOrUpdateSignedPreKey(key);
|
||||
|
||||
this.signedPreKeys[keyId] = data;
|
||||
await window.Signal.Data.createOrUpdateSignedPreKey(data);
|
||||
},
|
||||
async removeSignedPreKey(keyId) {
|
||||
delete this.signedPreKeys[keyId];
|
||||
await window.Signal.Data.removeSignedPreKeyById(keyId);
|
||||
},
|
||||
async clearSignedPreKeysStore() {
|
||||
this.signedPreKeys = Object.create(null);
|
||||
await window.Signal.Data.removeAllSignedPreKeys();
|
||||
},
|
||||
|
||||
// Sessions
|
||||
|
||||
async loadSession(encodedNumber) {
|
||||
if (encodedNumber === null || encodedNumber === undefined) {
|
||||
throw new Error('Tried to get session for undefined/null number');
|
||||
}
|
||||
|
||||
const session = await window.Signal.Data.getSessionById(encodedNumber);
|
||||
const session = this.sessions[encodedNumber];
|
||||
if (session) {
|
||||
return session.record;
|
||||
}
|
||||
|
@ -283,6 +337,7 @@
|
|||
record,
|
||||
};
|
||||
|
||||
this.sessions[encodedNumber] = data;
|
||||
await window.Signal.Data.createOrUpdateSession(data);
|
||||
},
|
||||
async getDeviceIds(number) {
|
||||
|
@ -290,11 +345,13 @@
|
|||
throw new Error('Tried to get device ids for undefined/null number');
|
||||
}
|
||||
|
||||
const sessions = await window.Signal.Data.getSessionsByNumber(number);
|
||||
const allSessions = Object.values(this.sessions);
|
||||
const sessions = allSessions.filter(session => session.number === number);
|
||||
return _.pluck(sessions, 'deviceId');
|
||||
},
|
||||
async removeSession(encodedNumber) {
|
||||
window.log.info('deleting session for ', encodedNumber);
|
||||
delete this.sessions[encodedNumber];
|
||||
await window.Signal.Data.removeSessionById(encodedNumber);
|
||||
},
|
||||
async removeAllSessions(number) {
|
||||
|
@ -302,6 +359,13 @@
|
|||
throw new Error('Tried to remove sessions for undefined/null number');
|
||||
}
|
||||
|
||||
const allSessions = Object.values(this.sessions);
|
||||
for (let i = 0, max = allSessions.length; i < max; i += 1) {
|
||||
const session = allSessions[i];
|
||||
if (session.number === number) {
|
||||
delete this.sessions[session.id];
|
||||
}
|
||||
}
|
||||
await window.Signal.Data.removeSessionsByNumber(number);
|
||||
},
|
||||
async archiveSiblingSessions(identifier) {
|
||||
|
@ -341,8 +405,12 @@
|
|||
);
|
||||
},
|
||||
async clearSessionStore() {
|
||||
this.sessions = Object.create(null);
|
||||
window.Signal.Data.removeAllSessions();
|
||||
},
|
||||
|
||||
// Identity Keys
|
||||
|
||||
async isTrustedIdentity(identifier, publicKey, direction) {
|
||||
if (identifier === null || identifier === undefined) {
|
||||
throw new Error('Tried to get identity key for undefined/null key');
|
||||
|
@ -350,9 +418,7 @@
|
|||
const number = textsecure.utils.unencodeNumber(identifier)[0];
|
||||
const isOurNumber = number === textsecure.storage.user.getNumber();
|
||||
|
||||
const identityRecord = await window.Signal.Data.getIdentityKeyById(
|
||||
number
|
||||
);
|
||||
const identityRecord = this.identityKeys[number];
|
||||
|
||||
if (isOurNumber) {
|
||||
const existing = identityRecord ? identityRecord.publicKey : null;
|
||||
|
@ -402,9 +468,7 @@
|
|||
throw new Error('Tried to get identity key for undefined/null key');
|
||||
}
|
||||
const number = textsecure.utils.unencodeNumber(identifier)[0];
|
||||
const identityRecord = await window.Signal.Data.getIdentityKeyById(
|
||||
number
|
||||
);
|
||||
const identityRecord = this.identityKeys[number];
|
||||
|
||||
if (identityRecord) {
|
||||
return identityRecord.publicKey;
|
||||
|
@ -412,6 +476,11 @@
|
|||
|
||||
return undefined;
|
||||
},
|
||||
async _saveIdentityKey(data) {
|
||||
const { id } = data;
|
||||
this.identityKeys[id] = data;
|
||||
await window.Signal.Data.createOrUpdateIdentityKey(data);
|
||||
},
|
||||
async saveIdentity(identifier, publicKey, nonblockingApproval) {
|
||||
if (identifier === null || identifier === undefined) {
|
||||
throw new Error('Tried to put identity key for undefined/null key');
|
||||
|
@ -426,14 +495,12 @@
|
|||
}
|
||||
|
||||
const number = textsecure.utils.unencodeNumber(identifier)[0];
|
||||
const identityRecord = await window.Signal.Data.getIdentityKeyById(
|
||||
number
|
||||
);
|
||||
const identityRecord = this.identityKeys[number];
|
||||
|
||||
if (!identityRecord || !identityRecord.publicKey) {
|
||||
// Lookup failed, or the current key was removed, so save this one.
|
||||
window.log.info('Saving new identity...');
|
||||
await window.Signal.Data.createOrUpdateIdentityKey({
|
||||
await this._saveIdentityKey({
|
||||
id: number,
|
||||
publicKey,
|
||||
firstUse: true,
|
||||
|
@ -459,7 +526,7 @@
|
|||
verifiedStatus = VerifiedStatus.DEFAULT;
|
||||
}
|
||||
|
||||
await window.Signal.Data.createOrUpdateIdentityKey({
|
||||
await this._saveIdentityKey({
|
||||
id: number,
|
||||
publicKey,
|
||||
firstUse: false,
|
||||
|
@ -483,7 +550,7 @@
|
|||
window.log.info('Setting approval status...');
|
||||
|
||||
identityRecord.nonblockingApproval = nonblockingApproval;
|
||||
await window.Signal.Data.createOrUpdateIdentityKey(identityRecord);
|
||||
await this._saveIdentityKey(identityRecord);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -503,9 +570,7 @@
|
|||
}
|
||||
|
||||
const number = textsecure.utils.unencodeNumber(identifier)[0];
|
||||
const identityRecord = await window.Signal.Data.getIdentityKeyById(
|
||||
number
|
||||
);
|
||||
const identityRecord = this.identityKeys[number];
|
||||
|
||||
const updates = {
|
||||
id: number,
|
||||
|
@ -515,7 +580,7 @@
|
|||
|
||||
const model = new IdentityRecord(updates);
|
||||
if (model.isValid()) {
|
||||
await window.Signal.Data.createOrUpdateIdentityKey(updates);
|
||||
await this._saveIdentityKey(updates);
|
||||
} else {
|
||||
throw model.validationError;
|
||||
}
|
||||
|
@ -529,16 +594,14 @@
|
|||
}
|
||||
|
||||
const number = textsecure.utils.unencodeNumber(identifier)[0];
|
||||
const identityRecord = await window.Signal.Data.getIdentityKeyById(
|
||||
number
|
||||
);
|
||||
const identityRecord = this.identityKeys[number];
|
||||
|
||||
if (!identityRecord) {
|
||||
throw new Error(`No identity record for ${number}`);
|
||||
}
|
||||
|
||||
identityRecord.nonblockingApproval = nonblockingApproval;
|
||||
await window.Signal.Data.createOrUpdateIdentityKey(identityRecord);
|
||||
await this._saveIdentityKey(identityRecord);
|
||||
},
|
||||
async setVerified(number, verifiedStatus, publicKey) {
|
||||
if (number === null || number === undefined) {
|
||||
|
@ -551,9 +614,7 @@
|
|||
throw new Error('Invalid public key');
|
||||
}
|
||||
|
||||
const identityRecord = await window.Signal.Data.getIdentityKeyById(
|
||||
number
|
||||
);
|
||||
const identityRecord = this.identityKeys[number];
|
||||
if (!identityRecord) {
|
||||
throw new Error(`No identity record for ${number}`);
|
||||
}
|
||||
|
@ -566,7 +627,7 @@
|
|||
|
||||
const model = new IdentityRecord(identityRecord);
|
||||
if (model.isValid()) {
|
||||
await window.Signal.Data.createOrUpdateIdentityKey(identityRecord);
|
||||
await this._saveIdentityKey(identityRecord);
|
||||
} else {
|
||||
throw identityRecord.validationError;
|
||||
}
|
||||
|
@ -579,10 +640,7 @@
|
|||
throw new Error('Tried to set verified for undefined/null key');
|
||||
}
|
||||
|
||||
const identityRecord = await window.Signal.Data.getIdentityKeyById(
|
||||
number
|
||||
);
|
||||
|
||||
const identityRecord = this.identityKeys[number];
|
||||
if (!identityRecord) {
|
||||
throw new Error(`No identity record for ${number}`);
|
||||
}
|
||||
|
@ -616,9 +674,7 @@
|
|||
throw new Error('Invalid public key');
|
||||
}
|
||||
|
||||
const identityRecord = await window.Signal.Data.getIdentityKeyById(
|
||||
number
|
||||
);
|
||||
const identityRecord = this.identityKeys[number];
|
||||
const isPresent = Boolean(identityRecord);
|
||||
let isEqual = false;
|
||||
|
||||
|
@ -683,9 +739,7 @@
|
|||
throw new Error('Invalid public key');
|
||||
}
|
||||
|
||||
const identityRecord = await window.Signal.Data.getIdentityKeyById(
|
||||
number
|
||||
);
|
||||
const identityRecord = this.identityKeys[number];
|
||||
|
||||
const isPresent = Boolean(identityRecord);
|
||||
let isEqual = false;
|
||||
|
@ -754,10 +808,7 @@
|
|||
throw new Error('Tried to set verified for undefined/null key');
|
||||
}
|
||||
|
||||
const identityRecord = await window.Signal.Data.getIdentityKeyById(
|
||||
number
|
||||
);
|
||||
|
||||
const identityRecord = this.identityKeys[number];
|
||||
if (!identityRecord) {
|
||||
throw new Error(`No identity record for ${number}`);
|
||||
}
|
||||
|
@ -773,11 +824,13 @@
|
|||
return false;
|
||||
},
|
||||
async removeIdentityKey(number) {
|
||||
delete this.identityKeys[number];
|
||||
await window.Signal.Data.removeIdentityKeyById(number);
|
||||
return textsecure.storage.protocol.removeAllSessions(number);
|
||||
await textsecure.storage.protocol.removeAllSessions(number);
|
||||
},
|
||||
|
||||
// Groups
|
||||
|
||||
async getGroup(groupId) {
|
||||
if (groupId === null || groupId === undefined) {
|
||||
throw new Error('Tried to get group for undefined/null id');
|
||||
|
@ -840,6 +893,7 @@
|
|||
},
|
||||
async removeAllData() {
|
||||
await window.Signal.Data.removeAll();
|
||||
await this.hydrateCaches();
|
||||
|
||||
window.storage.reset();
|
||||
await window.storage.fetch();
|
||||
|
@ -849,6 +903,7 @@
|
|||
},
|
||||
async removeAllConfiguration() {
|
||||
await window.Signal.Data.removeAllConfiguration();
|
||||
await this.hydrateCaches();
|
||||
|
||||
window.storage.reset();
|
||||
await window.storage.fetch();
|
||||
|
|
|
@ -10,8 +10,9 @@ describe('KeyChangeListener', () => {
|
|||
const newKey = libsignal.crypto.getRandomBytes(33);
|
||||
let store;
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
store = new SignalProtocolStore();
|
||||
await store.hydrateCaches();
|
||||
Whisper.KeyChangeListener.init(store);
|
||||
return store.saveIdentity(address.toString(), oldKey);
|
||||
});
|
||||
|
|
|
@ -10,6 +10,7 @@ describe('SignalProtocolStore', () => {
|
|||
|
||||
before(done => {
|
||||
store = textsecure.storage.protocol;
|
||||
store.hydrateCaches();
|
||||
identityKey = {
|
||||
pubKey: libsignal.crypto.getRandomBytes(33),
|
||||
privKey: libsignal.crypto.getRandomBytes(32),
|
||||
|
@ -86,6 +87,7 @@ describe('SignalProtocolStore', () => {
|
|||
verified: store.VerifiedStatus.DEFAULT,
|
||||
});
|
||||
|
||||
await store.hydrateCaches();
|
||||
await store.saveIdentity(identifier, newIdentity);
|
||||
});
|
||||
it('marks the key not firstUse', async () => {
|
||||
|
@ -107,6 +109,7 @@ describe('SignalProtocolStore', () => {
|
|||
nonblockingApproval: false,
|
||||
verified: store.VerifiedStatus.DEFAULT,
|
||||
});
|
||||
await store.hydrateCaches();
|
||||
|
||||
await store.saveIdentity(identifier, newIdentity);
|
||||
});
|
||||
|
@ -125,6 +128,8 @@ describe('SignalProtocolStore', () => {
|
|||
nonblockingApproval: false,
|
||||
verified: store.VerifiedStatus.VERIFIED,
|
||||
});
|
||||
|
||||
await store.hydrateCaches();
|
||||
await store.saveIdentity(identifier, newIdentity);
|
||||
});
|
||||
it('sets the new key to unverified', async () => {
|
||||
|
@ -147,6 +152,7 @@ describe('SignalProtocolStore', () => {
|
|||
verified: store.VerifiedStatus.UNVERIFIED,
|
||||
});
|
||||
|
||||
await store.hydrateCaches();
|
||||
await store.saveIdentity(identifier, newIdentity);
|
||||
});
|
||||
it('sets the new key to unverified', async () => {
|
||||
|
@ -168,12 +174,14 @@ describe('SignalProtocolStore', () => {
|
|||
nonblockingApproval: false,
|
||||
verified: store.VerifiedStatus.DEFAULT,
|
||||
});
|
||||
await store.hydrateCaches();
|
||||
});
|
||||
describe('If it is marked firstUse', () => {
|
||||
before(async () => {
|
||||
const identity = await window.Signal.Data.getIdentityKeyById(number);
|
||||
identity.firstUse = true;
|
||||
await window.Signal.Data.createOrUpdateIdentityKey(identity);
|
||||
await store.hydrateCaches();
|
||||
});
|
||||
it('nothing changes', async () => {
|
||||
await store.saveIdentity(identifier, testKey.pubKey, true);
|
||||
|
@ -188,6 +196,7 @@ describe('SignalProtocolStore', () => {
|
|||
const identity = await window.Signal.Data.getIdentityKeyById(number);
|
||||
identity.firstUse = false;
|
||||
await window.Signal.Data.createOrUpdateIdentityKey(identity);
|
||||
await store.hydrateCaches();
|
||||
});
|
||||
describe('If nonblocking approval is required', () => {
|
||||
let now;
|
||||
|
@ -198,6 +207,7 @@ describe('SignalProtocolStore', () => {
|
|||
);
|
||||
identity.timestamp = now;
|
||||
await window.Signal.Data.createOrUpdateIdentityKey(identity);
|
||||
await store.hydrateCaches();
|
||||
});
|
||||
it('sets non-blocking approval', async () => {
|
||||
await store.saveIdentity(identifier, testKey.pubKey, true);
|
||||
|
@ -311,6 +321,7 @@ describe('SignalProtocolStore', () => {
|
|||
verified: store.VerifiedStatus.DEFAULT,
|
||||
nonblockingApproval: false,
|
||||
});
|
||||
await store.hydrateCaches();
|
||||
}
|
||||
describe('with no public key argument', () => {
|
||||
before(saveRecordDefault);
|
||||
|
@ -370,6 +381,7 @@ describe('SignalProtocolStore', () => {
|
|||
describe('when there is no existing record', () => {
|
||||
before(async () => {
|
||||
await window.Signal.Data.removeIdentityKeyById(number);
|
||||
await store.hydrateCaches();
|
||||
});
|
||||
|
||||
it('does nothing', async () => {
|
||||
|
@ -403,6 +415,7 @@ describe('SignalProtocolStore', () => {
|
|||
verified: store.VerifiedStatus.VERIFIED,
|
||||
nonblockingApproval: false,
|
||||
});
|
||||
await store.hydrateCaches();
|
||||
});
|
||||
|
||||
it('does not save the new identity (because this is a less secure state)', async () => {
|
||||
|
@ -434,6 +447,7 @@ describe('SignalProtocolStore', () => {
|
|||
verified: store.VerifiedStatus.VERIFIED,
|
||||
nonblockingApproval: false,
|
||||
});
|
||||
await store.hydrateCaches();
|
||||
});
|
||||
|
||||
it('updates the verified status', async () => {
|
||||
|
@ -462,6 +476,7 @@ describe('SignalProtocolStore', () => {
|
|||
verified: store.VerifiedStatus.DEFAULT,
|
||||
nonblockingApproval: false,
|
||||
});
|
||||
await store.hydrateCaches();
|
||||
});
|
||||
|
||||
it('does not hang', async () => {
|
||||
|
@ -480,6 +495,7 @@ describe('SignalProtocolStore', () => {
|
|||
describe('when there is no existing record', () => {
|
||||
before(async () => {
|
||||
await window.Signal.Data.removeIdentityKeyById(number);
|
||||
await store.hydrateCaches();
|
||||
});
|
||||
|
||||
it('saves the new identity and marks it verified', async () => {
|
||||
|
@ -510,6 +526,7 @@ describe('SignalProtocolStore', () => {
|
|||
verified: store.VerifiedStatus.VERIFIED,
|
||||
nonblockingApproval: false,
|
||||
});
|
||||
await store.hydrateCaches();
|
||||
});
|
||||
|
||||
it('saves the new identity and marks it UNVERIFIED', async () => {
|
||||
|
@ -541,6 +558,7 @@ describe('SignalProtocolStore', () => {
|
|||
verified: store.VerifiedStatus.DEFAULT,
|
||||
nonblockingApproval: false,
|
||||
});
|
||||
await store.hydrateCaches();
|
||||
});
|
||||
|
||||
it('updates the verified status', async () => {
|
||||
|
@ -571,6 +589,7 @@ describe('SignalProtocolStore', () => {
|
|||
verified: store.VerifiedStatus.UNVERIFIED,
|
||||
nonblockingApproval: false,
|
||||
});
|
||||
await store.hydrateCaches();
|
||||
});
|
||||
|
||||
it('does not hang', async () => {
|
||||
|
@ -589,6 +608,7 @@ describe('SignalProtocolStore', () => {
|
|||
describe('when there is no existing record', () => {
|
||||
before(async () => {
|
||||
await window.Signal.Data.removeIdentityKeyById(number);
|
||||
await store.hydrateCaches();
|
||||
});
|
||||
|
||||
it('saves the new identity and marks it verified', async () => {
|
||||
|
@ -615,6 +635,7 @@ describe('SignalProtocolStore', () => {
|
|||
verified: store.VerifiedStatus.VERIFIED,
|
||||
nonblockingApproval: false,
|
||||
});
|
||||
await store.hydrateCaches();
|
||||
});
|
||||
|
||||
it('saves the new identity and marks it VERIFIED', async () => {
|
||||
|
@ -646,6 +667,7 @@ describe('SignalProtocolStore', () => {
|
|||
verified: store.VerifiedStatus.UNVERIFIED,
|
||||
nonblockingApproval: false,
|
||||
});
|
||||
await store.hydrateCaches();
|
||||
});
|
||||
|
||||
it('saves the identity and marks it verified', async () => {
|
||||
|
@ -676,6 +698,7 @@ describe('SignalProtocolStore', () => {
|
|||
verified: store.VerifiedStatus.VERIFIED,
|
||||
nonblockingApproval: false,
|
||||
});
|
||||
await store.hydrateCaches();
|
||||
});
|
||||
|
||||
it('does not hang', async () => {
|
||||
|
@ -703,6 +726,7 @@ describe('SignalProtocolStore', () => {
|
|||
nonblockingApproval: false,
|
||||
});
|
||||
|
||||
await store.hydrateCaches();
|
||||
const untrusted = await store.isUntrusted(number);
|
||||
assert.strictEqual(untrusted, false);
|
||||
});
|
||||
|
@ -716,6 +740,7 @@ describe('SignalProtocolStore', () => {
|
|||
firstUse: false,
|
||||
nonblockingApproval: true,
|
||||
});
|
||||
await store.hydrateCaches();
|
||||
|
||||
const untrusted = await store.isUntrusted(number);
|
||||
assert.strictEqual(untrusted, false);
|
||||
|
@ -730,6 +755,7 @@ describe('SignalProtocolStore', () => {
|
|||
firstUse: true,
|
||||
nonblockingApproval: false,
|
||||
});
|
||||
await store.hydrateCaches();
|
||||
|
||||
const untrusted = await store.isUntrusted(number);
|
||||
assert.strictEqual(untrusted, false);
|
||||
|
@ -744,6 +770,8 @@ describe('SignalProtocolStore', () => {
|
|||
firstUse: false,
|
||||
nonblockingApproval: false,
|
||||
});
|
||||
await store.hydrateCaches();
|
||||
|
||||
const untrusted = await store.isUntrusted(number);
|
||||
assert.strictEqual(untrusted, true);
|
||||
});
|
||||
|
|
|
@ -67,7 +67,7 @@ export class Image extends React.Component<Props> {
|
|||
<div
|
||||
role={canClick ? 'button' : undefined}
|
||||
onClick={() => {
|
||||
if (canClick) {
|
||||
if (canClick && onClick) {
|
||||
onClick(attachment);
|
||||
}
|
||||
}}
|
||||
|
|
Loading…
Reference in a new issue