Skip to content

Instance of 'InvalidKeyException' #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
alsiPanda opened this issue Jan 27, 2021 · 7 comments
Closed

Instance of 'InvalidKeyException' #23

alsiPanda opened this issue Jan 27, 2021 · 7 comments

Comments

@alsiPanda
Copy link

I am getting the following error when tryng to encrypt text:

Assertion failed: Instance of 'InvalidKeyException'
#0 SessionState.getSenderRatchetKey (package:libsignal_protocol_dart/src/state/SessionState.dart:103:7)
#1 SessionCipher.encrypt (package:libsignal_protocol_dart/src/SessionCipher.dart:59:40)
#2 SignalProtocol.sessionEncrypt (package:Okuna/services/signal_protocol.dart:166:50)
#3 OBDConversationState.sendMessage (package:Okuna/pages/home/pages/channel_chat/conversation.dart:155:26)
#4 ChatInputToolbar._sendMessage (package:dash_chat/src/chat_input_toolbar.dart:163:19)
#5 ChatInputToolbar.build. (package:dash_chat/src/chat_input_toolbar.dart:149:31)
#6 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:993:19)
#7 _InkResponseState.build. (package:flutter/src/material/ink_well.dart:1111:38)
#8 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:183:24)
#9 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:598:11)
#10 BaseTapGestureRecognizer._checkUp

This is my code - SignalProtocol class:

`class SignalProtocol{

  IdentityKeyPair identityKeyPair;
  int registrationId;

  List<PreKeyRecord> preKeys;

  SignedPreKeyRecord signedPreKey;

  int userId, deviceid;

  UserService userService;

  // No way to store
  InMemorySessionStore sessionStore;
  InMemoryPreKeyStore preKeyStore;
  InMemorySignedPreKeyStore signedPreKeyStore;
  InMemoryIdentityKeyStore identityStore;
  PreKeyBundle myBundle;

  Map<String, String> bundleMap = Map<String, String>();

  SignalProtocol({@required this.userService});

  void install(int UserId, int DeviceId) async {


    await userService.getIdentityKeyPair().then((s){
      if(s==null){
        identityKeyPair = KeyHelper.generateIdentityKeyPair();
      }else{
        Uint8List d = Uint8List.fromList(jsonDecode(s).cast<int>());
        identityKeyPair = IdentityKeyPair.fromSerialized(d);
      }
    });

    await userService.getUserId().then((s){
      if(s==null){
        userId = UserId;
      }else{
        userId = int.parse(s);
      }
    });
    await userService.getRegistrationId().then((s){
      if(s==null){
        registrationId = KeyHelper.generateRegistrationId(false);
      }else{
        registrationId = int.parse(s);
      }
    });
    await userService.getDeviceid().then((s){
      if(s==null){
        deviceid = DeviceId;
      }else{
        deviceid = int.parse(s);
      }
    });

    await userService.getSignedPreKeyRecord().then((s){
      if(s==null){
        signedPreKey = KeyHelper.generateSignedPreKey(identityKeyPair, 0);
      }else{
        Uint8List d = Uint8List.fromList(jsonDecode(s).cast<int>());
        signedPreKey = SignedPreKeyRecord.fromSerialized(d);
      }
    });
    await userService.getPreKeyRecordList().then((s){
      // print('sp prekeys - ${s} ');
      if(s==null){
        preKeys = KeyHelper.generatePreKeys(0, 110);
        print('prekeys init - ${preKeys.runtimeType}}');
      }else{
        print('jsonDecode prekeys ${(jsonDecode(s)).runtimeType}');

        var stringList = jsonDecode(s) as List<dynamic>;
        print('sp string list - ${stringList.runtimeType}');
        preKeys = stringList.map((e) {
          print('inloop ${e.runtimeType}');
          var elist = Uint8List.fromList((e as List<dynamic>).cast<int>());
          print('after cast ${elist.runtimeType}');
          return PreKeyRecord.fromBuffer(elist);
        }
             ).toList();

      }
      print('prekeys - ${preKeys.length} ');
    });

    sessionStore = InMemorySessionStore();
    preKeyStore = InMemoryPreKeyStore();
    signedPreKeyStore = InMemorySignedPreKeyStore();
    identityStore = InMemoryIdentityKeyStore(identityKeyPair, registrationId);

    print('prekeys - ${preKeys.length} ');
    for (var p in preKeys) {
      preKeyStore.storePreKey(p.id, p);
    }
    signedPreKeyStore.storeSignedPreKey(signedPreKey.id, signedPreKey);

    myBundle = PreKeyBundle(
        registrationId,
        0, // deviceId
        preKeys.first.id,
        preKeys.first.getKeyPair().publicKey,
        signedPreKey.id,
        signedPreKey.getKeyPair().publicKey,
        signedPreKey.signature,
        identityKeyPair.getPublicKey());

    bundleMap['registrationId'] = registrationId.toString();
// List<String> pks = preKeys.map((e) => e.serialize().toString()).toList();
    PreKeyRecord pks = preKeys.first;
    bundleMap['preKey'] = jsonEncode(pks.getKeyPair().publicKey.serialize());
    bundleMap['preKeyId'] = preKeys.first.id.toString();
    bundleMap['signedPreKeyId'] = signedPreKey.id.toString();
    bundleMap['signedPreKey'] = jsonEncode(signedPreKey.getKeyPair().publicKey.serialize());
    bundleMap['signature'] = signedPreKey.signature.toString();
    bundleMap['identityKeyPair'] = identityKeyPair.getPublicKey().serialize().toString();

    userService.setSignalData(identityKeyPair: identityKeyPair,
          registrationId: registrationId,
          preKeys: preKeys, signedPreKey: signedPreKey,
          userId: UserId, deviceid: DeviceId);

}

  String sessionEncrypt(PreKeyBundle recievedBundle, String username, String msg) {
    var remoteAddress = SignalProtocolAddress('remote', 1);
    var sessionBuilder = SessionBuilder(sessionStore, preKeyStore,
        signedPreKeyStore, identityStore, remoteAddress);

// sessionBuilder.processPreKeyBundle(recievedBundle);

    var sessionCipher = SessionCipher(sessionStore, preKeyStore,
        signedPreKeyStore, identityStore, remoteAddress);
    CiphertextMessage ciphertext = sessionCipher.encrypt(utf8.encode(msg));

    return ciphertext.serialize().toString();
  }

  String sessionDecrypt(PreKeyBundle recievedBundle, String username, String ciphertext){
    var remoteAddress = SignalProtocolAddress(username, 1);
    var sessionBuilder = SessionBuilder(sessionStore, preKeyStore,
        signedPreKeyStore, identityStore, remoteAddress);

    sessionBuilder.processPreKeyBundle(recievedBundle);

   var sessionCipher = SessionCipher(sessionStore, preKeyStore,
        signedPreKeyStore, identityStore, remoteAddress);

    PreKeySignalMessage mess = PreKeySignalMessage(Uint8List.fromList(ciphertext.codeUnits));
    String text = sessionCipher.decrypt(mess).toString();

    print('Decrypted = ${text}');

    return text;
  }

  void groupSessionEncrypt() {
    var senderKeyName = SenderKeyName("", SignalProtocolAddress("sender", 1));
    var senderKeyStore = InMemorySenderKeyStore();
    var groupSession = GroupCipher(senderKeyStore, senderKeyName);
    groupSession.encrypt(utf8.encode("Hello Mixin"));
  }

}`

I should have mostly followed the example and cant figure out why I am still getting error. The receivedBundle is being reconstructed from its components saved in the server and fetched. The code used for bundle is as follows:

`var bundle = json.decode(otherUser.publicKey);

int registrationId = int.parse(bundle['registrationId'].toString());
List<int> stringList = jsonDecode(bundle['preKey']).cast<int>();
final key = DjbECPublicKey(Uint8List.fromList(stringList));

ECPublicKey preKey = libs.Curve.decodePoint(key.serialize(), 1);
final key2 = DjbECPublicKey(Uint8List.fromList(jsonDecode(bundle['signedPreKey']).cast<int>()));
ECPublicKey signedPreKey = libs.Curve.decodePoint(key2.serialize(), 1);
Uint8List signedPreKeySignature = Uint8List.fromList(jsonDecode(bundle['signature']).cast<int>());
int preKeyId = int.parse(bundle['preKeyId']);
int signedPreKeyId = int.parse(bundle['signedPreKeyId']);
IdentityKey identityKey = IdentityKey.fromBytes(Uint8List.fromList(jsonDecode(bundle['identityKeyPair']).cast<int>()), 0) ;
setState(() {
  recieverBundle = PreKeyBundle(registrationId, 1, preKeyId, preKey, signedPreKeyId, signedPreKey, signedPreKeySignature, identityKey);
});`

Need some help in figuring out what I am doing wrong in the implementation.

@xni06
Copy link

xni06 commented Jan 27, 2021

I see that you're not making use of the SignalProtocolStore - take a look at how it should be used.

Then, in your sessionEncrypt method, instead of doing the following:

var sessionBuilder = SessionBuilder(sessionStore, 
                                    preKeyStore,
                                    signedPreKeyStore, 
                                    identityStore, 
                                    remoteAddress);

