From 6e838978e3e08be9e6b45c2f6f89755d22db6b52 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Wed, 11 Aug 2021 11:54:28 +0200 Subject: [PATCH] Ask for system authentication before storing a new recovery code This will help to prevent data extraction via seedvault when somebody gets hold of an unlocked phone. However, it will not help against someone able to force you to provide fingerprints or other device secrets. --- README.md | 1 + app/src/main/AndroidManifest.xml | 3 ++ .../recoverycode/RecoveryCodeInputFragment.kt | 34 ++++++++++++++++++- app/src/main/res/values/strings.xml | 2 ++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 49a97dcf..f9f6aa44 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ It uses the same internal APIs as `adb backup` which is deprecated and thus need * `android.permission.ACCESS_MEDIA_LOCATION` to backup original media files e.g. without stripped EXIF metadata. * `android.permission.FOREGROUND_SERVICE` to do periodic storage backups without interruption. * `android.permission.MANAGE_DOCUMENTS` to retrieve the available storage roots (optional) for better UX. +* `android.permission.USE_BIOMETRIC` to authenticate saving a new recovery code ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/seedvault-app/seedvault. diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c8a8da5e..d3fa7189 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -47,6 +47,9 @@ android:name="android.permission.QUERY_ALL_PACKAGES" tools:ignore="QueryAllPackagesPermission" /> + + + = 30 && keyguardManager.isDeviceSecure) { + // if we have a lock-screen secret, we can ask for it before storing the code + storeNewCodeAfterAuth(input) + } else { + // user doesn't seem to care about security, store key without auth + viewModel.storeNewCode(input) + } } else { viewModel.verifyExistingCode(input) } } + @RequiresApi(30) + private fun storeNewCodeAfterAuth(input: List) { + val biometricPrompt = BiometricPrompt.Builder(context) + .setConfirmationRequired(true) + .setTitle(getString(R.string.recovery_code_auth_title)) + .setDescription(getString(R.string.recovery_code_auth_description)) + // BIOMETRIC_STRONG could be made optional in the future, setting guarded by credentials + .setAllowedAuthenticators(DEVICE_CREDENTIAL or BIOMETRIC_STRONG) + .build() + val callback = object : BiometricPrompt.AuthenticationCallback() { + override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult?) { + viewModel.storeNewCode(input) + } + } + biometricPrompt.authenticate(CancellationSignal(), getMainExecutor(context), callback) + } + private fun allFilledOut(input: List): Boolean { for (i in input.indices) { if (input[i].isNotEmpty()) continue diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7682402e..046ee7ac 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -94,6 +94,8 @@ Wait one second… Generating a new code will make your existing backups inaccessible. We\'ll try to delete them if possible.\n\nAre you sure you want to do this? New recovery code has been created successfully + Confirm it\'s really you + This ensures that nobody else can get your data when finding your phone unlocked. Backup notification