Skip to content

Commit

Permalink
Fix linked device compatibility with SSRE2
Browse files Browse the repository at this point in the history
  • Loading branch information
valldrac committed Jan 8, 2025
1 parent 28f8941 commit b3d0382
Show file tree
Hide file tree
Showing 10 changed files with 17 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public void onRun() throws IOException {
String registrationLockV2 = null;
SvrValues svrValues = SignalStore.svr();
int pniRegistrationId = RegistrationRepository.getPniRegistrationId();
String recoveryPassword = svrValues.hasPin() ? svrValues.getMasterKey().deriveRegistrationRecoveryPassword() : null;
String recoveryPassword = svrValues.getRecoveryPassword();

if (svrValues.isRegistrationLockEnabled()) {
registrationLockV2 = svrValues.getRegistrationLockToken();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ class StorageSyncJob private constructor(parameters: Parameters) : BaseJob(param

@Throws(IOException::class, RetryLaterException::class, UntrustedIdentityException::class)
override fun onRun() {
if (!SignalStore.svr.hasOptedInWithAccess() && !SignalStore.svr.hasOptedOut() && !SignalStore.storageService.hasStorageKeyFromPrimary()) {
if (!SignalStore.svr.hasOptedInWithAccess() && !SignalStore.svr.hasOptedOut()) {
Log.i(TAG, "Doesn't have a PIN. Skipping.")
return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,6 @@ class StorageServiceValues internal constructor(store: KeyValueStore) : SignalSt
remove(SYNC_STORAGE_KEY)
}

@Synchronized
fun hasStorageKeyFromPrimary(): Boolean {
return SignalStore.account.isLinkedDevice && store.containsKey(SYNC_STORAGE_KEY)
}

var lastSyncTime: Long by longValue(LAST_SYNC_TIME, 0)

var needsAccountRestore: Boolean by booleanValue(NEEDS_ACCOUNT_RESTORE, false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ class SvrValues internal constructor(store: KeyValueStore) : SignalStoreValues(s

@Synchronized
fun hasOptedInWithAccess(): Boolean {
return hasPin() || restoredViaAccountEntropyPool
return hasPin() || restoredViaAccountEntropyPool || SignalStore.account.isLinkedDevice
}

@Synchronized
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ private static boolean isEnabled() {
return false;
}

if (SignalStore.svr().hasOptedInWithAccess() || SignalStore.account().isLinkedDevice()) {
if (SignalStore.svr().hasOptedInWithAccess()) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
package org.thoughtcrime.securesms.messages

import android.content.Context
import okio.ByteString
import org.signal.core.util.Base64
import org.signal.core.util.Hex
import org.signal.core.util.isNotEmpty
import org.signal.core.util.orNull
import org.signal.libsignal.protocol.IdentityKey
import org.signal.libsignal.protocol.IdentityKeyPair
import org.signal.libsignal.protocol.InvalidKeyException
import org.signal.libsignal.protocol.InvalidMessageException
import org.signal.libsignal.protocol.SignalProtocolAddress
import org.signal.libsignal.protocol.state.SignedPreKeyRecord
import org.signal.libsignal.protocol.util.Pair
import org.signal.ringrtc.CallException
import org.signal.ringrtc.CallId
Expand All @@ -21,14 +17,12 @@ import org.thoughtcrime.securesms.attachments.DatabaseAttachment
import org.thoughtcrime.securesms.attachments.TombstoneAttachment
import org.thoughtcrime.securesms.components.emoji.EmojiUtil
import org.thoughtcrime.securesms.contactshare.Contact
import org.thoughtcrime.securesms.crypto.PreKeyUtil
import org.thoughtcrime.securesms.crypto.SecurityEvent
import org.thoughtcrime.securesms.database.AttachmentTable
import org.thoughtcrime.securesms.database.CallLinkTable
import org.thoughtcrime.securesms.database.CallTable
import org.thoughtcrime.securesms.database.GroupReceiptTable
import org.thoughtcrime.securesms.database.GroupTable
import org.thoughtcrime.securesms.database.IdentityTable
import org.thoughtcrime.securesms.database.MessageTable
import org.thoughtcrime.securesms.database.MessageTable.MarkedMessageInfo
import org.thoughtcrime.securesms.database.NoSuchMessageException
Expand Down Expand Up @@ -59,9 +53,7 @@ import org.thoughtcrime.securesms.jobs.MultiDeviceStickerPackSyncJob
import org.thoughtcrime.securesms.jobs.PushProcessEarlyMessagesJob
import org.thoughtcrime.securesms.jobs.RefreshCallLinkDetailsJob
import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob
import org.thoughtcrime.securesms.jobs.RotateCertificateJob
import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob
import org.thoughtcrime.securesms.jobs.StorageSyncJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.linkpreview.LinkPreview
import org.thoughtcrime.securesms.messages.MessageContentProcessor.Companion.log
Expand Down Expand Up @@ -100,16 +92,16 @@ import org.thoughtcrime.securesms.util.MediaUtil
import org.thoughtcrime.securesms.util.MessageConstraintsUtil
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.api.account.PreKeyUpload
import org.whispersystems.signalservice.api.AccountEntropyPool
import org.whispersystems.signalservice.api.backup.MediaRootBackupKey
import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata
import org.whispersystems.signalservice.api.kbs.MasterKey
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer
import org.whispersystems.signalservice.api.push.DistributionId
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.push.ServiceId.PNI
import org.whispersystems.signalservice.api.push.ServiceIdType
import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.api.storage.StorageKey
import org.whispersystems.signalservice.api.util.UuidUtil
import org.whispersystems.signalservice.internal.push.Content
import org.whispersystems.signalservice.internal.push.DataMessage
Expand All @@ -123,7 +115,6 @@ import org.whispersystems.signalservice.internal.push.SyncMessage.CallLogEvent
import org.whispersystems.signalservice.internal.push.SyncMessage.Configuration
import org.whispersystems.signalservice.internal.push.SyncMessage.FetchLatest
import org.whispersystems.signalservice.internal.push.SyncMessage.MessageRequestResponse
import org.whispersystems.signalservice.internal.push.SyncMessage.PniChangeNumber
import org.whispersystems.signalservice.internal.push.SyncMessage.Read
import org.whispersystems.signalservice.internal.push.SyncMessage.Request
import org.whispersystems.signalservice.internal.push.SyncMessage.Sent
Expand Down Expand Up @@ -162,9 +153,8 @@ object SyncMessageProcessor {
syncMessage.fetchLatest?.type != null -> handleSynchronizeFetchMessage(syncMessage.fetchLatest!!.type!!, envelope.timestamp!!)
syncMessage.messageRequestResponse != null -> handleSynchronizeMessageRequestResponse(syncMessage.messageRequestResponse!!, envelope.timestamp!!)
syncMessage.outgoingPayment != null -> handleSynchronizeOutgoingPayment(syncMessage.outgoingPayment!!, envelope.timestamp!!)
syncMessage.keys?.storageService != null -> handleSynchronizeKeys(syncMessage.keys!!.storageService!!, envelope.timestamp!!)
syncMessage.keys?.storageService != null -> handleSynchronizeKeys(syncMessage.keys!!, envelope.timestamp!!)
syncMessage.contacts != null -> handleSynchronizeContacts(syncMessage.contacts!!, envelope.timestamp!!)
syncMessage.pniChangeNumber != null -> handleSynchronizePniChangeNumber(syncMessage.pniChangeNumber!!, envelope.updatedPni, envelope.timestamp!!)
syncMessage.callEvent != null -> handleSynchronizeCallEvent(syncMessage.callEvent!!, envelope.timestamp!!)
syncMessage.callLinkUpdate != null -> handleSynchronizeCallLink(syncMessage.callLinkUpdate!!, envelope.timestamp!!)
syncMessage.callLogEvent != null -> handleSynchronizeCallLogEvent(syncMessage.callLogEvent!!, envelope.timestamp!!)
Expand Down Expand Up @@ -1188,15 +1178,17 @@ object SyncMessageProcessor {
log(envelopeTimestamp, "Outgoing payment, ignoring.")
}

private fun handleSynchronizeKeys(storageKey: ByteString, envelopeTimestamp: Long) {
private fun handleSynchronizeKeys(keys: SyncMessage.Keys, envelopeTimestamp: Long) {
if (SignalStore.account.isLinkedDevice) {
log(envelopeTimestamp, "Synchronize keys.")
} else {
log(envelopeTimestamp, "Primary device ignores synchronize keys.")
return
}

SignalStore.storageService.setStorageKeyFromPrimary(StorageKey(storageKey.toByteArray()))
keys.accountEntropyPool?.let { SignalStore.account.restoreAccountEntropyPool(AccountEntropyPool(it)) }
keys.mediaRootBackupKey?.let { SignalStore.backup.mediaRootBackupKey = MediaRootBackupKey(it.toByteArray()) }
keys.master?.let { SignalStore.svr.masterKeyForInitialDataRestore = MasterKey(it.toByteArray()) }
}

@Throws(IOException::class)
Expand Down Expand Up @@ -1645,65 +1637,4 @@ object SyncMessageProcessor {
return AttachmentTable.SyncAttachmentId(syncMessageId, uuid, digest, plaintextHash)
}
}

// MOLLY: FIXME
private fun handleSynchronizePniChangeNumber(pniChangeNumber: PniChangeNumber, updatedPni: String?, envelopeTimestamp: Long) {
if (SignalStore.account.isLinkedDevice) {
log(envelopeTimestamp, "Primary device changed number. Synchronizing.")
} else {
log(envelopeTimestamp, "Primary device should not receive number change updates. Ignoring.")
return
}

if (pniChangeNumber.identityKeyPair == null || pniChangeNumber.registrationId == null || pniChangeNumber.signedPreKey == null || updatedPni == null) {
warn(envelopeTimestamp, "Incomplete synchronize PNI number message. Ignoring.")
return
}

try {
val signedPreKey = SignedPreKeyRecord(pniChangeNumber.signedPreKey!!.toByteArray())
val pni = PNI.parseOrThrow(updatedPni)

val pniProtocolStore = AppDependencies.protocolStore.pni()
val pniMetadataStore = SignalStore.account.pniPreKeys

SignalStore.account.setPni(pni)
SignalStore.account.pniRegistrationId = pniChangeNumber.registrationId!!
SignalStore.account.setPniIdentityKeyAfterChangeNumber(IdentityKeyPair(pniChangeNumber.identityKeyPair!!.toByteArray()))

pniProtocolStore.storeSignedPreKey(signedPreKey.id, signedPreKey)
val oneTimePreKeys = PreKeyUtil.generateAndStoreOneTimeEcPreKeys(pniProtocolStore, pniMetadataStore)

pniMetadataStore.activeSignedPreKeyId = signedPreKey.id
AppDependencies.signalServiceAccountManager.setPreKeys(
PreKeyUpload(
serviceIdType = ServiceIdType.PNI,
signedPreKey = signedPreKey,
oneTimeEcPreKeys = oneTimePreKeys,
lastResortKyberPreKey = null,
oneTimeKyberPreKeys = null
)
)
pniMetadataStore.isSignedPreKeyRegistered = true

pniProtocolStore.identities().saveIdentityWithoutSideEffects(
Recipient.self().id,
pni,
pniProtocolStore.identityKeyPair.publicKey,
IdentityTable.VerifiedStatus.VERIFIED,
true,
System.currentTimeMillis(),
true
)

AppDependencies.groupsV2Authorization.clear()
} catch (e: InvalidMessageException) {
warn(envelopeTimestamp, "Invalid signed prekey received while synchronize number change", e)
return
}

AppDependencies.resetNetwork(restartMessageObserver = true)

AppDependencies.jobManager.add(RotateCertificateJob())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class LinkDeviceRepository(password: String) {
SignalStore.account.setDeviceName(deviceName)
SignalStore.account.setAciIdentityKeysFromPrimaryDevice(registration.aciIdentity)
SignalStore.account.setPniIdentityKeyAfterChangeNumber(registration.pniIdentity)
SignalStore.account.hasLinkedDevices = true
SignalStore.registration.hasUploadedProfile = true

AccountRegistrationResult(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ private RegistrationUtil() {}
public static void maybeMarkRegistrationComplete() {
if (!SignalStore.registration().isRegistrationComplete() &&
SignalStore.account().isRegistered() &&
((!Recipient.self().getProfileName().isEmpty() &&
(SignalStore.svr().hasOptedInWithAccess() || SignalStore.svr().hasOptedOut()) &&
(!RemoteConfig.restoreAfterRegistration() || (SignalStore.registration().hasSkippedTransferOrRestore() || SignalStore.registration().hasCompletedRestore()))) || SignalStore.account().isLinkedDevice()))
!Recipient.self().getProfileName().isEmpty() &&
(SignalStore.svr().hasOptedInWithAccess() || SignalStore.svr().hasOptedOut()) &&
(!RemoteConfig.restoreAfterRegistration() || (SignalStore.registration().hasSkippedTransferOrRestore() || SignalStore.registration().hasCompletedRestore())))
{
Log.i(TAG, "Marking registration completed.", new Throwable());
SignalStore.registration().markRegistrationComplete();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ public class SignalServiceEnvelopeEntity {
@JsonProperty
private Boolean urgent;

@JsonProperty
private String updatedPni;

@JsonProperty
private Boolean story;

Expand Down Expand Up @@ -103,10 +100,6 @@ public boolean isUrgent() {
return urgent == null || urgent;
}

public String getUpdatedPni() {
return updatedPni;
}

public boolean isStory() {
return story != null && story;
}
Expand Down
2 changes: 1 addition & 1 deletion libsignal-service/src/main/protowire/SignalService.proto
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ message Envelope {
optional string serverGuid = 9;
optional uint64 serverTimestamp = 10;
optional bool urgent = 14 [default = true];
optional string updatedPni = 15; // MOLLY: Used by linked device if primary device changes phone number
reserved /*updatedPni*/ 15; // Not used presently, may be used in the future
optional bool story = 16;
optional bytes reportingToken = 17;
reserved 18; // internal server use
Expand Down

0 comments on commit b3d0382

Please sign in to comment.