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 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)); }
public byte[] Decrypt(PreKeyWhisperMessage ciphertext, IDecryptionCallback callback) { lock(SESSION_LOCK) { SessionRecord sessionRecord = _sessionStore.LoadSession(_remoteAddress); Maybe<UInt32> unsignedPreKeyId = _sessionBuilder.Process(sessionRecord, ciphertext); byte[] plaintext = Decrypt(sessionRecord, ciphertext.Message); callback.HandlePlaintext(plaintext); _sessionStore.StoreSession(_remoteAddress, sessionRecord); if(unsignedPreKeyId.HasValue) { _preKeyStore.RemovePreKey(unsignedPreKeyId.Value); } return plaintext; } }
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 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(); } }
public byte[] Decrypt(PreKeyWhisperMessage ciphertext) { return Decrypt(ciphertext, new NullDecryptionCallback()); }
/// <summary> /// Encrypt a message. /// </summary> /// <param name="paddedMessage">The plaintext message bytes, optionally padded to a constant multiple.</param> public CiphertextMessage Encrypt(byte[] paddedMessage) { lock(SESSION_LOCK) { SessionRecord sessionRecord = _sessionStore.LoadSession(_remoteAddress); SessionState sessionState = sessionRecord.SessionState; ChainKey chainKey = sessionState.GetSenderChainKey(); MessageKeys messageKeys = chainKey.GetMessageKeys(); ECPublicKey senderEphemeral = sessionState.SenderRatchetKey; UInt32 previousCounter = sessionState.PreviousCounter; UInt32 sessionVersion = sessionState.GetSessionVersion(); byte[] ciphertextBody = GetCiphertext(sessionVersion, messageKeys, paddedMessage); CiphertextMessage ciphertextMessage = new WhisperMessage(sessionVersion, messageKeys.MacKey, senderEphemeral, chainKey.Index, previousCounter, ciphertextBody, sessionState.GetLocalIdentityKey(), sessionState.GetRemoteIdentityKey()); if(sessionState.HasUnacknowledgedPreKeyMessage()) { SessionState.UnacknowledgedPreKeyMessageItems items = sessionState.GetUnacknowledgedPreKeyMessageItems(); UInt32 localRegistrationId = sessionState.LocalRegistrationId; ciphertextMessage = new PreKeyWhisperMessage(sessionVersion, localRegistrationId, items.PreKeyId, items.SignedPreKeyId, items.BaseKey, sessionState.GetLocalIdentityKey(), (WhisperMessage)ciphertextMessage); } sessionState.SetSenderChainKey (chainKey.GetNextChainKey ()); _sessionStore.StoreSession(_remoteAddress, sessionRecord); return ciphertextMessage; } }