public void TestBadMessageBundle() { ISignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BobAddress); ISignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); EcKeyPair bobPreKeyPair = Curve.GenerateKeyPair(); EcKeyPair bobSignedPreKeyPair = Curve.GenerateKeyPair(); byte[] bobSignedPreKeySignature = Curve.CalculateSignature(bobStore.GetIdentityKeyPair().GetPrivateKey(), bobSignedPreKeyPair.GetPublicKey().Serialize()); PreKeyBundle bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1, 31337, bobPreKeyPair.GetPublicKey(), 22, bobSignedPreKeyPair.GetPublicKey(), bobSignedPreKeySignature, bobStore.GetIdentityKeyPair().GetPublicKey()); bobStore.StorePreKey(31337, new PreKeyRecord(bobPreKey.GetPreKeyId(), bobPreKeyPair)); bobStore.StoreSignedPreKey(22, new SignedPreKeyRecord(22, DateUtil.CurrentTimeMillis(), bobSignedPreKeyPair, bobSignedPreKeySignature)); aliceSessionBuilder.Process(bobPreKey); String originalMessage = "L'homme est condamné à être libre"; SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BobAddress); CiphertextMessage outgoingMessageOne = aliceSessionCipher.Encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(CiphertextMessage.PrekeyType, outgoingMessageOne.GetMessageType()); byte[] goodMessage = outgoingMessageOne.Serialize(); byte[] badMessage = new byte[goodMessage.Length]; Array.Copy(goodMessage, 0, badMessage, 0, badMessage.Length); badMessage[badMessage.Length - 10] ^= 0x01; PreKeySignalMessage incomingMessage = new PreKeySignalMessage(badMessage); SessionCipher bobSessionCipher = new SessionCipher(bobStore, AliceAddress); byte[] plaintext = new byte[0]; try { plaintext = bobSessionCipher.Decrypt(incomingMessage); throw new Exception("Decrypt should have failed!"); } catch (InvalidMessageException) { // good. } Assert.IsTrue(bobStore.ContainsPreKey(31337)); plaintext = bobSessionCipher.Decrypt(new PreKeySignalMessage(goodMessage)); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); Assert.IsFalse(bobStore.ContainsPreKey(31337)); }
public void TestRepeatBundleMessageV3() { ISignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BobAddress); ISignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); EcKeyPair bobPreKeyPair = Curve.GenerateKeyPair(); EcKeyPair bobSignedPreKeyPair = Curve.GenerateKeyPair(); byte[] bobSignedPreKeySignature = Curve.CalculateSignature(bobStore.GetIdentityKeyPair().GetPrivateKey(), bobSignedPreKeyPair.GetPublicKey().Serialize()); PreKeyBundle bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1, 31337, bobPreKeyPair.GetPublicKey(), 22, bobSignedPreKeyPair.GetPublicKey(), bobSignedPreKeySignature, bobStore.GetIdentityKeyPair().GetPublicKey()); bobStore.StorePreKey(31337, new PreKeyRecord(bobPreKey.GetPreKeyId(), bobPreKeyPair)); bobStore.StoreSignedPreKey(22, new SignedPreKeyRecord(22, DateUtil.CurrentTimeMillis(), bobSignedPreKeyPair, bobSignedPreKeySignature)); aliceSessionBuilder.Process(bobPreKey); String originalMessage = "L'homme est condamné à être libre"; SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BobAddress); CiphertextMessage outgoingMessageOne = aliceSessionCipher.Encrypt(Encoding.UTF8.GetBytes(originalMessage)); CiphertextMessage outgoingMessageTwo = aliceSessionCipher.Encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(CiphertextMessage.PrekeyType, outgoingMessageOne.GetMessageType()); Assert.AreEqual(CiphertextMessage.PrekeyType, outgoingMessageTwo.GetMessageType()); PreKeySignalMessage incomingMessage = new PreKeySignalMessage(outgoingMessageOne.Serialize()); SessionCipher bobSessionCipher = new SessionCipher(bobStore, AliceAddress); byte[] plaintext = bobSessionCipher.Decrypt(incomingMessage); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); CiphertextMessage bobOutgoingMessage = bobSessionCipher.Encrypt(Encoding.UTF8.GetBytes(originalMessage)); byte[] alicePlaintext = aliceSessionCipher.Decrypt(new SignalMessage(bobOutgoingMessage.Serialize())); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(alicePlaintext)); // The test PreKeySignalMessage incomingMessageTwo = new PreKeySignalMessage(outgoingMessageTwo.Serialize()); plaintext = bobSessionCipher.Decrypt(new PreKeySignalMessage(incomingMessageTwo.Serialize())); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); bobOutgoingMessage = bobSessionCipher.Encrypt(Encoding.UTF8.GetBytes(originalMessage)); alicePlaintext = aliceSessionCipher.Decrypt(new SignalMessage(bobOutgoingMessage.Serialize())); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(alicePlaintext)); }
public void TestOptionalOneTimePreKey() { ISignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BobAddress); ISignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); EcKeyPair bobPreKeyPair = Curve.GenerateKeyPair(); EcKeyPair bobSignedPreKeyPair = Curve.GenerateKeyPair(); byte[] bobSignedPreKeySignature = Curve.CalculateSignature(bobStore.GetIdentityKeyPair().GetPrivateKey(), bobSignedPreKeyPair.GetPublicKey().Serialize()); PreKeyBundle bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1, 0, null, 22, bobSignedPreKeyPair.GetPublicKey(), bobSignedPreKeySignature, bobStore.GetIdentityKeyPair().GetPublicKey()); aliceSessionBuilder.Process(bobPreKey); Assert.IsTrue(aliceStore.ContainsSession(BobAddress)); Assert.AreEqual((uint)3, aliceStore.LoadSession(BobAddress).GetSessionState().GetSessionVersion()); String originalMessage = "L'homme est condamné à être libre"; SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BobAddress); CiphertextMessage outgoingMessage = aliceSessionCipher.Encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(outgoingMessage.GetMessageType(), CiphertextMessage.PrekeyType); PreKeySignalMessage incomingMessage = new PreKeySignalMessage(outgoingMessage.Serialize()); Assert.IsFalse(incomingMessage.GetPreKeyId().HasValue); bobStore.StorePreKey(31337, new PreKeyRecord(bobPreKey.GetPreKeyId(), bobPreKeyPair)); bobStore.StoreSignedPreKey(22, new SignedPreKeyRecord(22, DateUtil.CurrentTimeMillis(), bobSignedPreKeyPair, bobSignedPreKeySignature)); SessionCipher bobSessionCipher = new SessionCipher(bobStore, AliceAddress); byte[] plaintext = bobSessionCipher.Decrypt(incomingMessage); Assert.IsTrue(bobStore.ContainsSession(AliceAddress)); Assert.AreEqual((uint)3, bobStore.LoadSession(AliceAddress).GetSessionState().GetSessionVersion()); Assert.IsNotNull(bobStore.LoadSession(AliceAddress).GetSessionState().GetAliceBaseKey()); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); }
/// <summary> /// Build a new session from a {@link org.whispersystems.libsignal.state.PreKeyBundle} retrieved from /// a server. /// </summary> /// @param preKey A PreKey for the destination recipient, retrieved from a server. /// @throws InvalidKeyException when the {@link org.whispersystems.libsignal.state.PreKeyBundle} is /// badly formatted. /// @throws org.whispersystems.libsignal.UntrustedIdentityException when the sender's /// {@link IdentityKey} is not /// trusted. /// public void Process(PreKeyBundle preKey) { lock (SessionCipher.SessionLock) { if (!_identityKeyStore.IsTrustedIdentity(_remoteAddress, preKey.GetIdentityKey(), Direction.Sending)) { throw new UntrustedIdentityException(_remoteAddress.Name, 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) { throw new InvalidKeyException("No signed prekey!"); } SessionRecord sessionRecord = _sessionStore.LoadSession(_remoteAddress); EcKeyPair ourBaseKey = Curve.GenerateKeyPair(); IEcPublicKey theirSignedPreKey = preKey.GetSignedPreKey(); IEcPublicKey test = preKey.GetPreKey(); May <IEcPublicKey> theirOneTimePreKey = (test == null) ? May <IEcPublicKey> .NoValue : new May <IEcPublicKey>(test); May <uint> theirOneTimePreKeyId = theirOneTimePreKey.HasValue ? new May <uint>(preKey.GetPreKeyId()) : May <uint> .NoValue; AliceSignalProtocolParameters.Builder parameters = AliceSignalProtocolParameters.NewBuilder(); parameters.SetOurBaseKey(ourBaseKey) .SetOurIdentityKey(_identityKeyStore.GetIdentityKeyPair()) .SetTheirIdentityKey(preKey.GetIdentityKey()) .SetTheirSignedPreKey(theirSignedPreKey) .SetTheirRatchetKey(theirSignedPreKey) .SetTheirOneTimePreKey(theirOneTimePreKey); if (!sessionRecord.IsFresh()) { sessionRecord.ArchiveCurrentState(); } RatchetingSession.InitializeSession(sessionRecord.GetSessionState(), 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()); _identityKeyStore.SaveIdentity(_remoteAddress, preKey.GetIdentityKey()); _sessionStore.StoreSession(_remoteAddress, sessionRecord); } }
/** * 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()); } }
static void Main(string[] args) { // 1. Sender setup // At install time, a libsignal client needs to generate its identity keys, registration id, and prekeys. var senderIdentityKeyPair = KeyHelper.GenerateIdentityKeyPair(); var senderRegistrationId = KeyHelper.GenerateRegistrationId(false); var senderPreKeys = KeyHelper.GeneratePreKeys(0, 100); var senderSignedPreKey = KeyHelper.GenerateSignedPreKey(senderIdentityKeyPair, KeyHelper.GenerateSenderKeyId()); var senderAddress = new SignalProtocolAddress("sender", 1); // TODO: Store identityKeyPair somewhere durable and safe. // TODO: Store registrationId somewhere durable and safe. // Store preKeys in PreKeyStore. var senderPreKeyStore = new InMemoryPreKeyStore(); foreach (var senderPreKey in senderPreKeys) { senderPreKeyStore.StorePreKey(senderPreKey.GetId(), senderPreKey); } // Store signed prekey in SignedPreKeyStore. var senderSignedPreKeyStore = new InMemorySignedPreKeyStore(); senderSignedPreKeyStore.StoreSignedPreKey(senderSignedPreKey.GetId(), senderSignedPreKey); var senderSessionStore = new InMemorySessionStore(); var senderIdentityStore = new InMemoryIdentityKeyStore(senderIdentityKeyPair, senderRegistrationId); var senderProtocolStore = new InMemorySignalProtocolStore(senderIdentityKeyPair, senderRegistrationId); var senderPreKeyBundle = new PreKeyBundle( senderProtocolStore.GetLocalRegistrationId(), senderAddress.DeviceId, senderPreKeys[0].GetId(), senderPreKeys[0].GetKeyPair().GetPublicKey(), senderSignedPreKey.GetId(), senderSignedPreKey.GetKeyPair().GetPublicKey(), senderSignedPreKey.GetSignature(), senderProtocolStore.GetIdentityKeyPair().GetPublicKey() ); senderProtocolStore.StorePreKey(senderPreKeys[0].GetId(), new PreKeyRecord(senderPreKeyBundle.GetPreKeyId(), senderPreKeys[0].GetKeyPair())); senderProtocolStore.StoreSignedPreKey(senderSignedPreKey.GetId(), new SignedPreKeyRecord(22, (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), senderSignedPreKey.GetKeyPair(), senderSignedPreKey.GetSignature())); // 2. Destination setup var destinationIdentityKeyPair = KeyHelper.GenerateIdentityKeyPair(); var destinationRegistrationId = KeyHelper.GenerateRegistrationId(false); var destinationPreKeys = KeyHelper.GeneratePreKeys(0, 100); var destinationSignedPreKey = KeyHelper.GenerateSignedPreKey(destinationIdentityKeyPair, KeyHelper.GenerateSenderKeyId()); var destinationAddress = new SignalProtocolAddress("destination", 1); // TODO: Store identityKeyPair somewhere durable and safe. // TODO: Store registrationId somewhere durable and safe. var destinationPreKeyStore = new InMemoryPreKeyStore(); foreach (var destinationPreKey in destinationPreKeys) { destinationPreKeyStore.StorePreKey(destinationPreKey.GetId(), destinationPreKey); } // Store signed prekey in SignedPreKeyStore. var destinationSignedPreKeyStore = new InMemorySignedPreKeyStore(); destinationSignedPreKeyStore.StoreSignedPreKey(destinationSignedPreKey.GetId(), destinationSignedPreKey); var destinationSessionStore = new InMemorySessionStore(); var destinationIdentityStore = new InMemoryIdentityKeyStore(destinationIdentityKeyPair, destinationRegistrationId); var destinationProtocolStore = new InMemorySignalProtocolStore(destinationIdentityKeyPair, destinationRegistrationId); var destinationPreKeyBundle = new PreKeyBundle( destinationProtocolStore.GetLocalRegistrationId(), destinationAddress.DeviceId, destinationPreKeys[0].GetId(), destinationPreKeys[0].GetKeyPair().GetPublicKey(), destinationSignedPreKey.GetId(), destinationSignedPreKey.GetKeyPair().GetPublicKey(), destinationSignedPreKey.GetSignature(), destinationProtocolStore.GetIdentityKeyPair().GetPublicKey() ); destinationProtocolStore.StorePreKey(destinationPreKeys[0].GetId(), new PreKeyRecord(destinationPreKeyBundle.GetPreKeyId(), destinationPreKeys[0].GetKeyPair())); destinationProtocolStore.StoreSignedPreKey(destinationSignedPreKey.GetId(), new SignedPreKeyRecord(22, (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), destinationSignedPreKey.GetKeyPair(), destinationSignedPreKey.GetSignature())); // Instantiate a SessionBuilder for a remote recipientId + deviceId tuple. var senderToDestinationSessionBuilder = new SessionBuilder(senderProtocolStore, destinationAddress); var destinationToSenderSessionBuilder = new SessionBuilder(destinationProtocolStore, senderAddress); // Build a session with a PreKey retrieved from the server. senderToDestinationSessionBuilder.Process(destinationPreKeyBundle); destinationToSenderSessionBuilder.Process(senderPreKeyBundle); SessionCipher senderToDestinationSessionCipher = new SessionCipher(senderProtocolStore, destinationAddress); while (true) { Console.Write("Enter the text to encrypt: "); var text = Console.ReadLine(); if (string.IsNullOrWhiteSpace(text) || text.Equals("quit", StringComparison.OrdinalIgnoreCase)) { break; } CiphertextMessage message = senderToDestinationSessionCipher.Encrypt(Encoding.UTF8.GetBytes(text)); var encryptedMessage = message.Serialize(); Console.WriteLine("Encrypted message: {0}", Convert.ToBase64String(encryptedMessage)); SessionCipher destinationToSenderSessionCipher = new SessionCipher(destinationProtocolStore, senderAddress); PreKeySignalMessage incomingMessage = new PreKeySignalMessage(encryptedMessage); var decryptedMessage = destinationToSenderSessionCipher.Decrypt(incomingMessage); Console.WriteLine("Decrypted message: {0}", Encoding.UTF8.GetString(decryptedMessage)); } }
public void TestBasicPreKeyV3() { ISignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BobAddress); ISignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); EcKeyPair bobPreKeyPair = Curve.GenerateKeyPair(); EcKeyPair bobSignedPreKeyPair = Curve.GenerateKeyPair(); byte[] bobSignedPreKeySignature = Curve.CalculateSignature(bobStore.GetIdentityKeyPair().GetPrivateKey(), bobSignedPreKeyPair.GetPublicKey().Serialize()); PreKeyBundle bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1, 31337, bobPreKeyPair.GetPublicKey(), 22, bobSignedPreKeyPair.GetPublicKey(), bobSignedPreKeySignature, bobStore.GetIdentityKeyPair().GetPublicKey()); aliceSessionBuilder.Process(bobPreKey); Assert.IsTrue(aliceStore.ContainsSession(BobAddress)); Assert.AreEqual((uint)3, aliceStore.LoadSession(BobAddress).GetSessionState().GetSessionVersion()); String originalMessage = "L'homme est condamné à être libre"; SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BobAddress); CiphertextMessage outgoingMessage = aliceSessionCipher.Encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(CiphertextMessage.PrekeyType, outgoingMessage.GetMessageType()); PreKeySignalMessage incomingMessage = new PreKeySignalMessage(outgoingMessage.Serialize()); bobStore.StorePreKey(31337, new PreKeyRecord(bobPreKey.GetPreKeyId(), bobPreKeyPair)); bobStore.StoreSignedPreKey(22, new SignedPreKeyRecord(22, DateUtil.CurrentTimeMillis(), bobSignedPreKeyPair, bobSignedPreKeySignature)); SessionCipher bobSessionCipher = new SessionCipher(bobStore, AliceAddress); byte[] plaintext = bobSessionCipher.Decrypt(incomingMessage, new BobDecryptionCallback(bobStore, originalMessage)); Assert.IsTrue(bobStore.ContainsSession(AliceAddress)); Assert.AreEqual((uint)3, bobStore.LoadSession(AliceAddress).GetSessionState().GetSessionVersion()); Assert.IsNotNull(bobStore.LoadSession(AliceAddress).GetSessionState().GetAliceBaseKey()); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); CiphertextMessage bobOutgoingMessage = bobSessionCipher.Encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(CiphertextMessage.WhisperType, bobOutgoingMessage.GetMessageType()); byte[] alicePlaintext = aliceSessionCipher.Decrypt(new SignalMessage(bobOutgoingMessage.Serialize())); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(alicePlaintext)); RunInteraction(aliceStore, bobStore); aliceStore = new TestInMemorySignalProtocolStore(); aliceSessionBuilder = new SessionBuilder(aliceStore, BobAddress); aliceSessionCipher = new SessionCipher(aliceStore, BobAddress); bobPreKeyPair = Curve.GenerateKeyPair(); bobSignedPreKeyPair = Curve.GenerateKeyPair(); bobSignedPreKeySignature = Curve.CalculateSignature(bobStore.GetIdentityKeyPair().GetPrivateKey(), bobSignedPreKeyPair.GetPublicKey().Serialize()); bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1, 31338, bobPreKeyPair.GetPublicKey(), 23, bobSignedPreKeyPair.GetPublicKey(), bobSignedPreKeySignature, bobStore.GetIdentityKeyPair().GetPublicKey()); bobStore.StorePreKey(31338, new PreKeyRecord(bobPreKey.GetPreKeyId(), bobPreKeyPair)); bobStore.StoreSignedPreKey(23, new SignedPreKeyRecord(23, DateUtil.CurrentTimeMillis(), bobSignedPreKeyPair, bobSignedPreKeySignature)); aliceSessionBuilder.Process(bobPreKey); outgoingMessage = aliceSessionCipher.Encrypt(Encoding.UTF8.GetBytes(originalMessage)); try { plaintext = bobSessionCipher.Decrypt(new PreKeySignalMessage(outgoingMessage.Serialize())); throw new Exception("shouldn't be trusted!"); } catch (UntrustedIdentityException) { bobStore.SaveIdentity(AliceAddress, new PreKeySignalMessage(outgoingMessage.Serialize()).GetIdentityKey()); } plaintext = bobSessionCipher.Decrypt(new PreKeySignalMessage(outgoingMessage.Serialize())); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1, 31337, Curve.GenerateKeyPair().GetPublicKey(), 23, bobSignedPreKeyPair.GetPublicKey(), bobSignedPreKeySignature, aliceStore.GetIdentityKeyPair().GetPublicKey()); try { aliceSessionBuilder.Process(bobPreKey); throw new Exception("shoulnd't be trusted!"); } catch (UntrustedIdentityException) { // good } }