Closed McCrafterIV closed 1 year ago
After stumbling on the issue #45 I changed my code a little. I think this approach is much more correct, but still not working. I now get the following exception:
InvalidMessageException - No key for: 1446805559
#0 GroupCipher.decryptWithCallback (package:libsignal_protocol_dart/src/groups/group_cipher.dart:69:7)
<asynchronous suspension>
#1 main (package:project/group.dart:57:21)
<asynchronous suspension>
Process finished with exit code 255
This is my code now:
final bobGroupSenderAddress = SignalProtocolAddress('+00000000001', 1);
final aliceGroupSenderAddress = SignalProtocolAddress('+00000000002', 1);
final deanGroupSenderAddress = SignalProtocolAddress('+00000000003', 1);
final bobGroupSender = SenderKeyName('Private group', bobGroupSenderAddress);
final aliceGroupSender =
SenderKeyName('Private group', aliceGroupSenderAddress);
final deanGroupSender =
SenderKeyName('Private group', deanGroupSenderAddress);
final aliceStore = InMemorySenderKeyStore();
final bobStore = InMemorySenderKeyStore();
final deanStore = InMemorySenderKeyStore();
final aliceGroupSessionBuilder = GroupSessionBuilder(aliceStore);
final bobGroupSessionBuilder = GroupSessionBuilder(bobStore);
final deanGroupSessionBuilder = GroupSessionBuilder(deanStore);
final aliceGroupCipher = GroupCipher(aliceStore, aliceGroupSender);
final bobGroupCipher = GroupCipher(bobStore, bobGroupSender);
final deanGroupCipher = GroupCipher(deanStore, deanGroupSender);
final aliceDistributionMessage =
await aliceGroupSessionBuilder.create(aliceGroupSender);
final bobDistributionMessage =
await bobGroupSessionBuilder.create(bobGroupSender);
final deanDistributionMessage =
await deanGroupSessionBuilder.create(deanGroupSender);
await bobGroupSessionBuilder.process(
aliceGroupSender, aliceDistributionMessage);
await bobGroupSessionBuilder.process(
deanGroupSender, deanDistributionMessage);
await deanGroupSessionBuilder.process(bobGroupSender, bobDistributionMessage);
await deanGroupSessionBuilder.process(
aliceGroupSender, aliceDistributionMessage);
await aliceGroupSessionBuilder.process(
bobGroupSender, bobDistributionMessage);
await aliceGroupSessionBuilder.process(
deanGroupSender, deanDistributionMessage);
final ciphertextFromAlice = await aliceGroupCipher
.encrypt(Uint8List.fromList(utf8.encode('Hello from Alice')));
final ciphertextFromBob = await bobGroupCipher
.encrypt(Uint8List.fromList(utf8.encode('Hello from Bob')));
final ciphertextFromDean = await deanGroupCipher
.encrypt(Uint8List.fromList(utf8.encode('Hello from Dean')));
print(utf8.decode(await bobGroupCipher.decrypt(ciphertextFromAlice)));
print(utf8.decode(await deanGroupCipher.decrypt(ciphertextFromBob)));
print(utf8.decode(await aliceGroupCipher.decrypt(ciphertextFromDean)));
You must use same sender for encrypt and decrypt.
final bobGroupSenderAddress = SignalProtocolAddress('+00000000001', 1);
final aliceGroupSenderAddress = SignalProtocolAddress('+00000000002', 1);
final deanGroupSenderAddress = SignalProtocolAddress('+00000000003', 1);
final bobGroupSender = SenderKeyName('Private group', bobGroupSenderAddress);
final aliceGroupSender =
SenderKeyName('Private group', aliceGroupSenderAddress);
final deanGroupSender =
SenderKeyName('Private group', deanGroupSenderAddress);
final aliceStore = InMemorySenderKeyStore();
final bobStore = InMemorySenderKeyStore();
final deanStore = InMemorySenderKeyStore();
final aliceGroupSessionBuilder = GroupSessionBuilder(aliceStore);
final bobGroupSessionBuilder = GroupSessionBuilder(bobStore);
final deanGroupSessionBuilder = GroupSessionBuilder(deanStore);
final aliceGroupCipher = GroupCipher(aliceStore, aliceGroupSender);
final bobGroupCipher = GroupCipher(bobStore, aliceGroupSender); // use alice as sender for decrypt
final deanGroupCipher = GroupCipher(deanStore, deanGroupSender);
final aliceDistributionMessage =
await aliceGroupSessionBuilder.create(aliceGroupSender);
final bobDistributionMessage =
await bobGroupSessionBuilder.create(bobGroupSender);
final deanDistributionMessage =
await deanGroupSessionBuilder.create(deanGroupSender);
await bobGroupSessionBuilder.process(
aliceGroupSender, aliceDistributionMessage);
await bobGroupSessionBuilder.process(
deanGroupSender, deanDistributionMessage);
await deanGroupSessionBuilder.process(bobGroupSender, bobDistributionMessage);
await deanGroupSessionBuilder.process(
aliceGroupSender, aliceDistributionMessage);
await aliceGroupSessionBuilder.process(
bobGroupSender, bobDistributionMessage);
await aliceGroupSessionBuilder.process(
deanGroupSender, deanDistributionMessage);
final ciphertextFromAlice = await aliceGroupCipher
.encrypt(Uint8List.fromList(utf8.encode('Hello from Alice')));
// final ciphertextFromBob = await bobGroupCipher
// .encrypt(Uint8List.fromList(utf8.encode('Hello from Bob')));
final ciphertextFromDean = await deanGroupCipher
.encrypt(Uint8List.fromList(utf8.encode('Hello from Dean')));
print(utf8.decode(await bobGroupCipher.decrypt(ciphertextFromAlice)));
// print(utf8.decode(await deanGroupCipher.decrypt(ciphertextFromBob)));
// print(utf8.decode(await aliceGroupCipher.decrypt(ciphertextFromDean)));
You must use same sender for encrypt and decrypt.
Thanks for the example. I now understood how to use it. This is my now working code from above with 3 group members all able to chat with one another:
But I've another question. When you take e.g. Signal Messenger, this GroupSession, GroupCipher and all would resemble the legacy groups in Signal Messenger, am I right? But what is the advantage from this system over just using a "regular" SessionCipher and SessionBuilder? I also haven't found a way yet to "kick someone (e.g. Dean) out" of a GroupSession. I tried to just create a new KeyDistributionMessage
s, but Dean was still able to decrypt the message (probably because it uses the old session instead). How would I achieve that functionallity? This is what I tried already:
// to kick Dean out
final sentAliceDistributionMessage2 =
await aliceSessionBuilder.create(groupSenderAlice);
bobSessionBuilder.process(groupSenderAlice, sentAliceDistributionMessage2);
final sentBobDistributionMessage2 =
await bobSessionBuilder.create(groupSenderBob);
aliceSessionBuilder.process(groupSenderBob, sentBobDistributionMessage2);
var ciphertextFromAlice2 = await GroupCipher(aliceStore, groupSenderAlice)
.encrypt(Uint8List.fromList(utf8.encode('Hello Mixin')));
var plaintextFromAliceDecryptedByBob2 =
await GroupCipher(bobStore, groupSenderAlice)
.decrypt(ciphertextFromAlice2);
var plaintextFromAliceDecryptedByDean2 =
await GroupCipher(deanStore, groupSenderAlice)
.decrypt(ciphertextFromAlice2); // should fail
print("Alice msg to Bob : ${plaintextFromAliceDecryptedByBob2}");
print("Alice msg to Dean : ${plaintextFromAliceDecryptedByDean2}");
@crossle are you able to help me?
final sentAliceDistributionMessage2 = await aliceSessionBuilder.create(groupSenderAlice);
This will just use the cache instead of creating a new KeyDistributionMessages
.
For more info about group messaging you can read this paper A Security Analysis of the Signal Protocol’s Group Messaging Capabilities in Comparison to Direct Messaging
This will just use the cache instead of creating a new
KeyDistributionMessages
.
I thought of something like that, so my problem remains. How can I start a new session or continue the old one without Dean in this example?
Thanks for the document, I'll look into it. I han't had the time to read the full paper yet but as far as I scanned it, I couldn't find an answer to my question. If you've a deeper understanding of the implementation @Tougee maybe you could explain to me in a few sentences where the difference between the group implementation and just using a "normal" session for every user lies?
In this demo you can just create new SenderKeyStore
for remaining members, and Dean can not decrypt later messages.
// to kick Dean out
aliceStore = InMemorySenderKeyStore();
bobStore = InMemorySenderKeyStore();
aliceGroupSessionBuilder = GroupSessionBuilder(aliceStore);
bobGroupSessionBuilder = GroupSessionBuilder(bobStore);
final sentAliceDistributionMessage2 = await aliceGroupSessionBuilder.create(aliceGroupSender);
await bobGroupSessionBuilder.process(aliceGroupSender, sentAliceDistributionMessage2);
final sentBobDistributionMessage2 = await bobGroupSessionBuilder.create(bobGroupSender);
await aliceGroupSessionBuilder.process(bobGroupSender, sentBobDistributionMessage2);
Each user in a group being tied to a unique group-ID, and using the direct messaging protocol to communicate with each host under the hood.
Group messaging provider what
In this demo you can just create new
SenderKeyStore
for remaining members, and Dean can not decrypt later messages.// to kick Dean out aliceStore = InMemorySenderKeyStore(); bobStore = InMemorySenderKeyStore(); aliceGroupSessionBuilder = GroupSessionBuilder(aliceStore); bobGroupSessionBuilder = GroupSessionBuilder(bobStore); final sentAliceDistributionMessage2 = await aliceGroupSessionBuilder.create(aliceGroupSender); await bobGroupSessionBuilder.process(aliceGroupSender, sentAliceDistributionMessage2); final sentBobDistributionMessage2 = await bobGroupSessionBuilder.create(bobGroupSender); await aliceGroupSessionBuilder.process(bobGroupSender, sentBobDistributionMessage2);
Ok thank you. But this would mean that I create a new Store for every group, am I right?
You wrote about "contractible membership" but when as you described above, a new group session needs to be created if one leaves the group, isn't that the opposite @Tougee?
Ok thank you. But this would mean that I create a new Store for every group, am I right?
Create a new _store
item inside InMemorySenderKeyStore
is OK.
add a new function like below for InMemorySenderKeyStore
,
Future<void> deleteSenderKey(SenderKeyName senderKeyName) async {
_store.remove(senderKeyName);
}
then call this delete method when someone exit group,
// to kick Dean out
await aliceStore.deleteSenderKey(aliceGroupSender);
await bobStore.deleteSenderKey(bobGroupSender);
aliceGroupSessionBuilder = GroupSessionBuilder(aliceStore);
bobGroupSessionBuilder = GroupSessionBuilder(bobStore);
and when next time call loadSenderKey
it can create a new SenderKeyRecord
for this group.
You wrote about "contractible membership" but when as you described above, a new group session needs to be created if one leaves the group, isn't that the opposite @Tougee?
What I wrote about the concept of group chat was pasted from that paper, just a perspective , and I think the design of this is not associated with this project, you need to do research by yourself.
Ok thank you. But this would mean that I create a new Store for every group, am I right?
Create a new
_store
item insideInMemorySenderKeyStore
is OK.add a new function like below for
InMemorySenderKeyStore
,Future<void> deleteSenderKey(SenderKeyName senderKeyName) async { _store.remove(senderKeyName); }
then call this delete method when someone exit group,
// to kick Dean out await aliceStore.deleteSenderKey(aliceGroupSender); await bobStore.deleteSenderKey(bobGroupSender); aliceGroupSessionBuilder = GroupSessionBuilder(aliceStore); bobGroupSessionBuilder = GroupSessionBuilder(bobStore);
and when next time call
loadSenderKey
it can create a newSenderKeyRecord
for this group.
great thank you, I'll try this.
Ok thank you. But this would mean that I create a new Store for every group, am I right?
Create a new
_store
item insideInMemorySenderKeyStore
is OK. add a new function like below forInMemorySenderKeyStore
,Future<void> deleteSenderKey(SenderKeyName senderKeyName) async { _store.remove(senderKeyName); }
then call this delete method when someone exit group,
// to kick Dean out await aliceStore.deleteSenderKey(aliceGroupSender); await bobStore.deleteSenderKey(bobGroupSender); aliceGroupSessionBuilder = GroupSessionBuilder(aliceStore); bobGroupSessionBuilder = GroupSessionBuilder(bobStore);
and when next time call
loadSenderKey
it can create a newSenderKeyRecord
for this group.great thank you, I'll try this.
how did you solve this at the end? Would be interested in this also.
Ok thank you. But this would mean that I create a new Store for every group, am I right?
Create a new
_store
item insideInMemorySenderKeyStore
is OK. add a new function like below forInMemorySenderKeyStore
,Future<void> deleteSenderKey(SenderKeyName senderKeyName) async { _store.remove(senderKeyName); }
then call this delete method when someone exit group,
// to kick Dean out await aliceStore.deleteSenderKey(aliceGroupSender); await bobStore.deleteSenderKey(bobGroupSender); aliceGroupSessionBuilder = GroupSessionBuilder(aliceStore); bobGroupSessionBuilder = GroupSessionBuilder(bobStore);
and when next time call
loadSenderKey
it can create a newSenderKeyRecord
for this group.great thank you, I'll try this.
how did you solve this at the end? Would be interested in this also.
I haven't implemented the solution. But if I would try to build a group chat using this system, the explained steps would be my starting point.
Can you provide and example with 3 group participants all sending a message? I can't get it to work for me. This is my code right now:
Which throws the following exception: