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
|
@ -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();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue