Better thread safety for session building <-> use.

This commit is contained in:
Moxie Marlinspike 2014-08-12 13:56:16 -07:00
parent 7b1a37bd91
commit 3e287f930d
2 changed files with 72 additions and 68 deletions

View File

@ -205,52 +205,53 @@ public class SessionBuilder {
* trusted. * trusted.
*/ */
public void process(PreKeyBundle preKey) throws InvalidKeyException, UntrustedIdentityException { public void process(PreKeyBundle preKey) throws InvalidKeyException, UntrustedIdentityException {
synchronized (SessionCipher.SESSION_LOCK) {
if (!identityKeyStore.isTrustedIdentity(recipientId, preKey.getIdentityKey())) {
throw new UntrustedIdentityException();
}
if (!identityKeyStore.isTrustedIdentity(recipientId, preKey.getIdentityKey())) { if (preKey.getSignedPreKey() != null &&
throw new UntrustedIdentityException(); !Curve.verifySignature(preKey.getIdentityKey().getPublicKey(),
preKey.getSignedPreKey().serialize(),
preKey.getSignedPreKeySignature()))
{
throw new InvalidKeyException("Invalid signature on device key!");
}
if (preKey.getSignedPreKey() == null && preKey.getPreKey() == null) {
throw new InvalidKeyException("Both signed and unsigned prekeys are absent!");
}
SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId);
ECKeyPair ourBaseKey = Curve.generateKeyPair();
ECPublicKey theirSignedPreKey = preKey.getSignedPreKey() != null ? preKey.getSignedPreKey() :
preKey.getPreKey();
AliceAxolotlParameters.Builder parameters = AliceAxolotlParameters.newBuilder();
parameters.setOurBaseKey(ourBaseKey)
.setOurIdentityKey(identityKeyStore.getIdentityKeyPair())
.setTheirIdentityKey(preKey.getIdentityKey())
.setTheirSignedPreKey(theirSignedPreKey)
.setTheirRatchetKey(theirSignedPreKey)
.setTheirOneTimePreKey(preKey.getSignedPreKey() != null ?
Optional.fromNullable(preKey.getPreKey()) :
Optional.<ECPublicKey>absent());
if (sessionRecord.getSessionState().getNeedsRefresh()) sessionRecord.archiveCurrentState();
else sessionRecord.reset();
RatchetingSession.initializeSession(sessionRecord.getSessionState(),
preKey.getSignedPreKey() == null ? 2 : 3,
parameters.create());
sessionRecord.getSessionState().setUnacknowledgedPreKeyMessage(preKey.getPreKeyId(), preKey.getSignedPreKeyId(), ourBaseKey.getPublicKey());
sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId());
sessionRecord.getSessionState().setRemoteRegistrationId(preKey.getRegistrationId());
sessionStore.storeSession(recipientId, deviceId, sessionRecord);
identityKeyStore.saveIdentity(recipientId, preKey.getIdentityKey());
} }
if (preKey.getSignedPreKey() != null &&
!Curve.verifySignature(preKey.getIdentityKey().getPublicKey(),
preKey.getSignedPreKey().serialize(),
preKey.getSignedPreKeySignature()))
{
throw new InvalidKeyException("Invalid signature on device key!");
}
if (preKey.getSignedPreKey() == null && preKey.getPreKey() == null) {
throw new InvalidKeyException("Both signed and unsigned prekeys are absent!");
}
SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId);
ECKeyPair ourBaseKey = Curve.generateKeyPair();
ECPublicKey theirSignedPreKey = preKey.getSignedPreKey() != null ? preKey.getSignedPreKey() :
preKey.getPreKey();
AliceAxolotlParameters.Builder parameters = AliceAxolotlParameters.newBuilder();
parameters.setOurBaseKey(ourBaseKey)
.setOurIdentityKey(identityKeyStore.getIdentityKeyPair())
.setTheirIdentityKey(preKey.getIdentityKey())
.setTheirSignedPreKey(theirSignedPreKey)
.setTheirRatchetKey(theirSignedPreKey)
.setTheirOneTimePreKey(preKey.getSignedPreKey() != null ?
Optional.fromNullable(preKey.getPreKey()) :
Optional.<ECPublicKey>absent());
if (sessionRecord.getSessionState().getNeedsRefresh()) sessionRecord.archiveCurrentState();
else sessionRecord.reset();
RatchetingSession.initializeSession(sessionRecord.getSessionState(),
preKey.getSignedPreKey() == null ? 2 : 3,
parameters.create());
sessionRecord.getSessionState().setUnacknowledgedPreKeyMessage(preKey.getPreKeyId(), preKey.getSignedPreKeyId(), ourBaseKey.getPublicKey());
sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId());
sessionRecord.getSessionState().setRemoteRegistrationId(preKey.getRegistrationId());
sessionStore.storeSession(recipientId, deviceId, sessionRecord);
identityKeyStore.saveIdentity(recipientId, preKey.getIdentityKey());
} }
/** /**
@ -264,17 +265,18 @@ public class SessionBuilder {
public KeyExchangeMessage process(KeyExchangeMessage message) public KeyExchangeMessage process(KeyExchangeMessage message)
throws InvalidKeyException, UntrustedIdentityException, StaleKeyExchangeException throws InvalidKeyException, UntrustedIdentityException, StaleKeyExchangeException
{ {
synchronized (SessionCipher.SESSION_LOCK) {
if (!identityKeyStore.isTrustedIdentity(recipientId, message.getIdentityKey())) {
throw new UntrustedIdentityException();
}
if (!identityKeyStore.isTrustedIdentity(recipientId, message.getIdentityKey())) { KeyExchangeMessage responseMessage = null;
throw new UntrustedIdentityException();
if (message.isInitiate()) responseMessage = processInitiate(message);
else processResponse(message);
return responseMessage;
} }
KeyExchangeMessage responseMessage = null;
if (message.isInitiate()) responseMessage = processInitiate(message);
else processResponse(message);
return responseMessage;
} }
private KeyExchangeMessage processInitiate(KeyExchangeMessage message) throws InvalidKeyException { private KeyExchangeMessage processInitiate(KeyExchangeMessage message) throws InvalidKeyException {
@ -375,22 +377,24 @@ public class SessionBuilder {
* @return the KeyExchangeMessage to deliver. * @return the KeyExchangeMessage to deliver.
*/ */
public KeyExchangeMessage process() { public KeyExchangeMessage process() {
try { synchronized (SessionCipher.SESSION_LOCK) {
int sequence = KeyHelper.getRandomSequence(65534) + 1; try {
int flags = KeyExchangeMessage.INITIATE_FLAG; int sequence = KeyHelper.getRandomSequence(65534) + 1;
ECKeyPair baseKey = Curve.generateKeyPair(); int flags = KeyExchangeMessage.INITIATE_FLAG;
ECKeyPair ratchetKey = Curve.generateKeyPair(); ECKeyPair baseKey = Curve.generateKeyPair();
IdentityKeyPair identityKey = identityKeyStore.getIdentityKeyPair(); ECKeyPair ratchetKey = Curve.generateKeyPair();
byte[] baseKeySignature = Curve.calculateSignature(identityKey.getPrivateKey(), baseKey.getPublicKey().serialize()); IdentityKeyPair identityKey = identityKeyStore.getIdentityKeyPair();
SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId); byte[] baseKeySignature = Curve.calculateSignature(identityKey.getPrivateKey(), baseKey.getPublicKey().serialize());
SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId);
sessionRecord.getSessionState().setPendingKeyExchange(sequence, baseKey, ratchetKey, identityKey); sessionRecord.getSessionState().setPendingKeyExchange(sequence, baseKey, ratchetKey, identityKey);
sessionStore.storeSession(recipientId, deviceId, sessionRecord); sessionStore.storeSession(recipientId, deviceId, sessionRecord);
return new KeyExchangeMessage(2, sequence, flags, baseKey.getPublicKey(), baseKeySignature, return new KeyExchangeMessage(2, sequence, flags, baseKey.getPublicKey(), baseKeySignature,
ratchetKey.getPublicKey(), identityKey.getPublicKey()); ratchetKey.getPublicKey(), identityKey.getPublicKey());
} catch (InvalidKeyException e) { } catch (InvalidKeyException e) {
throw new AssertionError(e); throw new AssertionError(e);
}
} }
} }

View File

@ -59,7 +59,7 @@ import static org.whispersystems.libaxolotl.state.SessionState.UnacknowledgedPre
*/ */
public class SessionCipher { public class SessionCipher {
private static final Object SESSION_LOCK = new Object(); public static final Object SESSION_LOCK = new Object();
private final SessionStore sessionStore; private final SessionStore sessionStore;
private final SessionBuilder sessionBuilder; private final SessionBuilder sessionBuilder;