示例#1
0
        /**
         * Build a new session from a {@link org.whispersystems.libaxolotl.state.PreKeyBundle} retrieved from
         * a server.
         *
         * @param preKey A PreKey for the destination recipient, retrieved from a server.
         * @throws InvalidKeyException when the {@link org.whispersystems.libaxolotl.state.PreKeyBundle} is
         *                             badly formatted.
         * @throws org.whispersystems.libaxolotl.UntrustedIdentityException when the sender's
         *                                                                  {@link IdentityKey} is not
         *                                                                  trusted.
         */
        public void process(PreKeyBundle preKey)
        {
            lock (SessionCipher.SESSION_LOCK)
            {
                if (!identityKeyStore.IsTrustedIdentity(remoteAddress.getName(), preKey.getIdentityKey()))
                {
                    throw new UntrustedIdentityException(remoteAddress.getName(), preKey.getIdentityKey());
                }

                if (preKey.getSignedPreKey() != null &&
                    !Curve.verifySignature(preKey.getIdentityKey().getPublicKey(),
                                           preKey.getSignedPreKey().serialize(),
                                           preKey.getSignedPreKeySignature()))
                {
                    throw new InvalidKeyException("Invalid signature on device key!");
                }

                if (preKey.getSignedPreKey() == null && preKey.getPreKey() == null)
                {
                    throw new InvalidKeyException("Both signed and unsigned prekeys are absent!");
                }

                bool              supportsV3        = preKey.getSignedPreKey() != null;
                SessionRecord     sessionRecord     = sessionStore.LoadSession(remoteAddress);
                ECKeyPair         ourBaseKey        = Curve.generateKeyPair();
                ECPublicKey       theirSignedPreKey = supportsV3 ? preKey.getSignedPreKey() : preKey.getPreKey();
                ECPublicKey       test = preKey.getPreKey(); // TODO: cleanup
                May <ECPublicKey> theirOneTimePreKey   = (test == null) ? May <ECPublicKey> .NoValue : new May <ECPublicKey>(test);
                May <uint>        theirOneTimePreKeyId = theirOneTimePreKey.HasValue ? new May <uint>(preKey.getPreKeyId()) :
                                                         May <uint> .NoValue;

                AliceAxolotlParameters.Builder parameters = AliceAxolotlParameters.newBuilder();

                parameters.setOurBaseKey(ourBaseKey)
                .setOurIdentityKey(identityKeyStore.GetIdentityKeyPair())
                .setTheirIdentityKey(preKey.getIdentityKey())
                .setTheirSignedPreKey(theirSignedPreKey)
                .setTheirRatchetKey(theirSignedPreKey)
                .setTheirOneTimePreKey(supportsV3 ? theirOneTimePreKey : May <ECPublicKey> .NoValue);

                if (!sessionRecord.isFresh())
                {
                    sessionRecord.archiveCurrentState();
                }

                RatchetingSession.initializeSession(sessionRecord.getSessionState(),
                                                    supportsV3 ? (uint)3 : 2,
                                                    parameters.create());

                sessionRecord.getSessionState().setUnacknowledgedPreKeyMessage(theirOneTimePreKeyId, preKey.getSignedPreKeyId(), ourBaseKey.getPublicKey());
                sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.GetLocalRegistrationId());
                sessionRecord.getSessionState().setRemoteRegistrationId(preKey.getRegistrationId());
                sessionRecord.getSessionState().setAliceBaseKey(ourBaseKey.getPublicKey().serialize());

                sessionStore.StoreSession(remoteAddress, sessionRecord);
                identityKeyStore.SaveIdentity(remoteAddress.getName(), preKey.getIdentityKey());
            }
        }
示例#2
0
        private KeyExchangeMessage processInitiate(KeyExchangeMessage message)
        {
            uint          flags         = KeyExchangeMessage.RESPONSE_FLAG;
            SessionRecord sessionRecord = sessionStore.LoadSession(remoteAddress);

            if (message.getVersion() >= 3 &&
                !Curve.verifySignature(message.getIdentityKey().getPublicKey(),
                                       message.getBaseKey().serialize(),
                                       message.getBaseKeySignature()))
            {
                throw new InvalidKeyException("Bad signature!");
            }

            SymmetricAxolotlParameters.Builder builder = SymmetricAxolotlParameters.newBuilder();

            if (!sessionRecord.getSessionState().hasPendingKeyExchange())
            {
                builder.setOurIdentityKey(identityKeyStore.GetIdentityKeyPair())
                .setOurBaseKey(Curve.generateKeyPair())
                .setOurRatchetKey(Curve.generateKeyPair());
            }
            else
            {
                builder.setOurIdentityKey(sessionRecord.getSessionState().getPendingKeyExchangeIdentityKey())
                .setOurBaseKey(sessionRecord.getSessionState().getPendingKeyExchangeBaseKey())
                .setOurRatchetKey(sessionRecord.getSessionState().getPendingKeyExchangeRatchetKey());
                flags |= KeyExchangeMessage.SIMULTAENOUS_INITIATE_FLAG;
            }

            builder.setTheirBaseKey(message.getBaseKey())
            .setTheirRatchetKey(message.getRatchetKey())
            .setTheirIdentityKey(message.getIdentityKey());

            SymmetricAxolotlParameters parameters = builder.create();

            if (!sessionRecord.isFresh())
            {
                sessionRecord.archiveCurrentState();
            }

            RatchetingSession.initializeSession(sessionRecord.getSessionState(),
                                                Math.Min(message.getMaxVersion(), CiphertextMessage.CURRENT_VERSION),
                                                parameters);

            sessionStore.StoreSession(remoteAddress, sessionRecord);
            identityKeyStore.SaveIdentity(remoteAddress.getName(), message.getIdentityKey());

            byte[] baseKeySignature = Curve.calculateSignature(parameters.getOurIdentityKey().getPrivateKey(),
                                                               parameters.getOurBaseKey().getPublicKey().serialize());

            return(new KeyExchangeMessage(sessionRecord.getSessionState().getSessionVersion(),
                                          message.getSequence(), flags,
                                          parameters.getOurBaseKey().getPublicKey(),
                                          baseKeySignature, parameters.getOurRatchetKey().getPublicKey(),
                                          parameters.getOurIdentityKey().getPublicKey()));
        }
示例#3
0
        private void processResponse(KeyExchangeMessage message)
        {
            SessionRecord sessionRecord                  = sessionStore.LoadSession(remoteAddress);
            SessionState  sessionState                   = sessionRecord.getSessionState();
            bool          hasPendingKeyExchange          = sessionState.hasPendingKeyExchange();
            bool          isSimultaneousInitiateResponse = message.isResponseForSimultaneousInitiate();

            if (!hasPendingKeyExchange || sessionState.getPendingKeyExchangeSequence() != message.getSequence())
            {
                //Log.w(TAG, "No matching sequence for response. Is simultaneous initiate response: " + isSimultaneousInitiateResponse);
                if (!isSimultaneousInitiateResponse)
                {
                    throw new StaleKeyExchangeException();
                }
                else
                {
                    return;
                }
            }

            SymmetricAxolotlParameters.Builder parameters = SymmetricAxolotlParameters.newBuilder();

            parameters.setOurBaseKey(sessionRecord.getSessionState().getPendingKeyExchangeBaseKey())
            .setOurRatchetKey(sessionRecord.getSessionState().getPendingKeyExchangeRatchetKey())
            .setOurIdentityKey(sessionRecord.getSessionState().getPendingKeyExchangeIdentityKey())
            .setTheirBaseKey(message.getBaseKey())
            .setTheirRatchetKey(message.getRatchetKey())
            .setTheirIdentityKey(message.getIdentityKey());

            if (!sessionRecord.isFresh())
            {
                sessionRecord.archiveCurrentState();
            }

            RatchetingSession.initializeSession(sessionRecord.getSessionState(),
                                                Math.Min(message.getMaxVersion(), CiphertextMessage.CURRENT_VERSION),
                                                parameters.create());

            if (sessionRecord.getSessionState().getSessionVersion() >= 3 &&
                !Curve.verifySignature(message.getIdentityKey().getPublicKey(),
                                       message.getBaseKey().serialize(),
                                       message.getBaseKeySignature()))
            {
                throw new InvalidKeyException("Base key signature doesn't match!");
            }

            sessionStore.StoreSession(remoteAddress, sessionRecord);
            identityKeyStore.SaveIdentity(remoteAddress.getName(), message.getIdentityKey());
        }
        private May <uint> processV3(SessionRecord sessionRecord, PreKeySignalMessage message)
        {
            if (sessionRecord.hasSessionState(message.getMessageVersion(), message.getBaseKey().serialize()))
            {
                Debug.WriteLine("We've already setup a session for this V3 message, letting bundled message fall through...");
                return(May <uint> .NoValue);
            }

            ECKeyPair ourSignedPreKey = signedPreKeyStore.LoadSignedPreKey(message.getSignedPreKeyId()).getKeyPair();

            BobSignalProtocolParameters.Builder parameters = BobSignalProtocolParameters.newBuilder();

            parameters.setTheirBaseKey(message.getBaseKey())
            .setTheirIdentityKey(message.getIdentityKey())
            .setOurIdentityKey(identityKeyStore.GetIdentityKeyPair())
            .setOurSignedPreKey(ourSignedPreKey)
            .setOurRatchetKey(ourSignedPreKey);

            if (message.getPreKeyId().HasValue)
            {
                parameters.setOurOneTimePreKey(new May <ECKeyPair>(preKeyStore.LoadPreKey(message.getPreKeyId().ForceGetValue()).getKeyPair()));
            }
            else
            {
                parameters.setOurOneTimePreKey(May <ECKeyPair> .NoValue);
            }

            if (!sessionRecord.isFresh())
            {
                sessionRecord.archiveCurrentState();
            }

            RatchetingSession.initializeSession(sessionRecord.getSessionState(), parameters.create());

            sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.GetLocalRegistrationId());
            sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId());
            sessionRecord.getSessionState().setAliceBaseKey(message.getBaseKey().serialize());

            if (message.getPreKeyId().HasValue&& message.getPreKeyId().ForceGetValue() != Medium.MAX_VALUE)
            {
                return(message.getPreKeyId());
            }
            else
            {
                return(May <uint> .NoValue);
            }
        }
        public static async Task SaveIdentityLocked(SignalProtocolAddress address, string identity)
        {
            LinkedList <SignalMessage> messages = null;

            lock (DBLock)
            {
                using (var ctx = new LibsignalDBContext())
                {
                    var old = ctx.Identities
                              .Where(i => i.Username == address.Name)
                              .FirstOrDefault(); //could contain stale data
                    if (old == null)
                    {
                        ctx.Identities.Add(new SignalIdentity()
                        {
                            IdentityKey    = identity,
                            Username       = address.Name,
                            VerifiedStatus = VerifiedStatus.Default
                        });
                    }
                    else if (old.IdentityKey != identity)
                    {
                        if (old.VerifiedStatus == VerifiedStatus.Verified)
                        {
                            old.VerifiedStatus = VerifiedStatus.Unverified;
                        }
                        old.IdentityKey = identity;
                        var oldSessions = ctx.Sessions
                                          .Where(s => s.Username == address.Name);
                        foreach (var oldSession in oldSessions)
                        {
                            SessionRecord sessionRecord = new SessionRecord(Base64.Decode(oldSession.Session));
                            sessionRecord.archiveCurrentState();
                            oldSession.Session = Base64.EncodeBytes(sessionRecord.serialize());
                            SessionsCache[GetSessionCacheIndex(address.Name, oldSession.DeviceId)] = sessionRecord;
                        }
                        messages = InsertIdentityChangedMessages(address.Name);
                    }
                    ctx.SaveChanges();
                }
            }
            if (messages != null)
            {
                await SignalLibHandle.Instance.DispatchHandleIdentityKeyChange(messages);
            }
        }
