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