public OutgoingPushMessage encrypt(SignalProtocolAddress destination, byte[] unpaddedMessage, bool legacy, bool silent) { SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, destination); PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion()); CiphertextMessage message = sessionCipher.encrypt(transportDetails.getPaddedMessageBody(unpaddedMessage)); uint remoteRegistrationId = sessionCipher.getRemoteRegistrationId(); String body = Base64.encodeBytes(message.serialize()); uint type; switch (message.getType()) { case CiphertextMessage.PREKEY_TYPE: type = (uint)Envelope.Types.Type.PrekeyBundle; break; // todo check case CiphertextMessage.WHISPER_TYPE: type = (uint)Envelope.Types.Type.Ciphertext; break; // todo check default: throw new Exception("Bad type: " + message.getType()); } return(new OutgoingPushMessage(type, destination.DeviceId, remoteRegistrationId, legacy ? body : null, legacy ? null : body, silent)); }
private async Task buildSessionForDevicesAsync(Dictionary <uint, SessionCipher> sessions, IList <SignalProtocolAddress> devices) { if (devices.Count <= 0) { return; } SignalProtocolAddress device = devices[0]; devices.RemoveAt(0); // Check if there exists already a session for this device: if (OMEMO_HELPER.OMEMO_STORE.ContainsSession(device)) { // If yes, the load it: SessionCipher cipher = OMEMO_HELPER.loadCipher(device); sessions.Add(device.getDeviceId(), cipher); Logger.Info("[OmemoSessionBuildHelper] Session for " + device.ToString() + " loaded from cache."); } else { // Else try to build a new one by requesting the devices bundle information: OmemoBundleInformationResultMessage bundleMsg = await requestBundleInformationAsync(device); if (!(bundleMsg is null)) { SignalProtocolAddress address = OMEMO_HELPER.newSession(CHAT_JID, bundleMsg); SessionCipher cipher = OMEMO_HELPER.loadCipher(address); sessions.Add(device.getDeviceId(), cipher); Logger.Info("[OmemoSessionBuildHelper] Session with " + device.ToString() + " established."); }
private byte[] decrypt(SignalServiceEnvelope envelope, byte[] ciphertext) { SignalProtocolAddress sourceAddress = new SignalProtocolAddress(envelope.getSource(), (uint)envelope.getSourceDevice()); SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, sourceAddress); byte[] paddedMessage; if (envelope.isPreKeySignalMessage()) { paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(ciphertext)); } else if (envelope.isSignalMessage()) { paddedMessage = sessionCipher.decrypt(new SignalMessage(ciphertext)); } else { throw new InvalidMessageException("Unknown type: " + envelope.getType() + " from " + envelope.getSource()); } PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion()); return(transportDetails.getStrippedPaddingMessageBody(paddedMessage)); }
public OutgoingPushMessage Encrypt(SignalProtocolAddress destination, UnidentifiedAccess?unidentifiedAccess, byte[] unpaddedMessage) { if (unidentifiedAccess != null) { SealedSessionCipher sessionCipher = new SealedSessionCipher(signalProtocolStore, localAddress.Uuid, localAddress.GetNumber(), 1); PushTransportDetails transportDetails = new PushTransportDetails((uint)sessionCipher.GetSessionVersion(destination)); byte[] ciphertext = sessionCipher.Encrypt(destination, unidentifiedAccess.UnidentifiedCertificate, transportDetails.getPaddedMessageBody(unpaddedMessage)); string body = Base64.EncodeBytes(ciphertext); uint remoteRegistrationId = (uint)sessionCipher.GetRemoteRegistrationId(destination); return(new OutgoingPushMessage((uint)Envelope.Types.Type.UnidentifiedSender, destination.DeviceId, remoteRegistrationId, body)); } else { SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, destination); PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion()); CiphertextMessage message = sessionCipher.encrypt(transportDetails.getPaddedMessageBody(unpaddedMessage)); uint remoteRegistrationId = sessionCipher.getRemoteRegistrationId(); string body = Base64.EncodeBytes(message.serialize()); var type = (message.getType()) switch { CiphertextMessage.PREKEY_TYPE => (uint)Envelope.Types.Type.PrekeyBundle, CiphertextMessage.WHISPER_TYPE => (uint)Envelope.Types.Type.Ciphertext, _ => throw new Exception("Bad type: " + message.getType()), }; return(new OutgoingPushMessage(type, destination.DeviceId, remoteRegistrationId, body)); } }
private bool onRequestBundleInformationMessage(IQMessage msg) { if (STATE != OmemoSessionBuildHelperState.REQUESTING_BUNDLE_INFORMATION) { return(true); } if (msg is OmemoBundleInformationResultMessage bundleMsg) { Logger.Info("[OmemoSessionBuildHelper] Session with " + curAddress.getName() + ':' + curAddress.getDeviceId() + " established."); SignalProtocolAddress address = OMEMO_HELPER.newSession(CHAT_JID, bundleMsg); SessionCipher cipher = OMEMO_HELPER.loadCipher(address); SESSION.DEVICE_SESSIONS.Add(curAddress.getDeviceId(), cipher); createSessionForNextDevice(); return(true); } else if (msg is IQErrorMessage errMsg) { if (errMsg.ERROR_OBJ.ERROR_NAME == ErrorName.ITEM_NOT_FOUND) { Logger.Error("[OmemoSessionBuildHelper] Failed to establish session - " + curAddress.getName() + ':' + curAddress.getDeviceId() + " doesn't support OMEMO: " + errMsg.ERROR_OBJ.ToString()); setState(OmemoSessionBuildHelperState.ERROR); ON_SESSION_RESULT(this, new OmemoSessionBuildResult(OmemoSessionBuildError.TARGET_DOES_NOT_SUPPORT_OMEMO)); } else { Logger.Error("[OmemoSessionBuildHelper] Failed to establish session - request bundle info failed(" + curAddress.getName() + ':' + curAddress.getDeviceId() + "): " + errMsg.ERROR_OBJ.ToString()); setState(OmemoSessionBuildHelperState.ERROR); ON_SESSION_RESULT(this, new OmemoSessionBuildResult(OmemoSessionBuildError.REQUEST_BUNDLE_INFORMATION_IQ_ERROR)); } return(true); } return(false); }
private byte[] decrypt(TextSecureEnvelope envelope, byte[] ciphertext) { AxolotlAddress sourceAddress = new AxolotlAddress(envelope.getSource(), envelope.getSourceDevice()); SessionCipher sessionCipher = new SessionCipher(axolotlStore, sourceAddress); byte[] paddedMessage; if (envelope.isPreKeyWhisperMessage()) { paddedMessage = sessionCipher.decrypt(new PreKeyWhisperMessage(ciphertext)); } else if (envelope.isWhisperMessage()) { paddedMessage = sessionCipher.decrypt(new WhisperMessage(ciphertext)); } else { throw new InvalidMessageException("Unknown type: " + envelope.getType()); } PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion()); return(transportDetails.getStrippedPaddingMessageBody(paddedMessage)); }
/// <summary> /// decrypt an incomming message /// </summary> /// <param name="from"></param> /// <param name="ciphertext"></param> /// <param name="type"></param> /// <param name="id"></param> /// <param name="t"></param> /// <param name="retry_from"></param> /// <param name="skip_unpad"></param> public object decryptMessage(string from, byte[] ciphertext, string type, string id, string t, string retry_from = null, bool skip_unpad = false) { string version = "1"; #region pkmsg routine if (type == "pkmsg") { if (v2Jids.Contains(ExtractNumber(from))) { version = "2"; } try { PreKeyWhisperMessage preKeyWhisperMessage = new PreKeyWhisperMessage(ciphertext); SessionCipher sessionCipher = getSessionCipher(ExtractNumber(from)); var plaintext = sessionCipher.decrypt(preKeyWhisperMessage); if (version == "2" && !skip_unpad) { return(unpadV2Plaintext(plaintext.ToString())); } } catch (Exception e) { } } #endregion #region WhisperMessage routine if (type == "msg") { if (v2Jids.Contains(ExtractNumber(from))) { version = "2"; } try { PreKeyWhisperMessage preKeyWhisperMessage = new PreKeyWhisperMessage(ciphertext); SessionCipher sessionCipher = getSessionCipher(ExtractNumber(from)); var plaintext = sessionCipher.decrypt(preKeyWhisperMessage); if (version == "2" && !skip_unpad) { return(unpadV2Plaintext(plaintext.ToString())); } } catch (Exception e) { } } #endregion #region Group message Cipher routine if (type == "skmsg") { throw new NotImplementedException(); } #endregion return(false); }
public void testBadMessageBundle() { SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS); SignalProtocolStore 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, BOB_ADDRESS); CiphertextMessage outgoingMessageOne = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(CiphertextMessage.PREKEY_TYPE, outgoingMessageOne.getType()); 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, ALICE_ADDRESS); 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 testSimultaneousInitiateLostMessage() { AxolotlStore aliceStore = new TestInMemoryAxolotlStore(); AxolotlStore bobStore = new TestInMemoryAxolotlStore(); PreKeyBundle alicePreKeyBundle = createAlicePreKeyBundle(aliceStore); PreKeyBundle bobPreKeyBundle = createBobPreKeyBundle(bobStore); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS); SessionBuilder bobSessionBuilder = new SessionBuilder(bobStore, ALICE_ADDRESS); SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS); SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS); aliceSessionBuilder.process(bobPreKeyBundle); bobSessionBuilder.process(alicePreKeyBundle); CiphertextMessage messageForBob = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes("hey there")); CiphertextMessage messageForAlice = bobSessionCipher.encrypt(Encoding.UTF8.GetBytes("sample message")); Assert.AreEqual(CiphertextMessage.PREKEY_TYPE, messageForBob.getType()); Assert.AreEqual(CiphertextMessage.PREKEY_TYPE, messageForAlice.getType()); Assert.IsFalse(isSessionIdEqual(aliceStore, bobStore)); byte[] alicePlaintext = aliceSessionCipher.decrypt(new PreKeyWhisperMessage(messageForAlice.serialize())); byte[] bobPlaintext = bobSessionCipher.decrypt(new PreKeyWhisperMessage(messageForBob.serialize())); Assert.AreEqual("sample message", Encoding.UTF8.GetString(alicePlaintext)); Assert.AreEqual("hey there", Encoding.UTF8.GetString(bobPlaintext)); Assert.AreEqual((uint)3, aliceStore.LoadSession(BOB_ADDRESS).getSessionState().getSessionVersion()); Assert.AreEqual((uint)3, bobStore.LoadSession(ALICE_ADDRESS).getSessionState().getSessionVersion()); Assert.IsFalse(isSessionIdEqual(aliceStore, bobStore)); CiphertextMessage aliceResponse = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes("second message")); Assert.AreEqual(CiphertextMessage.WHISPER_TYPE, aliceResponse.getType()); // byte[] responsePlaintext = bobSessionCipher.decrypt(new WhisperMessage(aliceResponse.serialize())); // // assertTrue(new String(responsePlaintext).equals("second message")); // assertTrue(isSessionIdEqual(aliceStore, bobStore)); Assert.IsFalse(isSessionIdEqual(aliceStore, bobStore)); CiphertextMessage finalMessage = bobSessionCipher.encrypt(Encoding.UTF8.GetBytes("third message")); Assert.AreEqual(CiphertextMessage.WHISPER_TYPE, finalMessage.getType()); byte[] finalPlaintext = aliceSessionCipher.decrypt(new WhisperMessage(finalMessage.serialize())); Assert.AreEqual("third message", Encoding.UTF8.GetString(finalPlaintext)); Assert.IsTrue(isSessionIdEqual(aliceStore, bobStore)); }
public void TestBasicSimultaneousInitiate() { ISignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); ISignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); PreKeyBundle alicePreKeyBundle = CreateAlicePreKeyBundle(aliceStore); PreKeyBundle bobPreKeyBundle = CreateBobPreKeyBundle(bobStore); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BobAddress); SessionBuilder bobSessionBuilder = new SessionBuilder(bobStore, AliceAddress); SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BobAddress); SessionCipher bobSessionCipher = new SessionCipher(bobStore, AliceAddress); aliceSessionBuilder.Process(bobPreKeyBundle); bobSessionBuilder.Process(alicePreKeyBundle); CiphertextMessage messageForBob = aliceSessionCipher.Encrypt(Encoding.UTF8.GetBytes("hey there")); CiphertextMessage messageForAlice = bobSessionCipher.Encrypt(Encoding.UTF8.GetBytes("sample message")); Assert.AreEqual(CiphertextMessage.PrekeyType, messageForBob.GetMessageType()); Assert.AreEqual(CiphertextMessage.PrekeyType, messageForAlice.GetMessageType()); Assert.IsFalse(IsSessionIdEqual(aliceStore, bobStore)); byte[] alicePlaintext = aliceSessionCipher.Decrypt(new PreKeySignalMessage(messageForAlice.Serialize())); byte[] bobPlaintext = bobSessionCipher.Decrypt(new PreKeySignalMessage(messageForBob.Serialize())); Assert.AreEqual("sample message", Encoding.UTF8.GetString(alicePlaintext)); Assert.AreEqual("hey there", Encoding.UTF8.GetString(bobPlaintext)); Assert.AreEqual((uint)3, aliceStore.LoadSession(BobAddress).GetSessionState().GetSessionVersion()); Assert.AreEqual((uint)3, bobStore.LoadSession(AliceAddress).GetSessionState().GetSessionVersion()); Assert.IsFalse(IsSessionIdEqual(aliceStore, bobStore)); CiphertextMessage aliceResponse = aliceSessionCipher.Encrypt(Encoding.UTF8.GetBytes("second message")); Assert.AreEqual(CiphertextMessage.WhisperType, aliceResponse.GetMessageType()); byte[] responsePlaintext = bobSessionCipher.Decrypt(new SignalMessage(aliceResponse.Serialize())); Assert.AreEqual("second message", Encoding.UTF8.GetString(responsePlaintext)); Assert.IsTrue(IsSessionIdEqual(aliceStore, bobStore)); CiphertextMessage finalMessage = bobSessionCipher.Encrypt(Encoding.UTF8.GetBytes("third message")); Assert.AreEqual(CiphertextMessage.WhisperType, finalMessage.GetMessageType()); byte[] finalPlaintext = aliceSessionCipher.Decrypt(new SignalMessage(finalMessage.Serialize())); Assert.AreEqual("third message", Encoding.UTF8.GetString(finalPlaintext)); Assert.IsTrue(IsSessionIdEqual(aliceStore, bobStore)); }
public void testRepeatBundleMessageV3() { SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS); SignalProtocolStore 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, BOB_ADDRESS); CiphertextMessage outgoingMessageOne = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); CiphertextMessage outgoingMessageTwo = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(CiphertextMessage.PREKEY_TYPE, outgoingMessageOne.getType()); Assert.AreEqual(CiphertextMessage.PREKEY_TYPE, outgoingMessageTwo.getType()); PreKeySignalMessage incomingMessage = new PreKeySignalMessage(outgoingMessageOne.serialize()); SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS); 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() { SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS); SignalProtocolStore 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(BOB_ADDRESS)); Assert.AreEqual((uint)3, aliceStore.LoadSession(BOB_ADDRESS).getSessionState().getSessionVersion()); String originalMessage = "L'homme est condamné à être libre"; SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS); CiphertextMessage outgoingMessage = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(outgoingMessage.getType(), CiphertextMessage.PREKEY_TYPE); 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, ALICE_ADDRESS); byte[] plaintext = bobSessionCipher.decrypt(incomingMessage); Assert.IsTrue(bobStore.ContainsSession(ALICE_ADDRESS)); Assert.AreEqual((uint)3, bobStore.LoadSession(ALICE_ADDRESS).getSessionState().getSessionVersion()); Assert.IsNotNull(bobStore.LoadSession(ALICE_ADDRESS).getSessionState().getAliceBaseKey()); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); }
private void createSessionForNextDevice() { if ((toDoDevicesRemote == null || toDoDevicesRemote.Count <= 0) && (toDoDevicesOwn == null || toDoDevicesOwn.Count <= 0)) { // All sessions created: if (SESSION.DEVICE_SESSIONS.Count <= 0) { setState(OmemoSessionBuildHelperState.ERROR); ON_SESSION_RESULT(this, new OmemoSessionBuildResult(OmemoSessionBuildError.TARGET_DOES_NOT_SUPPORT_OMEMO)); } else { setState(OmemoSessionBuildHelperState.ESTABLISHED); ON_SESSION_RESULT(this, new OmemoSessionBuildResult(SESSION)); } } else { if (toDoDevicesRemote == null || toDoDevicesRemote.Count <= 0) { curAddress = new SignalProtocolAddress(BARE_ACCOUNT_JID, toDoDevicesOwn[0]); toDoDevicesOwn.RemoveAt(0); } else { curAddress = new SignalProtocolAddress(CHAT_JID, toDoDevicesRemote[0]); toDoDevicesRemote.RemoveAt(0); } if (OMEMO_HELPER.containsSession(curAddress)) { SessionCipher cipher = OMEMO_HELPER.loadCipher(curAddress); SESSION.DEVICE_SESSIONS.Add(curAddress.getDeviceId(), cipher); createSessionForNextDevice(); } else { requestBundleInformation(); } } }
public byte[] Encrypt(SignalProtocolAddress destinationAddress, SenderCertificate senderCertificate, byte[] paddedPlaintext) { CiphertextMessage message = new SessionCipher(SignalProtocolStore, destinationAddress).encrypt(paddedPlaintext); IdentityKeyPair ourIdentity = SignalProtocolStore.GetIdentityKeyPair(); ECPublicKey theirIdentity = SignalProtocolStore.GetIdentity(destinationAddress).getPublicKey(); ECKeyPair ephemeral = Curve.generateKeyPair(); byte[] ephemeralSalt = ByteUtil.combine(Encoding.ASCII.GetBytes("UnidentifiedDelivery"), theirIdentity.serialize(), ephemeral.getPublicKey().serialize()); EphemeralKeys ephemeralKeys = CalculateEphemeralKeys(theirIdentity, ephemeral.getPrivateKey(), ephemeralSalt); byte[] staticKeyCiphertext = Encrypt(ephemeralKeys.CipherKey, ephemeralKeys.MacKey, ourIdentity.getPublicKey().getPublicKey().serialize()); byte[] staticSalt = ByteUtil.combine(ephemeralKeys.ChainKey, staticKeyCiphertext); StaticKeys staticKeys = CalculateStaticKeys(theirIdentity, ourIdentity.getPrivateKey(), staticSalt); UnidentifiedSenderMessageContent content = new UnidentifiedSenderMessageContent((int)message.getType(), senderCertificate, message.serialize()); byte[] messageBytes = Encrypt(staticKeys.CipherKey, staticKeys.MacKey, content.Serialized); return(new UnidentifiedSenderMessage(ephemeral.getPublicKey(), staticKeyCiphertext, messageBytes).Serialized); }
public void TestMessageKeyLimits() { SessionRecord aliceSessionRecord = new SessionRecord(); SessionRecord bobSessionRecord = new SessionRecord(); InitializeSessionsV3(aliceSessionRecord.GetSessionState(), bobSessionRecord.GetSessionState()); ISignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); ISignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); aliceStore.StoreSession(new SignalProtocolAddress("+14159999999", 1), aliceSessionRecord); bobStore.StoreSession(new SignalProtocolAddress("+14158888888", 1), bobSessionRecord); SessionCipher aliceCipher = new SessionCipher(aliceStore, new SignalProtocolAddress("+14159999999", 1)); SessionCipher bobCipher = new SessionCipher(bobStore, new SignalProtocolAddress("+14158888888", 1)); List <CiphertextMessage> inflight = new List <CiphertextMessage>(); for (int i = 0; i < 2010; i++) { inflight.Add(aliceCipher.Encrypt(Encoding.UTF8.GetBytes("you've never been so hungry, you've never been so cold"))); } bobCipher.Decrypt(new SignalMessage(inflight[1000].Serialize())); bobCipher.Decrypt(new SignalMessage(inflight[inflight.Count - 1].Serialize())); try { bobCipher.Decrypt(new SignalMessage(inflight[0].Serialize())); throw new Exception("Should have failed!"); } catch (DuplicateMessageException) { // good } }
private void runInteraction(SignalProtocolStore aliceStore, SignalProtocolStore bobStore) { SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS); SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS); String originalMessage = "smert ze smert"; CiphertextMessage aliceMessage = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(CiphertextMessage.WHISPER_TYPE, aliceMessage.getType()); byte[] plaintext = bobSessionCipher.decrypt(new SignalMessage(aliceMessage.serialize())); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); CiphertextMessage bobMessage = bobSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(CiphertextMessage.WHISPER_TYPE, bobMessage.getType()); plaintext = aliceSessionCipher.decrypt(new SignalMessage(bobMessage.serialize())); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); for (int i = 0; i < 10; i++) { String loopingMessage = ("What do we mean by saying that existence precedes essence? " + "We mean that man first of all exists, encounters himself, " + "surges up in the world--and defines himself aftward. " + i); CiphertextMessage aliceLoopingMessage = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(loopingMessage)); byte[] loopingPlaintext = bobSessionCipher.decrypt(new SignalMessage(aliceLoopingMessage.serialize())); Assert.AreEqual(loopingMessage, Encoding.UTF8.GetString(loopingPlaintext)); } for (int i = 0; i < 10; i++) { String loopingMessage = ("What do we mean by saying that existence precedes essence? " + "We mean that man first of all exists, encounters himself, " + "surges up in the world--and defines himself aftward. " + i); CiphertextMessage bobLoopingMessage = bobSessionCipher.encrypt(Encoding.UTF8.GetBytes(loopingMessage)); byte[] loopingPlaintext = aliceSessionCipher.decrypt(new SignalMessage(bobLoopingMessage.serialize())); Assert.AreEqual(loopingMessage, Encoding.UTF8.GetString(loopingPlaintext)); } HashSet <Pair <String, CiphertextMessage> > aliceOutOfOrderMessages = new HashSet <Pair <String, CiphertextMessage> >(); for (int i = 0; i < 10; i++) { String loopingMessage = ("What do we mean by saying that existence precedes essence? " + "We mean that man first of all exists, encounters himself, " + "surges up in the world--and defines himself aftward. " + i); CiphertextMessage aliceLoopingMessage = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(loopingMessage)); aliceOutOfOrderMessages.Add(new Pair <String, CiphertextMessage>(loopingMessage, aliceLoopingMessage)); } for (int i = 0; i < 10; i++) { String loopingMessage = ("What do we mean by saying that existence precedes essence? " + "We mean that man first of all exists, encounters himself, " + "surges up in the world--and defines himself aftward. " + i); CiphertextMessage aliceLoopingMessage = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(loopingMessage)); byte[] loopingPlaintext = bobSessionCipher.decrypt(new SignalMessage(aliceLoopingMessage.serialize())); Assert.AreEqual(loopingMessage, Encoding.UTF8.GetString(loopingPlaintext)); } for (int i = 0; i < 10; i++) { String loopingMessage = ("You can only desire based on what you know: " + i); CiphertextMessage bobLoopingMessage = bobSessionCipher.encrypt(Encoding.UTF8.GetBytes(loopingMessage)); byte[] loopingPlaintext = aliceSessionCipher.decrypt(new SignalMessage(bobLoopingMessage.serialize())); Assert.AreEqual(loopingMessage, Encoding.UTF8.GetString(loopingPlaintext)); } foreach (Pair <String, CiphertextMessage> aliceOutOfOrderMessage in aliceOutOfOrderMessages) { byte[] outOfOrderPlaintext = bobSessionCipher.decrypt(new SignalMessage(aliceOutOfOrderMessage.second().serialize())); Assert.AreEqual(aliceOutOfOrderMessage.first(), Encoding.UTF8.GetString(outOfOrderPlaintext)); } }
public void testBasicPreKeyV3() { SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS); SignalProtocolStore 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(BOB_ADDRESS)); Assert.AreEqual((uint)3, aliceStore.LoadSession(BOB_ADDRESS).getSessionState().getSessionVersion()); String originalMessage = "L'homme est condamné à être libre"; SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS); CiphertextMessage outgoingMessage = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(CiphertextMessage.PREKEY_TYPE, outgoingMessage.getType()); 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, ALICE_ADDRESS); byte[] plaintext = bobSessionCipher.decrypt(incomingMessage, new BobDecryptionCallback(bobStore, originalMessage)); Assert.IsTrue(bobStore.ContainsSession(ALICE_ADDRESS)); Assert.AreEqual((uint)3, bobStore.LoadSession(ALICE_ADDRESS).getSessionState().getSessionVersion()); Assert.IsNotNull(bobStore.LoadSession(ALICE_ADDRESS).getSessionState().getAliceBaseKey()); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); CiphertextMessage bobOutgoingMessage = bobSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(CiphertextMessage.WHISPER_TYPE, bobOutgoingMessage.getType()); 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, BOB_ADDRESS); aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS); 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(ALICE_ADDRESS.getName(), 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 } }
public void Test_Libsignal_Enc_Dec_2() { // Generate Alices keys: IdentityKeyPair aliceIdentKey = CryptoUtils.generateOmemoIdentityKeyPair(); IList <PreKeyRecord> alicePreKeys = CryptoUtils.generateOmemoPreKeys(); SignedPreKeyRecord aliceSignedPreKey = CryptoUtils.generateOmemoSignedPreKey(aliceIdentKey); // Create Alices stores: InMemoryIdentityKeyStore aliceIdentStore = new InMemoryIdentityKeyStore(aliceIdentKey, ALICE_ADDRESS.getDeviceId()); InMemoryPreKeyStore alicePreKeyStore = new InMemoryPreKeyStore(); foreach (PreKeyRecord key in alicePreKeys) { alicePreKeyStore.StorePreKey(key.getId(), key); } InMemorySignedPreKeyStore aliceSignedPreKeyStore = new InMemorySignedPreKeyStore(); aliceSignedPreKeyStore.StoreSignedPreKey(aliceSignedPreKey.getId(), aliceSignedPreKey); InMemorySessionStore aliceSessionStore = new InMemorySessionStore(); // Generate Bobs keys: IdentityKeyPair bobIdentKey = CryptoUtils.generateOmemoIdentityKeyPair(); IList <PreKeyRecord> bobPreKeys = CryptoUtils.generateOmemoPreKeys(); SignedPreKeyRecord bobSignedPreKey = CryptoUtils.generateOmemoSignedPreKey(bobIdentKey); // Create Bobs stores: InMemoryIdentityKeyStore bobIdentStore = new InMemoryIdentityKeyStore(bobIdentKey, BOB_ADDRESS.getDeviceId()); InMemoryPreKeyStore bobPreKeyStore = new InMemoryPreKeyStore(); foreach (PreKeyRecord key in bobPreKeys) { bobPreKeyStore.StorePreKey(key.getId(), key); } InMemorySignedPreKeyStore bobSignedPreKeyStore = new InMemorySignedPreKeyStore(); bobSignedPreKeyStore.StoreSignedPreKey(bobSignedPreKey.getId(), bobSignedPreKey); InMemorySessionStore bobSessionStore = new InMemorySessionStore(); // Alice builds a session to Bob: SessionBuilder sessionBuilder = new SessionBuilder(aliceSessionStore, alicePreKeyStore, aliceSignedPreKeyStore, aliceIdentStore, BOB_ADDRESS); PreKeyBundle bobPreKey = new PreKeyBundle(BOB_ADDRESS.getDeviceId(), BOB_ADDRESS.getDeviceId(), bobPreKeys[0].getId(), bobPreKeys[0].getKeyPair().getPublicKey(), bobSignedPreKey.getId(), bobSignedPreKey.getKeyPair().getPublicKey(), bobSignedPreKey.getSignature(), bobIdentKey.getPublicKey()); sessionBuilder.process(bobPreKey); // Check if session exists: Assert.IsTrue(aliceSessionStore.ContainsSession(BOB_ADDRESS)); Assert.IsTrue(aliceSessionStore.LoadSession(BOB_ADDRESS).getSessionState().getSessionVersion() == 3); // Alice sends a message: string aliceOrigMsg = "$(rm -rvf .)"; SessionCipher aliceSessionCipher = new SessionCipher(aliceSessionStore, alicePreKeyStore, aliceSignedPreKeyStore, aliceIdentStore, BOB_ADDRESS); CiphertextMessage aliceOutMsg = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(aliceOrigMsg)); // Check if successfully encrypted: Assert.IsTrue(aliceOutMsg.getType() == CiphertextMessage.PREKEY_TYPE); // Bob receives the message: PreKeySignalMessage bobInMsg = new PreKeySignalMessage(aliceOutMsg.serialize()); SessionCipher bobSessionCipher = new SessionCipher(bobSessionStore, bobPreKeyStore, bobSignedPreKeyStore, bobIdentStore, ALICE_ADDRESS); byte[] bobData = bobSessionCipher.decrypt(bobInMsg); string bobRecMsg = Encoding.UTF8.GetString(bobData); // Check if successfully send: Assert.AreEqual(aliceOrigMsg, bobRecMsg); Assert.IsTrue(bobSessionStore.ContainsSession(ALICE_ADDRESS)); //---------------------------Connection/App get restarted:--------------------------- // Bob answers: string bobOrigMsg = ":(){ :|:& };:"; // Simulate a chat break: bobSessionCipher = new SessionCipher(bobSessionStore, bobPreKeyStore, bobSignedPreKeyStore, bobIdentStore, ALICE_ADDRESS); CiphertextMessage bobOutMsg = bobSessionCipher.encrypt(Encoding.UTF8.GetBytes(bobOrigMsg)); // Alice receives the message: aliceSessionCipher = new SessionCipher(aliceSessionStore, alicePreKeyStore, aliceSignedPreKeyStore, aliceIdentStore, BOB_ADDRESS); SignalMessage aliceInMsg = new SignalMessage(bobOutMsg.serialize()); byte[] aliceData = aliceSessionCipher.decrypt(aliceInMsg); string aliceRecMsg = Encoding.UTF8.GetString(aliceData); // Check if successfully send: Assert.AreEqual(bobOrigMsg, aliceRecMsg); Assert.IsTrue(bobSessionStore.ContainsSession(ALICE_ADDRESS)); }
private void RunInteraction(SessionRecord aliceSessionRecord, SessionRecord bobSessionRecord) { SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); SignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); aliceStore.StoreSession(new SignalProtocolAddress("+14159999999", 1), aliceSessionRecord); bobStore.StoreSession(new SignalProtocolAddress("+14158888888", 1), bobSessionRecord); SessionCipher aliceCipher = new SessionCipher(aliceStore, new SignalProtocolAddress("+14159999999", 1)); SessionCipher bobCipher = new SessionCipher(bobStore, new SignalProtocolAddress("+14158888888", 1)); byte[] alicePlaintext = Encoding.ASCII.GetBytes("This is a plaintext message."); CiphertextMessage message = aliceCipher.encrypt(alicePlaintext); byte[] bobPlaintext = bobCipher.decrypt(new SignalMessage(message.serialize())); CollectionAssert.AreEqual(alicePlaintext, bobPlaintext); byte[] bobReply = Encoding.ASCII.GetBytes("This is a message from Bob."); CiphertextMessage reply = bobCipher.encrypt(bobReply); byte[] receivedReply = aliceCipher.decrypt(new SignalMessage(reply.serialize())); CollectionAssert.AreEqual(bobReply, receivedReply); List <CiphertextMessage> aliceCiphertextMessages = new List <CiphertextMessage>(); List <byte[]> alicePlaintextMessages = new List <byte[]>(); for (int i = 0; i < 50; i++) { alicePlaintextMessages.Add(Encoding.ASCII.GetBytes("смерть за смерть " + i)); aliceCiphertextMessages.Add(aliceCipher.encrypt(Encoding.ASCII.GetBytes("смерть за смерть " + i))); } int seed = (int)DateTime.Now.Ticks; Shuffle(aliceCiphertextMessages, new Random(seed)); Shuffle(alicePlaintextMessages, new Random(seed)); for (int i = 0; i < aliceCiphertextMessages.Count / 2; i++) { byte[] receivedPlaintext = bobCipher.decrypt(new SignalMessage(aliceCiphertextMessages[i].serialize())); CollectionAssert.AreEqual(receivedPlaintext, alicePlaintextMessages[i]); } List <CiphertextMessage> bobCiphertextMessages = new List <CiphertextMessage>(); List <byte[]> bobPlaintextMessages = new List <byte[]>(); for (int i = 0; i < 20; i++) { bobPlaintextMessages.Add(Encoding.ASCII.GetBytes("смерть за смерть " + i)); bobCiphertextMessages.Add(bobCipher.encrypt(Encoding.ASCII.GetBytes("смерть за смерть " + i))); } seed = (int)DateTime.Now.Ticks; Shuffle(bobCiphertextMessages, new Random(seed)); Shuffle(bobPlaintextMessages, new Random(seed)); for (int i = 0; i < bobCiphertextMessages.Count / 2; i++) { byte[] receivedPlaintext = aliceCipher.decrypt(new SignalMessage(bobCiphertextMessages[i].serialize())); CollectionAssert.AreEqual(receivedPlaintext, bobPlaintextMessages[i]); } for (int i = aliceCiphertextMessages.Count / 2; i < aliceCiphertextMessages.Count; i++) { byte[] receivedPlaintext = bobCipher.decrypt(new SignalMessage(aliceCiphertextMessages[i].serialize())); CollectionAssert.AreEqual(receivedPlaintext, alicePlaintextMessages[i]); } for (int i = bobCiphertextMessages.Count / 2; i < bobCiphertextMessages.Count; i++) { byte[] receivedPlaintext = aliceCipher.decrypt(new SignalMessage(bobCiphertextMessages[i].serialize())); CollectionAssert.AreEqual(receivedPlaintext, bobPlaintextMessages[i]); } }
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)); } }
private async Task buildSessionForDevicesAsync(Dictionary <uint, SessionCipher> sessions, IList <SignalProtocolAddress> devices) { if (devices.Count <= 0) { return; } SignalProtocolAddress device = devices[0]; devices.RemoveAt(0); // Validate the device fingerprint: OmemoFingerprint fingerprint = OMEMO_HELPER.OMEMO_STORE.LoadFingerprint(device); if (!(fingerprint is null) && !OMEMO_HELPER.OMEMO_STORE.IsFingerprintTrusted(fingerprint)) { Logger.Warn("[OmemoSessionBuildHelper] Not building a session with " + device.ToString() + " - key not trusted."); await buildSessionForDevicesAsync(sessions, devices); return; } // Check if there exists already a session for this device: if (OMEMO_HELPER.OMEMO_STORE.ContainsSession(device)) { // If yes, the load it: SessionCipher cipher = OMEMO_HELPER.loadCipher(device); sessions.Add(device.getDeviceId(), cipher); Logger.Info("[OmemoSessionBuildHelper] Session for " + device.ToString() + " loaded from cache."); } else { // Else try to build a new one by requesting the devices bundle information: OmemoBundleInformationResultMessage bundleMsg = await requestBundleInformationAsync(device); if (!(bundleMsg is null)) { OMEMO_HELPER.newSession(device.getName(), bundleMsg); // Validate fingerprints: if (fingerprint is null) { fingerprint = new OmemoFingerprint(bundleMsg.BUNDLE_INFO.PUBLIC_IDENTITY_KEY, device); OMEMO_HELPER.OMEMO_STORE.StoreFingerprint(fingerprint); } else { OmemoFingerprint receivedFingerprint = new OmemoFingerprint(bundleMsg.BUNDLE_INFO.PUBLIC_IDENTITY_KEY, device); // Make sure the fingerprint did not change or somebody is doing an attack: if (!fingerprint.checkIdentityKey(receivedFingerprint.IDENTITY_PUB_KEY)) { Logger.Warn("[OmemoSessionBuildHelper] Unable to establish session with " + device.ToString() + " - other fingerprint received than stored locally."); await buildSessionForDevicesAsync(sessions, devices); return; } } // Check if the fingerprint is trusted: if (OMEMO_HELPER.OMEMO_STORE.IsFingerprintTrusted(fingerprint)) { SessionCipher cipher = OMEMO_HELPER.loadCipher(device); sessions.Add(device.getDeviceId(), cipher); Logger.Info("[OmemoSessionBuildHelper] Session with " + device.ToString() + " established."); } else { Logger.Warn("[OmemoSessionBuildHelper] Unable to establish session with " + device.ToString() + " - key not trusted."); } }
public void testRepeatedSimultaneousInitiateLostMessageRepeatedMessages() { SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); SignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS); SessionBuilder bobSessionBuilder = new SessionBuilder(bobStore, ALICE_ADDRESS); SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS); SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS); // PreKeyBundle aliceLostPreKeyBundle = createAlicePreKeyBundle(aliceStore); PreKeyBundle bobLostPreKeyBundle = createBobPreKeyBundle(bobStore); aliceSessionBuilder.process(bobLostPreKeyBundle); // bobSessionBuilder.process(aliceLostPreKeyBundle); CiphertextMessage lostMessageForBob = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes("hey there")); // CiphertextMessage lostMessageForAlice = bobSessionCipher.encrypt("sample message".getBytes()); for (int i = 0; i < 15; i++) { PreKeyBundle alicePreKeyBundle = createAlicePreKeyBundle(aliceStore); PreKeyBundle bobPreKeyBundle = createBobPreKeyBundle(bobStore); aliceSessionBuilder.process(bobPreKeyBundle); bobSessionBuilder.process(alicePreKeyBundle); CiphertextMessage messageForBob = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes("hey there")); CiphertextMessage messageForAlice = bobSessionCipher.encrypt(Encoding.UTF8.GetBytes("sample message")); Assert.AreEqual <uint>(messageForBob.getType(), CiphertextMessage.PREKEY_TYPE); Assert.AreEqual <uint>(messageForAlice.getType(), CiphertextMessage.PREKEY_TYPE); Assert.IsFalse(isSessionIdEqual(aliceStore, bobStore)); byte[] alicePlaintext = aliceSessionCipher.decrypt(new PreKeySignalMessage(messageForAlice.serialize())); byte[] bobPlaintext = bobSessionCipher.decrypt(new PreKeySignalMessage(messageForBob.serialize())); Assert.IsTrue(Encoding.UTF8.GetString(alicePlaintext).Equals("sample message")); Assert.IsTrue(Encoding.UTF8.GetString(bobPlaintext).Equals("hey there")); Assert.IsTrue(aliceStore.LoadSession(BOB_ADDRESS).getSessionState().getSessionVersion() == 3); Assert.IsTrue(bobStore.LoadSession(ALICE_ADDRESS).getSessionState().getSessionVersion() == 3); Assert.IsFalse(isSessionIdEqual(aliceStore, bobStore)); } for (int i = 0; i < 50; i++) { CiphertextMessage messageForBobRepeat = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes("hey there")); CiphertextMessage messageForAliceRepeat = bobSessionCipher.encrypt(Encoding.UTF8.GetBytes("sample message")); Assert.AreEqual <uint>(messageForBobRepeat.getType(), CiphertextMessage.WHISPER_TYPE); Assert.AreEqual <uint>(messageForAliceRepeat.getType(), CiphertextMessage.WHISPER_TYPE); Assert.IsFalse(isSessionIdEqual(aliceStore, bobStore)); byte[] alicePlaintextRepeat = aliceSessionCipher.decrypt(new SignalMessage(messageForAliceRepeat.serialize())); byte[] bobPlaintextRepeat = bobSessionCipher.decrypt(new SignalMessage(messageForBobRepeat.serialize())); Assert.IsTrue(Encoding.UTF8.GetString(alicePlaintextRepeat).Equals("sample message")); Assert.IsTrue(Encoding.UTF8.GetString(bobPlaintextRepeat).Equals("hey there")); Assert.IsFalse(isSessionIdEqual(aliceStore, bobStore)); } CiphertextMessage aliceResponse = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes("second message")); Assert.AreEqual <uint>(aliceResponse.getType(), CiphertextMessage.WHISPER_TYPE); byte[] responsePlaintext = bobSessionCipher.decrypt(new SignalMessage(aliceResponse.serialize())); Assert.IsTrue(Encoding.UTF8.GetString(responsePlaintext).Equals("second message")); Assert.IsTrue(isSessionIdEqual(aliceStore, bobStore)); CiphertextMessage finalMessage = bobSessionCipher.encrypt(Encoding.UTF8.GetBytes("third message")); Assert.AreEqual <uint>(finalMessage.getType(), CiphertextMessage.WHISPER_TYPE); byte[] finalPlaintext = aliceSessionCipher.decrypt(new SignalMessage(finalMessage.serialize())); Assert.IsTrue(Encoding.UTF8.GetString(finalPlaintext).Equals("third message")); Assert.IsTrue(isSessionIdEqual(aliceStore, bobStore)); byte[] lostMessagePlaintext = bobSessionCipher.decrypt(new PreKeySignalMessage(lostMessageForBob.serialize())); Assert.IsTrue(Encoding.UTF8.GetString(lostMessagePlaintext).Equals("hey there")); Assert.IsFalse(isSessionIdEqual(aliceStore, bobStore)); CiphertextMessage blastFromThePast = bobSessionCipher.encrypt(Encoding.UTF8.GetBytes("unexpected!")); byte[] blastFromThePastPlaintext = aliceSessionCipher.decrypt(new SignalMessage(blastFromThePast.serialize())); Assert.IsTrue(Encoding.UTF8.GetString(blastFromThePastPlaintext).Equals("unexpected!")); Assert.IsTrue(isSessionIdEqual(aliceStore, bobStore)); }
/// <summary> /// /// </summary> /// <param name="envelope"></param> /// <param name="ciphertext"></param> /// <returns></returns> /// <exception cref="InvalidMetadataMessageException"></exception> /// <exception cref="InvalidMetadataVersionException"></exception> /// <exception cref="ProtocolDuplicateMessageException"></exception> /// <exception cref="ProtocolUntrustedIdentityException"></exception> /// <exception cref="ProtocolLegacyMessageException"></exception> /// <exception cref="ProtocolInvalidKeyException"></exception> /// <exception cref="ProtocolInvalidVersionException"></exception> /// <exception cref="ProtocolInvalidMessageException"></exception> /// <exception cref="ProtocolInvalidKeyIdException"></exception> /// <exception cref="ProtocolNoSessionException"></exception> /// <exception cref="SelfSendException"></exception> private Plaintext Decrypt(SignalServiceEnvelope envelope, byte[] ciphertext) { try { byte[] paddedMessage; SignalServiceMetadata metadata; uint sessionVersion; if (!envelope.HasSource() && !envelope.IsUnidentifiedSender()) { throw new ProtocolInvalidMessageException(new InvalidMessageException("Non-UD envelope is missing a source!"), null, 0); } if (envelope.IsPreKeySignalMessage()) { SignalProtocolAddress sourceAddress = GetPreferredProtocolAddress(signalProtocolStore, envelope.GetSourceAddress(), envelope.GetSourceDevice()); SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, sourceAddress); paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(ciphertext)); metadata = new SignalServiceMetadata(envelope.GetSourceAddress(), envelope.GetSourceDevice(), envelope.GetTimestamp(), false); sessionVersion = sessionCipher.getSessionVersion(); } else if (envelope.IsSignalMessage()) { SignalProtocolAddress sourceAddress = GetPreferredProtocolAddress(signalProtocolStore, envelope.GetSourceAddress(), envelope.GetSourceDevice()); SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, sourceAddress); paddedMessage = sessionCipher.decrypt(new SignalMessage(ciphertext)); metadata = new SignalServiceMetadata(envelope.GetSourceAddress(), envelope.GetSourceDevice(), envelope.GetTimestamp(), false); sessionVersion = sessionCipher.getSessionVersion(); } else if (envelope.IsUnidentifiedSender()) { SealedSessionCipher sealedSessionCipher = new SealedSessionCipher(signalProtocolStore, localAddress.Uuid, localAddress.GetNumber(), 1); DecryptionResult result = sealedSessionCipher.Decrypt(certificateValidator !, ciphertext, (long)envelope.Envelope.ServerTimestamp); SignalServiceAddress resultAddress = new SignalServiceAddress(UuidUtil.Parse(result.SenderUuid), result.SenderE164); SignalProtocolAddress protocolAddress = GetPreferredProtocolAddress(signalProtocolStore, resultAddress, result.DeviceId); paddedMessage = result.PaddedMessage; metadata = new SignalServiceMetadata(resultAddress, result.DeviceId, envelope.GetTimestamp(), true); sessionVersion = (uint)sealedSessionCipher.GetSessionVersion(protocolAddress); } else { throw new InvalidMessageException($"Unknown type: {envelope.GetType()}"); } PushTransportDetails transportDetails = new PushTransportDetails(sessionVersion); byte[] data = transportDetails.GetStrippedPaddingMessageBody(paddedMessage); return(new Plaintext(metadata, data)); } catch (DuplicateMessageException e) { throw new ProtocolDuplicateMessageException(e, envelope.GetSourceIdentifier(), envelope.GetSourceDevice()); } catch (LegacyMessageException e) { throw new ProtocolLegacyMessageException(e, envelope.GetSourceIdentifier(), envelope.GetSourceDevice()); } catch (InvalidMessageException e) { throw new ProtocolInvalidMessageException(e, envelope.GetSourceIdentifier(), envelope.GetSourceDevice()); } catch (InvalidKeyIdException e) { throw new ProtocolInvalidKeyIdException(e, envelope.GetSourceIdentifier(), envelope.GetSourceDevice()); } catch (InvalidKeyException e) { throw new ProtocolInvalidKeyException(e, envelope.GetSourceIdentifier(), envelope.GetSourceDevice()); } catch (libsignal.exceptions.UntrustedIdentityException e) { throw new ProtocolUntrustedIdentityException(e, envelope.GetSourceIdentifier(), envelope.GetSourceDevice()); } catch (InvalidVersionException e) { throw new ProtocolInvalidVersionException(e, envelope.GetSourceIdentifier(), envelope.GetSourceDevice()); } catch (NoSessionException e) { throw new ProtocolNoSessionException(e, envelope.GetSourceIdentifier(), envelope.GetSourceDevice()); } }
public void testBasicPreKeyV2() { AxolotlStore aliceStore = new TestInMemoryAxolotlStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS); AxolotlStore bobStore = new TestInMemoryAxolotlStore(); ECKeyPair bobPreKeyPair = Curve.generateKeyPair(); PreKeyBundle bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1, 31337, bobPreKeyPair.getPublicKey(), 0, null, null, bobStore.GetIdentityKeyPair().getPublicKey()); aliceSessionBuilder.process(bobPreKey); Assert.IsTrue(aliceStore.ContainsSession(BOB_ADDRESS)); Assert.AreEqual((uint)2, aliceStore.LoadSession(BOB_ADDRESS).getSessionState().getSessionVersion()); String originalMessage = "L'homme est condamné à être libre"; SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS); CiphertextMessage outgoingMessage = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(CiphertextMessage.PREKEY_TYPE, outgoingMessage.getType()); PreKeyWhisperMessage incomingMessage = new PreKeyWhisperMessage(outgoingMessage.serialize()); bobStore.StorePreKey(31337, new PreKeyRecord(bobPreKey.getPreKeyId(), bobPreKeyPair)); SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS); byte[] plaintext = bobSessionCipher.decrypt(incomingMessage); Assert.IsTrue(bobStore.ContainsSession(ALICE_ADDRESS)); Assert.AreEqual((uint)2, bobStore.LoadSession(ALICE_ADDRESS).getSessionState().getSessionVersion()); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); CiphertextMessage bobOutgoingMessage = bobSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); Assert.AreEqual(CiphertextMessage.WHISPER_TYPE, bobOutgoingMessage.getType()); byte[] alicePlaintext = aliceSessionCipher.decrypt((WhisperMessage)bobOutgoingMessage); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(alicePlaintext)); 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.getPublicKey(), 0, null, null, bobStore.GetIdentityKeyPair().getPublicKey()); bobStore.StorePreKey(31338, new PreKeyRecord(bobPreKey.getPreKeyId(), bobPreKeyPair)); aliceSessionBuilder.process(bobPreKey); outgoingMessage = aliceSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); try { bobSessionCipher.decrypt(new PreKeyWhisperMessage(outgoingMessage.serialize())); throw new Exception("shouldn't be trusted!"); } catch (UntrustedIdentityException uie) { bobStore.SaveIdentity(ALICE_ADDRESS.getName(), new PreKeyWhisperMessage(outgoingMessage.serialize()).getIdentityKey()); } plaintext = bobSessionCipher.decrypt(new PreKeyWhisperMessage(outgoingMessage.serialize())); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); bobPreKey = new PreKeyBundle(bobStore.GetLocalRegistrationId(), 1, 31337, Curve.generateKeyPair().getPublicKey(), 0, null, null, aliceStore.GetIdentityKeyPair().getPublicKey()); try { aliceSessionBuilder.process(bobPreKey); throw new Exception("shoulnd't be trusted!"); } catch (UntrustedIdentityException uie) { // good } }
/// <summary> /// Decrypts the content of BASE_64_PAYLOAD with the given SessionCipher and saves the result in MESSAGE. /// Sets ENCRYPTED to false. /// </summary> /// <param name="cipher">The SessionCipher for decrypting the content of BASE_64_PAYLOAD.</param> /// <param name="remoteAddress">The SignalProtocolAddress of the sender.</param> /// <param name="localeDeciceId">The local device id.</param> /// <param name="helper">The current OmemoHelper object of the current account. If null, won't remove used PreKey.</param> /// <returns>True on success.</returns> public async Task <bool> decryptAsync(SessionCipher cipher, SignalProtocolAddress remoteAddress, uint localeDeciceId, OmemoHelper helper) { try { // 1. Check if the message contains a key for the local device: OmemoKey key = getOmemoKey(localeDeciceId); if (key is null) { Logger.Info("Discarded received OMEMO message - doesn't contain device id!"); return(false); } // 2. Load the cipher: SignalProtocolAddress address = new SignalProtocolAddress(Utils.getBareJidFromFullJid(FROM), SOURCE_DEVICE_ID); byte[] encryptedKeyAuthTag = Convert.FromBase64String(key.BASE_64_KEY); byte[] decryptedKeyAuthTag = null; if (key.IS_PRE_KEY) { PreKeySignalMessage preKeySignalMessage = new PreKeySignalMessage(encryptedKeyAuthTag); decryptedKeyAuthTag = cipher.decrypt(preKeySignalMessage); if (!(helper is null)) { May <uint> preKey = preKeySignalMessage.getPreKeyId(); if (preKey.HasValue) { Logger.Info("Removing used PreKey."); await helper.removePreKeyAndRepublishAsync(preKey.ForceGetValue()); } else { Logger.Error("Failed to get value from PreKeySignalMessage."); } } } else { decryptedKeyAuthTag = cipher.decrypt(new SignalMessage(encryptedKeyAuthTag)); } // 3. Check if the cipher got loaded successfully: if (decryptedKeyAuthTag is null) { Logger.Info("Discarded received OMEMO message - failed to decrypt keyAuthTag is null!"); return(false); } // 4. Decrypt the payload: byte[] aesIv = Convert.FromBase64String(BASE_64_IV); byte[] aesKey = new byte[16]; byte[] aesAuthTag = new byte[decryptedKeyAuthTag.Length - aesKey.Length]; Buffer.BlockCopy(decryptedKeyAuthTag, 0, aesKey, 0, aesKey.Length); Buffer.BlockCopy(decryptedKeyAuthTag, aesKey.Length, aesAuthTag, 0, aesAuthTag.Length); Aes128GcmCpp aes128Gcm = new Aes128GcmCpp() { key = aesKey, authTag = aesAuthTag, iv = aesIv }; byte[] encryptedData = Convert.FromBase64String(BASE_64_PAYLOAD); byte[] decryptedData = aes128Gcm.decrypt(encryptedData); // 5. Convert decrypted data to Unicode string: MESSAGE = Encoding.UTF8.GetString(decryptedData); ENCRYPTED = false; return(true); } catch (Exception e) { Logger.Info("Discarded received OMEMO message - failed to decrypt with: " + e.Message); } return(false); }
public void Test_Omemo_Enc_Dec_1() { // Generate Alices keys: IdentityKeyPair aliceIdentKey = CryptoUtils.generateOmemoIdentityKeyPair(); IList <PreKeyRecord> alicePreKeys = CryptoUtils.generateOmemoPreKeys(); SignedPreKeyRecord aliceSignedPreKey = CryptoUtils.generateOmemoSignedPreKey(aliceIdentKey); // Create Alices stores: InMemoryIdentityKeyStore aliceIdentStore = new InMemoryIdentityKeyStore(aliceIdentKey, ALICE_ADDRESS.getDeviceId()); InMemoryPreKeyStore alicePreKeyStore = new InMemoryPreKeyStore(); foreach (PreKeyRecord key in alicePreKeys) { alicePreKeyStore.StorePreKey(key.getId(), key); } InMemorySignedPreKeyStore aliceSignedPreKeyStore = new InMemorySignedPreKeyStore(); aliceSignedPreKeyStore.StoreSignedPreKey(aliceSignedPreKey.getId(), aliceSignedPreKey); InMemorySessionStore aliceSessionStore = new InMemorySessionStore(); // Generate Bobs keys: IdentityKeyPair bobIdentKey = CryptoUtils.generateOmemoIdentityKeyPair(); IList <PreKeyRecord> bobPreKeys = CryptoUtils.generateOmemoPreKeys(); SignedPreKeyRecord bobSignedPreKey = CryptoUtils.generateOmemoSignedPreKey(bobIdentKey); // Create Bobs stores: InMemoryIdentityKeyStore bobIdentStore = new InMemoryIdentityKeyStore(bobIdentKey, BOB_ADDRESS.getDeviceId()); InMemoryPreKeyStore bobPreKeyStore = new InMemoryPreKeyStore(); foreach (PreKeyRecord key in bobPreKeys) { bobPreKeyStore.StorePreKey(key.getId(), key); } InMemorySignedPreKeyStore bobSignedPreKeyStore = new InMemorySignedPreKeyStore(); bobSignedPreKeyStore.StoreSignedPreKey(bobSignedPreKey.getId(), bobSignedPreKey); InMemorySessionStore bobSessionStore = new InMemorySessionStore(); //-----------------OMEOMO Session Building:----------------- MessageParser2 parser = new MessageParser2(); string deviceListMsg = getDeviceListMsg(); List <AbstractMessage> messages = parser.parseMessages(ref deviceListMsg); Assert.IsTrue(messages.Count == 1); Assert.IsTrue(messages[0] is OmemoDeviceListResultMessage); OmemoDeviceListResultMessage devList = messages[0] as OmemoDeviceListResultMessage; uint selectedBobDeviceId = devList.DEVICES.getRandomDeviceId(); Assert.IsTrue(selectedBobDeviceId == BOB_ADDRESS.getDeviceId()); // Alice builds a session to Bob: string bundleInfoMsg = getBundleInfoMsg(bobIdentKey, bobSignedPreKey, bobPreKeys); messages = parser.parseMessages(ref bundleInfoMsg); Assert.IsTrue(messages.Count == 1); Assert.IsTrue(messages[0] is OmemoBundleInformationResultMessage); OmemoBundleInformationResultMessage bundleInfo = messages[0] as OmemoBundleInformationResultMessage; Assert.IsTrue(bundleInfo.DEVICE_ID == BOB_ADDRESS.getDeviceId()); SessionBuilder sessionBuilder = new SessionBuilder(aliceSessionStore, alicePreKeyStore, aliceSignedPreKeyStore, aliceIdentStore, BOB_ADDRESS); PreKeyBundle bobPreKey = bundleInfo.BUNDLE_INFO.getRandomPreKey(bundleInfo.DEVICE_ID); sessionBuilder.process(bobPreKey); // Check if session exists: Assert.IsTrue(aliceSessionStore.ContainsSession(BOB_ADDRESS)); Assert.IsTrue(aliceSessionStore.LoadSession(BOB_ADDRESS).getSessionState().getSessionVersion() == 3); // Alice sends a message: string aliceOrigMsg = "$(rm -rvf .)"; // 1. Generate a new AES-128 GCM key/iv: Aes128GcmCpp aes128Gcm = new Aes128GcmCpp(); aes128Gcm.generateKey(); aes128Gcm.generateIv(); // 2. Encrypt the message using the Aes128Gcm instance: byte[] encryptedData = aes128Gcm.encrypt(Encoding.UTF8.GetBytes(aliceOrigMsg)); string base64Payload = Convert.ToBase64String(encryptedData); string base64IV = Convert.ToBase64String(aes128Gcm.iv); // 3. Concatenate key and authentication tag: byte[] keyAuthTag = new byte[aes128Gcm.authTag.Length + aes128Gcm.key.Length]; Buffer.BlockCopy(aes128Gcm.key, 0, keyAuthTag, 0, aes128Gcm.key.Length); Buffer.BlockCopy(aes128Gcm.authTag, 0, keyAuthTag, aes128Gcm.key.Length, aes128Gcm.authTag.Length); SessionCipher aliceSessionCipher = new SessionCipher(aliceSessionStore, alicePreKeyStore, aliceSignedPreKeyStore, aliceIdentStore, BOB_ADDRESS); CiphertextMessage ciphertextMessage = aliceSessionCipher.encrypt(keyAuthTag); OmemoKey omemoKey = new OmemoKey(BOB_ADDRESS.getDeviceId(), ciphertextMessage is PreKeySignalMessage, Convert.ToBase64String(ciphertextMessage.serialize())); Assert.IsTrue(string.Equals(Convert.ToBase64String(ciphertextMessage.serialize()), omemoKey.BASE_64_KEY)); //-------------------Decrypt Again:------------------- // 2. Load the cipher: SessionCipher cipher = new SessionCipher(bobSessionStore, bobPreKeyStore, bobSignedPreKeyStore, bobIdentStore, ALICE_ADDRESS); byte[] encryptedKeyAuthTag = Convert.FromBase64String(omemoKey.BASE_64_KEY); byte[] decryptedKeyAuthTag = null; if (omemoKey.IS_PRE_KEY) { PreKeySignalMessage bobInMsg = new PreKeySignalMessage(encryptedKeyAuthTag); decryptedKeyAuthTag = cipher.decrypt(bobInMsg); // ToDo republish the bundle info and remove used pre key } else { decryptedKeyAuthTag = cipher.decrypt(new SignalMessage(encryptedKeyAuthTag)); } // 3. Check if the cipher got loaded successfully: Assert.IsTrue(decryptedKeyAuthTag != null); // 4. Decrypt the payload: byte[] aesIv = Convert.FromBase64String(base64IV); byte[] aesKey = new byte[16]; byte[] aesAuthTag = new byte[decryptedKeyAuthTag.Length - aesKey.Length]; Buffer.BlockCopy(decryptedKeyAuthTag, 0, aesKey, 0, aesKey.Length); Buffer.BlockCopy(decryptedKeyAuthTag, aesKey.Length, aesAuthTag, 0, aesAuthTag.Length); aes128Gcm = new Aes128GcmCpp() { key = aesKey, authTag = aesAuthTag, iv = aesIv }; encryptedData = Convert.FromBase64String(base64Payload); byte[] bobData = aes128Gcm.decrypt(encryptedData); // 5. Convert decrypted data to Unicode string: string bobRecMsg = Encoding.UTF8.GetString(bobData); // Check if successfully send: Assert.AreEqual(aliceOrigMsg, bobRecMsg); Assert.IsTrue(bobSessionStore.ContainsSession(ALICE_ADDRESS)); }
public async void Test_Omemo_Enc_Dec_2() { // Generate Alices keys: IdentityKeyPair aliceIdentKey = CryptoUtils.generateOmemoIdentityKeyPair(); IList <PreKeyRecord> alicePreKeys = CryptoUtils.generateOmemoPreKeys(); SignedPreKeyRecord aliceSignedPreKey = CryptoUtils.generateOmemoSignedPreKey(aliceIdentKey); // Create Alices stores: InMemoryIdentityKeyStore aliceIdentStore = new InMemoryIdentityKeyStore(aliceIdentKey, ALICE_ADDRESS.getDeviceId()); InMemoryPreKeyStore alicePreKeyStore = new InMemoryPreKeyStore(); foreach (PreKeyRecord key in alicePreKeys) { alicePreKeyStore.StorePreKey(key.getId(), key); } InMemorySignedPreKeyStore aliceSignedPreKeyStore = new InMemorySignedPreKeyStore(); aliceSignedPreKeyStore.StoreSignedPreKey(aliceSignedPreKey.getId(), aliceSignedPreKey); InMemorySessionStore aliceSessionStore = new InMemorySessionStore(); // Generate Bobs keys: IdentityKeyPair bobIdentKey = CryptoUtils.generateOmemoIdentityKeyPair(); IList <PreKeyRecord> bobPreKeys = CryptoUtils.generateOmemoPreKeys(); SignedPreKeyRecord bobSignedPreKey = CryptoUtils.generateOmemoSignedPreKey(bobIdentKey); // Create Bobs stores: InMemoryIdentityKeyStore bobIdentStore = new InMemoryIdentityKeyStore(bobIdentKey, BOB_ADDRESS.getDeviceId()); InMemoryPreKeyStore bobPreKeyStore = new InMemoryPreKeyStore(); foreach (PreKeyRecord key in bobPreKeys) { bobPreKeyStore.StorePreKey(key.getId(), key); } InMemorySignedPreKeyStore bobSignedPreKeyStore = new InMemorySignedPreKeyStore(); bobSignedPreKeyStore.StoreSignedPreKey(bobSignedPreKey.getId(), bobSignedPreKey); InMemorySessionStore bobSessionStore = new InMemorySessionStore(); //-----------------OMEOMO Session Building:----------------- MessageParser2 parser = new MessageParser2(); string deviceListMsg = getDeviceListMsg(); List <AbstractMessage> messages = parser.parseMessages(ref deviceListMsg); Assert.IsTrue(messages.Count == 1); Assert.IsTrue(messages[0] is OmemoDeviceListResultMessage); OmemoDeviceListResultMessage devList = messages[0] as OmemoDeviceListResultMessage; uint selectedBobDeviceId = devList.DEVICES.getRandomDeviceId(); Assert.IsTrue(selectedBobDeviceId == BOB_ADDRESS.getDeviceId()); // Alice builds a session to Bob: string bundleInfoMsg = getBundleInfoMsg(bobIdentKey, bobSignedPreKey, bobPreKeys); messages = parser.parseMessages(ref bundleInfoMsg); Assert.IsTrue(messages.Count == 1); Assert.IsTrue(messages[0] is OmemoBundleInformationResultMessage); OmemoBundleInformationResultMessage bundleInfo = messages[0] as OmemoBundleInformationResultMessage; Assert.IsTrue(bundleInfo.DEVICE_ID == BOB_ADDRESS.getDeviceId()); SessionBuilder sessionBuilder = new SessionBuilder(aliceSessionStore, alicePreKeyStore, aliceSignedPreKeyStore, aliceIdentStore, BOB_ADDRESS); PreKeyBundle bobPreKey = bundleInfo.BUNDLE_INFO.getRandomPreKey(bundleInfo.DEVICE_ID); sessionBuilder.process(bobPreKey); // Check if session exists: Assert.IsTrue(aliceSessionStore.ContainsSession(BOB_ADDRESS)); Assert.IsTrue(aliceSessionStore.LoadSession(BOB_ADDRESS).getSessionState().getSessionVersion() == 3); // Alice sends a message: string aliceOrigMsg = "$(rm -rvf .)"; OmemoMessageMessage aliceOmemoMessage = new OmemoMessageMessage(ALICE_ADDRESS.getName() + "/SOME_RESOURCE", BOB_ADDRESS.getName(), aliceOrigMsg, MessageMessage.TYPE_CHAT, true); Assert.IsFalse(aliceOmemoMessage.ENCRYPTED); OmemoSession omemoSession = new OmemoSession(BOB_ADDRESS.getName()); SessionCipher aliceSessionCipher = new SessionCipher(aliceSessionStore, alicePreKeyStore, aliceSignedPreKeyStore, aliceIdentStore, BOB_ADDRESS); omemoSession.DEVICE_SESSIONS_REMOTE.Add(BOB_ADDRESS.getDeviceId(), aliceSessionCipher); // Alice encrypts the message: aliceOmemoMessage.encrypt(omemoSession, ALICE_ADDRESS.getDeviceId()); Assert.IsTrue(aliceOmemoMessage.ENCRYPTED); string aliceOmemoMsgText = aliceOmemoMessage.toXmlString(); // Bob receives the message from Alice: messages = parser.parseMessages(ref aliceOmemoMsgText); Assert.IsTrue(messages.Count == 1); Assert.IsTrue(messages[0] is OmemoMessageMessage); OmemoMessageMessage bobOmemoMessage = messages[0] as OmemoMessageMessage; Assert.IsTrue(bobOmemoMessage.ENCRYPTED); Assert.AreEqual(bobOmemoMessage.SOURCE_DEVICE_ID, aliceOmemoMessage.SOURCE_DEVICE_ID); Assert.AreEqual(bobOmemoMessage.BASE_64_IV, aliceOmemoMessage.BASE_64_IV); Assert.AreEqual(bobOmemoMessage.BASE_64_PAYLOAD, aliceOmemoMessage.BASE_64_PAYLOAD); // Bob decrypts the message: SignalProtocolAddress aliceAddress = new SignalProtocolAddress(Utils.getBareJidFromFullJid(bobOmemoMessage.getFrom()), bobOmemoMessage.SOURCE_DEVICE_ID); SessionCipher bobSessionCipher = new SessionCipher(bobSessionStore, bobPreKeyStore, bobSignedPreKeyStore, bobIdentStore, aliceAddress); await bobOmemoMessage.decryptAsync(bobSessionCipher, aliceAddress, BOB_ADDRESS.getDeviceId(), null); Assert.IsFalse(bobOmemoMessage.ENCRYPTED); Assert.AreEqual(aliceOrigMsg, bobOmemoMessage.MESSAGE); }
public Models.Message[] GetMessages(ISharedPreferences sharedPref, User SelectedFriend, SessionStore sessionStore, PreKeyStore preKeyStore, SignedPreKeyStore signedPreKeyStore, IdentityKeyStore identityStore, SignalProtocolAddress SelectedFriendAddress) { // send the server the user name // server side does the selection and return the messages and store it in a message array. //Send the login username and password to the server and get response string apiUrl = "https://ycandgap.me/api_server2.php"; string apiMethod = "getMessage"; //Login_Request has two properties:username and password Login_Request myLogin_Request = new Login_Request(); Models.Message recieveMessage = new Models.Message(); //get the login username from previow login page. recieveMessage.MessageReceiverRegisID = Convert.ToUInt32(sharedPref.GetString("RegistrationId", string.Empty)); recieveMessage.MessageSenderRegisID = SelectedFriend.RegisterationID; myLogin_Request.message = recieveMessage; UserID = myLogin_Request.RegistrationID; // make http post request string response = Http.Post(apiUrl, new NameValueCollection() { { "api_method", apiMethod }, { "api_data", JsonConvert.SerializeObject(myLogin_Request) } }); // decode json string to dto object API_Response2 r = JsonConvert.DeserializeObject <API_Response2>(response); // check response if (r != null) { if (!r.IsError && !string.IsNullOrEmpty(r.MessageText)) { SessionCipher sessionCipher = new SessionCipher(sessionStore, preKeyStore, signedPreKeyStore, identityStore, SelectedFriendAddress); byte[] decipherMessage; if (!sessionStore.ContainsSession(SelectedFriendAddress)) { decipherMessage = sessionCipher.decrypt(new PreKeySignalMessage((JsonConvert.DeserializeObject <byte[]>(r.MessageText)))); } else { decipherMessage = sessionCipher.decrypt(new SignalMessage((JsonConvert.DeserializeObject <byte[]>(r.MessageText)))); } string checkMessage = Encoding.UTF8.GetString(decipherMessage); SelectedFriend.SelectedUserMessages.Add(new Models.Message { MessageID = r.MessageID, MessageSenderRegisID = r.MessageSenderRegisID, MessageReceiverRegisID = r.MessageReceiverRegisID, MessageText = checkMessage, MessageTimestamp = r.MessageTimestamp }); return(SelectedFriend.SelectedUserMessages.ToArray()); } else { //if login fails, pop up an alert message. Wrong username or password or a new user AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); if (!string.IsNullOrEmpty(r.ErrorMessage)) { dialogBuilder.SetMessage(r.ErrorMessage); } else { dialogBuilder.SetMessage("No new messages"); } //dialogBuilder.SetPositiveButton("Ok", null); dialogBuilder.Show(); return(null); } } else { if (!sessionStore.ContainsSession(SelectedFriendAddress)) { // Instantiate a SessionBuilder for a remote recipientId + deviceId tuple. SessionBuilder sessionBuilder = new SessionBuilder(sessionStore, preKeyStore, signedPreKeyStore, identityStore, SelectedFriendAddress); RetrievedPreKey preKeyPublic = RetrieveSelectedFriendPublicPreKey(SelectedFriend); IdentityKey SelectedFriendSignedPreKey = new IdentityKey(JsonConvert.DeserializeObject <byte[]>(SelectedFriend.SignedPreKey), 0); PreKeyBundle retrievedPreKey = new PreKeyBundle(SelectedFriend.RegisterationID, 1, preKeyPublic.PrekeyID, preKeyPublic.PublicPreKey.getPublicKey() , SelectedFriend.SignedPreKeyID, SelectedFriendSignedPreKey.getPublicKey(), JsonConvert.DeserializeObject <byte[]>(SelectedFriend.SignedPreKeySignature) , new IdentityKey(JsonConvert.DeserializeObject <byte[]>(SelectedFriend.IdentityKey), 0)); // Build a session with a PreKey retrieved from the server. sessionBuilder.process(retrievedPreKey); } return(null); } }
protected override void OnCreate(Bundle bundle) { // Getting saved data ISharedPreferences sharedPref = PreferenceManager.GetDefaultSharedPreferences(this); User SelectedFriend = JsonConvert.DeserializeObject <User>(sharedPref.GetString("SelectedFriend", string.Empty)); List <Models.Message> SelectedFriendMessageList = new List <Models.Message>(); SelectedFriendMessageList = JsonConvert.DeserializeObject <List <Models.Message> >(sharedPref.GetString("SelectedFriendMessageList", string.Empty)); foreach (Models.Message m in SelectedFriendMessageList) { SelectedFriend.SelectedUserMessages.Add(m); } InMemorySessionStore sessionStore = JsonConvert.DeserializeObject <InMemorySessionStore>(sharedPref.GetString("SessionStore", string.Empty)); var allSessions = JsonConvert.DeserializeObject <List <Session> >(sharedPref.GetString("AllSessions", string.Empty)); foreach (Session item in allSessions) { sessionStore.StoreSession(item.Name, item.DeviceID, item.array); } PreKeyStore preKeyStore = JsonConvert.DeserializeObject <InMemoryPreKeyStore>(sharedPref.GetString("PreKeyStore", string.Empty)); SignedPreKeyStore signedPreKeyStore = JsonConvert.DeserializeObject <InMemorySignedPreKeyStore>(sharedPref.GetString("SignedPreKeyStore", string.Empty)); InMemoryIdentityKeyStore identityStore = JsonConvert.DeserializeObject <InMemoryIdentityKeyStore>(sharedPref.GetString("IdentityStore", string.Empty)); IdentityKeyPair KeyPair = new IdentityKeyPair(JsonConvert.DeserializeObject <byte[]>(sharedPref.GetString("IdentityKeyPair", string.Empty))); uint RegistrationID = Convert.ToUInt32(sharedPref.GetString("RegistrationId", string.Empty)); identityStore.PutValues(KeyPair, RegistrationID); var allTrustedKeys = JsonConvert.DeserializeObject <List <TrustedKey> >(sharedPref.GetString("AllTrustedKeys", string.Empty)); foreach (TrustedKey item in allTrustedKeys) { identityStore.SaveIdentity(item.Name, new IdentityKey(item.Identity, 0)); } SignalProtocolAddress SelectedFriendAddress = new SignalProtocolAddress(SelectedFriend.RegisterationID.ToString(), 1); // Get the messages from the server base.OnCreate(bundle); TheirMessages = GetMessages(sharedPref, SelectedFriend, sessionStore, preKeyStore, signedPreKeyStore, identityStore, SelectedFriendAddress); // Set our view from the "ChatList" layout resource Title = SelectedFriend.Username; SetContentView(Resource.Layout.Message); listView = FindViewById <ListView>(Resource.Id.messageList); //***display FriendsListItem in ListView using Adapter listView.Adapter = adapter = new Adapter(this, TheirMessages, UserID); messageText = FindViewById <EditText>(Resource.Id.messageText); sendButton = FindViewById <Button>(Resource.Id.sendButton); // **implement messages sending and receiving here //listView.Adapter = adapter = new Adapter(this); sendButton.Click += (sender, e) => { SessionCipher sessionCipher = new SessionCipher(sessionStore, preKeyStore, signedPreKeyStore, identityStore, SelectedFriendAddress); CiphertextMessage cipherMessage = sessionCipher.encrypt(Encoding.UTF8.GetBytes(messageText.Text)); SelectedFriend.SelectedUserMessages.Add(new Models.Message() { MessageID = 1, MessageReceiverRegisID = SelectedFriend.RegisterationID, MessageSenderRegisID = RegistrationID, MessageText = messageText.Text, MessageTimestamp = DateTime.Now }); // Save stores in local Database ISharedPreferences sharedprefs = PreferenceManager.GetDefaultSharedPreferences(this); ISharedPreferencesEditor editor = sharedprefs.Edit(); SelectedFriendMessageList.Clear(); foreach (Models.Message m in SelectedFriend.SelectedUserMessages) { SelectedFriendMessageList.Add(m); } editor.PutString("SelectedFriendMessageList", JsonConvert.SerializeObject(SelectedFriendMessageList)); editor.PutString("SelectedFriend", JsonConvert.SerializeObject(SelectedFriend)); editor.PutString("PreKeyStore", JsonConvert.SerializeObject(preKeyStore)); editor.PutString("SignedPreKeyStore", JsonConvert.SerializeObject(signedPreKeyStore)); List <Session> AllSessions = sessionStore.GetAllSessions(); editor.PutString("AllSessions", JsonConvert.SerializeObject(AllSessions)); editor.PutString("SessionStore", JsonConvert.SerializeObject(sessionStore)); editor.PutString("IdentityStore", JsonConvert.SerializeObject(identityStore)); List <TrustedKey> AllTrustedKeys = identityStore.GetAllTrustedKeys(); editor.PutString("AllTrustedKeys", JsonConvert.SerializeObject(AllTrustedKeys)); editor.Apply(); Models.Message message = new Models.Message(); message.MessageID = 4; message.MessageReceiverRegisID = SelectedFriend.RegisterationID; message.MessageSenderRegisID = Convert.ToUInt32(sharedPref.GetString("RegistrationId", string.Empty)); message.MessageText = JsonConvert.SerializeObject(cipherMessage.serialize()); message.MessageTimestamp = DateTime.Now; // call SendMessage() to send the SendMessage(message); // *display the messages in user's own screen using adapter (always display in MyMessageListItem). adapter.NotifyDataSetInvalidated(); listView.SetSelection(adapter.Count); }; }
private void RunInteraction(SessionRecord aliceSessionRecord, SessionRecord bobSessionRecord) { ISignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); ISignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); aliceStore.StoreSession(new SignalProtocolAddress("+14159999999", 1), aliceSessionRecord); bobStore.StoreSession(new SignalProtocolAddress("+14158888888", 1), bobSessionRecord); SessionCipher aliceCipher = new SessionCipher(aliceStore, new SignalProtocolAddress("+14159999999", 1)); SessionCipher bobCipher = new SessionCipher(bobStore, new SignalProtocolAddress("+14158888888", 1)); byte[] alicePlaintext = Encoding.UTF8.GetBytes("This is a plaintext message."); CiphertextMessage message = aliceCipher.Encrypt(alicePlaintext); byte[] bobPlaintext = bobCipher.Decrypt(new SignalMessage(message.Serialize())); CollectionAssert.AreEqual(alicePlaintext, bobPlaintext); byte[] bobReply = Encoding.UTF8.GetBytes("This is a message from Bob."); CiphertextMessage reply = bobCipher.Encrypt(bobReply); byte[] receivedReply = aliceCipher.Decrypt(new SignalMessage(reply.Serialize())); CollectionAssert.AreEqual(bobReply, receivedReply); List <CiphertextMessage> aliceCiphertextMessages = new List <CiphertextMessage>(); List <byte[]> alicePlaintextMessages = new List <byte[]>(); for (int i = 0; i < 50; i++) { alicePlaintextMessages.Add(Encoding.UTF8.GetBytes("смерть за смерть " + i)); aliceCiphertextMessages.Add(aliceCipher.Encrypt(Encoding.UTF8.GetBytes("смерть за смерть " + i))); } ulong seed = DateUtil.CurrentTimeMillis(); HelperMethods.Shuffle(aliceCiphertextMessages, new Random((int)seed)); HelperMethods.Shuffle(alicePlaintextMessages, new Random((int)seed)); for (int i = 0; i < aliceCiphertextMessages.Count / 2; i++) { byte[] receivedPlaintext = bobCipher.Decrypt(new SignalMessage(aliceCiphertextMessages[i].Serialize())); Assert.IsTrue(ByteUtil.IsEqual(receivedPlaintext, alicePlaintextMessages[i])); } List <CiphertextMessage> bobCiphertextMessages = new List <CiphertextMessage>(); List <byte[]> bobPlaintextMessages = new List <byte[]>(); for (int i = 0; i < 20; i++) { bobPlaintextMessages.Add(Encoding.UTF8.GetBytes("смерть за смерть " + i)); bobCiphertextMessages.Add(bobCipher.Encrypt(Encoding.UTF8.GetBytes("смерть за смерть " + i))); } seed = DateUtil.CurrentTimeMillis(); HelperMethods.Shuffle(bobCiphertextMessages, new Random((int)seed)); HelperMethods.Shuffle(bobPlaintextMessages, new Random((int)seed)); for (int i = 0; i < bobCiphertextMessages.Count / 2; i++) { byte[] receivedPlaintext = aliceCipher.Decrypt(new SignalMessage(bobCiphertextMessages[i].Serialize())); CollectionAssert.AreEqual(receivedPlaintext, bobPlaintextMessages[i]); } for (int i = aliceCiphertextMessages.Count / 2; i < aliceCiphertextMessages.Count; i++) { byte[] receivedPlaintext = bobCipher.Decrypt(new SignalMessage(aliceCiphertextMessages[i].Serialize())); CollectionAssert.AreEqual(receivedPlaintext, alicePlaintextMessages[i]); } for (int i = bobCiphertextMessages.Count / 2; i < bobCiphertextMessages.Count; i++) { byte[] receivedPlaintext = aliceCipher.Decrypt(new SignalMessage(bobCiphertextMessages[i].Serialize())); CollectionAssert.AreEqual(receivedPlaintext, bobPlaintextMessages[i]); } }