From 1e1b3e02e1957b573440ff3d59d32e645cbae6b1 Mon Sep 17 00:00:00 2001 From: Ryan ZHAO Date: Fri, 19 Feb 2021 13:36:05 +1100 Subject: [PATCH] clean up session cipher, sealed session cipher, and old message version --- .../securesms/jobs/PushDecryptJob.java | 4 +- .../libsignal/libsignal/SessionBuilder.java | 212 ----------- .../libsignal/libsignal/SessionCipher.java | 350 ------------------ .../libsignal/protocol/SignalMessage.java | 6 - .../metadata/SealedSessionCipher.java | 290 --------------- .../api/SignalServiceMessageSender.java | 3 +- .../api/crypto/SignalServiceCipher.java | 23 +- .../internal/push/PushTransportDetails.java | 18 +- 8 files changed, 7 insertions(+), 899 deletions(-) delete mode 100644 libsignal/src/main/java/org/session/libsignal/libsignal/SessionBuilder.java delete mode 100644 libsignal/src/main/java/org/session/libsignal/libsignal/SessionCipher.java delete mode 100644 libsignal/src/main/java/org/session/libsignal/metadata/SealedSessionCipher.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 28d94b796c..ad20f5346c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -218,10 +218,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType { private void handleMessage(@NonNull SignalServiceEnvelope envelope, @NonNull Optional smsMessageId, boolean isPushNotification) { try { GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context); - SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context)); LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(context); - SignalServiceCipher cipher = new SignalServiceCipher(localAddress, axolotlStore, new SessionProtocolImpl(context), apiDB); + SignalServiceCipher cipher = new SignalServiceCipher(new SessionProtocolImpl(context), apiDB); SignalServiceContent content = cipher.decrypt(envelope); diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/SessionBuilder.java b/libsignal/src/main/java/org/session/libsignal/libsignal/SessionBuilder.java deleted file mode 100644 index 75c4286e89..0000000000 --- a/libsignal/src/main/java/org/session/libsignal/libsignal/SessionBuilder.java +++ /dev/null @@ -1,212 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.session.libsignal.libsignal; - - -import org.session.libsignal.libsignal.ecc.Curve; -import org.session.libsignal.libsignal.ecc.ECKeyPair; -import org.session.libsignal.libsignal.ecc.ECPublicKey; -import org.session.libsignal.utilities.logging.Log; -import org.session.libsignal.libsignal.protocol.PreKeySignalMessage; -import org.session.libsignal.libsignal.protocol.SignalMessage; -import org.session.libsignal.libsignal.ratchet.AliceSignalProtocolParameters; -import org.session.libsignal.libsignal.ratchet.BobSignalProtocolParameters; -import org.session.libsignal.libsignal.ratchet.RatchetingSession; -import org.session.libsignal.libsignal.state.IdentityKeyStore; -import org.session.libsignal.libsignal.state.PreKeyBundle; -import org.session.libsignal.libsignal.state.PreKeyStore; -import org.session.libsignal.libsignal.state.SessionRecord; -import org.session.libsignal.libsignal.state.SessionStore; -import org.session.libsignal.libsignal.state.SignalProtocolStore; -import org.session.libsignal.libsignal.state.SignedPreKeyStore; -import org.session.libsignal.libsignal.util.guava.Optional; - -/** - * SessionBuilder is responsible for setting up encrypted sessions. - * Once a session has been established, {@link org.session.libsignal.libsignal.SessionCipher} - * can be used to encrypt/decrypt messages in that session. - *

- * Sessions are built from one of three different possible vectors: - *

    - *
  1. A {@link org.session.libsignal.libsignal.state.PreKeyBundle} retrieved from a server.
  2. - *
  3. A {@link PreKeySignalMessage} received from a client.
  4. - *
- * - * Sessions are constructed per recipientId + deviceId tuple. Remote logical users are identified - * by their recipientId, and each logical recipientId can have multiple physical devices. - * - * @author Moxie Marlinspike - */ -public class SessionBuilder { - - private static final String TAG = SessionBuilder.class.getSimpleName(); - - private final SessionStore sessionStore; - private final PreKeyStore preKeyStore; - private final SignedPreKeyStore signedPreKeyStore; - private final IdentityKeyStore identityKeyStore; - private final SignalProtocolAddress remoteAddress; - - /** - * Constructs a SessionBuilder. - * - * @param sessionStore The {@link org.session.libsignal.libsignal.state.SessionStore} to store the constructed session in. - * @param preKeyStore The {@link org.session.libsignal.libsignal.state.PreKeyStore} where the client's local {@link org.session.libsignal.libsignal.state.PreKeyRecord}s are stored. - * @param identityKeyStore The {@link org.session.libsignal.libsignal.state.IdentityKeyStore} containing the client's identity key information. - * @param remoteAddress The address of the remote user to build a session with. - */ - public SessionBuilder(SessionStore sessionStore, - PreKeyStore preKeyStore, - SignedPreKeyStore signedPreKeyStore, - IdentityKeyStore identityKeyStore, - SignalProtocolAddress remoteAddress) - { - this.sessionStore = sessionStore; - this.preKeyStore = preKeyStore; - this.signedPreKeyStore = signedPreKeyStore; - this.identityKeyStore = identityKeyStore; - this.remoteAddress = remoteAddress; - } - - /** - * Constructs a SessionBuilder - * @param store The {@link SignalProtocolStore} to store all state information in. - * @param remoteAddress The address of the remote user to build a session with. - */ - public SessionBuilder(SignalProtocolStore store, SignalProtocolAddress remoteAddress) { - this(store, store, store, store, remoteAddress); - } - - /** - * Build a new session from a received {@link PreKeySignalMessage}. - * - * After a session is constructed in this way, the embedded {@link SignalMessage} - * can be decrypted. - * - * @param message The received {@link PreKeySignalMessage}. - * @throws org.session.libsignal.libsignal.InvalidKeyIdException when there is no local - * {@link org.session.libsignal.libsignal.state.PreKeyRecord} - * that corresponds to the PreKey ID in - * the message. - * @throws org.session.libsignal.libsignal.InvalidKeyException when the message is formatted incorrectly. - * @throws org.session.libsignal.libsignal.UntrustedIdentityException when the {@link IdentityKey} of the sender is untrusted. - */ - /*package*/ Optional process(SessionRecord sessionRecord, PreKeySignalMessage message) - throws InvalidKeyIdException, InvalidKeyException, UntrustedIdentityException - { - IdentityKey theirIdentityKey = message.getIdentityKey(); - - if (!identityKeyStore.isTrustedIdentity(remoteAddress, theirIdentityKey, IdentityKeyStore.Direction.RECEIVING)) { - throw new UntrustedIdentityException(remoteAddress.getName(), theirIdentityKey); - } - - Optional unsignedPreKeyId = processV3(sessionRecord, message); - - identityKeyStore.saveIdentity(remoteAddress, theirIdentityKey); - - return unsignedPreKeyId; - } - - private Optional processV3(SessionRecord sessionRecord, PreKeySignalMessage message) - throws UntrustedIdentityException, InvalidKeyIdException, InvalidKeyException - { - - if (sessionRecord.hasSessionState(message.getMessageVersion(), message.getBaseKey().serialize())) { - Log.w(TAG, "We've already setup a session for this V3 message, letting bundled message fall through..."); - return Optional.absent(); - } - - ECKeyPair ourSignedPreKey = signedPreKeyStore.loadSignedPreKey(message.getSignedPreKeyId()).getKeyPair(); - - BobSignalProtocolParameters.Builder parameters = BobSignalProtocolParameters.newBuilder(); - - parameters.setTheirBaseKey(message.getBaseKey()) - .setTheirIdentityKey(message.getIdentityKey()) - .setOurIdentityKey(identityKeyStore.getIdentityKeyPair()) - .setOurSignedPreKey(ourSignedPreKey) - .setOurRatchetKey(ourSignedPreKey); - - if (message.getPreKeyId().isPresent()) { - parameters.setOurOneTimePreKey(Optional.of(preKeyStore.loadPreKey(message.getPreKeyId().get()).getKeyPair())); - } else { - parameters.setOurOneTimePreKey(Optional.absent()); - } - - if (!sessionRecord.isFresh()) sessionRecord.archiveCurrentState(); - - RatchetingSession.initializeSession(sessionRecord.getSessionState(), parameters.create()); - - sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId()); - sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId()); - sessionRecord.getSessionState().setAliceBaseKey(message.getBaseKey().serialize()); - - if (message.getPreKeyId().isPresent()) { - return message.getPreKeyId(); - } else { - return Optional.absent(); - } - } - - /** - * Build a new session from a {@link org.session.libsignal.libsignal.state.PreKeyBundle} retrieved from - * a server. - * - * @param preKey A PreKey for the destination recipient, retrieved from a server. - * @throws InvalidKeyException when the {@link org.session.libsignal.libsignal.state.PreKeyBundle} is - * badly formatted. - * @throws org.session.libsignal.libsignal.UntrustedIdentityException when the sender's - * {@link IdentityKey} is not - * trusted. - */ - public void process(PreKeyBundle preKey) throws InvalidKeyException, UntrustedIdentityException { - synchronized (SessionCipher.SESSION_LOCK) { - if (!identityKeyStore.isTrustedIdentity(remoteAddress, preKey.getIdentityKey(), IdentityKeyStore.Direction.SENDING)) { - throw new UntrustedIdentityException(remoteAddress.getName(), 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) { - throw new InvalidKeyException("No signed prekey!"); - } - - SessionRecord sessionRecord = sessionStore.loadSession(remoteAddress); - ECKeyPair ourBaseKey = Curve.generateKeyPair(); - ECPublicKey theirSignedPreKey = preKey.getSignedPreKey(); - Optional theirOneTimePreKey = Optional.fromNullable(preKey.getPreKey()); - Optional theirOneTimePreKeyId = theirOneTimePreKey.isPresent() ? Optional.of(preKey.getPreKeyId()) : - Optional.absent(); - - AliceSignalProtocolParameters.Builder parameters = AliceSignalProtocolParameters.newBuilder(); - - parameters.setOurBaseKey(ourBaseKey) - .setOurIdentityKey(identityKeyStore.getIdentityKeyPair()) - .setTheirIdentityKey(preKey.getIdentityKey()) - .setTheirSignedPreKey(theirSignedPreKey) - .setTheirRatchetKey(theirSignedPreKey) - .setTheirOneTimePreKey(theirOneTimePreKey); - - if (!sessionRecord.isFresh()) sessionRecord.archiveCurrentState(); - - RatchetingSession.initializeSession(sessionRecord.getSessionState(), parameters.create()); - - sessionRecord.getSessionState().setUnacknowledgedPreKeyMessage(theirOneTimePreKeyId, preKey.getSignedPreKeyId(), ourBaseKey.getPublicKey()); - sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId()); - sessionRecord.getSessionState().setRemoteRegistrationId(preKey.getRegistrationId()); - sessionRecord.getSessionState().setAliceBaseKey(ourBaseKey.getPublicKey().serialize()); - - identityKeyStore.saveIdentity(remoteAddress, preKey.getIdentityKey()); - sessionStore.storeSession(remoteAddress, sessionRecord); - } - } - -} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/SessionCipher.java b/libsignal/src/main/java/org/session/libsignal/libsignal/SessionCipher.java deleted file mode 100644 index 7d75806bc8..0000000000 --- a/libsignal/src/main/java/org/session/libsignal/libsignal/SessionCipher.java +++ /dev/null @@ -1,350 +0,0 @@ -/** - * Copyright (C) 2014-2016 Open Whisper Systems - * - * Licensed according to the LICENSE file in this repository. - */ -package org.session.libsignal.libsignal; - -import org.session.libsignal.libsignal.ecc.Curve; -import org.session.libsignal.libsignal.ecc.ECKeyPair; -import org.session.libsignal.libsignal.ecc.ECPublicKey; -import org.session.libsignal.libsignal.protocol.CiphertextMessage; -import org.session.libsignal.libsignal.protocol.PreKeySignalMessage; -import org.session.libsignal.libsignal.protocol.SignalMessage; -import org.session.libsignal.libsignal.ratchet.ChainKey; -import org.session.libsignal.libsignal.ratchet.MessageKeys; -import org.session.libsignal.libsignal.ratchet.RootKey; -import org.session.libsignal.libsignal.state.IdentityKeyStore; -import org.session.libsignal.libsignal.state.PreKeyStore; -import org.session.libsignal.libsignal.state.SessionRecord; -import org.session.libsignal.libsignal.state.SessionState; -import org.session.libsignal.libsignal.state.SessionStore; -import org.session.libsignal.libsignal.state.SignalProtocolStore; -import org.session.libsignal.libsignal.state.SignedPreKeyStore; -import org.session.libsignal.libsignal.util.Pair; -import org.session.libsignal.libsignal.util.guava.Optional; - -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -import static org.session.libsignal.libsignal.state.SessionState.UnacknowledgedPreKeyMessageItems; - -/** - * The main entry point for Signal Protocol encrypt/decrypt operations. - * - * Once a session has been established with {@link SessionBuilder}, - * this class can be used for all encrypt/decrypt operations within - * that session. - * - * @author Moxie Marlinspike - */ -public class SessionCipher { - - public static final Object SESSION_LOCK = new Object(); - - private final SessionStore sessionStore; - private final IdentityKeyStore identityKeyStore; - private final SessionBuilder sessionBuilder; - private final PreKeyStore preKeyStore; - private final SignalProtocolAddress remoteAddress; - - /** - * Construct a SessionCipher for encrypt/decrypt operations on a session. - * In order to use SessionCipher, a session must have already been created - * and stored using {@link SessionBuilder}. - * - * @param sessionStore The {@link SessionStore} that contains a session for this recipient. - * @param remoteAddress The remote address that messages will be encrypted to or decrypted from. - */ - public SessionCipher(SessionStore sessionStore, PreKeyStore preKeyStore, - SignedPreKeyStore signedPreKeyStore, IdentityKeyStore identityKeyStore, - SignalProtocolAddress remoteAddress) - { - this.sessionStore = sessionStore; - this.preKeyStore = preKeyStore; - this.identityKeyStore = identityKeyStore; - this.remoteAddress = remoteAddress; - this.sessionBuilder = new SessionBuilder(sessionStore, preKeyStore, signedPreKeyStore, - identityKeyStore, remoteAddress); - } - - public SessionCipher(SignalProtocolStore store, SignalProtocolAddress remoteAddress) { - this(store, store, store, store, remoteAddress); - } - - /** - * Encrypt a message. - * - * @param paddedMessage The plaintext message bytes, optionally padded to a constant multiple. - * @return A ciphertext message encrypted to the recipient+device tuple. - */ - public CiphertextMessage encrypt(byte[] paddedMessage) throws UntrustedIdentityException { - synchronized (SESSION_LOCK) { - SessionRecord sessionRecord = sessionStore.loadSession(remoteAddress); - SessionState sessionState = sessionRecord.getSessionState(); - ChainKey chainKey = sessionState.getSenderChainKey(); - MessageKeys messageKeys = chainKey.getMessageKeys(); - ECPublicKey senderEphemeral = sessionState.getSenderRatchetKey(); - int previousCounter = sessionState.getPreviousCounter(); - int sessionVersion = sessionState.getSessionVersion(); - - byte[] ciphertextBody = getCiphertext(messageKeys, paddedMessage); - CiphertextMessage ciphertextMessage = new SignalMessage(sessionVersion, messageKeys.getMacKey(), - senderEphemeral, chainKey.getIndex(), - previousCounter, ciphertextBody, - sessionState.getLocalIdentityKey(), - sessionState.getRemoteIdentityKey()); - - if (sessionState.hasUnacknowledgedPreKeyMessage()) { - UnacknowledgedPreKeyMessageItems items = sessionState.getUnacknowledgedPreKeyMessageItems(); - int localRegistrationId = sessionState.getLocalRegistrationId(); - - ciphertextMessage = new PreKeySignalMessage(sessionVersion, localRegistrationId, items.getPreKeyId(), - items.getSignedPreKeyId(), items.getBaseKey(), - sessionState.getLocalIdentityKey(), - (SignalMessage) ciphertextMessage); - } - - sessionState.setSenderChainKey(chainKey.getNextChainKey()); - - if (!identityKeyStore.isTrustedIdentity(remoteAddress, sessionState.getRemoteIdentityKey(), IdentityKeyStore.Direction.SENDING)) { - throw new UntrustedIdentityException(remoteAddress.getName(), sessionState.getRemoteIdentityKey()); - } - - identityKeyStore.saveIdentity(remoteAddress, sessionState.getRemoteIdentityKey()); - sessionStore.storeSession(remoteAddress, sessionRecord); - return ciphertextMessage; - } - } - - public byte[] decrypt(PreKeySignalMessage ciphertext) - throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, - InvalidKeyIdException, InvalidKeyException, UntrustedIdentityException - { - synchronized (SESSION_LOCK) { - SessionRecord sessionRecord = sessionStore.loadSession(remoteAddress); - Optional unsignedPreKeyId = sessionBuilder.process(sessionRecord, ciphertext); - byte[] plaintext = decrypt(sessionRecord, ciphertext.getWhisperMessage()); - - sessionStore.storeSession(remoteAddress, sessionRecord); - - if (unsignedPreKeyId.isPresent()) { - preKeyStore.removePreKey(unsignedPreKeyId.get()); - } - - return plaintext; - } - } - - public byte[] decrypt(SignalMessage ciphertext) - throws InvalidMessageException, DuplicateMessageException, LegacyMessageException, - NoSessionException, UntrustedIdentityException - { - synchronized (SESSION_LOCK) { - - if (!sessionStore.containsSession(remoteAddress)) { - throw new NoSessionException("No session for: " + remoteAddress); - } - - SessionRecord sessionRecord = sessionStore.loadSession(remoteAddress); - byte[] plaintext = decrypt(sessionRecord, ciphertext); - - if (!identityKeyStore.isTrustedIdentity(remoteAddress, sessionRecord.getSessionState().getRemoteIdentityKey(), IdentityKeyStore.Direction.RECEIVING)) { - throw new UntrustedIdentityException(remoteAddress.getName(), sessionRecord.getSessionState().getRemoteIdentityKey()); - } - - identityKeyStore.saveIdentity(remoteAddress, sessionRecord.getSessionState().getRemoteIdentityKey()); - - sessionStore.storeSession(remoteAddress, sessionRecord); - - return plaintext; - } - } - - private byte[] decrypt(SessionRecord sessionRecord, SignalMessage ciphertext) - throws DuplicateMessageException, LegacyMessageException, InvalidMessageException - { - synchronized (SESSION_LOCK) { - Iterator previousStates = sessionRecord.getPreviousSessionStates().iterator(); - List exceptions = new LinkedList(); - - try { - SessionState sessionState = new SessionState(sessionRecord.getSessionState()); - byte[] plaintext = decrypt(sessionState, ciphertext); - - sessionRecord.setState(sessionState); - return plaintext; - } catch (InvalidMessageException e) { - exceptions.add(e); - } - - while (previousStates.hasNext()) { - try { - SessionState promotedState = new SessionState(previousStates.next()); - byte[] plaintext = decrypt(promotedState, ciphertext); - - previousStates.remove(); - sessionRecord.promoteState(promotedState); - - return plaintext; - } catch (InvalidMessageException e) { - exceptions.add(e); - } - } - - throw new InvalidMessageException("No valid sessions.", exceptions); - } - } - - private byte[] decrypt(SessionState sessionState, SignalMessage ciphertextMessage) - throws InvalidMessageException, DuplicateMessageException, LegacyMessageException - { - if (!sessionState.hasSenderChain()) { - throw new InvalidMessageException("Uninitialized session!"); - } - - if (ciphertextMessage.getMessageVersion() != sessionState.getSessionVersion()) { - throw new InvalidMessageException(String.format("Message version %d, but session version %d", - ciphertextMessage.getMessageVersion(), - sessionState.getSessionVersion())); - } - - ECPublicKey theirEphemeral = ciphertextMessage.getSenderRatchetKey(); - int counter = ciphertextMessage.getCounter(); - ChainKey chainKey = getOrCreateChainKey(sessionState, theirEphemeral); - MessageKeys messageKeys = getOrCreateMessageKeys(sessionState, theirEphemeral, - chainKey, counter); - - ciphertextMessage.verifyMac(sessionState.getRemoteIdentityKey(), - sessionState.getLocalIdentityKey(), - messageKeys.getMacKey()); - - byte[] plaintext = getPlaintext(messageKeys, ciphertextMessage.getBody()); - - sessionState.clearUnacknowledgedPreKeyMessage(); - - return plaintext; - } - - public int getRemoteRegistrationId() { - synchronized (SESSION_LOCK) { - SessionRecord record = sessionStore.loadSession(remoteAddress); - return record.getSessionState().getRemoteRegistrationId(); - } - } - - public int getSessionVersion() { - synchronized (SESSION_LOCK) { - if (!sessionStore.containsSession(remoteAddress)) { - // Loki - If we have no session then we must be using the FallbackSessionCipher - return 3; - } - - SessionRecord record = sessionStore.loadSession(remoteAddress); - return record.getSessionState().getSessionVersion(); - } - } - - private ChainKey getOrCreateChainKey(SessionState sessionState, ECPublicKey theirEphemeral) - throws InvalidMessageException - { - try { - if (sessionState.hasReceiverChain(theirEphemeral)) { - return sessionState.getReceiverChainKey(theirEphemeral); - } else { - RootKey rootKey = sessionState.getRootKey(); - ECKeyPair ourEphemeral = sessionState.getSenderRatchetKeyPair(); - Pair receiverChain = rootKey.createChain(theirEphemeral, ourEphemeral); - ECKeyPair ourNewEphemeral = Curve.generateKeyPair(); - Pair senderChain = receiverChain.first().createChain(theirEphemeral, ourNewEphemeral); - - sessionState.setRootKey(senderChain.first()); - sessionState.addReceiverChain(theirEphemeral, receiverChain.second()); - sessionState.setPreviousCounter(Math.max(sessionState.getSenderChainKey().getIndex()-1, 0)); - sessionState.setSenderChain(ourNewEphemeral, senderChain.second()); - - return receiverChain.second(); - } - } catch (InvalidKeyException e) { - throw new InvalidMessageException(e); - } - } - - private MessageKeys getOrCreateMessageKeys(SessionState sessionState, - ECPublicKey theirEphemeral, - ChainKey chainKey, int counter) - throws InvalidMessageException, DuplicateMessageException - { - if (chainKey.getIndex() > counter) { - if (sessionState.hasMessageKeys(theirEphemeral, counter)) { - return sessionState.removeMessageKeys(theirEphemeral, counter); - } else { - throw new DuplicateMessageException("Received message with old counter: " + - chainKey.getIndex() + " , " + counter); - } - } - - if (counter - chainKey.getIndex() > 2000) { - throw new InvalidMessageException("Over 2000 messages into the future!"); - } - - while (chainKey.getIndex() < counter) { - MessageKeys messageKeys = chainKey.getMessageKeys(); - sessionState.setMessageKeys(theirEphemeral, messageKeys); - chainKey = chainKey.getNextChainKey(); - } - - sessionState.setReceiverChainKey(theirEphemeral, chainKey.getNextChainKey()); - return chainKey.getMessageKeys(); - } - - private byte[] getCiphertext(MessageKeys messageKeys, byte[] plaintext) { - try { - Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, messageKeys.getCipherKey(), messageKeys.getIv()); - return cipher.doFinal(plaintext); - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } catch (BadPaddingException e) { - throw new AssertionError(e); - } - } - - private byte[] getPlaintext(MessageKeys messageKeys, byte[] cipherText) - throws InvalidMessageException - { - try { - Cipher cipher = getCipher(Cipher.DECRYPT_MODE, messageKeys.getCipherKey(), messageKeys.getIv()); - return cipher.doFinal(cipherText); - } catch (IllegalBlockSizeException e) { - throw new InvalidMessageException(e); - } catch (BadPaddingException e) { - throw new InvalidMessageException(e); - } - } - - private Cipher getCipher(int mode, SecretKeySpec key, IvParameterSpec iv) { - try { - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - cipher.init(mode, key, iv); - return cipher; - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch (java.security.InvalidKeyException e) { - throw new AssertionError(e); - } catch (InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } - } -} diff --git a/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/SignalMessage.java b/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/SignalMessage.java index 04709a36cc..b72a9aae19 100644 --- a/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/SignalMessage.java +++ b/libsignal/src/main/java/org/session/libsignal/libsignal/protocol/SignalMessage.java @@ -154,10 +154,4 @@ public class SignalMessage implements CiphertextMessage { public int getType() { return CiphertextMessage.WHISPER_TYPE; } - - public static boolean isLegacy(byte[] message) { - return message != null && message.length >= 1 && - ByteUtil.highBitsToInt(message[0]) != CiphertextMessage.CURRENT_VERSION; - } - } diff --git a/libsignal/src/main/java/org/session/libsignal/metadata/SealedSessionCipher.java b/libsignal/src/main/java/org/session/libsignal/metadata/SealedSessionCipher.java deleted file mode 100644 index 53ed84db6b..0000000000 --- a/libsignal/src/main/java/org/session/libsignal/metadata/SealedSessionCipher.java +++ /dev/null @@ -1,290 +0,0 @@ -package org.session.libsignal.metadata; - -import org.session.libsignal.metadata.certificate.CertificateValidator; -import org.session.libsignal.metadata.certificate.InvalidCertificateException; -import org.session.libsignal.metadata.certificate.SenderCertificate; -import org.session.libsignal.metadata.protocol.UnidentifiedSenderMessage; -import org.session.libsignal.metadata.protocol.UnidentifiedSenderMessageContent; -import org.session.libsignal.libsignal.DuplicateMessageException; -import org.session.libsignal.libsignal.IdentityKey; -import org.session.libsignal.libsignal.IdentityKeyPair; -import org.session.libsignal.libsignal.InvalidKeyException; -import org.session.libsignal.libsignal.InvalidKeyIdException; -import org.session.libsignal.libsignal.InvalidMacException; -import org.session.libsignal.libsignal.InvalidMessageException; -import org.session.libsignal.libsignal.InvalidVersionException; -import org.session.libsignal.libsignal.LegacyMessageException; -import org.session.libsignal.libsignal.NoSessionException; -import org.session.libsignal.libsignal.SessionCipher; -import org.session.libsignal.libsignal.SignalProtocolAddress; -import org.session.libsignal.libsignal.UntrustedIdentityException; -import org.session.libsignal.libsignal.ecc.Curve; -import org.session.libsignal.libsignal.ecc.ECKeyPair; -import org.session.libsignal.libsignal.ecc.ECPrivateKey; -import org.session.libsignal.libsignal.ecc.ECPublicKey; -import org.session.libsignal.libsignal.kdf.HKDFv3; -import org.session.libsignal.libsignal.protocol.CiphertextMessage; -import org.session.libsignal.libsignal.protocol.PreKeySignalMessage; -import org.session.libsignal.libsignal.protocol.SignalMessage; -import org.session.libsignal.libsignal.state.SignalProtocolStore; -import org.session.libsignal.libsignal.util.ByteUtil; -import org.session.libsignal.utilities.Hex; -import org.session.libsignal.libsignal.util.Pair; - -import java.io.IOException; -import java.security.InvalidAlgorithmParameterException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.text.ParseException; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.Mac; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -public class SealedSessionCipher { - - private final SignalProtocolStore signalProtocolStore; - private final SignalProtocolAddress localAddress; - - public SealedSessionCipher(SignalProtocolStore signalProtocolStore, SignalProtocolAddress localAddress) - { - this.signalProtocolStore = signalProtocolStore; - this.localAddress = localAddress; - } - - public byte[] encrypt(SignalProtocolAddress destinationAddress, SenderCertificate senderCertificate, byte[] paddedPlaintext) - throws InvalidKeyException, UntrustedIdentityException - { - CiphertextMessage message = new SessionCipher(signalProtocolStore, destinationAddress).encrypt(paddedPlaintext); - return encrypt(destinationAddress, senderCertificate, message); - } - - public byte[] encrypt(SignalProtocolAddress destinationAddress, SenderCertificate senderCertificate, CiphertextMessage message) - throws InvalidKeyException - { - try { - IdentityKeyPair ourIdentity = signalProtocolStore.getIdentityKeyPair(); - byte[] theirPublicKey = Hex.fromStringCondensed(destinationAddress.getName()); - ECPublicKey theirIdentity = new IdentityKey(theirPublicKey, 0).getPublicKey(); - - ECKeyPair ephemeral = Curve.generateKeyPair(); - byte[] ephemeralSalt = ByteUtil.combine("UnidentifiedDelivery".getBytes(), theirIdentity.serialize(), ephemeral.getPublicKey().serialize()); - EphemeralKeys ephemeralKeys = calculateEphemeralKeys(theirIdentity, ephemeral.getPrivateKey(), ephemeralSalt); - byte[] staticKeyCiphertext = encrypt(ephemeralKeys.cipherKey, ephemeralKeys.macKey, ourIdentity.getPublicKey().serialize()); - - byte[] staticSalt = ByteUtil.combine(ephemeralKeys.chainKey, staticKeyCiphertext); - StaticKeys staticKeys = calculateStaticKeys(theirIdentity, ourIdentity.getPrivateKey(), staticSalt); - UnidentifiedSenderMessageContent content = new UnidentifiedSenderMessageContent(message.getType(), senderCertificate, message.serialize()); - byte[] messageBytes = encrypt(staticKeys.cipherKey, staticKeys.macKey, content.getSerialized()); - - return new UnidentifiedSenderMessage(ephemeral.getPublicKey(), staticKeyCiphertext, messageBytes).getSerialized(); - } catch (IOException e) { - throw new InvalidKeyException(e); - } - } - - /** - * Decrypt a sealed session message. - * This will return a Pair which is the CipherTextMessage type and the decrypted message content - */ - public Pair> decrypt(CertificateValidator validator, byte[] ciphertext, long timestamp, String prefixedPublicKey) - throws - InvalidMetadataMessageException, InvalidMetadataVersionException, - ProtocolInvalidMessageException, ProtocolInvalidKeyException, - ProtocolNoSessionException, ProtocolLegacyMessageException, - ProtocolInvalidVersionException, ProtocolDuplicateMessageException, - ProtocolInvalidKeyIdException, ProtocolUntrustedIdentityException, - SelfSendException, IOException - { - UnidentifiedSenderMessageContent content; - - try { - IdentityKeyPair ourIdentity = signalProtocolStore.getIdentityKeyPair(); - UnidentifiedSenderMessage wrapper = new UnidentifiedSenderMessage(ciphertext); - byte[] ephemeralSalt = ByteUtil.combine("UnidentifiedDelivery".getBytes(), ourIdentity.getPublicKey().serialize(), wrapper.getEphemeral().serialize()); - EphemeralKeys ephemeralKeys = calculateEphemeralKeys(wrapper.getEphemeral(), ourIdentity.getPrivateKey(), ephemeralSalt); - byte[] staticKeyBytes = decrypt(ephemeralKeys.cipherKey, ephemeralKeys.macKey, wrapper.getEncryptedStatic()); - - ECPublicKey staticKey = Curve.decodePoint(staticKeyBytes, 0); - byte[] staticSalt = ByteUtil.combine(ephemeralKeys.chainKey, wrapper.getEncryptedStatic()); - StaticKeys staticKeys = calculateStaticKeys(staticKey, ourIdentity.getPrivateKey(), staticSalt); - byte[] messageBytes = decrypt(staticKeys.cipherKey, staticKeys.macKey, wrapper.getEncryptedMessage()); - - content = new UnidentifiedSenderMessageContent(messageBytes); - validator.validate(content.getSenderCertificate(), timestamp); - - if (content.getSenderCertificate().getSender().equals(localAddress.getName()) && - content.getSenderCertificate().getSenderDeviceId() == localAddress.getDeviceId()) - { - throw new SelfSendException(); - } - } catch (InvalidKeyException e) { - throw new InvalidMetadataMessageException(e); - } catch (InvalidMacException e) { - throw new InvalidMetadataMessageException(e); - } catch (InvalidCertificateException e) { - throw new InvalidMetadataMessageException(e); - } - - try { - Pair dataPair = new Pair<>(content.getType(), decrypt(content)); - return new Pair<>( - new SignalProtocolAddress(content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()), - dataPair - ); - } catch (InvalidMessageException e) { - throw new ProtocolInvalidMessageException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); - } catch (InvalidKeyException e) { - throw new ProtocolInvalidKeyException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); - } catch (NoSessionException e) { - throw new ProtocolNoSessionException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); - } catch (LegacyMessageException e) { - throw new ProtocolLegacyMessageException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); - } catch (InvalidVersionException e) { - throw new ProtocolInvalidVersionException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); - } catch (DuplicateMessageException e) { - throw new ProtocolDuplicateMessageException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); - } catch (InvalidKeyIdException e) { - throw new ProtocolInvalidKeyIdException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); - } catch (UntrustedIdentityException e) { - throw new ProtocolUntrustedIdentityException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); - } - } - - public int getSessionVersion(SignalProtocolAddress remoteAddress) { - return new SessionCipher(signalProtocolStore, remoteAddress).getSessionVersion(); - } - - public int getRemoteRegistrationId(SignalProtocolAddress remoteAddress) { - return new SessionCipher(signalProtocolStore, remoteAddress).getRemoteRegistrationId(); - } - - private EphemeralKeys calculateEphemeralKeys(ECPublicKey ephemeralPublic, ECPrivateKey ephemeralPrivate, byte[] salt) throws InvalidKeyException { - try { - byte[] ephemeralSecret = Curve.calculateAgreement(ephemeralPublic, ephemeralPrivate); - byte[] ephemeralDerived = new HKDFv3().deriveSecrets(ephemeralSecret, salt, new byte[0], 96); - byte[][] ephemeralDerivedParts = ByteUtil.split(ephemeralDerived, 32, 32, 32); - - return new EphemeralKeys(ephemeralDerivedParts[0], ephemeralDerivedParts[1], ephemeralDerivedParts[2]); - } catch (ParseException e) { - throw new AssertionError(e); - } - } - - private StaticKeys calculateStaticKeys(ECPublicKey staticPublic, ECPrivateKey staticPrivate, byte[] salt) throws InvalidKeyException { - try { - byte[] staticSecret = Curve.calculateAgreement(staticPublic, staticPrivate); - byte[] staticDerived = new HKDFv3().deriveSecrets(staticSecret, salt, new byte[0], 96); - byte[][] staticDerivedParts = ByteUtil.split(staticDerived, 32, 32, 32); - - return new StaticKeys(staticDerivedParts[1], staticDerivedParts[2]); - } catch (ParseException e) { - throw new AssertionError(e); - } - } - - private byte[] decrypt(UnidentifiedSenderMessageContent message) - throws InvalidVersionException, InvalidMessageException, InvalidKeyException, DuplicateMessageException, InvalidKeyIdException, UntrustedIdentityException, LegacyMessageException, NoSessionException - { - - SignalProtocolAddress sender = new SignalProtocolAddress(message.getSenderCertificate().getSender(), message.getSenderCertificate().getSenderDeviceId()); - - switch (message.getType()) { - case CiphertextMessage.WHISPER_TYPE: return new SessionCipher(signalProtocolStore, sender).decrypt(new SignalMessage(message.getContent())); - case CiphertextMessage.PREKEY_TYPE: return new SessionCipher(signalProtocolStore, sender).decrypt(new PreKeySignalMessage(message.getContent())); - default: throw new InvalidMessageException("Unknown type: " + message.getType()); - } - } - - private byte[] encrypt(SecretKeySpec cipherKey, SecretKeySpec macKey, byte[] plaintext) { - try { - Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); - cipher.init(Cipher.ENCRYPT_MODE, cipherKey, new IvParameterSpec(new byte[16])); - - Mac mac = Mac.getInstance("HmacSHA256"); - mac.init(macKey); - - byte[] ciphertext = cipher.doFinal(plaintext); - byte[] ourFullMac = mac.doFinal(ciphertext); - byte[] ourMac = ByteUtil.trim(ourFullMac, 10); - - return ByteUtil.combine(ciphertext, ourMac); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch (java.security.InvalidKeyException e) { - throw new AssertionError(e); - } catch (BadPaddingException e) { - throw new AssertionError(e); - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } catch (InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } - } - - private byte[] decrypt(SecretKeySpec cipherKey, SecretKeySpec macKey, byte[] ciphertext) throws InvalidMacException { - try { - if (ciphertext.length < 10) { - throw new InvalidMacException("Ciphertext not long enough for MAC!"); - } - - byte[][] ciphertextParts = ByteUtil.split(ciphertext, ciphertext.length - 10, 10); - - Mac mac = Mac.getInstance("HmacSHA256"); - mac.init(macKey); - - byte[] digest = mac.doFinal(ciphertextParts[0]); - byte[] ourMac = ByteUtil.trim(digest, 10); - byte[] theirMac = ciphertextParts[1]; - - if (!MessageDigest.isEqual(ourMac, theirMac)) { - throw new InvalidMacException("Bad mac!"); - } - - Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); - cipher.init(Cipher.DECRYPT_MODE, cipherKey, new IvParameterSpec(new byte[16])); - - return cipher.doFinal(ciphertextParts[0]); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (java.security.InvalidKeyException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch (InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } catch (BadPaddingException e) { - throw new AssertionError(e); - } - } - - private static class EphemeralKeys { - private final byte[] chainKey; - private final SecretKeySpec cipherKey; - private final SecretKeySpec macKey; - - private EphemeralKeys(byte[] chainKey, byte[] cipherKey, byte[] macKey) { - this.chainKey = chainKey; - this.cipherKey = new SecretKeySpec(cipherKey, "AES"); - this.macKey = new SecretKeySpec(macKey, "HmacSHA256"); - } - } - - private static class StaticKeys { - private final SecretKeySpec cipherKey; - private final SecretKeySpec macKey; - - private StaticKeys(byte[] cipherKey, byte[] macKey) { - this.cipherKey = new SecretKeySpec(cipherKey, "AES"); - this.macKey = new SecretKeySpec(macKey, "HmacSHA256"); - } - } -} diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/SignalServiceMessageSender.java b/libsignal/src/main/java/org/session/libsignal/service/api/SignalServiceMessageSender.java index d1ca970ac9..aa0c47adfc 100644 --- a/libsignal/src/main/java/org/session/libsignal/service/api/SignalServiceMessageSender.java +++ b/libsignal/src/main/java/org/session/libsignal/service/api/SignalServiceMessageSender.java @@ -878,7 +878,6 @@ public class SignalServiceMessageSender { { List messages = new LinkedList<>(); - PushTransportDetails transportDetails = new PushTransportDetails(3); String publicKey = recipient.getNumber(); // Could be a contact's public key or the public key of a SSK group boolean isClosedGroup = apiDatabase.isClosedGroup(publicKey); String encryptionPublicKey; @@ -888,7 +887,7 @@ public class SignalServiceMessageSender { } else { encryptionPublicKey = publicKey; } - byte[] ciphertext = sessionProtocolImpl.encrypt(transportDetails.getPaddedMessageBody(plaintext), encryptionPublicKey); + byte[] ciphertext = sessionProtocolImpl.encrypt(PushTransportDetails.getPaddedMessageBody(plaintext), encryptionPublicKey); String body = Base64.encodeBytes(ciphertext); int type = isClosedGroup ? SignalServiceProtos.Envelope.Type.CLOSED_GROUP_CIPHERTEXT_VALUE : SignalServiceProtos.Envelope.Type.UNIDENTIFIED_SENDER_VALUE; diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/crypto/SignalServiceCipher.java b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/SignalServiceCipher.java index dec47a68e9..bb7e9c21d3 100644 --- a/libsignal/src/main/java/org/session/libsignal/service/api/crypto/SignalServiceCipher.java +++ b/libsignal/src/main/java/org/session/libsignal/service/api/crypto/SignalServiceCipher.java @@ -11,11 +11,7 @@ import com.google.protobuf.InvalidProtocolBufferException; import org.session.libsignal.libsignal.ecc.ECKeyPair; import org.session.libsignal.metadata.InvalidMetadataMessageException; import org.session.libsignal.metadata.ProtocolInvalidMessageException; -import org.session.libsignal.metadata.SealedSessionCipher; import org.session.libsignal.libsignal.InvalidMessageException; -import org.session.libsignal.libsignal.SessionCipher; -import org.session.libsignal.libsignal.SignalProtocolAddress; -import org.session.libsignal.libsignal.state.SignalProtocolStore; import org.session.libsignal.libsignal.util.guava.Optional; import org.session.libsignal.service.api.messages.SignalServiceAttachment; import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer; @@ -55,18 +51,12 @@ public class SignalServiceCipher { @SuppressWarnings("unused") private static final String TAG = SignalServiceCipher.class.getSimpleName(); - private final SignalProtocolStore signalProtocolStore; - private final SignalServiceAddress localAddress; private final SessionProtocol sessionProtocolImpl; private final LokiAPIDatabaseProtocol apiDB; - public SignalServiceCipher(SignalServiceAddress localAddress, - SignalProtocolStore signalProtocolStore, - SessionProtocol sessionProtocolImpl, + public SignalServiceCipher(SessionProtocol sessionProtocolImpl, LokiAPIDatabaseProtocol apiDB) { - this.signalProtocolStore = signalProtocolStore; - this.localAddress = localAddress; this.sessionProtocolImpl = sessionProtocolImpl; this.apiDB = apiDB; } @@ -135,13 +125,8 @@ public class SignalServiceCipher { protected Plaintext decrypt(SignalServiceEnvelope envelope, byte[] ciphertext) throws InvalidMetadataMessageException { - SignalProtocolAddress sourceAddress = new SignalProtocolAddress(envelope.getSource(), envelope.getSourceDevice()); - SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, sourceAddress); - SealedSessionCipher sealedSessionCipher = new SealedSessionCipher(signalProtocolStore, new SignalProtocolAddress(localAddress.getNumber(), 1)); - byte[] paddedMessage; Metadata metadata; - int sessionVersion; if (envelope.isClosedGroupCiphertext()) { String groupPublicKey = envelope.getSource(); @@ -149,20 +134,16 @@ public class SignalServiceCipher { paddedMessage = plaintextAndSenderPublicKey.getFirst(); String senderPublicKey = plaintextAndSenderPublicKey.getSecond(); metadata = new Metadata(senderPublicKey, 1, envelope.getTimestamp(), false); - sessionVersion = sessionCipher.getSessionVersion(); } else if (envelope.isUnidentifiedSender()) { ECKeyPair userX25519KeyPair = apiDB.getUserX25519KeyPair(); kotlin.Pair plaintextAndSenderPublicKey = sessionProtocolImpl.decrypt(ciphertext, userX25519KeyPair); paddedMessage = plaintextAndSenderPublicKey.getFirst(); String senderPublicKey = plaintextAndSenderPublicKey.getSecond(); metadata = new Metadata(senderPublicKey, 1, envelope.getTimestamp(), false); - sessionVersion = sealedSessionCipher.getSessionVersion(new SignalProtocolAddress(metadata.getSender(), metadata.getSenderDevice())); } else { throw new InvalidMetadataMessageException("Unknown type: " + envelope.getType()); } - - PushTransportDetails transportDetails = new PushTransportDetails(sessionVersion); - byte[] data = transportDetails.getStrippedPaddingMessageBody(paddedMessage); + byte[] data = PushTransportDetails.getStrippedPaddingMessageBody(paddedMessage); return new Plaintext(metadata, data); } diff --git a/libsignal/src/main/java/org/session/libsignal/service/internal/push/PushTransportDetails.java b/libsignal/src/main/java/org/session/libsignal/service/internal/push/PushTransportDetails.java index 03058a69be..dda2357cb1 100644 --- a/libsignal/src/main/java/org/session/libsignal/service/internal/push/PushTransportDetails.java +++ b/libsignal/src/main/java/org/session/libsignal/service/internal/push/PushTransportDetails.java @@ -13,16 +13,7 @@ public class PushTransportDetails { private static final String TAG = PushTransportDetails.class.getSimpleName(); - private final int messageVersion; - - public PushTransportDetails(int messageVersion) { - this.messageVersion = messageVersion; - } - - public byte[] getStrippedPaddingMessageBody(byte[] messageWithPadding) { - if (messageVersion < 2) throw new AssertionError("Unknown version: " + messageVersion); - else if (messageVersion == 2) return messageWithPadding; - + public static byte[] getStrippedPaddingMessageBody(byte[] messageWithPadding) { int paddingStart = 0; for (int i=messageWithPadding.length-1;i>=0;i--) { @@ -41,10 +32,7 @@ public class PushTransportDetails { return strippedMessage; } - public byte[] getPaddedMessageBody(byte[] messageBody) { - if (messageVersion < 2) throw new AssertionError("Unknown version: " + messageVersion); - else if (messageVersion == 2) return messageBody; - + public static byte[] getPaddedMessageBody(byte[] messageBody) { // NOTE: This is dumb. We have our own padding scheme, but so does the cipher. // The +1 -1 here is to make sure the Cipher has room to add one padding byte, // otherwise it'll add a full 16 extra bytes. @@ -55,7 +43,7 @@ public class PushTransportDetails { return paddedMessage; } - private int getPaddedMessageLength(int messageLength) { + private static int getPaddedMessageLength(int messageLength) { int messageLengthWithTerminator = messageLength + 1; int messagePartCount = messageLengthWithTerminator / 160;