public void TestBadMessageBundle()
        {
            var aliceStore          = new TestInMemoryAxolotlStore();
            var aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS);

            var bobStore = new TestInMemoryAxolotlStore();

            var bobPreKeyPair            = Curve.GenerateKeyPair();
            var bobSignedPreKeyPair      = Curve.GenerateKeyPair();
            var bobSignedPreKeySignature = Curve.CalculateSignature(bobStore.GetIdentityKeyPair().PrivateKey,
                                                                          bobSignedPreKeyPair.PublicKey.Serialize());

            PreKeyBundle bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1,
                                                      31337, bobPreKeyPair.PublicKey,
                                                      22, bobSignedPreKeyPair.PublicKey, bobSignedPreKeySignature,
                                                      bobStore.GetIdentityKeyPair().PublicKey);

            bobStore.StorePreKey(31337, new PreKeyRecord(bobPreKey.PreKeyId, bobPreKeyPair));
            bobStore.StoreSignedPreKey(22, new SignedPreKeyRecord(22, currentMillis, bobSignedPreKeyPair, bobSignedPreKeySignature));

            aliceSessionBuilder.Process(bobPreKey);

            var originalMessage    = "L'homme est condamné à être libre";
            var aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS);
            var outgoingMessageOne = aliceSessionCipher.Encrypt (Encoding.UTF8.GetBytes (originalMessage));

            Assert.True(outgoingMessageOne.GetKeyType() == CiphertextMessage.PREKEY_TYPE);

            var goodMessage = outgoingMessageOne.Serialize();
            var badMessage  = new byte[goodMessage.Length];

            Array.Copy(goodMessage, 0, badMessage, 0, badMessage.Length);

            badMessage[badMessage.Length-10] ^= 0x01;

            var incomingMessage  = new PreKeyWhisperMessage(badMessage);
            var bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS);

            var plaintext = new byte[0];

            try {
                plaintext = bobSessionCipher.Decrypt(incomingMessage);
                throw new InvalidOperationException("Decrypt should have failed!");
            } catch (InvalidMessageException e) {
                Assert.Pass ();
            }

            Assert.True(bobStore.ContainsPreKey(31337));

            plaintext = bobSessionCipher.Decrypt(new PreKeyWhisperMessage(goodMessage));

            Assert.True(originalMessage.Equals(Encoding.UTF8.GetString(plaintext)));
            Assert.True(!bobStore.ContainsPreKey(31337));
        }
