MixinNetwork / libsignal_protocol_dart

Signal Protocol library for Dart/Flutter
https://pub.dev/packages/libsignal_protocol_dart
GNU General Public License v3.0
159 stars 42 forks source link

UntrustedIdentityException #46

Closed McCrafterIV closed 3 years ago

McCrafterIV commented 3 years ago

I want to create an encrypted chat app. In the example below which reproduces the error, Bob and Jean want to send a message to Alice. The first message (in this case from Bob) can be decrypted without any problems, but the second message (from Jean) fails with the following UntrustedIdentityException exception:

Unhandled exception:
Instance of 'UntrustedIdentityException'
#0      SessionBuilder.process (package:libsignal_protocol_dart/src/session_builder.dart:42:7)
<asynchronous suspension>
#1      SessionCipher.decryptWithCallback (package:libsignal_protocol_dart/src/session_cipher.dart:108:9)
<asynchronous suspension>
#2      main (package:project/test.dart:171:5)
<asynchronous suspension>

This is the code I used to reproduces the error:

import 'dart:convert';
import 'dart:typed_data';

import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';

void main() async {
  //
  // BOB SETUP
  //
  final bobAddress = SignalProtocolAddress('bob', 1);
  final bobIdentityKeyPair = generateIdentityKeyPair();
  final bobRegistrationId = generateRegistrationId(false);

  final bobPreKeys = generatePreKeys(0, 110);
  final bobSignedPreKey = generateSignedPreKey(bobIdentityKeyPair, 0);

  final bobPreKeyStore = InMemoryPreKeyStore();
  final bobSessionStore = InMemorySessionStore();
  final bobSignedPreKeyStore = InMemorySignedPreKeyStore();
  final bobIdentityKeyStore =
      InMemoryIdentityKeyStore(bobIdentityKeyPair, bobRegistrationId);

  for (var p in bobPreKeys) {
    await bobPreKeyStore.storePreKey(p.id, p);
  }
  await bobSignedPreKeyStore.storeSignedPreKey(
    bobSignedPreKey.id,
    bobSignedPreKey,
  );

  //
  // JEAN SETUP
  //
  final jeanRegistrationId = generateRegistrationId(false);
  final jeanIdentityKeyPair = generateIdentityKeyPair();
  final jeanAddress = SignalProtocolAddress('jean', 1);

  final jeanPreKeys = generatePreKeys(0, 110);
  final jeanSignedPreKey = generateSignedPreKey(jeanIdentityKeyPair, 0);

  final jeanPreKeyStore = InMemoryPreKeyStore();
  final jeanSessionStore = InMemorySessionStore();
  final jeanSignedPreKeyStore = InMemorySignedPreKeyStore();
  final jeanIdentityKeyStore =
      InMemoryIdentityKeyStore(jeanIdentityKeyPair, jeanRegistrationId);

  final jeanSignalProtocolStore =
      InMemorySignalProtocolStore(jeanIdentityKeyPair, 1);

  for (var p in jeanPreKeys) {
    await jeanSignalProtocolStore.storePreKey(p.id, p);
  }
  await jeanSignalProtocolStore.storeSignedPreKey(
      jeanSignedPreKey.id, jeanSignedPreKey);

  //
  // ALICE SETUP
  //
  // Should get remote from the server
  final aliceRegistrationId = generateRegistrationId(false);
  final aliceIdentityKeyPair = generateIdentityKeyPair();
  final aliceAddress = SignalProtocolAddress('alice', 1);

  final alicePreKeys = generatePreKeys(0, 110);
  final aliceSignedPreKey = generateSignedPreKey(aliceIdentityKeyPair, 0);
  final aliceIdentityKeyStore =
      InMemoryIdentityKeyStore(aliceIdentityKeyPair, aliceRegistrationId);

  final aliceSignalProtocolStore =
      InMemorySignalProtocolStore(aliceIdentityKeyPair, 1);

  for (var p in alicePreKeys) {
    await aliceSignalProtocolStore.storePreKey(p.id, p);
  }
  await aliceSignalProtocolStore.storeSignedPreKey(
      aliceSignedPreKey.id, aliceSignedPreKey);

  //
  // Preparing connection
  //
  final bobSessionBuilder = SessionBuilder(
    bobSessionStore,
    bobPreKeyStore,
    bobSignedPreKeyStore,
    bobIdentityKeyStore,
    bobAddress,
  );
  await bobSessionBuilder.processPreKeyBundle(
    PreKeyBundle(
      aliceRegistrationId,
      1,
      alicePreKeys[0].id,
      alicePreKeys[0].getKeyPair().publicKey,
      aliceSignedPreKey.id,
      aliceSignedPreKey.getKeyPair().publicKey,
      aliceSignedPreKey.signature,
      aliceIdentityKeyPair.getPublicKey(),
    ),
  );

  final jeanSessionBuilder = SessionBuilder(
    jeanSessionStore,
    jeanPreKeyStore,
    jeanSignedPreKeyStore,
    jeanIdentityKeyStore,
    jeanAddress,
  );

  await jeanSessionBuilder.processPreKeyBundle(
    PreKeyBundle(
      aliceRegistrationId,
      1,
      alicePreKeys[0].id,
      alicePreKeys[0].getKeyPair().publicKey,
      aliceSignedPreKey.id,
      aliceSignedPreKey.getKeyPair().publicKey,
      aliceSignedPreKey.signature,
      aliceIdentityKeyPair.getPublicKey(),
    ),
  );

  //
  // ENCRYPTION
  //
  final bobSessionCipher = SessionCipher(
    bobSessionStore,
    bobPreKeyStore,
    bobSignedPreKeyStore,
    bobIdentityKeyStore,
    bobAddress,
  );
  final ciphertextFromBob = await bobSessionCipher
      .encrypt(Uint8List.fromList(utf8.encode('Hello from Bob')));

  final jeanSessionCipher = SessionCipher(
    jeanSessionStore,
    jeanPreKeyStore,
    jeanSignedPreKeyStore,
    jeanIdentityKeyStore,
    jeanAddress,
  );
  final ciphertextFromJean = await jeanSessionCipher
      .encrypt(Uint8List.fromList(utf8.encode('Hello from Jean')));

  //
  // DECRYPTION
  //
  final aliceSessionCipher = SessionCipher(
    aliceSignalProtocolStore.sessionStore,
    aliceSignalProtocolStore.preKeyStore,
    aliceSignalProtocolStore.signedPreKeyStore,
    aliceIdentityKeyStore,
    aliceAddress,
  );

  if (ciphertextFromBob.getType() == CiphertextMessage.prekeyType) {
    await aliceSessionCipher.decryptWithCallback(
        ciphertextFromBob as PreKeySignalMessage, (plaintext) {
      // ignore: avoid_print
      print(utf8.decode(plaintext));
    });
  }

  if (ciphertextFromJean.getType() == CiphertextMessage.prekeyType) {
    await aliceSessionCipher.decryptWithCallback(
        ciphertextFromJean as PreKeySignalMessage, (plaintext) {
      // ignore: avoid_print
      print(utf8.decode(plaintext));
    });
  }
}

