public void testBasicSimultaneousInitiate() { 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())); 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.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 OutgoingPushMessage encrypt(AxolotlAddress destination, byte[] unpaddedMessage, bool legacy) { SessionCipher sessionCipher = new SessionCipher(axolotlStore, 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.PREKEY_BUNDLE; 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); }
private void runInteraction(AxolotlStore aliceStore, AxolotlStore 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 WhisperMessage(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 WhisperMessage(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 WhisperMessage(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 WhisperMessage(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 WhisperMessage(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 WhisperMessage(bobLoopingMessage.serialize())); Assert.AreEqual(loopingMessage, Encoding.UTF8.GetString(loopingPlaintext)); } foreach (Pair<String, CiphertextMessage> aliceOutOfOrderMessage in aliceOutOfOrderMessages) { byte[] outOfOrderPlaintext = bobSessionCipher.decrypt(new WhisperMessage(aliceOutOfOrderMessage.second().serialize())); Assert.AreEqual(aliceOutOfOrderMessage.first(), Encoding.UTF8.GetString(outOfOrderPlaintext)); } }
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); }
public void testOptionalOneTimePreKey() { AxolotlStore aliceStore = new TestInMemoryAxolotlStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS); AxolotlStore bobStore = new TestInMemoryAxolotlStore(); 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); PreKeyWhisperMessage incomingMessage = new PreKeyWhisperMessage(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)); }
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 } }
public void testBadMessageBundle() { AxolotlStore aliceStore = new TestInMemoryAxolotlStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS); AxolotlStore bobStore = new TestInMemoryAxolotlStore(); 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; PreKeyWhisperMessage incomingMessage = new PreKeyWhisperMessage(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 e) { // good. } Assert.IsTrue(bobStore.ContainsPreKey(31337)); plaintext = bobSessionCipher.decrypt(new PreKeyWhisperMessage(goodMessage)); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); Assert.IsFalse(bobStore.ContainsPreKey(31337)); }
public void testRepeatBundleMessageV3() { AxolotlStore aliceStore = new TestInMemoryAxolotlStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS); AxolotlStore bobStore = new TestInMemoryAxolotlStore(); 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()); PreKeyWhisperMessage incomingMessage = new PreKeyWhisperMessage(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 WhisperMessage(bobOutgoingMessage.serialize())); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(alicePlaintext)); // The test PreKeyWhisperMessage incomingMessageTwo = new PreKeyWhisperMessage(outgoingMessageTwo.serialize()); plaintext = bobSessionCipher.decrypt(new PreKeyWhisperMessage(incomingMessageTwo.serialize())); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(plaintext)); bobOutgoingMessage = bobSessionCipher.encrypt(Encoding.UTF8.GetBytes(originalMessage)); alicePlaintext = aliceSessionCipher.decrypt(new WhisperMessage(bobOutgoingMessage.serialize())); Assert.AreEqual(originalMessage, Encoding.UTF8.GetString(alicePlaintext)); }
/// <summary> /// Start a session for communicating with a remote party. /// </summary> /// <param name="remoteAddress">Remote party</param> /// <param name="preKey">Pre Key for that remote party, pulled from the server (or pulled from memory, like in this demo).</param> public void SetupSession(AxolotlAddress remoteAddress, PreKeyBundle preKey) { sessionBuilder = new SessionBuilder(axolotlStore, remoteAddress); sessionBuilder.process(preKey); sessionCipher = new SessionCipher(axolotlStore, remoteAddress); }