public Maybe<UInt32> Process(SessionRecord sessionRecord, PreKeyWhisperMessage message)
        {
            var messageVersion   = message.MessageVersion;
            var theirIdentityKey = message.IdentityKey;

            Maybe<UInt32> unsignedPreKeyId;

            if (!_identityKeyStore.IsTrustedIdentity(_remoteAddress.Name, theirIdentityKey)) {
              throw new UntrustedIdentityException();
            }

            switch (messageVersion)
            {
              case 2:
                  unsignedPreKeyId = ProcessV2(sessionRecord, message);
                  break;
              case 3:
                  unsignedPreKeyId = ProcessV3(sessionRecord, message);
                  break;
              default:
                throw new InvalidOperationException("Unknown version: " + messageVersion);
            }

            _identityKeyStore.SaveIdentity(_remoteAddress.Name, theirIdentityKey);
            return unsignedPreKeyId;
        }
        public void TestBasicSessionV3()
        {
            var aliceSessionRecord = new SessionRecord();
            var bobSessionRecord   = new SessionRecord();

            InitializeSessionsV3(aliceSessionRecord.SessionState, bobSessionRecord.SessionState);
            RunInteraction(aliceSessionRecord, bobSessionRecord);
        }
 public void StoreSession(AxolotlAddress address, SessionRecord record)
 {
     _sessions.Add(address, record.Serialize());
 }
        private void RunInteraction(SessionRecord aliceSessionRecord, SessionRecord bobSessionRecord)
        {
            var aliceStore = new TestInMemoryAxolotlStore();
            var bobStore   = new TestInMemoryAxolotlStore();

            aliceStore.StoreSession(new AxolotlAddress("+14159999999", 1), aliceSessionRecord);
            bobStore.StoreSession(new AxolotlAddress("+14158888888", 1), bobSessionRecord);

            var aliceCipher    = new SessionCipher(aliceStore, new AxolotlAddress("+14159999999", 1));
            var bobCipher      = new SessionCipher(bobStore, new AxolotlAddress("+14158888888", 1));

            var alicePlaintext = Encoding.UTF8.GetBytes ("This is a plaintext message.");
            var message        = aliceCipher.Encrypt(alicePlaintext);
            var bobPlaintext   = bobCipher.Decrypt(new WhisperMessage(message.Serialize()));

            Assert.True(ArrayComparer.Compare(alicePlaintext, bobPlaintext));

            var bobReply = Encoding.UTF8.GetBytes ("This is a message from Bob.");
            var reply         = bobCipher.Encrypt(bobReply);
            var receivedReply = aliceCipher.Decrypt(new WhisperMessage(reply.Serialize()));

            Assert.True(ArrayComparer.Compare(bobReply, receivedReply));

            var aliceCiphertextMessages = new List<CiphertextMessage>();
            var alicePlaintextMessages  = new List<byte[]>();

            for (int i = 0; i < 50; i++) {
                alicePlaintextMessages.Add (Encoding.UTF8.GetBytes (string.Format ("смерть за смерть {0}", i)));
                aliceCiphertextMessages.Add(aliceCipher.Encrypt(Encoding.UTF8.GetBytes (string.Format ("смерть за смерть {0}", i))));
            }

            long seed = CurrentMillis;

            aliceCiphertextMessages.Sort ((x, y) => new Random((int)CurrentMillis + DateTime.UtcNow.Day).Next (-1, 2));
            alicePlaintextMessages.Sort ((x, y) => new Random ((int)CurrentMillis + DateTime.UtcNow.Minute).Next (-1, 2));

            for (int i = 0; i < aliceCiphertextMessages.Count / 2; i++) {
                byte[] receivedPlaintext = bobCipher.Decrypt(new WhisperMessage(aliceCiphertextMessages[i].Serialize()));
                Assert.True(ArrayComparer.Compare(receivedPlaintext, alicePlaintextMessages[i]));
            }

            var bobCiphertextMessages = new List<CiphertextMessage>();
            var bobPlaintextMessages  = new List<byte[]>();

            for (int i=0;i<20;i++) {
                bobPlaintextMessages.Add(Encoding.UTF8.GetBytes (string.Format ("смерть за смерть {0}", i)));
                bobCiphertextMessages.Add(bobCipher.Encrypt(Encoding.UTF8.GetBytes (string.Format ("смерть за смерть {0}", i))));
            }

            seed = CurrentMillis;

            bobCiphertextMessages.Sort ((x, y) => new Random((int)CurrentMillis + DateTime.UtcNow.Day).Next (-1, 2));
            bobPlaintextMessages.Sort ((x, y) => new Random ((int)CurrentMillis + DateTime.UtcNow.Minute).Next (-1, 2));

            for (int i = 0; i < bobCiphertextMessages.Count / 2; i++) {
                byte[] receivedPlaintext = aliceCipher.Decrypt(new WhisperMessage(bobCiphertextMessages[i].Serialize()));
                Assert.True(ArrayComparer.Compare(receivedPlaintext, bobPlaintextMessages[i]));
            }

            for (int i = aliceCiphertextMessages.Count / 2; i < aliceCiphertextMessages.Count; i++) {
                byte[] receivedPlaintext = bobCipher.Decrypt(new WhisperMessage(aliceCiphertextMessages[i].Serialize()));
                Assert.True(ArrayComparer.Compare(receivedPlaintext, alicePlaintextMessages[i]));
            }

            for (int i=bobCiphertextMessages.Count / 2; i < bobCiphertextMessages.Count; i++) {
                byte[] receivedPlaintext = aliceCipher.Decrypt(new WhisperMessage(bobCiphertextMessages[i].Serialize()));
                Assert.True(ArrayComparer.Compare(receivedPlaintext, bobPlaintextMessages[i]));
            }
        }
        private Maybe<UInt32> ProcessV3(SessionRecord sessionRecord, PreKeyWhisperMessage message)
        {
            if (sessionRecord.HasSessionState(message.MessageVersion, message.BaseKey.Serialize()))
            {
                Logger.w("SessionBuilder : ", "We've already setup a session for this V3 message, letting bundled message fall through...");
                return Maybe<UInt32>.Nothing;
            }

            var ourSignedPreKey = _signedPreKeyStore.LoadSignedPreKey(message.SignedPreKeyId).keyPair;

            var bobParams = BobAxolotlParameters.NewBuilder ();
            bobParams.SetTheirBaseKey(message.BaseKey)
                        .SetTheirIdentityKey(message.IdentityKey)
                        .SetOurIdentityKey(_identityKeyStore.GetIdentityKeyPair())
                        .SetOurSignedPreKey(ourSignedPreKey)
                        .SetOurRatchetKey(ourSignedPreKey);

            if (message.PreKeyId.IsSomething()) {
                message.PreKeyId.Do (pKid => {
                    bobParams.SetOurOneTimePreKey(_preKeyStore.LoadPreKey(pKid).KeyPair.ToMaybe());
                });
            } else {
                bobParams.SetOurOneTimePreKey(Maybe<ECKeyPair>.Nothing);
            }

            if (!sessionRecord.IsFresh) sessionRecord.ArchiveCurrentState();

            RatchetingSession.InitializeSession(sessionRecord.SessionState, message.MessageVersion, bobParams.Create());

            sessionRecord.SessionState.LocalRegistrationId  = _identityKeyStore.GetLocalRegistrationId();
            sessionRecord.SessionState.RemoteRegistrationId = message.RegistrationId;
            sessionRecord.SessionState.AliceBaseKey 		= message.BaseKey.Serialize();

            if (message.PreKeyId.IsSomething() &&
                message.PreKeyId.Value != Medium.MAX_VALUE) {
                return message.PreKeyId;
            } else {
                return Maybe<UInt32>.Nothing;
            }
        }
        private Maybe<UInt32> ProcessV2(SessionRecord sessionRecord, PreKeyWhisperMessage message)
        {
            if (message.PreKeyId.IsNothing()) {
                throw new InvalidKeyIdException("V2 message requires one time prekey id!");
            }

            if (!_preKeyStore.ContainsPreKey(message.PreKeyId.Value) &&
                _sessionStore.ContainsSession(_remoteAddress))
            {
                Console.WriteLine("TAG" + "We've already processed the prekey part of this V2 session, letting bundled message fall through...");
                return Maybe<UInt32>.Nothing;
            }

            var ourPreKey = _preKeyStore.LoadPreKey(message.PreKeyId.Value).KeyPair;

            var bobParams = BobAxolotlParameters.NewBuilder();

            bobParams.SetOurIdentityKey(_identityKeyStore.GetIdentityKeyPair())
                    .SetOurSignedPreKey(ourPreKey)
                    .SetOurRatchetKey(ourPreKey)
                    .SetOurOneTimePreKey(Maybe<ECKeyPair>.Nothing)
                    .SetTheirIdentityKey(message.IdentityKey)
                    .SetTheirBaseKey(message.BaseKey);

            if (!sessionRecord.IsFresh) sessionRecord.ArchiveCurrentState();

            RatchetingSession.InitializeSession(sessionRecord.SessionState, message.MessageVersion, bobParams.Create());

            sessionRecord.SessionState.LocalRegistrationId 	= _identityKeyStore.GetLocalRegistrationId();
            sessionRecord.SessionState.RemoteRegistrationId = message.RegistrationId;
            sessionRecord.SessionState.AliceBaseKey 		= message.BaseKey.Serialize();

            if (message.PreKeyId.Value != Medium.MAX_VALUE) {
                return message.PreKeyId;
            } else {
                return Maybe<UInt32>.Nothing;
            }
        }
 public void StoreSession(AxolotlAddress address, SessionRecord record)
 {
     _sessionStore.StoreSession(address, record);
 }
Beispiel #8
0
        private byte[] Decrypt(SessionRecord sessionRecord, WhisperMessage ciphertext)
        {
            lock(SESSION_LOCK)
            {
                var previousStates = new List<SessionState>(sessionRecord.PreviousStates);
                var exceptions = new List<Exception>();

                try
                {
                    var sessionState = new SessionState(sessionRecord.SessionState);
                    byte[] plaintext = Decrypt(sessionState, ciphertext);

                    sessionRecord.SetState(sessionState);
                    return plaintext;
                }
                catch(InvalidMessageException e)
                {
                    exceptions.Add(e);
                }

                // TODO: Check~
                foreach(var state in previousStates)
                {
                    try
                    {
                        var promotedState = new SessionState(state);
                        byte[] plainText = Decrypt(promotedState, ciphertext);
                        sessionRecord.PreviousStates.Remove(state);
                        return plainText;
                    }
                    catch(InvalidMessageException e)
                    {
                        exceptions.Add(e);
                    }

                }

                throw new InvalidMessageException("No valid sessions.", exceptions);
            }
        }