public void testSimultaneousKeyExchange() { SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS); SignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); SessionBuilder bobSessionBuilder = new SessionBuilder(bobStore, ALICE_ADDRESS); KeyExchangeMessage aliceKeyExchange = aliceSessionBuilder.process(); KeyExchangeMessage bobKeyExchange = bobSessionBuilder.process(); Assert.IsNotNull(aliceKeyExchange); Assert.IsNotNull(bobKeyExchange); KeyExchangeMessage aliceResponse = aliceSessionBuilder.process(bobKeyExchange); KeyExchangeMessage bobResponse = bobSessionBuilder.process(aliceKeyExchange); Assert.IsNotNull(aliceResponse); Assert.IsNotNull(bobResponse); KeyExchangeMessage aliceAck = aliceSessionBuilder.process(bobResponse); KeyExchangeMessage bobAck = bobSessionBuilder.process(aliceResponse); Assert.IsNull(aliceAck); Assert.IsNull(bobAck); runInteraction(aliceStore, bobStore); }
private KeyExchangeMessage ProcessInitiate(KeyExchangeMessage message) { uint flags = KeyExchangeMessage.RESPONSE_FLAG; SessionRecord sessionRecord = sessionStore.LoadSession(remoteAddress); if (message.GetVersion() >= 3 && !Curve.VerifySignature(message.GetIdentityKey().GetPublicKey(), message.GetBaseKey().Serialize(), message.GetBaseKeySignature())) { throw new InvalidKeyException("Bad signature!"); } SymmetricAxolotlParameters.Builder builder = SymmetricAxolotlParameters.NewBuilder(); if (!sessionRecord.GetSessionState().HasPendingKeyExchange()) { builder.SetOurIdentityKey(identityKeyStore.GetIdentityKeyPair()) .SetOurBaseKey(Curve.GenerateKeyPair()) .SetOurRatchetKey(Curve.GenerateKeyPair()); } else { builder.SetOurIdentityKey(sessionRecord.GetSessionState().GetPendingKeyExchangeIdentityKey()) .SetOurBaseKey(sessionRecord.GetSessionState().GetPendingKeyExchangeBaseKey()) .SetOurRatchetKey(sessionRecord.GetSessionState().GetPendingKeyExchangeRatchetKey()); flags |= KeyExchangeMessage.SIMULTAENOUS_INITIATE_FLAG; } builder.SetTheirBaseKey(message.GetBaseKey()) .SetTheirRatchetKey(message.GetRatchetKey()) .SetTheirIdentityKey(message.GetIdentityKey()); SymmetricAxolotlParameters parameters = builder.Create(); if (!sessionRecord.IsFresh()) { sessionRecord.ArchiveCurrentState(); } RatchetingSession.InitializeSession(sessionRecord.GetSessionState(), Math.Min(message.GetMaxVersion(), CipherTextMessage.CURRENT_VERSION), parameters); sessionStore.StoreSession(remoteAddress, sessionRecord); identityKeyStore.SaveIdentity(remoteAddress.GetName(), message.GetIdentityKey()); byte[] baseKeySignature = Curve.CalculateSignature(parameters.GetOurIdentityKey().GetPrivateKey(), parameters.GetOurBaseKey().GetPublicKey().Serialize()); return(new KeyExchangeMessage(sessionRecord.GetSessionState().GetSessionVersion(), message.GetSequence(), flags, parameters.GetOurBaseKey().GetPublicKey(), baseKeySignature, parameters.GetOurRatchetKey().GetPublicKey(), parameters.GetOurIdentityKey().GetPublicKey())); }
private KeyExchangeMessage processInitiate(KeyExchangeMessage message) { uint flags = KeyExchangeMessage.RESPONSE_FLAG; SessionRecord sessionRecord = sessionStore.LoadSession(remoteAddress); if (!Curve.verifySignature(message.getIdentityKey().getPublicKey(), message.getBaseKey().serialize(), message.getBaseKeySignature())) { throw new InvalidKeyException("Bad signature!"); } SymmetricSignalProtocolParameters.Builder builder = SymmetricSignalProtocolParameters.newBuilder(); if (!sessionRecord.getSessionState().hasPendingKeyExchange()) { builder.setOurIdentityKey(identityKeyStore.GetIdentityKeyPair()) .setOurBaseKey(Curve.generateKeyPair()) .setOurRatchetKey(Curve.generateKeyPair()); } else { builder.setOurIdentityKey(sessionRecord.getSessionState().getPendingKeyExchangeIdentityKey()) .setOurBaseKey(sessionRecord.getSessionState().getPendingKeyExchangeBaseKey()) .setOurRatchetKey(sessionRecord.getSessionState().getPendingKeyExchangeRatchetKey()); flags |= KeyExchangeMessage.SIMULTAENOUS_INITIATE_FLAG; } builder.setTheirBaseKey(message.getBaseKey()) .setTheirRatchetKey(message.getRatchetKey()) .setTheirIdentityKey(message.getIdentityKey()); SymmetricSignalProtocolParameters parameters = builder.create(); if (!sessionRecord.isFresh()) { sessionRecord.archiveCurrentState(); } RatchetingSession.initializeSession(sessionRecord.getSessionState(), parameters); sessionStore.StoreSession(remoteAddress, sessionRecord); identityKeyStore.SaveIdentity(remoteAddress.getName(), message.getIdentityKey()); byte[] baseKeySignature = Curve.calculateSignature(parameters.getOurIdentityKey().getPrivateKey(), parameters.getOurBaseKey().getPublicKey().serialize()); return(new KeyExchangeMessage(sessionRecord.getSessionState().getSessionVersion(), message.getSequence(), flags, parameters.getOurBaseKey().getPublicKey(), baseKeySignature, parameters.getOurRatchetKey().getPublicKey(), parameters.getOurIdentityKey().getPublicKey())); }
private void ProcessResponse(KeyExchangeMessage message) { SessionRecord sessionRecord = sessionStore.LoadSession(remoteAddress); SessionState sessionState = sessionRecord.GetSessionState(); bool hasPendingKeyExchange = sessionState.HasPendingKeyExchange(); bool isSimultaneousInitiateResponse = message.IsResponseForSimultaneousInitiate(); if (!hasPendingKeyExchange || sessionState.GetPendingKeyExchangeSequence() != message.GetSequence()) { //Log.w(TAG, "No matching sequence for response. Is simultaneous initiate response: " + isSimultaneousInitiateResponse); if (!isSimultaneousInitiateResponse) { throw new StaleKeyExchangeException(); } else { return; } } SymmetricAxolotlParameters.Builder parameters = SymmetricAxolotlParameters.NewBuilder(); parameters.SetOurBaseKey(sessionRecord.GetSessionState().GetPendingKeyExchangeBaseKey()) .SetOurRatchetKey(sessionRecord.GetSessionState().GetPendingKeyExchangeRatchetKey()) .SetOurIdentityKey(sessionRecord.GetSessionState().GetPendingKeyExchangeIdentityKey()) .SetTheirBaseKey(message.GetBaseKey()) .SetTheirRatchetKey(message.GetRatchetKey()) .SetTheirIdentityKey(message.GetIdentityKey()); if (!sessionRecord.IsFresh()) { sessionRecord.ArchiveCurrentState(); } RatchetingSession.InitializeSession(sessionRecord.GetSessionState(), Math.Min(message.GetMaxVersion(), CipherTextMessage.CURRENT_VERSION), parameters.Create()); if (sessionRecord.GetSessionState().GetSessionVersion() >= 3 && !Curve.VerifySignature(message.GetIdentityKey().GetPublicKey(), message.GetBaseKey().Serialize(), message.GetBaseKeySignature())) { throw new InvalidKeyException("Base key signature doesn't match!"); } sessionStore.StoreSession(remoteAddress, sessionRecord); identityKeyStore.SaveIdentity(remoteAddress.GetName(), message.GetIdentityKey()); }
private void processResponse(KeyExchangeMessage message) { SessionRecord sessionRecord = sessionStore.LoadSession(remoteAddress); SessionState sessionState = sessionRecord.getSessionState(); bool hasPendingKeyExchange = sessionState.hasPendingKeyExchange(); bool isSimultaneousInitiateResponse = message.isResponseForSimultaneousInitiate(); if (!hasPendingKeyExchange || sessionState.getPendingKeyExchangeSequence() != message.getSequence()) { Debug.WriteLine("No matching sequence for response. Is simultaneous initiate response: " + isSimultaneousInitiateResponse); if (!isSimultaneousInitiateResponse) { throw new StaleKeyExchangeException(); } else { return; } } SymmetricSignalProtocolParameters.Builder parameters = SymmetricSignalProtocolParameters.newBuilder(); parameters.setOurBaseKey(sessionRecord.getSessionState().getPendingKeyExchangeBaseKey()) .setOurRatchetKey(sessionRecord.getSessionState().getPendingKeyExchangeRatchetKey()) .setOurIdentityKey(sessionRecord.getSessionState().getPendingKeyExchangeIdentityKey()) .setTheirBaseKey(message.getBaseKey()) .setTheirRatchetKey(message.getRatchetKey()) .setTheirIdentityKey(message.getIdentityKey()); if (!sessionRecord.isFresh()) { sessionRecord.archiveCurrentState(); } RatchetingSession.initializeSession(sessionRecord.getSessionState(), parameters.create()); if (!Curve.verifySignature(message.getIdentityKey().getPublicKey(), message.getBaseKey().serialize(), message.getBaseKeySignature())) { throw new InvalidKeyException("Base key signature doesn't match!"); } sessionStore.StoreSession(remoteAddress, sessionRecord); identityKeyStore.SaveIdentity(remoteAddress.getName(), message.getIdentityKey()); }
public void testBasicKeyExchange() { SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS); SignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); SessionBuilder bobSessionBuilder = new SessionBuilder(bobStore, ALICE_ADDRESS); KeyExchangeMessage aliceKeyExchangeMessage = aliceSessionBuilder.process(); Assert.IsNotNull(aliceKeyExchangeMessage); byte[] aliceKeyExchangeMessageBytes = aliceKeyExchangeMessage.serialize(); KeyExchangeMessage bobKeyExchangeMessage = bobSessionBuilder.process(new KeyExchangeMessage(aliceKeyExchangeMessageBytes)); Assert.IsNotNull(bobKeyExchangeMessage); byte[] bobKeyExchangeMessageBytes = bobKeyExchangeMessage.serialize(); KeyExchangeMessage response = aliceSessionBuilder.process(new KeyExchangeMessage(bobKeyExchangeMessageBytes)); Assert.IsNull(response); Assert.IsTrue(aliceStore.ContainsSession(BOB_ADDRESS)); Assert.IsTrue(bobStore.ContainsSession(ALICE_ADDRESS)); runInteraction(aliceStore, bobStore); aliceStore = new TestInMemorySignalProtocolStore(); aliceSessionBuilder = new SessionBuilder(aliceStore, BOB_ADDRESS); aliceKeyExchangeMessage = aliceSessionBuilder.process(); try { bobKeyExchangeMessage = bobSessionBuilder.process(aliceKeyExchangeMessage); throw new Exception("This identity shouldn't be trusted!"); } catch (UntrustedIdentityException) { bobStore.SaveIdentity(ALICE_ADDRESS.getName(), aliceKeyExchangeMessage.getIdentityKey()); bobKeyExchangeMessage = bobSessionBuilder.process(aliceKeyExchangeMessage); } Assert.IsNull(aliceSessionBuilder.process(bobKeyExchangeMessage)); runInteraction(aliceStore, bobStore); }
/** * Build a new session from a {@link KeyExchangeMessage} * received from a remote client. * * @param message The received KeyExchangeMessage. * @return The KeyExchangeMessage to respond with, or null if no response is necessary. * @throws InvalidKeyException if the received KeyExchangeMessage is badly formatted. */ public KeyExchangeMessage process(KeyExchangeMessage message) { lock (SessionCipher.SESSION_LOCK) { if (!identityKeyStore.IsTrustedIdentity(remoteAddress.getName(), message.getIdentityKey())) { throw new UntrustedIdentityException(remoteAddress.getName(), message.getIdentityKey()); } KeyExchangeMessage responseMessage = null; if (message.isInitiate()) { responseMessage = processInitiate(message); } else { processResponse(message); } return(responseMessage); } }