示例#6
0
        private May <uint> processV2(SessionRecord sessionRecord, PreKeyWhisperMessage message)
        {
            if (!message.getPreKeyId().HasValue)
            {
                throw new InvalidKeyIdException("V2 message requires one time prekey id!");
            }

            if (!preKeyStore.ContainsPreKey(message.getPreKeyId().ForceGetValue()) &&
                sessionStore.ContainsSession(remoteAddress))
            {
                //Log.w(TAG, "We've already processed the prekey part of this V2 session, letting bundled message fall through...");
                return(May <uint> .NoValue); //May.absent();
            }

            ECKeyPair ourPreKey = preKeyStore.LoadPreKey(message.getPreKeyId().ForceGetValue()).getKeyPair();

            BobAxolotlParameters.Builder parameters = BobAxolotlParameters.newBuilder();

            parameters.setOurIdentityKey(identityKeyStore.GetIdentityKeyPair())
            .setOurSignedPreKey(ourPreKey)
            .setOurRatchetKey(ourPreKey)
            .setOurOneTimePreKey(May <ECKeyPair> .NoValue)             //absent
            .setTheirIdentityKey(message.getIdentityKey())
            .setTheirBaseKey(message.getBaseKey());

            if (!sessionRecord.isFresh())
            {
                sessionRecord.archiveCurrentState();
            }

            RatchetingSession.initializeSession(sessionRecord.getSessionState(), message.getMessageVersion(), parameters.create());

            sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.GetLocalRegistrationId());
            sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId());
            sessionRecord.getSessionState().setAliceBaseKey(message.getBaseKey().serialize());

            if (message.getPreKeyId().ForceGetValue() != Medium.MAX_VALUE)
            {
                return(message.getPreKeyId());
            }
            else
            {
                return(May <uint> .NoValue); // May.absent();
            }
        }