Example #2
0
        public void Process(PreKeyBundle preKey)
        {
            lock (SessionCipher.SESSION_LOCK)
            {
                if (!_identityKeyStore.IsTrustedIdentity(_remoteAddress.Name, preKey.IdentityKey)) {
                    throw new UntrustedIdentityException();
                }

                if (preKey.SignedPreKeyPublic != null &&
                    !Curve.VerifySignature(preKey.IdentityKey.PublicKey,
                                       preKey.SignedPreKeyPublic.Serialize(),
                                       preKey.SignedPreKeySignature))
                {
                    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();
                Maybe<ECPublicKey> theirOneTimePreKey      = preKey.GetPreKey().ToMaybe();
                Maybe<UInt32>     theirOneTimePreKeyId 	   = theirOneTimePreKey.IsSomething() ? preKey.PreKeyId.ToMaybe() : Maybe<UInt32>.Nothing;

                var aliceParams = AliceAxolotlParameters.NewBuilder();

                aliceParams.SetOurBaseKey(ourBaseKey)
                        .SetOurIdentityKey(_identityKeyStore.GetIdentityKeyPair())
                        .SetTheirIdentityKey(preKey.IdentityKey)
                        .SetTheirSignedPreKey(theirSignedPreKey)
                        .SetTheirRatchetKey(theirSignedPreKey)
                        .SetTheirOneTimePreKey(supportsV3 ? theirOneTimePreKey : Maybe<ECPublicKey>.Nothing);

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

                RatchetingSession.InitializeSession(sessionRecord.SessionState,
                                                    (UInt32)(supportsV3 ? 3 : 2),
                                                    aliceParams.Create());

                sessionRecord.SessionState.SetUnacknowledgedPreKeyMessage(theirOneTimePreKeyId, preKey.SignedPreKeyID, ourBaseKey.PublicKey);
                sessionRecord.SessionState.LocalRegistrationId 	= _identityKeyStore.GetLocalRegistrationId();
                sessionRecord.SessionState.RemoteRegistrationId = preKey.RegistrationID;
                sessionRecord.SessionState.AliceBaseKey 		= ourBaseKey.PublicKey.Serialize();

                _sessionStore.StoreSession(_remoteAddress, sessionRecord);
                _identityKeyStore.SaveIdentity(_remoteAddress.Name, preKey.IdentityKey);
            }
        }
        public void TestBadSignedPreKeySignature()
        {
            var aliceStore          = new TestInMemoryAxolotlStore();
            var aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS);

            IIdentityKeyStore bobIdentityKeyStore = new TestInMemoryIdentityKeyStore();

            ECKeyPair bobPreKeyPair            = Curve.GenerateKeyPair();
            ECKeyPair bobSignedPreKeyPair      = Curve.GenerateKeyPair();
            byte[]    bobSignedPreKeySignature = Curve.CalculateSignature(bobIdentityKeyStore.GetIdentityKeyPair().PrivateKey,
                                                                          bobSignedPreKeyPair.PublicKey.Serialize());

            for (int i = 0; i < bobSignedPreKeySignature.Length * 8; i++)
            {
                var modifiedSignature = new byte[bobSignedPreKeySignature.Length];

                Array.Copy (bobSignedPreKeySignature, 0, modifiedSignature, 0, modifiedSignature.Length);

                modifiedSignature[i/8] ^= (byte)(0x01 << (i % 8));

                PreKeyBundle bobPreKey = new PreKeyBundle(bobIdentityKeyStore.GetLocalRegistrationId(), 1,
                                                          31337, bobPreKeyPair.PublicKey,
                                                          22, bobSignedPreKeyPair.PublicKey, modifiedSignature,
                                                          bobIdentityKeyStore.GetIdentityKeyPair().PublicKey);

                try {
                    aliceSessionBuilder.Process(bobPreKey);
                    throw new InvalidOperationException("Accepted modified device key signature!");
                } catch (InvalidKeyException ike) {
                    Assert.Pass();
                }
            }
        }
        public void TestRepeatBundleMessageV3()
        {
            var aliceStore          = new TestInMemoryAxolotlStore();
            var aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS);

            var bobStore = new TestInMemoryAxolotlStore();

            var bobPreKeyPair            = Curve.GenerateKeyPair();
            var bobSignedPreKeyPair      = Curve.GenerateKeyPair();
            var bobSignedPreKeySignature = Curve.CalculateSignature(bobStore.GetIdentityKeyPair().PrivateKey,
                                                                          bobSignedPreKeyPair.PublicKey.Serialize());

            PreKeyBundle bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1,
                                                      31337, bobPreKeyPair.PublicKey,
                                                      22, bobSignedPreKeyPair.PublicKey, bobSignedPreKeySignature,
                                                      bobStore.GetIdentityKeyPair().PublicKey);

            bobStore.StorePreKey(31337, new PreKeyRecord(bobPreKey.PreKeyId, bobPreKeyPair));
            bobStore.StoreSignedPreKey(22, new SignedPreKeyRecord(22, currentMillis, bobSignedPreKeyPair, bobSignedPreKeySignature));

            aliceSessionBuilder.Process(bobPreKey);

            var originalMessage    = "L'homme est condamné à être libre";
            var aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS);
            var outgoingMessageOne = aliceSessionCipher.Encrypt (Encoding.UTF8.GetBytes (originalMessage));
            var outgoingMessageTwo = aliceSessionCipher.Encrypt(Encoding.UTF8.GetBytes (originalMessage));

            Assert.True(outgoingMessageOne.GetKeyType() == CiphertextMessage.PREKEY_TYPE);
            Assert.True(outgoingMessageTwo.GetKeyType() == CiphertextMessage.PREKEY_TYPE);

            var incomingMessage = new PreKeyWhisperMessage(outgoingMessageOne.Serialize());

            var bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS);

            var plaintext = bobSessionCipher.Decrypt(incomingMessage);
            Assert.True(originalMessage.Equals(Encoding.UTF8.GetString(plaintext)));

            var bobOutgoingMessage = bobSessionCipher.Encrypt (Encoding.UTF8.GetBytes (originalMessage));

            var alicePlaintext = aliceSessionCipher.Decrypt(new WhisperMessage(bobOutgoingMessage.Serialize()));
            Assert.True(originalMessage.Equals(Encoding.UTF8.GetString(alicePlaintext)));

            // The test

            var incomingMessageTwo = new PreKeyWhisperMessage(outgoingMessageTwo.Serialize());

            plaintext = bobSessionCipher.Decrypt(new PreKeyWhisperMessage(incomingMessageTwo.Serialize()));
            Assert.True(originalMessage.Equals(Encoding.UTF8.GetString(plaintext)));

            bobOutgoingMessage = bobSessionCipher.Encrypt (Encoding.UTF8.GetBytes (originalMessage));
            alicePlaintext = aliceSessionCipher.Decrypt(new WhisperMessage(bobOutgoingMessage.Serialize()));
            Assert.True(originalMessage.Equals(Encoding.UTF8.GetString (alicePlaintext)));
        }
        public void TestOptionalOneTimePreKey()
        {
            var aliceStore          = new TestInMemoryAxolotlStore();
            var aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS);

            var bobStore = new TestInMemoryAxolotlStore();

            var bobPreKeyPair            = Curve.GenerateKeyPair();
            var bobSignedPreKeyPair      = Curve.GenerateKeyPair();
            var bobSignedPreKeySignature = Curve.CalculateSignature(bobStore.GetIdentityKeyPair().PrivateKey,
                                                                          bobSignedPreKeyPair.PublicKey.Serialize());

            var bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1,
                                                      0, null,
                                                      22, bobSignedPreKeyPair.PublicKey,
                                                      bobSignedPreKeySignature,
                                                      bobStore.GetIdentityKeyPair().PublicKey);

            aliceSessionBuilder.Process(bobPreKey);

            Assert.True(aliceStore.ContainsSession(BOB_ADDRESS));
            Assert.True(aliceStore.LoadSession(BOB_ADDRESS).SessionState.GetSessionVersion() == 3);

            var originalMessage    = "L'homme est condamné à être libre";
            var aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS);
            var outgoingMessage = aliceSessionCipher.Encrypt (Encoding.UTF8.GetBytes (originalMessage));

            Assert.True(outgoingMessage.GetKeyType() == CiphertextMessage.PREKEY_TYPE);

            var incomingMessage = new PreKeyWhisperMessage(outgoingMessage.Serialize());
            Assert.True(!incomingMessage.PreKeyId.HasValue);

            bobStore.StorePreKey(31337, new PreKeyRecord(bobPreKey.PreKeyId, bobPreKeyPair));
            bobStore.StoreSignedPreKey(22, new SignedPreKeyRecord(22, currentMillis, bobSignedPreKeyPair, bobSignedPreKeySignature));

            var bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS);
            var plaintext        = bobSessionCipher.Decrypt(incomingMessage);

            Assert.True(bobStore.ContainsSession(ALICE_ADDRESS));
            Assert.True(bobStore.LoadSession(ALICE_ADDRESS).SessionState.GetSessionVersion() == 3);
            Assert.True(bobStore.LoadSession(ALICE_ADDRESS).SessionState.AliceBaseKey != null);
            Assert.True(originalMessage.Equals(Encoding.UTF8.GetString(plaintext)));
        }
        public void TestBasicPreKeyV3()
        {
            var aliceStore         		 = new TestInMemoryAxolotlStore();
            var aliceSessionBuilder		 = new SessionBuilder(aliceStore, BOB_ADDRESS);

            var bobStore           		 = new TestInMemoryAxolotlStore();
            var bobPreKeyPair      		 = Curve.GenerateKeyPair();
            var bobSignedPreKeyPair		 = Curve.GenerateKeyPair();
            var bobSignedPreKeySignature = Curve.CalculateSignature(bobStore.GetIdentityKeyPair().PrivateKey,
                                                                             bobSignedPreKeyPair.PublicKey.Serialize());

            var bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1,
                                                      31337, bobPreKeyPair.PublicKey,
                                                      22, bobSignedPreKeyPair.PublicKey,
                                                      bobSignedPreKeySignature,
                                                      bobStore.GetIdentityKeyPair().PublicKey);

            aliceSessionBuilder.Process(bobPreKey);

            Assert.True(aliceStore.ContainsSession(BOB_ADDRESS));
            Assert.True(aliceStore.LoadSession(BOB_ADDRESS).SessionState.GetSessionVersion() == 3);

            var originalMessage    = "L'homme est condamné à être libre";
            var aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS);
            var outgoingMessage = aliceSessionCipher.Encrypt (Encoding.UTF8.GetBytes (originalMessage));

            Assert.True(outgoingMessage.GetKeyType() == CiphertextMessage.PREKEY_TYPE);

            PreKeyWhisperMessage incomingMessage = new PreKeyWhisperMessage(outgoingMessage.Serialize());
            bobStore.StorePreKey(31337, new PreKeyRecord(bobPreKey.PreKeyId, bobPreKeyPair));
            bobStore.StoreSignedPreKey(22, new SignedPreKeyRecord(22, currentMillis, bobSignedPreKeyPair, bobSignedPreKeySignature));

            SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS);
            byte[] plaintext = bobSessionCipher.Decrypt(incomingMessage, new TestDecryptionCallback());

            Assert.True(bobStore.ContainsSession(ALICE_ADDRESS));
            Assert.True(bobStore.LoadSession(ALICE_ADDRESS).SessionState.GetSessionVersion() == 3);
            Assert.True(bobStore.LoadSession(ALICE_ADDRESS).SessionState.AliceBaseKey != null);
            Assert.True(originalMessage.Equals(Encoding.UTF8.GetString(plaintext)));

            CiphertextMessage bobOutgoingMessage = bobSessionCipher.Encrypt (Encoding.UTF8.GetBytes (originalMessage));
            Assert.True(bobOutgoingMessage.GetKeyType() == CiphertextMessage.WHISPER_TYPE);

            var alicePlaintext = aliceSessionCipher.Decrypt(new WhisperMessage(bobOutgoingMessage.Serialize()));
            Assert.True (Encoding.UTF8.GetString(alicePlaintext).Equals (originalMessage));

            RunInteraction(aliceStore, bobStore);

            aliceStore          = new TestInMemoryAxolotlStore();
            aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS);
            aliceSessionCipher  = new SessionCipher(aliceStore, BOB_ADDRESS);

            bobPreKeyPair            = Curve.GenerateKeyPair();
            bobSignedPreKeyPair      = Curve.GenerateKeyPair();
            bobSignedPreKeySignature = Curve.CalculateSignature(bobStore.GetIdentityKeyPair().PrivateKey, bobSignedPreKeyPair.PublicKey.Serialize());
            bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(),
                                         1, 31338, bobPreKeyPair.PublicKey,
                                         23, bobSignedPreKeyPair.PublicKey, bobSignedPreKeySignature,
                                         bobStore.GetIdentityKeyPair().PublicKey);

            bobStore.StorePreKey(31338, new PreKeyRecord(bobPreKey.PreKeyId, bobPreKeyPair));
            bobStore.StoreSignedPreKey(23, new SignedPreKeyRecord(23, currentMillis, bobSignedPreKeyPair, bobSignedPreKeySignature));
            aliceSessionBuilder.Process(bobPreKey);

            outgoingMessage = aliceSessionCipher.Encrypt (Encoding.UTF8.GetBytes (originalMessage));

            try {
                plaintext = bobSessionCipher.Decrypt(new PreKeyWhisperMessage(outgoingMessage.Serialize()));
                throw new InvalidOperationException("shouldn't be trusted!");
            } catch (UntrustedIdentityException uie) {
                bobStore.SaveIdentity(ALICE_ADDRESS.Name, new PreKeyWhisperMessage(outgoingMessage.Serialize()).IdentityKey);
            }

            plaintext = bobSessionCipher.Decrypt(new PreKeyWhisperMessage(outgoingMessage.Serialize()));
            Assert.True(Encoding.UTF8.GetString(plaintext).Equals(originalMessage));

            bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1,
                                         31337, Curve.GenerateKeyPair().PublicKey,
                                         23, bobSignedPreKeyPair.PublicKey, bobSignedPreKeySignature,
                                         aliceStore.GetIdentityKeyPair().PublicKey);

            try {
                aliceSessionBuilder.Process(bobPreKey);
                throw new InvalidOperationException("shoulnd't be trusted!");
            } catch (UntrustedIdentityException uie) {
                Assert.Pass();
            }
        }
        public void TestBasicPreKeyV2()
        {
            IAxolotlStore   aliceStore         = new TestInMemoryAxolotlStore();
            SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS);

            IAxolotlStore bobStore      = new TestInMemoryAxolotlStore();
            ECKeyPair    bobPreKeyPair = Curve.GenerateKeyPair();
            PreKeyBundle bobPreKey     = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1,
                                                          31337, bobPreKeyPair.PublicKey,
                                                          0, null, null,
                                                          bobStore.GetIdentityKeyPair().PublicKey);

            aliceSessionBuilder.Process(bobPreKey);

            Assert.True(aliceStore.ContainsSession(BOB_ADDRESS));
            Assert.True(aliceStore.LoadSession(BOB_ADDRESS).SessionState.GetSessionVersion() == 2);

            var originalMessage    = "L'homme est condamné à être libre";
            var aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS);
            var outgoingMessage    = aliceSessionCipher.Encrypt(Encoding.UTF8.GetBytes(originalMessage));

            Assert.True(outgoingMessage.GetKeyType() == CiphertextMessage.PREKEY_TYPE);

            PreKeyWhisperMessage incomingMessage = new PreKeyWhisperMessage(outgoingMessage.Serialize());
            bobStore.StorePreKey((UInt32)31337, new PreKeyRecord(bobPreKey.PreKeyId, bobPreKeyPair));

            var bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS);
            var plaintext        = bobSessionCipher.Decrypt(incomingMessage);

            Assert.True(bobStore.ContainsSession(ALICE_ADDRESS));
            Assert.True(bobStore.LoadSession(ALICE_ADDRESS).SessionState.GetSessionVersion() == 2);
            Assert.True(originalMessage.Equals(new string(plaintext.Select(x => (char)x).ToArray())));

            var bobOutgoingMessage = bobSessionCipher.Encrypt (Encoding.UTF8.GetBytes (originalMessage));
            Assert.True(bobOutgoingMessage.GetKeyType() == CiphertextMessage.WHISPER_TYPE);

            var alicePlaintext = aliceSessionCipher.Decrypt((WhisperMessage)bobOutgoingMessage);
            Assert.True(new string(alicePlaintext.Select(x => (char)x).ToArray()).Equals(originalMessage));

            RunInteraction(aliceStore, bobStore);

            aliceStore          = new TestInMemoryAxolotlStore();
            aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS);
            aliceSessionCipher  = new SessionCipher(aliceStore, BOB_ADDRESS);

            bobPreKeyPair = Curve.GenerateKeyPair();
            bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(),
                                         1, 31338, bobPreKeyPair.PublicKey,
                                         0, null, null, bobStore.GetIdentityKeyPair().PublicKey	);

            bobStore.StorePreKey((UInt32)31338, new PreKeyRecord(bobPreKey.PreKeyId, bobPreKeyPair));
            aliceSessionBuilder.Process(bobPreKey);

            outgoingMessage = aliceSessionCipher.Encrypt (Encoding.UTF8.GetBytes (originalMessage));

            try {
                bobSessionCipher.Decrypt(new PreKeyWhisperMessage(outgoingMessage.Serialize()));
                throw new InvalidOperationException("shouldn't be trusted!");
            } catch (UntrustedIdentityException uie) {
                bobStore.SaveIdentity(ALICE_ADDRESS.Name, new PreKeyWhisperMessage(outgoingMessage.Serialize()).IdentityKey);
            }

            plaintext = bobSessionCipher.Decrypt(new PreKeyWhisperMessage(outgoingMessage.Serialize()));

            Assert.True(new string(plaintext.Select(x => (char)x).ToArray()).Equals(originalMessage));

            bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1,
                                         31337, Curve.GenerateKeyPair().PublicKey,
                                         0, null, null,
                                         aliceStore.GetIdentityKeyPair().PublicKey);

            try {
                aliceSessionBuilder.Process(bobPreKey);
                throw new InvalidOperationException("shoulnd't be trusted!");
            } catch (UntrustedIdentityException uie) {
                Assert.Pass();
            }
        }