Add device linking infrastructure.

This commit is contained in:
Cody Henthorne
2025-08-01 14:16:31 -04:00
parent e6e869e074
commit e29abdea91
23 changed files with 440 additions and 119 deletions

View File

@@ -88,6 +88,7 @@ class AccountValues internal constructor(store: KeyValueStore, context: Context)
private const val KEY_ACCOUNT_ENTROPY_POOL = "account.account_entropy_pool"
private const val KEY_RESTORED_ACCOUNT_ENTROPY_KEY = "account.restored_account_entropy_pool"
private const val KEY_RESTORED_ACCOUNT_ENTROPY_KEY_FROM_PRIMARY = "account.restore_account_entropy_pool_primary"
private val AEP_LOCK = ReentrantLock()
}
@@ -151,6 +152,17 @@ class AccountValues internal constructor(store: KeyValueStore, context: Context)
}
}
fun setAccountEntropyPoolFromPrimaryDevice(aep: AccountEntropyPool) {
AEP_LOCK.withLock {
Log.i(TAG, "Setting new AEP from primary device")
store
.beginWrite()
.putString(KEY_ACCOUNT_ENTROPY_POOL, aep.value)
.putBoolean(KEY_RESTORED_ACCOUNT_ENTROPY_KEY_FROM_PRIMARY, true)
.commit()
}
}
fun restoreAccountEntropyPool(aep: AccountEntropyPool) {
AEP_LOCK.withLock {
store
@@ -173,9 +185,10 @@ class AccountValues internal constructor(store: KeyValueStore, context: Context)
}
@get:JvmName("restoredAccountEntropyPool")
@get:Synchronized
val restoredAccountEntropyPool by booleanValue(KEY_RESTORED_ACCOUNT_ENTROPY_KEY, false)
val restoredAccountEntropyPoolFromPrimary by booleanValue(KEY_RESTORED_ACCOUNT_ENTROPY_KEY_FROM_PRIMARY, false)
/** The local user's [ACI]. */
val aci: ACI?
get() = ACI.parseOrNull(getString(KEY_ACI, null))
@@ -300,18 +313,6 @@ class AccountValues internal constructor(store: KeyValueStore, context: Context)
}
}
/** When acting as a linked device, this method lets you store the identity keys sent from the primary device */
fun setAciIdentityKeysFromPrimaryDevice(aciKeys: IdentityKeyPair) {
synchronized(this) {
require(isLinkedDevice) { "Must be a linked device!" }
store
.beginWrite()
.putBlob(KEY_ACI_IDENTITY_PUBLIC_KEY, aciKeys.publicKey.serialize())
.putBlob(KEY_ACI_IDENTITY_PRIVATE_KEY, aciKeys.privateKey.serialize())
.commit()
}
}
/** Set an identity key pair for the PNI identity via change number. */
fun setPniIdentityKeyAfterChangeNumber(key: IdentityKeyPair) {
synchronized(this) {
@@ -475,12 +476,7 @@ class AccountValues internal constructor(store: KeyValueStore, context: Context)
Recipient.self().live().refresh()
}
val deviceName: String?
get() = getString(KEY_DEVICE_NAME, null)
fun setDeviceName(deviceName: String) {
putString(KEY_DEVICE_NAME, deviceName)
}
var deviceName: String? by stringValue(KEY_DEVICE_NAME, null)
var deviceId: Int by integerValue(KEY_DEVICE_ID, SignalServiceAddress.DEFAULT_DEVICE_ID)

View File

@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.keyvalue
import org.signal.core.util.logging.Log
import org.whispersystems.signalservice.api.storage.SignalStorageManifest
import org.whispersystems.signalservice.api.storage.StorageKey
import org.whispersystems.signalservice.api.util.Preconditions
class StorageServiceValues internal constructor(store: KeyValueStore) : SignalStoreValues(store) {
companion object {
@@ -12,9 +11,6 @@ class StorageServiceValues internal constructor(store: KeyValueStore) : SignalSt
private const val LAST_SYNC_TIME = "storage.last_sync_time"
private const val NEEDS_ACCOUNT_RESTORE = "storage.needs_account_restore"
private const val MANIFEST = "storage.manifest"
// TODO [linked-device] No need to track this separately -- we'd get the AEP from the primary
private const val SYNC_STORAGE_KEY = "storage.syncStorageKey"
}
public override fun onFirstEverAppLaunch() = Unit
@@ -23,24 +19,9 @@ class StorageServiceValues internal constructor(store: KeyValueStore) : SignalSt
val storageKey: StorageKey
get() {
if (store.containsKey(SYNC_STORAGE_KEY)) {
return StorageKey(getBlob(SYNC_STORAGE_KEY, null))
}
return SignalStore.svr.masterKey.deriveStorageServiceKey()
}
@Synchronized
fun setStorageKeyFromPrimary(storageKey: StorageKey) {
Preconditions.checkState(SignalStore.account.isLinkedDevice, "Can only set storage key directly on linked devices")
putBlob(SYNC_STORAGE_KEY, storageKey.serialize())
}
@Synchronized
fun clearStorageKeyFromPrimary() {
Preconditions.checkState(SignalStore.account.isLinkedDevice, "Can only clear storage key directly on linked devices")
remove(SYNC_STORAGE_KEY)
}
var lastSyncTime: Long by longValue(LAST_SYNC_TIME, 0)
var needsAccountRestore: Boolean by booleanValue(NEEDS_ACCOUNT_RESTORE, false)