kopia lustrzana https://github.com/ryukoposting/Signal-Android
Perform individual decryptions inside a database transaction.
Required a lot of random locking work to prevent deadlocking, but overall this results in about a 2x speed increase for decryptions.fork-5.53.8
rodzic
d56607a686
commit
28f3ded4bd
|
@ -335,7 +335,7 @@ dependencies {
|
||||||
implementation project(':video')
|
implementation project(':video')
|
||||||
|
|
||||||
implementation 'org.signal:zkgroup-android:0.7.0'
|
implementation 'org.signal:zkgroup-android:0.7.0'
|
||||||
implementation 'org.whispersystems:signal-client-android:0.1.6'
|
implementation 'org.whispersystems:signal-client-android:0.1.7'
|
||||||
implementation 'com.google.protobuf:protobuf-javalite:3.10.0'
|
implementation 'com.google.protobuf:protobuf-javalite:3.10.0'
|
||||||
implementation 'org.signal:argon2:13.1@aar'
|
implementation 'org.signal:argon2:13.1@aar'
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
|
||||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
|
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.MessageDatabase;
|
import org.thoughtcrime.securesms.database.MessageDatabase;
|
||||||
|
@ -27,13 +28,12 @@ import org.thoughtcrime.securesms.util.Base64;
|
||||||
import org.thoughtcrime.securesms.util.VerifySpan;
|
import org.thoughtcrime.securesms.util.VerifySpan;
|
||||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
|
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
|
|
||||||
|
|
||||||
public class ConfirmIdentityDialog extends AlertDialog {
|
public class ConfirmIdentityDialog extends AlertDialog {
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
|
@ -94,7 +94,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Void... params) {
|
protected Void doInBackground(Void... params) {
|
||||||
synchronized (SESSION_LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(Recipient.resolved(recipientId).requireServiceId(), 1);
|
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(Recipient.resolved(recipientId).requireServiceId(), 1);
|
||||||
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(getContext());
|
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(getContext());
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ import androidx.fragment.app.FragmentTransaction;
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
import org.thoughtcrime.securesms.color.MaterialColor;
|
import org.thoughtcrime.securesms.color.MaterialColor;
|
||||||
import org.thoughtcrime.securesms.components.camera.CameraView;
|
import org.thoughtcrime.securesms.components.camera.CameraView;
|
||||||
|
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
@ -91,14 +92,13 @@ import org.whispersystems.libsignal.fingerprint.Fingerprint;
|
||||||
import org.whispersystems.libsignal.fingerprint.FingerprintParsingException;
|
import org.whispersystems.libsignal.fingerprint.FingerprintParsingException;
|
||||||
import org.whispersystems.libsignal.fingerprint.FingerprintVersionMismatchException;
|
import org.whispersystems.libsignal.fingerprint.FingerprintVersionMismatchException;
|
||||||
import org.whispersystems.libsignal.fingerprint.NumericFingerprintGenerator;
|
import org.whispersystems.libsignal.fingerprint.NumericFingerprintGenerator;
|
||||||
|
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activity for verifying identity keys.
|
* Activity for verifying identity keys.
|
||||||
*
|
*
|
||||||
|
@ -620,7 +620,7 @@ public class VerifyIdentityActivity extends PassphraseRequiredActivity implement
|
||||||
new AsyncTask<Recipient, Void, Void>() {
|
new AsyncTask<Recipient, Void, Void>() {
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Recipient... params) {
|
protected Void doInBackground(Recipient... params) {
|
||||||
synchronized (SESSION_LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
Log.i(TAG, "Saving identity: " + params[0].getId());
|
Log.i(TAG, "Saving identity: " + params[0].getId());
|
||||||
DatabaseFactory.getIdentityDatabase(getActivity())
|
DatabaseFactory.getIdentityDatabase(getActivity())
|
||||||
|
|
|
@ -8,15 +8,15 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||||
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
||||||
|
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
|
|
||||||
|
|
||||||
public class UntrustedSendDialog extends AlertDialog.Builder implements DialogInterface.OnClickListener {
|
public class UntrustedSendDialog extends AlertDialog.Builder implements DialogInterface.OnClickListener {
|
||||||
|
|
||||||
private final List<IdentityRecord> untrustedRecords;
|
private final List<IdentityRecord> untrustedRecords;
|
||||||
|
@ -43,7 +43,7 @@ public class UntrustedSendDialog extends AlertDialog.Builder implements DialogIn
|
||||||
final IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(getContext());
|
final IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(getContext());
|
||||||
|
|
||||||
SimpleTask.run(() -> {
|
SimpleTask.run(() -> {
|
||||||
synchronized (SESSION_LOCK) {
|
try(SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
for (IdentityRecord identityRecord : untrustedRecords) {
|
for (IdentityRecord identityRecord : untrustedRecords) {
|
||||||
identityDatabase.setApproval(identityRecord.getRecipientId(), true);
|
identityDatabase.setApproval(identityRecord.getRecipientId(), true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,14 +8,14 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||||
|
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
|
|
||||||
|
|
||||||
public class UnverifiedSendDialog extends AlertDialog.Builder implements DialogInterface.OnClickListener {
|
public class UnverifiedSendDialog extends AlertDialog.Builder implements DialogInterface.OnClickListener {
|
||||||
|
|
||||||
private final List<IdentityRecord> untrustedRecords;
|
private final List<IdentityRecord> untrustedRecords;
|
||||||
|
@ -44,7 +44,7 @@ public class UnverifiedSendDialog extends AlertDialog.Builder implements DialogI
|
||||||
new AsyncTask<Void, Void, Void>() {
|
new AsyncTask<Void, Void, Void>() {
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Void... params) {
|
protected Void doInBackground(Void... params) {
|
||||||
synchronized (SESSION_LOCK) {
|
try(SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
for (IdentityRecord identityRecord : untrustedRecords) {
|
for (IdentityRecord identityRecord : untrustedRecords) {
|
||||||
identityDatabase.setVerified(identityRecord.getRecipientId(),
|
identityDatabase.setVerified(identityRecord.getRecipientId(),
|
||||||
identityRecord.getIdentityKey(),
|
identityRecord.getIdentityKey(),
|
||||||
|
|
|
@ -141,6 +141,7 @@ import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeDialog
|
||||||
import org.thoughtcrime.securesms.conversation.ui.groupcall.GroupCallViewModel;
|
import org.thoughtcrime.securesms.conversation.ui.groupcall.GroupCallViewModel;
|
||||||
import org.thoughtcrime.securesms.conversation.ui.mentions.MentionsPickerViewModel;
|
import org.thoughtcrime.securesms.conversation.ui.mentions.MentionsPickerViewModel;
|
||||||
import org.thoughtcrime.securesms.conversationlist.model.MessageResult;
|
import org.thoughtcrime.securesms.conversationlist.model.MessageResult;
|
||||||
|
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
|
||||||
import org.thoughtcrime.securesms.crypto.SecurityEvent;
|
import org.thoughtcrime.securesms.crypto.SecurityEvent;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.DraftDatabase;
|
import org.thoughtcrime.securesms.database.DraftDatabase;
|
||||||
|
@ -279,6 +280,7 @@ import org.thoughtcrime.securesms.wallpaper.ChatWallpaperDimLevelUtil;
|
||||||
import org.whispersystems.libsignal.InvalidMessageException;
|
import org.whispersystems.libsignal.InvalidMessageException;
|
||||||
import org.whispersystems.libsignal.util.Pair;
|
import org.whispersystems.libsignal.util.Pair;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -295,7 +297,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import static org.thoughtcrime.securesms.TransportOption.Type;
|
import static org.thoughtcrime.securesms.TransportOption.Type;
|
||||||
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
||||||
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activity for displaying a message thread, as well as
|
* Activity for displaying a message thread, as well as
|
||||||
|
@ -3620,7 +3621,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||||
new AsyncTask<Void, Void, Void>() {
|
new AsyncTask<Void, Void, Void>() {
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Void... params) {
|
protected Void doInBackground(Void... params) {
|
||||||
synchronized (SESSION_LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
for (IdentityRecord identityRecord : unverifiedIdentities) {
|
for (IdentityRecord identityRecord : unverifiedIdentities) {
|
||||||
identityDatabase.setVerified(identityRecord.getRecipientId(),
|
identityDatabase.setVerified(identityRecord.getRecipientId(),
|
||||||
identityRecord.getIdentityKey(),
|
identityRecord.getIdentityKey(),
|
||||||
|
|
|
@ -12,6 +12,7 @@ import com.annimon.stream.Stream;
|
||||||
|
|
||||||
import org.signal.core.util.concurrent.SignalExecutors;
|
import org.signal.core.util.concurrent.SignalExecutors;
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
|
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
|
||||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
|
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||||
|
@ -25,12 +26,11 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||||
import org.whispersystems.libsignal.IdentityKey;
|
import org.whispersystems.libsignal.IdentityKey;
|
||||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||||
|
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
|
|
||||||
|
|
||||||
final class SafetyNumberChangeRepository {
|
final class SafetyNumberChangeRepository {
|
||||||
|
|
||||||
private static final String TAG = SafetyNumberChangeRepository.class.getSimpleName();
|
private static final String TAG = SafetyNumberChangeRepository.class.getSimpleName();
|
||||||
|
@ -90,7 +90,7 @@ final class SafetyNumberChangeRepository {
|
||||||
private TrustAndVerifyResult trustOrVerifyChangedRecipientsInternal(@NonNull List<ChangedRecipient> changedRecipients) {
|
private TrustAndVerifyResult trustOrVerifyChangedRecipientsInternal(@NonNull List<ChangedRecipient> changedRecipients) {
|
||||||
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
|
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
|
||||||
|
|
||||||
synchronized (SESSION_LOCK) {
|
try(SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
for (ChangedRecipient changedRecipient : changedRecipients) {
|
for (ChangedRecipient changedRecipient : changedRecipients) {
|
||||||
IdentityRecord identityRecord = changedRecipient.getIdentityRecord();
|
IdentityRecord identityRecord = changedRecipient.getIdentityRecord();
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ final class SafetyNumberChangeRepository {
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private TrustAndVerifyResult trustOrVerifyChangedRecipientsAndResendInternal(@NonNull List<ChangedRecipient> changedRecipients,
|
private TrustAndVerifyResult trustOrVerifyChangedRecipientsAndResendInternal(@NonNull List<ChangedRecipient> changedRecipients,
|
||||||
@NonNull MessageRecord messageRecord) {
|
@NonNull MessageRecord messageRecord) {
|
||||||
synchronized (SESSION_LOCK) {
|
try(SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
for (ChangedRecipient changedRecipient : changedRecipients) {
|
for (ChangedRecipient changedRecipient : changedRecipients) {
|
||||||
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(changedRecipient.getRecipient().requireServiceId(), 1);
|
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(changedRecipient.getRecipient().requireServiceId(), 1);
|
||||||
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(context);
|
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(context);
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
package org.thoughtcrime.securesms.crypto;
|
||||||
|
|
||||||
|
import net.sqlcipher.database.SQLiteDatabase;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
|
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of {@link SignalSessionLock} that effectively re-uses our database lock.
|
||||||
|
*/
|
||||||
|
public enum DatabaseSessionLock implements SignalSessionLock {
|
||||||
|
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Lock acquire() {
|
||||||
|
SQLiteDatabase db = DatabaseFactory.getInstance(ApplicationDependencies.getApplication()).getRawDatabase();
|
||||||
|
|
||||||
|
if (db.isDbLockedByCurrentThread()) {
|
||||||
|
return () -> {};
|
||||||
|
}
|
||||||
|
|
||||||
|
db.beginTransaction();
|
||||||
|
|
||||||
|
return () -> {
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
db.endTransaction();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import android.content.Context;
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||||
import org.thoughtcrime.securesms.crypto.SessionUtil;
|
import org.thoughtcrime.securesms.crypto.SessionUtil;
|
||||||
|
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||||
|
@ -18,6 +19,7 @@ import org.whispersystems.libsignal.IdentityKeyPair;
|
||||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||||
import org.whispersystems.libsignal.state.IdentityKeyStore;
|
import org.whispersystems.libsignal.state.IdentityKeyStore;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@ -45,7 +47,7 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey, boolean nonBlockingApproval) {
|
public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey, boolean nonBlockingApproval) {
|
||||||
synchronized (LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
|
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
|
||||||
Recipient recipient = Recipient.external(context, address.getName());
|
Recipient recipient = Recipient.external(context, address.getName());
|
||||||
Optional<IdentityRecord> identityRecord = identityDatabase.getIdentity(recipient.getId());
|
Optional<IdentityRecord> identityRecord = identityDatabase.getIdentity(recipient.getId());
|
||||||
|
@ -91,7 +93,7 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
|
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
|
||||||
synchronized (LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
|
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
|
||||||
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
|
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
|
||||||
RecipientId ourRecipientId = Recipient.self().getId();
|
RecipientId ourRecipientId = Recipient.self().getId();
|
||||||
|
|
|
@ -4,12 +4,14 @@ import android.content.Context;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.whispersystems.libsignal.InvalidKeyIdException;
|
import org.whispersystems.libsignal.InvalidKeyIdException;
|
||||||
import org.whispersystems.libsignal.state.PreKeyRecord;
|
import org.whispersystems.libsignal.state.PreKeyRecord;
|
||||||
import org.whispersystems.libsignal.state.PreKeyStore;
|
import org.whispersystems.libsignal.state.PreKeyStore;
|
||||||
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||||
import org.whispersystems.libsignal.state.SignedPreKeyStore;
|
import org.whispersystems.libsignal.state.SignedPreKeyStore;
|
||||||
|
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -18,8 +20,6 @@ public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore {
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static final String TAG = TextSecurePreKeyStore.class.getSimpleName();
|
private static final String TAG = TextSecurePreKeyStore.class.getSimpleName();
|
||||||
|
|
||||||
private static final Object FILE_LOCK = new Object();
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException {
|
public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException {
|
||||||
synchronized (FILE_LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
PreKeyRecord preKeyRecord = DatabaseFactory.getPreKeyDatabase(context).getPreKey(preKeyId);
|
PreKeyRecord preKeyRecord = DatabaseFactory.getPreKeyDatabase(context).getPreKey(preKeyId);
|
||||||
|
|
||||||
if (preKeyRecord == null) throw new InvalidKeyIdException("No such key: " + preKeyId);
|
if (preKeyRecord == null) throw new InvalidKeyIdException("No such key: " + preKeyId);
|
||||||
|
@ -39,7 +39,7 @@ public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException {
|
public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException {
|
||||||
synchronized (FILE_LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
SignedPreKeyRecord signedPreKeyRecord = DatabaseFactory.getSignedPreKeyDatabase(context).getSignedPreKey(signedPreKeyId);
|
SignedPreKeyRecord signedPreKeyRecord = DatabaseFactory.getSignedPreKeyDatabase(context).getSignedPreKey(signedPreKeyId);
|
||||||
|
|
||||||
if (signedPreKeyRecord == null) throw new InvalidKeyIdException("No such signed prekey: " + signedPreKeyId);
|
if (signedPreKeyRecord == null) throw new InvalidKeyIdException("No such signed prekey: " + signedPreKeyId);
|
||||||
|
@ -49,21 +49,21 @@ public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SignedPreKeyRecord> loadSignedPreKeys() {
|
public List<SignedPreKeyRecord> loadSignedPreKeys() {
|
||||||
synchronized (FILE_LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
return DatabaseFactory.getSignedPreKeyDatabase(context).getAllSignedPreKeys();
|
return DatabaseFactory.getSignedPreKeyDatabase(context).getAllSignedPreKeys();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storePreKey(int preKeyId, PreKeyRecord record) {
|
public void storePreKey(int preKeyId, PreKeyRecord record) {
|
||||||
synchronized (FILE_LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
DatabaseFactory.getPreKeyDatabase(context).insertPreKey(preKeyId, record);
|
DatabaseFactory.getPreKeyDatabase(context).insertPreKey(preKeyId, record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) {
|
public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) {
|
||||||
synchronized (FILE_LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
DatabaseFactory.getSignedPreKeyDatabase(context).insertSignedPreKey(signedPreKeyId, record);
|
DatabaseFactory.getSignedPreKeyDatabase(context).insertSignedPreKey(signedPreKeyId, record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,5 +87,4 @@ public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore {
|
||||||
public void removeSignedPreKey(int signedPreKeyId) {
|
public void removeSignedPreKey(int signedPreKeyId) {
|
||||||
DatabaseFactory.getSignedPreKeyDatabase(context).removeSignedPreKey(signedPreKeyId);
|
DatabaseFactory.getSignedPreKeyDatabase(context).removeSignedPreKey(signedPreKeyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.content.Context;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
|
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.SessionDatabase;
|
import org.thoughtcrime.securesms.database.SessionDatabase;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
@ -13,6 +14,7 @@ import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||||
import org.whispersystems.libsignal.protocol.CiphertextMessage;
|
import org.whispersystems.libsignal.protocol.CiphertextMessage;
|
||||||
import org.whispersystems.libsignal.state.SessionRecord;
|
import org.whispersystems.libsignal.state.SessionRecord;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceSessionStore;
|
import org.whispersystems.signalservice.api.SignalServiceSessionStore;
|
||||||
|
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -21,8 +23,6 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||||
|
|
||||||
private static final String TAG = TextSecureSessionStore.class.getSimpleName();
|
private static final String TAG = TextSecureSessionStore.class.getSimpleName();
|
||||||
|
|
||||||
private static final Object FILE_LOCK = new Object();
|
|
||||||
|
|
||||||
@NonNull private final Context context;
|
@NonNull private final Context context;
|
||||||
|
|
||||||
public TextSecureSessionStore(@NonNull Context context) {
|
public TextSecureSessionStore(@NonNull Context context) {
|
||||||
|
@ -31,7 +31,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SessionRecord loadSession(@NonNull SignalProtocolAddress address) {
|
public SessionRecord loadSession(@NonNull SignalProtocolAddress address) {
|
||||||
synchronized (FILE_LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
|
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
|
||||||
SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(recipientId, address.getDeviceId());
|
SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(recipientId, address.getDeviceId());
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storeSession(@NonNull SignalProtocolAddress address, @NonNull SessionRecord record) {
|
public void storeSession(@NonNull SignalProtocolAddress address, @NonNull SessionRecord record) {
|
||||||
synchronized (FILE_LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
RecipientId id = Recipient.external(context, address.getName()).getId();
|
RecipientId id = Recipient.external(context, address.getName()).getId();
|
||||||
DatabaseFactory.getSessionDatabase(context).store(id, address.getDeviceId(), record);
|
DatabaseFactory.getSessionDatabase(context).store(id, address.getDeviceId(), record);
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsSession(SignalProtocolAddress address) {
|
public boolean containsSession(SignalProtocolAddress address) {
|
||||||
synchronized (FILE_LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
|
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
|
||||||
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
|
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
|
||||||
SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(recipientId, address.getDeviceId());
|
SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(recipientId, address.getDeviceId());
|
||||||
|
@ -70,7 +70,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteSession(SignalProtocolAddress address) {
|
public void deleteSession(SignalProtocolAddress address) {
|
||||||
synchronized (FILE_LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
|
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
|
||||||
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
|
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
|
||||||
DatabaseFactory.getSessionDatabase(context).delete(recipientId, address.getDeviceId());
|
DatabaseFactory.getSessionDatabase(context).delete(recipientId, address.getDeviceId());
|
||||||
|
@ -82,7 +82,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteAllSessions(String name) {
|
public void deleteAllSessions(String name) {
|
||||||
synchronized (FILE_LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(name)) {
|
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(name)) {
|
||||||
RecipientId recipientId = Recipient.external(context, name).getId();
|
RecipientId recipientId = Recipient.external(context, name).getId();
|
||||||
DatabaseFactory.getSessionDatabase(context).deleteAllFor(recipientId);
|
DatabaseFactory.getSessionDatabase(context).deleteAllFor(recipientId);
|
||||||
|
@ -92,7 +92,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Integer> getSubDeviceSessions(String name) {
|
public List<Integer> getSubDeviceSessions(String name) {
|
||||||
synchronized (FILE_LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(name)) {
|
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(name)) {
|
||||||
RecipientId recipientId = Recipient.external(context, name).getId();
|
RecipientId recipientId = Recipient.external(context, name).getId();
|
||||||
return DatabaseFactory.getSessionDatabase(context).getSubDevices(recipientId);
|
return DatabaseFactory.getSessionDatabase(context).getSubDevices(recipientId);
|
||||||
|
@ -105,7 +105,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void archiveSession(SignalProtocolAddress address) {
|
public void archiveSession(SignalProtocolAddress address) {
|
||||||
synchronized (FILE_LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
|
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
|
||||||
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
|
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
|
||||||
archiveSession(recipientId, address.getDeviceId());
|
archiveSession(recipientId, address.getDeviceId());
|
||||||
|
@ -114,7 +114,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void archiveSession(@NonNull RecipientId recipientId, int deviceId) {
|
public void archiveSession(@NonNull RecipientId recipientId, int deviceId) {
|
||||||
synchronized (FILE_LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
SessionRecord session = DatabaseFactory.getSessionDatabase(context).load(recipientId, deviceId);
|
SessionRecord session = DatabaseFactory.getSessionDatabase(context).load(recipientId, deviceId);
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
session.archiveCurrentState();
|
session.archiveCurrentState();
|
||||||
|
@ -124,7 +124,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void archiveSiblingSessions(@NonNull SignalProtocolAddress address) {
|
public void archiveSiblingSessions(@NonNull SignalProtocolAddress address) {
|
||||||
synchronized (FILE_LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
|
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
|
||||||
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
|
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
|
||||||
List<SessionDatabase.SessionRow> sessions = DatabaseFactory.getSessionDatabase(context).getAllFor(recipientId);
|
List<SessionDatabase.SessionRow> sessions = DatabaseFactory.getSessionDatabase(context).getAllFor(recipientId);
|
||||||
|
@ -142,7 +142,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void archiveAllSessions() {
|
public void archiveAllSessions() {
|
||||||
synchronized (FILE_LOCK) {
|
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
List<SessionDatabase.SessionRow> sessions = DatabaseFactory.getSessionDatabase(context).getAll();
|
List<SessionDatabase.SessionRow> sessions = DatabaseFactory.getSessionDatabase(context).getAll();
|
||||||
|
|
||||||
for (SessionDatabase.SessionRow row : sessions) {
|
for (SessionDatabase.SessionRow row : sessions) {
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package org.thoughtcrime.securesms.database;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import net.sqlcipher.database.SQLiteDatabase;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
|
||||||
|
public final class DatabaseLock {
|
||||||
|
|
||||||
|
public static @NonNull Lock acquire(@NonNull Context context) {
|
||||||
|
SQLiteDatabase db = DatabaseFactory.getInstance(context).getRawDatabase();
|
||||||
|
|
||||||
|
if (db.isDbLockedByCurrentThread()) {
|
||||||
|
return () -> {};
|
||||||
|
}
|
||||||
|
|
||||||
|
db.beginTransaction();
|
||||||
|
|
||||||
|
return () -> {
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
db.endTransaction();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Lock extends Closeable {
|
||||||
|
@Override
|
||||||
|
void close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,27 +4,20 @@ import android.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.greenrobot.eventbus.EventBus;
|
|
||||||
import org.signal.core.util.concurrent.SignalExecutors;
|
import org.signal.core.util.concurrent.SignalExecutors;
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
import org.thoughtcrime.securesms.BuildConfig;
|
import org.thoughtcrime.securesms.BuildConfig;
|
||||||
import org.thoughtcrime.securesms.components.TypingStatusRepository;
|
import org.thoughtcrime.securesms.components.TypingStatusRepository;
|
||||||
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
||||||
import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl;
|
import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseObserver;
|
import org.thoughtcrime.securesms.database.DatabaseObserver;
|
||||||
import org.thoughtcrime.securesms.database.JobDatabase;
|
import org.thoughtcrime.securesms.database.JobDatabase;
|
||||||
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Constraint;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.ConstraintObserver;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||||
import org.thoughtcrime.securesms.jobmanager.JobMigrator;
|
import org.thoughtcrime.securesms.jobmanager.JobMigrator;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.FactoryJobPredicate;
|
import org.thoughtcrime.securesms.jobmanager.impl.FactoryJobPredicate;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
|
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
|
||||||
import org.thoughtcrime.securesms.jobmanager.persistence.JobStorage;
|
|
||||||
import org.thoughtcrime.securesms.jobs.FastJobStorage;
|
import org.thoughtcrime.securesms.jobs.FastJobStorage;
|
||||||
import org.thoughtcrime.securesms.jobs.GroupCallUpdateSendJob;
|
import org.thoughtcrime.securesms.jobs.GroupCallUpdateSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.JobManagerFactories;
|
import org.thoughtcrime.securesms.jobs.JobManagerFactories;
|
||||||
|
@ -36,7 +29,6 @@ import org.thoughtcrime.securesms.jobs.PushProcessMessageJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.ReactionSendJob;
|
import org.thoughtcrime.securesms.jobs.ReactionSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.TypingSendJob;
|
import org.thoughtcrime.securesms.jobs.TypingSendJob;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
|
||||||
import org.thoughtcrime.securesms.megaphone.MegaphoneRepository;
|
import org.thoughtcrime.securesms.megaphone.MegaphoneRepository;
|
||||||
import org.thoughtcrime.securesms.messages.BackgroundMessageRetriever;
|
import org.thoughtcrime.securesms.messages.BackgroundMessageRetriever;
|
||||||
import org.thoughtcrime.securesms.messages.IncomingMessageObserver;
|
import org.thoughtcrime.securesms.messages.IncomingMessageObserver;
|
||||||
|
@ -57,23 +49,18 @@ import org.thoughtcrime.securesms.util.EarlyMessageCache;
|
||||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||||
import org.thoughtcrime.securesms.util.FrameRateTracker;
|
import org.thoughtcrime.securesms.util.FrameRateTracker;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
|
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
|
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
|
import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
|
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
|
|
||||||
import org.whispersystems.signalservice.api.util.CredentialsProvider;
|
import org.whispersystems.signalservice.api.util.CredentialsProvider;
|
||||||
import org.whispersystems.signalservice.api.util.SleepTimer;
|
import org.whispersystems.signalservice.api.util.SleepTimer;
|
||||||
import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
|
import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
|
||||||
import org.whispersystems.signalservice.api.websocket.ConnectivityListener;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import okhttp3.Response;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of {@link ApplicationDependencies.Provider} that provides real app dependencies.
|
* Implementation of {@link ApplicationDependencies.Provider} that provides real app dependencies.
|
||||||
*/
|
*/
|
||||||
|
@ -117,6 +104,7 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
|
||||||
return new SignalServiceMessageSender(provideSignalServiceNetworkAccess().getConfiguration(context),
|
return new SignalServiceMessageSender(provideSignalServiceNetworkAccess().getConfiguration(context),
|
||||||
new DynamicCredentialsProvider(context),
|
new DynamicCredentialsProvider(context),
|
||||||
new SignalProtocolStoreImpl(context),
|
new SignalProtocolStoreImpl(context),
|
||||||
|
DatabaseSessionLock.INSTANCE,
|
||||||
BuildConfig.SIGNAL_AGENT,
|
BuildConfig.SIGNAL_AGENT,
|
||||||
TextSecurePreferences.isMultiDevice(context),
|
TextSecurePreferences.isMultiDevice(context),
|
||||||
Optional.fromNullable(IncomingMessageObserver.getPipe()),
|
Optional.fromNullable(IncomingMessageObserver.getPipe()),
|
||||||
|
|
|
@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.jobs;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
|
@ -26,6 +25,7 @@ import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||||
import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl;
|
import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl;
|
||||||
|
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||||
import org.thoughtcrime.securesms.database.PushDatabase;
|
import org.thoughtcrime.securesms.database.PushDatabase;
|
||||||
|
@ -111,7 +111,11 @@ public final class PushDecryptMessageJob extends BaseJob {
|
||||||
JobManager jobManager = ApplicationDependencies.getJobManager();
|
JobManager jobManager = ApplicationDependencies.getJobManager();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
List<Job> jobs = handleMessage(envelope);
|
List<Job> jobs;
|
||||||
|
|
||||||
|
try (DatabaseSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
|
jobs = handleMessage(envelope);
|
||||||
|
}
|
||||||
|
|
||||||
for (Job job: jobs) {
|
for (Job job: jobs) {
|
||||||
jobManager.add(job);
|
jobManager.add(job);
|
||||||
|
@ -156,7 +160,7 @@ public final class PushDecryptMessageJob extends BaseJob {
|
||||||
try {
|
try {
|
||||||
SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context);
|
SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context);
|
||||||
SignalServiceAddress localAddress = new SignalServiceAddress(Optional.of(TextSecurePreferences.getLocalUuid(context)), Optional.of(TextSecurePreferences.getLocalNumber(context)));
|
SignalServiceAddress localAddress = new SignalServiceAddress(Optional.of(TextSecurePreferences.getLocalUuid(context)), Optional.of(TextSecurePreferences.getLocalNumber(context)));
|
||||||
SignalServiceCipher cipher = new SignalServiceCipher(localAddress, axolotlStore, UnidentifiedAccessUtil.getCertificateValidator());
|
SignalServiceCipher cipher = new SignalServiceCipher(localAddress, axolotlStore, DatabaseSessionLock.INSTANCE, UnidentifiedAccessUtil.getCertificateValidator());
|
||||||
|
|
||||||
SignalServiceContent content = cipher.decrypt(envelope);
|
SignalServiceContent content = cipher.decrypt(envelope);
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import androidx.annotation.StringRes;
|
||||||
|
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
|
||||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
|
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
|
||||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
|
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
@ -35,12 +36,11 @@ import org.whispersystems.libsignal.state.IdentityKeyStore;
|
||||||
import org.whispersystems.libsignal.state.SessionRecord;
|
import org.whispersystems.libsignal.state.SessionRecord;
|
||||||
import org.whispersystems.libsignal.state.SessionStore;
|
import org.whispersystems.libsignal.state.SessionStore;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
|
|
||||||
|
|
||||||
public class IdentityUtil {
|
public class IdentityUtil {
|
||||||
|
|
||||||
private static final String TAG = IdentityUtil.class.getSimpleName();
|
private static final String TAG = IdentityUtil.class.getSimpleName();
|
||||||
|
@ -147,7 +147,7 @@ public class IdentityUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void saveIdentity(Context context, String user, IdentityKey identityKey) {
|
public static void saveIdentity(Context context, String user, IdentityKey identityKey) {
|
||||||
synchronized (SESSION_LOCK) {
|
try(SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(context);
|
IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(context);
|
||||||
SessionStore sessionStore = new TextSecureSessionStore(context);
|
SessionStore sessionStore = new TextSecureSessionStore(context);
|
||||||
SignalProtocolAddress address = new SignalProtocolAddress(user, 1);
|
SignalProtocolAddress address = new SignalProtocolAddress(user, 1);
|
||||||
|
@ -164,7 +164,7 @@ public class IdentityUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void processVerifiedMessage(Context context, VerifiedMessage verifiedMessage) {
|
public static void processVerifiedMessage(Context context, VerifiedMessage verifiedMessage) {
|
||||||
synchronized (SESSION_LOCK) {
|
try(SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||||
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
|
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
|
||||||
Recipient recipient = Recipient.externalPush(context, verifiedMessage.getDestination());
|
Recipient recipient = Recipient.externalPush(context, verifiedMessage.getDestination());
|
||||||
Optional<IdentityRecord> identityRecord = identityDatabase.getIdentity(recipient.getId());
|
Optional<IdentityRecord> identityRecord = identityDatabase.getIdentity(recipient.getId());
|
||||||
|
|
|
@ -426,11 +426,11 @@ dependencyVerification {
|
||||||
['org.threeten:threetenbp:1.3.6',
|
['org.threeten:threetenbp:1.3.6',
|
||||||
'f4c23ffaaed717c3b99c003e0ee02d6d66377fd47d866fec7d971bd8644fc1a7'],
|
'f4c23ffaaed717c3b99c003e0ee02d6d66377fd47d866fec7d971bd8644fc1a7'],
|
||||||
|
|
||||||
['org.whispersystems:signal-client-android:0.1.6',
|
['org.whispersystems:signal-client-android:0.1.7',
|
||||||
'1fade2c159934cd34782474fc4a1010b822e7cd22026ac5da1b25098c99ad6f6'],
|
'1fade2c159934cd34782474fc4a1010b822e7cd22026ac5da1b25098c99ad6f6'],
|
||||||
|
|
||||||
['org.whispersystems:signal-client-java:0.1.6',
|
['org.whispersystems:signal-client-java:0.1.7',
|
||||||
'34f79d5dd063c70d93570734b6e5649ad6176d9288e6beb382b2d62692e8952c'],
|
'59dd701f9564c2130177ddaca374d14ce54927075955288f85ff8f55565a78f0'],
|
||||||
|
|
||||||
['pl.tajchert:waitingdots:0.1.0',
|
['pl.tajchert:waitingdots:0.1.0',
|
||||||
'2835d49e0787dbcb606c5a60021ced66578503b1e9fddcd7a5ef0cd5f095ba2c'],
|
'2835d49e0787dbcb606c5a60021ced66578503b1e9fddcd7a5ef0cd5f095ba2c'],
|
||||||
|
|
|
@ -21,7 +21,7 @@ dependencies {
|
||||||
api 'com.googlecode.libphonenumber:libphonenumber:8.12.17'
|
api 'com.googlecode.libphonenumber:libphonenumber:8.12.17'
|
||||||
api 'com.fasterxml.jackson.core:jackson-databind:2.9.9.2'
|
api 'com.fasterxml.jackson.core:jackson-databind:2.9.9.2'
|
||||||
|
|
||||||
api 'org.whispersystems:signal-client-java:0.1.6'
|
api 'org.whispersystems:signal-client-java:0.1.7'
|
||||||
api 'com.squareup.okhttp3:okhttp:3.12.10'
|
api 'com.squareup.okhttp3:okhttp:3.12.10'
|
||||||
implementation 'org.threeten:threetenbp:1.3.6'
|
implementation 'org.threeten:threetenbp:1.3.6'
|
||||||
|
|
||||||
|
|
|
@ -14,11 +14,11 @@ import org.whispersystems.libsignal.SessionBuilder;
|
||||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||||
import org.whispersystems.libsignal.logging.Log;
|
import org.whispersystems.libsignal.logging.Log;
|
||||||
import org.whispersystems.libsignal.state.PreKeyBundle;
|
import org.whispersystems.libsignal.state.PreKeyBundle;
|
||||||
import org.whispersystems.libsignal.state.SignalProtocolStore;
|
|
||||||
import org.whispersystems.libsignal.util.Pair;
|
import org.whispersystems.libsignal.util.Pair;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.crypto.AttachmentCipherOutputStream;
|
import org.whispersystems.signalservice.api.crypto.AttachmentCipherOutputStream;
|
||||||
import org.whispersystems.signalservice.api.crypto.SignalServiceCipher;
|
import org.whispersystems.signalservice.api.crypto.SignalServiceCipher;
|
||||||
|
import org.whispersystems.signalservice.api.crypto.SignalSessionBuilder;
|
||||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
|
||||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
||||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||||
|
@ -120,6 +120,7 @@ public class SignalServiceMessageSender {
|
||||||
|
|
||||||
private final PushServiceSocket socket;
|
private final PushServiceSocket socket;
|
||||||
private final SignalServiceProtocolStore store;
|
private final SignalServiceProtocolStore store;
|
||||||
|
private final SignalSessionLock sessionLock;
|
||||||
private final SignalServiceAddress localAddress;
|
private final SignalServiceAddress localAddress;
|
||||||
private final Optional<EventListener> eventListener;
|
private final Optional<EventListener> eventListener;
|
||||||
|
|
||||||
|
@ -144,6 +145,7 @@ public class SignalServiceMessageSender {
|
||||||
public SignalServiceMessageSender(SignalServiceConfiguration urls,
|
public SignalServiceMessageSender(SignalServiceConfiguration urls,
|
||||||
UUID uuid, String e164, String password,
|
UUID uuid, String e164, String password,
|
||||||
SignalServiceProtocolStore store,
|
SignalServiceProtocolStore store,
|
||||||
|
SignalSessionLock sessionLock,
|
||||||
String signalAgent,
|
String signalAgent,
|
||||||
boolean isMultiDevice,
|
boolean isMultiDevice,
|
||||||
Optional<SignalServiceMessagePipe> pipe,
|
Optional<SignalServiceMessagePipe> pipe,
|
||||||
|
@ -153,12 +155,13 @@ public class SignalServiceMessageSender {
|
||||||
ExecutorService executor,
|
ExecutorService executor,
|
||||||
boolean automaticNetworkRetry)
|
boolean automaticNetworkRetry)
|
||||||
{
|
{
|
||||||
this(urls, new StaticCredentialsProvider(uuid, e164, password), store, signalAgent, isMultiDevice, pipe, unidentifiedPipe, eventListener, clientZkProfileOperations, executor, 0, automaticNetworkRetry);
|
this(urls, new StaticCredentialsProvider(uuid, e164, password), store, sessionLock, signalAgent, isMultiDevice, pipe, unidentifiedPipe, eventListener, clientZkProfileOperations, executor, 0, automaticNetworkRetry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SignalServiceMessageSender(SignalServiceConfiguration urls,
|
public SignalServiceMessageSender(SignalServiceConfiguration urls,
|
||||||
CredentialsProvider credentialsProvider,
|
CredentialsProvider credentialsProvider,
|
||||||
SignalServiceProtocolStore store,
|
SignalServiceProtocolStore store,
|
||||||
|
SignalSessionLock sessionLock,
|
||||||
String signalAgent,
|
String signalAgent,
|
||||||
boolean isMultiDevice,
|
boolean isMultiDevice,
|
||||||
Optional<SignalServiceMessagePipe> pipe,
|
Optional<SignalServiceMessagePipe> pipe,
|
||||||
|
@ -171,6 +174,7 @@ public class SignalServiceMessageSender {
|
||||||
{
|
{
|
||||||
this.socket = new PushServiceSocket(urls, credentialsProvider, signalAgent, clientZkProfileOperations, automaticNetworkRetry);
|
this.socket = new PushServiceSocket(urls, credentialsProvider, signalAgent, clientZkProfileOperations, automaticNetworkRetry);
|
||||||
this.store = store;
|
this.store = store;
|
||||||
|
this.sessionLock = sessionLock;
|
||||||
this.localAddress = new SignalServiceAddress(credentialsProvider.getUuid(), credentialsProvider.getE164());
|
this.localAddress = new SignalServiceAddress(credentialsProvider.getUuid(), credentialsProvider.getE164());
|
||||||
this.pipe = new AtomicReference<>(pipe);
|
this.pipe = new AtomicReference<>(pipe);
|
||||||
this.unidentifiedPipe = new AtomicReference<>(unidentifiedPipe);
|
this.unidentifiedPipe = new AtomicReference<>(unidentifiedPipe);
|
||||||
|
@ -1549,7 +1553,7 @@ public class SignalServiceMessageSender {
|
||||||
throws IOException, InvalidKeyException, UntrustedIdentityException
|
throws IOException, InvalidKeyException, UntrustedIdentityException
|
||||||
{
|
{
|
||||||
SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(recipient.getIdentifier(), deviceId);
|
SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(recipient.getIdentifier(), deviceId);
|
||||||
SignalServiceCipher cipher = new SignalServiceCipher(localAddress, store, null);
|
SignalServiceCipher cipher = new SignalServiceCipher(localAddress, store, sessionLock, null);
|
||||||
|
|
||||||
if (!store.containsSession(signalProtocolAddress)) {
|
if (!store.containsSession(signalProtocolAddress)) {
|
||||||
try {
|
try {
|
||||||
|
@ -1558,7 +1562,7 @@ public class SignalServiceMessageSender {
|
||||||
for (PreKeyBundle preKey : preKeys) {
|
for (PreKeyBundle preKey : preKeys) {
|
||||||
try {
|
try {
|
||||||
SignalProtocolAddress preKeyAddress = new SignalProtocolAddress(recipient.getIdentifier(), preKey.getDeviceId());
|
SignalProtocolAddress preKeyAddress = new SignalProtocolAddress(recipient.getIdentifier(), preKey.getDeviceId());
|
||||||
SessionBuilder sessionBuilder = new SessionBuilder(store, preKeyAddress);
|
SignalSessionBuilder sessionBuilder = new SignalSessionBuilder(sessionLock, new SessionBuilder(store, preKeyAddress));
|
||||||
sessionBuilder.process(preKey);
|
sessionBuilder.process(preKey);
|
||||||
} catch (org.whispersystems.libsignal.UntrustedIdentityException e) {
|
} catch (org.whispersystems.libsignal.UntrustedIdentityException e) {
|
||||||
throw new UntrustedIdentityException("Untrusted identity key!", recipient.getIdentifier(), preKey.getIdentityKey());
|
throw new UntrustedIdentityException("Untrusted identity key!", recipient.getIdentifier(), preKey.getIdentityKey());
|
||||||
|
@ -1598,7 +1602,7 @@ public class SignalServiceMessageSender {
|
||||||
PreKeyBundle preKey = socket.getPreKey(recipient, missingDeviceId);
|
PreKeyBundle preKey = socket.getPreKey(recipient, missingDeviceId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SessionBuilder sessionBuilder = new SessionBuilder(store, new SignalProtocolAddress(recipient.getIdentifier(), missingDeviceId));
|
SignalSessionBuilder sessionBuilder = new SignalSessionBuilder(sessionLock, new SessionBuilder(store, new SignalProtocolAddress(recipient.getIdentifier(), missingDeviceId)));
|
||||||
sessionBuilder.process(preKey);
|
sessionBuilder.process(preKey);
|
||||||
} catch (org.whispersystems.libsignal.UntrustedIdentityException e) {
|
} catch (org.whispersystems.libsignal.UntrustedIdentityException e) {
|
||||||
throw new UntrustedIdentityException("Untrusted identity key!", recipient.getIdentifier(), preKey.getIdentityKey());
|
throw new UntrustedIdentityException("Untrusted identity key!", recipient.getIdentifier(), preKey.getIdentityKey());
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package org.whispersystems.signalservice.api;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface to allow the injection of a lock that will be used to keep interactions with
|
||||||
|
* ecryptions/decryptions thread-safe.
|
||||||
|
*/
|
||||||
|
public interface SignalSessionLock {
|
||||||
|
|
||||||
|
Lock acquire();
|
||||||
|
|
||||||
|
interface Lock extends Closeable {
|
||||||
|
@Override
|
||||||
|
void close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package org.whispersystems.signalservice.api.crypto;
|
||||||
|
|
||||||
|
import org.signal.libsignal.metadata.InvalidMetadataMessageException;
|
||||||
|
import org.signal.libsignal.metadata.InvalidMetadataVersionException;
|
||||||
|
import org.signal.libsignal.metadata.ProtocolDuplicateMessageException;
|
||||||
|
import org.signal.libsignal.metadata.ProtocolInvalidKeyException;
|
||||||
|
import org.signal.libsignal.metadata.ProtocolInvalidKeyIdException;
|
||||||
|
import org.signal.libsignal.metadata.ProtocolInvalidMessageException;
|
||||||
|
import org.signal.libsignal.metadata.ProtocolInvalidVersionException;
|
||||||
|
import org.signal.libsignal.metadata.ProtocolLegacyMessageException;
|
||||||
|
import org.signal.libsignal.metadata.ProtocolNoSessionException;
|
||||||
|
import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException;
|
||||||
|
import org.signal.libsignal.metadata.SealedSessionCipher;
|
||||||
|
import org.signal.libsignal.metadata.SelfSendException;
|
||||||
|
import org.signal.libsignal.metadata.certificate.CertificateValidator;
|
||||||
|
import org.signal.libsignal.metadata.certificate.SenderCertificate;
|
||||||
|
import org.whispersystems.libsignal.InvalidKeyException;
|
||||||
|
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||||
|
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A thread-safe wrapper around {@link SealedSessionCipher}.
|
||||||
|
*/
|
||||||
|
public class SignalSealedSessionCipher {
|
||||||
|
|
||||||
|
private final SignalSessionLock lock;
|
||||||
|
private final SealedSessionCipher cipher;
|
||||||
|
|
||||||
|
public SignalSealedSessionCipher(SignalSessionLock lock, SealedSessionCipher cipher) {
|
||||||
|
this.lock = lock;
|
||||||
|
this.cipher = cipher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] encrypt(SignalProtocolAddress destinationAddress, SenderCertificate senderCertificate, byte[] paddedPlaintext) throws InvalidKeyException, org.whispersystems.libsignal.UntrustedIdentityException {
|
||||||
|
try (SignalSessionLock.Lock unused = lock.acquire()) {
|
||||||
|
return cipher.encrypt(destinationAddress, senderCertificate, paddedPlaintext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SealedSessionCipher.DecryptionResult decrypt(CertificateValidator validator, byte[] ciphertext, long timestamp) throws InvalidMetadataMessageException, InvalidMetadataVersionException, ProtocolInvalidMessageException, ProtocolInvalidKeyException, ProtocolNoSessionException, ProtocolLegacyMessageException, ProtocolInvalidVersionException, ProtocolDuplicateMessageException, ProtocolInvalidKeyIdException, ProtocolUntrustedIdentityException, SelfSendException {
|
||||||
|
try (SignalSessionLock.Lock unused = lock.acquire()) {
|
||||||
|
return cipher.decrypt(validator, ciphertext, timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSessionVersion(SignalProtocolAddress remoteAddress) {
|
||||||
|
try (SignalSessionLock.Lock unused = lock.acquire()) {
|
||||||
|
return cipher.getSessionVersion(remoteAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRemoteRegistrationId(SignalProtocolAddress remoteAddress) {
|
||||||
|
try (SignalSessionLock.Lock unused = lock.acquire()) {
|
||||||
|
return cipher.getRemoteRegistrationId(remoteAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,6 +37,7 @@ import org.whispersystems.libsignal.protocol.PreKeySignalMessage;
|
||||||
import org.whispersystems.libsignal.protocol.SignalMessage;
|
import org.whispersystems.libsignal.protocol.SignalMessage;
|
||||||
import org.whispersystems.libsignal.state.SignalProtocolStore;
|
import org.whispersystems.libsignal.state.SignalProtocolStore;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceMetadata;
|
import org.whispersystems.signalservice.api.messages.SignalServiceMetadata;
|
||||||
|
@ -63,14 +64,17 @@ public class SignalServiceCipher {
|
||||||
private static final String TAG = SignalServiceCipher.class.getSimpleName();
|
private static final String TAG = SignalServiceCipher.class.getSimpleName();
|
||||||
|
|
||||||
private final SignalProtocolStore signalProtocolStore;
|
private final SignalProtocolStore signalProtocolStore;
|
||||||
|
private final SignalSessionLock sessionLock;
|
||||||
private final SignalServiceAddress localAddress;
|
private final SignalServiceAddress localAddress;
|
||||||
private final CertificateValidator certificateValidator;
|
private final CertificateValidator certificateValidator;
|
||||||
|
|
||||||
public SignalServiceCipher(SignalServiceAddress localAddress,
|
public SignalServiceCipher(SignalServiceAddress localAddress,
|
||||||
SignalProtocolStore signalProtocolStore,
|
SignalProtocolStore signalProtocolStore,
|
||||||
|
SignalSessionLock sessionLock,
|
||||||
CertificateValidator certificateValidator)
|
CertificateValidator certificateValidator)
|
||||||
{
|
{
|
||||||
this.signalProtocolStore = signalProtocolStore;
|
this.signalProtocolStore = signalProtocolStore;
|
||||||
|
this.sessionLock = sessionLock;
|
||||||
this.localAddress = localAddress;
|
this.localAddress = localAddress;
|
||||||
this.certificateValidator = certificateValidator;
|
this.certificateValidator = certificateValidator;
|
||||||
}
|
}
|
||||||
|
@ -81,7 +85,7 @@ public class SignalServiceCipher {
|
||||||
throws UntrustedIdentityException, InvalidKeyException
|
throws UntrustedIdentityException, InvalidKeyException
|
||||||
{
|
{
|
||||||
if (unidentifiedAccess.isPresent()) {
|
if (unidentifiedAccess.isPresent()) {
|
||||||
SealedSessionCipher sessionCipher = new SealedSessionCipher(signalProtocolStore, localAddress.getUuid().orNull(), localAddress.getNumber().orNull(), 1);
|
SignalSealedSessionCipher sessionCipher = new SignalSealedSessionCipher(sessionLock, new SealedSessionCipher(signalProtocolStore, localAddress.getUuid().orNull(), localAddress.getNumber().orNull(), 1));
|
||||||
PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion(destination));
|
PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion(destination));
|
||||||
byte[] ciphertext = sessionCipher.encrypt(destination, unidentifiedAccess.get().getUnidentifiedCertificate(), transportDetails.getPaddedMessageBody(unpaddedMessage));
|
byte[] ciphertext = sessionCipher.encrypt(destination, unidentifiedAccess.get().getUnidentifiedCertificate(), transportDetails.getPaddedMessageBody(unpaddedMessage));
|
||||||
String body = Base64.encodeBytes(ciphertext);
|
String body = Base64.encodeBytes(ciphertext);
|
||||||
|
@ -89,7 +93,7 @@ public class SignalServiceCipher {
|
||||||
|
|
||||||
return new OutgoingPushMessage(Type.UNIDENTIFIED_SENDER_VALUE, destination.getDeviceId(), remoteRegistrationId, body);
|
return new OutgoingPushMessage(Type.UNIDENTIFIED_SENDER_VALUE, destination.getDeviceId(), remoteRegistrationId, body);
|
||||||
} else {
|
} else {
|
||||||
SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, destination);
|
SignalSessionCipher sessionCipher = new SignalSessionCipher(sessionLock, new SessionCipher(signalProtocolStore, destination));
|
||||||
PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion());
|
PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion());
|
||||||
CiphertextMessage message = sessionCipher.encrypt(transportDetails.getPaddedMessageBody(unpaddedMessage));
|
CiphertextMessage message = sessionCipher.encrypt(transportDetails.getPaddedMessageBody(unpaddedMessage));
|
||||||
int remoteRegistrationId = sessionCipher.getRemoteRegistrationId();
|
int remoteRegistrationId = sessionCipher.getRemoteRegistrationId();
|
||||||
|
@ -173,20 +177,20 @@ public class SignalServiceCipher {
|
||||||
|
|
||||||
if (envelope.isPreKeySignalMessage()) {
|
if (envelope.isPreKeySignalMessage()) {
|
||||||
SignalProtocolAddress sourceAddress = getPreferredProtocolAddress(signalProtocolStore, envelope.getSourceAddress(), envelope.getSourceDevice());
|
SignalProtocolAddress sourceAddress = getPreferredProtocolAddress(signalProtocolStore, envelope.getSourceAddress(), envelope.getSourceDevice());
|
||||||
SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, sourceAddress);
|
SignalSessionCipher sessionCipher = new SignalSessionCipher(sessionLock, new SessionCipher(signalProtocolStore, sourceAddress));
|
||||||
|
|
||||||
paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(ciphertext));
|
paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(ciphertext));
|
||||||
metadata = new SignalServiceMetadata(envelope.getSourceAddress(), envelope.getSourceDevice(), envelope.getTimestamp(), envelope.getServerReceivedTimestamp(), envelope.getServerDeliveredTimestamp(), false);
|
metadata = new SignalServiceMetadata(envelope.getSourceAddress(), envelope.getSourceDevice(), envelope.getTimestamp(), envelope.getServerReceivedTimestamp(), envelope.getServerDeliveredTimestamp(), false);
|
||||||
sessionVersion = sessionCipher.getSessionVersion();
|
sessionVersion = sessionCipher.getSessionVersion();
|
||||||
} else if (envelope.isSignalMessage()) {
|
} else if (envelope.isSignalMessage()) {
|
||||||
SignalProtocolAddress sourceAddress = getPreferredProtocolAddress(signalProtocolStore, envelope.getSourceAddress(), envelope.getSourceDevice());
|
SignalProtocolAddress sourceAddress = getPreferredProtocolAddress(signalProtocolStore, envelope.getSourceAddress(), envelope.getSourceDevice());
|
||||||
SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, sourceAddress);
|
SignalSessionCipher sessionCipher = new SignalSessionCipher(sessionLock, new SessionCipher(signalProtocolStore, sourceAddress));
|
||||||
|
|
||||||
paddedMessage = sessionCipher.decrypt(new SignalMessage(ciphertext));
|
paddedMessage = sessionCipher.decrypt(new SignalMessage(ciphertext));
|
||||||
metadata = new SignalServiceMetadata(envelope.getSourceAddress(), envelope.getSourceDevice(), envelope.getTimestamp(), envelope.getServerReceivedTimestamp(), envelope.getServerDeliveredTimestamp(), false);
|
metadata = new SignalServiceMetadata(envelope.getSourceAddress(), envelope.getSourceDevice(), envelope.getTimestamp(), envelope.getServerReceivedTimestamp(), envelope.getServerDeliveredTimestamp(), false);
|
||||||
sessionVersion = sessionCipher.getSessionVersion();
|
sessionVersion = sessionCipher.getSessionVersion();
|
||||||
} else if (envelope.isUnidentifiedSender()) {
|
} else if (envelope.isUnidentifiedSender()) {
|
||||||
SealedSessionCipher sealedSessionCipher = new SealedSessionCipher(signalProtocolStore, localAddress.getUuid().orNull(), localAddress.getNumber().orNull(), 1);
|
SignalSealedSessionCipher sealedSessionCipher = new SignalSealedSessionCipher(sessionLock, new SealedSessionCipher(signalProtocolStore, localAddress.getUuid().orNull(), localAddress.getNumber().orNull(), 1));
|
||||||
DecryptionResult result = sealedSessionCipher.decrypt(certificateValidator, ciphertext, envelope.getServerReceivedTimestamp());
|
DecryptionResult result = sealedSessionCipher.decrypt(certificateValidator, ciphertext, envelope.getServerReceivedTimestamp());
|
||||||
SignalServiceAddress resultAddress = new SignalServiceAddress(UuidUtil.parse(result.getSenderUuid().orNull()), result.getSenderE164());
|
SignalServiceAddress resultAddress = new SignalServiceAddress(UuidUtil.parse(result.getSenderUuid().orNull()), result.getSenderE164());
|
||||||
SignalProtocolAddress protocolAddress = getPreferredProtocolAddress(signalProtocolStore, resultAddress, result.getDeviceId());
|
SignalProtocolAddress protocolAddress = getPreferredProtocolAddress(signalProtocolStore, resultAddress, result.getDeviceId());
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package org.whispersystems.signalservice.api.crypto;
|
||||||
|
|
||||||
|
import org.whispersystems.libsignal.InvalidKeyException;
|
||||||
|
import org.whispersystems.libsignal.SessionBuilder;
|
||||||
|
import org.whispersystems.libsignal.UntrustedIdentityException;
|
||||||
|
import org.whispersystems.libsignal.state.PreKeyBundle;
|
||||||
|
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A thread-safe wrapper around {@link SessionBuilder}.
|
||||||
|
*/
|
||||||
|
public class SignalSessionBuilder {
|
||||||
|
|
||||||
|
private final SignalSessionLock lock;
|
||||||
|
private final SessionBuilder builder;
|
||||||
|
|
||||||
|
public SignalSessionBuilder(SignalSessionLock lock, SessionBuilder builder) {
|
||||||
|
this.lock = lock;
|
||||||
|
this.builder = builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process(PreKeyBundle preKey) throws InvalidKeyException, UntrustedIdentityException {
|
||||||
|
try (SignalSessionLock.Lock unused = lock.acquire()) {
|
||||||
|
builder.process(preKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package org.whispersystems.signalservice.api.crypto;
|
||||||
|
|
||||||
|
import org.whispersystems.libsignal.DuplicateMessageException;
|
||||||
|
import org.whispersystems.libsignal.InvalidKeyException;
|
||||||
|
import org.whispersystems.libsignal.InvalidKeyIdException;
|
||||||
|
import org.whispersystems.libsignal.InvalidMessageException;
|
||||||
|
import org.whispersystems.libsignal.LegacyMessageException;
|
||||||
|
import org.whispersystems.libsignal.NoSessionException;
|
||||||
|
import org.whispersystems.libsignal.SessionCipher;
|
||||||
|
import org.whispersystems.libsignal.UntrustedIdentityException;
|
||||||
|
import org.whispersystems.libsignal.protocol.CiphertextMessage;
|
||||||
|
import org.whispersystems.libsignal.protocol.PreKeySignalMessage;
|
||||||
|
import org.whispersystems.libsignal.protocol.SignalMessage;
|
||||||
|
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A thread-safe wrapper around {@link SessionCipher}.
|
||||||
|
*/
|
||||||
|
public class SignalSessionCipher {
|
||||||
|
|
||||||
|
private final SignalSessionLock lock;
|
||||||
|
private final SessionCipher cipher;
|
||||||
|
|
||||||
|
public SignalSessionCipher(SignalSessionLock lock, SessionCipher cipher) {
|
||||||
|
this.lock = lock;
|
||||||
|
this.cipher = cipher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CiphertextMessage encrypt(byte[] paddedMessage) throws org.whispersystems.libsignal.UntrustedIdentityException {
|
||||||
|
try (SignalSessionLock.Lock unused = lock.acquire()) {
|
||||||
|
return cipher.encrypt(paddedMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] decrypt(PreKeySignalMessage ciphertext) throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, InvalidKeyIdException, InvalidKeyException, org.whispersystems.libsignal.UntrustedIdentityException {
|
||||||
|
try (SignalSessionLock.Lock unused = lock.acquire()) {
|
||||||
|
return cipher.decrypt(ciphertext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] decrypt(SignalMessage ciphertext) throws InvalidMessageException, DuplicateMessageException, LegacyMessageException, NoSessionException, UntrustedIdentityException {
|
||||||
|
try (SignalSessionLock.Lock unused = lock.acquire()) {
|
||||||
|
return cipher.decrypt(ciphertext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRemoteRegistrationId() {
|
||||||
|
try (SignalSessionLock.Lock unused = lock.acquire()) {
|
||||||
|
return cipher.getRemoteRegistrationId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSessionVersion() {
|
||||||
|
try (SignalSessionLock.Lock unused = lock.acquire()) {
|
||||||
|
return cipher.getSessionVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,7 +30,7 @@ dependencyVerification {
|
||||||
['org.threeten:threetenbp:1.3.6',
|
['org.threeten:threetenbp:1.3.6',
|
||||||
'f4c23ffaaed717c3b99c003e0ee02d6d66377fd47d866fec7d971bd8644fc1a7'],
|
'f4c23ffaaed717c3b99c003e0ee02d6d66377fd47d866fec7d971bd8644fc1a7'],
|
||||||
|
|
||||||
['org.whispersystems:signal-client-java:0.1.6',
|
['org.whispersystems:signal-client-java:0.1.7',
|
||||||
'34f79d5dd063c70d93570734b6e5649ad6176d9288e6beb382b2d62692e8952c'],
|
'59dd701f9564c2130177ddaca374d14ce54927075955288f85ff8f55565a78f0'],
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue