feat: _decryptMessage explicitly tells us that a ratchet was created
This commit is contained in:
parent
54eeb816eb
commit
6c4dd62c5a
@ -28,6 +28,12 @@ import 'package:omemo_dart/src/trust/base.dart';
|
|||||||
import 'package:omemo_dart/src/x3dh/x3dh.dart';
|
import 'package:omemo_dart/src/x3dh/x3dh.dart';
|
||||||
import 'package:synchronized/synchronized.dart';
|
import 'package:synchronized/synchronized.dart';
|
||||||
|
|
||||||
|
class _InternalDecryptionResult {
|
||||||
|
const _InternalDecryptionResult(this.ratchetCreated, this.payload);
|
||||||
|
final bool ratchetCreated;
|
||||||
|
final String? payload;
|
||||||
|
}
|
||||||
|
|
||||||
class OmemoManager {
|
class OmemoManager {
|
||||||
OmemoManager(
|
OmemoManager(
|
||||||
this._device,
|
this._device,
|
||||||
@ -246,7 +252,7 @@ class OmemoManager {
|
|||||||
/// element, then [ciphertext] must be set to null. In this case, this function
|
/// element, then [ciphertext] must be set to null. In this case, this function
|
||||||
/// will return null as there is no message to be decrypted. This, however, is used
|
/// will return null as there is no message to be decrypted. This, however, is used
|
||||||
/// to set up sessions or advance the ratchets.
|
/// to set up sessions or advance the ratchets.
|
||||||
Future<String?> _decryptMessage(List<int>? ciphertext, String senderJid, int senderDeviceId, List<EncryptedKey> keys, int timestamp) async {
|
Future<_InternalDecryptionResult> _decryptMessage(List<int>? ciphertext, String senderJid, int senderDeviceId, List<EncryptedKey> keys, int timestamp) async {
|
||||||
// Try to find a session we can decrypt with.
|
// Try to find a session we can decrypt with.
|
||||||
var device = await getDevice();
|
var device = await getDevice();
|
||||||
final rawKey = keys.firstWhereOrNull((key) => key.rid == device.id);
|
final rawKey = keys.firstWhereOrNull((key) => key.rid == device.id);
|
||||||
@ -260,6 +266,7 @@ class OmemoManager {
|
|||||||
OmemoAuthenticatedMessage authMessage;
|
OmemoAuthenticatedMessage authMessage;
|
||||||
OmemoDoubleRatchet? oldRatchet;
|
OmemoDoubleRatchet? oldRatchet;
|
||||||
OmemoMessage? message;
|
OmemoMessage? message;
|
||||||
|
var ratchetCreated = false;
|
||||||
if (rawKey.kex) {
|
if (rawKey.kex) {
|
||||||
// If the ratchet already existed, we store it. If it didn't, oldRatchet will stay
|
// If the ratchet already existed, we store it. If it didn't, oldRatchet will stay
|
||||||
// null.
|
// null.
|
||||||
@ -294,7 +301,10 @@ class OmemoManager {
|
|||||||
decrypted,
|
decrypted,
|
||||||
);
|
);
|
||||||
_addSession(senderJid, senderDeviceId, oldRatchet);
|
_addSession(senderJid, senderDeviceId, oldRatchet);
|
||||||
return plaintext;
|
return _InternalDecryptionResult(
|
||||||
|
true,
|
||||||
|
plaintext,
|
||||||
|
);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
_log.finest('Failed to use old ratchet with KEX for existing ratchet');
|
_log.finest('Failed to use old ratchet with KEX for existing ratchet');
|
||||||
}
|
}
|
||||||
@ -303,6 +313,7 @@ class OmemoManager {
|
|||||||
final r = await _addSessionFromKeyExchange(senderJid, senderDeviceId, kex);
|
final r = await _addSessionFromKeyExchange(senderJid, senderDeviceId, kex);
|
||||||
await _trustManager.onNewSession(senderJid, senderDeviceId);
|
await _trustManager.onNewSession(senderJid, senderDeviceId);
|
||||||
_addSession(senderJid, senderDeviceId, r);
|
_addSession(senderJid, senderDeviceId, r);
|
||||||
|
ratchetCreated = true;
|
||||||
|
|
||||||
// Replace the OPK
|
// Replace the OPK
|
||||||
// TODO(PapaTutuWawa): Replace the OPK when we know that the KEX worked
|
// TODO(PapaTutuWawa): Replace the OPK when we know that the KEX worked
|
||||||
@ -348,7 +359,10 @@ class OmemoManager {
|
|||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return _decryptAndVerifyHmac(ciphertext, keyAndHmac);
|
return _InternalDecryptionResult(
|
||||||
|
ratchetCreated,
|
||||||
|
await _decryptAndVerifyHmac(ciphertext, keyAndHmac),
|
||||||
|
);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
_restoreRatchet(ratchetKey, oldRatchet);
|
_restoreRatchet(ratchetKey, oldRatchet);
|
||||||
rethrow;
|
rethrow;
|
||||||
@ -519,10 +533,9 @@ class OmemoManager {
|
|||||||
await _enterRatchetCriticalSection(stanza.bareSenderJid);
|
await _enterRatchetCriticalSection(stanza.bareSenderJid);
|
||||||
|
|
||||||
final ratchetKey = RatchetMapKey(stanza.bareSenderJid, stanza.senderDeviceId);
|
final ratchetKey = RatchetMapKey(stanza.bareSenderJid, stanza.senderDeviceId);
|
||||||
final ratchetCreated = !_ratchetMap.containsKey(ratchetKey);
|
final _InternalDecryptionResult result;
|
||||||
String? payload;
|
|
||||||
try {
|
try {
|
||||||
payload = await _decryptMessage(
|
result = await _decryptMessage(
|
||||||
base64.decode(stanza.payload),
|
base64.decode(stanza.payload),
|
||||||
stanza.bareSenderJid,
|
stanza.bareSenderJid,
|
||||||
stanza.senderDeviceId,
|
stanza.senderDeviceId,
|
||||||
@ -541,7 +554,9 @@ class OmemoManager {
|
|||||||
final ratchet = _getRatchet(ratchetKey);
|
final ratchet = _getRatchet(ratchetKey);
|
||||||
assert(ratchet != null, 'We decrypted the message, so the ratchet must exist');
|
assert(ratchet != null, 'We decrypted the message, so the ratchet must exist');
|
||||||
|
|
||||||
if (ratchet!.nr > 53) {
|
if (ratchet!.acknowledged) {
|
||||||
|
// Ratchet is acknowledged
|
||||||
|
if (ratchet.nr > 53 || result.ratchetCreated) {
|
||||||
await sendEmptyOmemoMessage(
|
await sendEmptyOmemoMessage(
|
||||||
await _encryptToJids(
|
await _encryptToJids(
|
||||||
[stanza.bareSenderJid],
|
[stanza.bareSenderJid],
|
||||||
@ -552,15 +567,14 @@ class OmemoManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ratchet is acked
|
// Ratchet is acked
|
||||||
if (!ratchetCreated && ratchet.acknowledged) {
|
|
||||||
await _leaveRatchetCriticalSection(stanza.bareSenderJid);
|
await _leaveRatchetCriticalSection(stanza.bareSenderJid);
|
||||||
return DecryptionResult(
|
return DecryptionResult(
|
||||||
payload,
|
result.payload,
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
|
// Ratchet is not acked.
|
||||||
// Ratchet is not acked. Mark as acked and send an empty OMEMO message.
|
// Mark as acked and send an empty OMEMO message.
|
||||||
await ratchetAcknowledged(
|
await ratchetAcknowledged(
|
||||||
stanza.bareSenderJid,
|
stanza.bareSenderJid,
|
||||||
stanza.senderDeviceId,
|
stanza.senderDeviceId,
|
||||||
@ -576,10 +590,11 @@ class OmemoManager {
|
|||||||
|
|
||||||
await _leaveRatchetCriticalSection(stanza.bareSenderJid);
|
await _leaveRatchetCriticalSection(stanza.bareSenderJid);
|
||||||
return DecryptionResult(
|
return DecryptionResult(
|
||||||
payload,
|
result.payload,
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Call when sending out an encrypted stanza. Will handle everything and
|
/// Call when sending out an encrypted stanza. Will handle everything and
|
||||||
/// encrypt it.
|
/// encrypt it.
|
||||||
|
@ -71,9 +71,10 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
expect(bobResult.payload, 'Hello world');
|
||||||
|
expect(bobResult.error, null);
|
||||||
expect(aliceEmptyMessageSent, 0);
|
expect(aliceEmptyMessageSent, 0);
|
||||||
expect(bobEmptyMessageSent, 1);
|
expect(bobEmptyMessageSent, 1);
|
||||||
expect(bobResult.payload, 'Hello world');
|
|
||||||
|
|
||||||
// Alice receives the ack message
|
// Alice receives the ack message
|
||||||
await aliceManager.ratchetAcknowledged(
|
await aliceManager.ratchetAcknowledged(
|
||||||
|
Loading…
Reference in New Issue
Block a user