Collapse RatchetingSessionV2 and RatchetingSessionV3.

pull/1/head
Moxie Marlinspike 10 years ago
parent 811479d168
commit d6c5e92c9d

@ -12,8 +12,7 @@ import org.whispersystems.libaxolotl.SessionCipher;
import org.whispersystems.libaxolotl.ecc.Curve; import org.whispersystems.libaxolotl.ecc.Curve;
import org.whispersystems.libaxolotl.ecc.ECKeyPair; import org.whispersystems.libaxolotl.ecc.ECKeyPair;
import org.whispersystems.libaxolotl.protocol.CiphertextMessage; import org.whispersystems.libaxolotl.protocol.CiphertextMessage;
import org.whispersystems.libaxolotl.ratchet.RatchetingSessionV2; import org.whispersystems.libaxolotl.ratchet.RatchetingSession;
import org.whispersystems.libaxolotl.ratchet.RatchetingSessionV3;
import org.whispersystems.libaxolotl.state.SessionRecord; import org.whispersystems.libaxolotl.state.SessionRecord;
import org.whispersystems.libaxolotl.state.SessionState; import org.whispersystems.libaxolotl.state.SessionState;
import org.whispersystems.libaxolotl.state.SessionStore; import org.whispersystems.libaxolotl.state.SessionStore;
@ -136,13 +135,15 @@ public class SessionCipherTest extends AndroidTestCase {
ECKeyPair bobEphemeralKey = bobBaseKey; ECKeyPair bobEphemeralKey = bobBaseKey;
RatchetingSessionV2.initializeSession(aliceSessionState, aliceBaseKey, bobBaseKey.getPublicKey(), RatchetingSession.initializeSession(aliceSessionState, 2, aliceBaseKey, bobBaseKey.getPublicKey(),
aliceEphemeralKey, bobEphemeralKey.getPublicKey(), aliceEphemeralKey, bobEphemeralKey.getPublicKey(),
aliceIdentityKey, bobIdentityKey.getPublicKey()); null, null,
aliceIdentityKey, bobIdentityKey.getPublicKey());
RatchetingSessionV2.initializeSession(bobSessionState, bobBaseKey, aliceBaseKey.getPublicKey(), RatchetingSession.initializeSession(bobSessionState, 2, bobBaseKey, aliceBaseKey.getPublicKey(),
bobEphemeralKey, aliceEphemeralKey.getPublicKey(), bobEphemeralKey, aliceEphemeralKey.getPublicKey(),
bobIdentityKey, aliceIdentityKey.getPublicKey()); null, null,
bobIdentityKey, aliceIdentityKey.getPublicKey());
} }
private void initializeSessionsV3(SessionState aliceSessionState, SessionState bobSessionState) private void initializeSessionsV3(SessionState aliceSessionState, SessionState bobSessionState)
@ -165,15 +166,15 @@ public class SessionCipherTest extends AndroidTestCase {
ECKeyPair bobPreKey = Curve.generateKeyPair(true); ECKeyPair bobPreKey = Curve.generateKeyPair(true);
RatchetingSessionV3.initializeSession(aliceSessionState, aliceBaseKey, bobBaseKey.getPublicKey(), RatchetingSession.initializeSession(aliceSessionState, 3, aliceBaseKey, bobBaseKey.getPublicKey(),
aliceEphemeralKey, bobEphemeralKey.getPublicKey(), aliceEphemeralKey, bobEphemeralKey.getPublicKey(),
alicePreKey, bobPreKey.getPublicKey(), alicePreKey, bobPreKey.getPublicKey(),
aliceIdentityKey, bobIdentityKey.getPublicKey()); aliceIdentityKey, bobIdentityKey.getPublicKey());
RatchetingSessionV3.initializeSession(bobSessionState, bobBaseKey, aliceBaseKey.getPublicKey(), RatchetingSession.initializeSession(bobSessionState, 3, bobBaseKey, aliceBaseKey.getPublicKey(),
bobEphemeralKey, aliceEphemeralKey.getPublicKey(), bobEphemeralKey, aliceEphemeralKey.getPublicKey(),
bobPreKey, alicePreKey.getPublicKey(), bobPreKey, alicePreKey.getPublicKey(),
bobIdentityKey, aliceIdentityKey.getPublicKey()); bobIdentityKey, aliceIdentityKey.getPublicKey());
} }
} }

