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); }
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); } }