@@ -31,7 +31,7 @@ import org.signal.libsignal.messagebackup.BackupKey
31
31
*
32
32
* 1. Create a [Network] instance and get the [SvrB] service via [Network.svrB]
33
33
* 2. Call [SvrB.store]
34
- * - Pass the secret data from the last **successful** [SvrB.store] call
34
+ * - Pass the `nextBackupSecretData` from the last **successful** [SvrB.store] or [SvrB.restore ] call
35
35
* - If no previous backup exists or the secret data is unavailable, pass `null`
36
36
* 3. Use the returned forward secrecy token to derive encryption keys
37
37
* 4. Encrypt and upload the backup data to the user's remote, off-device storage location, including the
@@ -42,8 +42,8 @@ import org.signal.libsignal.messagebackup.BackupKey
42
42
* ## Secret handling
43
43
*
44
44
* When calling [SvrB.store], the `previousSecretData` parameter
45
- * must be from the last call to [SvrB.store] that
46
- * succeeded. The returned secret from a successful `store()` call should
45
+ * must be from the last call to [SvrB.store] or [SvrB.restore] that
46
+ * succeeded. The returned secret from a successful `store()` or `restore()` call should
47
47
* be persisted until it is overwritten by the value from a subsequent
48
48
* successful call. The caller should pass `null` as `previousSecretData`
49
49
* only for the very first backup from a device.
@@ -52,9 +52,10 @@ import org.signal.libsignal.messagebackup.BackupKey
52
52
*
53
53
* 1. Create a [Network] instance and get the [SvrB] service via [Network.svrB]
54
54
* 2. Fetch the backup metadata from storage
55
- * 3. Call [SvrB.fetchForwardSecrecyTokenFromServer ] to get the forward secrecy token
55
+ * 3. Call [SvrB.restore ] to get the forward secrecy token
56
56
* 4. Use the token to derive decryption keys
57
57
* 5. Decrypt and restore the backup data
58
+ * 6. Store the [SvrBRestoreResponse.nextBackupSecretData] locally.
58
59
*
59
60
* ## Usage
60
61
* ```kotlin
@@ -85,9 +86,8 @@ public class SvrB internal constructor(
85
86
*
86
87
* @param backupKey The backup key derived from the Account Entropy Pool (AEP).
87
88
* @param previousSecretData Optional secret data from the most recent previous backup.
88
- * **Critical**: This MUST be the [SvrBStoreResponse.nextBackupSecretData] data
89
- * from the last [store] whose returned [SvrBStoreResponse.metadata] was
90
- * successfully uploaded, and whose `nextBackupSecretData` was persisted.
89
+ * **Critical**: This MUST be the secret data from the last [store] or [restore]
90
+ * whose returned `metadata` was successfully uploaded, and whose `nextBackupSecretData` was persisted.
91
91
* If `null`, starts a new chain and renders any prior backups unretrievable; this should
92
92
* only be used for the very first backup from a device.
93
93
* @return a [CompletableFuture] that completes with:
@@ -96,7 +96,6 @@ public class SvrB internal constructor(
96
96
* - [Result.failure] containing [NetworkException] if the network operation fails (connection, service, or timeout errors)
97
97
* - [Result.failure] containing [NetworkProtocolException] if there is a protocol error
98
98
* - [Result.failure] containing [AttestationFailedException] if enclave attestation fails
99
- * - [Result.failure] containing [DataMissingException] if the request fails with MISSING status
100
99
* - [Result.failure] containing [SvrException] for other SVR request failures
101
100
*/
102
101
public fun store (
@@ -117,14 +116,14 @@ public class SvrB internal constructor(
117
116
}
118
117
119
118
return nativeFuture.thenApply { backupResponseHandle ->
120
- val response = BackupResponse (backupResponseHandle)
119
+ val response = BackupStoreResponse (backupResponseHandle)
121
120
response.guardedMap { _ ->
122
121
SvrBStoreResponse (
123
122
forwardSecrecyToken = BackupForwardSecrecyToken (
124
- response.guardedMapChecked(Native ::BackupResponse_GetForwardSecrecyToken ),
123
+ response.guardedMapChecked(Native ::BackupStoreResponse_GetForwardSecrecyToken ),
125
124
),
126
- nextBackupSecretData = response.guardedMapChecked(Native ::BackupResponse_GetNextBackupSecretData ),
127
- metadata = response.guardedMapChecked(Native ::BackupResponse_GetOpaqueMetadata ),
125
+ nextBackupSecretData = response.guardedMapChecked(Native ::BackupStoreResponse_GetNextBackupSecretData ),
126
+ metadata = response.guardedMapChecked(Native ::BackupStoreResponse_GetOpaqueMetadata ),
128
127
)
129
128
}
130
129
}.toResultFuture()
@@ -142,6 +141,7 @@ public class SvrB internal constructor(
142
141
* 2. Call this function to retrieve the forward secrecy token from SVR-B
143
142
* 3. Use the token to derive message backup keys
144
143
* 4. Decrypt and restore the backup data
144
+ * 5. Store the returned [SvrBRestoreResponse.nextBackupSecretData] locally.
145
145
*
146
146
* @param backupKey The backup key derived from the Account Entropy Pool (AEP).
147
147
* @param metadata The metadata that was stored in a header in the backup file during backup creation.
@@ -155,10 +155,10 @@ public class SvrB internal constructor(
155
155
* - [Result.failure] containing [AttestationFailedException] if enclave attestation fails
156
156
* - [Result.failure] containing [SvrException] for other SVR request failures
157
157
*/
158
- public fun fetchForwardSecrecyTokenFromServer (
158
+ public fun restore (
159
159
backupKey : BackupKey ,
160
160
metadata : ByteArray ,
161
- ): CompletableFuture <Result <BackupForwardSecrecyToken >> {
161
+ ): CompletableFuture <Result <SvrBRestoreResponse >> {
162
162
val nativeFuture = network.asyncContext.guardedMap { asyncContextHandle ->
163
163
network.connectionManager.guardedMap { connectionManagerHandle ->
164
164
Native .SecureValueRecoveryForBackups_RestoreBackupFromServer (
@@ -172,20 +172,39 @@ public class SvrB internal constructor(
172
172
}
173
173
}
174
174
175
- return nativeFuture.thenApply { bytes ->
176
- BackupForwardSecrecyToken (bytes)
175
+ return nativeFuture.thenApply { backupResponseHandle ->
176
+ val response = BackupRestoreResponse (backupResponseHandle)
177
+ response.guardedMap { _ ->
178
+ SvrBRestoreResponse (
179
+ forwardSecrecyToken = BackupForwardSecrecyToken (
180
+ response.guardedMapChecked(Native ::BackupRestoreResponse_GetForwardSecrecyToken ),
181
+ ),
182
+ nextBackupSecretData = response.guardedMapChecked(Native ::BackupRestoreResponse_GetNextBackupSecretData ),
183
+ )
184
+ }
177
185
}.toResultFuture()
178
186
}
179
187
}
180
188
181
189
/* *
182
190
* Native handle wrapper for backup response from the store operation.
183
191
*/
184
- private class BackupResponse internal constructor(
192
+ private class BackupStoreResponse internal constructor(
193
+ nativeHandle : Long ,
194
+ ) : NativeHandleGuard.SimpleOwner(nativeHandle) {
195
+ override fun release (nativeHandle : Long ) {
196
+ Native .BackupStoreResponse_Destroy (nativeHandle)
197
+ }
198
+ }
199
+
200
+ /* *
201
+ * Native handle wrapper for backup response from the restore operation.
202
+ */
203
+ private class BackupRestoreResponse internal constructor(
185
204
nativeHandle : Long ,
186
205
) : NativeHandleGuard.SimpleOwner(nativeHandle) {
187
206
override fun release (nativeHandle : Long ) {
188
- Native .BackupResponse_Destroy (nativeHandle)
207
+ Native .BackupRestoreResponse_Destroy (nativeHandle)
189
208
}
190
209
}
191
210
@@ -226,3 +245,31 @@ public data class SvrBStoreResponse(
226
245
*/
227
246
public val metadata : ByteArray ,
228
247
)
248
+
249
+ /* *
250
+ * The result of restoring a backup.
251
+ *
252
+ * This context contains all the necessary components to decrypt a backup using a
253
+ * key derived from both the user's Account Entropy Pool and the SVR-B-protected
254
+ * Forward Secrecy Token.
255
+ *
256
+ * @see [BackupForwardSecrecyToken]
257
+ */
258
+ public data class SvrBRestoreResponse (
259
+ /* *
260
+ * The forward secrecy token used to derive [MessageBackupKey] instances.
261
+ *
262
+ * This token provides forward secrecy guarantees by ensuring that compromise of the backup key
263
+ * alone is insufficient to decrypt backups. Each backup is protected by a value stored on
264
+ * the SVR-B server that must be retrieved during restoration.
265
+ */
266
+ public val forwardSecrecyToken : BackupForwardSecrecyToken ,
267
+
268
+ /* *
269
+ * Opaque value that must be persisted and provided to the next call to [SvrB.store].
270
+ *
271
+ * See the [SvrB] documentation for lifecycle and persistence handling
272
+ * for this value.
273
+ */
274
+ public val nextBackupSecretData : ByteArray ,
275
+ )
0 commit comments