kopia lustrzana https://github.com/ryukoposting/Signal-Android
Replace prekey jobs with one overall sync job.
rodzic
2740b5e300
commit
3252871ed5
|
@ -0,0 +1,212 @@
|
|||
package org.thoughtcrime.securesms.jobs
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.signal.libsignal.protocol.ecc.Curve
|
||||
import org.thoughtcrime.securesms.crypto.storage.PreKeyMetadataStore
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.dependencies.InstrumentationApplicationDependencyProvider
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.testing.Get
|
||||
import org.thoughtcrime.securesms.testing.Put
|
||||
import org.thoughtcrime.securesms.testing.SignalActivityRule
|
||||
import org.thoughtcrime.securesms.testing.assertIs
|
||||
import org.thoughtcrime.securesms.testing.assertIsNot
|
||||
import org.thoughtcrime.securesms.testing.parsedRequestBody
|
||||
import org.thoughtcrime.securesms.testing.success
|
||||
import org.whispersystems.signalservice.api.push.SignedPreKeyEntity
|
||||
import org.whispersystems.signalservice.internal.push.PreKeyState
|
||||
import org.whispersystems.signalservice.internal.push.PreKeyStatus
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class PreKeysSyncJobTest {
|
||||
|
||||
@get:Rule
|
||||
val harness = SignalActivityRule()
|
||||
|
||||
private val aciPreKeyMeta: PreKeyMetadataStore
|
||||
get() = SignalStore.account().aciPreKeys
|
||||
|
||||
private val pniPreKeyMeta: PreKeyMetadataStore
|
||||
get() = SignalStore.account().pniPreKeys
|
||||
|
||||
private lateinit var job: PreKeysSyncJob
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
job = PreKeysSyncJob()
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
InstrumentationApplicationDependencyProvider.clearHandlers()
|
||||
}
|
||||
|
||||
/**
|
||||
* Create signed prekeys for both identities when both do not have registered prekeys according
|
||||
* to our local state.
|
||||
*/
|
||||
@Test
|
||||
fun runWithoutRegisteredKeysForBothIdentities() {
|
||||
// GIVEN
|
||||
aciPreKeyMeta.isSignedPreKeyRegistered = false
|
||||
pniPreKeyMeta.isSignedPreKeyRegistered = false
|
||||
|
||||
lateinit var aciSignedPreKey: SignedPreKeyEntity
|
||||
lateinit var pniSignedPreKey: SignedPreKeyEntity
|
||||
|
||||
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
|
||||
Put("/v2/keys/signed?identity=aci") { r ->
|
||||
aciSignedPreKey = r.parsedRequestBody()
|
||||
MockResponse().success()
|
||||
},
|
||||
Put("/v2/keys/signed?identity=pni") { r ->
|
||||
pniSignedPreKey = r.parsedRequestBody()
|
||||
MockResponse().success()
|
||||
},
|
||||
)
|
||||
|
||||
// WHEN
|
||||
val result: Job.Result = job.run()
|
||||
|
||||
// THEN
|
||||
result.isSuccess assertIs true
|
||||
|
||||
aciPreKeyMeta.isSignedPreKeyRegistered assertIs true
|
||||
pniPreKeyMeta.isSignedPreKeyRegistered assertIs true
|
||||
|
||||
val aciVerifySignatureResult = Curve.verifySignature(
|
||||
ApplicationDependencies.getProtocolStore().aci().identityKeyPair.publicKey.publicKey,
|
||||
aciSignedPreKey.publicKey.serialize(),
|
||||
aciSignedPreKey.signature
|
||||
)
|
||||
aciVerifySignatureResult assertIs true
|
||||
|
||||
val pniVerifySignatureResult = Curve.verifySignature(
|
||||
ApplicationDependencies.getProtocolStore().pni().identityKeyPair.publicKey.publicKey,
|
||||
pniSignedPreKey.publicKey.serialize(),
|
||||
pniSignedPreKey.signature
|
||||
)
|
||||
pniVerifySignatureResult assertIs true
|
||||
}
|
||||
|
||||
/**
|
||||
* With 100 prekeys registered for each identity, do nothing.
|
||||
*/
|
||||
@Test
|
||||
fun runWithRegisteredKeysForBothIdentities() {
|
||||
// GIVEN
|
||||
val currentAciKeyId = aciPreKeyMeta.activeSignedPreKeyId
|
||||
val currentPniKeyId = pniPreKeyMeta.activeSignedPreKeyId
|
||||
|
||||
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
|
||||
Get("/v2/keys?identity=aci") { MockResponse().success(PreKeyStatus(100)) },
|
||||
Get("/v2/keys?identity=pni") { MockResponse().success(PreKeyStatus(100)) },
|
||||
)
|
||||
|
||||
// WHEN
|
||||
val result: Job.Result = job.run()
|
||||
|
||||
// THEN
|
||||
result.isSuccess assertIs true
|
||||
|
||||
aciPreKeyMeta.activeSignedPreKeyId assertIs currentAciKeyId
|
||||
pniPreKeyMeta.activeSignedPreKeyId assertIs currentPniKeyId
|
||||
}
|
||||
|
||||
/**
|
||||
* With 100 prekeys registered for ACI, but no PNI prekeys registered according to local state,
|
||||
* do nothing for ACI but create PNI prekeys and update local state.
|
||||
*/
|
||||
@Test
|
||||
fun runWithRegisteredKeysForAciIdentityOnly() {
|
||||
// GIVEN
|
||||
pniPreKeyMeta.isSignedPreKeyRegistered = false
|
||||
|
||||
val currentAciKeyId = aciPreKeyMeta.activeSignedPreKeyId
|
||||
val currentPniKeyId = pniPreKeyMeta.activeSignedPreKeyId
|
||||
|
||||
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
|
||||
Get("/v2/keys?identity=aci") { MockResponse().success(PreKeyStatus(100)) },
|
||||
Put("/v2/keys/signed?identity=pni") { MockResponse().success() },
|
||||
)
|
||||
|
||||
// WHEN
|
||||
val result: Job.Result = job.run()
|
||||
|
||||
// THEN
|
||||
result.isSuccess assertIs true
|
||||
|
||||
pniPreKeyMeta.isSignedPreKeyRegistered assertIs true
|
||||
aciPreKeyMeta.activeSignedPreKeyId assertIs currentAciKeyId
|
||||
pniPreKeyMeta.activeSignedPreKeyId assertIsNot currentPniKeyId
|
||||
}
|
||||
|
||||
/**
|
||||
* With <10 prekeys registered for each identity, upload new.
|
||||
*/
|
||||
@Test
|
||||
fun runWithLowNumberOfRegisteredKeysForBothIdentities() {
|
||||
// GIVEN
|
||||
val currentAciKeyId = aciPreKeyMeta.activeSignedPreKeyId
|
||||
val currentPniKeyId = pniPreKeyMeta.activeSignedPreKeyId
|
||||
|
||||
val currentNextAciPreKeyId = aciPreKeyMeta.nextOneTimePreKeyId
|
||||
val currentNextPniPreKeyId = pniPreKeyMeta.nextOneTimePreKeyId
|
||||
|
||||
lateinit var aciPreKeyStateRequest: PreKeyState
|
||||
lateinit var pniPreKeyStateRequest: PreKeyState
|
||||
|
||||
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
|
||||
Get("/v2/keys?identity=aci") { MockResponse().success(PreKeyStatus(5)) },
|
||||
Get("/v2/keys?identity=pni") { MockResponse().success(PreKeyStatus(5)) },
|
||||
Put("/v2/keys/?identity=aci") { r ->
|
||||
aciPreKeyStateRequest = r.parsedRequestBody()
|
||||
MockResponse().success()
|
||||
},
|
||||
Put("/v2/keys/?identity=pni") { r ->
|
||||
pniPreKeyStateRequest = r.parsedRequestBody()
|
||||
MockResponse().success()
|
||||
},
|
||||
)
|
||||
|
||||
// WHEN
|
||||
val result: Job.Result = job.run()
|
||||
|
||||
// THEN
|
||||
result.isSuccess assertIs true
|
||||
aciPreKeyMeta.activeSignedPreKeyId assertIsNot currentAciKeyId
|
||||
pniPreKeyMeta.activeSignedPreKeyId assertIsNot currentPniKeyId
|
||||
|
||||
aciPreKeyMeta.nextOneTimePreKeyId assertIsNot currentNextAciPreKeyId
|
||||
pniPreKeyMeta.nextOneTimePreKeyId assertIsNot currentNextPniPreKeyId
|
||||
|
||||
ApplicationDependencies.getProtocolStore().aci().identityKeyPair.publicKey.let { aciIdentityKey ->
|
||||
aciPreKeyStateRequest.identityKey assertIs aciIdentityKey
|
||||
|
||||
val verifySignatureResult = Curve.verifySignature(
|
||||
aciIdentityKey.publicKey,
|
||||
aciPreKeyStateRequest.signedPreKey.publicKey.serialize(),
|
||||
aciPreKeyStateRequest.signedPreKey.signature
|
||||
)
|
||||
verifySignatureResult assertIs true
|
||||
}
|
||||
|
||||
ApplicationDependencies.getProtocolStore().pni().identityKeyPair.publicKey.let { pniIdentityKey ->
|
||||
pniPreKeyStateRequest.identityKey assertIs pniIdentityKey
|
||||
|
||||
val verifySignatureResult = Curve.verifySignature(
|
||||
pniIdentityKey.publicKey,
|
||||
pniPreKeyStateRequest.signedPreKey.publicKey.serialize(),
|
||||
pniPreKeyStateRequest.signedPreKey.signature
|
||||
)
|
||||
verifySignatureResult assertIs true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -50,16 +50,15 @@ import org.thoughtcrime.securesms.emoji.EmojiSource;
|
|||
import org.thoughtcrime.securesms.emoji.JumboEmoji;
|
||||
import org.thoughtcrime.securesms.gcm.FcmJobService;
|
||||
import org.thoughtcrime.securesms.jobs.CheckServiceReachabilityJob;
|
||||
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
|
||||
import org.thoughtcrime.securesms.jobs.DownloadLatestEmojiDataJob;
|
||||
import org.thoughtcrime.securesms.jobs.EmojiSearchIndexDownloadJob;
|
||||
import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
|
||||
import org.thoughtcrime.securesms.jobs.FontDownloaderJob;
|
||||
import org.thoughtcrime.securesms.jobs.GroupV2UpdateSelfProfileKeyJob;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob;
|
||||
import org.thoughtcrime.securesms.jobs.ProfileUploadJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveRemoteAnnouncementsJob;
|
||||
import org.thoughtcrime.securesms.jobs.StoryOnboardingDownloadJob;
|
||||
|
@ -180,13 +179,12 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
|
|||
.addNonBlocking(this::initializeRevealableMessageManager)
|
||||
.addNonBlocking(this::initializePendingRetryReceiptManager)
|
||||
.addNonBlocking(this::initializeFcmCheck)
|
||||
.addNonBlocking(CreateSignedPreKeyJob::enqueueIfNeeded)
|
||||
.addNonBlocking(PreKeysSyncJob::enqueueIfNeeded)
|
||||
.addNonBlocking(this::initializePeriodicTasks)
|
||||
.addNonBlocking(this::initializeCircumvention)
|
||||
.addNonBlocking(this::initializePendingMessages)
|
||||
.addNonBlocking(this::initializeCleanup)
|
||||
.addNonBlocking(this::initializeGlideCodecs)
|
||||
.addNonBlocking(RefreshPreKeysJob::scheduleIfNecessary)
|
||||
.addNonBlocking(StorageSyncHelper::scheduleRoutineSync)
|
||||
.addNonBlocking(() -> ApplicationDependencies.getJobManager().beginJobLoop())
|
||||
.addNonBlocking(EmojiSource::refresh)
|
||||
|
|
|
@ -217,7 +217,7 @@ class ChangeNumberRepository(private val accountManager: SignalServiceAccountMan
|
|||
|
||||
// Signed Prekeys
|
||||
val signedPreKeyRecord = if (deviceId == primaryDeviceId) {
|
||||
PreKeyUtil.generateAndStoreSignedPreKey(pniProtocolStore, pniMetadataStore, pniIdentity.privateKey, false)
|
||||
PreKeyUtil.generateAndStoreSignedPreKey(pniProtocolStore, pniMetadataStore, pniIdentity.privateKey)
|
||||
} else {
|
||||
PreKeyUtil.generateSignedPreKey(SecureRandom().nextInt(Medium.MAX_VALUE), pniIdentity.privateKey)
|
||||
}
|
||||
|
|
|
@ -64,14 +64,13 @@ public class PreKeyUtil {
|
|||
return records;
|
||||
}
|
||||
|
||||
public synchronized static @NonNull SignedPreKeyRecord generateAndStoreSignedPreKey(@NonNull SignalProtocolStore protocolStore, @NonNull PreKeyMetadataStore metadataStore, boolean setAsActive) {
|
||||
return generateAndStoreSignedPreKey(protocolStore, metadataStore, protocolStore.getIdentityKeyPair().getPrivateKey(), setAsActive);
|
||||
public synchronized static @NonNull SignedPreKeyRecord generateAndStoreSignedPreKey(@NonNull SignalProtocolStore protocolStore, @NonNull PreKeyMetadataStore metadataStore) {
|
||||
return generateAndStoreSignedPreKey(protocolStore, metadataStore, protocolStore.getIdentityKeyPair().getPrivateKey());
|
||||
}
|
||||
|
||||
public synchronized static @NonNull SignedPreKeyRecord generateAndStoreSignedPreKey(@NonNull SignalProtocolStore protocolStore,
|
||||
@NonNull PreKeyMetadataStore metadataStore,
|
||||
@NonNull ECPrivateKey privateKey,
|
||||
boolean setAsActive)
|
||||
@NonNull ECPrivateKey privateKey)
|
||||
{
|
||||
Log.i(TAG, "Generating signed prekeys...");
|
||||
|
||||
|
@ -81,10 +80,6 @@ public class PreKeyUtil {
|
|||
protocolStore.storeSignedPreKey(signedPreKeyId, record);
|
||||
metadataStore.setNextSignedPreKeyId((signedPreKeyId + 1) % Medium.MAX_VALUE);
|
||||
|
||||
if (setAsActive) {
|
||||
metadataStore.setActiveSignedPreKeyId(signedPreKeyId);
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,7 @@ import org.thoughtcrime.securesms.database.helpers.SignalDatabaseMigrations
|
|||
import org.thoughtcrime.securesms.database.helpers.SignalDatabaseMigrations.migrate
|
||||
import org.thoughtcrime.securesms.database.helpers.SignalDatabaseMigrations.migratePostTransaction
|
||||
import org.thoughtcrime.securesms.database.model.AvatarPickerDatabase
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob
|
||||
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob
|
||||
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob
|
||||
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob.DatabaseUpgradeListener
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService
|
||||
|
@ -145,7 +144,7 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data
|
|||
val masterSecret = KeyCachingService.getMasterSecret(context)
|
||||
if (masterSecret != null) SQLCipherMigrationHelper.migrateCiphertext(context, masterSecret, legacyDb, db, null) else TextSecurePreferences.setNeedsSqlCipherMigration(context, true)
|
||||
if (!PreKeyMigrationHelper.migratePreKeys(context, db)) {
|
||||
ApplicationDependencies.getJobManager().add(RefreshPreKeysJob())
|
||||
PreKeysSyncJob.enqueue()
|
||||
}
|
||||
SessionStoreMigrationHelper.migrateSessions(context, db)
|
||||
PreKeyMigrationHelper.cleanUpPreKeys(context)
|
||||
|
|
|
@ -30,9 +30,8 @@ import org.thoughtcrime.securesms.database.RecipientDatabase
|
|||
import org.thoughtcrime.securesms.database.helpers.migration.MyStoryMigration
|
||||
import org.thoughtcrime.securesms.database.helpers.migration.UrgentMslFlagMigration
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ReactionList
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob
|
||||
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels
|
||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
|
||||
|
@ -224,7 +223,7 @@ object SignalDatabaseMigrations {
|
|||
db.execSQL("CREATE TABLE one_time_prekeys (_id INTEGER PRIMARY KEY, key_id INTEGER UNIQUE, public_key TEXT NOT NULL, private_key TEXT NOT NULL)")
|
||||
|
||||
if (!PreKeyMigrationHelper.migratePreKeys(context, db)) {
|
||||
ApplicationDependencies.getJobManager().add(RefreshPreKeysJob())
|
||||
PreKeysSyncJob.enqueue()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,11 +31,11 @@ import org.thoughtcrime.securesms.jobmanager.JobManager;
|
|||
import org.thoughtcrime.securesms.jobmanager.JobMigrator;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.FactoryJobPredicate;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
|
||||
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
|
||||
import org.thoughtcrime.securesms.jobs.FastJobStorage;
|
||||
import org.thoughtcrime.securesms.jobs.GroupCallUpdateSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.JobManagerFactories;
|
||||
import org.thoughtcrime.securesms.jobs.MarkerJob;
|
||||
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushDecryptMessageJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushMediaSendJob;
|
||||
|
@ -317,7 +317,7 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
|
|||
}
|
||||
|
||||
if (needsPreKeyJob) {
|
||||
CreateSignedPreKeyJob.enqueueIfNeeded();
|
||||
PreKeysSyncJob.enqueueIfNeeded();
|
||||
}
|
||||
|
||||
SignalBaseIdentityKeyStore baseIdentityStore = new SignalBaseIdentityKeyStore(context);
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
|
@ -14,6 +15,8 @@ import java.util.Map;
|
|||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import static androidx.annotation.VisibleForTesting.PACKAGE_PRIVATE;
|
||||
|
||||
/**
|
||||
* A durable unit of work.
|
||||
*
|
||||
|
@ -193,15 +196,18 @@ public abstract class Job {
|
|||
return new Result(ResultType.FAILURE, runtimeException, null, INVALID_BACKOFF);
|
||||
}
|
||||
|
||||
boolean isSuccess() {
|
||||
@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
|
||||
public boolean isSuccess() {
|
||||
return resultType == ResultType.SUCCESS;
|
||||
}
|
||||
|
||||
boolean isRetry() {
|
||||
@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
|
||||
public boolean isRetry() {
|
||||
return resultType == ResultType.RETRY;
|
||||
}
|
||||
|
||||
boolean isFailure() {
|
||||
@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
|
||||
public boolean isFailure() {
|
||||
return resultType == ResultType.FAILURE;
|
||||
}
|
||||
|
||||
|
|
|
@ -92,8 +92,8 @@ public abstract class BaseJob extends Job {
|
|||
warn(tag, "", message, null);
|
||||
}
|
||||
|
||||
protected void warn(@NonNull String tag, @NonNull String event, @NonNull String message) {
|
||||
warn(tag, event, message, null);
|
||||
protected void warn(@NonNull String tag, @NonNull Object extra, @NonNull String message) {
|
||||
warn(tag, extra.toString(), message, null);
|
||||
}
|
||||
|
||||
protected void warn(@NonNull String tag, @Nullable Throwable t) {
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.libsignal.protocol.state.SignalProtocolStore;
|
||||
import org.signal.libsignal.protocol.state.SignedPreKeyRecord;
|
||||
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.storage.PreKeyMetadataStore;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
||||
import org.whispersystems.signalservice.api.push.ServiceIdType;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Creates and uploads a new signed prekey for an identity if one hasn't been uploaded yet.
|
||||
*/
|
||||
public class CreateSignedPreKeyJob extends BaseJob {
|
||||
|
||||
public static final String KEY = "CreateSignedPreKeyJob";
|
||||
|
||||
private static final String TAG = Log.tag(CreateSignedPreKeyJob.class);
|
||||
|
||||
private CreateSignedPreKeyJob() {
|
||||
this(new Job.Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setMaxInstancesForFactory(1)
|
||||
.setQueue("CreateSignedPreKeyJob")
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(30))
|
||||
.setMaxAttempts(Parameters.UNLIMITED)
|
||||
.build());
|
||||
}
|
||||
|
||||
private CreateSignedPreKeyJob(@NonNull Job.Parameters parameters) {
|
||||
super(parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues an instance of this job if we not yet created + uploaded signed prekeys for one of our identities.
|
||||
*/
|
||||
public static void enqueueIfNeeded() {
|
||||
if (!SignalStore.account().aciPreKeys().isSignedPreKeyRegistered() || !SignalStore.account().pniPreKeys().isSignedPreKeyRegistered()) {
|
||||
Log.i(TAG, "Some signed prekeys aren't registered yet. Enqueuing a job.");
|
||||
ApplicationDependencies.getJobManager().add(new CreateSignedPreKeyJob());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Data serialize() {
|
||||
return Data.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getFactoryKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun() throws IOException {
|
||||
if (!SignalStore.account().isRegistered() || SignalStore.account().getAci() == null || SignalStore.account().getPni() == null) {
|
||||
Log.w(TAG, "Not yet registered...");
|
||||
return;
|
||||
}
|
||||
|
||||
createPreKeys(ServiceIdType.ACI, SignalStore.account().getAci(), ApplicationDependencies.getProtocolStore().aci(), SignalStore.account().aciPreKeys());
|
||||
createPreKeys(ServiceIdType.PNI, SignalStore.account().getPni(), ApplicationDependencies.getProtocolStore().pni(), SignalStore.account().pniPreKeys());
|
||||
}
|
||||
|
||||
private void createPreKeys(@NonNull ServiceIdType serviceIdType, @Nullable ServiceId serviceId, @NonNull SignalProtocolStore protocolStore, @NonNull PreKeyMetadataStore metadataStore)
|
||||
throws IOException
|
||||
{
|
||||
if (serviceId == null) {
|
||||
warn(TAG, "AccountId not set!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (metadataStore.isSignedPreKeyRegistered()) {
|
||||
warn(TAG, "Signed prekey for " + serviceIdType + " already registered...");
|
||||
return;
|
||||
}
|
||||
|
||||
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
||||
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore, true);
|
||||
|
||||
accountManager.setSignedPreKey(serviceIdType, signedPreKeyRecord);
|
||||
metadataStore.setSignedPreKeyRegistered(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
||||
if (exception instanceof PushNetworkException) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static final class Factory implements Job.Factory<CreateSignedPreKeyJob> {
|
||||
@Override
|
||||
public @NonNull CreateSignedPreKeyJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||
return new CreateSignedPreKeyJob(parameters);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,7 +46,6 @@ import org.thoughtcrime.securesms.migrations.EmojiDownloadMigrationJob;
|
|||
import org.thoughtcrime.securesms.migrations.KbsEnclaveMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.MigrationCompleteJob;
|
||||
import org.thoughtcrime.securesms.migrations.SyncDistributionListsMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.PassingMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.PinOptOutMigration;
|
||||
import org.thoughtcrime.securesms.migrations.PinReminderMigrationJob;
|
||||
|
@ -62,6 +61,7 @@ import org.thoughtcrime.securesms.migrations.StickerLaunchMigrationJob;
|
|||
import org.thoughtcrime.securesms.migrations.StickerMyDailyLifeMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.StorageCapabilityMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.StorageServiceMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.SyncDistributionListsMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.TrimByLengthSettingsMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.UserNotificationMigrationJob;
|
||||
import org.thoughtcrime.securesms.migrations.UuidMigrationJob;
|
||||
|
@ -89,7 +89,6 @@ public final class JobManagerFactories {
|
|||
put(ClearFallbackKbsEnclaveJob.KEY, new ClearFallbackKbsEnclaveJob.Factory());
|
||||
put(ConversationShortcutUpdateJob.KEY, new ConversationShortcutUpdateJob.Factory());
|
||||
put(CreateReleaseChannelJob.KEY, new CreateReleaseChannelJob.Factory());
|
||||
put(CreateSignedPreKeyJob.KEY, new CreateSignedPreKeyJob.Factory());
|
||||
put(DirectoryRefreshJob.KEY, new DirectoryRefreshJob.Factory());
|
||||
put(DonationReceiptRedemptionJob.KEY, new DonationReceiptRedemptionJob.Factory());
|
||||
put(DownloadLatestEmojiDataJob.KEY, new DownloadLatestEmojiDataJob.Factory());
|
||||
|
@ -136,6 +135,7 @@ public final class JobManagerFactories {
|
|||
put(PaymentNotificationSendJob.KEY, new PaymentNotificationSendJob.Factory());
|
||||
put(PaymentSendJob.KEY, new PaymentSendJob.Factory());
|
||||
put(PaymentTransactionCheckJob.KEY, new PaymentTransactionCheckJob.Factory());
|
||||
put(PreKeysSyncJob.KEY, new PreKeysSyncJob.Factory());
|
||||
put(ProfileKeySendJob.KEY, new ProfileKeySendJob.Factory());
|
||||
put(ProfileUploadJob.KEY, new ProfileUploadJob.Factory());
|
||||
put(PushDecryptMessageJob.KEY, new PushDecryptMessageJob.Factory());
|
||||
|
@ -152,7 +152,6 @@ public final class JobManagerFactories {
|
|||
put(RecipientChangedNumberJob.KEY, new RecipientChangedNumberJob.Factory());
|
||||
put(RefreshAttributesJob.KEY, new RefreshAttributesJob.Factory());
|
||||
put(RefreshOwnProfileJob.KEY, new RefreshOwnProfileJob.Factory());
|
||||
put(RefreshPreKeysJob.KEY, new RefreshPreKeysJob.Factory());
|
||||
put(RemoteConfigRefreshJob.KEY, new RemoteConfigRefreshJob.Factory());
|
||||
put(RemoteDeleteSendJob.KEY, new RemoteDeleteSendJob.Factory());
|
||||
put(ReportSpamJob.KEY, new ReportSpamJob.Factory());
|
||||
|
@ -165,7 +164,6 @@ public final class JobManagerFactories {
|
|||
put(RetrieveRemoteAnnouncementsJob.KEY, new RetrieveRemoteAnnouncementsJob.Factory());
|
||||
put(RotateCertificateJob.KEY, new RotateCertificateJob.Factory());
|
||||
put(RotateProfileKeyJob.KEY, new RotateProfileKeyJob.Factory());
|
||||
put(RotateSignedPreKeyJob.KEY, new RotateSignedPreKeyJob.Factory());
|
||||
put(SenderKeyDistributionSendJob.KEY, new SenderKeyDistributionSendJob.Factory());
|
||||
put(SendDeliveryReceiptJob.KEY, new SendDeliveryReceiptJob.Factory());
|
||||
put(SendReadReceiptJob.KEY, new SendReadReceiptJob.Factory(application));
|
||||
|
@ -241,6 +239,9 @@ public final class JobManagerFactories {
|
|||
put("LeaveGroupJob", new FailingJob.Factory());
|
||||
put("PushGroupUpdateJob", new FailingJob.Factory());
|
||||
put("RequestGroupInfoJob", new FailingJob.Factory());
|
||||
put("RotateSignedPreKeyJob", new PreKeysSyncJob.Factory());
|
||||
put("CreateSignedPreKeyJob", new PreKeysSyncJob.Factory());
|
||||
put("RefreshPreKeysJob", new PreKeysSyncJob.Factory());
|
||||
}};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
package org.thoughtcrime.securesms.jobs
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.libsignal.protocol.state.SignalProtocolStore
|
||||
import org.thoughtcrime.securesms.crypto.PreKeyUtil
|
||||
import org.thoughtcrime.securesms.crypto.storage.PreKeyMetadataStore
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.jobmanager.Data
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
import org.whispersystems.signalservice.api.push.ServiceIdType
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Regardless of the current state of affairs with respect to prekeys for either ACI or PNI identities, will
|
||||
* attempt to make the state valid.
|
||||
*
|
||||
* If prekeys aren't registered for an identity they will be created.
|
||||
*
|
||||
* If prekeys are registered but the count is below the minimum threshold, then new ones will be uploaded.
|
||||
*/
|
||||
class PreKeysSyncJob private constructor(private val forceRotate: Boolean = false, parameters: Parameters) : BaseJob(parameters) {
|
||||
|
||||
companion object {
|
||||
const val KEY = "PreKeysSyncJob"
|
||||
|
||||
private val TAG = Log.tag(PreKeysSyncJob::class.java)
|
||||
private val KEY_FORCE_ROTATE = "force_rotate"
|
||||
private const val PREKEY_MINIMUM = 10
|
||||
private val REFRESH_INTERVAL = TimeUnit.DAYS.toMillis(3)
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun enqueue(forceRotate: Boolean = false) {
|
||||
ApplicationDependencies.getJobManager().add(PreKeysSyncJob(forceRotate))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun enqueueIfNeeded() {
|
||||
if (!SignalStore.account().aciPreKeys.isSignedPreKeyRegistered || !SignalStore.account().pniPreKeys.isSignedPreKeyRegistered) {
|
||||
Log.i(TAG, "Some signed prekeys aren't registered yet. Enqueuing a job. ACI: ${SignalStore.account().aciPreKeys.isSignedPreKeyRegistered} PNI: ${SignalStore.account().pniPreKeys.isSignedPreKeyRegistered}")
|
||||
ApplicationDependencies.getJobManager().add(PreKeysSyncJob())
|
||||
} else if (SignalStore.account().aciPreKeys.activeSignedPreKeyId < 0 || SignalStore.account().pniPreKeys.activeSignedPreKeyId < 0) {
|
||||
Log.i(TAG, "Some signed prekeys aren't active yet. Enqueuing a job. ACI: ${SignalStore.account().aciPreKeys.activeSignedPreKeyId >= 0} PNI: ${SignalStore.account().pniPreKeys.activeSignedPreKeyId >= 0}")
|
||||
ApplicationDependencies.getJobManager().add(PreKeysSyncJob())
|
||||
} else {
|
||||
val timeSinceLastRefresh = System.currentTimeMillis() - SignalStore.misc().lastPrekeyRefreshTime
|
||||
|
||||
if (timeSinceLastRefresh > REFRESH_INTERVAL) {
|
||||
Log.i(TAG, "Scheduling a prekey refresh. Time since last schedule: $timeSinceLastRefresh ms")
|
||||
ApplicationDependencies.getJobManager().add(PreKeysSyncJob())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
constructor(forceRotate: Boolean = false) : this(
|
||||
forceRotate,
|
||||
Parameters.Builder()
|
||||
.setQueue("PreKeysSyncJob")
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setMaxInstancesForFactory(1)
|
||||
.setMaxAttempts(Parameters.UNLIMITED)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(30))
|
||||
.build()
|
||||
)
|
||||
|
||||
override fun getFactoryKey(): String = KEY
|
||||
|
||||
override fun serialize(): Data {
|
||||
return Data.Builder()
|
||||
.putBoolean(KEY_FORCE_ROTATE, forceRotate)
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun onRun() {
|
||||
if (!SignalStore.account().isRegistered || SignalStore.account().aci == null || SignalStore.account().pni == null) {
|
||||
warn(TAG, "Not yet registered")
|
||||
return
|
||||
}
|
||||
|
||||
syncPreKeys(ServiceIdType.ACI, SignalStore.account().aci, ApplicationDependencies.getProtocolStore().aci(), SignalStore.account().aciPreKeys)
|
||||
syncPreKeys(ServiceIdType.PNI, SignalStore.account().pni, ApplicationDependencies.getProtocolStore().pni(), SignalStore.account().pniPreKeys)
|
||||
SignalStore.misc().lastPrekeyRefreshTime = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
private fun syncPreKeys(serviceIdType: ServiceIdType, serviceId: ServiceId?, protocolStore: SignalProtocolStore, metadataStore: PreKeyMetadataStore) {
|
||||
if (serviceId == null) {
|
||||
warn(TAG, serviceIdType, "AccountId not set!")
|
||||
return
|
||||
}
|
||||
|
||||
if (metadataStore.isSignedPreKeyRegistered && metadataStore.activeSignedPreKeyId >= 0) {
|
||||
if (forceRotate || System.currentTimeMillis() > TextSecurePreferences.getSignedPreKeyRotationTime(context) || metadataStore.signedPreKeyFailureCount > 5) {
|
||||
log(serviceIdType, "Rotating signed prekey...")
|
||||
rotateSignedPreKey(serviceIdType, protocolStore, metadataStore)
|
||||
} else {
|
||||
log(serviceIdType, "Refreshing prekeys...")
|
||||
refreshKeys(serviceIdType, protocolStore, metadataStore)
|
||||
}
|
||||
} else {
|
||||
log(serviceIdType, "Creating signed prekey...")
|
||||
rotateSignedPreKey(serviceIdType, protocolStore, metadataStore)
|
||||
}
|
||||
}
|
||||
|
||||
private fun rotateSignedPreKey(serviceIdType: ServiceIdType, protocolStore: SignalProtocolStore, metadataStore: PreKeyMetadataStore) {
|
||||
val signedPreKeyRecord = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore)
|
||||
ApplicationDependencies.getSignalServiceAccountManager().setSignedPreKey(serviceIdType, signedPreKeyRecord)
|
||||
|
||||
metadataStore.activeSignedPreKeyId = signedPreKeyRecord.id
|
||||
metadataStore.isSignedPreKeyRegistered = true
|
||||
metadataStore.signedPreKeyFailureCount = 0
|
||||
}
|
||||
|
||||
private fun refreshKeys(serviceIdType: ServiceIdType, protocolStore: SignalProtocolStore, metadataStore: PreKeyMetadataStore) {
|
||||
val accountManager = ApplicationDependencies.getSignalServiceAccountManager()
|
||||
val availableKeys = accountManager.getPreKeysCount(serviceIdType)
|
||||
|
||||
log(serviceIdType, "Available keys: $availableKeys")
|
||||
|
||||
if (availableKeys >= PREKEY_MINIMUM && metadataStore.isSignedPreKeyRegistered) {
|
||||
log(serviceIdType, "Available keys sufficient.")
|
||||
return
|
||||
}
|
||||
|
||||
val preKeyRecords = PreKeyUtil.generateAndStoreOneTimePreKeys(protocolStore, metadataStore)
|
||||
val signedPreKeyRecord = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore)
|
||||
val identityKey = protocolStore.identityKeyPair
|
||||
|
||||
log(serviceIdType, "Registering new prekeys...")
|
||||
|
||||
accountManager.setPreKeys(serviceIdType, identityKey.publicKey, signedPreKeyRecord, preKeyRecords)
|
||||
metadataStore.activeSignedPreKeyId = signedPreKeyRecord.id
|
||||
metadataStore.isSignedPreKeyRegistered = true
|
||||
|
||||
log(serviceIdType, "Cleaning prekeys...")
|
||||
PreKeyUtil.cleanSignedPreKeys(protocolStore, metadataStore)
|
||||
|
||||
SignalStore.misc().lastPrekeyRefreshTime = System.currentTimeMillis()
|
||||
log(serviceIdType, "Successfully refreshed prekeys.")
|
||||
}
|
||||
|
||||
override fun onShouldRetry(e: Exception): Boolean {
|
||||
return when (e) {
|
||||
is NonSuccessfulResponseCodeException -> false
|
||||
is PushNetworkException -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure() {
|
||||
val aciStore = SignalStore.account().aciPreKeys
|
||||
val pniStore = SignalStore.account().pniPreKeys
|
||||
|
||||
if ((aciStore.isSignedPreKeyRegistered || pniStore.isSignedPreKeyRegistered) && forceRotate) {
|
||||
aciStore.signedPreKeyFailureCount++
|
||||
pniStore.signedPreKeyFailureCount++
|
||||
}
|
||||
}
|
||||
|
||||
private fun log(serviceIdType: ServiceIdType, message: String) {
|
||||
Log.i(TAG, "[$serviceIdType] $message")
|
||||
}
|
||||
|
||||
class Factory : Job.Factory<PreKeysSyncJob> {
|
||||
override fun create(parameters: Parameters, data: Data): PreKeysSyncJob {
|
||||
return PreKeysSyncJob(data.getBooleanOrDefault(KEY_FORCE_ROTATE, false), parameters)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -103,7 +103,7 @@ public abstract class PushSendJob extends SendJob {
|
|||
@Override
|
||||
protected final void onSend() throws Exception {
|
||||
if (SignalStore.account().aciPreKeys().getSignedPreKeyFailureCount() > 5) {
|
||||
ApplicationDependencies.getJobManager().add(new RotateSignedPreKeyJob());
|
||||
PreKeysSyncJob.enqueue(true);
|
||||
throw new TextSecureExpiredException("Too many signed prekey rotation failures");
|
||||
}
|
||||
|
||||
|
|
|
@ -1,148 +0,0 @@
|
|||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.libsignal.protocol.IdentityKeyPair;
|
||||
import org.signal.libsignal.protocol.state.PreKeyRecord;
|
||||
import org.signal.libsignal.protocol.state.SignalProtocolStore;
|
||||
import org.signal.libsignal.protocol.state.SignedPreKeyRecord;
|
||||
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.storage.PreKeyMetadataStore;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.push.ServiceIdType;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Ensures that our prekeys are up to date for both our ACI and PNI identities.
|
||||
* Specifically, if we have less than {@link #PREKEY_MINIMUM} one-time prekeys, we will generate and upload
|
||||
* a new batch of one-time prekeys, as well as a new signed prekey.
|
||||
*/
|
||||
public class RefreshPreKeysJob extends BaseJob {
|
||||
|
||||
public static final String KEY = "RefreshPreKeysJob";
|
||||
|
||||
private static final String TAG = Log.tag(RefreshPreKeysJob.class);
|
||||
|
||||
private static final int PREKEY_MINIMUM = 10;
|
||||
|
||||
private static final long REFRESH_INTERVAL = TimeUnit.DAYS.toMillis(3);
|
||||
|
||||
public RefreshPreKeysJob() {
|
||||
this(new Job.Parameters.Builder()
|
||||
.setQueue("RefreshPreKeysJob")
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setMaxInstancesForFactory(1)
|
||||
.setMaxAttempts(Parameters.UNLIMITED)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(30))
|
||||
.build());
|
||||
}
|
||||
|
||||
public static void scheduleIfNecessary() {
|
||||
long timeSinceLastRefresh = System.currentTimeMillis() - SignalStore.misc().getLastPrekeyRefreshTime();
|
||||
|
||||
if (timeSinceLastRefresh > REFRESH_INTERVAL) {
|
||||
Log.i(TAG, "Scheduling a prekey refresh. Time since last schedule: " + timeSinceLastRefresh + " ms");
|
||||
ApplicationDependencies.getJobManager().add(new RefreshPreKeysJob());
|
||||
}
|
||||
}
|
||||
|
||||
private RefreshPreKeysJob(@NonNull Job.Parameters parameters) {
|
||||
super(parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Data serialize() {
|
||||
return Data.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getFactoryKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun() throws IOException {
|
||||
if (!SignalStore.account().isRegistered() || SignalStore.account().getAci() == null || SignalStore.account().getPni() == null) {
|
||||
Log.w(TAG, "Not registered. Skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
SignalProtocolStore aciProtocolStore = ApplicationDependencies.getProtocolStore().aci();
|
||||
PreKeyMetadataStore aciPreKeyStore = SignalStore.account().aciPreKeys();
|
||||
|
||||
SignalProtocolStore pniProtocolStore = ApplicationDependencies.getProtocolStore().pni();
|
||||
PreKeyMetadataStore pniPreKeyStore = SignalStore.account().pniPreKeys();
|
||||
|
||||
if (refreshKeys(ServiceIdType.ACI, aciProtocolStore, aciPreKeyStore)) {
|
||||
PreKeyUtil.cleanSignedPreKeys(aciProtocolStore, aciPreKeyStore);
|
||||
}
|
||||
|
||||
if (refreshKeys(ServiceIdType.PNI, pniProtocolStore, pniPreKeyStore)) {
|
||||
PreKeyUtil.cleanSignedPreKeys(pniProtocolStore, pniPreKeyStore);
|
||||
}
|
||||
|
||||
SignalStore.misc().setLastPrekeyRefreshTime(System.currentTimeMillis());
|
||||
Log.i(TAG, "Successfully refreshed prekeys.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if we need to clean prekeys, otherwise false.
|
||||
*/
|
||||
private boolean refreshKeys(@NonNull ServiceIdType serviceIdType, @NonNull SignalProtocolStore protocolStore, @NonNull PreKeyMetadataStore metadataStore) throws IOException {
|
||||
String logPrefix = "[" + serviceIdType + "] ";
|
||||
|
||||
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
||||
|
||||
int availableKeys = accountManager.getPreKeysCount(serviceIdType);
|
||||
log(TAG, logPrefix + "Available keys: " + availableKeys);
|
||||
|
||||
if (availableKeys >= PREKEY_MINIMUM && metadataStore.isSignedPreKeyRegistered()) {
|
||||
log(TAG, logPrefix + "Available keys sufficient.");
|
||||
return false;
|
||||
}
|
||||
|
||||
List<PreKeyRecord> preKeyRecords = PreKeyUtil.generateAndStoreOneTimePreKeys(protocolStore, metadataStore);
|
||||
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore, false);
|
||||
IdentityKeyPair identityKey = protocolStore.getIdentityKeyPair();
|
||||
|
||||
log(TAG, logPrefix + "Registering new prekeys...");
|
||||
|
||||
accountManager.setPreKeys(serviceIdType, identityKey.getPublicKey(), signedPreKeyRecord, preKeyRecords);
|
||||
|
||||
metadataStore.setActiveSignedPreKeyId(signedPreKeyRecord.getId());
|
||||
metadataStore.setSignedPreKeyRegistered(true);
|
||||
|
||||
log(TAG, logPrefix + "Need to clean prekeys.");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
||||
if (exception instanceof NonSuccessfulResponseCodeException) return false;
|
||||
if (exception instanceof PushNetworkException) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
}
|
||||
|
||||
public static final class Factory implements Job.Factory<RefreshPreKeysJob> {
|
||||
@Override
|
||||
public @NonNull RefreshPreKeysJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||
return new RefreshPreKeysJob(parameters);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.libsignal.protocol.state.SignalProtocolStore;
|
||||
import org.signal.libsignal.protocol.state.SignedPreKeyRecord;
|
||||
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.storage.PreKeyMetadataStore;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.push.ACI;
|
||||
import org.whispersystems.signalservice.api.push.PNI;
|
||||
import org.whispersystems.signalservice.api.push.ServiceIdType;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Forces the creation + upload of new signed prekeys for both the ACI and PNI identities.
|
||||
*/
|
||||
public class RotateSignedPreKeyJob extends BaseJob {
|
||||
|
||||
public static final String KEY = "RotateSignedPreKeyJob";
|
||||
|
||||
private static final String TAG = Log.tag(RotateSignedPreKeyJob.class);
|
||||
|
||||
public RotateSignedPreKeyJob() {
|
||||
this(new Job.Parameters.Builder()
|
||||
.setQueue("RotateSignedPreKeyJob")
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setMaxInstancesForFactory(1)
|
||||
.setMaxAttempts(Parameters.UNLIMITED)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(2))
|
||||
.build());
|
||||
}
|
||||
|
||||
private RotateSignedPreKeyJob(@NonNull Job.Parameters parameters) {
|
||||
super(parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Data serialize() {
|
||||
return Data.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getFactoryKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun() throws Exception {
|
||||
if (!SignalStore.account().isRegistered() || SignalStore.account().getAci() == null || SignalStore.account().getPni() == null) {
|
||||
Log.w(TAG, "Not registered. Skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.i(TAG, "Rotating signed prekey...");
|
||||
|
||||
ACI aci = SignalStore.account().getAci();
|
||||
PNI pni = SignalStore.account().getPni();
|
||||
|
||||
if (aci == null) {
|
||||
Log.w(TAG, "ACI is unset!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pni == null) {
|
||||
Log.w(TAG, "PNI is unset!");
|
||||
return;
|
||||
}
|
||||
|
||||
rotate(ServiceIdType.ACI, ApplicationDependencies.getProtocolStore().aci(), SignalStore.account().aciPreKeys());
|
||||
rotate(ServiceIdType.PNI, ApplicationDependencies.getProtocolStore().pni(), SignalStore.account().pniPreKeys());
|
||||
}
|
||||
|
||||
private void rotate(@NonNull ServiceIdType serviceIdType, @NonNull SignalProtocolStore protocolStore, @NonNull PreKeyMetadataStore metadataStore)
|
||||
throws IOException
|
||||
{
|
||||
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
||||
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore, false);
|
||||
|
||||
accountManager.setSignedPreKey(serviceIdType, signedPreKeyRecord);
|
||||
|
||||
metadataStore.setActiveSignedPreKeyId(signedPreKeyRecord.getId());
|
||||
metadataStore.setSignedPreKeyRegistered(true);
|
||||
metadataStore.setSignedPreKeyFailureCount(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
||||
return exception instanceof PushNetworkException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
PreKeyMetadataStore aciStore = SignalStore.account().aciPreKeys();
|
||||
PreKeyMetadataStore pniStore = SignalStore.account().pniPreKeys();
|
||||
|
||||
aciStore.setSignedPreKeyFailureCount(aciStore.getSignedPreKeyFailureCount() + 1);
|
||||
pniStore.setSignedPreKeyFailureCount(pniStore.getSignedPreKeyFailureCount() + 1);
|
||||
}
|
||||
|
||||
public static final class Factory implements Job.Factory<RotateSignedPreKeyJob> {
|
||||
@Override
|
||||
public @NonNull RotateSignedPreKeyJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||
return new RotateSignedPreKeyJob(parameters);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,7 +33,7 @@ import org.thoughtcrime.securesms.groups.BadGroupIdException;
|
|||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobs.AutomaticSessionResetJob;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
||||
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob;
|
||||
import org.thoughtcrime.securesms.jobs.SendRetryReceiptJob;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.logsubmit.SubmitDebugLogActivity;
|
||||
|
@ -43,7 +43,6 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
|||
import org.thoughtcrime.securesms.notifications.NotificationIds;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.whispersystems.signalservice.api.InvalidMessageStructureException;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountDataStore;
|
||||
import org.whispersystems.signalservice.api.crypto.ContentHint;
|
||||
|
@ -101,7 +100,7 @@ public final class MessageDecryptionUtil {
|
|||
List<Job> jobs = new LinkedList<>();
|
||||
|
||||
if (envelope.isPreKeySignalMessage()) {
|
||||
jobs.add(new RefreshPreKeysJob());
|
||||
PreKeysSyncJob.enqueue();
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
@ -20,8 +20,8 @@ import org.thoughtcrime.securesms.jobmanager.Data;
|
|||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
||||
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
|
||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushDecryptMessageJob;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
|
@ -129,7 +129,7 @@ public class LegacyMigrationJob extends MigrationJob {
|
|||
}
|
||||
|
||||
if (lastSeenVersion < SIGNED_PREKEY_VERSION) {
|
||||
CreateSignedPreKeyJob.enqueueIfNeeded();
|
||||
PreKeysSyncJob.enqueueIfNeeded();
|
||||
}
|
||||
|
||||
if (lastSeenVersion < NO_DECRYPT_QUEUE_VERSION) {
|
||||
|
|
|
@ -75,10 +75,11 @@ public class PniAccountInitializationMigrationJob extends MigrationJob {
|
|||
|
||||
if (!metadataStore.isSignedPreKeyRegistered()) {
|
||||
Log.i(TAG, "Uploading signed prekey for PNI.");
|
||||
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore, true);
|
||||
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore);
|
||||
List<PreKeyRecord> oneTimePreKeys = PreKeyUtil.generateAndStoreOneTimePreKeys(protocolStore, metadataStore);
|
||||
|
||||
accountManager.setPreKeys(ServiceIdType.PNI, protocolStore.getIdentityKeyPair().getPublicKey(), signedPreKey, oneTimePreKeys);
|
||||
metadataStore.setActiveSignedPreKeyId(signedPreKey.getId());
|
||||
metadataStore.setSignedPreKeyRegistered(true);
|
||||
} else {
|
||||
Log.w(TAG, "Already uploaded signed prekey for PNI. Skipping this step.");
|
||||
|
|
|
@ -190,10 +190,11 @@ public final class RegistrationRepository {
|
|||
@NonNull PreKeyMetadataStore metadataStore)
|
||||
throws IOException
|
||||
{
|
||||
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore, true);
|
||||
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore);
|
||||
List<PreKeyRecord> oneTimePreKeys = PreKeyUtil.generateAndStoreOneTimePreKeys(protocolStore, metadataStore);
|
||||
|
||||
accountManager.setPreKeys(serviceIdType, protocolStore.getIdentityKeyPair().getPublicKey(), signedPreKey, oneTimePreKeys);
|
||||
metadataStore.setActiveSignedPreKeyId(signedPreKey.getId());
|
||||
metadataStore.setSignedPreKeyRegistered(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobs.RotateSignedPreKeyJob;
|
||||
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
|
@ -23,7 +23,7 @@ public class RotateSignedPreKeyListener extends PersistentAlarmManagerListener {
|
|||
@Override
|
||||
protected long onAlarm(Context context, long scheduledTime) {
|
||||
if (scheduledTime != 0 && SignalStore.account().isRegistered()) {
|
||||
ApplicationDependencies.getJobManager().add(new RotateSignedPreKeyJob());
|
||||
PreKeysSyncJob.enqueue(true);
|
||||
}
|
||||
|
||||
long nextTime = System.currentTimeMillis() + INTERVAL;
|
||||
|
|
|
@ -15,6 +15,10 @@ public class PreKeyStatus {
|
|||
|
||||
public PreKeyStatus() {}
|
||||
|
||||
public PreKeyStatus(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue