feat: Guard against invalid X3DH signatures
This commit is contained in:
parent
c7ded4c824
commit
af33ed51d1
@ -1,10 +1,7 @@
|
||||
abstract class OmemoError {}
|
||||
|
||||
/// Triggered during X3DH if the signature if the SPK does verify to the actual SPK.
|
||||
class InvalidSignatureException extends OmemoError implements Exception {
|
||||
String errMsg() =>
|
||||
'The signature of the SPK does not match the provided signature';
|
||||
}
|
||||
class InvalidKeyExchangeSignatureError extends OmemoError {}
|
||||
|
||||
/// Triggered by the Double Ratchet if the computed HMAC does not match the attached HMAC.
|
||||
class InvalidMessageHMACError extends OmemoError {}
|
||||
|
@ -509,6 +509,7 @@ class OmemoManager {
|
||||
ciphertext = [];
|
||||
}
|
||||
|
||||
final encryptionErrors = <String, List<EncryptToJidError>>{};
|
||||
final addedRatchetKeys = List<RatchetMapKey>.empty(growable: true);
|
||||
final kex = <RatchetMapKey, OMEMOKeyExchange>{};
|
||||
for (final jid in stanza.recipientJids) {
|
||||
@ -521,10 +522,24 @@ class OmemoManager {
|
||||
_log.finest('Building new ratchet $jid:${bundle.id}');
|
||||
final ratchetKey = RatchetMapKey(jid, bundle.id);
|
||||
final ownDevice = await getDevice();
|
||||
final kexResult = await x3dhFromBundle(
|
||||
final kexResultRaw = await x3dhFromBundle(
|
||||
bundle,
|
||||
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(
|
||||
bundle.spk,
|
||||
bundle.spkId,
|
||||
@ -567,7 +582,6 @@ class OmemoManager {
|
||||
}
|
||||
|
||||
// Encrypt the symmetric key for all devices.
|
||||
final encryptionErrors = <String, List<EncryptToJidError>>{};
|
||||
final encryptedKeys = <String, List<EncryptedKey>>{};
|
||||
for (final jid in stanza.recipientJids) {
|
||||
// Check if we know about any devices to use
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
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/errors.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
|
||||
/// pair [ik].
|
||||
Future<X3DHAliceResult> x3dhFromBundle(
|
||||
Future<Result<InvalidKeyExchangeSignatureError, X3DHAliceResult>> x3dhFromBundle(
|
||||
OmemoBundle bundle,
|
||||
OmemoKeyPair ik,
|
||||
) async {
|
||||
@ -84,7 +85,7 @@ Future<X3DHAliceResult> x3dhFromBundle(
|
||||
);
|
||||
|
||||
if (!signatureValue) {
|
||||
throw InvalidSignatureException();
|
||||
return Result(InvalidKeyExchangeSignatureError());
|
||||
}
|
||||
|
||||
// Generate EK
|
||||
@ -106,7 +107,7 @@ Future<X3DHAliceResult> x3dhFromBundle(
|
||||
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
|
||||
|
@ -28,7 +28,8 @@ void main() {
|
||||
);
|
||||
|
||||
// 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
|
||||
// ...
|
||||
|
@ -26,7 +26,8 @@ void main() {
|
||||
);
|
||||
|
||||
// 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
|
||||
// ...
|
||||
@ -68,18 +69,7 @@ void main() {
|
||||
);
|
||||
|
||||
// Alice does X3DH
|
||||
var exception = false;
|
||||
try {
|
||||
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');
|
||||
final result = await x3dhFromBundle(bundleBob, ikAlice);
|
||||
expect(result.isType<InvalidKeyExchangeSignatureError>(), isTrue);
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user