diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md index faea5710..59583ce1 100644 --- a/.github/ISSUE_TEMPLATE/bug.md +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -11,7 +11,7 @@ guidelines](/CONTRIBUTING.md#bug-reports) before submitting an issue. --> * __Version__: * __Source__: (Google Play/GitHub/F-Droid/?) -* __Vault encrypted:__ Yes (with fingerprint unlock)/Yes/No +* __Vault encrypted:__ Yes (with biometric unlock)/Yes/No * __Device:__ * __Android version and ROM:__ diff --git a/README.md b/README.md index 7bbe749b..4372f2e9 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ document](docs/vault.md). - Secure - Encryption (AES-256) - Password (scrypt) - - Fingerprint (Android Keystore) + - Biometrics (Android Keystore) - Screen capture prevention - Tap to reveal ability - Multiple ways to add new entries @@ -120,7 +120,6 @@ Certificate fingerprints: - [AppIntro](https://github.com/AppIntro/AppIntro) by Paolo Rotolo - [Krop](https://github.com/avito-tech/krop) by Avito Technology - [SpongyCastle](https://github.com/rtyley/spongycastle) by Roberto Tyley -- [Swirl](https://github.com/mattprecious/swirl) by Matthew Precious - [CircleImageView](https://github.com/hdodenhof/CircleImageView) by Henning Dodenhof - [barcodescanner](https://github.com/dm77/barcodescanner) by Dushyanth diff --git a/app/build.gradle b/app/build.gradle index af11b092..8170ab64 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -67,6 +67,7 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' implementation 'androidx.appcompat:appcompat:1.1.0' + implementation "androidx.biometric:biometric:1.0.0" implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.preference:preference:1.1.0' implementation 'com.google.android.material:material:1.0.0' @@ -75,7 +76,6 @@ dependencies { implementation 'com.github.apl-devs:appintro:5.1.0' implementation 'com.github.avito-tech:krop:0.44' implementation 'com.madgag.spongycastle:core:1.58.0.0' - implementation 'com.mattprecious.swirl:swirl:1.2.0' implementation 'de.hdodenhof:circleimageview:3.0.1' implementation 'me.dm7.barcodescanner:zxing:1.9.8' implementation "com.github.topjohnwu.libsu:core:${libsuVersion}" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cdfd0848..099e395a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,7 +4,7 @@ package="com.beemdevelopment.aegis"> - + diff --git a/app/src/main/java/com/beemdevelopment/aegis/db/slots/FingerprintSlot.java b/app/src/main/java/com/beemdevelopment/aegis/db/slots/BiometricSlot.java similarity index 57% rename from app/src/main/java/com/beemdevelopment/aegis/db/slots/FingerprintSlot.java rename to app/src/main/java/com/beemdevelopment/aegis/db/slots/BiometricSlot.java index a846b262..79330de9 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/db/slots/FingerprintSlot.java +++ b/app/src/main/java/com/beemdevelopment/aegis/db/slots/BiometricSlot.java @@ -4,17 +4,17 @@ import com.beemdevelopment.aegis.crypto.CryptParameters; import java.util.UUID; -public class FingerprintSlot extends RawSlot { - public FingerprintSlot() { +public class BiometricSlot extends RawSlot { + public BiometricSlot() { super(); } - FingerprintSlot(UUID uuid, byte[] key, CryptParameters keyParams) { + BiometricSlot(UUID uuid, byte[] key, CryptParameters keyParams) { super(uuid, key, keyParams); } @Override public byte getType() { - return TYPE_FINGERPRINT; + return TYPE_BIOMETRIC; } } diff --git a/app/src/main/java/com/beemdevelopment/aegis/db/slots/Slot.java b/app/src/main/java/com/beemdevelopment/aegis/db/slots/Slot.java index 73781657..f2066604 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/db/slots/Slot.java +++ b/app/src/main/java/com/beemdevelopment/aegis/db/slots/Slot.java @@ -28,7 +28,7 @@ import javax.crypto.spec.SecretKeySpec; public abstract class Slot extends UUIDMap.Value { public final static byte TYPE_RAW = 0x00; public final static byte TYPE_DERIVED = 0x01; - public final static byte TYPE_FINGERPRINT = 0x02; + public final static byte TYPE_BIOMETRIC = 0x02; private byte[] _encryptedMasterKey; private CryptParameters _encryptedMasterKeyParams; @@ -138,8 +138,8 @@ public abstract class Slot extends UUIDMap.Value { boolean repaired = obj.optBoolean("repaired", false); slot = new PasswordSlot(uuid, key, keyParams, scryptParams, repaired); break; - case Slot.TYPE_FINGERPRINT: - slot = new FingerprintSlot(uuid, key, keyParams); + case Slot.TYPE_BIOMETRIC: + slot = new BiometricSlot(uuid, key, keyParams); break; default: throw new SlotException("unrecognized slot type"); diff --git a/app/src/main/java/com/beemdevelopment/aegis/helpers/BiometricSlotInitializer.java b/app/src/main/java/com/beemdevelopment/aegis/helpers/BiometricSlotInitializer.java new file mode 100644 index 00000000..7d634146 --- /dev/null +++ b/app/src/main/java/com/beemdevelopment/aegis/helpers/BiometricSlotInitializer.java @@ -0,0 +1,136 @@ +package com.beemdevelopment.aegis.helpers; + +import androidx.annotation.NonNull; +import androidx.biometric.BiometricPrompt; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; + +import com.beemdevelopment.aegis.crypto.KeyStoreHandle; +import com.beemdevelopment.aegis.crypto.KeyStoreHandleException; +import com.beemdevelopment.aegis.db.slots.BiometricSlot; +import com.beemdevelopment.aegis.db.slots.Slot; +import com.beemdevelopment.aegis.db.slots.SlotException; + +import java.util.Objects; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; + +/** + * A class that can prepare initialization of a BiometricSlot by generating a new + * key in the Android KeyStore and authenticating a cipher for it through a + * BiometricPrompt. + */ +public class BiometricSlotInitializer extends BiometricPrompt.AuthenticationCallback { + private BiometricSlot _slot; + private Listener _listener; + private BiometricPrompt _prompt; + + public BiometricSlotInitializer(Fragment fragment, Listener listener) { + _listener = listener; + _prompt = new BiometricPrompt(fragment, new UiThreadExecutor(), this); + } + + public BiometricSlotInitializer(FragmentActivity activity, Listener listener) { + _listener = listener; + _prompt = new BiometricPrompt(activity, new UiThreadExecutor(), this); + } + + /** + * Generates a new key in the Android KeyStore for the new BiometricSlot, + * initializes a cipher with it and shows a BiometricPrompt to the user for + * authentication. If authentication is successful, the new slot will be + * initialized and delivered back through the listener. + */ + public void authenticate(BiometricPrompt.PromptInfo info) { + if (_slot != null) { + throw new IllegalStateException("Biometric authentication already in progress"); + } + + KeyStoreHandle keyStore; + try { + keyStore = new KeyStoreHandle(); + } catch (KeyStoreHandleException e) { + fail(e); + return; + } + + // generate a new Android KeyStore key + // and assign it the UUID of the new slot as an alias + Cipher cipher; + BiometricSlot slot = new BiometricSlot(); + try { + SecretKey key = keyStore.generateKey(slot.getUUID().toString()); + cipher = Slot.createEncryptCipher(key); + } catch (KeyStoreHandleException | SlotException e) { + fail(e); + return; + } + + _slot = slot; + _prompt.authenticate(info, new BiometricPrompt.CryptoObject(cipher)); + } + + /** + * Cancels the BiometricPrompt and resets the state of the initializer. It will + * also attempt to delete the previously generated Android KeyStore key. + */ + public void cancelAuthentication() { + if (_slot == null) { + throw new IllegalStateException("Biometric authentication not in progress"); + } + + reset(); + _prompt.cancelAuthentication(); + } + + private void reset() { + if (_slot != null) { + try { + // clean up the unused KeyStore key + // this is non-critical, so just fail silently if an error occurs + String uuid = _slot.getUUID().toString(); + KeyStoreHandle keyStore = new KeyStoreHandle(); + if (keyStore.containsKey(uuid)) { + keyStore.deleteKey(uuid); + } + } catch (KeyStoreHandleException e) { + e.printStackTrace(); + } + + _slot = null; + } + } + + private void fail(int errorCode, CharSequence errString) { + reset(); + _listener.onSlotInitializationFailed(errorCode, errString); + } + + private void fail(Exception e) { + e.printStackTrace(); + fail(0, e.toString()); + } + + @Override + public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) { + super.onAuthenticationError(errorCode, errString); + fail(errorCode, errString.toString()); + } + + @Override + public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) { + super.onAuthenticationSucceeded(result); + _listener.onInitializeSlot(_slot, Objects.requireNonNull(result.getCryptoObject()).getCipher()); + } + + @Override + public void onAuthenticationFailed() { + super.onAuthenticationFailed(); + } + + public interface Listener { + void onInitializeSlot(BiometricSlot slot, Cipher cipher); + void onSlotInitializationFailed(int errorCode, @NonNull CharSequence errString); + } +} diff --git a/app/src/main/java/com/beemdevelopment/aegis/helpers/BiometricsHelper.java b/app/src/main/java/com/beemdevelopment/aegis/helpers/BiometricsHelper.java new file mode 100644 index 00000000..eab2e5e4 --- /dev/null +++ b/app/src/main/java/com/beemdevelopment/aegis/helpers/BiometricsHelper.java @@ -0,0 +1,30 @@ +package com.beemdevelopment.aegis.helpers; + +import android.content.Context; + +import androidx.biometric.BiometricConstants; +import androidx.biometric.BiometricManager; + +public class BiometricsHelper { + private BiometricsHelper() { + + } + + public static BiometricManager getManager(Context context) { + BiometricManager manager = BiometricManager.from(context); + if (manager.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) { + return manager; + } + return null; + } + + public static boolean isCanceled(int errorCode) { + return errorCode == BiometricConstants.ERROR_CANCELED + || errorCode == BiometricConstants.ERROR_USER_CANCELED + || errorCode == BiometricConstants.ERROR_NEGATIVE_BUTTON; + } + + public static boolean isAvailable(Context context) { + return getManager(context) != null; + } +} diff --git a/app/src/main/java/com/beemdevelopment/aegis/helpers/FingerprintHelper.java b/app/src/main/java/com/beemdevelopment/aegis/helpers/FingerprintHelper.java deleted file mode 100644 index 0f377935..00000000 --- a/app/src/main/java/com/beemdevelopment/aegis/helpers/FingerprintHelper.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.beemdevelopment.aegis.helpers; - -import android.Manifest; -import android.content.Context; -import android.hardware.fingerprint.FingerprintManager; -import android.os.Build; - -import androidx.annotation.RequiresApi; - -public class FingerprintHelper { - private FingerprintHelper() { - - } - - @RequiresApi(api = Build.VERSION_CODES.M) - public static FingerprintManager getManager(Context context) { - if (PermissionHelper.granted(context, Manifest.permission.USE_FINGERPRINT)) { - FingerprintManager manager = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE); - if (manager != null && manager.isHardwareDetected() && manager.hasEnrolledFingerprints()) { - return manager; - } - } - return null; - } - - @RequiresApi(api = Build.VERSION_CODES.M) - public static boolean isAvailable(Context context) { - return getManager(context) != null; - } - - public static boolean isSupported() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; - } -} diff --git a/app/src/main/java/com/beemdevelopment/aegis/helpers/FingerprintUiHelper.java b/app/src/main/java/com/beemdevelopment/aegis/helpers/FingerprintUiHelper.java deleted file mode 100644 index 69a65c58..00000000 --- a/app/src/main/java/com/beemdevelopment/aegis/helpers/FingerprintUiHelper.java +++ /dev/null @@ -1,156 +0,0 @@ -// This file was originally taken from https://github.com/googlesamples/android-FingerprintDialog/blob/2feb02945ae220ebd1bc2c2b620a1d43e30daea8/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintUiHelper.java -// It has been modified to suit Aegis' needs - -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.beemdevelopment.aegis.helpers; - -import android.hardware.fingerprint.FingerprintManager; -import android.os.Build; -import android.os.CancellationSignal; -import android.widget.TextView; - -import com.beemdevelopment.aegis.R; -import com.mattprecious.swirl.SwirlView; - -import androidx.annotation.RequiresApi; - -/** - * Small helper class to manage text/icon around fingerprint authentication UI. - */ -@RequiresApi(api = Build.VERSION_CODES.M) -public class FingerprintUiHelper extends FingerprintManager.AuthenticationCallback { - - private static final long ERROR_TIMEOUT_MILLIS = 1600; - private static final long SUCCESS_DELAY_MILLIS = 100; - - private final FingerprintManager mFingerprintManager; - private final SwirlView mIcon; - private final TextView mErrorTextView; - private final Callback mCallback; - private CancellationSignal mCancellationSignal; - - private boolean mSelfCancelled; - - /** - * Constructor for {@link FingerprintUiHelper}. - */ - public FingerprintUiHelper(FingerprintManager fingerprintManager, - SwirlView icon, TextView errorTextView, Callback callback) { - mFingerprintManager = fingerprintManager; - mIcon = icon; - mErrorTextView = errorTextView; - mCallback = callback; - } - - public boolean isFingerprintAuthAvailable() { - // The line below prevents the false positive inspection from Android Studio - // noinspection ResourceType - return mFingerprintManager.isHardwareDetected() - && mFingerprintManager.hasEnrolledFingerprints(); - } - - public void startListening(FingerprintManager.CryptoObject cryptoObject) { - if (!isFingerprintAuthAvailable()) { - return; - } - mCancellationSignal = new CancellationSignal(); - mSelfCancelled = false; - // The line below prevents the false positive inspection from Android Studio - // noinspection ResourceType - mFingerprintManager - .authenticate(cryptoObject, mCancellationSignal, 0 /* flags */, this, null); - mIcon.setState(SwirlView.State.ON); - } - - public void stopListening() { - if (mCancellationSignal != null) { - mSelfCancelled = true; - mCancellationSignal.cancel(); - mCancellationSignal = null; - } - } - - @Override - public void onAuthenticationError(int errMsgId, CharSequence errString) { - if (!mSelfCancelled) { - showError(errString); - mIcon.postDelayed(new Runnable() { - @Override - public void run() { - mCallback.onError(); - } - }, ERROR_TIMEOUT_MILLIS); - } - } - - @Override - public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { - showError(helpString); - } - - @Override - public void onAuthenticationFailed() { - showError(mIcon.getResources().getString( - R.string.fingerprint_not_recognized)); - } - - @Override - public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { - mErrorTextView.removeCallbacks(mResetErrorTextRunnable); - mIcon.setState(SwirlView.State.OFF); - mErrorTextView.setText( - mErrorTextView.getResources().getString(R.string.fingerprint_success)); - mIcon.postDelayed(new Runnable() { - @Override - public void run() { - mCallback.onAuthenticated(); - } - }, SUCCESS_DELAY_MILLIS); - - // ugly hack to keep the fingerprint icon visible while also giving visual feedback of success to the user - mIcon.postDelayed(new Runnable() { - @Override - public void run() { - mIcon.setState(SwirlView.State.ON); - } - }, 500); - } - - private void showError(CharSequence error) { - mIcon.setState(SwirlView.State.ERROR); - mErrorTextView.setText(error); - mErrorTextView.removeCallbacks(mResetErrorTextRunnable); - mErrorTextView.postDelayed(mResetErrorTextRunnable, ERROR_TIMEOUT_MILLIS); - } - - private Runnable mResetErrorTextRunnable = new Runnable() { - @Override - public void run() { - mErrorTextView.setText( - mErrorTextView.getResources().getString(R.string.fingerprint_hint)); - mIcon.setState(SwirlView.State.ON); - } - }; - - public interface Callback { - - void onAuthenticated(); - - void onError(); - } -} diff --git a/app/src/main/java/com/beemdevelopment/aegis/helpers/UiThreadExecutor.java b/app/src/main/java/com/beemdevelopment/aegis/helpers/UiThreadExecutor.java new file mode 100644 index 00000000..4cb0aa5b --- /dev/null +++ b/app/src/main/java/com/beemdevelopment/aegis/helpers/UiThreadExecutor.java @@ -0,0 +1,17 @@ +package com.beemdevelopment.aegis.helpers; + +import android.os.Handler; +import android.os.Looper; + +import androidx.annotation.NonNull; + +import java.util.concurrent.Executor; + +public class UiThreadExecutor implements Executor { + private final Handler _handler = new Handler(Looper.getMainLooper()); + + @Override + public void execute(@NonNull Runnable command) { + _handler.post(command); + } +} diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/AuthActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/AuthActivity.java index d5e57b8f..e3034a13 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/AuthActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/AuthActivity.java @@ -1,61 +1,56 @@ package com.beemdevelopment.aegis.ui; -import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; -import android.hardware.fingerprint.FingerprintManager; -import android.os.Build; import android.os.Bundle; import android.view.KeyEvent; import android.view.View; -import android.view.ViewGroup; import android.view.WindowManager; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; -import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.biometric.BiometricPrompt; -import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.CancelAction; +import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.crypto.KeyStoreHandle; import com.beemdevelopment.aegis.crypto.KeyStoreHandleException; import com.beemdevelopment.aegis.db.DatabaseFileCredentials; -import com.beemdevelopment.aegis.db.slots.FingerprintSlot; +import com.beemdevelopment.aegis.db.slots.BiometricSlot; import com.beemdevelopment.aegis.db.slots.PasswordSlot; import com.beemdevelopment.aegis.db.slots.Slot; import com.beemdevelopment.aegis.db.slots.SlotException; import com.beemdevelopment.aegis.db.slots.SlotList; +import com.beemdevelopment.aegis.helpers.BiometricsHelper; import com.beemdevelopment.aegis.helpers.EditTextHelper; -import com.beemdevelopment.aegis.helpers.FingerprintHelper; -import com.beemdevelopment.aegis.helpers.FingerprintUiHelper; +import com.beemdevelopment.aegis.helpers.UiThreadExecutor; import com.beemdevelopment.aegis.ui.tasks.SlotListTask; -import com.mattprecious.swirl.SwirlView; import javax.crypto.Cipher; import javax.crypto.SecretKey; -import androidx.appcompat.app.AlertDialog; - -public class AuthActivity extends AegisActivity implements FingerprintUiHelper.Callback, SlotListTask.Callback { +public class AuthActivity extends AegisActivity implements SlotListTask.Callback { private EditText _textPassword; private CancelAction _cancelAction; private SlotList _slots; - private FingerprintUiHelper _fingerHelper; - private FingerprintManager.CryptoObject _fingerCryptoObj; + private BiometricPrompt.CryptoObject _bioCryptoObj; + private BiometricPrompt _bioPrompt; @Override - @SuppressLint("NewApi") protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_auth); _textPassword = findViewById(R.id.text_password); - LinearLayout boxFingerprint = findViewById(R.id.box_fingerprint); - LinearLayout boxFingerprintInfo = findViewById(R.id.box_fingerprint_info); - TextView textFingerprint = findViewById(R.id.text_fingerprint); + LinearLayout boxBiometricInfo = findViewById(R.id.box_biometric_info); Button decryptButton = findViewById(R.id.button_decrypt); + Button biometricsButton = findViewById(R.id.button_biometrics); _textPassword.setOnEditorActionListener((v, actionId, event) -> { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) { @@ -64,25 +59,17 @@ public class AuthActivity extends AegisActivity implements FingerprintUiHelper.C return false; }); - SwirlView imgFingerprint = null; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - ViewGroup insertPoint = findViewById(R.id.img_fingerprint_insert); - imgFingerprint = new SwirlView(this); - insertPoint.addView(imgFingerprint, 0, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); - } - Intent intent = getIntent(); _slots = (SlotList) intent.getSerializableExtra("slots"); _cancelAction = (CancelAction) intent.getSerializableExtra("cancelAction"); - // only show the fingerprint controls if the api version is new enough, permission is granted, a scanner is found and a fingerprint slot is found - if (_slots.has(FingerprintSlot.class) && FingerprintHelper.isSupported() && FingerprintHelper.isAvailable(this)) { + // only show the biometric prompt if the api version is new enough, permission is granted, a scanner is found and a biometric slot is found + if (_slots.has(BiometricSlot.class) && BiometricsHelper.isAvailable(this)) { boolean invalidated = false; - FingerprintManager manager = FingerprintHelper.getManager(this); try { - // find a fingerprint slot with an id that matches an alias in the keystore - for (FingerprintSlot slot : _slots.findAll(FingerprintSlot.class)) { + // find a biometric slot with an id that matches an alias in the keystore + for (BiometricSlot slot : _slots.findAll(BiometricSlot.class)) { String id = slot.getUUID().toString(); KeyStoreHandle handle = new KeyStoreHandle(); if (handle.containsKey(id)) { @@ -92,10 +79,11 @@ public class AuthActivity extends AegisActivity implements FingerprintUiHelper.C invalidated = true; continue; } + Cipher cipher = slot.createDecryptCipher(key); - _fingerCryptoObj = new FingerprintManager.CryptoObject(cipher); - _fingerHelper = new FingerprintUiHelper(manager, imgFingerprint, textFingerprint, this); - boxFingerprint.setVisibility(View.VISIBLE); + _bioCryptoObj = new BiometricPrompt.CryptoObject(cipher); + _bioPrompt = new BiometricPrompt(this, new UiThreadExecutor(), new BiometricPromptListener()); + biometricsButton.setVisibility(View.VISIBLE); invalidated = false; break; } @@ -106,7 +94,7 @@ public class AuthActivity extends AegisActivity implements FingerprintUiHelper.C // display a help message if a matching invalidated keystore entry was found if (invalidated) { - boxFingerprintInfo.setVisibility(View.VISIBLE); + boxBiometricInfo.setVisibility(View.VISIBLE); } } @@ -121,7 +109,11 @@ public class AuthActivity extends AegisActivity implements FingerprintUiHelper.C } }); - if (_fingerHelper == null) { + biometricsButton.setOnClickListener(v -> { + showBiometricPrompt(); + }); + + if (_bioCryptoObj == null) { _textPassword.requestFocus(); getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); } @@ -144,7 +136,7 @@ public class AuthActivity extends AegisActivity implements FingerprintUiHelper.C private void selectPassword() { _textPassword.selectAll(); - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); } @@ -162,36 +154,31 @@ public class AuthActivity extends AegisActivity implements FingerprintUiHelper.C } @Override - @SuppressLint("NewApi") public void onResume() { super.onResume(); - if (_fingerHelper != null) { - _fingerHelper.startListening(_fingerCryptoObj); + if (_bioPrompt != null) { + showBiometricPrompt(); } } + public void showBiometricPrompt() { + BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder() + .setTitle(getString(R.string.authentication)) + .setNegativeButtonText(getString(android.R.string.cancel)) + .build(); + _bioPrompt.authenticate(info, _bioCryptoObj); + } + @Override - @SuppressLint("NewApi") public void onPause() { super.onPause(); - if (_fingerHelper != null) { - _fingerHelper.stopListening(); + if (_bioPrompt != null) { + _bioPrompt.cancelAuthentication(); } } - @Override - @SuppressLint("NewApi") - public void onAuthenticated() { - trySlots(FingerprintSlot.class, _fingerCryptoObj.getCipher()); - } - - @Override - public void onError() { - - } - @Override public void onTaskFinished(SlotListTask.Result result) { if (result != null) { @@ -210,4 +197,25 @@ public class AuthActivity extends AegisActivity implements FingerprintUiHelper.C showError(); } } + + private class BiometricPromptListener extends BiometricPrompt.AuthenticationCallback { + @Override + public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) { + super.onAuthenticationError(errorCode, errString); + if (!BiometricsHelper.isCanceled(errorCode)) { + Toast.makeText(AuthActivity.this, errString, Toast.LENGTH_LONG).show(); + } + } + + @Override + public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) { + super.onAuthenticationSucceeded(result); + trySlots(BiometricSlot.class, _bioCryptoObj.getCipher()); + } + + @Override + public void onAuthenticationFailed() { + super.onAuthenticationFailed(); + } + } } diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/Dialogs.java b/app/src/main/java/com/beemdevelopment/aegis/ui/Dialogs.java index bf47dfa2..514d7404 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/Dialogs.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/Dialogs.java @@ -4,8 +4,6 @@ import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; -import android.hardware.fingerprint.FingerprintManager; -import android.os.Build; import android.text.Editable; import android.text.InputType; import android.text.TextWatcher; @@ -18,30 +16,21 @@ import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.EditText; import android.widget.NumberPicker; -import android.widget.TextView; + +import androidx.annotation.StringRes; +import androidx.appcompat.app.AlertDialog; import com.beemdevelopment.aegis.Preferences; import com.beemdevelopment.aegis.R; -import com.beemdevelopment.aegis.crypto.KeyStoreHandle; -import com.beemdevelopment.aegis.crypto.KeyStoreHandleException; -import com.beemdevelopment.aegis.db.slots.FingerprintSlot; import com.beemdevelopment.aegis.db.slots.PasswordSlot; import com.beemdevelopment.aegis.db.slots.Slot; import com.beemdevelopment.aegis.db.slots.SlotException; import com.beemdevelopment.aegis.helpers.EditTextHelper; -import com.beemdevelopment.aegis.helpers.FingerprintHelper; -import com.beemdevelopment.aegis.helpers.FingerprintUiHelper; import com.beemdevelopment.aegis.ui.tasks.DerivationTask; -import com.mattprecious.swirl.SwirlView; import java.util.concurrent.atomic.AtomicReference; import javax.crypto.Cipher; -import javax.crypto.SecretKey; - -import androidx.annotation.RequiresApi; -import androidx.annotation.StringRes; -import androidx.appcompat.app.AlertDialog; public class Dialogs { private Dialogs() { @@ -195,52 +184,6 @@ public class Dialogs { showSecureDialog(dialog); } - @RequiresApi(api = Build.VERSION_CODES.M) - public static void showFingerprintDialog(Activity activity, Dialogs.SlotListener listener) { - View view = activity.getLayoutInflater().inflate(R.layout.dialog_fingerprint, null); - TextView textFingerprint = view.findViewById(R.id.text_fingerprint); - SwirlView imgFingerprint = view.findViewById(R.id.img_fingerprint); - - FingerprintManager.CryptoObject obj; - FingerprintSlot slot; - final AtomicReference helper = new AtomicReference<>(); - FingerprintManager manager = FingerprintHelper.getManager(activity); - - try { - slot = new FingerprintSlot(); - SecretKey key = new KeyStoreHandle().generateKey(slot.getUUID().toString()); - Cipher cipher = Slot.createEncryptCipher(key); - obj = new FingerprintManager.CryptoObject(cipher); - } catch (KeyStoreHandleException | SlotException e) { - throw new RuntimeException(e); - } - - AlertDialog dialog = new AlertDialog.Builder(activity) - .setTitle(R.string.register_fingerprint) - .setView(view) - .setNegativeButton(android.R.string.cancel, null) - .setOnDismissListener(d -> { - helper.get().stopListening(); - }) - .create(); - - helper.set(new FingerprintUiHelper(manager, imgFingerprint, textFingerprint, new FingerprintUiHelper.Callback() { - @Override - public void onAuthenticated() { - listener.onSlotResult(slot, obj.getCipher()); - dialog.dismiss(); - } - - @Override - public void onError() { - - } - })); - - helper.get().startListening(obj); - showSecureDialog(dialog); - } - public interface NumberInputListener { void onNumberInputResult(int number); } diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/IntroActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/IntroActivity.java index 96d228a4..059b26d6 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/IntroActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/IntroActivity.java @@ -4,6 +4,8 @@ import android.content.Intent; import android.os.Bundle; import android.view.WindowManager; +import androidx.fragment.app.Fragment; + import com.beemdevelopment.aegis.Preferences; import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.db.Database; @@ -12,11 +14,10 @@ import com.beemdevelopment.aegis.db.DatabaseFileCredentials; import com.beemdevelopment.aegis.db.DatabaseFileException; import com.beemdevelopment.aegis.db.DatabaseManager; import com.beemdevelopment.aegis.db.DatabaseManagerException; -import com.beemdevelopment.aegis.db.slots.FingerprintSlot; +import com.beemdevelopment.aegis.db.slots.BiometricSlot; import com.beemdevelopment.aegis.db.slots.PasswordSlot; import com.beemdevelopment.aegis.db.slots.Slot; import com.beemdevelopment.aegis.db.slots.SlotException; -import com.beemdevelopment.aegis.db.slots.SlotList; import com.beemdevelopment.aegis.ui.slides.CustomAuthenticatedSlide; import com.beemdevelopment.aegis.ui.slides.CustomAuthenticationSlide; import com.beemdevelopment.aegis.ui.tasks.DerivationTask; @@ -29,8 +30,6 @@ import org.json.JSONObject; import javax.crypto.Cipher; import javax.crypto.SecretKey; -import androidx.fragment.app.Fragment; - public class IntroActivity extends AppIntro2 implements DerivationTask.Callback { public static final int RESULT_OK = 0; public static final int RESULT_EXCEPTION = 1; @@ -135,7 +134,6 @@ public class IntroActivity extends AppIntro2 implements DerivationTask.Callback creds = new DatabaseFileCredentials(); } - SlotList slots = null; if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_NONE) { // encrypt the master key with a key derived from the user's password // and add it to the list of slots @@ -150,18 +148,14 @@ public class IntroActivity extends AppIntro2 implements DerivationTask.Callback } } - if (cryptType == CustomAuthenticationSlide.CRYPT_TYPE_FINGER) { + if (cryptType == CustomAuthenticationSlide.CRYPT_TYPE_BIOMETRIC) { + BiometricSlot slot = _authenticatedSlide.getBiometricSlot(); try { - // encrypt the master key with the fingerprint key - // and add it to the list of slots - FingerprintSlot slot = _authenticatedSlide.getFingerSlot(); - Cipher cipher = _authenticatedSlide.getFingerCipher(); - slot.setKey(creds.getKey(), cipher); - creds.getSlots().add(slot); + slot.setKey(creds.getKey(), _authenticatedSlide.getBiometriCipher()); } catch (SlotException e) { setException(e); - return; } + creds.getSlots().add(slot); } // finally, save the database diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/PreferencesFragment.java b/app/src/main/java/com/beemdevelopment/aegis/ui/PreferencesFragment.java index 74efd5c0..eb249ec6 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/PreferencesFragment.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/PreferencesFragment.java @@ -12,6 +12,12 @@ import android.view.Window; import android.view.WindowManager; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.biometric.BiometricPrompt; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; + import com.beemdevelopment.aegis.AegisApplication; import com.beemdevelopment.aegis.BuildConfig; import com.beemdevelopment.aegis.CancelAction; @@ -25,12 +31,13 @@ import com.beemdevelopment.aegis.db.DatabaseFileCredentials; import com.beemdevelopment.aegis.db.DatabaseFileException; import com.beemdevelopment.aegis.db.DatabaseManager; import com.beemdevelopment.aegis.db.DatabaseManagerException; -import com.beemdevelopment.aegis.db.slots.FingerprintSlot; +import com.beemdevelopment.aegis.db.slots.BiometricSlot; import com.beemdevelopment.aegis.db.slots.PasswordSlot; import com.beemdevelopment.aegis.db.slots.Slot; import com.beemdevelopment.aegis.db.slots.SlotException; import com.beemdevelopment.aegis.db.slots.SlotList; -import com.beemdevelopment.aegis.helpers.FingerprintHelper; +import com.beemdevelopment.aegis.helpers.BiometricSlotInitializer; +import com.beemdevelopment.aegis.helpers.BiometricsHelper; import com.beemdevelopment.aegis.helpers.PermissionHelper; import com.beemdevelopment.aegis.importers.AegisImporter; import com.beemdevelopment.aegis.importers.DatabaseImporter; @@ -56,10 +63,6 @@ import java.util.concurrent.atomic.AtomicReference; import javax.crypto.Cipher; -import androidx.appcompat.app.AlertDialog; -import androidx.preference.Preference; -import androidx.preference.PreferenceFragmentCompat; - public class PreferencesFragment extends PreferenceFragmentCompat { // activity request codes private static final int CODE_IMPORT = 0; @@ -81,7 +84,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat { private UUIDMap _importerEntries; private SwitchPreference _encryptionPreference; - private SwitchPreference _fingerprintPreference; + private SwitchPreference _biometricsPreference; private Preference _autoLockPreference; private Preference _setPasswordPreference; private Preference _slotsPreference; @@ -289,20 +292,25 @@ public class PreferencesFragment extends PreferenceFragmentCompat { } }); - _fingerprintPreference = (SwitchPreference) findPreference("pref_fingerprint"); - _fingerprintPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + _biometricsPreference = (SwitchPreference) findPreference("pref_biometrics"); + _biometricsPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { DatabaseFileCredentials creds = _db.getCredentials(); SlotList slots = creds.getSlots(); - if (!slots.has(FingerprintSlot.class)) { - if (FingerprintHelper.isSupported() && FingerprintHelper.isAvailable(getContext())) { - Dialogs.showFingerprintDialog(getActivity(), new RegisterFingerprintListener()); + if (!slots.has(BiometricSlot.class)) { + if (BiometricsHelper.isAvailable(getContext())) { + BiometricSlotInitializer initializer = new BiometricSlotInitializer(PreferencesFragment.this, new RegisterBiometricsListener()); + BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder() + .setTitle(getString(R.string.set_up_biometric)) + .setNegativeButtonText(getString(android.R.string.cancel)) + .build(); + initializer.authenticate(info); } } else { - // remove the fingerprint slot - FingerprintSlot slot = slots.find(FingerprintSlot.class); + // remove the biometric slot + BiometricSlot slot = slots.find(BiometricSlot.class); slots.remove(slot); _db.setCredentials(creds); @@ -676,24 +684,24 @@ public class PreferencesFragment extends PreferenceFragmentCompat { boolean encrypted = _db.isEncryptionEnabled(); _encryptionPreference.setChecked(encrypted, true); _setPasswordPreference.setVisible(encrypted); - _fingerprintPreference.setVisible(encrypted); + _biometricsPreference.setVisible(encrypted); _slotsPreference.setEnabled(encrypted); _autoLockPreference.setVisible(encrypted); if (encrypted) { SlotList slots = _db.getCredentials().getSlots(); boolean multiPassword = slots.findAll(PasswordSlot.class).size() > 1; - boolean multiFinger = slots.findAll(FingerprintSlot.class).size() > 1; - boolean showSlots = BuildConfig.DEBUG || multiPassword || multiFinger; - boolean canUseFinger = FingerprintHelper.isSupported() && FingerprintHelper.isAvailable(getContext()); + boolean multiBio = slots.findAll(BiometricSlot.class).size() > 1; + boolean showSlots = BuildConfig.DEBUG || multiPassword || multiBio; + boolean canUseBio = BiometricsHelper.isAvailable(getContext()); _setPasswordPreference.setEnabled(!multiPassword); - _fingerprintPreference.setEnabled(canUseFinger && !multiFinger); - _fingerprintPreference.setChecked(slots.has(FingerprintSlot.class), true); + _biometricsPreference.setEnabled(canUseBio && !multiBio); + _biometricsPreference.setChecked(slots.has(BiometricSlot.class), true); _slotsPreference.setVisible(showSlots); } else { _setPasswordPreference.setEnabled(false); - _fingerprintPreference.setEnabled(false); - _fingerprintPreference.setChecked(false, true); + _biometricsPreference.setEnabled(false); + _biometricsPreference.setChecked(false, true); _slotsPreference.setVisible(false); } } @@ -730,20 +738,17 @@ public class PreferencesFragment extends PreferenceFragmentCompat { } } - private class RegisterFingerprintListener implements Dialogs.SlotListener { + private class RegisterBiometricsListener implements BiometricSlotInitializer.Listener { @Override - public void onSlotResult(Slot slot, Cipher cipher) { + public void onInitializeSlot(BiometricSlot slot, Cipher cipher) { DatabaseFileCredentials creds = _db.getCredentials(); - SlotList slots = creds.getSlots(); - try { slot.setKey(creds.getKey(), cipher); } catch (SlotException e) { - onException(e); + onSlotInitializationFailed(0, e.toString()); return; } - - slots.add(slot); + creds.getSlots().add(slot); _db.setCredentials(creds); saveDatabase(); @@ -751,8 +756,10 @@ public class PreferencesFragment extends PreferenceFragmentCompat { } @Override - public void onException(Exception e) { - Toast.makeText(getActivity(), getString(R.string.encryption_enable_fingerprint_error) + e.getMessage(), Toast.LENGTH_SHORT).show(); + public void onSlotInitializationFailed(int errorCode, @NonNull CharSequence errString) { + if (!BiometricsHelper.isCanceled(errorCode)) { + Toast.makeText(getActivity(), String.format("%s: %s", getString(R.string.encryption_enable_biometrics_error), errString), Toast.LENGTH_LONG).show(); + } } } diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/SlotManagerActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/SlotManagerActivity.java index 31367f76..7b9cd1b6 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/SlotManagerActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/SlotManagerActivity.java @@ -7,26 +7,29 @@ import android.view.MenuItem; import android.view.View; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AlertDialog; +import androidx.biometric.BiometricPrompt; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.crypto.KeyStoreHandle; import com.beemdevelopment.aegis.crypto.KeyStoreHandleException; import com.beemdevelopment.aegis.db.DatabaseFileCredentials; -import com.beemdevelopment.aegis.db.slots.FingerprintSlot; +import com.beemdevelopment.aegis.db.slots.BiometricSlot; import com.beemdevelopment.aegis.db.slots.PasswordSlot; import com.beemdevelopment.aegis.db.slots.Slot; import com.beemdevelopment.aegis.db.slots.SlotException; import com.beemdevelopment.aegis.db.slots.SlotList; -import com.beemdevelopment.aegis.helpers.FingerprintHelper; +import com.beemdevelopment.aegis.helpers.BiometricSlotInitializer; +import com.beemdevelopment.aegis.helpers.BiometricsHelper; import com.beemdevelopment.aegis.ui.views.SlotAdapter; import javax.crypto.Cipher; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AlertDialog; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Listener, Dialogs.SlotListener { +public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Listener { private DatabaseFileCredentials _creds; private SlotAdapter _adapter; @@ -42,14 +45,19 @@ public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Li bar.setHomeAsUpIndicator(R.drawable.ic_close); bar.setDisplayHomeAsUpEnabled(true); - findViewById(R.id.button_add_fingerprint).setOnClickListener(view -> { - if (FingerprintHelper.isSupported() && FingerprintHelper.isAvailable(this)) { - Dialogs.showFingerprintDialog(this ,this); + findViewById(R.id.button_add_biometric).setOnClickListener(view -> { + if (BiometricsHelper.isAvailable(this)) { + BiometricSlotInitializer initializer = new BiometricSlotInitializer(SlotManagerActivity.this, new RegisterBiometricsListener()); + BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder() + .setTitle(getString(R.string.add_biometric_slot)) + .setNegativeButtonText(getString(android.R.string.cancel)) + .build(); + initializer.authenticate(info); } }); findViewById(R.id.button_add_password).setOnClickListener(view -> { - Dialogs.showSetPasswordDialog(this, this); + Dialogs.showSetPasswordDialog(this, new PasswordListener()); }); // set up the recycler view @@ -66,17 +74,17 @@ public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Li _adapter.addSlot(slot); } - updateFingerprintButton(); + updateBiometricsButton(); } - private void updateFingerprintButton() { - // only show the fingerprint option if we can get an instance of the fingerprint manager + private void updateBiometricsButton() { + // only show the biometrics option if we can get an instance of the biometrics manager // and if none of the slots in the collection has a matching alias in the keystore int visibility = View.VISIBLE; - if (FingerprintHelper.isSupported() && FingerprintHelper.isAvailable(this)) { + if (BiometricsHelper.isAvailable(this)) { try { KeyStoreHandle keyStore = new KeyStoreHandle(); - for (FingerprintSlot slot : _creds.getSlots().findAll(FingerprintSlot.class)) { + for (BiometricSlot slot : _creds.getSlots().findAll(BiometricSlot.class)) { if (keyStore.containsKey(slot.getUUID().toString())) { visibility = View.GONE; break; @@ -88,7 +96,7 @@ public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Li } else { visibility = View.GONE; } - findViewById(R.id.button_add_fingerprint).setVisibility(visibility); + findViewById(R.id.button_add_biometric).setVisibility(visibility); } private void onSave() { @@ -164,29 +172,60 @@ public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Li slots.remove(slot); _adapter.removeSlot(slot); _edited = true; - updateFingerprintButton(); + updateBiometricsButton(); }) .setNegativeButton(android.R.string.no, null) .create()); } - @Override - public void onSlotResult(Slot slot, Cipher cipher) { - try { - slot.setKey(_creds.getKey(), cipher); - } catch (SlotException e) { - onException(e); - return; - } - + private void addSlot(Slot slot) { _creds.getSlots().add(slot); _adapter.addSlot(slot); _edited = true; - updateFingerprintButton(); + updateBiometricsButton(); } - @Override - public void onException(Exception e) { - Toast.makeText(this, getString(R.string.adding_new_slot_error) + e.getMessage(), Toast.LENGTH_SHORT).show(); + private void showSlotError(String error) { + Toast.makeText(SlotManagerActivity.this, getString(R.string.adding_new_slot_error) + error, Toast.LENGTH_SHORT).show(); + } + + private class RegisterBiometricsListener implements BiometricSlotInitializer.Listener { + + @Override + public void onInitializeSlot(BiometricSlot slot, Cipher cipher) { + try { + slot.setKey(_creds.getKey(), cipher); + addSlot(slot); + } catch (SlotException e) { + onSlotInitializationFailed(0, e.toString()); + } + } + + @Override + public void onSlotInitializationFailed(int errorCode, @NonNull CharSequence errString) { + if (!BiometricsHelper.isCanceled(errorCode)) { + showSlotError(errString.toString()); + } + } + } + + private class PasswordListener implements Dialogs.SlotListener { + + @Override + public void onSlotResult(Slot slot, Cipher cipher) { + try { + slot.setKey(_creds.getKey(), cipher); + } catch (SlotException e) { + onException(e); + return; + } + + addSlot(slot); + } + + @Override + public void onException(Exception e) { + showSlotError(e.toString()); + } } } diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/slides/CustomAuthenticatedSlide.java b/app/src/main/java/com/beemdevelopment/aegis/ui/slides/CustomAuthenticatedSlide.java index dbb782f3..ba44058b 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/slides/CustomAuthenticatedSlide.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/slides/CustomAuthenticatedSlide.java @@ -1,53 +1,39 @@ package com.beemdevelopment.aegis.ui.slides; -import android.annotation.SuppressLint; -import android.content.Context; import android.content.Intent; -import android.hardware.fingerprint.FingerprintManager; -import android.os.Build; import android.os.Bundle; import android.text.method.PasswordTransformationMethod; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; -import android.widget.CompoundButton; import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.biometric.BiometricPrompt; +import androidx.fragment.app.Fragment; import com.beemdevelopment.aegis.R; -import com.beemdevelopment.aegis.crypto.KeyStoreHandle; -import com.beemdevelopment.aegis.crypto.KeyStoreHandleException; -import com.beemdevelopment.aegis.db.slots.FingerprintSlot; -import com.beemdevelopment.aegis.db.slots.Slot; +import com.beemdevelopment.aegis.db.slots.BiometricSlot; +import com.beemdevelopment.aegis.helpers.BiometricSlotInitializer; +import com.beemdevelopment.aegis.helpers.BiometricsHelper; import com.beemdevelopment.aegis.helpers.EditTextHelper; -import com.beemdevelopment.aegis.helpers.FingerprintUiHelper; import com.github.paolorotolo.appintro.ISlidePolicy; import com.github.paolorotolo.appintro.ISlideSelectionListener; import com.google.android.material.snackbar.Snackbar; -import com.mattprecious.swirl.SwirlView; import javax.crypto.Cipher; -import javax.crypto.SecretKey; -import androidx.fragment.app.Fragment; - -public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiHelper.Callback, ISlidePolicy, ISlideSelectionListener { +public class CustomAuthenticatedSlide extends Fragment implements ISlidePolicy, ISlideSelectionListener { private int _cryptType; private EditText _textPassword; private EditText _textPasswordConfirm; private CheckBox _checkPasswordVisibility; private int _bgColor; - private LinearLayout _boxFingerprint; - private SwirlView _imgFingerprint; - private TextView _textFingerprint; - private FingerprintUiHelper _fingerHelper; - private KeyStoreHandle _storeHandle; - private FingerprintSlot _fingerSlot; - private FingerprintManager.CryptoObject _fingerCryptoObj; - private boolean _fingerAuthenticated; + private BiometricSlot _bioSlot; + private Cipher _bioCipher; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -55,13 +41,6 @@ public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiH _textPassword = view.findViewById(R.id.text_password); _textPasswordConfirm = view.findViewById(R.id.text_password_confirm); _checkPasswordVisibility = view.findViewById(R.id.check_toggle_visibility); - _boxFingerprint = view.findViewById(R.id.box_fingerprint); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - ViewGroup insertPoint = view.findViewById(R.id.img_fingerprint_insert); - _imgFingerprint = new SwirlView(getContext()); - insertPoint.addView(_imgFingerprint, 0, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); - } _checkPasswordVisibility.setOnCheckedChangeListener((buttonView, isChecked) -> { if (isChecked) { @@ -74,7 +53,6 @@ public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiH } }); - _textFingerprint = view.findViewById(R.id.text_fingerprint); view.findViewById(R.id.main).setBackgroundColor(_bgColor); return view; } @@ -83,82 +61,53 @@ public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiH return _cryptType; } + public BiometricSlot getBiometricSlot() { + return _bioSlot; + } + + public Cipher getBiometriCipher() { + return _bioCipher; + } + public char[] getPassword() { return EditTextHelper.getEditTextChars(_textPassword); } - @SuppressLint("NewApi") - public Cipher getFingerCipher() { - return _fingerCryptoObj.getCipher(); - } - - public FingerprintSlot getFingerSlot() { - return _fingerSlot; - } - public void setBgColor(int color) { _bgColor = color; } + public void showBiometricPrompt() { + BiometricSlotInitializer initializer = new BiometricSlotInitializer(this, new BiometricsListener()); + BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder() + .setTitle(getString(R.string.set_up_biometric)) + .setNegativeButtonText(getString(android.R.string.cancel)) + .build(); + initializer.authenticate(info); + } + @Override - @SuppressLint("NewApi") public void onSlideSelected() { Intent intent = getActivity().getIntent(); _cryptType = intent.getIntExtra("cryptType", CustomAuthenticationSlide.CRYPT_TYPE_INVALID); - switch(_cryptType) { - case CustomAuthenticationSlide.CRYPT_TYPE_NONE: - case CustomAuthenticationSlide.CRYPT_TYPE_PASS: - break; - case CustomAuthenticationSlide.CRYPT_TYPE_FINGER: - _boxFingerprint.setVisibility(View.VISIBLE); - - SecretKey key; - try { - if (_storeHandle == null) { - _storeHandle = new KeyStoreHandle(); - _fingerSlot = new FingerprintSlot(); - } - key = _storeHandle.generateKey(_fingerSlot.getUUID().toString()); - } catch (KeyStoreHandleException e) { - throw new RuntimeException(e); - } - - if (_fingerHelper == null) { - FingerprintManager fingerManager = (FingerprintManager) getContext().getSystemService(Context.FINGERPRINT_SERVICE); - _fingerHelper = new FingerprintUiHelper(fingerManager, _imgFingerprint, _textFingerprint, this); - } - - try { - Cipher cipher = Slot.createEncryptCipher(key); - _fingerCryptoObj = new FingerprintManager.CryptoObject(cipher); - } catch (Exception e) { - throw new RuntimeException(e); - } - _fingerHelper.startListening(_fingerCryptoObj); - break; - default: - throw new RuntimeException(); + if (_cryptType == CustomAuthenticationSlide.CRYPT_TYPE_BIOMETRIC) { + showBiometricPrompt(); } } @Override - @SuppressLint("NewApi") public void onSlideDeselected() { - if (_fingerHelper != null) { - _fingerAuthenticated = false; - _boxFingerprint.setVisibility(View.INVISIBLE); - _fingerHelper.stopListening(); - } + } @Override public boolean isPolicyRespected() { - switch(_cryptType) { + switch (_cryptType) { case CustomAuthenticationSlide.CRYPT_TYPE_NONE: return true; - case CustomAuthenticationSlide.CRYPT_TYPE_FINGER: - if (!_fingerAuthenticated) { + case CustomAuthenticationSlide.CRYPT_TYPE_BIOMETRIC: + if (_bioSlot == null) { return false; } // intentional fallthrough @@ -169,7 +118,7 @@ public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiH return false; default: - throw new RuntimeException(); + return false; } } @@ -178,26 +127,30 @@ public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiH String message; if (!EditTextHelper.areEditTextsEqual(_textPassword, _textPasswordConfirm) && !_checkPasswordVisibility.isChecked()) { message = getString(R.string.password_equality_error); - } else if (!_fingerAuthenticated) { - message = getString(R.string.register_fingerprint); - } else { - return; - } - View view = getView(); - if (view != null) { - Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_LONG); - snackbar.show(); + View view = getView(); + if (view != null) { + Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_LONG); + snackbar.show(); + } + } else if (_bioSlot == null) { + showBiometricPrompt(); } } - @Override - public void onAuthenticated() { - _fingerAuthenticated = true; - } + private class BiometricsListener implements BiometricSlotInitializer.Listener { - @Override - public void onError() { + @Override + public void onInitializeSlot(BiometricSlot slot, Cipher cipher) { + _bioSlot = slot; + _bioCipher = cipher; + } + @Override + public void onSlotInitializationFailed(int errorCode, @NonNull CharSequence errString) { + if (!BiometricsHelper.isCanceled(errorCode)) { + Toast.makeText(CustomAuthenticatedSlide.this.getContext(), errString, Toast.LENGTH_LONG).show(); + } + } } } diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/slides/CustomAuthenticationSlide.java b/app/src/main/java/com/beemdevelopment/aegis/ui/slides/CustomAuthenticationSlide.java index 85d8f0b4..289b5980 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/slides/CustomAuthenticationSlide.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/slides/CustomAuthenticationSlide.java @@ -1,8 +1,6 @@ package com.beemdevelopment.aegis.ui.slides; -import android.annotation.SuppressLint; import android.content.Intent; -import android.hardware.fingerprint.FingerprintManager; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -11,18 +9,18 @@ import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.TextView; +import androidx.fragment.app.Fragment; + import com.beemdevelopment.aegis.R; -import com.beemdevelopment.aegis.helpers.FingerprintHelper; +import com.beemdevelopment.aegis.helpers.BiometricsHelper; import com.github.paolorotolo.appintro.ISlidePolicy; import com.google.android.material.snackbar.Snackbar; -import androidx.fragment.app.Fragment; - public class CustomAuthenticationSlide extends Fragment implements ISlidePolicy, RadioGroup.OnCheckedChangeListener { public static final int CRYPT_TYPE_INVALID = 0; public static final int CRYPT_TYPE_NONE = 1; public static final int CRYPT_TYPE_PASS = 2; - public static final int CRYPT_TYPE_FINGER = 3; + public static final int CRYPT_TYPE_BIOMETRIC = 3; private RadioGroup _buttonGroup; private int _bgColor; @@ -35,9 +33,9 @@ public class CustomAuthenticationSlide extends Fragment implements ISlidePolicy, onCheckedChanged(_buttonGroup, _buttonGroup.getCheckedRadioButtonId()); // only enable the fingerprint option if the api version is new enough, permission is granted and a scanner is found - if (FingerprintHelper.isSupported() && FingerprintHelper.isAvailable(getContext())) { - RadioButton button = view.findViewById(R.id.rb_fingerprint); - TextView text = view.findViewById(R.id.text_rb_fingerprint); + if (BiometricsHelper.isAvailable(getContext())) { + RadioButton button = view.findViewById(R.id.rb_biometrics); + TextView text = view.findViewById(R.id.text_rb_biometrics); button.setEnabled(true); text.setEnabled(true); } @@ -75,8 +73,8 @@ public class CustomAuthenticationSlide extends Fragment implements ISlidePolicy, case R.id.rb_password: id = CRYPT_TYPE_PASS; break; - case R.id.rb_fingerprint: - id = CRYPT_TYPE_FINGER; + case R.id.rb_biometrics: + id = CRYPT_TYPE_BIOMETRIC; break; default: throw new RuntimeException(); diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/tasks/SlotListTask.java b/app/src/main/java/com/beemdevelopment/aegis/ui/tasks/SlotListTask.java index fad63bd3..5bcf6f81 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/tasks/SlotListTask.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/tasks/SlotListTask.java @@ -6,7 +6,7 @@ import android.content.Context; import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.crypto.CryptoUtils; import com.beemdevelopment.aegis.crypto.MasterKey; -import com.beemdevelopment.aegis.db.slots.FingerprintSlot; +import com.beemdevelopment.aegis.db.slots.BiometricSlot; import com.beemdevelopment.aegis.db.slots.PasswordSlot; import com.beemdevelopment.aegis.db.slots.Slot; import com.beemdevelopment.aegis.db.slots.SlotException; @@ -38,8 +38,8 @@ public class SlotListTask extends ProgressDialogTask extends ProgressDialogTask - - - - - - - - + android:layout_height="wrap_content"> + + + + + + android:text="@string/invalidated_biometrics"/> diff --git a/app/src/main/res/layout/activity_slots.xml b/app/src/main/res/layout/activity_slots.xml index 6306d0d1..036f42a8 100644 --- a/app/src/main/res/layout/activity_slots.xml +++ b/app/src/main/res/layout/activity_slots.xml @@ -53,7 +53,7 @@ diff --git a/app/src/main/res/layout/dialog_fingerprint.xml b/app/src/main/res/layout/dialog_fingerprint.xml deleted file mode 100644 index 2c2a339f..00000000 --- a/app/src/main/res/layout/dialog_fingerprint.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - diff --git a/app/src/main/res/layout/fragment_authenticated_slide.xml b/app/src/main/res/layout/fragment_authenticated_slide.xml index 40b3b847..f7d9cac4 100644 --- a/app/src/main/res/layout/fragment_authenticated_slide.xml +++ b/app/src/main/res/layout/fragment_authenticated_slide.xml @@ -68,43 +68,4 @@ - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_authentication_slide.xml b/app/src/main/res/layout/fragment_authentication_slide.xml index 495373f2..6b87d1f7 100644 --- a/app/src/main/res/layout/fragment_authentication_slide.xml +++ b/app/src/main/res/layout/fragment_authentication_slide.xml @@ -60,22 +60,22 @@ android:textColor="@color/secondary_text_inverted" /> diff --git a/app/src/main/res/raw/notices.xml b/app/src/main/res/raw/notices.xml index 368572fd..c46052ac 100644 --- a/app/src/main/res/raw/notices.xml +++ b/app/src/main/res/raw/notices.xml @@ -36,12 +36,6 @@ Copyright (c) 2000-2017 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) Apache Software License 2.0 - - Swirl - https://github.com/mattprecious/swirl - Copyright 2016 Matthew Precious - Apache Software License 2.0 - CircleImageView https://github.com/hdodenhof/CircleImageView diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index 1b049075..c8f22317 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -38,22 +38,14 @@ Automatische Sperre, wenn die App geschlossen oder dein Gerät gesperrt wird. Verschlüsselung Verschlüssele die Datenbank und entsperre mit einem Passwort oder Fingerabdruck. - Fingerabdruck - Ermöglicht es, dass auf diesem Gerät registrierte Fingerabdrücke den Tresor freischalten. Passwort ändern Legen ein neues Passwort fest, das erforderlich ist, um deinen Tresor freizuschalten. - Berührungssensor - Fingerabdruck nicht erkannt. Versuche es noch einmal. - Fingerabdruck erkannt - Sicherheit Keine Du brauchst kein Passwort, um den Tresor freizuschalten, und es wird nicht verschlüsselt. Diese Option wird nicht empfohlen. Passwort Du benötigst ein Passwort, um den Tresor freizuschalten. - Fingerabdruck - Zusätzlich zu einem Passwort können auf diesem Gerät registrierte Fingerabdrücke verwendet werden, um den Tresor freizuschalten. Passwort Gib dein Passwort ein Entsperre den Tresor. @@ -61,7 +53,6 @@ Bitte gib einen Gruppennamen ein Bitte gib eine Zahl ein Bitte bestätige das Passwort - Es wurde eine Änderung der Sicherheitseinstellungen deines Geräts festgestellt. Bitte gehe zu \"Aegis -> Einstellungen -> Fingerabdruck\" und füge deinen Fingerabdruck erneut hinzu. Entsperren Fortgeschritten @@ -72,7 +63,6 @@ QR-Code scannen Bild scannen Manuell eingeben - Fingerabdruck hinzufügen Passwort hinzufügen Der Tresor ist nur so sicher wie dein schwächster Schlüssel. Wenn ein neuer Fingerabdruck zu deinem Gerät hinzugefügt wird, musst du die Fingerabdruck-Authentifizierung in Aegis reaktivieren. Kopieren @@ -85,7 +75,6 @@ Der Tresor konnte nicht entsperrt werden. Falsches Passwort. Achte darauf, dass du dein Passwort nicht falsch eingibst. Passwörter sollten identisch und nicht leer sein. - Registriere deinen Fingerabdruck Bitte wähle eine Authentifizierungsmethode aus Verschlüsseln des Tresors Eintrag löschen @@ -124,7 +113,6 @@ Die Datenbank wurde exportiert nach: Diese Aktion exportiert die Datenbank aus dem privaten Speicher von Aegis. Beim Versuch, das Passwort festzulegen, ist ein Fehler aufgetreten: - Beim Versuch den Fingerabdruck zu entsperren, ist ein Fehler aufgetreten: Keine Kameras verfügbar Beim Versuch, den QR-Code zu lesen, ist ein Fehler aufgetreten. Raw diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml index 2b6f8bf2..cde2eafa 100644 --- a/app/src/main/res/values-fr-rFR/strings.xml +++ b/app/src/main/res/values-fr-rFR/strings.xml @@ -41,22 +41,14 @@ Verrouiller automatiquement lorsque vous fermez l\'application ou verrouillez votre appareil. Chiffrement Chiffrer la base de données et la déverrouiller avec un mot de passe ou une empreinte digitale - Empreinte digitale - Autoriser les empreintes digitales enregistrées sur cet appareil à déverrouiller le coffre-fort Changer le mot de passe Définir un mot de passe dont vous aurez besoin pour déverrouiller votre coffre-fort - Appuyez sur le capteur - Empreinte digitale non reconnue. Réessayez. - Empreinte digitale reconnue - Sécurité Aucun Vous n\'avez pas besoin d\'un mot de passe pour déverrouiller le coffre-fort et il ne sera pas chiffré. Cette option n\'est pas recommandée. Mot de passe Vous avez besoin d\'un mot de passe pour déverrouiller le coffre-fort. - Empreinte digitale - En plus d\'un mot de passe, les empreintes digitales enregistrées sur cet appareil peuvent être utilisées pour déverrouiller le coffre-fort. Mot de passe Saisissez votre mot de passe Déverrouiller le coffre-fort @@ -64,7 +56,6 @@ Veuillez saisir un nom de groupe Veuillez saisir un nombre Veuillez confirmer le mot de passe - Un changement dans les paramètres de sécurité de votre appareil a été détecté. Veuillez aller dans \"Aegis -> Paramètres -> Empreinte digitale\" et ajouter à nouveau votre empreinte digitale. Déverrouiller Avancé @@ -75,7 +66,6 @@ Scanner code QR Scanner image Saisir manuellement - Ajouter une empreinte digitale Ajouter un mot de passe Le coffre-fort est seulement aussi sécurisé que votre code secret le plus faible. Quand une nouvelle empreinte digitale est ajoutée à votre appareil, vous devrez réactiver l\'authentification par empreinte digitale dans Aegis. Copier @@ -88,7 +78,6 @@ Impossible de déverrouiller le coffre-fort Mot de passe incorrect. Assurez-vous de ne pas avoir fait de faute de frappe lors de la saisie de votre mot de passe. Les mots de passe doivent être identiques et non vides - Enregistrer votre empreinte digitale Veuillez sélectionner une méthode d\'authentification Chiffrement du coffre-fort Supprimer entrée @@ -127,7 +116,6 @@ La base de données a été exportée vers : Cette action va exporter la base de données en dehors du stockage privé d\'Aegis. Une erreur est survenue en essayant de définir le mot de passe : - Une erreur est survenue en essayant d\'activer le déverrouillage par empreinte digitale : Aucun appareil photo disponible Une erreur est survenue en essayant de lire le code QR Brut diff --git a/app/src/main/res/values-nl-rNL/strings.xml b/app/src/main/res/values-nl-rNL/strings.xml index 42639a4c..10b36ea7 100644 --- a/app/src/main/res/values-nl-rNL/strings.xml +++ b/app/src/main/res/values-nl-rNL/strings.xml @@ -36,22 +36,14 @@ Time-out voor drukken om te laten zien Encryptie Encrypt de database en ontgrendel deze met een wachtwoord of vingerafdruk - Vingerafdruk - Schakel in om met de op dit toestel geregistreerde vingerafdrukken de kluis te kunnen ontgrendelen Wachtwoord wijzigen Stel een nieuw wachtwoord in waarmee je de kluis kunt ontgrendelen - Aanraaksensor - Vingerafdruk niet herkend. Probeer opnieuw. - Vingerafdruk herkend - Beveiliging Geen Je hebt geen wachtwoord nodig om de kluis te ontgrendelen en de kluis zal niet worden geëncrypt. Deze optie wordt niet aanbevolen. Wachtwoord Je hebt een wachtwoord nodig om de kluis te ontgrendelen. - Vingerafdruk - Als toevoeging op een wachtwoord kan er ook gebruik gemaakt worden van de op dit toestel geregistreerde vingerafdrukken om de kluis te ontgrendelen. Wachtwoord Vul je wachtwoord in Kluis ontgrendelen @@ -59,7 +51,6 @@ Vul een groepsnaam in Kies een getal Bevestig het wachtwoord - Er is een verandering in de beveiligingsinstellingen van je toestel gedetecteerd. Ga naar \"Instellingen -> Vingerafdruk\" om je vingerafdruk opnieuw toe te voegen. Ontgrendel Geavanceerd @@ -69,7 +60,6 @@ Sleutel Scan QR-code Handmatig invoeren - Vingerafdruk toevoegen Wachtwoord toevoegen De kluis is slechts even veilig als je zwakste wachtwoord. Wanneer een nieuw vingerafdruk is toegevoegd aan je toestel, moet authenticatie met vingerafdruk opnieuw worden geactiveerd binnen Aegis. Kopiëren @@ -82,7 +72,6 @@ Kluis kon niet worden ontgrendeld Wachtwoord onjuist. Controleer of je het wachtwoord correct hebt ingetypt. Wachtwoorden moeten overeen komen en mogen niet leeg zijn - Registreer je vingerafdruk Kies een inlogmethode De kluis wordt geëncrypt Item verwijderen @@ -117,7 +106,6 @@ De database is geëxporteerd naar: Deze actie zal de database uit de privé-opslag van Aegis exporteren. Er is een fout opgetreden tijdens het instellen van het wachtwoord: - Er is een fout opgetreden tijdens het inschakelen van ontgrendeling met vingerafdruk: Geen camera\'s beschikbaar Er is een fout opgetreden tijdens het lezen van de QR-code Onbewerkt diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml index c1dab9e5..372f20ca 100644 --- a/app/src/main/res/values-ru-rRU/strings.xml +++ b/app/src/main/res/values-ru-rRU/strings.xml @@ -36,22 +36,14 @@ Автоматическая блокировка при закрытии приложения и блокировке устройства. Шифрование Зашифруйте базу данных и разблокируйте ее с помощью пароля или отпечатка пальца - Отпечаток пальца - Разрешить разблокировку хранилища при помощи отпечатков пальцев, зарегистрированных на этом устройстве Изменить пароль Установите новый пароль, который вам понадобится, чтобы разблокировать ваше хранилище - Прикоснитесь к сенсору - Отпечаток пальца не распознан. Попробуйте снова. - Отпечаток пальца распознан - Безопасность Нет Вам не нужен пароль для разблокировки хранилища, и оно не будет зашифровано. Этот вариант не рекомендуется. Пароль Вам нужен пароль, чтобы разблокировать хранилище. - Отпечаток пальца - В дополнение к паролю, для разблокировки хранилища можно использовать отпечатки пальцев, зарегистрированные на этом устройстве. Пароль Введите свой пароль Разблокировать хранилище @@ -59,7 +51,6 @@ Пожалуйста, введите название группы Пожалуйста, введите номер Пожалуйста, подтвердите пароль - Обнаружено изменение настроек безопасности вашего устройства. Перейдите в «Настройки - Отпечаток пальца» и повторно добавьте свой отпечаток пальца. Разблокировать Расширенные @@ -70,7 +61,6 @@ Сканировать QR-код Сканировать изображение Ввести вручную - Добавить отпечаток Добавить пароль Хранилище надежно защищено. При добавлении нового отпечатка пальца на ваше устройство необходимо будет заново настроить разблокировку хранилища по отпечатку пальца в Aegis. Копировать @@ -83,7 +73,6 @@ Не удалось разблокировать хранилище Некорректный пароль. Убедитесь, что вы не допустили опечатку в вашем пароле. Пароли должны быть одинаковыми и непустыми - Зарегистрируйте свой отпечаток пальца Пожалуйста, выберите метод аутентификации Шифрование хранилища Удалить запись @@ -122,7 +111,6 @@ База данных была экспортирована в: Это действие экспортирует базу данных из приватного хранилища Aegis. Произошла ошибка при попытке установить пароль: - Произошла ошибка при попытке разблокировать отпечаток пальца: Нет доступных камер Произошла ошибка при попытке прочитать QR-код Необработанный diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 40225b9c..a07cc0c1 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -40,20 +40,13 @@ 关闭应用或锁定设备时自动锁定 加密 加密数据库并使用密码或指纹解锁数据库 - 指纹 - 允许在此设备上注册的指纹解锁数据库 更改密码 设置解锁数据库所需的新密码 - 触摸感应器 - 无法识别指纹。再试一次。 - 识别指纹 安全 未设置 您无需密码即可解锁保管库,也不会对其进行加密。不建议使用此选项。 密码 您需要密码来解锁数据库。 - 指纹 - 除了密码之外,在此设备上注册的指纹也可用于解锁数据库。 密码 输入您的密码 打开数据库 @@ -61,7 +54,6 @@ 请输入组名 请输入号码 请确认密码 - 检测到设备安全设置更改。请到“Aegis- >设置- >指纹”,重新添加您的指纹。 解锁 高级设置 秒 @@ -71,7 +63,6 @@ 扫描二维码 扫描图像 手动输入 - 添加指纹 添加密码 数据库与您最薄弱的秘密一样安全。向设备添加新指纹时,您需要在 Aegis 中重新激活指纹身份验证。 复制 @@ -84,7 +75,6 @@ 无法解锁数据库 密码错误。确保您未输错密码。 密码应该是相同且非空的。 - 注册您的指纹 请选择身份验证方法 加密数据库 删除条目 @@ -123,7 +113,6 @@ 数据库已经导出至: 此操作将导出 Aegis 专用存储的数据库。 试图设置密码时出错: - 试图启用指纹解锁时出错: 未找到可用的相机 试图读取二维码时出错 源文件 diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 2cb894f9..0530e7d6 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -3,7 +3,7 @@ @string/authentication_method_none @string/authentication_method_password - @string/authentication_method_fingerprint + @string/authentication_method_biometrics diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index c12cd4e3..8bdc4883 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -19,11 +19,6 @@ #ffffff #DBDBDB - #434343 - #FF5252 - #FFFFFF - #FF5252 - #757575 #ffffff #ffffff diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7cbc26a3..20303240 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -40,23 +40,19 @@ Auto lock Automatically lock when you close the app or lock your device. Encryption - Encrypt the database and unlock it with a password or fingerprint - Fingerprint - Allow fingerprints registered on this device to unlock the vault + Encrypt the database and unlock it with a password or biometrics + Biometric unlock + Allow biometric authentication to unlock the vault Change password Set a new password which you will need to unlock your vault - Touch sensor - Fingerprint not recognized. Try again. - Fingerprint recognized - Security None You don\'t need a password to unlock the vault and it will not be encrypted. This option is not recommended. Password You need a password to unlock the vault. - Fingerprint - In addition to a password, fingerprints registered on this device can be used to unlock the vault. + Biometrics + In addition to a password, biometrics registered on this device, like a fingerprint or your face, can be used to unlock the vault. Password Enter your password Unlock the vault @@ -64,9 +60,10 @@ Please enter a group name Please enter a number Please confirm the password - A change in your device\'s security settings has been detected. Please go to \"Aegis -> Settings -> Fingerprint\" and re-add your fingerprint. + A change in your device\'s security settings has been detected. Please go to \"Aegis -> Settings -> Biometrics\" and re-enable biometric unlock. Unlock + Biometrics Advanced seconds Counter @@ -75,9 +72,11 @@ Scan QR code Scan image Enter manually - Add fingerprint + Add biometric + Add biometric slot + Set up biometric unlock Add password - The vault is only as secure as your weakest secret. When a new fingerprint is added to your device, you will need to reactivate fingerprint authentication within Aegis. + The vault is only as secure as your weakest secret. If you change the biometric authentication settings of your device, you will need to reactivate biometric unlock within Aegis. Copy Edit Delete @@ -89,7 +88,6 @@ Couldn\'t unlock vault Incorrect password. Make sure you didn\'t mistype your password. Passwords should be identical and non-empty - Register your fingerprint Please select an authentication method Encrypting the vault Delete entry @@ -132,7 +130,7 @@ The database has been exported to: This action will export the database out of Aegis\' private storage. An error occurred while trying to set the password: - An error occurred while trying to enable fingerprint unlock: + An error occurred while trying to enable biometric unlock No cameras available An error occurred while trying to read the QR code Raw diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 78525f1c..fc846494 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -11,8 +11,6 @@ @@ -35,9 +33,6 @@ @color/icon_primary @color/icon_primary_inverted - @color/colorSwirlPrimary - @color/colorSwirlError - @style/ActionModeStyle @style/ThemeOverlay.AppCompat.Dark.ActionBar @style/DialogStyle @@ -104,9 +99,6 @@ @color/icon_primary_dark @color/icon_primary_dark_inverted - @color/colorSwirlPrimaryDark - @color/colorSwirlErrorDark - @style/ActionModeStyle.Dark @style/DialogStyle.Dark diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 9e25838b..dac6ff11 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -103,9 +103,9 @@ app:iconSpaceReserved="false"/> diff --git a/docs/diagram.svg b/docs/diagram.svg index b71da7f9..58b0fd20 100644 --- a/docs/diagram.svg +++ b/docs/diagram.svg @@ -1 +1 @@ -Master key256-bit[Not supported by viewer]Password slot- Encrypted key- Parameters- Salt[Not supported by viewer]Fingerprint slot- Encrypted key[Not supported by viewer]Raw slot- Encrypted key[Not supported by viewer]AES-256 GCMEncrypt master key[Not supported by viewer]Android KeyStoreAuthorize use of key using fingerprint[Not supported by viewer]scryptDerive key from password<b>scrypt</b><br>Derive key from password<br>Vault[Not supported by viewer] \ No newline at end of file +Master key256-bit[Not supported by viewer]Password slot- Encrypted key- Parameters- Salt[Not supported by viewer]Biometric slot- Encrypted key[Not supported by viewer]Raw slot- Encrypted key[Not supported by viewer]AES-256 GCMEncrypt master key[Not supported by viewer]Android KeyStoreAuthorize use of key using biometrics[Not supported by viewer]scryptDerive key from password<b>scrypt</b><br>Derive key from password<br>Vault[Not supported by viewer] \ No newline at end of file diff --git a/docs/vault.md b/docs/vault.md index b98612da..dc0d3d40 100644 --- a/docs/vault.md +++ b/docs/vault.md @@ -70,8 +70,8 @@ __master key__. Aegis supports unlocking a vault with multiple different credentials. The main credential is a key derived from a user-provided password. In addition to that, -users can also add a key backed by the Android KeyStore (authorized by the scan -of a fingerprint) as a credential. +users can also add a key backed by the Android KeyStore (authorized by biometrics) +as a credential. #### Slots @@ -146,7 +146,7 @@ The different slot types are identified with a numerical ID. | Type | ID | | :---------- | :--- | | Raw | 0x00 | -| Fingerprint | 0x01 | +| Biometric | 0x01 | | Password | 0x02 | ##### Raw @@ -172,9 +172,9 @@ a unique randomly generated ``UUID`` (version 4). } ``` -##### Fingerprint +##### Biometric -The structure of the Fingerprint slot is exactly the same as the Raw slot. The +The structure of the Biometric slot is exactly the same as the Raw slot. The difference is that the wrapper key is backed by the Android KeyStore, whereas Raw slots don't imply use of a particular storage type. diff --git a/metadata/en-US/full_description.txt b/metadata/en-US/full_description.txt index 12cac6ad..115bc19d 100644 --- a/metadata/en-US/full_description.txt +++ b/metadata/en-US/full_description.txt @@ -7,10 +7,10 @@ to set a password, which is highly recommended, the vault will be encrypted using AES-256. If someone with malicious intent gets a hold of the vault file, it’s impossible for them to retrieve the contents without knowing the password. -Fingerprint unlock +Biometric unlock Entering your password each time you need access to a one-time password can be cumbersome. Fortunately, you can also enable -fingerprint unlock if your device has a fingerprint scanner. +biometric unlock if your device has a biometrics sensor. Compatibility Aegis supports the HOTP and TOTP algorithms. These two algorithms are