How can I resolve this issue? What have I done/understood wrong?

Thanks in advance

crossle commented 3 years ago

When you create SessionCipher, please use remote address on the last param.

McCrafterIV commented 3 years ago

When you create SessionCipher, please use remote address on the last param.

I followed your introductions but still get an Exception:

Unhandled exception:
Assertion failed: Instance of 'InvalidKeyException'
#0      SessionState.getSenderRatchetKey (package:libsignal_protocol_dart/src/state/session_state.dart:104:7)
#1      SessionCipher.encrypt (package:libsignal_protocol_dart/src/session_cipher.dart:58:42)
<asynchronous suspension>
#2      main (package:project/test.dart:132:29)
<asynchronous suspension>

My code now looks the following:

import 'dart:convert';
import 'dart:typed_data';

import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';

void main() async {
  //
  // BOB SETUP
  //
  final bobAddress = SignalProtocolAddress('bob', 1);
  final bobIdentityKeyPair = generateIdentityKeyPair();
  final bobRegistrationId = generateRegistrationId(false);

  final bobPreKeys = generatePreKeys(0, 110);
  final bobSignedPreKey = generateSignedPreKey(bobIdentityKeyPair, 0);

  final bobPreKeyStore = InMemoryPreKeyStore();
  final bobSessionStore = InMemorySessionStore();
  final bobSignedPreKeyStore = InMemorySignedPreKeyStore();
  final bobIdentityKeyStore =
      InMemoryIdentityKeyStore(bobIdentityKeyPair, bobRegistrationId);

  for (var p in bobPreKeys) {
    await bobPreKeyStore.storePreKey(p.id, p);
  }
  await bobSignedPreKeyStore.storeSignedPreKey(
    bobSignedPreKey.id,
    bobSignedPreKey,
  );

  //
  // JEAN SETUP
  //
  final jeanRegistrationId = generateRegistrationId(false);
  final jeanIdentityKeyPair = generateIdentityKeyPair();
  final jeanAddress = SignalProtocolAddress('jean', 1);

  final jeanPreKeys = generatePreKeys(0, 110);
  final jeanSignedPreKey = generateSignedPreKey(jeanIdentityKeyPair, 0);

  final jeanPreKeyStore = InMemoryPreKeyStore();
  final jeanSessionStore = InMemorySessionStore();
  final jeanSignedPreKeyStore = InMemorySignedPreKeyStore();
  final jeanIdentityKeyStore =
      InMemoryIdentityKeyStore(jeanIdentityKeyPair, jeanRegistrationId);

  final jeanSignalProtocolStore =
      InMemorySignalProtocolStore(jeanIdentityKeyPair, 1);

  for (var p in jeanPreKeys) {
    await jeanSignalProtocolStore.storePreKey(p.id, p);
  }
  await jeanSignalProtocolStore.storeSignedPreKey(
      jeanSignedPreKey.id, jeanSignedPreKey);

  //
  // ALICE SETUP
  //
  // Should get remote from the server
  final aliceRegistrationId = generateRegistrationId(false);
  final aliceIdentityKeyPair = generateIdentityKeyPair();
  final aliceAddress = SignalProtocolAddress('alice', 1);

  final alicePreKeys = generatePreKeys(0, 110);
  final aliceSignedPreKey = generateSignedPreKey(aliceIdentityKeyPair, 0);
  final aliceIdentityKeyStore =
      InMemoryIdentityKeyStore(aliceIdentityKeyPair, aliceRegistrationId);

  final aliceSignalProtocolStore =
      InMemorySignalProtocolStore(aliceIdentityKeyPair, 1);

  for (var p in alicePreKeys) {
    await aliceSignalProtocolStore.storePreKey(p.id, p);
  }
  await aliceSignalProtocolStore.storeSignedPreKey(
      aliceSignedPreKey.id, aliceSignedPreKey);

  //
  // Preparing connection
  //
  final bobSessionBuilder = SessionBuilder(
    bobSessionStore,
    bobPreKeyStore,
    bobSignedPreKeyStore,
    bobIdentityKeyStore,
    bobAddress,
  );
  await bobSessionBuilder.processPreKeyBundle(
    PreKeyBundle(
      aliceRegistrationId,
      1,
      alicePreKeys[0].id,
      alicePreKeys[0].getKeyPair().publicKey,
      aliceSignedPreKey.id,
      aliceSignedPreKey.getKeyPair().publicKey,
      aliceSignedPreKey.signature,
      aliceIdentityKeyPair.getPublicKey(),
    ),
  );

  final jeanSessionBuilder = SessionBuilder(
    jeanSessionStore,
    jeanPreKeyStore,
    jeanSignedPreKeyStore,
    jeanIdentityKeyStore,
    jeanAddress,
  );

  await jeanSessionBuilder.processPreKeyBundle(
    PreKeyBundle(
      aliceRegistrationId,
      1,
      alicePreKeys[0].id,
      alicePreKeys[0].getKeyPair().publicKey,
      aliceSignedPreKey.id,
      aliceSignedPreKey.getKeyPair().publicKey,
      aliceSignedPreKey.signature,
      aliceIdentityKeyPair.getPublicKey(),
    ),
  );

  //
  // ENCRYPTION
  //
  final bobSessionCipher = SessionCipher(
    bobSessionStore,
    bobPreKeyStore,
    bobSignedPreKeyStore,
    bobIdentityKeyStore,
    aliceAddress,
  );
  final ciphertextFromBob = await bobSessionCipher
      .encrypt(Uint8List.fromList(utf8.encode('Hello from Bob')));

  final jeanSessionCipher = SessionCipher(
    jeanSessionStore,
    jeanPreKeyStore,
    jeanSignedPreKeyStore,
    jeanIdentityKeyStore,
    aliceAddress,
  );
  final ciphertextFromJean = await jeanSessionCipher
      .encrypt(Uint8List.fromList(utf8.encode('Hello from Jean')));

  //
  // DECRYPTION
  //
  final aliceSessionCipher1 = SessionCipher(
    aliceSignalProtocolStore.sessionStore,
    aliceSignalProtocolStore.preKeyStore,
    aliceSignalProtocolStore.signedPreKeyStore,
    aliceIdentityKeyStore,
    bobAddress,
  );
  final aliceSessionCipher2 = SessionCipher(
    aliceSignalProtocolStore.sessionStore,
    aliceSignalProtocolStore.preKeyStore,
    aliceSignalProtocolStore.signedPreKeyStore,
    aliceIdentityKeyStore,
    jeanAddress,
  );

  if (ciphertextFromBob.getType() == CiphertextMessage.prekeyType) {
    await aliceSessionCipher1.decryptWithCallback(
        ciphertextFromBob as PreKeySignalMessage, (plaintext) {
      // ignore: avoid_print
      print(utf8.decode(plaintext));
    });
  }

  if (ciphertextFromJean.getType() == CiphertextMessage.prekeyType) {
    await aliceSessionCipher2.decryptWithCallback(
        ciphertextFromJean as PreKeySignalMessage, (plaintext) {
      // ignore: avoid_print
      print(utf8.decode(plaintext));
    });
  }
}