var sessionCipher = SessionCipher(sessionStore, 
                                  preKeyStore,
                                  signedPreKeyStore, 
                                  identityStore, 
                                  remoteAddress);

... do something like this instead:

String sessionEncrypt(PreKeyBundle preKeyBundle, 
                      SignalProtocolStore store, // NEW
                      String msg) {

    final remoteAddress = SignalProtocolAddress('remote', 1);

    if (!store.containsSession(remoteAddress)) {
        SessionBuilder.fromSignalStore(store, remoteAddress).processPreKeyBundle(preKeyBundle);
    }

    final sessionCipher = SessionCipher.fromStore(store, address);
    return ciphertext.serialize().toString();

@alsiPanda
Copy link
Author

Thanks @xni06 , The encryption part seems to be working with this. The decryption part is giving the following error:

InvalidKeyIdException - No such signedprekeyrecord! 0

The code for my decryption is :

String sessionDecrypt(PreKeyBundle recievedBundle, String username, String ciphertext){
    final remoteAddress = SignalProtocolAddress('remote', 1);
    if (!spStore.containsSession(remoteAddress)) {
      SessionBuilder.fromSignalStore(spStore, remoteAddress).processPreKeyBundle(
          recievedBundle);
    }
    final sessionCipher = SessionCipher.fromStore(spStore, remoteAddress);

    PreKeySignalMessage mess = PreKeySignalMessage(Uint8List.fromList(ciphertext.codeUnits));
    String text = sessionCipher.decrypt(mess).toString();
    print('Decrypted = ${text}');
    return text;
  }

Also, can the 'remote' used to create remoteAddress be replaced by a unique username ?

@xni06
Copy link

xni06 commented Jan 27, 2021

Similar to when encrypting a message - it all depends on whether your store contains a session or not. In the case of decrypting:

if store contains the session {
    use SessionCipher.decryptFromSignal
}
else {
  use SessionCipher.decrypt
}

Also, can the 'remote' used to create remoteAddress be replaced by a unique username ?

Yes

@alsiPanda
Copy link
Author

alsiPanda commented Jan 28, 2021

Thanks @xni06 , I have made the changes, but now getting the following error on the recieving end:

InvalidMessageException - InvalidProtocolBufferException: Protocol
message contained an invalid tag (zero).

I am converting the encrypted message from Uint8List to String using String.fromCharCodes(ciphertext.serialize()) and send this via websocket. On the reciever end I am using

String sessionDecrypt(PreKeyBundle recievedBundle, String username, String ciphertext){
    final remoteAddress = SignalProtocolAddress('$username', 1);
    final sessionCipher = SessionCipher.fromStore(spStore, remoteAddress);
    String text = '';

    if (!spStore.containsSession(remoteAddress)) {
      SignalMessage mess = SignalMessage.fromSerialized(Uint8List.fromList(ciphertext.codeUnits));
      text = String.fromCharCodes(sessionCipher.decryptFromSignal(mess));
    }else{
      PreKeySignalMessage mess = PreKeySignalMessage(Uint8List.fromList(ciphertext.codeUnits));
      text = String.fromCharCodes(sessionCipher.decrypt(mess));
    }

    print('Decrypted = ${text}');

    return text;
  }

I have checked the string after encrypting and the string recieved before decrypting, and they are the same.
I have also tried saving the uint8list as List instead of string. Still getting the same error. Here is one of the messages encrypted :

[51,8,254,255,255,7,18,33,5,229,245,176,108,213,142,164,187,91,113,153,179,193,53,235,54,200,101,235,116,45,52,121,239,12,187,81,209,165,158,7,14,26,33,5,3
2,183,174,91,146,188,54,92,226,166,144,233,29,245,85,104,210,117,149,252,103,136,105,53,227,229,76,86,4,123,157,82,34,66,51,10,33,5,202,205,163,241,85,15,135,138,226,57,154,203,19,118,1
27,191,113,65,41,101,87,0,53,36,58,146,118,193,153,45,84,117,16,0,24,0,34,16,191,79,16,99,95,30,15,0,250,92,154,245,236,69,228,101,113,2,228,241,239,148,51,57,40,163,94,48,0]

It has a length of 514. I tried removing the final 0 using removeLast(), but still getting the same error.

@xni06
Copy link

xni06 commented Jan 28, 2021

Are there any obvious differences in approach between your implementation and that of the existing unit tests?

@woinbo
Copy link

woinbo commented Jan 28, 2021

Hey, @xni06 I am not getting how to implement this package into my flutter chatting app. Can you please guide me on how to implement signal protocall?

@crossle
Copy link
Member

crossle commented Feb 2, 2021

Example work now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants