From 6c301ab88fa1fad8a1c303af2703645535884f2b Mon Sep 17 00:00:00 2001 From: "Alexander \"PapaTutuWawa" Date: Thu, 15 Jun 2023 16:42:17 +0200 Subject: [PATCH] feat: Guard against malformed ciphertext --- lib/src/common/result.dart | 2 +- lib/src/crypto.dart | 24 ++++++++++++++-------- lib/src/double_ratchet/double_ratchet.dart | 6 +++++- lib/src/errors.dart | 20 ++++++++---------- lib/src/omemo/omemo.dart | 20 ++++++++++-------- 5 files changed, 42 insertions(+), 30 deletions(-) diff --git a/lib/src/common/result.dart b/lib/src/common/result.dart index 60d4808..911b7dd 100644 --- a/lib/src/common/result.dart +++ b/lib/src/common/result.dart @@ -3,7 +3,7 @@ class Result { 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; diff --git a/lib/src/crypto.dart b/lib/src/crypto.dart index ce421c2..4933615 100644 --- a/lib/src/crypto.dart +++ b/lib/src/crypto.dart @@ -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> 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> aes256CbcDecrypt( +Future>> aes256CbcDecrypt( List ciphertext, List key, List iv, @@ -100,13 +102,19 @@ Future> 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. diff --git a/lib/src/double_ratchet/double_ratchet.dart b/lib/src/double_ratchet/double_ratchet.dart index 78b7bbd..122aa7c 100644 --- a/lib/src/double_ratchet/double_ratchet.dart +++ b/lib/src/double_ratchet/double_ratchet.dart @@ -261,7 +261,11 @@ class OmemoDoubleRatchet { } final plaintext = await aes256CbcDecrypt(ciphertext, keys.encryptionKey, keys.iv); - return Result(plaintext); + if (plaintext.isType()) { + return Result(plaintext.get()); + } + + return Result(plaintext.get>()); } /// Checks whether we could decrypt the payload in [header] with a skipped key. If yes, diff --git a/lib/src/errors.dart b/lib/src/errors.dart index 858a618..8e7aecf 100644 --- a/lib/src/errors.dart +++ b/lib/src/errors.dart @@ -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; +} diff --git a/lib/src/omemo/omemo.dart b/lib/src/omemo/omemo.dart index 060a683..2c6a54e 100644 --- a/lib/src/omemo/omemo.dart +++ b/lib/src/omemo/omemo.dart @@ -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()) { + return Result( + result.get(), + ); + } + return Result( utf8.decode( - await aes256CbcDecrypt( - ciphertext, - derivedKeys.encryptionKey, - derivedKeys.iv, - ), + result.get>(), ), ); } @@ -316,8 +322,6 @@ class OmemoManager { kexMessage.ek, KeyPairType.x25519, ); - - // TODO: Guard against invalid signatures final kex = await x3dhFromInitialMessage( X3DHMessage( kexIk,