feat: Guard against malformed ciphertext
This commit is contained in:
parent
f1ec8d1793
commit
6c301ab88f
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user