What am I doing wrong now?

crossle commented 3 years ago

When you create SessionBuilder should use remote address on the last param.

  final bobSessionBuilder = SessionBuilder(
    bobSessionStore,
    bobPreKeyStore,
    bobSignedPreKeyStore,
    bobIdentityKeyStore,
    aliceAddress,
  );
McCrafterIV commented 3 years ago

Now it works, thanks! I after that got InvalidKeyIdException - No such prekeyrecord! - 0 which was fixed by using the preKey index 1 (or any one that wasn't used before, since pre keys can only be used once) on the jeanSessionBuilder. For everyone interested, this is the now fixed and working code:

Click to expand ```dart import 'dart:convert'; import 'dart:typed_data'; import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'; void main() async { // // BOB SETUP // final bobAddress = SignalProtocolAddress('bob', 1); final bobIdentityKeyPair = generateIdentityKeyPair(); final bobRegistrationId = generateRegistrationId(false); final bobPreKeys = generatePreKeys(0, 110); final bobSignedPreKey = generateSignedPreKey(bobIdentityKeyPair, 0); final bobPreKeyStore = InMemoryPreKeyStore(); final bobSessionStore = InMemorySessionStore(); final bobSignedPreKeyStore = InMemorySignedPreKeyStore(); final bobIdentityKeyStore = InMemoryIdentityKeyStore(bobIdentityKeyPair, bobRegistrationId); for (var p in bobPreKeys) { await bobPreKeyStore.storePreKey(p.id, p); } await bobSignedPreKeyStore.storeSignedPreKey( bobSignedPreKey.id, bobSignedPreKey, ); // // JEAN SETUP // final jeanRegistrationId = generateRegistrationId(false); final jeanIdentityKeyPair = generateIdentityKeyPair(); final jeanAddress = SignalProtocolAddress('jean', 1); final jeanPreKeys = generatePreKeys(0, 110); final jeanSignedPreKey = generateSignedPreKey(jeanIdentityKeyPair, 0); final jeanPreKeyStore = InMemoryPreKeyStore(); final jeanSessionStore = InMemorySessionStore(); final jeanSignedPreKeyStore = InMemorySignedPreKeyStore(); final jeanIdentityKeyStore = InMemoryIdentityKeyStore(jeanIdentityKeyPair, jeanRegistrationId); final jeanSignalProtocolStore = InMemorySignalProtocolStore(jeanIdentityKeyPair, 1); for (var p in jeanPreKeys) { await jeanSignalProtocolStore.storePreKey(p.id, p); } await jeanSignalProtocolStore.storeSignedPreKey( jeanSignedPreKey.id, jeanSignedPreKey); // // ALICE SETUP // // Should get remote from the server final aliceRegistrationId = generateRegistrationId(false); final aliceIdentityKeyPair = generateIdentityKeyPair(); final aliceAddress = SignalProtocolAddress('alice', 1); final alicePreKeys = generatePreKeys(0, 110); final aliceSignedPreKey = generateSignedPreKey(aliceIdentityKeyPair, 0); final aliceIdentityKeyStore = InMemoryIdentityKeyStore(aliceIdentityKeyPair, aliceRegistrationId); final aliceSignalProtocolStore = InMemorySignalProtocolStore(aliceIdentityKeyPair, 1); for (var p in alicePreKeys) { await aliceSignalProtocolStore.storePreKey(p.id, p); } await aliceSignalProtocolStore.storeSignedPreKey( aliceSignedPreKey.id, aliceSignedPreKey); // // Preparing connection // final bobSessionBuilder = SessionBuilder( bobSessionStore, bobPreKeyStore, bobSignedPreKeyStore, bobIdentityKeyStore, aliceAddress, ); await bobSessionBuilder.processPreKeyBundle( PreKeyBundle( aliceRegistrationId, 1, alicePreKeys[0].id, alicePreKeys[0].getKeyPair().publicKey, aliceSignedPreKey.id, aliceSignedPreKey.getKeyPair().publicKey, aliceSignedPreKey.signature, aliceIdentityKeyPair.getPublicKey(), ), ); final jeanSessionBuilder = SessionBuilder( jeanSessionStore, jeanPreKeyStore, jeanSignedPreKeyStore, jeanIdentityKeyStore, aliceAddress, ); await jeanSessionBuilder.processPreKeyBundle( PreKeyBundle( aliceRegistrationId, 1, alicePreKeys[1].id, alicePreKeys[1].getKeyPair().publicKey, aliceSignedPreKey.id, aliceSignedPreKey.getKeyPair().publicKey, aliceSignedPreKey.signature, aliceIdentityKeyPair.getPublicKey(), ), ); // // ENCRYPTION // final bobSessionCipher = SessionCipher( bobSessionStore, bobPreKeyStore, bobSignedPreKeyStore, bobIdentityKeyStore, aliceAddress, ); final ciphertextFromBob = await bobSessionCipher .encrypt(Uint8List.fromList(utf8.encode('Hello from Bob'))); final jeanSessionCipher = SessionCipher( jeanSessionStore, jeanPreKeyStore, jeanSignedPreKeyStore, jeanIdentityKeyStore, aliceAddress, ); final ciphertextFromJean = await jeanSessionCipher .encrypt(Uint8List.fromList(utf8.encode('Hello from Jean'))); // // DECRYPTION // final aliceSessionCipher1 = SessionCipher( aliceSignalProtocolStore.sessionStore, aliceSignalProtocolStore.preKeyStore, aliceSignalProtocolStore.signedPreKeyStore, aliceIdentityKeyStore, bobAddress, ); final aliceSessionCipher2 = SessionCipher( aliceSignalProtocolStore.sessionStore, aliceSignalProtocolStore.preKeyStore, aliceSignalProtocolStore.signedPreKeyStore, aliceIdentityKeyStore, jeanAddress, ); if (ciphertextFromBob.getType() == CiphertextMessage.prekeyType) { await aliceSessionCipher1.decryptWithCallback( ciphertextFromBob as PreKeySignalMessage, (plaintext) { // ignore: avoid_print print(utf8.decode(plaintext)); }); } if (ciphertextFromJean.getType() == CiphertextMessage.prekeyType) { await aliceSessionCipher2.decryptWithCallback( ciphertextFromJean as PreKeySignalMessage, (plaintext) { // ignore: avoid_print print(utf8.decode(plaintext)); }); } } ```