@ -5,16 +5,16 @@ import android.test.AndroidTestCase;
import org.whispersystems.libaxolotl.IdentityKey; import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.IdentityKeyPair; import org.whispersystems.libaxolotl.IdentityKeyPair;
import org.whispersystems.libaxolotl.InvalidKeyException; import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.state.SessionState;
import org.whispersystems.libaxolotl.ecc.Curve; import org.whispersystems.libaxolotl.ecc.Curve;
import org.whispersystems.libaxolotl.ecc.ECKeyPair; import org.whispersystems.libaxolotl.ecc.ECKeyPair;
import org.whispersystems.libaxolotl.ecc.ECPrivateKey; import org.whispersystems.libaxolotl.ecc.ECPrivateKey;
import org.whispersystems.libaxolotl.ecc.ECPublicKey; import org.whispersystems.libaxolotl.ecc.ECPublicKey;
import org.whispersystems.libaxolotl.ratchet.RatchetingSessionV2; import org.whispersystems.libaxolotl.ratchet.RatchetingSession;
import org.whispersystems.libaxolotl.state.SessionState;
import java.util.Arrays; import java.util.Arrays;
public class RatchetingSessionV2Test extends AndroidTestCase { public class RatchetingSessionTest extends AndroidTestCase {
public void testRatchetingSessionAsBob() throws InvalidKeyException { public void testRatchetingSessionAsBob() throws InvalidKeyException {
byte[] bobPublic = {(byte) 0x05, (byte) 0x2c, (byte) 0xb4, (byte) 0x97, byte[] bobPublic = {(byte) 0x05, (byte) 0x2c, (byte) 0xb4, (byte) 0x97,
@ -107,9 +107,10 @@ public class RatchetingSessionV2Test extends AndroidTestCase {
SessionState session = new SessionState(); SessionState session = new SessionState();
RatchetingSessionV2.initializeSession(session, bobBaseKey, aliceBasePublicKey, RatchetingSession.initializeSession(session, 2, bobBaseKey, aliceBasePublicKey,
bobEphemeralKey, aliceEphemeralPublicKey, bobEphemeralKey, aliceEphemeralPublicKey,
bobIdentityKey, aliceIdentityPublicKey); null, null,
bobIdentityKey, aliceIdentityPublicKey);
assertTrue(session.getLocalIdentityKey().equals(bobIdentityKey.getPublicKey())); assertTrue(session.getLocalIdentityKey().equals(bobIdentityKey.getPublicKey()));
assertTrue(session.getRemoteIdentityKey().equals(aliceIdentityPublicKey)); assertTrue(session.getRemoteIdentityKey().equals(aliceIdentityPublicKey));
@ -204,9 +205,10 @@ public class RatchetingSessionV2Test extends AndroidTestCase {
SessionState session = new SessionState(); SessionState session = new SessionState();
RatchetingSessionV2.initializeSession(session, aliceBaseKey, bobBasePublicKey, RatchetingSession.initializeSession(session, 2, aliceBaseKey, bobBasePublicKey,
aliceEphemeralKey, bobEphemeralPublicKey, aliceEphemeralKey, bobEphemeralPublicKey,
aliceIdentityKey, bobIdentityKey); null, null,
aliceIdentityKey, bobIdentityKey);
assertTrue(session.getLocalIdentityKey().equals(aliceIdentityKey.getPublicKey())); assertTrue(session.getLocalIdentityKey().equals(aliceIdentityKey.getPublicKey()));
assertTrue(session.getRemoteIdentityKey().equals(bobIdentityKey)); assertTrue(session.getRemoteIdentityKey().equals(bobIdentityKey));

@ -7,8 +7,7 @@ import org.whispersystems.libaxolotl.ecc.ECKeyPair;
import org.whispersystems.libaxolotl.ecc.ECPublicKey; import org.whispersystems.libaxolotl.ecc.ECPublicKey;
import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage; import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage;
import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage; import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage;
import org.whispersystems.libaxolotl.ratchet.RatchetingSessionV2; import org.whispersystems.libaxolotl.ratchet.RatchetingSession;
import org.whispersystems.libaxolotl.ratchet.RatchetingSessionV3;
import org.whispersystems.libaxolotl.state.DeviceKeyRecord; import org.whispersystems.libaxolotl.state.DeviceKeyRecord;
import org.whispersystems.libaxolotl.state.DeviceKeyStore; import org.whispersystems.libaxolotl.state.DeviceKeyStore;
import org.whispersystems.libaxolotl.state.IdentityKeyStore; import org.whispersystems.libaxolotl.state.IdentityKeyStore;
@ -21,8 +20,6 @@ import org.whispersystems.libaxolotl.state.SessionStore;
import org.whispersystems.libaxolotl.util.KeyHelper; import org.whispersystems.libaxolotl.util.KeyHelper;
import org.whispersystems.libaxolotl.util.Medium; import org.whispersystems.libaxolotl.util.Medium;
import java.util.Arrays;
/** /**
* SessionBuilder is responsible for setting up encrypted sessions. * SessionBuilder is responsible for setting up encrypted sessions.
* Once a session has been established, {@link org.whispersystems.libaxolotl.SessionCipher} * Once a session has been established, {@link org.whispersystems.libaxolotl.SessionCipher}
@ -138,11 +135,12 @@ public class SessionBuilder {
if (!simultaneousInitiate) sessionRecord.reset(); if (!simultaneousInitiate) sessionRecord.reset();
else sessionRecord.archiveCurrentState(); else sessionRecord.archiveCurrentState();
RatchetingSessionV3.initializeSession(sessionRecord.getSessionState(), RatchetingSession.initializeSession(sessionRecord.getSessionState(),
ourBaseKey, theirBaseKey, message.getMessageVersion(),
ourEphemeralKey, theirEphemeralKey, ourBaseKey, theirBaseKey,
ourPreKey, theirPreKey, ourEphemeralKey, theirEphemeralKey,
ourIdentityKey, theirIdentityKey); ourPreKey, theirPreKey,
ourIdentityKey, theirIdentityKey);
sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId()); sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId());
sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId()); sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId());
@ -186,10 +184,12 @@ public class SessionBuilder {
if (!simultaneousInitiate) sessionRecord.reset(); if (!simultaneousInitiate) sessionRecord.reset();
else sessionRecord.archiveCurrentState(); else sessionRecord.archiveCurrentState();
RatchetingSessionV2.initializeSession(sessionRecord.getSessionState(), RatchetingSession.initializeSession(sessionRecord.getSessionState(),
ourBaseKey, theirBaseKey, message.getMessageVersion(),
ourEphemeralKey, theirEphemeralKey, ourBaseKey, theirBaseKey,
ourIdentityKey, theirIdentityKey); ourEphemeralKey, theirEphemeralKey,
null, null,
ourIdentityKey, theirIdentityKey);
sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId()); sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId());
sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId()); sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId());
@ -242,15 +242,11 @@ public class SessionBuilder {
if (sessionRecord.getSessionState().getNeedsRefresh()) sessionRecord.archiveCurrentState(); if (sessionRecord.getSessionState().getNeedsRefresh()) sessionRecord.archiveCurrentState();
else sessionRecord.reset(); else sessionRecord.reset();
if (preKey.getDeviceKey() == null) { RatchetingSession.initializeSession(sessionRecord.getSessionState(),
RatchetingSessionV2.initializeSession(sessionRecord.getSessionState(), preKey.getDeviceKey() == null ? 2 : 3,
ourBaseKey, theirBaseKey, ourEphemeralKey, ourBaseKey, theirBaseKey, ourEphemeralKey,
theirEphemeralKey, ourIdentityKey, theirIdentityKey); theirEphemeralKey, ourPreKey, theirPreKey,
} else { ourIdentityKey, theirIdentityKey);
RatchetingSessionV3.initializeSession(sessionRecord.getSessionState(),
ourBaseKey, theirBaseKey, ourEphemeralKey, theirEphemeralKey,
ourPreKey, theirPreKey, ourIdentityKey, theirIdentityKey);
}
sessionRecord.getSessionState().setPendingPreKey(preKey.getPreKeyId(), preKey.getDeviceKeyId(), ourBaseKey.getPublicKey()); sessionRecord.getSessionState().setPendingPreKey(preKey.getPreKeyId(), preKey.getDeviceKeyId(), ourBaseKey.getPublicKey());
sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId()); sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId());
@ -310,10 +306,12 @@ public class SessionBuilder {
sessionRecord.reset(); sessionRecord.reset();
RatchetingSessionV2.initializeSession(sessionRecord.getSessionState(), RatchetingSession.initializeSession(sessionRecord.getSessionState(),
ourBaseKey, message.getBaseKey(), 2,
ourEphemeralKey, message.getEphemeralKey(), ourBaseKey, message.getBaseKey(),
ourIdentityKey, message.getIdentityKey()); ourEphemeralKey, message.getEphemeralKey(),
null, null,
ourIdentityKey, message.getIdentityKey());
sessionRecord.getSessionState().setSessionVersion(message.getVersion()); sessionRecord.getSessionState().setSessionVersion(message.getVersion());
sessionStore.storeSession(recipientId, deviceId, sessionRecord); sessionStore.storeSession(recipientId, deviceId, sessionRecord);

@ -19,21 +19,22 @@ package org.whispersystems.libaxolotl.ratchet;
import org.whispersystems.libaxolotl.IdentityKey; import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.IdentityKeyPair; import org.whispersystems.libaxolotl.IdentityKeyPair;
import org.whispersystems.libaxolotl.InvalidKeyException; import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.state.SessionState;
import org.whispersystems.libaxolotl.ecc.Curve; import org.whispersystems.libaxolotl.ecc.Curve;
import org.whispersystems.libaxolotl.ecc.ECKeyPair; import org.whispersystems.libaxolotl.ecc.ECKeyPair;
import org.whispersystems.libaxolotl.ecc.ECPublicKey; import org.whispersystems.libaxolotl.ecc.ECPublicKey;
import org.whispersystems.libaxolotl.kdf.DerivedSecrets; import org.whispersystems.libaxolotl.kdf.DerivedSecrets;
import org.whispersystems.libaxolotl.kdf.HKDF; import org.whispersystems.libaxolotl.kdf.HKDF;
import org.whispersystems.libaxolotl.state.SessionState;
import org.whispersystems.libaxolotl.util.Pair; import org.whispersystems.libaxolotl.util.Pair;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
public class RatchetingSessionV3 { public class RatchetingSession {
public static void initializeSession(SessionState sessionState, public static void initializeSession(SessionState sessionState,
int sessionVersion,
ECKeyPair ourBaseKey, ECKeyPair ourBaseKey,
ECPublicKey theirBaseKey, ECPublicKey theirBaseKey,
ECKeyPair ourEphemeralKey, ECKeyPair ourEphemeralKey,
@ -45,17 +46,18 @@ public class RatchetingSessionV3 {
throws InvalidKeyException throws InvalidKeyException
{ {
if (isAlice(ourBaseKey.getPublicKey(), theirBaseKey, ourEphemeralKey.getPublicKey(), theirEphemeralKey)) { if (isAlice(ourBaseKey.getPublicKey(), theirBaseKey, ourEphemeralKey.getPublicKey(), theirEphemeralKey)) {
initializeSessionAsAlice(sessionState, ourBaseKey, theirBaseKey, theirEphemeralKey, initializeSessionAsAlice(sessionState, sessionVersion, ourBaseKey, theirBaseKey, theirEphemeralKey,
ourPreKey, theirPreKey, ourIdentityKey, theirIdentityKey); ourPreKey, theirPreKey, ourIdentityKey, theirIdentityKey);
} else { } else {
initializeSessionAsBob(sessionState, ourBaseKey, theirBaseKey, ourEphemeralKey, initializeSessionAsBob(sessionState, sessionVersion, ourBaseKey, theirBaseKey, ourEphemeralKey,
ourPreKey, theirPreKey, ourIdentityKey, theirIdentityKey); ourPreKey, theirPreKey, ourIdentityKey, theirIdentityKey);
} }
sessionState.setSessionVersion(3); sessionState.setSessionVersion(sessionVersion);
} }
private static void initializeSessionAsAlice(SessionState sessionState, private static void initializeSessionAsAlice(SessionState sessionState,
int sessionVersion,
ECKeyPair ourBaseKey, ECPublicKey theirBaseKey, ECKeyPair ourBaseKey, ECPublicKey theirBaseKey,
ECPublicKey theirEphemeralKey, ECPublicKey theirEphemeralKey,
ECKeyPair ourPreKey, ECPublicKey theirPreKey, ECKeyPair ourPreKey, ECPublicKey theirPreKey,
@ -67,7 +69,8 @@ public class RatchetingSessionV3 {
sessionState.setLocalIdentityKey(ourIdentityKey.getPublicKey()); sessionState.setLocalIdentityKey(ourIdentityKey.getPublicKey());
ECKeyPair sendingKey = Curve.generateKeyPair(true); ECKeyPair sendingKey = Curve.generateKeyPair(true);
Pair<RootKey, ChainKey> receivingChain = calculate4DHE(true, ourBaseKey, theirBaseKey, Pair<RootKey, ChainKey> receivingChain = calculate4DHE(true, sessionVersion,
ourBaseKey, theirBaseKey,
ourPreKey, theirPreKey, ourPreKey, theirPreKey,
ourIdentityKey, theirIdentityKey); ourIdentityKey, theirIdentityKey);
Pair<RootKey, ChainKey> sendingChain = receivingChain.first().createChain(theirEphemeralKey, sendingKey); Pair<RootKey, ChainKey> sendingChain = receivingChain.first().createChain(theirEphemeralKey, sendingKey);
@ -78,6 +81,7 @@ public class RatchetingSessionV3 {
} }
private static void initializeSessionAsBob(SessionState sessionState, private static void initializeSessionAsBob(SessionState sessionState,
int sessionVersion,
ECKeyPair ourBaseKey, ECPublicKey theirBaseKey, ECKeyPair ourBaseKey, ECPublicKey theirBaseKey,
ECKeyPair ourEphemeralKey, ECKeyPair ourEphemeralKey,
ECKeyPair ourPreKey, ECPublicKey theirPreKey, ECKeyPair ourPreKey, ECPublicKey theirPreKey,
@ -88,7 +92,8 @@ public class RatchetingSessionV3 {
sessionState.setRemoteIdentityKey(theirIdentityKey); sessionState.setRemoteIdentityKey(theirIdentityKey);
sessionState.setLocalIdentityKey(ourIdentityKey.getPublicKey()); sessionState.setLocalIdentityKey(ourIdentityKey.getPublicKey());
Pair<RootKey, ChainKey> sendingChain = calculate4DHE(false, ourBaseKey, theirBaseKey, Pair<RootKey, ChainKey> sendingChain = calculate4DHE(false, sessionVersion,
ourBaseKey, theirBaseKey,
ourPreKey, theirPreKey, ourPreKey, theirPreKey,
ourIdentityKey, theirIdentityKey); ourIdentityKey, theirIdentityKey);
@ -96,18 +101,20 @@ public class RatchetingSessionV3 {
sessionState.setRootKey(sendingChain.first()); sessionState.setRootKey(sendingChain.first());
} }
private static Pair<RootKey, ChainKey> calculate4DHE(boolean isAlice, private static Pair<RootKey, ChainKey> calculate4DHE(boolean isAlice, int sessionVersion,
ECKeyPair ourEphemeral, ECPublicKey theirEphemeral, ECKeyPair ourEphemeral, ECPublicKey theirEphemeral,
ECKeyPair ourPreKey, ECPublicKey theirPreKey, ECKeyPair ourPreKey, ECPublicKey theirPreKey,
IdentityKeyPair ourIdentity, IdentityKey theirIdentity) IdentityKeyPair ourIdentity, IdentityKey theirIdentity)
throws InvalidKeyException throws InvalidKeyException
{ {
try { try {
byte[] discontinuity = new byte[32]; byte[] discontinuity = new byte[32];
ByteArrayOutputStream secrets = new ByteArrayOutputStream(); ByteArrayOutputStream secrets = new ByteArrayOutputStream();
Arrays.fill(discontinuity, (byte)0xFF); if (sessionVersion >= 3) {
secrets.write(discontinuity); Arrays.fill(discontinuity, (byte) 0xFF);
secrets.write(discontinuity);
}
if (isAlice) { if (isAlice) {
secrets.write(Curve.calculateAgreement(theirEphemeral, ourIdentity.getPrivateKey())); secrets.write(Curve.calculateAgreement(theirEphemeral, ourIdentity.getPrivateKey()));
@ -119,7 +126,7 @@ public class RatchetingSessionV3 {
secrets.write(Curve.calculateAgreement(theirEphemeral, ourEphemeral.getPrivateKey())); secrets.write(Curve.calculateAgreement(theirEphemeral, ourEphemeral.getPrivateKey()));
if (ourPreKey != null && theirPreKey != null) { if (sessionVersion >= 3 && ourPreKey != null && theirPreKey != null) {
secrets.write(Curve.calculateAgreement(theirPreKey, ourPreKey.getPrivateKey())); secrets.write(Curve.calculateAgreement(theirPreKey, ourPreKey.getPrivateKey()));
} }

@ -1,138 +0,0 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.libaxolotl.ratchet;
import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.IdentityKeyPair;
import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.state.SessionState;
import org.whispersystems.libaxolotl.ecc.Curve;
import org.whispersystems.libaxolotl.ecc.ECKeyPair;
import org.whispersystems.libaxolotl.ecc.ECPublicKey;
import org.whispersystems.libaxolotl.kdf.DerivedSecrets;
import org.whispersystems.libaxolotl.kdf.HKDF;
import org.whispersystems.libaxolotl.util.Pair;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class RatchetingSessionV2 {
public static void initializeSession(SessionState sessionState,
ECKeyPair ourBaseKey,
ECPublicKey theirBaseKey,
ECKeyPair ourEphemeralKey,
ECPublicKey theirEphemeralKey,
IdentityKeyPair ourIdentityKey,
IdentityKey theirIdentityKey)
throws InvalidKeyException
{
if (isAlice(ourBaseKey.getPublicKey(), theirBaseKey, ourEphemeralKey.getPublicKey(), theirEphemeralKey)) {
initializeSessionAsAlice(sessionState, ourBaseKey, theirBaseKey, theirEphemeralKey,
ourIdentityKey, theirIdentityKey);
} else {
initializeSessionAsBob(sessionState, ourBaseKey, theirBaseKey,
ourEphemeralKey, ourIdentityKey, theirIdentityKey);
}
sessionState.setSessionVersion(2);
}
private static void initializeSessionAsAlice(SessionState sessionState,
ECKeyPair ourBaseKey, ECPublicKey theirBaseKey,
ECPublicKey theirEphemeralKey,
IdentityKeyPair ourIdentityKey,
IdentityKey theirIdentityKey)
throws InvalidKeyException
{
sessionState.setRemoteIdentityKey(theirIdentityKey);
sessionState.setLocalIdentityKey(ourIdentityKey.getPublicKey());
ECKeyPair sendingKey = Curve.generateKeyPair(true);
Pair<RootKey, ChainKey> receivingChain = calculate3DHE(true, ourBaseKey, theirBaseKey, ourIdentityKey, theirIdentityKey);
Pair<RootKey, ChainKey> sendingChain = receivingChain.first().createChain(theirEphemeralKey, sendingKey);
sessionState.addReceiverChain(theirEphemeralKey, receivingChain.second());
sessionState.setSenderChain(sendingKey, sendingChain.second());
sessionState.setRootKey(sendingChain.first());
}
private static void initializeSessionAsBob(SessionState sessionState,
ECKeyPair ourBaseKey, ECPublicKey theirBaseKey,
ECKeyPair ourEphemeralKey,
IdentityKeyPair ourIdentityKey,
IdentityKey theirIdentityKey)
throws InvalidKeyException
{
sessionState.setRemoteIdentityKey(theirIdentityKey);
sessionState.setLocalIdentityKey(ourIdentityKey.getPublicKey());
Pair<RootKey, ChainKey> sendingChain = calculate3DHE(false, ourBaseKey, theirBaseKey,
ourIdentityKey, theirIdentityKey);
sessionState.setSenderChain(ourEphemeralKey, sendingChain.second());
sessionState.setRootKey(sendingChain.first());
}
private static Pair<RootKey, ChainKey> calculate3DHE(boolean isAlice,
ECKeyPair ourEphemeral, ECPublicKey theirEphemeral,
IdentityKeyPair ourIdentity, IdentityKey theirIdentity)
throws InvalidKeyException
{
try {
ByteArrayOutputStream secrets = new ByteArrayOutputStream();
if (isAlice) {
secrets.write(Curve.calculateAgreement(theirEphemeral, ourIdentity.getPrivateKey()));
secrets.write(Curve.calculateAgreement(theirIdentity.getPublicKey(), ourEphemeral.getPrivateKey()));
} else {
secrets.write(Curve.calculateAgreement(theirIdentity.getPublicKey(), ourEphemeral.getPrivateKey()));
secrets.write(Curve.calculateAgreement(theirEphemeral, ourIdentity.getPrivateKey()));
}
secrets.write(Curve.calculateAgreement(theirEphemeral, ourEphemeral.getPrivateKey()));
DerivedSecrets derivedSecrets = new HKDF().deriveSecrets(secrets.toByteArray(),
"WhisperText".getBytes());
return new Pair<RootKey, ChainKey>(new RootKey(derivedSecrets.getCipherKey().getEncoded()),
new ChainKey(derivedSecrets.getMacKey().getEncoded(), 0));
} catch (IOException e) {
throw new AssertionError(e);
}
}
private static boolean isAlice(ECPublicKey ourBaseKey, ECPublicKey theirBaseKey,
ECPublicKey ourEphemeralKey, ECPublicKey theirEphemeralKey)
{
if (ourEphemeralKey.equals(ourBaseKey)) {
return false;
}
if (theirEphemeralKey.equals(theirBaseKey)) {
return true;
}
return isLowEnd(ourBaseKey, theirBaseKey);
}
private static boolean isLowEnd(ECPublicKey ourKey, ECPublicKey theirKey) {
return ourKey.compareTo(theirKey) < 0;
}
}
Loading…
Cancel
Save