style: Formattiing issues
This commit is contained in:
parent
3783ec6f13
commit
3376929c24
@ -7,7 +7,10 @@ import 'package:omemo_dart/src/keys.dart';
|
|||||||
/// is the identity key. This is needed since the identity key pair/public key is
|
/// is the identity key. This is needed since the identity key pair/public key is
|
||||||
/// an Ed25519 key, but we need them as X25519 keys for DH.
|
/// an Ed25519 key, but we need them as X25519 keys for DH.
|
||||||
Future<List<int>> omemoDH(
|
Future<List<int>> omemoDH(
|
||||||
OmemoKeyPair kp, OmemoPublicKey pk, int identityKey,) async {
|
OmemoKeyPair kp,
|
||||||
|
OmemoPublicKey pk,
|
||||||
|
int identityKey,
|
||||||
|
) async {
|
||||||
var ckp = kp;
|
var ckp = kp;
|
||||||
var cpk = pk;
|
var cpk = pk;
|
||||||
|
|
||||||
@ -62,13 +65,19 @@ Future<HkdfKeyResult> deriveEncryptionKeys(List<int> input, String info) async {
|
|||||||
final bytes = await result.extractBytes();
|
final bytes = await result.extractBytes();
|
||||||
|
|
||||||
return HkdfKeyResult(
|
return HkdfKeyResult(
|
||||||
bytes.sublist(0, 32), bytes.sublist(32, 64), bytes.sublist(64, 80),);
|
bytes.sublist(0, 32),
|
||||||
|
bytes.sublist(32, 64),
|
||||||
|
bytes.sublist(64, 80),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A small helper function to make AES-256-CBC easier. Encrypt [plaintext] using [key] as
|
/// A small helper function to make AES-256-CBC easier. Encrypt [plaintext] using [key] as
|
||||||
/// the encryption key and [iv] as the IV. Returns the ciphertext.
|
/// the encryption key and [iv] as the IV. Returns the ciphertext.
|
||||||
Future<List<int>> aes256CbcEncrypt(
|
Future<List<int>> aes256CbcEncrypt(
|
||||||
List<int> plaintext, List<int> key, List<int> iv,) async {
|
List<int> plaintext,
|
||||||
|
List<int> key,
|
||||||
|
List<int> iv,
|
||||||
|
) async {
|
||||||
final algorithm = AesCbc.with256bits(
|
final algorithm = AesCbc.with256bits(
|
||||||
macAlgorithm: MacAlgorithm.empty,
|
macAlgorithm: MacAlgorithm.empty,
|
||||||
);
|
);
|
||||||
@ -84,7 +93,10 @@ Future<List<int>> aes256CbcEncrypt(
|
|||||||
/// A small helper function to make AES-256-CBC easier. Decrypt [ciphertext] using [key] as
|
/// 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.
|
/// the encryption key and [iv] as the IV. Returns the ciphertext.
|
||||||
Future<List<int>> aes256CbcDecrypt(
|
Future<List<int>> aes256CbcDecrypt(
|
||||||
List<int> ciphertext, List<int> key, List<int> iv,) async {
|
List<int> ciphertext,
|
||||||
|
List<int> key,
|
||||||
|
List<int> iv,
|
||||||
|
) async {
|
||||||
final algorithm = AesCbc.with256bits(
|
final algorithm = AesCbc.with256bits(
|
||||||
macAlgorithm: MacAlgorithm.empty,
|
macAlgorithm: MacAlgorithm.empty,
|
||||||
);
|
);
|
||||||
|
@ -10,8 +10,12 @@ const encryptHkdfInfoString = 'OMEMO Message Key Material';
|
|||||||
/// Signals ENCRYPT function as specified by OMEMO 0.8.3.
|
/// Signals ENCRYPT function as specified by OMEMO 0.8.3.
|
||||||
/// Encrypt [plaintext] using the message key [mk], given associated_data [associatedData]
|
/// Encrypt [plaintext] using the message key [mk], given associated_data [associatedData]
|
||||||
/// and the AD output from the X3DH [sessionAd].
|
/// and the AD output from the X3DH [sessionAd].
|
||||||
Future<List<int>> encrypt(List<int> mk, List<int> plaintext,
|
Future<List<int>> encrypt(
|
||||||
List<int> associatedData, List<int> sessionAd,) async {
|
List<int> mk,
|
||||||
|
List<int> plaintext,
|
||||||
|
List<int> associatedData,
|
||||||
|
List<int> sessionAd,
|
||||||
|
) async {
|
||||||
// Generate encryption, authentication key and IV
|
// Generate encryption, authentication key and IV
|
||||||
final keys = await deriveEncryptionKeys(mk, encryptHkdfInfoString);
|
final keys = await deriveEncryptionKeys(mk, encryptHkdfInfoString);
|
||||||
final ciphertext =
|
final ciphertext =
|
||||||
@ -32,8 +36,12 @@ Future<List<int>> encrypt(List<int> mk, List<int> plaintext,
|
|||||||
/// Signals DECRYPT function as specified by OMEMO 0.8.3.
|
/// Signals DECRYPT function as specified by OMEMO 0.8.3.
|
||||||
/// Decrypt [ciphertext] with the message key [mk], given the associated_data [associatedData]
|
/// Decrypt [ciphertext] with the message key [mk], given the associated_data [associatedData]
|
||||||
/// and the AD output from the X3DH.
|
/// and the AD output from the X3DH.
|
||||||
Future<List<int>> decrypt(List<int> mk, List<int> ciphertext,
|
Future<List<int>> decrypt(
|
||||||
List<int> associatedData, List<int> sessionAd,) async {
|
List<int> mk,
|
||||||
|
List<int> ciphertext,
|
||||||
|
List<int> associatedData,
|
||||||
|
List<int> sessionAd,
|
||||||
|
) async {
|
||||||
// Generate encryption, authentication key and IV
|
// Generate encryption, authentication key and IV
|
||||||
final keys = await deriveEncryptionKeys(mk, encryptHkdfInfoString);
|
final keys = await deriveEncryptionKeys(mk, encryptHkdfInfoString);
|
||||||
|
|
||||||
|
@ -179,8 +179,13 @@ class OmemoDoubleRatchet {
|
|||||||
/// Create an OMEMO session using the Signed Pre Key [spk], the shared secret [sk] that
|
/// Create an OMEMO session using the Signed Pre Key [spk], the shared secret [sk] that
|
||||||
/// was obtained using a X3DH and the associated data [ad] that was also obtained through
|
/// was obtained using a X3DH and the associated data [ad] that was also obtained through
|
||||||
/// a X3DH. [ik] refers to Bob's (the receiver's) IK public key.
|
/// a X3DH. [ik] refers to Bob's (the receiver's) IK public key.
|
||||||
static Future<OmemoDoubleRatchet> initiateNewSession(OmemoPublicKey spk,
|
static Future<OmemoDoubleRatchet> initiateNewSession(
|
||||||
OmemoPublicKey ik, List<int> sk, List<int> ad, int timestamp,) async {
|
OmemoPublicKey spk,
|
||||||
|
OmemoPublicKey ik,
|
||||||
|
List<int> sk,
|
||||||
|
List<int> ad,
|
||||||
|
int timestamp,
|
||||||
|
) async {
|
||||||
final dhs = await OmemoKeyPair.generateNewPair(KeyPairType.x25519);
|
final dhs = await OmemoKeyPair.generateNewPair(KeyPairType.x25519);
|
||||||
final dhr = spk;
|
final dhr = spk;
|
||||||
final rk = await kdfRk(sk, await omemoDH(dhs, dhr, 0));
|
final rk = await kdfRk(sk, await omemoDH(dhs, dhr, 0));
|
||||||
@ -208,8 +213,13 @@ class OmemoDoubleRatchet {
|
|||||||
/// Pre Key keypair [spk], the shared secret [sk] that was obtained through a X3DH and
|
/// Pre Key keypair [spk], the shared secret [sk] that was obtained through a X3DH and
|
||||||
/// the associated data [ad] that was also obtained through a X3DH. [ik] refers to
|
/// the associated data [ad] that was also obtained through a X3DH. [ik] refers to
|
||||||
/// Alice's (the initiator's) IK public key.
|
/// Alice's (the initiator's) IK public key.
|
||||||
static Future<OmemoDoubleRatchet> acceptNewSession(OmemoKeyPair spk,
|
static Future<OmemoDoubleRatchet> acceptNewSession(
|
||||||
OmemoPublicKey ik, List<int> sk, List<int> ad, int kexTimestamp,) async {
|
OmemoKeyPair spk,
|
||||||
|
OmemoPublicKey ik,
|
||||||
|
List<int> sk,
|
||||||
|
List<int> ad,
|
||||||
|
int kexTimestamp,
|
||||||
|
) async {
|
||||||
return OmemoDoubleRatchet(
|
return OmemoDoubleRatchet(
|
||||||
spk,
|
spk,
|
||||||
null,
|
null,
|
||||||
@ -264,7 +274,9 @@ class OmemoDoubleRatchet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<List<int>?> _trySkippedMessageKeys(
|
Future<List<int>?> _trySkippedMessageKeys(
|
||||||
OmemoMessage header, List<int> ciphertext,) async {
|
OmemoMessage header,
|
||||||
|
List<int> ciphertext,
|
||||||
|
) async {
|
||||||
final key = SkippedKey(
|
final key = SkippedKey(
|
||||||
OmemoPublicKey.fromBytes(header.dhPub!, KeyPairType.x25519),
|
OmemoPublicKey.fromBytes(header.dhPub!, KeyPairType.x25519),
|
||||||
header.n!,
|
header.n!,
|
||||||
@ -273,8 +285,12 @@ class OmemoDoubleRatchet {
|
|||||||
final mk = mkSkipped[key]!;
|
final mk = mkSkipped[key]!;
|
||||||
mkSkipped.remove(key);
|
mkSkipped.remove(key);
|
||||||
|
|
||||||
return decrypt(mk, ciphertext,
|
return decrypt(
|
||||||
concat([sessionAd, header.writeToBuffer()]), sessionAd,);
|
mk,
|
||||||
|
ciphertext,
|
||||||
|
concat([sessionAd, header.writeToBuffer()]),
|
||||||
|
sessionAd,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -326,8 +342,12 @@ class OmemoDoubleRatchet {
|
|||||||
|
|
||||||
return RatchetStep(
|
return RatchetStep(
|
||||||
header,
|
header,
|
||||||
await encrypt(mk, plaintext, concat([sessionAd, header.writeToBuffer()]),
|
await encrypt(
|
||||||
sessionAd,),
|
mk,
|
||||||
|
plaintext,
|
||||||
|
concat([sessionAd, header.writeToBuffer()]),
|
||||||
|
sessionAd,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,7 +356,9 @@ class OmemoDoubleRatchet {
|
|||||||
///
|
///
|
||||||
/// Throws an SkippingTooManyMessagesException if too many messages were to be skipped.
|
/// Throws an SkippingTooManyMessagesException if too many messages were to be skipped.
|
||||||
Future<List<int>> ratchetDecrypt(
|
Future<List<int>> ratchetDecrypt(
|
||||||
OmemoMessage header, List<int> ciphertext,) async {
|
OmemoMessage header,
|
||||||
|
List<int> ciphertext,
|
||||||
|
) async {
|
||||||
// Check if we skipped too many messages
|
// Check if we skipped too many messages
|
||||||
final plaintext = await _trySkippedMessageKeys(header, ciphertext);
|
final plaintext = await _trySkippedMessageKeys(header, ciphertext);
|
||||||
if (plaintext != null) {
|
if (plaintext != null) {
|
||||||
@ -359,7 +381,11 @@ class OmemoDoubleRatchet {
|
|||||||
nr++;
|
nr++;
|
||||||
|
|
||||||
return decrypt(
|
return decrypt(
|
||||||
mk, ciphertext, concat([sessionAd, header.writeToBuffer()]), sessionAd,);
|
mk,
|
||||||
|
ciphertext,
|
||||||
|
concat([sessionAd, header.writeToBuffer()]),
|
||||||
|
sessionAd,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
OmemoDoubleRatchet clone() {
|
OmemoDoubleRatchet clone() {
|
||||||
|
@ -44,7 +44,10 @@ int generateRandom32BitNumber() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
OmemoPublicKey? decodeKeyIfNotNull(
|
OmemoPublicKey? decodeKeyIfNotNull(
|
||||||
Map<String, dynamic> map, String key, KeyPairType type,) {
|
Map<String, dynamic> map,
|
||||||
|
String key,
|
||||||
|
KeyPairType type,
|
||||||
|
) {
|
||||||
if (map[key] == null) return null;
|
if (map[key] == null) return null;
|
||||||
|
|
||||||
return OmemoPublicKey.fromBytes(
|
return OmemoPublicKey.fromBytes(
|
||||||
|
@ -31,8 +31,10 @@ class OmemoPublicKey {
|
|||||||
Future<String> asBase64() async => base64Encode(_pubkey.bytes);
|
Future<String> asBase64() async => base64Encode(_pubkey.bytes);
|
||||||
|
|
||||||
Future<OmemoPublicKey> toCurve25519() async {
|
Future<OmemoPublicKey> toCurve25519() async {
|
||||||
assert(type == KeyPairType.ed25519,
|
assert(
|
||||||
'Cannot convert non-Ed25519 public key to X25519',);
|
type == KeyPairType.ed25519,
|
||||||
|
'Cannot convert non-Ed25519 public key to X25519',
|
||||||
|
);
|
||||||
|
|
||||||
final pkc = Uint8List(publicKeyLength);
|
final pkc = Uint8List(publicKeyLength);
|
||||||
TweetNaClExt.crypto_sign_ed25519_pk_to_x25519_pk(
|
TweetNaClExt.crypto_sign_ed25519_pk_to_x25519_pk(
|
||||||
@ -41,7 +43,8 @@ class OmemoPublicKey {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return OmemoPublicKey(
|
return OmemoPublicKey(
|
||||||
SimplePublicKey(List<int>.from(pkc), type: KeyPairType.x25519),);
|
SimplePublicKey(List<int>.from(pkc), type: KeyPairType.x25519),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
SimplePublicKey asPublicKey() => _pubkey;
|
SimplePublicKey asPublicKey() => _pubkey;
|
||||||
@ -64,8 +67,10 @@ class OmemoPrivateKey {
|
|||||||
Future<List<int>> getBytes() async => _privkey;
|
Future<List<int>> getBytes() async => _privkey;
|
||||||
|
|
||||||
Future<OmemoPrivateKey> toCurve25519() async {
|
Future<OmemoPrivateKey> toCurve25519() async {
|
||||||
assert(type == KeyPairType.ed25519,
|
assert(
|
||||||
'Cannot convert non-Ed25519 private key to X25519',);
|
type == KeyPairType.ed25519,
|
||||||
|
'Cannot convert non-Ed25519 private key to X25519',
|
||||||
|
);
|
||||||
|
|
||||||
final skc = Uint8List(privateKeyLength);
|
final skc = Uint8List(privateKeyLength);
|
||||||
TweetNaClExt.crypto_sign_ed25519_sk_to_x25519_sk(
|
TweetNaClExt.crypto_sign_ed25519_sk_to_x25519_sk(
|
||||||
@ -93,7 +98,10 @@ class OmemoKeyPair {
|
|||||||
/// Create an OmemoKeyPair just from a [type] and the bytes of the private and public
|
/// Create an OmemoKeyPair just from a [type] and the bytes of the private and public
|
||||||
/// key.
|
/// key.
|
||||||
factory OmemoKeyPair.fromBytes(
|
factory OmemoKeyPair.fromBytes(
|
||||||
List<int> publicKey, List<int> privateKey, KeyPairType type,) {
|
List<int> publicKey,
|
||||||
|
List<int> privateKey,
|
||||||
|
KeyPairType type,
|
||||||
|
) {
|
||||||
return OmemoKeyPair(
|
return OmemoKeyPair(
|
||||||
OmemoPublicKey.fromBytes(
|
OmemoPublicKey.fromBytes(
|
||||||
publicKey,
|
publicKey,
|
||||||
@ -110,8 +118,10 @@ class OmemoKeyPair {
|
|||||||
/// Generate a completely new random OmemoKeyPair of type [type]. [type] must be either
|
/// Generate a completely new random OmemoKeyPair of type [type]. [type] must be either
|
||||||
/// KeyPairType.ed25519 or KeyPairType.x25519.
|
/// KeyPairType.ed25519 or KeyPairType.x25519.
|
||||||
static Future<OmemoKeyPair> generateNewPair(KeyPairType type) async {
|
static Future<OmemoKeyPair> generateNewPair(KeyPairType type) async {
|
||||||
assert(type == KeyPairType.ed25519 || type == KeyPairType.x25519,
|
assert(
|
||||||
'Keypair must be either Ed25519 or X25519',);
|
type == KeyPairType.ed25519 || type == KeyPairType.x25519,
|
||||||
|
'Keypair must be either Ed25519 or X25519',
|
||||||
|
);
|
||||||
|
|
||||||
SimpleKeyPair kp;
|
SimpleKeyPair kp;
|
||||||
if (type == KeyPairType.ed25519) {
|
if (type == KeyPairType.ed25519) {
|
||||||
@ -140,8 +150,10 @@ class OmemoKeyPair {
|
|||||||
|
|
||||||
/// Return the bytes that comprise the public key.
|
/// Return the bytes that comprise the public key.
|
||||||
Future<OmemoKeyPair> toCurve25519() async {
|
Future<OmemoKeyPair> toCurve25519() async {
|
||||||
assert(type == KeyPairType.ed25519,
|
assert(
|
||||||
'Cannot convert non-Ed25519 keypair to X25519',);
|
type == KeyPairType.ed25519,
|
||||||
|
'Cannot convert non-Ed25519 keypair to X25519',
|
||||||
|
);
|
||||||
|
|
||||||
return OmemoKeyPair(
|
return OmemoKeyPair(
|
||||||
await pk.toCurve25519(),
|
await pk.toCurve25519(),
|
||||||
|
@ -93,8 +93,10 @@ class OmemoDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a completely new device, i.e. cryptographic identity.
|
/// Generate a completely new device, i.e. cryptographic identity.
|
||||||
static Future<OmemoDevice> generateNewDevice(String jid,
|
static Future<OmemoDevice> generateNewDevice(
|
||||||
{int opkAmount = 100,}) async {
|
String jid, {
|
||||||
|
int opkAmount = 100,
|
||||||
|
}) async {
|
||||||
final id = generateRandom32BitNumber();
|
final id = generateRandom32BitNumber();
|
||||||
final ik = await OmemoKeyPair.generateNewPair(KeyPairType.ed25519);
|
final ik = await OmemoKeyPair.generateNewPair(KeyPairType.ed25519);
|
||||||
final spk = await OmemoKeyPair.generateNewPair(KeyPairType.x25519);
|
final spk = await OmemoKeyPair.generateNewPair(KeyPairType.x25519);
|
||||||
|
@ -5,8 +5,12 @@ import 'package:omemo_dart/src/omemo/ratchet_map_key.dart';
|
|||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class EncryptionResult {
|
class EncryptionResult {
|
||||||
const EncryptionResult(this.ciphertext, this.encryptedKeys,
|
const EncryptionResult(
|
||||||
this.deviceEncryptionErrors, this.jidEncryptionErrors,);
|
this.ciphertext,
|
||||||
|
this.encryptedKeys,
|
||||||
|
this.deviceEncryptionErrors,
|
||||||
|
this.jidEncryptionErrors,
|
||||||
|
);
|
||||||
|
|
||||||
/// The actual message that was encrypted.
|
/// The actual message that was encrypted.
|
||||||
final List<int>? ciphertext;
|
final List<int>? ciphertext;
|
||||||
|
@ -6,7 +6,12 @@ abstract class OmemoEvent {}
|
|||||||
/// Triggered when a ratchet has been modified
|
/// Triggered when a ratchet has been modified
|
||||||
class RatchetModifiedEvent extends OmemoEvent {
|
class RatchetModifiedEvent extends OmemoEvent {
|
||||||
RatchetModifiedEvent(
|
RatchetModifiedEvent(
|
||||||
this.jid, this.deviceId, this.ratchet, this.added, this.replaced,);
|
this.jid,
|
||||||
|
this.deviceId,
|
||||||
|
this.ratchet,
|
||||||
|
this.added,
|
||||||
|
this.replaced,
|
||||||
|
);
|
||||||
final String jid;
|
final String jid;
|
||||||
final int deviceId;
|
final int deviceId;
|
||||||
final OmemoDoubleRatchet ratchet;
|
final OmemoDoubleRatchet ratchet;
|
||||||
|
@ -33,8 +33,10 @@ class _InternalDecryptionResult {
|
|||||||
this.ratchetCreated,
|
this.ratchetCreated,
|
||||||
this.ratchetReplaced,
|
this.ratchetReplaced,
|
||||||
this.payload,
|
this.payload,
|
||||||
) : assert(!ratchetCreated || !ratchetReplaced,
|
) : assert(
|
||||||
'Ratchet must be either replaced or created',);
|
!ratchetCreated || !ratchetReplaced,
|
||||||
|
'Ratchet must be either replaced or created',
|
||||||
|
);
|
||||||
final bool ratchetCreated;
|
final bool ratchetCreated;
|
||||||
final bool ratchetReplaced;
|
final bool ratchetReplaced;
|
||||||
final String? payload;
|
final String? payload;
|
||||||
@ -132,7 +134,9 @@ class OmemoManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> _decryptAndVerifyHmac(
|
Future<String?> _decryptAndVerifyHmac(
|
||||||
List<int>? ciphertext, List<int> keyAndHmac,) async {
|
List<int>? ciphertext,
|
||||||
|
List<int> keyAndHmac,
|
||||||
|
) async {
|
||||||
// Empty OMEMO messages should just have the key decrypted and/or session set up.
|
// Empty OMEMO messages should just have the key decrypted and/or session set up.
|
||||||
if (ciphertext == null) {
|
if (ciphertext == null) {
|
||||||
return null;
|
return null;
|
||||||
@ -149,7 +153,10 @@ class OmemoManager {
|
|||||||
|
|
||||||
return utf8.decode(
|
return utf8.decode(
|
||||||
await aes256CbcDecrypt(
|
await aes256CbcDecrypt(
|
||||||
ciphertext, derivedKeys.encryptionKey, derivedKeys.iv,),
|
ciphertext,
|
||||||
|
derivedKeys.encryptionKey,
|
||||||
|
derivedKeys.iv,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +192,10 @@ class OmemoManager {
|
|||||||
/// from the key exchange [kex]. In case [kex] contains an unknown Signed Prekey
|
/// from the key exchange [kex]. In case [kex] contains an unknown Signed Prekey
|
||||||
/// identifier an UnknownSignedPrekeyException will be thrown.
|
/// identifier an UnknownSignedPrekeyException will be thrown.
|
||||||
Future<OmemoDoubleRatchet> _addSessionFromKeyExchange(
|
Future<OmemoDoubleRatchet> _addSessionFromKeyExchange(
|
||||||
String jid, int deviceId, OmemoKeyExchange kex,) async {
|
String jid,
|
||||||
|
int deviceId,
|
||||||
|
OmemoKeyExchange kex,
|
||||||
|
) async {
|
||||||
// Pick the correct SPK
|
// Pick the correct SPK
|
||||||
final device = await getDevice();
|
final device = await getDevice();
|
||||||
OmemoKeyPair spk;
|
OmemoKeyPair spk;
|
||||||
@ -225,7 +235,10 @@ class OmemoManager {
|
|||||||
/// [deviceId] from the bundle [bundle].
|
/// [deviceId] from the bundle [bundle].
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
Future<OmemoKeyExchange> addSessionFromBundle(
|
Future<OmemoKeyExchange> addSessionFromBundle(
|
||||||
String jid, int deviceId, OmemoBundle bundle,) async {
|
String jid,
|
||||||
|
int deviceId,
|
||||||
|
OmemoBundle bundle,
|
||||||
|
) async {
|
||||||
final device = await getDevice();
|
final device = await getDevice();
|
||||||
final kexResult = await x3dhFromBundle(
|
final kexResult = await x3dhFromBundle(
|
||||||
bundle,
|
bundle,
|
||||||
@ -255,7 +268,8 @@ class OmemoManager {
|
|||||||
/// NOTE: Must be called from within the ratchet critical section
|
/// NOTE: Must be called from within the ratchet critical section
|
||||||
void _restoreRatchet(RatchetMapKey mapKey, OmemoDoubleRatchet oldRatchet) {
|
void _restoreRatchet(RatchetMapKey mapKey, OmemoDoubleRatchet oldRatchet) {
|
||||||
_log.finest(
|
_log.finest(
|
||||||
'Restoring ratchet ${mapKey.jid}:${mapKey.deviceId} to ${oldRatchet.nr}',);
|
'Restoring ratchet ${mapKey.jid}:${mapKey.deviceId} to ${oldRatchet.nr}',
|
||||||
|
);
|
||||||
_ratchetMap[mapKey] = oldRatchet;
|
_ratchetMap[mapKey] = oldRatchet;
|
||||||
|
|
||||||
// Commit the ratchet
|
// Commit the ratchet
|
||||||
@ -283,11 +297,12 @@ class OmemoManager {
|
|||||||
/// will return null as there is no message to be decrypted. This, however, is used
|
/// will return null as there is no message to be decrypted. This, however, is used
|
||||||
/// to set up sessions or advance the ratchets.
|
/// to set up sessions or advance the ratchets.
|
||||||
Future<_InternalDecryptionResult> _decryptMessage(
|
Future<_InternalDecryptionResult> _decryptMessage(
|
||||||
List<int>? ciphertext,
|
List<int>? ciphertext,
|
||||||
String senderJid,
|
String senderJid,
|
||||||
int senderDeviceId,
|
int senderDeviceId,
|
||||||
List<EncryptedKey> keys,
|
List<EncryptedKey> keys,
|
||||||
int timestamp,) async {
|
int timestamp,
|
||||||
|
) async {
|
||||||
// Try to find a session we can decrypt with.
|
// Try to find a session we can decrypt with.
|
||||||
var device = await getDevice();
|
var device = await getDevice();
|
||||||
final rawKey = keys.firstWhereOrNull((key) => key.rid == device.id);
|
final rawKey = keys.firstWhereOrNull((key) => key.rid == device.id);
|
||||||
@ -312,7 +327,8 @@ class OmemoManager {
|
|||||||
// Guard against old key exchanges
|
// Guard against old key exchanges
|
||||||
if (oldRatchet != null) {
|
if (oldRatchet != null) {
|
||||||
_log.finest(
|
_log.finest(
|
||||||
'KEX for existent ratchet ${ratchetKey.toJsonKey()}. ${oldRatchet.kexTimestamp} > $timestamp: ${oldRatchet.kexTimestamp > timestamp}',);
|
'KEX for existent ratchet ${ratchetKey.toJsonKey()}. ${oldRatchet.kexTimestamp} > $timestamp: ${oldRatchet.kexTimestamp > timestamp}',
|
||||||
|
);
|
||||||
if (oldRatchet.kexTimestamp > timestamp) {
|
if (oldRatchet.kexTimestamp > timestamp) {
|
||||||
throw InvalidKeyExchangeException();
|
throw InvalidKeyExchangeException();
|
||||||
}
|
}
|
||||||
@ -461,7 +477,9 @@ class OmemoManager {
|
|||||||
/// the result will be null as well.
|
/// the result will be null as well.
|
||||||
/// NOTE: Must be called within the ratchet critical section
|
/// NOTE: Must be called within the ratchet critical section
|
||||||
Future<EncryptionResult> _encryptToJids(
|
Future<EncryptionResult> _encryptToJids(
|
||||||
List<String> jids, String? plaintext,) async {
|
List<String> jids,
|
||||||
|
String? plaintext,
|
||||||
|
) async {
|
||||||
final encryptedKeys = List<EncryptedKey>.empty(growable: true);
|
final encryptedKeys = List<EncryptedKey>.empty(growable: true);
|
||||||
|
|
||||||
var ciphertext = const <int>[];
|
var ciphertext = const <int>[];
|
||||||
@ -564,7 +582,8 @@ class OmemoManager {
|
|||||||
} else {
|
} else {
|
||||||
// The ratchet is not acked but we don't have the old key exchange
|
// The ratchet is not acked but we don't have the old key exchange
|
||||||
_log.warning(
|
_log.warning(
|
||||||
'Ratchet for $jid:$deviceId is not acked but the kex attribute is null',);
|
'Ratchet for $jid:$deviceId is not acked but the kex attribute is null',
|
||||||
|
);
|
||||||
encryptedKeys.add(
|
encryptedKeys.add(
|
||||||
EncryptedKey(
|
EncryptedKey(
|
||||||
jid,
|
jid,
|
||||||
@ -632,7 +651,9 @@ class OmemoManager {
|
|||||||
// Check if the ratchet is acked
|
// Check if the ratchet is acked
|
||||||
final ratchet = getRatchet(ratchetKey);
|
final ratchet = getRatchet(ratchetKey);
|
||||||
assert(
|
assert(
|
||||||
ratchet != null, 'We decrypted the message, so the ratchet must exist',);
|
ratchet != null,
|
||||||
|
'We decrypted the message, so the ratchet must exist',
|
||||||
|
);
|
||||||
|
|
||||||
if (ratchet!.acknowledged) {
|
if (ratchet!.acknowledged) {
|
||||||
// Ratchet is acknowledged
|
// Ratchet is acknowledged
|
||||||
@ -704,8 +725,11 @@ class OmemoManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Mark the ratchet for device [deviceId] from [jid] as acked.
|
/// Mark the ratchet for device [deviceId] from [jid] as acked.
|
||||||
Future<void> ratchetAcknowledged(String jid, int deviceId,
|
Future<void> ratchetAcknowledged(
|
||||||
{bool enterCriticalSection = true,}) async {
|
String jid,
|
||||||
|
int deviceId, {
|
||||||
|
bool enterCriticalSection = true,
|
||||||
|
}) async {
|
||||||
if (enterCriticalSection) await _enterRatchetCriticalSection(jid);
|
if (enterCriticalSection) await _enterRatchetCriticalSection(jid);
|
||||||
|
|
||||||
final key = RatchetMapKey(jid, deviceId);
|
final key = RatchetMapKey(jid, deviceId);
|
||||||
@ -717,7 +741,8 @@ class OmemoManager {
|
|||||||
.add(RatchetModifiedEvent(jid, deviceId, ratchet, false, false));
|
.add(RatchetModifiedEvent(jid, deviceId, ratchet, false, false));
|
||||||
} else {
|
} else {
|
||||||
_log.severe(
|
_log.severe(
|
||||||
'Attempted to acknowledge ratchet ${key.toJsonKey()}, even though it does not exist',);
|
'Attempted to acknowledge ratchet ${key.toJsonKey()}, even though it does not exist',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enterCriticalSection) await _leaveRatchetCriticalSection(jid);
|
if (enterCriticalSection) await _leaveRatchetCriticalSection(jid);
|
||||||
@ -788,8 +813,10 @@ class OmemoManager {
|
|||||||
_eventStreamController.add(DeviceListModifiedEvent(_deviceList));
|
_eventStreamController.add(DeviceListModifiedEvent(_deviceList));
|
||||||
}
|
}
|
||||||
|
|
||||||
void initialize(Map<RatchetMapKey, OmemoDoubleRatchet> ratchetMap,
|
void initialize(
|
||||||
Map<String, List<int>> deviceList,) {
|
Map<RatchetMapKey, OmemoDoubleRatchet> ratchetMap,
|
||||||
|
Map<String, List<int>> deviceList,
|
||||||
|
) {
|
||||||
_deviceList = deviceList;
|
_deviceList = deviceList;
|
||||||
_ratchetMap = ratchetMap;
|
_ratchetMap = ratchetMap;
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,10 @@ abstract class BlindTrustBeforeVerificationTrustManager extends TrustManager {
|
|||||||
|
|
||||||
/// Sets the trust of [jid]'s device with identifier [deviceId] to [state].
|
/// Sets the trust of [jid]'s device with identifier [deviceId] to [state].
|
||||||
Future<void> setDeviceTrust(
|
Future<void> setDeviceTrust(
|
||||||
String jid, int deviceId, BTBVTrustState state,) async {
|
String jid,
|
||||||
|
int deviceId,
|
||||||
|
BTBVTrustState state,
|
||||||
|
) async {
|
||||||
await _lock.synchronized(() async {
|
await _lock.synchronized(() async {
|
||||||
trustCache[RatchetMapKey(jid, deviceId)] = state;
|
trustCache[RatchetMapKey(jid, deviceId)] = state;
|
||||||
|
|
||||||
@ -205,7 +208,8 @@ abstract class BlindTrustBeforeVerificationTrustManager extends TrustManager {
|
|||||||
/// From a serialized version of a BTBV trust manager, extract the trust cache.
|
/// From a serialized version of a BTBV trust manager, extract the trust cache.
|
||||||
/// NOTE: This is needed as Dart cannot just cast a List<dynamic> to List<int> and so on.
|
/// NOTE: This is needed as Dart cannot just cast a List<dynamic> to List<int> and so on.
|
||||||
static Map<RatchetMapKey, BTBVTrustState> trustCacheFromJson(
|
static Map<RatchetMapKey, BTBVTrustState> trustCacheFromJson(
|
||||||
Map<String, dynamic> json,) {
|
Map<String, dynamic> json,
|
||||||
|
) {
|
||||||
return (json['trust']! as Map<String, dynamic>)
|
return (json['trust']! as Map<String, dynamic>)
|
||||||
.map<RatchetMapKey, BTBVTrustState>(
|
.map<RatchetMapKey, BTBVTrustState>(
|
||||||
(key, value) => MapEntry(
|
(key, value) => MapEntry(
|
||||||
@ -218,7 +222,8 @@ abstract class BlindTrustBeforeVerificationTrustManager extends TrustManager {
|
|||||||
/// From a serialized version of a BTBV trust manager, extract the enable cache.
|
/// From a serialized version of a BTBV trust manager, extract the enable cache.
|
||||||
/// NOTE: This is needed as Dart cannot just cast a List<dynamic> to List<int> and so on.
|
/// NOTE: This is needed as Dart cannot just cast a List<dynamic> to List<int> and so on.
|
||||||
static Map<RatchetMapKey, bool> enableCacheFromJson(
|
static Map<RatchetMapKey, bool> enableCacheFromJson(
|
||||||
Map<String, dynamic> json,) {
|
Map<String, dynamic> json,
|
||||||
|
) {
|
||||||
return (json['enable']! as Map<String, dynamic>).map<RatchetMapKey, bool>(
|
return (json['enable']! as Map<String, dynamic>).map<RatchetMapKey, bool>(
|
||||||
(key, value) => MapEntry(
|
(key, value) => MapEntry(
|
||||||
RatchetMapKey.fromJsonKey(key),
|
RatchetMapKey.fromJsonKey(key),
|
||||||
|
@ -37,7 +37,9 @@ class X3DHBobResult {
|
|||||||
/// a Ed25519 keypair.
|
/// a Ed25519 keypair.
|
||||||
Future<List<int>> sig(OmemoKeyPair keyPair, List<int> message) async {
|
Future<List<int>> sig(OmemoKeyPair keyPair, List<int> message) async {
|
||||||
assert(
|
assert(
|
||||||
keyPair.type == KeyPairType.ed25519, 'Signature keypair must be Ed25519',);
|
keyPair.type == KeyPairType.ed25519,
|
||||||
|
'Signature keypair must be Ed25519',
|
||||||
|
);
|
||||||
final signature = await Ed25519().sign(
|
final signature = await Ed25519().sign(
|
||||||
message,
|
message,
|
||||||
keyPair: await keyPair.asKeyPair(),
|
keyPair: await keyPair.asKeyPair(),
|
||||||
@ -69,7 +71,9 @@ Future<List<int>> kdf(List<int> km) async {
|
|||||||
/// Alice builds a session with Bob using his bundle [bundle] and Alice's identity key
|
/// Alice builds a session with Bob using his bundle [bundle] and Alice's identity key
|
||||||
/// pair [ik].
|
/// pair [ik].
|
||||||
Future<X3DHAliceResult> x3dhFromBundle(
|
Future<X3DHAliceResult> x3dhFromBundle(
|
||||||
OmemoBundle bundle, OmemoKeyPair ik,) async {
|
OmemoBundle bundle,
|
||||||
|
OmemoKeyPair ik,
|
||||||
|
) async {
|
||||||
// Check the signature first
|
// Check the signature first
|
||||||
final signatureValue = await Ed25519().verify(
|
final signatureValue = await Ed25519().verify(
|
||||||
await bundle.spk.getBytes(),
|
await bundle.spk.getBytes(),
|
||||||
@ -107,8 +111,12 @@ Future<X3DHAliceResult> x3dhFromBundle(
|
|||||||
|
|
||||||
/// Bob builds the X3DH shared secret from the inital message [msg], the SPK [spk], the
|
/// Bob builds the X3DH shared secret from the inital message [msg], the SPK [spk], the
|
||||||
/// OPK [opk] that was selected by Alice and our IK [ik]. Returns the shared secret.
|
/// OPK [opk] that was selected by Alice and our IK [ik]. Returns the shared secret.
|
||||||
Future<X3DHBobResult> x3dhFromInitialMessage(X3DHMessage msg, OmemoKeyPair spk,
|
Future<X3DHBobResult> x3dhFromInitialMessage(
|
||||||
OmemoKeyPair opk, OmemoKeyPair ik,) async {
|
X3DHMessage msg,
|
||||||
|
OmemoKeyPair spk,
|
||||||
|
OmemoKeyPair opk,
|
||||||
|
OmemoKeyPair ik,
|
||||||
|
) async {
|
||||||
final dh1 = await omemoDH(spk, msg.ik, 2);
|
final dh1 = await omemoDH(spk, msg.ik, 2);
|
||||||
final dh2 = await omemoDH(ik, msg.ek, 1);
|
final dh2 = await omemoDH(ik, msg.ek, 1);
|
||||||
final dh3 = await omemoDH(spk, msg.ek, 0);
|
final dh3 = await omemoDH(spk, msg.ek, 0);
|
||||||
|
@ -813,9 +813,10 @@ void main() {
|
|||||||
|
|
||||||
expect(aliceResult.isSuccess(1), false);
|
expect(aliceResult.isSuccess(1), false);
|
||||||
expect(
|
expect(
|
||||||
aliceResult.jidEncryptionErrors[bobJid]
|
aliceResult.jidEncryptionErrors[bobJid]
|
||||||
is NoKeyMaterialAvailableException,
|
is NoKeyMaterialAvailableException,
|
||||||
true,);
|
true,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Test sending a message two two JIDs with failed lookups', () async {
|
test('Test sending a message two two JIDs with failed lookups', () async {
|
||||||
@ -866,9 +867,10 @@ void main() {
|
|||||||
|
|
||||||
expect(aliceResult.isSuccess(2), true);
|
expect(aliceResult.isSuccess(2), true);
|
||||||
expect(
|
expect(
|
||||||
aliceResult.jidEncryptionErrors[cocoJid]
|
aliceResult.jidEncryptionErrors[cocoJid]
|
||||||
is NoKeyMaterialAvailableException,
|
is NoKeyMaterialAvailableException,
|
||||||
true,);
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
// Bob decrypts it
|
// Bob decrypts it
|
||||||
final bobResult = await bobManager.onIncomingStanza(
|
final bobResult = await bobManager.onIncomingStanza(
|
||||||
|
@ -73,8 +73,11 @@ void main() {
|
|||||||
await x3dhFromBundle(bundleBob, ikAlice);
|
await x3dhFromBundle(bundleBob, ikAlice);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
exception = true;
|
exception = true;
|
||||||
expect(e is InvalidSignatureException, true,
|
expect(
|
||||||
reason: 'Expected InvalidSignatureException, but got $e',);
|
e is InvalidSignatureException,
|
||||||
|
true,
|
||||||
|
reason: 'Expected InvalidSignatureException, but got $e',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(exception, true, reason: 'Expected test failure');
|
expect(exception, true, reason: 'Expected test failure');
|
||||||
|
Loading…
Reference in New Issue
Block a user