Backup and Recovery

Overview

As outlined in the Backup and Recovery Overview, the backup and recovery features enable the creation of an encrypted copy of the end-user key share, which is then sent to Fireblocks for safekeeping. This process becomes essential when a user may lose access to their device or need to transition to a new one.

The application or the user must generate the recovery passphrase for AES encryption of the end-user key share. The end user must securely preserve this passphrase. This precaution ensures that the private key share can be decrypted in a recovery situation, granting the user access to their key and enabling them to operate as usual.

Backing up the passphrase can be accomplished through various methods, and Fireblocks does not mandate any specific approach. For example, the end user can store the recovery passphrase in their iCloud account or Google Drive, or they may download and keep it locally on their device.

Terms to know

  • passphrase: The chosen passphrase for the backup. MPC key share #2 is encrypted using this passphrase and saved/encrypted along with the passphraseId from the Fireblocks cloud servers.
  • passphraseId: The UUID created by the application.
  • passphraseResolver: A callback that knows how to fetch the passphrase from its saved location when given a passphraseId value. For example, if you saved the passphrase on the end user's iCloud account, then when given the passphraseId value, the passphraseResolver callback fetches the passphrase from the iCloud account so that the recoverKeys function can decrypt the MPC key share.

Backup procedure

🚧

Prerequisites

Before running a backup procedure, you will need to have an MPC key share on the device.

This can be obtained by either:

  • Generating an MPC key share (new wallet scenarios) as shown here.
  • Running a recovery flow, as later described in this article.
await fireblocksNCW.backupKeys(passphrase, passphraseId);
// create a symmetric key for the encryption of the backup
var backupEncryptionKey = fireblocksSdk.generateRandomPassPhrase();

// store the backupEncryptionKey somewhere (user’s iCloud/Google, d/l, convert to seed phrase or other)

// backup the keys (including encryption)
fireblocks.backupKeys(passphrase, passphraseId) {
  Timber.d("Backup keys result: $it")
  callback.invoke(it)
}


let passphrase = Fireblocks.generateRandomPassPhrase()
// using concurrency
let result = await instance.backupKeys(passphrase: passphrase)

<--- OR --->

// using callback
try instance?.backupKeys(passphrase: passphrase, callback: { [weak self] result in
//handle result
})

Please note that steps 1 and 2 are under your implementation, while step 3 calls the Fireblocks SDK backup.

  1. Save the passphrase and the passphraseId on your preferred user cloud, such as iCloud or Google Drive. Note that passphraseId should be a UUID (enforced by Fireblocks).
  2. In your backend proxy database, save the passphraseId and the path to the passphrase. This data should indicate which user cloud you used and the path to the passphrase.
  3. Call the Fireblocks implementation for backup together with the passphrase and the passphraseId.

🚧

Backing up your keys post extension

In case you have extended your key set, as elaborated here, you must repeat the above steps again, in order to back up your new, extended key set, as well.

Recovery procedure

await fireblocksNCW.recoverKeys(passphraseResolver);
// recover the backed up keys. We will use the given backupEncryptionKey to decrypt the keys
Fireblocks.getInstance(deviceId).recoverKeys(passphraseResolver = passphraseResolver) {
	Timber.d("Recover keys result: $it")
  callback.invoke(it)
}

// using concurrency 
let result = await instance.recoverKeys(passphrase: passphrase)

<--- OR --->
    
// using callback
try instance.recoverKeys(passphrase: passphrase, callback: { [weak self] result in
	//handle result
})


  1. Call the Fireblocks SDK's recoverKeys function with a callback that implements a function that, when given a passphraseId, will fetch the associated passphrase for the user.
  2. The Fireblocks SDK fetches the last encrypted key share with the associated passphraseId and then uses your callback to decrypt the private key share locally.

Additional recovery info

The encrypted backup is associated with the end user's deviceId. Therefore, to recover and decrypt the key share saved on the Fireblocks cloud servers, the NCW SDK must initialize with the same deviceId that created the backup. The NCW SDK cannot recover a key share of a different deviceId.

If you want to let your end users go into recovery mode, you need to determine the deviceId that will run the recovery procedure. To support this, Fireblocks has provided a function within the Fireblocks SDK that fetches the latest details about the backup of the specified walletId. You call this function from your backend implementation.

await signer.NCW.getLatestBackup(walletId);
curl --request GET \
     --url https://sandbox-api.fireblocks.io/v1/ncw/wallets/walletId/backup/latest \
     --header 'accept: application/json'

🚧

After Recovering

After a successful recovery, the device with the deviceId used to decrypt the backup will have its certificate with Fireblocks disabled, and it will be unable to perform any MPC-related operations, such as signing transactions, creating backups, adding devices, or takeovers. Because of this, you should only consider performing a recovery when you have lost access to all of the devices associated with a wallet. If you still have access to one of the devices, you should add the device, as shown in the Multiple Devices article.