???: Move code around
This commit is contained in:
109
lib/src/double_ratchet/crypto.dart
Normal file
109
lib/src/double_ratchet/crypto.dart
Normal file
@@ -0,0 +1,109 @@
|
||||
import 'dart:convert';
|
||||
import 'package:cryptography/cryptography.dart';
|
||||
import 'package:omemo_dart/protobuf/schema.pb.dart';
|
||||
import 'package:omemo_dart/src/helpers.dart';
|
||||
|
||||
/// Info string for ENCRYPT
|
||||
const encryptHkdfInfoString = 'OMEMO Message Key Material';
|
||||
|
||||
/// 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 {}
|
||||
}
|
||||
|
||||
/// Signals ENCRYPT function as specified by OMEMO 0.8.0.
|
||||
/// Encrypt [plaintext] using the message key [mk], given associated_data [associatedData]
|
||||
/// and the AD output from the X3DH [sessionAd].
|
||||
Future<List<int>> encrypt(List<int> mk, List<int> plaintext, List<int> associatedData, List<int> sessionAd) async {
|
||||
final hkdf = Hkdf(
|
||||
hmac: Hmac(Sha256()),
|
||||
outputLength: 80,
|
||||
);
|
||||
final hkdfResult = await hkdf.deriveKey(
|
||||
secretKey: SecretKey(mk),
|
||||
nonce: List<int>.filled(32, 0x0),
|
||||
info: utf8.encode(encryptHkdfInfoString),
|
||||
);
|
||||
final hkdfBytes = await hkdfResult.extractBytes();
|
||||
|
||||
// Split hkdfBytes into encryption, authentication key and IV
|
||||
final encryptionKey = hkdfBytes.sublist(0, 32);
|
||||
final authenticationKey = hkdfBytes.sublist(32, 64);
|
||||
final iv = hkdfBytes.sublist(64, 80);
|
||||
|
||||
final aesResult = await AesCbc.with256bits(
|
||||
macAlgorithm: MacAlgorithm.empty,
|
||||
).encrypt(
|
||||
plaintext,
|
||||
secretKey: SecretKey(encryptionKey),
|
||||
nonce: iv,
|
||||
);
|
||||
|
||||
final header = OMEMOMessage.fromBuffer(associatedData.sublist(sessionAd.length))
|
||||
..ciphertext = aesResult.cipherText;
|
||||
final headerBytes = header.writeToBuffer();
|
||||
final hmacInput = concat([sessionAd, headerBytes]);
|
||||
final hmacResult = (await Hmac.sha256().calculateMac(
|
||||
hmacInput,
|
||||
secretKey: SecretKey(authenticationKey),
|
||||
)).bytes.sublist(0, 16);
|
||||
|
||||
final message = OMEMOAuthenticatedMessage()
|
||||
..mac = hmacResult
|
||||
..message = headerBytes;
|
||||
return message.writeToBuffer();
|
||||
}
|
||||
|
||||
/// Signals DECRYPT function as specified by OMEMO 0.8.0.
|
||||
/// Decrypt [ciphertext] with the message key [mk], given the associated_data [associatedData]
|
||||
/// and the AD output from the X3DH.
|
||||
Future<List<int>> decrypt(List<int> mk, List<int> ciphertext, List<int> associatedData, List<int> sessionAd) async {
|
||||
// Generate the keys and iv from mk
|
||||
final hkdf = Hkdf(
|
||||
hmac: Hmac(Sha256()),
|
||||
outputLength: 80,
|
||||
);
|
||||
final hkdfResult = await hkdf.deriveKey(
|
||||
secretKey: SecretKey(mk),
|
||||
nonce: List<int>.filled(32, 0x0),
|
||||
info: utf8.encode(encryptHkdfInfoString),
|
||||
);
|
||||
final hkdfBytes = await hkdfResult.extractBytes();
|
||||
|
||||
// Split hkdfBytes into encryption, authentication key and IV
|
||||
final encryptionKey = hkdfBytes.sublist(0, 32);
|
||||
final authenticationKey = hkdfBytes.sublist(32, 64);
|
||||
final iv = hkdfBytes.sublist(64, 80);
|
||||
|
||||
// Assumption ciphertext is a OMEMOAuthenticatedMessage
|
||||
final message = OMEMOAuthenticatedMessage.fromBuffer(ciphertext);
|
||||
final header = OMEMOMessage.fromBuffer(message.message);
|
||||
|
||||
final hmacInput = concat([sessionAd, header.writeToBuffer()]);
|
||||
final hmacResult = (await Hmac.sha256().calculateMac(
|
||||
hmacInput,
|
||||
secretKey: SecretKey(authenticationKey),
|
||||
)).bytes.sublist(0, 16);
|
||||
|
||||
// TODO(PapaTutuWawa): Check the HMAC result
|
||||
|
||||
final plaintext = await AesCbc.with256bits(
|
||||
macAlgorithm: MacAlgorithm.empty,
|
||||
).decrypt(
|
||||
NoMacSecretBox(
|
||||
header.ciphertext,
|
||||
nonce: iv,
|
||||
),
|
||||
secretKey: SecretKey(encryptionKey),
|
||||
);
|
||||
|
||||
return plaintext;
|
||||
}
|
||||
35
lib/src/double_ratchet/kdf.dart
Normal file
35
lib/src/double_ratchet/kdf.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
import 'dart:convert';
|
||||
import 'package:cryptography/cryptography.dart';
|
||||
|
||||
/// Info string for KDF_RK
|
||||
const kdfRkInfoString = 'OMEMO Root Chain';
|
||||
|
||||
/// Flags for KDF_CK
|
||||
const kdfCkNextMessageKey = 0x01;
|
||||
const kdfCkNextChainKey = 0x02;
|
||||
|
||||
/// Signals KDF_CK function as specified by OMEMO 0.8.0.
|
||||
Future<List<int>> kdfCk(List<int> ck, int constant) async {
|
||||
final hkdf = Hkdf(hmac: Hmac(Sha256()), outputLength: 32);
|
||||
final result = await hkdf.deriveKey(
|
||||
secretKey: SecretKey(ck),
|
||||
nonce: [constant],
|
||||
);
|
||||
|
||||
return result.extractBytes();
|
||||
}
|
||||
|
||||
/// Signals KDF_RK function as specified by OMEMO 0.8.0.
|
||||
Future<List<int>> kdfRk(List<int> rk, List<int> dhOut) async {
|
||||
final algorithm = Hkdf(
|
||||
hmac: Hmac(Sha256()),
|
||||
outputLength: 32,
|
||||
);
|
||||
final result = await algorithm.deriveKey(
|
||||
secretKey: SecretKey(dhOut),
|
||||
nonce: rk,
|
||||
info: utf8.encode(kdfRkInfoString),
|
||||
);
|
||||
|
||||
return result.extractBytes();
|
||||
}
|
||||
Reference in New Issue
Block a user