From 71227561e70c8c212003c5d1addb256853bfd279 Mon Sep 17 00:00:00 2001 From: null Date: Tue, 30 Jun 2026 21:24:30 -0500 Subject: [PATCH] feat(backup): pass recovery phrase through RestoreManager fulfill/complete (R24-c) --- .../java/app/closer/data/backup/RestoreManager.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/app/closer/data/backup/RestoreManager.kt b/app/src/main/java/app/closer/data/backup/RestoreManager.kt index 1123d98a..25f1c287 100644 --- a/app/src/main/java/app/closer/data/backup/RestoreManager.kt +++ b/app/src/main/java/app/closer/data/backup/RestoreManager.kt @@ -76,7 +76,7 @@ class RestoreManager @Inject constructor( ): Result = runCatching { val keybox = request.keybox ?: error("no keybox yet") val privateKey = userKeyManager.loadPrivateKey() ?: error("device key missing") - val keyset = coupleKeyTransfer.unwrapCoupleKey( + val transferred = coupleKeyTransfer.unwrapCoupleKey( keyboxB64 = keybox, recipientPrivateKey = privateKey, coupleId = session.coupleId, @@ -84,7 +84,8 @@ class RestoreManager @Inject constructor( recipientUid = session.recipientUid, nonce = request.requestNonce ) - encryptionManager.storeTransferredKeyset(session.coupleId, keyset) + // Store the key AND, if the partner included it, the couple's recovery phrase. + encryptionManager.storeTransferredKeyset(session.coupleId, transferred.keyset, transferred.recoveryPhrase) // Consume the request so no wrapped couple key lingers server-side. backupDataSource.deleteRestoreRequest(session.coupleId, session.recipientUid) backupRestoreManager.restore() @@ -106,13 +107,17 @@ class RestoreManager @Inject constructor( val expected = CoupleKeyTransfer.verificationCode(request.recipientPublicKey, request.requestNonce) require(enteredCode.trim() == expected) { "verification code does not match" } val coupleKeyset = encryptionManager.exportKeysetForTransfer(coupleId) ?: error("couple key missing on this device") + // Hand back the couple's recovery phrase too, if we have it, so the restored device isn't left + // without the self-recovery backstop. Null (we're phrase-less ourselves) → key-only, as before. + val recoveryPhrase = encryptionManager.recoveryPhrase(coupleId) val keybox = coupleKeyTransfer.wrapCoupleKey( coupleKeyset = coupleKeyset, recipientPublicKeyB64 = request.recipientPublicKey, coupleId = coupleId, senderUid = uid, recipientUid = recipientUid, - nonce = request.requestNonce + nonce = request.requestNonce, + recoveryPhrase = recoveryPhrase ) // Make sure the partner can also pull fresh content right after unlocking the key. runCatching { backupManager.backupNow(force = true) }