feat: Add a untest decrypt function
This commit is contained in:
parent
e34e0cc7fb
commit
4d6dbef549
@ -32,6 +32,19 @@ class HkdfKeyResult {
|
||||
final List<int> iv;
|
||||
}
|
||||
|
||||
/// cryptography _really_ wants to check the MAC output from AES-256-CBC. Since
|
||||
/// we don't have it, we need the MAC check to always "pass".
|
||||
class NoMacSecretBox extends SecretBox {
|
||||
NoMacSecretBox(super.cipherText, { required super.nonce }) : super(mac: Mac.empty);
|
||||
|
||||
@override
|
||||
Future<void> checkMac({
|
||||
required MacAlgorithm macAlgorithm,
|
||||
required SecretKey secretKey,
|
||||
required List<int> aad,
|
||||
}) async {}
|
||||
}
|
||||
|
||||
/// OMEMO 0.8.3 often derives the three keys for encryption, authentication and the IV from
|
||||
/// some input using HKDF-SHA-256. As such, this is a helper function that already provides
|
||||
/// those three keys from [input] and the info string [info].
|
||||
@ -65,6 +78,21 @@ Future<List<int>> aes256CbcEncrypt(List<int> plaintext, List<int> key, List<int>
|
||||
return result.cipherText;
|
||||
}
|
||||
|
||||
/// 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(List<int> ciphertext, List<int> key, List<int> iv) async {
|
||||
final algorithm = AesCbc.with256bits(
|
||||
macAlgorithm: MacAlgorithm.empty,
|
||||
);
|
||||
return algorithm.decrypt(
|
||||
NoMacSecretBox(
|
||||
ciphertext,
|
||||
nonce: iv,
|
||||
),
|
||||
secretKey: SecretKey(key),
|
||||
);
|
||||
}
|
||||
|
||||
/// OMEMO often uses the output of a HMAC-SHA-256 truncated to its first 16 bytes.
|
||||
/// Calculate the HMAC-SHA-256 of [input] using the authentication key [key] and
|
||||
/// truncate the output to 16 bytes.
|
||||
|
@ -3,7 +3,8 @@ class InvalidSignatureException implements Exception {
|
||||
String errMsg() => 'The signature of the SPK does not match the provided signature';
|
||||
}
|
||||
|
||||
/// Triggered by the Double Ratchet if the computet HMAC does not match the attached HMAC.
|
||||
/// Triggered by the Double Ratchet if the computed HMAC does not match the attached HMAC.
|
||||
/// Triggered by the Session Manager if the computed HMAC does not match the attached HMAC.
|
||||
class InvalidMessageHMACException implements Exception {
|
||||
String errMsg() => 'The computed HMAC does not match the provided HMAC';
|
||||
}
|
||||
@ -13,3 +14,13 @@ class InvalidMessageHMACException implements Exception {
|
||||
class SkippingTooManyMessagesException implements Exception {
|
||||
String errMsg() => 'Skipping messages would cause a skip bigger than MAXSKIP';
|
||||
}
|
||||
|
||||
/// Triggered by the Session Manager if the message key is not encrypted for the device.
|
||||
class NotEncryptedForDeviceException implements Exception {
|
||||
String errMsg() => 'Not encrypted for this device';
|
||||
}
|
||||
|
||||
/// Triggered by the Session Manager when there is no key for decrypting the message.
|
||||
class NoDecryptionKeyException implements Exception {
|
||||
String errMsg() => 'No key available for decrypting the message';
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
import 'dart:convert';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:omemo_dart/protobuf/schema.pb.dart';
|
||||
import 'package:omemo_dart/src/crypto.dart';
|
||||
import 'package:omemo_dart/src/double_ratchet/double_ratchet.dart';
|
||||
import 'package:omemo_dart/src/errors.dart';
|
||||
import 'package:omemo_dart/src/helpers.dart';
|
||||
import 'package:omemo_dart/src/omemo/device.dart';
|
||||
import 'package:synchronized/synchronized.dart';
|
||||
@ -20,6 +23,13 @@ class EncryptionResult {
|
||||
final Map<String, List<int>> encryptedKeys;
|
||||
}
|
||||
|
||||
class EncryptedKey {
|
||||
|
||||
const EncryptedKey(this.rid, this.value);
|
||||
final String rid;
|
||||
final String value;
|
||||
}
|
||||
|
||||
class OmemoSessionManager {
|
||||
|
||||
OmemoSessionManager(this.device) : _ratchetMap = {}, _deviceMap = {}, _lock = Lock();
|
||||
@ -92,4 +102,42 @@ class OmemoSessionManager {
|
||||
encryptedKeys,
|
||||
);
|
||||
}
|
||||
|
||||
/// Attempt to decrypt [ciphertext]. [keys] refers to the <key /> elements inside the
|
||||
/// <keys /> element with a "jid" attribute matching our own. [senderJid] refers to the
|
||||
/// bare Jid of the sender. [senderDeviceId] refers to the "sid" attribute of the
|
||||
/// <encrypted /> element.
|
||||
Future<String> decryptMessage(List<int> ciphertext, String senderJid, String senderDeviceId, List<EncryptedKey> keys) async {
|
||||
// Try to find a session we can decrypt with.
|
||||
final rawKey = keys.firstWhereOrNull((key) => key.rid == device.id);
|
||||
if (rawKey == null) {
|
||||
throw NotEncryptedForDeviceException();
|
||||
}
|
||||
|
||||
final devices = _deviceMap[senderJid];
|
||||
if (devices == null) {
|
||||
throw NoDecryptionKeyException();
|
||||
}
|
||||
if (!devices.contains(senderDeviceId)) {
|
||||
throw NoDecryptionKeyException();
|
||||
}
|
||||
|
||||
final decodedRawKey = base64.decode(rawKey.value);
|
||||
final authMessage = OMEMOAuthenticatedMessage.fromBuffer(decodedRawKey);
|
||||
final message = OMEMOMessage.fromBuffer(authMessage.message);
|
||||
|
||||
final ratchet = _ratchetMap[senderDeviceId]!;
|
||||
final keyAndHmac = await ratchet.ratchetDecrypt(message, message.ciphertext);
|
||||
final key = keyAndHmac.sublist(0, 32);
|
||||
final hmac = keyAndHmac.sublist(32, 48);
|
||||
final derivedKeys = await deriveEncryptionKeys(key, omemoPayloadInfoString);
|
||||
|
||||
final computedHmac = await truncatedHmac(ciphertext, derivedKeys.authenticationKey);
|
||||
if (!listsEqual(hmac, computedHmac)) {
|
||||
throw InvalidMessageHMACException();
|
||||
}
|
||||
|
||||
final plaintext = await aes256CbcDecrypt(ciphertext, derivedKeys.encryptionKey, derivedKeys.iv);
|
||||
return utf8.decode(plaintext);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ environment:
|
||||
sdk: '>=2.17.0 <3.0.0'
|
||||
|
||||
dependencies:
|
||||
collection: ^1.16.0
|
||||
cryptography: ^2.0.5
|
||||
pinenacl: ^0.5.1
|
||||
protobuf: ^2.1.0
|
||||
|
Loading…
Reference in New Issue
Block a user