From 3e287f930d3c9899bbc4ecb5e7aeaa6873562585 Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Tue, 12 Aug 2014 13:56:16 -0700 Subject: [PATCH] Better thread safety for session building <-> use. --- .../libaxolotl/SessionBuilder.java | 138 +++++++++--------- .../libaxolotl/SessionCipher.java | 2 +- 2 files changed, 72 insertions(+), 68 deletions(-) diff --git a/libaxolotl/src/main/java/org/whispersystems/libaxolotl/SessionBuilder.java b/libaxolotl/src/main/java/org/whispersystems/libaxolotl/SessionBuilder.java index 53a501d2ea..be9ed18f3e 100644 --- a/libaxolotl/src/main/java/org/whispersystems/libaxolotl/SessionBuilder.java +++ b/libaxolotl/src/main/java/org/whispersystems/libaxolotl/SessionBuilder.java @@ -205,52 +205,53 @@ public class SessionBuilder { * trusted. */ 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())) { - throw new UntrustedIdentityException(); + 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.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.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) throws InvalidKeyException, UntrustedIdentityException, StaleKeyExchangeException { + synchronized (SessionCipher.SESSION_LOCK) { + if (!identityKeyStore.isTrustedIdentity(recipientId, message.getIdentityKey())) { + throw new UntrustedIdentityException(); + } - if (!identityKeyStore.isTrustedIdentity(recipientId, message.getIdentityKey())) { - throw new UntrustedIdentityException(); + KeyExchangeMessage responseMessage = null; + + 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 { @@ -375,22 +377,24 @@ public class SessionBuilder { * @return the KeyExchangeMessage to deliver. */ public KeyExchangeMessage process() { - try { - int sequence = KeyHelper.getRandomSequence(65534) + 1; - int flags = KeyExchangeMessage.INITIATE_FLAG; - ECKeyPair baseKey = Curve.generateKeyPair(); - ECKeyPair ratchetKey = Curve.generateKeyPair(); - IdentityKeyPair identityKey = identityKeyStore.getIdentityKeyPair(); - byte[] baseKeySignature = Curve.calculateSignature(identityKey.getPrivateKey(), baseKey.getPublicKey().serialize()); - SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId); + synchronized (SessionCipher.SESSION_LOCK) { + try { + int sequence = KeyHelper.getRandomSequence(65534) + 1; + int flags = KeyExchangeMessage.INITIATE_FLAG; + ECKeyPair baseKey = Curve.generateKeyPair(); + ECKeyPair ratchetKey = Curve.generateKeyPair(); + IdentityKeyPair identityKey = identityKeyStore.getIdentityKeyPair(); + byte[] baseKeySignature = Curve.calculateSignature(identityKey.getPrivateKey(), baseKey.getPublicKey().serialize()); + SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId); - sessionRecord.getSessionState().setPendingKeyExchange(sequence, baseKey, ratchetKey, identityKey); - sessionStore.storeSession(recipientId, deviceId, sessionRecord); + sessionRecord.getSessionState().setPendingKeyExchange(sequence, baseKey, ratchetKey, identityKey); + sessionStore.storeSession(recipientId, deviceId, sessionRecord); - return new KeyExchangeMessage(2, sequence, flags, baseKey.getPublicKey(), baseKeySignature, - ratchetKey.getPublicKey(), identityKey.getPublicKey()); - } catch (InvalidKeyException e) { - throw new AssertionError(e); + return new KeyExchangeMessage(2, sequence, flags, baseKey.getPublicKey(), baseKeySignature, + ratchetKey.getPublicKey(), identityKey.getPublicKey()); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } } } diff --git a/libaxolotl/src/main/java/org/whispersystems/libaxolotl/SessionCipher.java b/libaxolotl/src/main/java/org/whispersystems/libaxolotl/SessionCipher.java index 2f8628acf5..ceabbd18fd 100644 --- a/libaxolotl/src/main/java/org/whispersystems/libaxolotl/SessionCipher.java +++ b/libaxolotl/src/main/java/org/whispersystems/libaxolotl/SessionCipher.java @@ -59,7 +59,7 @@ import static org.whispersystems.libaxolotl.state.SessionState.UnacknowledgedPre */ 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 SessionBuilder sessionBuilder;