feat: Reimplement the OMEMOMessage protobuf schema
This commit is contained in:
parent
08ec093675
commit
a82b5d0990
72
lib/src/protobuf/omemo_message.dart
Normal file
72
lib/src/protobuf/omemo_message.dart
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import 'package:omemo_dart/src/helpers.dart';
|
||||||
|
import 'package:omemo_dart/src/protobuf/protobuf.dart';
|
||||||
|
|
||||||
|
class OmemoMessage {
|
||||||
|
|
||||||
|
const OmemoMessage(this.n, this.pn, this.dhPub, this.ciphertext);
|
||||||
|
|
||||||
|
factory OmemoMessage.fromBuffer(List<int> data) {
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
// required uint32 n = 1;
|
||||||
|
if (data[0] != 8) {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
var decode = decodeVarint(data, 1);
|
||||||
|
final n = decode.n;
|
||||||
|
i += decode.length + 1;
|
||||||
|
|
||||||
|
// required uint32 pn = 2;
|
||||||
|
if (data[i] != 16) {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
decode = decodeVarint(data, i + 1);
|
||||||
|
final pn = decode.n;
|
||||||
|
i += decode.length + 1;
|
||||||
|
|
||||||
|
// required bytes dh_pub = 3;
|
||||||
|
if (data[i] != ((3 << 3) | 2)) {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
final dhPub = data.sublist(i + 2, i + 2 + data[i + 1]);
|
||||||
|
i += 2 + data[i + 1];
|
||||||
|
|
||||||
|
// optional bytes ciphertext = 4;
|
||||||
|
List<int>? ciphertext;
|
||||||
|
if (i < data.length) {
|
||||||
|
if (data[i] != ((4 << 3) | 2)) {
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
ciphertext = data.sublist(i + 2, i + 2 + data[i + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return OmemoMessage(n, pn, dhPub, ciphertext);
|
||||||
|
}
|
||||||
|
|
||||||
|
final int n;
|
||||||
|
final int pn;
|
||||||
|
final List<int> dhPub;
|
||||||
|
final List<int>? ciphertext;
|
||||||
|
|
||||||
|
List<int> writeToBuffer() {
|
||||||
|
final data = concat([
|
||||||
|
[8],
|
||||||
|
encodeVarint(n),
|
||||||
|
[16],
|
||||||
|
encodeVarint(pn),
|
||||||
|
[((3 << 3) | 2), dhPub.length],
|
||||||
|
dhPub,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (ciphertext != null) {
|
||||||
|
return concat([
|
||||||
|
data,
|
||||||
|
[((4 << 3) | 2), ciphertext!.length],
|
||||||
|
ciphertext!,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
60
lib/src/protobuf/protobuf.dart
Normal file
60
lib/src/protobuf/protobuf.dart
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/// Masks the 7 LSB
|
||||||
|
const lsb7Mask = 0x7F;
|
||||||
|
|
||||||
|
/// Constant for setting the MSB
|
||||||
|
const msb = 1 << 7;
|
||||||
|
|
||||||
|
class VarintDecode {
|
||||||
|
|
||||||
|
const VarintDecode(this.n, this.length);
|
||||||
|
final int n;
|
||||||
|
final int length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decode a Varint that begins at [input]'s index [offset].
|
||||||
|
VarintDecode decodeVarint(List<int> input, int offset) {
|
||||||
|
// The return value
|
||||||
|
var n = 0;
|
||||||
|
// The byte offset counter
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
// Iterate until the MSB of the byte is 0
|
||||||
|
while (true) {
|
||||||
|
// Mask only the 7 LSB and "move" them accordingly
|
||||||
|
n += (input[offset + i] & lsb7Mask) << (7 * i);
|
||||||
|
|
||||||
|
// Break if we reached the end
|
||||||
|
if (input[offset + i] & 1 << 7 == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return VarintDecode(n, i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encodes the integer [i] into a Varint.
|
||||||
|
List<int> encodeVarint(int i) {
|
||||||
|
assert(i >= 0, "Two's complement is not implemented");
|
||||||
|
final ret = List<int>.empty(growable: true);
|
||||||
|
|
||||||
|
var j = 0;
|
||||||
|
while (true) {
|
||||||
|
// The 7 LSB of the byte we're creating
|
||||||
|
final x = (i & (lsb7Mask << j * 7)) >> j * 7;
|
||||||
|
// The next bits
|
||||||
|
final next = i & (lsb7Mask << (j + 1) * 7);
|
||||||
|
|
||||||
|
if (next == 0) {
|
||||||
|
// If we were to shift further, we only get zero, so we're at the end
|
||||||
|
ret.add(x);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// We still have at least one bit more to go, so set the MSB to 1
|
||||||
|
ret.add(x + msb);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
110
test/protobuf.dart
Normal file
110
test/protobuf.dart
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import 'package:omemo_dart/protobuf/schema.pb.dart';
|
||||||
|
import 'package:omemo_dart/src/protobuf/omemo_message.dart';
|
||||||
|
import 'package:omemo_dart/src/protobuf/protobuf.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('Base 128 Varints', () {
|
||||||
|
test('Test simple parsing of Varints', () {
|
||||||
|
expect(
|
||||||
|
decodeVarint(<int>[1], 0).n,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
decodeVarint(<int>[1], 0).length,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
decodeVarint(<int>[0x96, 0x01, 0x00], 0).n,
|
||||||
|
150,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
decodeVarint(<int>[0x96, 0x01, 0x00], 0).length,
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
decodeVarint(<int>[172, 2, 0x8], 0).n,
|
||||||
|
300,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
decodeVarint(<int>[172, 2, 0x8], 0).length,
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Test encoding Varints', () {
|
||||||
|
expect(
|
||||||
|
encodeVarint(1),
|
||||||
|
<int>[1],
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
encodeVarint(150),
|
||||||
|
<int>[0x96, 0x01],
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
encodeVarint(300),
|
||||||
|
<int>[172, 2],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('OMEMOMessage', () {
|
||||||
|
test('Decode a OMEMOMessage', () {
|
||||||
|
final pbMessage = OMEMOMessage()
|
||||||
|
..n = 1
|
||||||
|
..pn = 5
|
||||||
|
..dhPub = <int>[1, 2, 3]
|
||||||
|
..ciphertext = <int>[4, 5, 6];
|
||||||
|
final serial = pbMessage.writeToBuffer();
|
||||||
|
final msg = OmemoMessage.fromBuffer(serial);
|
||||||
|
|
||||||
|
expect(msg.n, 1);
|
||||||
|
expect(msg.pn, 5);
|
||||||
|
expect(msg.dhPub, <int>[1, 2, 3]);
|
||||||
|
expect(msg.ciphertext, <int>[4, 5, 6]);
|
||||||
|
});
|
||||||
|
test('Decode a OMEMOMessage without ciphertext', () {
|
||||||
|
final pbMessage = OMEMOMessage()
|
||||||
|
..n = 1
|
||||||
|
..pn = 5
|
||||||
|
..dhPub = <int>[1, 2, 3];
|
||||||
|
final serial = pbMessage.writeToBuffer();
|
||||||
|
final msg = OmemoMessage.fromBuffer(serial);
|
||||||
|
|
||||||
|
expect(msg.n, 1);
|
||||||
|
expect(msg.pn, 5);
|
||||||
|
expect(msg.dhPub, <int>[1, 2, 3]);
|
||||||
|
expect(msg.ciphertext, null);
|
||||||
|
});
|
||||||
|
test('Encode a OMEMOMessage', () {
|
||||||
|
const m = OmemoMessage(
|
||||||
|
1,
|
||||||
|
5,
|
||||||
|
<int>[1, 2, 3],
|
||||||
|
<int>[4, 5, 6],
|
||||||
|
);
|
||||||
|
final serial = m.writeToBuffer();
|
||||||
|
final msg = OMEMOMessage.fromBuffer(serial);
|
||||||
|
|
||||||
|
expect(msg.n, 1);
|
||||||
|
expect(msg.pn, 5);
|
||||||
|
expect(msg.dhPub, <int>[1, 2, 3]);
|
||||||
|
expect(msg.ciphertext, <int>[4, 5, 6]);
|
||||||
|
});
|
||||||
|
test('Encode a OMEMOMessage without ciphertext', () {
|
||||||
|
const m = OmemoMessage(
|
||||||
|
1,
|
||||||
|
5,
|
||||||
|
<int>[1, 2, 3],
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
final serial = m.writeToBuffer();
|
||||||
|
final msg = OMEMOMessage.fromBuffer(serial);
|
||||||
|
|
||||||
|
expect(msg.n, 1);
|
||||||
|
expect(msg.pn, 5);
|
||||||
|
expect(msg.dhPub, <int>[1, 2, 3]);
|
||||||
|
expect(msg.ciphertext, <int>[]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user