WIP: clean up signal protocols

This commit is contained in:
Ryan ZHAO
2021-02-19 11:03:58 +11:00
parent 13c2995746
commit 958ec690f1
49 changed files with 568 additions and 882 deletions

View File

@@ -278,6 +278,12 @@ message GroupContext {
repeated string members = 4;
optional AttachmentPointer avatar = 5;
repeated string admins = 6;
// Loki - These fields are only used internally for the Android code base.
// This is so that we can differentiate adding/kicking.
// DO NOT USE WHEN SENDING MESSAGES.
repeated string newMembers = 998;
repeated string removedMembers = 999;
}
message ContactDetails {

View File

@@ -1,78 +0,0 @@
package org.session.libsignal.libsignal.loki
import org.session.libsignal.libsignal.DecryptionCallback
import org.session.libsignal.libsignal.SessionCipher
import org.session.libsignal.libsignal.SignalProtocolAddress
import org.session.libsignal.libsignal.protocol.PreKeySignalMessage
import org.session.libsignal.libsignal.protocol.SignalMessage
import org.session.libsignal.libsignal.state.SessionState
import org.session.libsignal.libsignal.state.SignalProtocolStore
/**
* A wrapper class for `SessionCipher`.
* This applies session reset logic on decryption.
*/
class LokiSessionCipher(private val protocolStore: SignalProtocolStore, private var sessionResetProtocol: SessionResetProtocol, val address: SignalProtocolAddress) : SessionCipher(protocolStore, address) {
override fun decrypt(ciphertext: PreKeySignalMessage?, callback: DecryptionCallback?): ByteArray {
// Record the current session state as it may change during decryption
val activeSession = getCurrentSessionState()
if (activeSession == null && ciphertext != null) {
sessionResetProtocol.validatePreKeySignalMessage(address.name, ciphertext)
}
val plainText = super.decrypt(ciphertext, callback)
handleSessionResetRequestIfNeeded(activeSession)
return plainText
}
override fun decrypt(ciphertext: SignalMessage?, callback: DecryptionCallback?): ByteArray {
// Record the current session state as it may change during decryption
val activeSession = getCurrentSessionState()
val plainText = super.decrypt(ciphertext, callback)
handleSessionResetRequestIfNeeded(activeSession)
return plainText
}
private fun getCurrentSessionState(): SessionState? {
val sessionRecord = protocolStore.loadSession(address)
return sessionRecord.sessionState
}
private fun handleSessionResetRequestIfNeeded(oldSession: SessionState?) {
if (oldSession == null) { return }
val publicKey = address.name
val currentSessionResetStatus = sessionResetProtocol.getSessionResetStatus(publicKey)
if (currentSessionResetStatus == SessionResetStatus.NONE) return
val currentSession = getCurrentSessionState()
if (currentSession == null || currentSession.aliceBaseKey?.contentEquals(oldSession.aliceBaseKey) != true) {
if (currentSessionResetStatus == SessionResetStatus.REQUEST_RECEIVED) {
// The other user used an old session to contact us; wait for them to switch to a new one.
restoreSession(oldSession)
} else {
// Our session reset was successful; we initiated one and got a new session back from the other user.
deleteAllSessionsExcept(currentSession)
sessionResetProtocol.setSessionResetStatus(publicKey, SessionResetStatus.NONE)
sessionResetProtocol.onNewSessionAdopted(publicKey, currentSessionResetStatus)
}
} else if (currentSessionResetStatus == SessionResetStatus.REQUEST_RECEIVED) {
// Our session reset was successful; we received a message with the same session from the other user.
deleteAllSessionsExcept(oldSession)
sessionResetProtocol.setSessionResetStatus(publicKey, SessionResetStatus.NONE)
sessionResetProtocol.onNewSessionAdopted(publicKey, currentSessionResetStatus)
}
}
private fun restoreSession(state: SessionState) {
val session = protocolStore.loadSession(address)
session.previousSessionStates.removeAll { it.aliceBaseKey?.contentEquals(state.aliceBaseKey) ?: false }
session.promoteState(state)
protocolStore.storeSession(address, session)
}
private fun deleteAllSessionsExcept(state: SessionState?) {
val sessionRecord = protocolStore.loadSession(address)
sessionRecord.removePreviousSessionStates()
sessionRecord.setState(state ?: SessionState())
protocolStore.storeSession(address, sessionRecord)
}
}

View File

@@ -1,11 +0,0 @@
package org.session.libsignal.libsignal.loki
import org.session.libsignal.libsignal.protocol.PreKeySignalMessage
interface SessionResetProtocol {
fun getSessionResetStatus(publicKey: String): SessionResetStatus
fun setSessionResetStatus(publicKey: String, sessionResetStatus: SessionResetStatus)
fun validatePreKeySignalMessage(publicKey: String, message: PreKeySignalMessage)
fun onNewSessionAdopted(publicKey: String, oldSessionResetStatus: SessionResetStatus)
}

View File

@@ -1,7 +0,0 @@
package org.session.libsignal.libsignal.loki
enum class SessionResetStatus(val rawValue: Int) {
NONE(0),
IN_PROGRESS(1),
REQUEST_RECEIVED(2)
}

View File

@@ -24,8 +24,6 @@ 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.loki.FallbackSessionCipher;
import org.session.libsignal.libsignal.loki.LokiSessionCipher;
import org.session.libsignal.libsignal.loki.SessionResetProtocol;
import org.session.libsignal.libsignal.protocol.CiphertextMessage;
import org.session.libsignal.libsignal.protocol.PreKeySignalMessage;
import org.session.libsignal.libsignal.protocol.SignalMessage;
@@ -51,15 +49,11 @@ import javax.crypto.spec.SecretKeySpec;
public class SealedSessionCipher {
private final SignalProtocolStore signalProtocolStore;
private final SessionResetProtocol sessionResetProtocol;
private final SignalProtocolAddress localAddress;
public SealedSessionCipher(SignalProtocolStore signalProtocolStore,
SessionResetProtocol sessionResetProtocol,
SignalProtocolAddress localAddress)
public SealedSessionCipher(SignalProtocolStore signalProtocolStore, SignalProtocolAddress localAddress)
{
this.signalProtocolStore = signalProtocolStore;
this.sessionResetProtocol = sessionResetProtocol;
this.localAddress = localAddress;
}
@@ -201,8 +195,8 @@ public class SealedSessionCipher {
SignalProtocolAddress sender = new SignalProtocolAddress(message.getSenderCertificate().getSender(), message.getSenderCertificate().getSenderDeviceId());
switch (message.getType()) {
case CiphertextMessage.WHISPER_TYPE: return new LokiSessionCipher(signalProtocolStore, sessionResetProtocol, sender).decrypt(new SignalMessage(message.getContent()));
case CiphertextMessage.PREKEY_TYPE: return new LokiSessionCipher(signalProtocolStore, sessionResetProtocol, sender).decrypt(new PreKeySignalMessage(message.getContent()));
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()));
case CiphertextMessage.FALLBACK_MESSAGE_TYPE: {
try {
byte[] privateKey = signalProtocolStore.getIdentityKeyPair().getPrivateKey().serialize();

View File

@@ -6,12 +6,10 @@
package org.session.libsignal.service.api;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.jetbrains.annotations.Nullable;
import org.session.libsignal.libsignal.ecc.ECKeyPair;
import org.session.libsignal.utilities.logging.Log;
import org.session.libsignal.libsignal.loki.SessionResetProtocol;
import org.session.libsignal.libsignal.state.SignalProtocolStore;
import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.crypto.AttachmentCipherOutputStream;
@@ -67,24 +65,20 @@ import org.session.libsignal.service.loki.database.LokiOpenGroupDatabaseProtocol
import org.session.libsignal.service.loki.database.LokiPreKeyBundleDatabaseProtocol;
import org.session.libsignal.service.loki.database.LokiThreadDatabaseProtocol;
import org.session.libsignal.service.loki.database.LokiUserDatabaseProtocol;
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities;
import org.session.libsignal.service.loki.protocol.sessionmanagement.SessionManagementProtocol;
import org.session.libsignal.service.loki.utilities.TTLUtilities;
import org.session.libsignal.service.loki.utilities.Broadcaster;
import org.session.libsignal.service.loki.utilities.HexEncodingKt;
import org.session.libsignal.service.loki.utilities.PlaintextOutputStreamFactory;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import kotlin.Unit;
@@ -100,10 +94,8 @@ public class SignalServiceMessageSender {
private static final String TAG = SignalServiceMessageSender.class.getSimpleName();
private final PushServiceSocket socket;
private final SignalProtocolStore store;
private final SignalServiceAddress localAddress;
private final Optional<EventListener> eventListener;
private final AtomicReference<Optional<SignalServiceMessagePipe>> pipe;
private final AtomicReference<Optional<SignalServiceMessagePipe>> unidentifiedPipe;
@@ -113,9 +105,7 @@ public class SignalServiceMessageSender {
private final LokiAPIDatabaseProtocol apiDatabase;
private final LokiThreadDatabaseProtocol threadDatabase;
private final LokiMessageDatabaseProtocol messageDatabase;
private final LokiPreKeyBundleDatabaseProtocol preKeyBundleDatabase;
private final SessionProtocol sessionProtocolImpl;
private final SessionResetProtocol sessionResetImpl;
private final LokiUserDatabaseProtocol userDatabase;
private final LokiOpenGroupDatabaseProtocol openGroupDatabase;
private final Broadcaster broadcaster;
@@ -123,65 +113,48 @@ public class SignalServiceMessageSender {
/**
* Construct a SignalServiceMessageSender.
*
* @param urls The URL of the Signal Service.
* @param user The Signal Service username (eg phone number).
* @param password The Signal Service user password.
* @param store The SignalProtocolStore.
* @param eventListener An optional event listener, which fires whenever sessions are
* setup or torn down for a recipient.
*/
public SignalServiceMessageSender(SignalServiceConfiguration urls,
String user, String password,
public SignalServiceMessageSender(String user, String password,
SignalProtocolStore store,
String userAgent,
Optional<SignalServiceMessagePipe> pipe,
Optional<SignalServiceMessagePipe> unidentifiedPipe,
Optional<EventListener> eventListener,
String userPublicKey,
LokiAPIDatabaseProtocol apiDatabase,
LokiThreadDatabaseProtocol threadDatabase,
LokiMessageDatabaseProtocol messageDatabase,
LokiPreKeyBundleDatabaseProtocol preKeyBundleDatabase,
SessionProtocol sessionProtocolImpl,
SessionResetProtocol sessionResetImpl,
LokiUserDatabaseProtocol userDatabase,
LokiOpenGroupDatabaseProtocol openGroupDatabase,
Broadcaster broadcaster)
{
this(urls, new StaticCredentialsProvider(user, password, null), store, userAgent, pipe, unidentifiedPipe, eventListener, userPublicKey, apiDatabase, threadDatabase, messageDatabase, preKeyBundleDatabase, sessionProtocolImpl, sessionResetImpl, userDatabase, openGroupDatabase, broadcaster);
this(new StaticCredentialsProvider(user, password, null), store, pipe, unidentifiedPipe, userPublicKey, apiDatabase, threadDatabase, messageDatabase, sessionProtocolImpl, userDatabase, openGroupDatabase, broadcaster);
}
public SignalServiceMessageSender(SignalServiceConfiguration urls,
CredentialsProvider credentialsProvider,
public SignalServiceMessageSender(CredentialsProvider credentialsProvider,
SignalProtocolStore store,
String userAgent,
Optional<SignalServiceMessagePipe> pipe,
Optional<SignalServiceMessagePipe> unidentifiedPipe,
Optional<EventListener> eventListener,
String userPublicKey,
LokiAPIDatabaseProtocol apiDatabase,
LokiThreadDatabaseProtocol threadDatabase,
LokiMessageDatabaseProtocol messageDatabase,
LokiPreKeyBundleDatabaseProtocol preKeyBundleDatabase,
SessionProtocol sessionProtocolImpl,
SessionResetProtocol sessionResetImpl,
LokiUserDatabaseProtocol userDatabase,
LokiOpenGroupDatabaseProtocol openGroupDatabase,
Broadcaster broadcaster)
{
this.socket = new PushServiceSocket(urls, credentialsProvider, userAgent);
this.store = store;
this.localAddress = new SignalServiceAddress(credentialsProvider.getUser());
this.pipe = new AtomicReference<>(pipe);
this.unidentifiedPipe = new AtomicReference<>(unidentifiedPipe);
this.eventListener = eventListener;
this.userPublicKey = userPublicKey;
this.apiDatabase = apiDatabase;
this.threadDatabase = threadDatabase;
this.messageDatabase = messageDatabase;
this.preKeyBundleDatabase = preKeyBundleDatabase;
this.sessionProtocolImpl = sessionProtocolImpl;
this.sessionResetImpl = sessionResetImpl;
this.userDatabase = userDatabase;
this.openGroupDatabase = openGroupDatabase;
this.broadcaster = broadcaster;

View File

@@ -10,31 +10,11 @@ import com.google.protobuf.InvalidProtocolBufferException;
import org.session.libsignal.libsignal.ecc.ECKeyPair;
import org.session.libsignal.metadata.InvalidMetadataMessageException;
import org.session.libsignal.metadata.InvalidMetadataVersionException;
import org.session.libsignal.metadata.ProtocolDuplicateMessageException;
import org.session.libsignal.metadata.ProtocolInvalidKeyException;
import org.session.libsignal.metadata.ProtocolInvalidKeyIdException;
import org.session.libsignal.metadata.ProtocolInvalidMessageException;
import org.session.libsignal.metadata.ProtocolInvalidVersionException;
import org.session.libsignal.metadata.ProtocolLegacyMessageException;
import org.session.libsignal.metadata.ProtocolNoSessionException;
import org.session.libsignal.metadata.ProtocolUntrustedIdentityException;
import org.session.libsignal.metadata.SealedSessionCipher;
import org.session.libsignal.metadata.SelfSendException;
import org.session.libsignal.libsignal.DuplicateMessageException;
import org.session.libsignal.libsignal.InvalidKeyException;
import org.session.libsignal.libsignal.InvalidKeyIdException;
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.loki.LokiSessionCipher;
import org.session.libsignal.libsignal.loki.SessionResetProtocol;
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.guava.Optional;
import org.session.libsignal.service.api.messages.SignalServiceAttachment;
@@ -60,7 +40,6 @@ import org.session.libsignal.service.loki.api.crypto.SessionProtocol;
import org.session.libsignal.service.loki.api.crypto.SessionProtocolUtilities;
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
@@ -77,19 +56,16 @@ public class SignalServiceCipher {
private static final String TAG = SignalServiceCipher.class.getSimpleName();
private final SignalProtocolStore signalProtocolStore;
private final SessionResetProtocol sessionResetProtocol;
private final SignalServiceAddress localAddress;
private final SessionProtocol sessionProtocolImpl;
private final LokiAPIDatabaseProtocol apiDB;
public SignalServiceCipher(SignalServiceAddress localAddress,
SignalProtocolStore signalProtocolStore,
SessionResetProtocol sessionResetProtocol,
SessionProtocol sessionProtocolImpl,
LokiAPIDatabaseProtocol apiDB)
{
this.signalProtocolStore = signalProtocolStore;
this.sessionResetProtocol = sessionResetProtocol;
this.localAddress = localAddress;
this.sessionProtocolImpl = sessionProtocolImpl;
this.apiDB = apiDB;
@@ -160,8 +136,8 @@ public class SignalServiceCipher {
protected Plaintext decrypt(SignalServiceEnvelope envelope, byte[] ciphertext) throws InvalidMetadataMessageException
{
SignalProtocolAddress sourceAddress = new SignalProtocolAddress(envelope.getSource(), envelope.getSourceDevice());
SessionCipher sessionCipher = new LokiSessionCipher(signalProtocolStore, sessionResetProtocol, sourceAddress);
SealedSessionCipher sealedSessionCipher = new SealedSessionCipher(signalProtocolStore, sessionResetProtocol, new SignalProtocolAddress(localAddress.getNumber(), 1));
SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, sourceAddress);
SealedSessionCipher sealedSessionCipher = new SealedSessionCipher(signalProtocolStore, new SignalProtocolAddress(localAddress.getNumber(), 1));
byte[] paddedMessage;
Metadata metadata;

View File

@@ -7,9 +7,7 @@
package org.session.libsignal.service.api.messages;
import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.messages.calls.SignalServiceCallMessage;
import org.session.libsignal.service.internal.push.SignalServiceProtos;
import org.session.libsignal.service.loki.protocol.sessionmanagement.PreKeyBundleMessage;
public class SignalServiceContent {
private final String sender;
@@ -24,7 +22,6 @@ public class SignalServiceContent {
// Loki
public Optional<SignalServiceProtos.Content> configurationMessageProto = Optional.absent();
public Optional<PreKeyBundleMessage> preKeyBundleMessage = Optional.absent();
public Optional<String> senderDisplayName = Optional.absent();
public Optional<String> senderProfilePictureURL = Optional.absent();

View File

@@ -11,7 +11,7 @@ import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.messages.shared.SharedContact;
import org.session.libsignal.service.api.push.SignalServiceAddress;
import org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.ClosedGroupControlMessage;
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities;
import org.session.libsignal.service.loki.utilities.TTLUtilities;
import java.util.LinkedList;
import java.util.List;

View File

@@ -1,6 +1,6 @@
package org.session.libsignal.service.api.messages;
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities;
import org.session.libsignal.service.loki.utilities.TTLUtilities;
public class SignalServiceNullMessage {

View File

@@ -1,7 +1,7 @@
package org.session.libsignal.service.api.messages;
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities;
import org.session.libsignal.service.loki.utilities.TTLUtilities;
import java.util.List;

View File

@@ -1,7 +1,6 @@
package org.session.libsignal.service.api.messages;
import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities;
import org.session.libsignal.service.loki.utilities.TTLUtilities;
public class SignalServiceTypingMessage {

View File

@@ -1,7 +1,7 @@
package org.session.libsignal.service.api.messages.calls;
import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities;
import org.session.libsignal.service.loki.utilities.TTLUtilities;
import java.util.LinkedList;
import java.util.List;

View File

@@ -24483,6 +24483,70 @@ public final class SignalServiceProtos {
*/
com.google.protobuf.ByteString
getAdminsBytes(int index);
// repeated string newMembers = 998;
/**
* <code>repeated string newMembers = 998;</code>
*
* <pre>
* Loki - These fields are only used internally for the Android code base.
* This is so that we can differentiate adding/kicking.
* DO NOT USE WHEN SENDING MESSAGES.
* </pre>
*/
java.util.List<java.lang.String>
getNewMembersList();
/**
* <code>repeated string newMembers = 998;</code>
*
* <pre>
* Loki - These fields are only used internally for the Android code base.
* This is so that we can differentiate adding/kicking.
* DO NOT USE WHEN SENDING MESSAGES.
* </pre>
*/
int getNewMembersCount();
/**
* <code>repeated string newMembers = 998;</code>
*
* <pre>
* Loki - These fields are only used internally for the Android code base.
* This is so that we can differentiate adding/kicking.
* DO NOT USE WHEN SENDING MESSAGES.
* </pre>
*/
java.lang.String getNewMembers(int index);
/**
* <code>repeated string newMembers = 998;</code>
*
* <pre>
* Loki - These fields are only used internally for the Android code base.
* This is so that we can differentiate adding/kicking.
* DO NOT USE WHEN SENDING MESSAGES.
* </pre>
*/
com.google.protobuf.ByteString
getNewMembersBytes(int index);
// repeated string removedMembers = 999;
/**
* <code>repeated string removedMembers = 999;</code>
*/
java.util.List<java.lang.String>
getRemovedMembersList();
/**
* <code>repeated string removedMembers = 999;</code>
*/
int getRemovedMembersCount();
/**
* <code>repeated string removedMembers = 999;</code>
*/
java.lang.String getRemovedMembers(int index);
/**
* <code>repeated string removedMembers = 999;</code>
*/
com.google.protobuf.ByteString
getRemovedMembersBytes(int index);
}
/**
* Protobuf type {@code signalservice.GroupContext}
@@ -24585,6 +24649,22 @@ public final class SignalServiceProtos {
admins_.add(input.readBytes());
break;
}
case 7986: {
if (!((mutable_bitField0_ & 0x00000040) == 0x00000040)) {
newMembers_ = new com.google.protobuf.LazyStringArrayList();
mutable_bitField0_ |= 0x00000040;
}
newMembers_.add(input.readBytes());
break;
}
case 7994: {
if (!((mutable_bitField0_ & 0x00000080) == 0x00000080)) {
removedMembers_ = new com.google.protobuf.LazyStringArrayList();
mutable_bitField0_ |= 0x00000080;
}
removedMembers_.add(input.readBytes());
break;
}
}
}
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -24599,6 +24679,12 @@ public final class SignalServiceProtos {
if (((mutable_bitField0_ & 0x00000020) == 0x00000020)) {
admins_ = new com.google.protobuf.UnmodifiableLazyStringList(admins_);
}
if (((mutable_bitField0_ & 0x00000040) == 0x00000040)) {
newMembers_ = new com.google.protobuf.UnmodifiableLazyStringList(newMembers_);
}
if (((mutable_bitField0_ & 0x00000080) == 0x00000080)) {
removedMembers_ = new com.google.protobuf.UnmodifiableLazyStringList(removedMembers_);
}
this.unknownFields = unknownFields.build();
makeExtensionsImmutable();
}
@@ -24913,6 +24999,90 @@ public final class SignalServiceProtos {
return admins_.getByteString(index);
}
// repeated string newMembers = 998;
public static final int NEWMEMBERS_FIELD_NUMBER = 998;
private com.google.protobuf.LazyStringList newMembers_;
/**
* <code>repeated string newMembers = 998;</code>
*
* <pre>
* Loki - These fields are only used internally for the Android code base.
* This is so that we can differentiate adding/kicking.
* DO NOT USE WHEN SENDING MESSAGES.
* </pre>
*/
public java.util.List<java.lang.String>
getNewMembersList() {
return newMembers_;
}
/**
* <code>repeated string newMembers = 998;</code>
*
* <pre>
* Loki - These fields are only used internally for the Android code base.
* This is so that we can differentiate adding/kicking.
* DO NOT USE WHEN SENDING MESSAGES.
* </pre>
*/
public int getNewMembersCount() {
return newMembers_.size();
}
/**
* <code>repeated string newMembers = 998;</code>
*
* <pre>
* Loki - These fields are only used internally for the Android code base.
* This is so that we can differentiate adding/kicking.
* DO NOT USE WHEN SENDING MESSAGES.
* </pre>
*/
public java.lang.String getNewMembers(int index) {
return newMembers_.get(index);
}
/**
* <code>repeated string newMembers = 998;</code>
*
* <pre>
* Loki - These fields are only used internally for the Android code base.
* This is so that we can differentiate adding/kicking.
* DO NOT USE WHEN SENDING MESSAGES.
* </pre>
*/
public com.google.protobuf.ByteString
getNewMembersBytes(int index) {
return newMembers_.getByteString(index);
}
// repeated string removedMembers = 999;
public static final int REMOVEDMEMBERS_FIELD_NUMBER = 999;
private com.google.protobuf.LazyStringList removedMembers_;
/**
* <code>repeated string removedMembers = 999;</code>
*/
public java.util.List<java.lang.String>
getRemovedMembersList() {
return removedMembers_;
}
/**
* <code>repeated string removedMembers = 999;</code>
*/
public int getRemovedMembersCount() {
return removedMembers_.size();
}
/**
* <code>repeated string removedMembers = 999;</code>
*/
public java.lang.String getRemovedMembers(int index) {
return removedMembers_.get(index);
}
/**
* <code>repeated string removedMembers = 999;</code>
*/
public com.google.protobuf.ByteString
getRemovedMembersBytes(int index) {
return removedMembers_.getByteString(index);
}
private void initFields() {
id_ = com.google.protobuf.ByteString.EMPTY;
type_ = org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Type.UNKNOWN;
@@ -24920,6 +25090,8 @@ public final class SignalServiceProtos {
members_ = com.google.protobuf.LazyStringArrayList.EMPTY;
avatar_ = org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer.getDefaultInstance();
admins_ = com.google.protobuf.LazyStringArrayList.EMPTY;
newMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY;
removedMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY;
}
private byte memoizedIsInitialized = -1;
public final boolean isInitialized() {
@@ -24951,6 +25123,12 @@ public final class SignalServiceProtos {
for (int i = 0; i < admins_.size(); i++) {
output.writeBytes(6, admins_.getByteString(i));
}
for (int i = 0; i < newMembers_.size(); i++) {
output.writeBytes(998, newMembers_.getByteString(i));
}
for (int i = 0; i < removedMembers_.size(); i++) {
output.writeBytes(999, removedMembers_.getByteString(i));
}
getUnknownFields().writeTo(output);
}
@@ -24994,6 +25172,24 @@ public final class SignalServiceProtos {
size += dataSize;
size += 1 * getAdminsList().size();
}
{
int dataSize = 0;
for (int i = 0; i < newMembers_.size(); i++) {
dataSize += com.google.protobuf.CodedOutputStream
.computeBytesSizeNoTag(newMembers_.getByteString(i));
}
size += dataSize;
size += 2 * getNewMembersList().size();
}
{
int dataSize = 0;
for (int i = 0; i < removedMembers_.size(); i++) {
dataSize += com.google.protobuf.CodedOutputStream
.computeBytesSizeNoTag(removedMembers_.getByteString(i));
}
size += dataSize;
size += 2 * getRemovedMembersList().size();
}
size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size;
return size;
@@ -25127,6 +25323,10 @@ public final class SignalServiceProtos {
bitField0_ = (bitField0_ & ~0x00000010);
admins_ = com.google.protobuf.LazyStringArrayList.EMPTY;
bitField0_ = (bitField0_ & ~0x00000020);
newMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY;
bitField0_ = (bitField0_ & ~0x00000040);
removedMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY;
bitField0_ = (bitField0_ & ~0x00000080);
return this;
}
@@ -25187,6 +25387,18 @@ public final class SignalServiceProtos {
bitField0_ = (bitField0_ & ~0x00000020);
}
result.admins_ = admins_;
if (((bitField0_ & 0x00000040) == 0x00000040)) {
newMembers_ = new com.google.protobuf.UnmodifiableLazyStringList(
newMembers_);
bitField0_ = (bitField0_ & ~0x00000040);
}
result.newMembers_ = newMembers_;
if (((bitField0_ & 0x00000080) == 0x00000080)) {
removedMembers_ = new com.google.protobuf.UnmodifiableLazyStringList(
removedMembers_);
bitField0_ = (bitField0_ & ~0x00000080);
}
result.removedMembers_ = removedMembers_;
result.bitField0_ = to_bitField0_;
onBuilt();
return result;
@@ -25237,6 +25449,26 @@ public final class SignalServiceProtos {
}
onChanged();
}
if (!other.newMembers_.isEmpty()) {
if (newMembers_.isEmpty()) {
newMembers_ = other.newMembers_;
bitField0_ = (bitField0_ & ~0x00000040);
} else {
ensureNewMembersIsMutable();
newMembers_.addAll(other.newMembers_);
}
onChanged();
}
if (!other.removedMembers_.isEmpty()) {
if (removedMembers_.isEmpty()) {
removedMembers_ = other.removedMembers_;
bitField0_ = (bitField0_ & ~0x00000080);
} else {
ensureRemovedMembersIsMutable();
removedMembers_.addAll(other.removedMembers_);
}
onChanged();
}
this.mergeUnknownFields(other.getUnknownFields());
return this;
}
@@ -25745,6 +25977,246 @@ public final class SignalServiceProtos {
return this;
}
// repeated string newMembers = 998;
private com.google.protobuf.LazyStringList newMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY;
private void ensureNewMembersIsMutable() {
if (!((bitField0_ & 0x00000040) == 0x00000040)) {
newMembers_ = new com.google.protobuf.LazyStringArrayList(newMembers_);
bitField0_ |= 0x00000040;
}
}
/**
* <code>repeated string newMembers = 998;</code>
*
* <pre>
* Loki - These fields are only used internally for the Android code base.
* This is so that we can differentiate adding/kicking.
* DO NOT USE WHEN SENDING MESSAGES.
* </pre>
*/
public java.util.List<java.lang.String>
getNewMembersList() {
return java.util.Collections.unmodifiableList(newMembers_);
}
/**
* <code>repeated string newMembers = 998;</code>
*
* <pre>
* Loki - These fields are only used internally for the Android code base.
* This is so that we can differentiate adding/kicking.
* DO NOT USE WHEN SENDING MESSAGES.
* </pre>
*/
public int getNewMembersCount() {
return newMembers_.size();
}
/**
* <code>repeated string newMembers = 998;</code>
*
* <pre>
* Loki - These fields are only used internally for the Android code base.
* This is so that we can differentiate adding/kicking.
* DO NOT USE WHEN SENDING MESSAGES.
* </pre>
*/
public java.lang.String getNewMembers(int index) {
return newMembers_.get(index);
}
/**
* <code>repeated string newMembers = 998;</code>
*
* <pre>
* Loki - These fields are only used internally for the Android code base.
* This is so that we can differentiate adding/kicking.
* DO NOT USE WHEN SENDING MESSAGES.
* </pre>
*/
public com.google.protobuf.ByteString
getNewMembersBytes(int index) {
return newMembers_.getByteString(index);
}
/**
* <code>repeated string newMembers = 998;</code>
*
* <pre>
* Loki - These fields are only used internally for the Android code base.
* This is so that we can differentiate adding/kicking.
* DO NOT USE WHEN SENDING MESSAGES.
* </pre>
*/
public Builder setNewMembers(
int index, java.lang.String value) {
if (value == null) {
throw new NullPointerException();
}
ensureNewMembersIsMutable();
newMembers_.set(index, value);
onChanged();
return this;
}
/**
* <code>repeated string newMembers = 998;</code>
*
* <pre>
* Loki - These fields are only used internally for the Android code base.
* This is so that we can differentiate adding/kicking.
* DO NOT USE WHEN SENDING MESSAGES.
* </pre>
*/
public Builder addNewMembers(
java.lang.String value) {
if (value == null) {
throw new NullPointerException();
}
ensureNewMembersIsMutable();
newMembers_.add(value);
onChanged();
return this;
}
/**
* <code>repeated string newMembers = 998;</code>
*
* <pre>
* Loki - These fields are only used internally for the Android code base.
* This is so that we can differentiate adding/kicking.
* DO NOT USE WHEN SENDING MESSAGES.
* </pre>
*/
public Builder addAllNewMembers(
java.lang.Iterable<java.lang.String> values) {
ensureNewMembersIsMutable();
super.addAll(values, newMembers_);
onChanged();
return this;
}
/**
* <code>repeated string newMembers = 998;</code>
*
* <pre>
* Loki - These fields are only used internally for the Android code base.
* This is so that we can differentiate adding/kicking.
* DO NOT USE WHEN SENDING MESSAGES.
* </pre>
*/
public Builder clearNewMembers() {
newMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY;
bitField0_ = (bitField0_ & ~0x00000040);
onChanged();
return this;
}
/**
* <code>repeated string newMembers = 998;</code>
*
* <pre>
* Loki - These fields are only used internally for the Android code base.
* This is so that we can differentiate adding/kicking.
* DO NOT USE WHEN SENDING MESSAGES.
* </pre>
*/
public Builder addNewMembersBytes(
com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
ensureNewMembersIsMutable();
newMembers_.add(value);
onChanged();
return this;
}
// repeated string removedMembers = 999;
private com.google.protobuf.LazyStringList removedMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY;
private void ensureRemovedMembersIsMutable() {
if (!((bitField0_ & 0x00000080) == 0x00000080)) {
removedMembers_ = new com.google.protobuf.LazyStringArrayList(removedMembers_);
bitField0_ |= 0x00000080;
}
}
/**
* <code>repeated string removedMembers = 999;</code>
*/
public java.util.List<java.lang.String>
getRemovedMembersList() {
return java.util.Collections.unmodifiableList(removedMembers_);
}
/**
* <code>repeated string removedMembers = 999;</code>
*/
public int getRemovedMembersCount() {
return removedMembers_.size();
}
/**
* <code>repeated string removedMembers = 999;</code>
*/
public java.lang.String getRemovedMembers(int index) {
return removedMembers_.get(index);
}
/**
* <code>repeated string removedMembers = 999;</code>
*/
public com.google.protobuf.ByteString
getRemovedMembersBytes(int index) {
return removedMembers_.getByteString(index);
}
/**
* <code>repeated string removedMembers = 999;</code>
*/
public Builder setRemovedMembers(
int index, java.lang.String value) {
if (value == null) {
throw new NullPointerException();
}
ensureRemovedMembersIsMutable();
removedMembers_.set(index, value);
onChanged();
return this;
}
/**
* <code>repeated string removedMembers = 999;</code>
*/
public Builder addRemovedMembers(
java.lang.String value) {
if (value == null) {
throw new NullPointerException();
}
ensureRemovedMembersIsMutable();
removedMembers_.add(value);
onChanged();
return this;
}
/**
* <code>repeated string removedMembers = 999;</code>
*/
public Builder addAllRemovedMembers(
java.lang.Iterable<java.lang.String> values) {
ensureRemovedMembersIsMutable();
super.addAll(values, removedMembers_);
onChanged();
return this;
}
/**
* <code>repeated string removedMembers = 999;</code>
*/
public Builder clearRemovedMembers() {
removedMembers_ = com.google.protobuf.LazyStringArrayList.EMPTY;
bitField0_ = (bitField0_ & ~0x00000080);
onChanged();
return this;
}
/**
* <code>repeated string removedMembers = 999;</code>
*/
public Builder addRemovedMembersBytes(
com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
ensureRemovedMembersIsMutable();
removedMembers_.add(value);
onChanged();
return this;
}
// @@protoc_insertion_point(builder_scope:signalservice.GroupContext)
}
@@ -30486,28 +30958,30 @@ public final class SignalServiceProtos {
"thumbnail\030\005 \001(\014\022\016\n\006digest\030\006 \001(\014\022\020\n\010fileN" +
"ame\030\007 \001(\t\022\r\n\005flags\030\010 \001(\r\022\r\n\005width\030\t \001(\r\022" +
"\016\n\006height\030\n \001(\r\022\017\n\007caption\030\013 \001(\t\022\013\n\003url\030",
"e \001(\t\"\032\n\005Flags\022\021\n\rVOICE_MESSAGE\020\001\"\365\001\n\014Gr" +
"e \001(\t\"\032\n\005Flags\022\021\n\rVOICE_MESSAGE\020\001\"\243\002\n\014Gr" +
"oupContext\022\n\n\002id\030\001 \001(\014\022.\n\004type\030\002 \001(\0162 .s" +
"ignalservice.GroupContext.Type\022\014\n\004name\030\003" +
" \001(\t\022\017\n\007members\030\004 \003(\t\0220\n\006avatar\030\005 \001(\0132 ." +
"signalservice.AttachmentPointer\022\016\n\006admin" +
"s\030\006 \003(\t\"H\n\004Type\022\013\n\007UNKNOWN\020\000\022\n\n\006UPDATE\020\001" +
"\022\013\n\007DELIVER\020\002\022\010\n\004QUIT\020\003\022\020\n\014REQUEST_INFO\020" +
"\004\"\356\001\n\016ContactDetails\022\016\n\006number\030\001 \001(\t\022\014\n\004" +
"name\030\002 \001(\t\0224\n\006avatar\030\003 \001(\0132$.signalservi" +
"ce.ContactDetails.Avatar\022\r\n\005color\030\004 \001(\t\022",
"\022\n\nprofileKey\030\006 \001(\014\022\017\n\007blocked\030\007 \001(\010\022\023\n\013" +
"expireTimer\030\010 \001(\r\022\020\n\010nickname\030e \001(\t\032-\n\006A" +
"vatar\022\023\n\013contentType\030\001 \001(\t\022\016\n\006length\030\002 \001" +
"(\r\"\367\001\n\014GroupDetails\022\n\n\002id\030\001 \001(\014\022\014\n\004name\030" +
"\002 \001(\t\022\017\n\007members\030\003 \003(\t\0222\n\006avatar\030\004 \001(\0132\"" +
".signalservice.GroupDetails.Avatar\022\024\n\006ac" +
"tive\030\005 \001(\010:\004true\022\023\n\013expireTimer\030\006 \001(\r\022\r\n" +
"\005color\030\007 \001(\t\022\017\n\007blocked\030\010 \001(\010\022\016\n\006admins\030" +
"\t \003(\t\032-\n\006Avatar\022\023\n\013contentType\030\001 \001(\t\022\016\n\006" +
"length\030\002 \001(\r\"\"\n\016PublicChatInfo\022\020\n\010server",
"ID\030\001 \001(\004BB\n+org.session.libsignal.servic" +
"e.internal.pushB\023SignalServiceProtos"
"s\030\006 \003(\t\022\023\n\nnewMembers\030\346\007 \003(\t\022\027\n\016removedM" +
"embers\030\347\007 \003(\t\"H\n\004Type\022\013\n\007UNKNOWN\020\000\022\n\n\006UP" +
"DATE\020\001\022\013\n\007DELIVER\020\002\022\010\n\004QUIT\020\003\022\020\n\014REQUEST" +
"_INFO\020\004\"\356\001\n\016ContactDetails\022\016\n\006number\030\001 \001" +
"(\t\022\014\n\004name\030\002 \001(\t\0224\n\006avatar\030\003 \001(\0132$.signa",
"lservice.ContactDetails.Avatar\022\r\n\005color\030" +
"\004 \001(\t\022\022\n\nprofileKey\030\006 \001(\014\022\017\n\007blocked\030\007 \001" +
"(\010\022\023\n\013expireTimer\030\010 \001(\r\022\020\n\010nickname\030e \001(" +
"\t\032-\n\006Avatar\022\023\n\013contentType\030\001 \001(\t\022\016\n\006leng" +
"th\030\002 \001(\r\"\367\001\n\014GroupDetails\022\n\n\002id\030\001 \001(\014\022\014\n" +
"\004name\030\002 \001(\t\022\017\n\007members\030\003 \003(\t\0222\n\006avatar\030\004" +
" \001(\0132\".signalservice.GroupDetails.Avatar" +
"\022\024\n\006active\030\005 \001(\010:\004true\022\023\n\013expireTimer\030\006 " +
"\001(\r\022\r\n\005color\030\007 \001(\t\022\017\n\007blocked\030\010 \001(\010\022\016\n\006a" +
"dmins\030\t \003(\t\032-\n\006Avatar\022\023\n\013contentType\030\001 \001",
"(\t\022\016\n\006length\030\002 \001(\r\"\"\n\016PublicChatInfo\022\020\n\010" +
"serverID\030\001 \001(\004BB\n+org.session.libsignal." +
"service.internal.pushB\023SignalServiceProt" +
"os"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@@ -30651,7 +31125,7 @@ public final class SignalServiceProtos {
internal_static_signalservice_GroupContext_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_signalservice_GroupContext_descriptor,
new java.lang.String[] { "Id", "Type", "Name", "Members", "Avatar", "Admins", });
new java.lang.String[] { "Id", "Type", "Name", "Members", "Avatar", "Admins", "NewMembers", "RemovedMembers", });
internal_static_signalservice_ContactDetails_descriptor =
getDescriptor().getMessageTypes().get(10);
internal_static_signalservice_ContactDetails_fieldAccessorTable = new

View File

@@ -5,7 +5,7 @@ import nl.komponents.kovenant.deferred
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.utilities.Base64
import org.session.libsignal.service.loki.api.crypto.ProofOfWork
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities
import org.session.libsignal.service.loki.utilities.TTLUtilities
import org.session.libsignal.utilities.ThreadUtils
import org.session.libsignal.service.loki.utilities.prettifiedDescription

View File

@@ -1,24 +0,0 @@
package org.session.libsignal.service.loki.protocol.meta
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
public class SessionMetaProtocol(private val apiDatabase: LokiAPIDatabaseProtocol, private val userPublicKey: String) {
// region Initialization
companion object {
public lateinit var shared: SessionMetaProtocol
public fun configureIfNeeded(apiDatabase: LokiAPIDatabaseProtocol, userPublicKey: String) {
if (::shared.isInitialized) { return; }
shared = SessionMetaProtocol(apiDatabase, userPublicKey)
}
}
// endregion
// region Utilities
public fun isNoteToSelf(publicKey: String): Boolean {
return userPublicKey == publicKey // return MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey).contains(publicKey)
}
// endregion
}

View File

@@ -1,23 +0,0 @@
package org.session.libsignal.service.loki.protocol.sessionmanagement
import org.session.libsignal.libsignal.IdentityKey
import org.session.libsignal.libsignal.ecc.Curve
import org.session.libsignal.libsignal.state.PreKeyBundle
data class PreKeyBundleMessage(
val identityKey: ByteArray,
val deviceID: Int,
val preKeyID: Int,
val signedPreKeyID: Int,
val preKey: ByteArray,
val signedPreKey: ByteArray,
val signedPreKeySignature: ByteArray
) {
constructor(preKeyBundle: PreKeyBundle) : this(preKeyBundle.identityKey.serialize(), preKeyBundle.deviceId, preKeyBundle.preKeyId,
preKeyBundle.signedPreKeyId, preKeyBundle.preKey.serialize(), preKeyBundle.signedPreKey.serialize(), preKeyBundle.signedPreKeySignature)
fun getPreKeyBundle(registrationID: Int): PreKeyBundle {
return PreKeyBundle(registrationID, deviceID, preKeyID, Curve.decodePoint(preKey, 0), signedPreKeyID, Curve.decodePoint(signedPreKey, 0), signedPreKeySignature, IdentityKey(identityKey, 0))
}
}

View File

@@ -1,53 +0,0 @@
package org.session.libsignal.service.loki.protocol.sessionmanagement
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.libsignal.loki.SessionResetProtocol
import org.session.libsignal.libsignal.loki.SessionResetStatus
import org.session.libsignal.libsignal.util.guava.Optional
import org.session.libsignal.service.api.SignalServiceMessageSender
import org.session.libsignal.service.api.push.SignalServiceAddress
public class SessionManagementProtocol(private val sessionResetImpl: SessionResetProtocol, private val delegate: SessionManagementProtocolDelegate) {
// region Initialization
companion object {
public lateinit var shared: SessionManagementProtocol
public fun configureIfNeeded(sessionResetImpl: SessionResetProtocol, delegate: SessionManagementProtocolDelegate) {
if (::shared.isInitialized) { return; }
shared = SessionManagementProtocol(sessionResetImpl, delegate)
}
}
// endregion
// region Sending
/**
* Called after an end session message is sent.
*/
public fun setSessionResetStatusToInProgressIfNeeded(recipient: SignalServiceAddress, eventListener: Optional<SignalServiceMessageSender.EventListener>) {
val publicKey = recipient.number
val sessionResetStatus = sessionResetImpl.getSessionResetStatus(publicKey)
if (sessionResetStatus == SessionResetStatus.REQUEST_RECEIVED) { return }
Log.d("Loki", "Starting session reset")
sessionResetImpl.setSessionResetStatus(publicKey, SessionResetStatus.IN_PROGRESS)
if (!eventListener.isPresent) { return }
eventListener.get().onSecurityEvent(recipient)
}
public fun repairSessionIfNeeded(recipient: SignalServiceAddress, isClosedGroup: Boolean) {
val publicKey = recipient.number
if (!isClosedGroup) { return }
delegate.sendSessionRequestIfNeeded(publicKey)
}
public fun shouldIgnoreMissingPreKeyBundleException(isClosedGroup: Boolean): Boolean {
// When a closed group is created, members try to establish sessions with eachother in the background through
// session requests. Until ALL users those session requests were sent to have come online, stored the pre key
// bundles contained in the session requests and replied with background messages to finalize the session
// creation, a given user won't be able to successfully send a message to all members of a group. This check
// is so that until we can do better on this front the user at least won't see this as an error in the UI.
return isClosedGroup
}
// endregion
}

View File

@@ -1,6 +0,0 @@
package org.session.libsignal.service.loki.protocol.sessionmanagement
interface SessionManagementProtocolDelegate {
fun sendSessionRequestIfNeeded(publicKey: String)
}

View File

@@ -1,4 +1,4 @@
package org.session.libsignal.service.loki.protocol.meta
package org.session.libsignal.service.loki.utilities
public object TTLUtilities {

View File

@@ -1,3 +1,3 @@
package org.session.libsignal.service.loki.protocol.mentions
package org.session.libsignal.service.loki.utilities.mentions
data class Mention(val publicKey: String, val displayName: String)

View File

@@ -1,4 +1,4 @@
package org.session.libsignal.service.loki.protocol.mentions
package org.session.libsignal.service.loki.utilities.mentions
import org.session.libsignal.service.loki.database.LokiThreadDatabaseProtocol
import org.session.libsignal.service.loki.database.LokiUserDatabaseProtocol