Key Backup

This guide shows how to create a backup for user's private key in order to implement multiple-device support and prevent loss of all user's data in case of losing the private key, using E3Kit.

Private key backup is a user's private key encrypted with user's backup password and saved on Virgil Cloud, so that the user can access their backup whenever they need to (when trying to decrypt their encrypted data from another device, for example). The backup helps a user share their private key between multiple devices and restore their private key in case of its loss.

If a user loses their private key before making a backup, they won't be able to decrypt previously encrypted data.

If a user's private key has been lost before the backup step, proceed to the Reset user step.

How strongly are my users' private keys protected in the cloud?

We know it might seem counter-intuitive to allow a private key off your users' devices. But it's actually quite safe. The function uses elliptic curve BLS12-381 (built by the zCash cryptocurrency team) to derive a 128-bit strong key from the password specified. This key is used to encrypt your user's private key before E3Kit saves it in the Virgil Cloud.

Before you begin

Be sure to implement the following:

Back up key

During the back up process, use the eThree.backupPrivateKey function to set a backup password and upload the encrypted private key to the cloud. The function must be called from the device that the user initially used to register his/her public key. A backup password is a string that is required to regenerate the user's private key. It must be a secret only known to that user.

While tempting, it will create a security weakness if you allow the user to use their identity or another publicly available identifier as the backup password. We highly recommend requiring your users to create a unique, strong password as their backup password. It's not recommended to store this password in your service.

OnCompleteListener backupListener = new OnCompleteListener() {
    @Override public void onSuccess() {
        // You're done
    }

    @Override public void onError(@NotNull Throwable throwable) {
        // Error handling
    }
};

// Backup user's private key to the cloud (encrypted using her password).
// This will enable your user to log in from another device and have access
// to the same private key there.
eThree.backupPrivateKey(keyPassword).addCallback(backupListener);

What if the user forgets backup password?

If the user forgets the password, the only secure measure is to rotate their private key and make a new backup using a new password. See the Reset user section on how to rotate private keys. Note that the complete loss of the previous private key can mean the loss of access to all the previously encrypted data. As an alternative for not requiring users to remember a password, see our article on a three questions approach here.

Make one more back up of the key

As we mentioned above, if the user forgets the password, they can't restore the private key, as a result, they lose access to all the previously encrypted data. But E3Kit helps you to predict this case and provide you with the additional backup parameter keyName by activating which you can specify one more password (different one) for the user's key that will encrypt the key in parallel. For example, additional password can be a concatenated string of the 3 secret questions.

eThree.backupPrivateKey(keyName, keyPassword).addCallback(backupListener);

Make user's password the backup password

If you authorize users using password in your application, please do not use the same password to backup their private key, since it breaks the concept of end-to-end encryption. Instead, you can derive from your original user password two different ones, and use them during login and private key backup process correspondingly:

DerivedPasswords derivedPasswords = EThree.derivePasswords(userPassword);

// This password should be used for backup/restore PrivateKey
String backupPassword = derivedPasswords.getBackupPassword();

// This password should be used for other purposes, e.g user authorization
String loginPassword = derivedPasswords.getLoginPassword();

Restore key

To obtain their private key from another device, the user needs to have its backup and the backup password to decrypt it. So first, the user checks if the private key is already present on the device. If it's not, they retrieve their backup of a private key (encrypted private key) from Virgil Cloud. Using the backup password, the user decrypts the backup and gets their original private key.

On each new device or after logout use the eThree.restorePrivateKey method with the backup password to download the user's private key from the cloud and decrypt it:

OnCompleteListener restoreListener = new OnCompleteListener() {
    @Override public void onSuccess() {
        // You're done
    }

    @Override public void onError(@NotNull Throwable throwable) {
        // Error handling
    }
};

// If user wants to restore their private key from backup in Virgil's cloud.
// * While user in session - key can be removed and restore multiply times (via cleanup/restorePrivateKey functions). To know whether private key is present on device now use hasLocalPrivateKey() function:
if (!eThree.hasLocalPrivateKey()) {
    eThree.restorePrivateKey(keyPassword).addCallback(restoreListener);
}

If you are not sure whether the user is using the original device or a new device, you can use eThree.hasLocalPrivateKey method to check if the user's private key already exists on the device. If the user's private key is already present on the device, then you can encrypt/decrypt data. Otherwise, use the eThree.restorePrivateKey method to access the user's private key using their backup password.

Restore key with keyName parameter

To restore a key with the keyName parameter use the following code:

if (!eThree.hasLocalPrivateKey()) {
    eThree.restorePrivateKey(keyName, keyPassword).addCallback(restoreListener);
}

Change backup password

Backup password is a password that the user used to encrypt their private key (create a backup) at the Back up key step. The eThree.changePassword method re-encrypts the user's private key on their device with the new password, and then uploads it to the Virgil Cloud.

Use the new backup password to re-encrypt user's private key with it:

OnCompleteListener changeListener = new OnCompleteListener() {
    @Override public void onSuccess() {
        // You're done
    }

    @Override public void onError(@NotNull Throwable throwable) {
        // Error handling
    }
};

// If the user wants to change his password for private key backup
eThree.changePassword(oldPassword, newPassword).addCallback(changeListener);

Change password with keyName parameter

To change backup password of the key with the keyName parameter use the following code:

// If the user wants to change his password
eThree.changePassword(keyName, oldPassword, newPassword).addCallback(changeListener);

Delete backup

If you've made user's private key backup before, you can easily delete this backup from Virgil Cloud. This step is necessary during the Reset user step.

To delete a user's encrypted private key from Virgil Cloud, use the eThree.resetPrivateKeyBackup method:

OnCompleteListener resetListener = new OnCompleteListener() {
    @Override public void onSuccess() {
        // You're done
    }

    @Override public void onError(@NotNull Throwable throwable) {
        // Error handling
    }
};

// If user wants to delete their account, this is the function to
// delete their private key
eThree.resetPrivateKeyBackup(keyPassword).addCallback(resetListener);

If user forgot their backup password, you can do the full reset using eThree.resetPrivateKeyBackup method. Be aware that if you use Keyknox with E3Kit and call this method, all the data will be removed from Keyknox.

Delete backup with keyName parameter

To delete a backup key with the keyName parameter use the following code:

// If user wants to delete their account, use the following function
eThree.resetPrivateKeyBackupWithKeyName(keyName).addCallback(resetListener);