diff --git a/lib/src/omemo/sessionmanager.dart b/lib/src/omemo/sessionmanager.dart index 7a3357c..8cba9ca 100644 --- a/lib/src/omemo/sessionmanager.dart +++ b/lib/src/omemo/sessionmanager.dart @@ -369,6 +369,19 @@ class OmemoSessionManager { return fingerprints; } + + /// Replaces the Signed Prekey and its signature in our own device bundle. Triggers + /// a DeviceModifiedEvent when done. + /// See https://xmpp.org/extensions/xep-0384.html#protocol-key_exchange under the point + /// "signed PreKey rotation period" for recommendations. + Future rotateSignedPrekey() async { + await _deviceLock.synchronized(() async { + _device = await _device.replaceSignedPrekey(); + + // Commit the new device + _eventStreamController.add(DeviceModifiedEvent(_device)); + }); + } @visibleForTesting OmemoDoubleRatchet getRatchet(String jid, int deviceId) => _ratchetMap[RatchetMapKey(jid, deviceId)]!; diff --git a/test/omemo_test.dart b/test/omemo_test.dart index bf0a9f7..45545c9 100644 --- a/test/omemo_test.dart +++ b/test/omemo_test.dart @@ -221,4 +221,28 @@ void main() { // This call must not cause an exception bobSession.getRatchet(aliceJid, (await aliceSession.getDevice()).id); }); + + test('Test rotating the Signed Prekey', () async { + // Generate the session + const aliceJid = 'alice@some.server'; + final aliceSession = await OmemoSessionManager.generateNewIdentity(aliceJid, opkAmount: 1); + + // Setup an event listener + final oldDevice = await aliceSession.getDevice(); + Device? newDevice; + aliceSession.eventStream.listen((event) { + if (event is DeviceModifiedEvent) { + newDevice = event.device; + } + }); + + // Rotate the Signed Prekey + await aliceSession.rotateSignedPrekey(); + + // Just for safety... + await Future.delayed(const Duration(seconds: 2)); + + expect(await oldDevice.equals(newDevice!), false); + expect(await newDevice!.equals(await aliceSession.getDevice()), true); + }); }