omemo keyring
This commit is contained in:
@@ -76,10 +76,13 @@ var Account = {
|
||||
Object.keys(localStorage).filter(k => k.startsWith(prefix)).forEach(k => keys[k] = localStorage.getItem(k));
|
||||
let deviceId = await store.getLocalRegistrationId();
|
||||
|
||||
// The decrypted bodies too: our own messages are not encrypted for this device and,
|
||||
// the sessions being reset on restore, the older ones cannot be decrypted again
|
||||
let payload = JSON.stringify({
|
||||
jid: store.jid,
|
||||
deviceId: deviceId,
|
||||
keys: keys
|
||||
keys: keys,
|
||||
messages: await ChatOmemoDB.getAllMessages()
|
||||
});
|
||||
|
||||
const salt = crypto.getRandomValues(new Uint8Array(16));
|
||||
@@ -143,6 +146,15 @@ var Account = {
|
||||
Object.keys(localStorage).filter(k => k.startsWith(prefix)).forEach(k => localStorage.removeItem(k));
|
||||
Object.entries(payload.keys).forEach(([k, v]) => localStorage.setItem(k, v));
|
||||
|
||||
// The ratchets of the backup are behind the ones of our contacts: they cannot decrypt
|
||||
// their new messages and break our next ones, new sessions are built on the next message
|
||||
store.removeAllSessions();
|
||||
|
||||
// Backups made before the messages were included don't have them
|
||||
if (Array.isArray(payload.messages)) {
|
||||
await ChatOmemoDB.putMissingMessages(payload.messages);
|
||||
}
|
||||
|
||||
localStorage.setObject(OMEMO_RESTORED_KEY, { replaced: replaced != payload.deviceId ? replaced ?? null : null });
|
||||
|
||||
// The page reload publishes the restored key (ChatOmemo.ownDevicesReceived)
|
||||
|
||||
@@ -272,12 +272,29 @@ var ChatOmemo = {
|
||||
|
||||
var store = new ChatOmemoStorage();
|
||||
|
||||
let resolvedId = message.mine
|
||||
// Our own messages are stored under the id this browser sent them with: in 1:1
|
||||
// the ones coming back from the archive (MAM) have another local id (m_…)
|
||||
let resolvedId = (message.mine || message.jidfrom == USER_JID) && message.messageid
|
||||
? message.messageid
|
||||
: message.id;
|
||||
|
||||
// A received message can be decrypted only once, but its local id changes when it comes
|
||||
// back from the archive (MAM): its plaintext is also kept under the sender and the id they chose
|
||||
let stableId = resolvedId == message.id && message.messageid
|
||||
? ['received', message.jidfrom, message.type == 'groupchat' ? message.resource : '', message.messageid].join('|')
|
||||
: null;
|
||||
|
||||
if (stableId) {
|
||||
let stable = await ChatOmemoDB.getMessage(stableId);
|
||||
if (typeof stable == 'string') return stable;
|
||||
}
|
||||
|
||||
let maybeDecrypted = await ChatOmemoDB.getMessage(resolvedId);
|
||||
|
||||
if (typeof maybeDecrypted == 'string' && stableId) {
|
||||
ChatOmemoDB.putMessage(stableId, maybeDecrypted);
|
||||
}
|
||||
|
||||
if (maybeDecrypted !== undefined) {
|
||||
return maybeDecrypted;
|
||||
}
|
||||
@@ -369,6 +386,7 @@ var ChatOmemo = {
|
||||
let plaintext = MovimUtils.arrayBufferToString(decryptedBuffer);
|
||||
|
||||
ChatOmemoDB.putMessage(resolvedId, plaintext);
|
||||
if (stableId) ChatOmemoDB.putMessage(stableId, plaintext);
|
||||
return plaintext;
|
||||
},
|
||||
enableContactState: function (jid, muc) {
|
||||
|
||||
@@ -65,6 +65,41 @@ var ChatOmemoDB = {
|
||||
return promise;
|
||||
},
|
||||
|
||||
// All the decrypted bodies, for the key backup (Account.omemoBackup)
|
||||
getAllMessages: async function() {
|
||||
return new Promise(resolve => {
|
||||
var tx = ChatOmemoDB.db.result.transaction(ChatOmemoDB.storeName, 'readonly');
|
||||
var request = tx.objectStore(ChatOmemoDB.storeName).getAll();
|
||||
request.onsuccess = function(event) {
|
||||
resolve(event.target.result.filter(m => typeof m.body == 'string'));
|
||||
}
|
||||
request.onerror = function() {
|
||||
resolve([]);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Restore them without overwriting what this browser already decrypted
|
||||
putMissingMessages: async function(messages) {
|
||||
return new Promise(resolve => {
|
||||
var tx = ChatOmemoDB.db.result.transaction(ChatOmemoDB.storeName, 'readwrite');
|
||||
var os = tx.objectStore(ChatOmemoDB.storeName);
|
||||
|
||||
messages.forEach(m => {
|
||||
if (typeof m.id != 'string' || typeof m.body != 'string') return;
|
||||
|
||||
os.get(m.id).onsuccess = function(event) {
|
||||
if (!event.target.result || typeof event.target.result.body != 'string') {
|
||||
os.put({"id" : m.id, "body" : m.body});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tx.oncomplete = () => resolve();
|
||||
tx.onerror = () => resolve();
|
||||
});
|
||||
},
|
||||
|
||||
getMessage: async function(id) {
|
||||
let promise = new Promise(resolve => {
|
||||
if (id in ChatOmemoDB.cachedMessages) {
|
||||
|
||||
Reference in New Issue
Block a user