feat: Guard against malformed ciphertext

This commit is contained in:
PapaTutuWawa 2023-06-15 16:42:17 +02:00
parent f1ec8d1793
commit 6c301ab88f
5 changed files with 42 additions and 30 deletions

View File

@ -3,7 +3,7 @@ class Result<T, V> {
const Result(this._data)
: assert(
_data is T || _data is V,
'Invalid data type: Must be either $T or $V',
'Invalid data type $_data: Must be either $T or $V',
);
final dynamic _data;

View File

@ -1,5 +1,7 @@
import 'dart:convert';
import 'package:cryptography/cryptography.dart';
import 'package:omemo_dart/src/common/result.dart';
import 'package:omemo_dart/src/errors.dart';
import 'package:omemo_dart/src/keys.dart';
/// Performs X25519 with [kp] and [pk]. If [identityKey] is set, then
@ -92,7 +94,7 @@ Future<List<int>> aes256CbcEncrypt(
/// A small helper function to make AES-256-CBC easier. Decrypt [ciphertext] using [key] as
/// the encryption key and [iv] as the IV. Returns the ciphertext.
Future<List<int>> aes256CbcDecrypt(
Future<Result<MalformedCiphertextError, List<int>>> aes256CbcDecrypt(
List<int> ciphertext,
List<int> key,
List<int> iv,
@ -100,13 +102,19 @@ Future<List<int>> aes256CbcDecrypt(
final algorithm = AesCbc.with256bits(
macAlgorithm: MacAlgorithm.empty,
);
return algorithm.decrypt(
NoMacSecretBox(
ciphertext,
nonce: iv,
),
secretKey: SecretKey(key),
);
try {
return Result(
await algorithm.decrypt(
NoMacSecretBox(
ciphertext,
nonce: iv,
),
secretKey: SecretKey(key),
),
);
} catch (ex) {
return Result(MalformedCiphertextError(ex));
}
}
/// OMEMO often uses the output of a HMAC-SHA-256 truncated to its first 16 bytes.

View File

@ -261,7 +261,11 @@ class OmemoDoubleRatchet {
}
final plaintext = await aes256CbcDecrypt(ciphertext, keys.encryptionKey, keys.iv);
return Result(plaintext);
if (plaintext.isType<MalformedCiphertextError>()) {
return Result(plaintext.get<MalformedCiphertextError>());
}
return Result(plaintext.get<List<int>>());
}
/// Checks whether we could decrypt the payload in [header] with a skipped key. If yes,

View File

@ -13,22 +13,10 @@ class SkippingTooManyKeysError extends OmemoError {}
/// Triggered by the Session Manager if the message key is not encrypted for the device.
class NotEncryptedForDeviceError extends OmemoError {}
/// Triggered by the Session Manager when there is no key for decrypting the message.
class NoDecryptionKeyException extends OmemoError implements Exception {
String errMsg() => 'No key available for decrypting the message';
}
/// Triggered by the Session Manager when the identifier of the used Signed Prekey
/// is neither the current SPK's identifier nor the old one's.
class UnknownSignedPrekeyError extends OmemoError {}
/// Triggered by the Session Manager when the received Key Exchange message does not meet
/// the requirement that a key exchange, given that the ratchet already exists, must be
/// sent after its creation.
class InvalidKeyExchangeException extends OmemoError implements Exception {
String errMsg() => 'The key exchange was sent before the last kex finished';
}
/// Triggered by the OmemoManager when we could not encrypt a message as we have
/// no key material available. That happens, for example, when we want to create a
/// ratchet session with a JID we had no session with but fetching the device bundle
@ -38,3 +26,11 @@ class NoKeyMaterialAvailableError extends OmemoError {}
/// A non-key-exchange message was received that was encrypted for our device, but we have no ratchet with
/// the device that sent the message.
class NoSessionWithDeviceError extends OmemoError {}
/// Caused when the AES-256 CBC decryption failed.
class MalformedCiphertextError extends OmemoError {
MalformedCiphertextError(this.ex);
/// The exception that was raised while decryption.
final Object ex;
}

View File

@ -152,14 +152,20 @@ class OmemoManager {
return Result(InvalidMessageHMACError());
}
// TODO: Handle an exception from the crypto implementation
final result = await aes256CbcDecrypt(
ciphertext,
derivedKeys.encryptionKey,
derivedKeys.iv,
);
if (result.isType<MalformedCiphertextError>()) {
return Result(
result.get<MalformedCiphertextError>(),
);
}
return Result(
utf8.decode(
await aes256CbcDecrypt(
ciphertext,
derivedKeys.encryptionKey,
derivedKeys.iv,
),
result.get<List<int>>(),
),
);
}
@ -316,8 +322,6 @@ class OmemoManager {
kexMessage.ek,
KeyPairType.x25519,
);
// TODO: Guard against invalid signatures
final kex = await x3dhFromInitialMessage(
X3DHMessage(
kexIk,