feat: Guard against invalid X3DH signatures
This commit is contained in:
parent
c7ded4c824
commit
af33ed51d1
@ -1,10 +1,7 @@
|
|||||||
abstract class OmemoError {}
|
abstract class OmemoError {}
|
||||||
|
|
||||||
/// Triggered during X3DH if the signature if the SPK does verify to the actual SPK.
|
/// Triggered during X3DH if the signature if the SPK does verify to the actual SPK.
|
||||||
class InvalidSignatureException extends OmemoError implements Exception {
|
class InvalidKeyExchangeSignatureError extends OmemoError {}
|
||||||
String errMsg() =>
|
|
||||||
'The signature of the SPK does not match the provided signature';
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Triggered by the Double Ratchet if the computed HMAC does not match the attached HMAC.
|
/// Triggered by the Double Ratchet if the computed HMAC does not match the attached HMAC.
|
||||||
class InvalidMessageHMACError extends OmemoError {}
|
class InvalidMessageHMACError extends OmemoError {}
|
||||||
|
@ -509,6 +509,7 @@ class OmemoManager {
|
|||||||
ciphertext = [];
|
ciphertext = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final encryptionErrors = <String, List<EncryptToJidError>>{};
|
||||||
final addedRatchetKeys = List<RatchetMapKey>.empty(growable: true);
|
final addedRatchetKeys = List<RatchetMapKey>.empty(growable: true);
|
||||||
final kex = <RatchetMapKey, OMEMOKeyExchange>{};
|
final kex = <RatchetMapKey, OMEMOKeyExchange>{};
|
||||||
for (final jid in stanza.recipientJids) {
|
for (final jid in stanza.recipientJids) {
|
||||||
@ -521,10 +522,24 @@ class OmemoManager {
|
|||||||
_log.finest('Building new ratchet $jid:${bundle.id}');
|
_log.finest('Building new ratchet $jid:${bundle.id}');
|
||||||
final ratchetKey = RatchetMapKey(jid, bundle.id);
|
final ratchetKey = RatchetMapKey(jid, bundle.id);
|
||||||
final ownDevice = await getDevice();
|
final ownDevice = await getDevice();
|
||||||
final kexResult = await x3dhFromBundle(
|
final kexResultRaw = await x3dhFromBundle(
|
||||||
bundle,
|
bundle,
|
||||||
ownDevice.ik,
|
ownDevice.ik,
|
||||||
);
|
);
|
||||||
|
// TODO: Track the failure and do not attempt to encrypt to this device
|
||||||
|
// on every send.
|
||||||
|
if (kexResultRaw.isType<InvalidKeyExchangeSignatureError>()) {
|
||||||
|
encryptionErrors.appendOrCreate(
|
||||||
|
jid,
|
||||||
|
EncryptToJidError(
|
||||||
|
bundle.id,
|
||||||
|
kexResultRaw.get<InvalidKeyExchangeSignatureError>(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final kexResult = kexResultRaw.get<X3DHAliceResult>();
|
||||||
final newRatchet = await OmemoDoubleRatchet.initiateNewSession(
|
final newRatchet = await OmemoDoubleRatchet.initiateNewSession(
|
||||||
bundle.spk,
|
bundle.spk,
|
||||||
bundle.spkId,
|
bundle.spkId,
|
||||||
@ -567,7 +582,6 @@ class OmemoManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt the symmetric key for all devices.
|
// Encrypt the symmetric key for all devices.
|
||||||
final encryptionErrors = <String, List<EncryptToJidError>>{};
|
|
||||||
final encryptedKeys = <String, List<EncryptedKey>>{};
|
final encryptedKeys = <String, List<EncryptedKey>>{};
|
||||||
for (final jid in stanza.recipientJids) {
|
for (final jid in stanza.recipientJids) {
|
||||||
// Check if we know about any devices to use
|
// Check if we know about any devices to use
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:cryptography/cryptography.dart';
|
import 'package:cryptography/cryptography.dart';
|
||||||
|
import 'package:omemo_dart/src/common/result.dart';
|
||||||
import 'package:omemo_dart/src/crypto.dart';
|
import 'package:omemo_dart/src/crypto.dart';
|
||||||
import 'package:omemo_dart/src/errors.dart';
|
import 'package:omemo_dart/src/errors.dart';
|
||||||
import 'package:omemo_dart/src/helpers.dart';
|
import 'package:omemo_dart/src/helpers.dart';
|
||||||
@ -70,7 +71,7 @@ Future<List<int>> kdf(List<int> km) async {
|
|||||||
|
|
||||||
/// Alice builds a session with Bob using his bundle [bundle] and Alice's identity key
|
/// Alice builds a session with Bob using his bundle [bundle] and Alice's identity key
|
||||||
/// pair [ik].
|
/// pair [ik].
|
||||||
Future<X3DHAliceResult> x3dhFromBundle(
|
Future<Result<InvalidKeyExchangeSignatureError, X3DHAliceResult>> x3dhFromBundle(
|
||||||
OmemoBundle bundle,
|
OmemoBundle bundle,
|
||||||
OmemoKeyPair ik,
|
OmemoKeyPair ik,
|
||||||
) async {
|
) async {
|
||||||
@ -84,7 +85,7 @@ Future<X3DHAliceResult> x3dhFromBundle(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!signatureValue) {
|
if (!signatureValue) {
|
||||||
throw InvalidSignatureException();
|
return Result(InvalidKeyExchangeSignatureError());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate EK
|
// Generate EK
|
||||||
@ -106,7 +107,7 @@ Future<X3DHAliceResult> x3dhFromBundle(
|
|||||||
await bundle.ik.getBytes(),
|
await bundle.ik.getBytes(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return X3DHAliceResult(ek, sk, opkId, ad);
|
return Result(X3DHAliceResult(ek, sk, opkId, ad));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bob builds the X3DH shared secret from the inital message [msg], the SPK [spk], the
|
/// Bob builds the X3DH shared secret from the inital message [msg], the SPK [spk], the
|
||||||
|
@ -28,7 +28,8 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Alice does X3DH
|
// Alice does X3DH
|
||||||
final resultAlice = await x3dhFromBundle(bundleBob, ikAlice);
|
final resultAliceRaw = await x3dhFromBundle(bundleBob, ikAlice);
|
||||||
|
final resultAlice = resultAliceRaw.get<X3DHAliceResult>();
|
||||||
|
|
||||||
// Alice sends the inital message to Bob
|
// Alice sends the inital message to Bob
|
||||||
// ...
|
// ...
|
||||||
|
@ -26,7 +26,8 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Alice does X3DH
|
// Alice does X3DH
|
||||||
final resultAlice = await x3dhFromBundle(bundleBob, ikAlice);
|
final resultAliceRaw = await x3dhFromBundle(bundleBob, ikAlice);
|
||||||
|
final resultAlice = resultAliceRaw.get<X3DHAliceResult>();
|
||||||
|
|
||||||
// Alice sends the inital message to Bob
|
// Alice sends the inital message to Bob
|
||||||
// ...
|
// ...
|
||||||
@ -68,18 +69,7 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Alice does X3DH
|
// Alice does X3DH
|
||||||
var exception = false;
|
final result = await x3dhFromBundle(bundleBob, ikAlice);
|
||||||
try {
|
expect(result.isType<InvalidKeyExchangeSignatureError>(), isTrue);
|
||||||
await x3dhFromBundle(bundleBob, ikAlice);
|
|
||||||
} catch (e) {
|
|
||||||
exception = true;
|
|
||||||
expect(
|
|
||||||
e is InvalidSignatureException,
|
|
||||||
true,
|
|
||||||
reason: 'Expected InvalidSignatureException, but got $e',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(exception, true, reason: 'Expected test failure');
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user