Performance: Automate clean up of old material after hitting a limit in libsignal-protocol

This commit is contained in:
Fedor Indutny 2021-04-13 08:52:26 -07:00 committed by GitHub
parent 62f1a42c25
commit d933e3a6fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 57 additions and 2 deletions

View file

@ -24690,6 +24690,25 @@ libsignal.SessionBuilder = function (storage, remoteAddress) {
this.processV3 = builder.processV3.bind(builder); this.processV3 = builder.processV3.bind(builder);
}; };
function cleanOldMessageKeys(messageKeys) {
var limit = 2000;
var counters = Object.keys(messageKeys);
if (counters.length <= limit) {
return;
}
console.log('cleaning old message keys', counters.length);
// Sort counters in increasing order
var intCounters = counters
.map(string => parseInt(string, 10))
.sort((a, b) => a - b);
while (intCounters.length > limit) {
delete messageKeys[intCounters.shift()];
}
}
function SessionCipher(storage, remoteAddress, options) { function SessionCipher(storage, remoteAddress, options) {
this.remoteAddress = remoteAddress; this.remoteAddress = remoteAddress;
this.storage = storage; this.storage = storage;
@ -25028,8 +25047,13 @@ SessionCipher.prototype = {
throw error; throw error;
}); });
}, },
fillMessageKeys: function(chain, counter) { fillMessageKeys: function(chain, counter, hasChanged = false) {
if (chain.chainKey.counter >= counter) { if (chain.chainKey.counter >= counter) {
// End of recursive iteration. Time to cleanup
if (hasChanged) {
cleanOldMessageKeys(chain.messageKeys);
}
return Promise.resolve(); // Already calculated return Promise.resolve(); // Already calculated
} }
@ -25060,7 +25084,7 @@ SessionCipher.prototype = {
chain.messageKeys[chain.chainKey.counter + 1] = mac; chain.messageKeys[chain.chainKey.counter + 1] = mac;
chain.chainKey.key = key; chain.chainKey.key = key;
chain.chainKey.counter += 1; chain.chainKey.counter += 1;
return this.fillMessageKeys(chain, counter); return this.fillMessageKeys(chain, counter, true);
}.bind(this)); }.bind(this));
}.bind(this)); }.bind(this));
}, },
@ -25209,6 +25233,9 @@ libsignal.SessionCipher = function(storage, remoteAddress) {
this.deleteAllSessionsForDevice = cipher.deleteAllSessionsForDevice.bind(cipher); this.deleteAllSessionsForDevice = cipher.deleteAllSessionsForDevice.bind(cipher);
}; };
// Only for tests
libsignal.SessionCipher.cleanOldMessageKeys = cleanOldMessageKeys;
/* /*
* jobQueue manages multiple queues indexed by device to serialize * jobQueue manages multiple queues indexed by device to serialize
* session io ops on the database. * session io ops on the database.

View file

@ -105,4 +105,32 @@ describe('Protocol Wrapper', function protocolWrapperDescribe() {
}); });
}); });
}); });
describe('cleanOldMessageKeys', () => {
it('should clean old message keys', () => {
const messageKeys = {};
const LIMIT = 2000;
for (let i = 0; i < 2 * LIMIT; i += 1) {
messageKeys[i] = i;
}
libsignal.SessionCipher.cleanOldMessageKeys(messageKeys);
for (let i = 0; i < LIMIT; i += 1) {
assert(
!Object.prototype.hasOwnProperty.call(messageKeys, i),
`should delete old key ${i}`
);
}
for (let i = LIMIT; i < 2 * LIMIT; i += 1) {
assert(
Object.prototype.hasOwnProperty.call(messageKeys, i),
`should have fresh key ${i}`
);
}
});
});
}); });