Skip to main content

Verifiable Backup

Verifiable Backup allows users to generate a backup of their MPC wallet that can be verified as correct without decryption. This feature enhances security and privacy in backup management.

info

Since backup is a sensitive operation, we don't expose the backup endpoint directly in the SDK. Instead, we provide an endpoint in the cloud node /v3/backup-key that can be used to generate the verifiable backup. We provide a method in the client SDK to verify the backup without decryption.

It's the responsibility of the company using the SDK to expose this endpoint to the user in a secure way.

For example:

  • When the user requests a backup, the backend can request additional authentication from the user (e.g., 2FA) before generating the backup.
  • Once the user is authenticated, the backend can get the verifiable backup from the cloud node and pass it to the user.
  • The user can then verify the backup using duoSession.verifyBackup().
warning

The v3/backup-key endpoint is not exposed directly in the SDK. Here for the sake of simplicity, we will use the getBackupData method to make the request from the client. Do NOT expose this endpoint to the user directly in a production application.

Step 1 : Create Session


Step 2 : Perform Verification


  • Get the backup data ready. Here for the sake of simplicity, we will use the getBackupData method to make the request from the client.

  • Call duoSession.verifyBackup() with all the required params (as explained below) which returns Result of Success (which represents the operation outcome) or Failure with exception.

Example


Example.kt
object Constants {
val rsaPublicKey = """
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAp69leU/BZJUTrrdUht/fb+REtPuNIGCH8lV+98p3oZDFi7u1XLOA
ZDxanD0pqnWrZJ0DNM2g7Ve/5/zIAQAUgIrkhu7DSFQjg5OgQH2TnzDdWLofVILq
UW42YkZ+smXaur8LsvgghSAQQng8fVYfoAZC/QPVHe9PX3BG4rvuaRziXP0DcX0I
NGuzk7r++6FgDwaN8oyy/1CvCUNLfEmXytqBl9xy5ElipZAguRZVybHE2wndCWln
zC3lwmPppr8tiWMYNlJO3VjL3wgchYzoZUXBSUa3ZyocI7jLYp9qNelEWlbukYBY
TrtUcVKIxp3OfyZ0edqcR8xKD1wBQKRzLQIDAQAB
-----END RSA PUBLIC KEY-----
""".trimIndent()
}

suspend fun backupAndVerify(keyshare: ByteArray, duoSession: DuoSession): Boolean {
return withContext(Dispatchers.IO) {
//Get BackupData from another party
val backupKeyResponse = getBackupData(
url = buildString {
append(websocketConfig.postRequestUrl)
append("/v3")
append("/backup-key")
}, keyId = SilentShard.ECDSA.getKeyshareKeyId<String>(
keyshare = keyshare,
resultType = SilentShard.ResultType.UrlSafeBase64
).getOrThrow(), label = "test-backup"
)

//Verify if BackupData is valid
duoSession.verifyBackup(
keyshare = keyshare,
backupData = backupKeyResponse.verifiableBackup.decodeBase64Bytes(),
rsaPublicKey = rsaPublicKey.encodeToByteArray(),
label = "test-backup"
).getOrThrow()
}
}

private fun getBackupData(url: String, keyId: String, label: String) = BackupKeyRequest(
keyId = keyId, rsaPublicKey = Constants.rsaPublicKey, label = label
).let {
json.encodeToString(it)
}.let { request: String ->
sendPostRequest(
url, null, request
).decodeToString().let { response: String ->
json.decodeFromString<BackupKeyResponse>(response)
}
}

data class BackupKeyRequest(
@SerialName("key_id") val keyId: String,
@SerialName("rsa_pubkey_pem") val rsaPublicKey: String,
@SerialName("label") val label: String,
)

data class BackupKeyResponse(
@SerialName("verifiable_backup") val verifiableBackup: String,
)

  • rsaPublicKeyPEM: The RSA public key in PEM format.
  • label: The label used as associated data while performing RSA encryption of the server's keyshare. The label is required while decrypting/verifying the backup.
  • keyshare: The client's share of the MPC wallet.
  • backup: The verifiable encrypted backup of the server's keyshare.
  • isVerified: A boolean indicating whether the backup is verified or not.