fix: Migrate to custom protobuf

This commit is contained in:
PapaTutuWawa 2022-08-05 12:59:10 +02:00
parent 5c3cc424de
commit 9ed94c8f3a
6 changed files with 93 additions and 83 deletions

View File

@ -1,12 +1,12 @@
import 'package:cryptography/cryptography.dart'; import 'package:cryptography/cryptography.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.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/crypto.dart'; import 'package:omemo_dart/src/double_ratchet/crypto.dart';
import 'package:omemo_dart/src/double_ratchet/kdf.dart'; import 'package:omemo_dart/src/double_ratchet/kdf.dart';
import 'package:omemo_dart/src/errors.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/keys.dart'; import 'package:omemo_dart/src/keys.dart';
import 'package:omemo_dart/src/protobuf/omemo_message.dart';
/// Amount of messages we may skip per session /// Amount of messages we may skip per session
const maxSkip = 1000; const maxSkip = 1000;
@ -14,7 +14,7 @@ const maxSkip = 1000;
class RatchetStep { class RatchetStep {
const RatchetStep(this.header, this.ciphertext); const RatchetStep(this.header, this.ciphertext);
final OMEMOMessage header; final OmemoMessage header;
final List<int> ciphertext; final List<int> ciphertext;
} }
@ -111,10 +111,10 @@ class OmemoDoubleRatchet {
); );
} }
Future<List<int>?> _trySkippedMessageKeys(OMEMOMessage header, List<int> ciphertext) async { Future<List<int>?> _trySkippedMessageKeys(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!,
); );
if (mkSkipped.containsKey(key)) { if (mkSkipped.containsKey(key)) {
final mk = mkSkipped[key]!; final mk = mkSkipped[key]!;
@ -142,11 +142,11 @@ class OmemoDoubleRatchet {
} }
} }
Future<void> _dhRatchet(OMEMOMessage header) async { Future<void> _dhRatchet(OmemoMessage header) async {
pn = header.n; pn = header.n!;
ns = 0; ns = 0;
nr = 0; nr = 0;
dhr = OmemoPublicKey.fromBytes(header.dhPub, KeyPairType.x25519); dhr = OmemoPublicKey.fromBytes(header.dhPub!, KeyPairType.x25519);
final newRk = await kdfRk(rk, await omemoDH(dhs, dhr!, 0)); final newRk = await kdfRk(rk, await omemoDH(dhs, dhr!, 0));
rk = newRk; rk = newRk;
@ -163,7 +163,7 @@ class OmemoDoubleRatchet {
final mk = await kdfCk(cks!, kdfCkNextMessageKey); final mk = await kdfCk(cks!, kdfCkNextMessageKey);
cks = newCks; cks = newCks;
final header = OMEMOMessage() final header = OmemoMessage()
..dhPub = await dhs.pk.getBytes() ..dhPub = await dhs.pk.getBytes()
..pn = pn ..pn = pn
..n = ns; ..n = ns;
@ -180,7 +180,7 @@ class OmemoDoubleRatchet {
/// Ratchet. Returns the decrypted (raw) plaintext. /// Ratchet. Returns the decrypted (raw) plaintext.
/// ///
/// 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(OMEMOMessage header, List<int> ciphertext) async { Future<List<int>> ratchetDecrypt(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) {
@ -188,11 +188,11 @@ class OmemoDoubleRatchet {
} }
if (header.dhPub != await dhr?.getBytes()) { if (header.dhPub != await dhr?.getBytes()) {
await _skipMessageKeys(header.pn); await _skipMessageKeys(header.pn!);
await _dhRatchet(header); await _dhRatchet(header);
} }
await _skipMessageKeys(header.n); await _skipMessageKeys(header.n!);
final newCkr = await kdfCk(ckr!, kdfCkNextChainKey); final newCkr = await kdfCk(ckr!, kdfCkNextChainKey);
final mk = await kdfCk(ckr!, kdfCkNextMessageKey); final mk = await kdfCk(ckr!, kdfCkNextMessageKey);
ckr = newCkr; ckr = newCkr;

View File

@ -1,7 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:cryptography/cryptography.dart'; import 'package:cryptography/cryptography.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/errors.dart';
@ -9,6 +8,9 @@ import 'package:omemo_dart/src/helpers.dart';
import 'package:omemo_dart/src/keys.dart'; import 'package:omemo_dart/src/keys.dart';
import 'package:omemo_dart/src/omemo/bundle.dart'; import 'package:omemo_dart/src/omemo/bundle.dart';
import 'package:omemo_dart/src/omemo/device.dart'; import 'package:omemo_dart/src/omemo/device.dart';
import 'package:omemo_dart/src/protobuf/omemo_authenticated_message.dart';
import 'package:omemo_dart/src/protobuf/omemo_key_exchange.dart';
import 'package:omemo_dart/src/protobuf/omemo_message.dart';
import 'package:omemo_dart/src/x3dh/x3dh.dart'; import 'package:omemo_dart/src/x3dh/x3dh.dart';
import 'package:synchronized/synchronized.dart'; import 'package:synchronized/synchronized.dart';
@ -80,7 +82,7 @@ class OmemoSessionManager {
/// Create a ratchet session initiated by Alice to the user with Jid [jid] and the device /// Create a ratchet session initiated by Alice to the user with Jid [jid] and the device
/// [deviceId] from the bundle [bundle]. /// [deviceId] from the bundle [bundle].
Future<OMEMOKeyExchange> addSessionFromBundle(String jid, int deviceId, OmemoBundle bundle) async { Future<OmemoKeyExchange> addSessionFromBundle(String jid, int deviceId, OmemoBundle bundle) async {
final kexResult = await x3dhFromBundle( final kexResult = await x3dhFromBundle(
bundle, bundle,
device.ik, device.ik,
@ -93,9 +95,8 @@ class OmemoSessionManager {
await addSession(jid, deviceId, ratchet); await addSession(jid, deviceId, ratchet);
return OMEMOKeyExchange() return OmemoKeyExchange()
..pkId = kexResult.opkId ..pkId = kexResult.opkId
// TODO(PapaTutuWawa): Fix
..spkId = 0 ..spkId = 0
..ik = await device.ik.pk.getBytes() ..ik = await device.ik.pk.getBytes()
..ek = await kexResult.ek.pk.getBytes(); ..ek = await kexResult.ek.pk.getBytes();
@ -104,15 +105,15 @@ class OmemoSessionManager {
/// Build a new session with the user at [jid] with the device [deviceId] using data /// Build a new session with the user at [jid] with the device [deviceId] using data
/// from the key exchange [kex]. /// from the key exchange [kex].
// TODO(PapaTutuWawa): Replace the OPK // TODO(PapaTutuWawa): Replace the OPK
Future<void> addSessionFromKeyExchange(String jid, int deviceId, OMEMOKeyExchange kex) async { Future<void> addSessionFromKeyExchange(String jid, int deviceId, OmemoKeyExchange kex) async {
final kexResult = await x3dhFromInitialMessage( final kexResult = await x3dhFromInitialMessage(
X3DHMessage( X3DHMessage(
OmemoPublicKey.fromBytes(kex.ik, KeyPairType.ed25519), OmemoPublicKey.fromBytes(kex.ik!, KeyPairType.ed25519),
OmemoPublicKey.fromBytes(kex.ek, KeyPairType.x25519), OmemoPublicKey.fromBytes(kex.ek!, KeyPairType.x25519),
kex.pkId, kex.pkId!,
), ),
device.spk, device.spk,
device.opks.values.elementAt(kex.pkId), device.opks.values.elementAt(kex.pkId!),
device.ik, device.ik,
); );
final ratchet = await OmemoDoubleRatchet.acceptNewSession( final ratchet = await OmemoDoubleRatchet.acceptNewSession(
@ -174,8 +175,8 @@ class OmemoSessionManager {
} }
final decodedRawKey = base64.decode(rawKey.value); final decodedRawKey = base64.decode(rawKey.value);
final authMessage = OMEMOAuthenticatedMessage.fromBuffer(decodedRawKey); final authMessage = OmemoAuthenticatedMessage.fromBuffer(decodedRawKey);
final message = OMEMOMessage.fromBuffer(authMessage.message); final message = OmemoMessage.fromBuffer(authMessage.message!);
final ratchet = _ratchetMap[senderDeviceId]!; final ratchet = _ratchetMap[senderDeviceId]!;
final keyAndHmac = await ratchet.ratchetDecrypt(message, decodedRawKey); final keyAndHmac = await ratchet.ratchetDecrypt(message, decodedRawKey);

View File

@ -3,7 +3,7 @@ import 'package:omemo_dart/src/protobuf/protobuf.dart';
class OmemoAuthenticatedMessage { class OmemoAuthenticatedMessage {
const OmemoAuthenticatedMessage(this.mac, this.message); OmemoAuthenticatedMessage();
factory OmemoAuthenticatedMessage.fromBuffer(List<int> data) { factory OmemoAuthenticatedMessage.fromBuffer(List<int> data) {
var i = 0; var i = 0;
@ -20,18 +20,20 @@ class OmemoAuthenticatedMessage {
} }
final message = data.sublist(i + 2, i + 2 + data[i + 1]); final message = data.sublist(i + 2, i + 2 + data[i + 1]);
return OmemoAuthenticatedMessage(mac, message); return OmemoAuthenticatedMessage()
..mac = mac
..message = message;
} }
final List<int> mac; List<int>? mac;
final List<int> message; List<int>? message;
List<int> writeToBuffer() { List<int> writeToBuffer() {
return concat([ return concat([
[fieldId(1, fieldTypeByteArray), mac.length], [fieldId(1, fieldTypeByteArray), mac!.length],
mac, mac!,
[fieldId(2, fieldTypeByteArray), message.length], [fieldId(2, fieldTypeByteArray), message!.length],
message, message!,
]); ]);
} }
} }

View File

@ -4,7 +4,7 @@ import 'package:omemo_dart/src/protobuf/protobuf.dart';
class OmemoKeyExchange { class OmemoKeyExchange {
const OmemoKeyExchange(this.pkId, this.spkId, this.ik, this.ek, this.message); OmemoKeyExchange();
factory OmemoKeyExchange.fromBuffer(List<int> data) { factory OmemoKeyExchange.fromBuffer(List<int> data) {
var i = 0; var i = 0;
@ -40,26 +40,31 @@ class OmemoKeyExchange {
} }
final message = OmemoAuthenticatedMessage.fromBuffer(data.sublist(i + 2)); final message = OmemoAuthenticatedMessage.fromBuffer(data.sublist(i + 2));
return OmemoKeyExchange(pkId, spkId, ik, ek, message); return OmemoKeyExchange()
..pkId = pkId
..spkId = spkId
..ik = ik
..ek = ek
..message = message;
} }
final int pkId; int? pkId;
final int spkId; int? spkId;
final List<int> ik; List<int>? ik;
final List<int> ek; List<int>? ek;
final OmemoAuthenticatedMessage message; OmemoAuthenticatedMessage? message;
List<int> writeToBuffer() { List<int> writeToBuffer() {
final msg = message.writeToBuffer(); final msg = message!.writeToBuffer();
return concat([ return concat([
[fieldId(1, fieldTypeUint32)], [fieldId(1, fieldTypeUint32)],
encodeVarint(pkId), encodeVarint(pkId!),
[fieldId(2, fieldTypeUint32)], [fieldId(2, fieldTypeUint32)],
encodeVarint(spkId), encodeVarint(spkId!),
[fieldId(3, fieldTypeByteArray), ik.length], [fieldId(3, fieldTypeByteArray), ik!.length],
ik, ik!,
[fieldId(4, fieldTypeByteArray), ek.length], [fieldId(4, fieldTypeByteArray), ek!.length],
ek, ek!,
[fieldId(5, fieldTypeByteArray), msg.length], [fieldId(5, fieldTypeByteArray), msg.length],
msg, msg,
]); ]);

View File

@ -3,7 +3,7 @@ import 'package:omemo_dart/src/protobuf/protobuf.dart';
class OmemoMessage { class OmemoMessage {
const OmemoMessage(this.n, this.pn, this.dhPub, this.ciphertext); OmemoMessage();
factory OmemoMessage.fromBuffer(List<int> data) { factory OmemoMessage.fromBuffer(List<int> data) {
var i = 0; var i = 0;
@ -41,28 +41,32 @@ class OmemoMessage {
ciphertext = data.sublist(i + 2, i + 2 + data[i + 1]); ciphertext = data.sublist(i + 2, i + 2 + data[i + 1]);
} }
return OmemoMessage(n, pn, dhPub, ciphertext); return OmemoMessage()
..n = n
..pn = pn
..dhPub = dhPub
..ciphertext = ciphertext;
} }
final int n; int? n;
final int pn; int? pn;
final List<int> dhPub; List<int>? dhPub;
final List<int>? ciphertext; List<int>? ciphertext;
List<int> writeToBuffer() { List<int> writeToBuffer() {
final data = concat([ final data = concat([
[8], [fieldId(1, fieldTypeUint32)],
encodeVarint(n), encodeVarint(n!),
[16], [fieldId(2, fieldTypeUint32)],
encodeVarint(pn), encodeVarint(pn!),
[((3 << 3) | 2), dhPub.length], [fieldId(3, fieldTypeByteArray), dhPub!.length],
dhPub, dhPub!,
]); ]);
if (ciphertext != null) { if (ciphertext != null) {
return concat([ return concat([
data, data,
[((4 << 3) | 2), ciphertext!.length], [fieldId(4, fieldTypeByteArray), ciphertext!.length],
ciphertext!, ciphertext!,
]); ]);
} }

View File

@ -79,12 +79,11 @@ void main() {
expect(msg.ciphertext, null); expect(msg.ciphertext, null);
}); });
test('Encode a OMEMOMessage', () { test('Encode a OMEMOMessage', () {
const m = OmemoMessage( final m = OmemoMessage()
1, ..n = 1
5, ..pn = 5
<int>[1, 2, 3], ..dhPub = <int>[1, 2, 3]
<int>[4, 5, 6], ..ciphertext = <int>[4, 5, 6];
);
final serial = m.writeToBuffer(); final serial = m.writeToBuffer();
final msg = OMEMOMessage.fromBuffer(serial); final msg = OMEMOMessage.fromBuffer(serial);
@ -94,12 +93,10 @@ void main() {
expect(msg.ciphertext, <int>[4, 5, 6]); expect(msg.ciphertext, <int>[4, 5, 6]);
}); });
test('Encode a OMEMOMessage without ciphertext', () { test('Encode a OMEMOMessage without ciphertext', () {
const m = OmemoMessage( final m = OmemoMessage()
1, ..n = 1
5, ..pn = 5
<int>[1, 2, 3], ..dhPub = <int>[1, 2, 3];
null,
);
final serial = m.writeToBuffer(); final serial = m.writeToBuffer();
final msg = OMEMOMessage.fromBuffer(serial); final msg = OMEMOMessage.fromBuffer(serial);
@ -112,7 +109,9 @@ void main() {
group('OMEMOAuthenticatedMessage', () { group('OMEMOAuthenticatedMessage', () {
test('Test encoding a message', () { test('Test encoding a message', () {
const msg = OmemoAuthenticatedMessage(<int>[1, 2, 3], <int>[4, 5, 6]); final msg = OmemoAuthenticatedMessage()
..mac = <int>[1, 2, 3]
..message = <int>[4, 5, 6];
final decoded = OMEMOAuthenticatedMessage.fromBuffer(msg.writeToBuffer()); final decoded = OMEMOAuthenticatedMessage.fromBuffer(msg.writeToBuffer());
expect(decoded.mac, <int>[1, 2, 3]); expect(decoded.mac, <int>[1, 2, 3]);
@ -132,16 +131,15 @@ void main() {
group('OMEMOKeyExchange', () { group('OMEMOKeyExchange', () {
test('Test encoding a message', () { test('Test encoding a message', () {
const message = OmemoKeyExchange( final authMessage = OmemoAuthenticatedMessage()
698, ..mac = <int>[5, 6, 8, 0]
245, ..message = <int>[4, 5, 7, 3, 2];
<int>[1, 4, 6], final message = OmemoKeyExchange()
<int>[4, 6, 7, 80], ..pkId = 698
OmemoAuthenticatedMessage( ..spkId = 245
<int>[5, 6, 8, 0], ..ik = <int>[1, 4, 6]
<int>[4, 5, 7, 3, 2], ..ek = <int>[4, 6, 7, 80]
), ..message = authMessage;
);
final kex = OMEMOKeyExchange.fromBuffer(message.writeToBuffer()); final kex = OMEMOKeyExchange.fromBuffer(message.writeToBuffer());
expect(kex.pkId, 698); expect(kex.pkId, 698);
@ -169,8 +167,8 @@ void main() {
expect(decoded.ik, <int>[1, 4, 6]); expect(decoded.ik, <int>[1, 4, 6]);
expect(decoded.ek, <int>[4 ,6 ,7 , 80]); expect(decoded.ek, <int>[4 ,6 ,7 , 80]);
expect(decoded.message.mac, <int>[5, 6, 8, 0]); expect(decoded.message!.mac, <int>[5, 6, 8, 0]);
expect(decoded.message.message, <int>[4, 5, 7, 3, 2]); expect(decoded.message!.message, <int>[4, 5, 7, 3, 2]);
}); });
}); });
} }