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;
|
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
|
/// 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
|
/// 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].
|
/// 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;
|
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.
|
/// 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
|
/// Calculate the HMAC-SHA-256 of [input] using the authentication key [key] and
|
||||||
/// truncate the output to 16 bytes.
|
/// 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';
|
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 {
|
class InvalidMessageHMACException implements Exception {
|
||||||
String errMsg() => 'The computed HMAC does not match the provided HMAC';
|
String errMsg() => 'The computed HMAC does not match the provided HMAC';
|
||||||
}
|
}
|
||||||
@ -13,3 +14,13 @@ class InvalidMessageHMACException implements Exception {
|
|||||||
class SkippingTooManyMessagesException implements Exception {
|
class SkippingTooManyMessagesException implements Exception {
|
||||||
String errMsg() => 'Skipping messages would cause a skip bigger than MAXSKIP';
|
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 '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/crypto.dart';
|
||||||
import 'package:omemo_dart/src/double_ratchet/double_ratchet.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/helpers.dart';
|
||||||
import 'package:omemo_dart/src/omemo/device.dart';
|
import 'package:omemo_dart/src/omemo/device.dart';
|
||||||
import 'package:synchronized/synchronized.dart';
|
import 'package:synchronized/synchronized.dart';
|
||||||
@ -20,6 +23,13 @@ class EncryptionResult {
|
|||||||
final Map<String, List<int>> encryptedKeys;
|
final Map<String, List<int>> encryptedKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EncryptedKey {
|
||||||
|
|
||||||
|
const EncryptedKey(this.rid, this.value);
|
||||||
|
final String rid;
|
||||||
|
final String value;
|
||||||
|
}
|
||||||
|
|
||||||
class OmemoSessionManager {
|
class OmemoSessionManager {
|
||||||
|
|
||||||
OmemoSessionManager(this.device) : _ratchetMap = {}, _deviceMap = {}, _lock = Lock();
|
OmemoSessionManager(this.device) : _ratchetMap = {}, _deviceMap = {}, _lock = Lock();
|
||||||
@ -92,4 +102,42 @@ class OmemoSessionManager {
|
|||||||
encryptedKeys,
|
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'
|
sdk: '>=2.17.0 <3.0.0'
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
|
collection: ^1.16.0
|
||||||
cryptography: ^2.0.5
|
cryptography: ^2.0.5
|
||||||
pinenacl: ^0.5.1
|
pinenacl: ^0.5.1
|
||||||
protobuf: ^2.1.0
|
protobuf: ^2.1.0
|
||||||
|
Loading…
Reference in New Issue
Block a user