Replace the custom fingerprint auth UI with BiometricPrompt

This patch replaces the usage of the deprecated FingerprintManager API with
BiometricPrompt. This uses the Android X library, so we get the native biometric
prompt on recent versions of Android and a Google-made one on older versions. By
not working with custom prompts for biometric authentication like we do now, we
can be sure that any issues like #70, #81, #237 are not actually our fault.
Here's what it looks like:

![](https://alexbakker.me/u/b2rmf3x0b9.jpeg)

As a nice aside, this also adds support for the new facial recognition as an
authentication method on Pixel 4 phones.

This is still a draft, but early feedback is welcome.
This commit is contained in:
Alexander Bakker 2019-10-16 22:16:47 +02:00
parent a93ced6e34
commit 3be9aecb88
39 changed files with 499 additions and 716 deletions

View file

@ -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:__

View file

@ -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

View file

@ -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}"

View file

@ -4,7 +4,7 @@
package="com.beemdevelopment.aegis">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

View file

@ -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;
}
}

View file

@ -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");

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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();
}
}

View file

@ -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);
}
}

View file

@ -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();
}
}
}

View file

@ -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<FingerprintUiHelper> 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);
}

View file

@ -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

View file

@ -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<DatabaseEntry> _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();
}
}
}

View file

@ -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());
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -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();

View file

@ -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<T extends Slot> extends ProgressDialogTask<SlotListTas
if (slot instanceof PasswordSlot) {
char[] password = (char[]) params.getObj();
return decryptPasswordSlot((PasswordSlot) slot, password);
} else if (slot instanceof FingerprintSlot) {
return decryptFingerprintSlot((FingerprintSlot) slot, (Cipher) params.getObj());
} else if (slot instanceof BiometricSlot) {
return decryptBiometricSlot((BiometricSlot) slot, (Cipher) params.getObj());
}
} catch (SlotException e) {
throw new RuntimeException(e);
@ -51,7 +51,7 @@ public class SlotListTask<T extends Slot> extends ProgressDialogTask<SlotListTas
return null;
}
private Result decryptFingerprintSlot(FingerprintSlot slot, Cipher cipher)
private Result decryptBiometricSlot(BiometricSlot slot, Cipher cipher)
throws SlotException, SlotIntegrityException {
MasterKey key = slot.getKey(cipher);
return new Result(key, slot);

View file

@ -5,16 +5,16 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
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.slots.FingerprintSlot;
import com.beemdevelopment.aegis.db.slots.BiometricSlot;
import com.beemdevelopment.aegis.db.slots.PasswordSlot;
import com.beemdevelopment.aegis.db.slots.RawSlot;
import com.beemdevelopment.aegis.db.slots.Slot;
import com.beemdevelopment.aegis.helpers.FingerprintHelper;
import androidx.recyclerview.widget.RecyclerView;
import com.beemdevelopment.aegis.helpers.BiometricsHelper;
public class SlotHolder extends RecyclerView.ViewHolder {
private TextView _slotUsed;
@ -36,10 +36,10 @@ public class SlotHolder extends RecyclerView.ViewHolder {
if (slot instanceof PasswordSlot) {
_slotName.setText(R.string.password);
_slotImg.setImageResource(R.drawable.ic_create_black_24dp);
} else if (slot instanceof FingerprintSlot) {
_slotName.setText(R.string.authentication_method_fingerprint);
} else if (slot instanceof BiometricSlot) {
_slotName.setText(R.string.authentication_method_biometrics);
_slotImg.setImageResource(R.drawable.ic_fingerprint_black_24dp);
if (FingerprintHelper.isSupported()) {
if (BiometricsHelper.isAvailable(itemView.getContext())) {
try {
KeyStoreHandle keyStore = new KeyStoreHandle();
if (keyStore.containsKey(slot.getUUID().toString())) {

View file

@ -49,40 +49,29 @@
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/button_decrypt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="125dp"
android:text="@string/unlock" />
</LinearLayout>
<LinearLayout
android:id="@+id/box_fingerprint"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="12dp"
android:visibility="gone">
<LinearLayout
android:id="@+id/img_fingerprint_insert"
android:layout_width="60dp"
android:layout_height="60dp"
android:orientation="horizontal"/>
<TextView
android:id="@+id/text_fingerprint"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="15dp"
android:text="@string/fingerprint_hint"
android:textColor="?attr/secondaryText"/>
android:layout_height="wrap_content">
<Button
android:id="@+id/button_decrypt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="125dp"
android:text="@string/unlock" />
<Button
android:id="@+id/button_biometrics"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="125dp"
android:text="@string/biometrics"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/box_fingerprint_info"
android:id="@+id/box_biometric_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@ -98,7 +87,7 @@
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/invalidated_fingerprint"/>
android:text="@string/invalidated_biometrics"/>
</LinearLayout>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -53,7 +53,7 @@
</LinearLayout>
<LinearLayout
android:id="@+id/button_add_fingerprint"
android:id="@+id/button_add_biometric"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@ -75,7 +75,7 @@
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/add_fingerprint"
android:text="@string/add_biometric"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorSecondary"/>
</LinearLayout>

View file

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:paddingTop="20dp">
<com.mattprecious.swirl.SwirlView
android:id="@+id/img_fingerprint"
android:layout_width="60dp"
android:layout_height="60dp"/>
<TextView
android:id="@+id/text_fingerprint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:text="@string/fingerprint_hint"
android:textColor="?attr/secondaryText"
android:layout_gravity="center_vertical"/>
</LinearLayout>

View file

@ -68,43 +68,4 @@
</LinearLayout>
<LinearLayout
android:id="@+id/box_fingerprint"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:visibility="invisible">
<TextView
android:text="@string/authentication_method_fingerprint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp"
android:textColor="@color/primary_text_inverted"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="12dp">
<LinearLayout
android:id="@+id/img_fingerprint_insert"
android:layout_width="60dp"
android:layout_height="60dp"
android:orientation="horizontal"/>
<TextView
android:id="@+id/text_fingerprint"
android:textColor="@color/primary_text_inverted"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="15dp"
android:text="@string/fingerprint_hint"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View file

@ -60,22 +60,22 @@
android:textColor="@color/secondary_text_inverted" />
<RadioButton
android:id="@+id/rb_fingerprint"
android:id="@+id/rb_biometrics"
android:enabled="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/authentication_method_fingerprint"
android:text="@string/authentication_method_biometrics"
android:textSize="16sp" />
<TextView
android:id="@+id/text_rb_fingerprint"
android:id="@+id/text_rb_biometrics"
android:enabled="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="-5dp"
android:text="@string/authentication_method_fingerprint_description"
android:text="@string/authentication_method_biometrics_description"
android:textColor="@color/disabled_textview_colors" />
</RadioGroup>

View file

@ -36,12 +36,6 @@
<copyright>Copyright (c) 2000-2017 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org)</copyright>
<license>Apache Software License 2.0</license>
</notice>
<notice>
<name>Swirl</name>
<url>https://github.com/mattprecious/swirl</url>
<copyright>Copyright 2016 Matthew Precious</copyright>
<license>Apache Software License 2.0</license>
</notice>
<notice>
<name>CircleImageView</name>
<url>https://github.com/hdodenhof/CircleImageView</url>

View file

@ -38,22 +38,14 @@
<string name="pref_auto_lock_summary">Automatische Sperre, wenn die App geschlossen oder dein Gerät gesperrt wird.</string>
<string name="pref_encryption_title">Verschlüsselung</string>
<string name="pref_encryption_summary">Verschlüssele die Datenbank und entsperre mit einem Passwort oder Fingerabdruck.</string>
<string name="pref_fingerprint_title">Fingerabdruck</string>
<string name="pref_fingerprint_summary">Ermöglicht es, dass auf diesem Gerät registrierte Fingerabdrücke den Tresor freischalten.</string>
<string name="pref_set_password_title">Passwort ändern</string>
<string name="pref_set_password_summary">Legen ein neues Passwort fest, das erforderlich ist, um deinen Tresor freizuschalten.</string>
<string name="fingerprint_hint">Berührungssensor</string>
<string name="fingerprint_not_recognized">Fingerabdruck nicht erkannt. Versuche es noch einmal.</string>
<string name="fingerprint_success">Fingerabdruck erkannt</string>
<string name="choose_authentication_method">Sicherheit</string>
<string name="authentication_method_none">Keine</string>
<string name="authentication_method_none_description">Du brauchst kein Passwort, um den Tresor freizuschalten, und es wird nicht verschlüsselt. Diese Option wird nicht empfohlen.</string>
<string name="authentication_method_password">Passwort</string>
<string name="authentication_method_password_description">Du benötigst ein Passwort, um den Tresor freizuschalten.</string>
<string name="authentication_method_fingerprint">Fingerabdruck</string>
<string name="authentication_method_fingerprint_description">Zusätzlich zu einem Passwort können auf diesem Gerät registrierte Fingerabdrücke verwendet werden, um den Tresor freizuschalten.</string>
<string name="authentication_method_set_password">Passwort</string>
<string name="authentication_enter_password">Gib dein Passwort ein</string>
<string name="authentication">Entsperre den Tresor.</string>
@ -61,7 +53,6 @@
<string name="set_group">Bitte gib einen Gruppennamen ein</string>
<string name="set_number">Bitte gib eine Zahl ein</string>
<string name="set_password_confirm">Bitte bestätige das Passwort</string>
<string name="invalidated_fingerprint">Es wurde eine Änderung der Sicherheitseinstellungen deines Geräts festgestellt. Bitte gehe zu \"Aegis -> Einstellungen -> Fingerabdruck\" und füge deinen Fingerabdruck erneut hinzu.</string>
<string name="unlock">Entsperren</string>
<string name="advanced">Fortgeschritten</string>
@ -72,7 +63,6 @@
<string name="scan">QR-Code scannen</string>
<string name="scan_image">Bild scannen</string>
<string name="enter_manually">Manuell eingeben</string>
<string name="add_fingerprint">Fingerabdruck hinzufügen</string>
<string name="add_password">Passwort hinzufügen</string>
<string name="slots_warning">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.</string>
<string name="copy">Kopieren</string>
@ -85,7 +75,6 @@
<string name="unlock_vault_error">Der Tresor konnte nicht entsperrt werden.</string>
<string name="unlock_vault_error_description">Falsches Passwort. Achte darauf, dass du dein Passwort nicht falsch eingibst.</string>
<string name="password_equality_error">Passwörter sollten identisch und nicht leer sein.</string>
<string name="register_fingerprint">Registriere deinen Fingerabdruck</string>
<string name="snackbar_authentication_method">Bitte wähle eine Authentifizierungsmethode aus</string>
<string name="encrypting_vault">Verschlüsseln des Tresors</string>
<string name="delete_entry">Eintrag löschen</string>
@ -124,7 +113,6 @@
<string name="export_database_location">Die Datenbank wurde exportiert nach:</string>
<string name="export_warning">Diese Aktion exportiert die Datenbank aus dem privaten Speicher von Aegis.</string>
<string name="encryption_set_password_error">Beim Versuch, das Passwort festzulegen, ist ein Fehler aufgetreten: </string>
<string name="encryption_enable_fingerprint_error">Beim Versuch den Fingerabdruck zu entsperren, ist ein Fehler aufgetreten: </string>
<string name="no_cameras_available">Keine Kameras verfügbar</string>
<string name="read_qr_error">Beim Versuch, den QR-Code zu lesen, ist ein Fehler aufgetreten.</string>
<string name="authentication_method_raw">Raw</string>

View file

@ -41,22 +41,14 @@
<string name="pref_auto_lock_summary">Verrouiller automatiquement lorsque vous fermez l\'application ou verrouillez votre appareil.</string>
<string name="pref_encryption_title">Chiffrement</string>
<string name="pref_encryption_summary">Chiffrer la base de données et la déverrouiller avec un mot de passe ou une empreinte digitale</string>
<string name="pref_fingerprint_title">Empreinte digitale</string>
<string name="pref_fingerprint_summary">Autoriser les empreintes digitales enregistrées sur cet appareil à déverrouiller le coffre-fort</string>
<string name="pref_set_password_title">Changer le mot de passe</string>
<string name="pref_set_password_summary">Définir un mot de passe dont vous aurez besoin pour déverrouiller votre coffre-fort</string>
<string name="fingerprint_hint">Appuyez sur le capteur</string>
<string name="fingerprint_not_recognized">Empreinte digitale non reconnue. Réessayez.</string>
<string name="fingerprint_success">Empreinte digitale reconnue</string>
<string name="choose_authentication_method">Sécurité</string>
<string name="authentication_method_none">Aucun</string>
<string name="authentication_method_none_description">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.</string>
<string name="authentication_method_password">Mot de passe</string>
<string name="authentication_method_password_description">Vous avez besoin d\'un mot de passe pour déverrouiller le coffre-fort.</string>
<string name="authentication_method_fingerprint">Empreinte digitale</string>
<string name="authentication_method_fingerprint_description">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.</string>
<string name="authentication_method_set_password">Mot de passe</string>
<string name="authentication_enter_password">Saisissez votre mot de passe</string>
<string name="authentication">Déverrouiller le coffre-fort</string>
@ -64,7 +56,6 @@
<string name="set_group">Veuillez saisir un nom de groupe</string>
<string name="set_number">Veuillez saisir un nombre</string>
<string name="set_password_confirm">Veuillez confirmer le mot de passe</string>
<string name="invalidated_fingerprint">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.</string>
<string name="unlock">Déverrouiller</string>
<string name="advanced">Avancé</string>
@ -75,7 +66,6 @@
<string name="scan">Scanner code QR</string>
<string name="scan_image">Scanner image</string>
<string name="enter_manually">Saisir manuellement</string>
<string name="add_fingerprint">Ajouter une empreinte digitale</string>
<string name="add_password">Ajouter un mot de passe</string>
<string name="slots_warning">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.</string>
<string name="copy">Copier</string>
@ -88,7 +78,6 @@
<string name="unlock_vault_error">Impossible de déverrouiller le coffre-fort</string>
<string name="unlock_vault_error_description">Mot de passe incorrect. Assurez-vous de ne pas avoir fait de faute de frappe lors de la saisie de votre mot de passe.</string>
<string name="password_equality_error">Les mots de passe doivent être identiques et non vides</string>
<string name="register_fingerprint">Enregistrer votre empreinte digitale</string>
<string name="snackbar_authentication_method">Veuillez sélectionner une méthode d\'authentification</string>
<string name="encrypting_vault">Chiffrement du coffre-fort</string>
<string name="delete_entry">Supprimer entrée</string>
@ -127,7 +116,6 @@
<string name="export_database_location">La base de données a été exportée vers :</string>
<string name="export_warning">Cette action va exporter la base de données en dehors du stockage privé d\'Aegis.</string>
<string name="encryption_set_password_error">Une erreur est survenue en essayant de définir le mot de passe : </string>
<string name="encryption_enable_fingerprint_error">Une erreur est survenue en essayant d\'activer le déverrouillage par empreinte digitale : </string>
<string name="no_cameras_available">Aucun appareil photo disponible</string>
<string name="read_qr_error">Une erreur est survenue en essayant de lire le code QR</string>
<string name="authentication_method_raw">Brut</string>

View file

@ -36,22 +36,14 @@
<string name="pref_tap_to_reveal_time_title">Time-out voor drukken om te laten zien</string>
<string name="pref_encryption_title">Encryptie</string>
<string name="pref_encryption_summary">Encrypt de database en ontgrendel deze met een wachtwoord of vingerafdruk</string>
<string name="pref_fingerprint_title">Vingerafdruk</string>
<string name="pref_fingerprint_summary">Schakel in om met de op dit toestel geregistreerde vingerafdrukken de kluis te kunnen ontgrendelen</string>
<string name="pref_set_password_title">Wachtwoord wijzigen</string>
<string name="pref_set_password_summary">Stel een nieuw wachtwoord in waarmee je de kluis kunt ontgrendelen</string>
<string name="fingerprint_hint">Aanraaksensor</string>
<string name="fingerprint_not_recognized">Vingerafdruk niet herkend. Probeer opnieuw.</string>
<string name="fingerprint_success">Vingerafdruk herkend</string>
<string name="choose_authentication_method">Beveiliging</string>
<string name="authentication_method_none">Geen</string>
<string name="authentication_method_none_description">Je hebt geen wachtwoord nodig om de kluis te ontgrendelen en de kluis zal niet worden geëncrypt. Deze optie wordt niet aanbevolen.</string>
<string name="authentication_method_password">Wachtwoord</string>
<string name="authentication_method_password_description">Je hebt een wachtwoord nodig om de kluis te ontgrendelen.</string>
<string name="authentication_method_fingerprint">Vingerafdruk</string>
<string name="authentication_method_fingerprint_description">Als toevoeging op een wachtwoord kan er ook gebruik gemaakt worden van de op dit toestel geregistreerde vingerafdrukken om de kluis te ontgrendelen.</string>
<string name="authentication_method_set_password">Wachtwoord</string>
<string name="authentication_enter_password">Vul je wachtwoord in</string>
<string name="authentication">Kluis ontgrendelen</string>
@ -59,7 +51,6 @@
<string name="set_group">Vul een groepsnaam in</string>
<string name="set_number">Kies een getal</string>
<string name="set_password_confirm">Bevestig het wachtwoord</string>
<string name="invalidated_fingerprint">Er is een verandering in de beveiligingsinstellingen van je toestel gedetecteerd. Ga naar \"Instellingen -> Vingerafdruk\" om je vingerafdruk opnieuw toe te voegen.</string>
<string name="unlock">Ontgrendel</string>
<string name="advanced">Geavanceerd</string>
@ -69,7 +60,6 @@
<string name="secret">Sleutel</string>
<string name="scan">Scan QR-code</string>
<string name="enter_manually">Handmatig invoeren</string>
<string name="add_fingerprint">Vingerafdruk toevoegen</string>
<string name="add_password">Wachtwoord toevoegen</string>
<string name="slots_warning">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.</string>
<string name="copy">Kopiëren</string>
@ -82,7 +72,6 @@
<string name="unlock_vault_error">Kluis kon niet worden ontgrendeld</string>
<string name="unlock_vault_error_description">Wachtwoord onjuist. Controleer of je het wachtwoord correct hebt ingetypt.</string>
<string name="password_equality_error">Wachtwoorden moeten overeen komen en mogen niet leeg zijn</string>
<string name="register_fingerprint">Registreer je vingerafdruk</string>
<string name="snackbar_authentication_method">Kies een inlogmethode</string>
<string name="encrypting_vault">De kluis wordt geëncrypt</string>
<string name="delete_entry">Item verwijderen</string>
@ -117,7 +106,6 @@
<string name="export_database_location">De database is geëxporteerd naar:</string>
<string name="export_warning">Deze actie zal de database uit de privé-opslag van Aegis exporteren.</string>
<string name="encryption_set_password_error">Er is een fout opgetreden tijdens het instellen van het wachtwoord: </string>
<string name="encryption_enable_fingerprint_error">Er is een fout opgetreden tijdens het inschakelen van ontgrendeling met vingerafdruk: </string>
<string name="no_cameras_available">Geen camera\'s beschikbaar</string>
<string name="read_qr_error">Er is een fout opgetreden tijdens het lezen van de QR-code</string>
<string name="authentication_method_raw">Onbewerkt</string>

View file

@ -36,22 +36,14 @@
<string name="pref_auto_lock_summary">Автоматическая блокировка при закрытии приложения и блокировке устройства.</string>
<string name="pref_encryption_title">Шифрование</string>
<string name="pref_encryption_summary">Зашифруйте базу данных и разблокируйте ее с помощью пароля или отпечатка пальца</string>
<string name="pref_fingerprint_title">Отпечаток пальца</string>
<string name="pref_fingerprint_summary">Разрешить разблокировку хранилища при помощи отпечатков пальцев, зарегистрированных на этом устройстве</string>
<string name="pref_set_password_title">Изменить пароль</string>
<string name="pref_set_password_summary">Установите новый пароль, который вам понадобится, чтобы разблокировать ваше хранилище</string>
<string name="fingerprint_hint">Прикоснитесь к сенсору</string>
<string name="fingerprint_not_recognized">Отпечаток пальца не распознан. Попробуйте снова.</string>
<string name="fingerprint_success">Отпечаток пальца распознан</string>
<string name="choose_authentication_method">Безопасность</string>
<string name="authentication_method_none">Нет</string>
<string name="authentication_method_none_description">Вам не нужен пароль для разблокировки хранилища, и оно не будет зашифровано. Этот вариант не рекомендуется.</string>
<string name="authentication_method_password">Пароль</string>
<string name="authentication_method_password_description">Вам нужен пароль, чтобы разблокировать хранилище.</string>
<string name="authentication_method_fingerprint">Отпечаток пальца</string>
<string name="authentication_method_fingerprint_description">В дополнение к паролю, для разблокировки хранилища можно использовать отпечатки пальцев, зарегистрированные на этом устройстве.</string>
<string name="authentication_method_set_password">Пароль</string>
<string name="authentication_enter_password">Введите свой пароль</string>
<string name="authentication">Разблокировать хранилище</string>
@ -59,7 +51,6 @@
<string name="set_group">Пожалуйста, введите название группы</string>
<string name="set_number">Пожалуйста, введите номер</string>
<string name="set_password_confirm">Пожалуйста, подтвердите пароль</string>
<string name="invalidated_fingerprint">Обнаружено изменение настроек безопасности вашего устройства. Перейдите в «Настройки - Отпечаток пальца» и повторно добавьте свой отпечаток пальца.</string>
<string name="unlock">Разблокировать</string>
<string name="advanced">Расширенные</string>
@ -70,7 +61,6 @@
<string name="scan">Сканировать QR-код</string>
<string name="scan_image">Сканировать изображение</string>
<string name="enter_manually">Ввести вручную</string>
<string name="add_fingerprint">Добавить отпечаток</string>
<string name="add_password">Добавить пароль</string>
<string name="slots_warning">Хранилище надежно защищено. При добавлении нового отпечатка пальца на ваше устройство необходимо будет заново настроить разблокировку хранилища по отпечатку пальца в Aegis.</string>
<string name="copy">Копировать</string>
@ -83,7 +73,6 @@
<string name="unlock_vault_error">Не удалось разблокировать хранилище</string>
<string name="unlock_vault_error_description">Некорректный пароль. Убедитесь, что вы не допустили опечатку в вашем пароле.</string>
<string name="password_equality_error">Пароли должны быть одинаковыми и непустыми</string>
<string name="register_fingerprint">Зарегистрируйте свой отпечаток пальца</string>
<string name="snackbar_authentication_method">Пожалуйста, выберите метод аутентификации</string>
<string name="encrypting_vault">Шифрование хранилища</string>
<string name="delete_entry">Удалить запись</string>
@ -122,7 +111,6 @@
<string name="export_database_location">База данных была экспортирована в:</string>
<string name="export_warning">Это действие экспортирует базу данных из приватного хранилища Aegis.</string>
<string name="encryption_set_password_error">Произошла ошибка при попытке установить пароль:</string>
<string name="encryption_enable_fingerprint_error">Произошла ошибка при попытке разблокировать отпечаток пальца:</string>
<string name="no_cameras_available">Нет доступных камер</string>
<string name="read_qr_error">Произошла ошибка при попытке прочитать QR-код</string>
<string name="authentication_method_raw">Необработанный</string>

View file

@ -40,20 +40,13 @@
<string name="pref_auto_lock_summary">关闭应用或锁定设备时自动锁定</string>
<string name="pref_encryption_title">加密</string>
<string name="pref_encryption_summary">加密数据库并使用密码或指纹解锁数据库</string>
<string name="pref_fingerprint_title">指纹</string>
<string name="pref_fingerprint_summary">允许在此设备上注册的指纹解锁数据库</string>
<string name="pref_set_password_title">更改密码</string>
<string name="pref_set_password_summary">设置解锁数据库所需的新密码</string>
<string name="fingerprint_hint">触摸感应器</string>
<string name="fingerprint_not_recognized">无法识别指纹。再试一次。</string>
<string name="fingerprint_success">识别指纹</string>
<string name="choose_authentication_method">安全</string>
<string name="authentication_method_none">未设置</string>
<string name="authentication_method_none_description">您无需密码即可解锁保管库,也不会对其进行加密。不建议使用此选项。</string>
<string name="authentication_method_password">密码</string>
<string name="authentication_method_password_description">您需要密码来解锁数据库。</string>
<string name="authentication_method_fingerprint">指纹</string>
<string name="authentication_method_fingerprint_description">除了密码之外,在此设备上注册的指纹也可用于解锁数据库。</string>
<string name="authentication_method_set_password">密码</string>
<string name="authentication_enter_password">输入您的密码</string>
<string name="authentication">打开数据库</string>
@ -61,7 +54,6 @@
<string name="set_group">请输入组名</string>
<string name="set_number">请输入号码</string>
<string name="set_password_confirm">请确认密码</string>
<string name="invalidated_fingerprint">检测到设备安全设置更改。请到“Aegis- &gt;设置- &gt;指纹”,重新添加您的指纹。</string>
<string name="unlock">解锁</string>
<string name="advanced">高级设置</string>
<string name="seconds"></string>
@ -71,7 +63,6 @@
<string name="scan">扫描二维码</string>
<string name="scan_image">扫描图像</string>
<string name="enter_manually">手动输入</string>
<string name="add_fingerprint">添加指纹</string>
<string name="add_password">添加密码</string>
<string name="slots_warning">数据库与您最薄弱的秘密一样安全。向设备添加新指纹时,您需要在 Aegis 中重新激活指纹身份验证。</string>
<string name="copy">复制</string>
@ -84,7 +75,6 @@
<string name="unlock_vault_error">无法解锁数据库</string>
<string name="unlock_vault_error_description">密码错误。确保您未输错密码。</string>
<string name="password_equality_error">密码应该是相同且非空的。</string>
<string name="register_fingerprint">注册您的指纹</string>
<string name="snackbar_authentication_method">请选择身份验证方法</string>
<string name="encrypting_vault">加密数据库</string>
<string name="delete_entry">删除条目</string>
@ -123,7 +113,6 @@
<string name="export_database_location">数据库已经导出至:</string>
<string name="export_warning">此操作将导出 Aegis 专用存储的数据库。</string>
<string name="encryption_set_password_error">试图设置密码时出错:</string>
<string name="encryption_enable_fingerprint_error">试图启用指纹解锁时出错:</string>
<string name="no_cameras_available">未找到可用的相机</string>
<string name="read_qr_error">试图读取二维码时出错</string>
<string name="authentication_method_raw">源文件</string>

View file

@ -3,7 +3,7 @@
<string-array name="authentication_methods">
<item>@string/authentication_method_none</item>
<item>@string/authentication_method_password</item>
<item>@string/authentication_method_fingerprint</item>
<item>@string/authentication_method_biometrics</item>
</string-array>
<string-array name="otp_types_array">

View file

@ -19,11 +19,6 @@
<color name="card_background">#ffffff</color>
<color name="card_background_focused">#DBDBDB</color>
<color name="colorSwirlPrimary">#434343</color>
<color name="colorSwirlError">#FF5252</color>
<color name="colorSwirlPrimaryDark">#FFFFFF</color>
<color name="colorSwirlErrorDark">#FF5252</color>
<color name="icon_primary">#757575</color>
<color name="icon_primary_inverted">#ffffff</color>
<color name="icon_primary_dark">#ffffff</color>

View file

@ -40,23 +40,19 @@
<string name="pref_auto_lock_title">Auto lock</string>
<string name="pref_auto_lock_summary">Automatically lock when you close the app or lock your device.</string>
<string name="pref_encryption_title">Encryption</string>
<string name="pref_encryption_summary">Encrypt the database and unlock it with a password or fingerprint</string>
<string name="pref_fingerprint_title">Fingerprint</string>
<string name="pref_fingerprint_summary">Allow fingerprints registered on this device to unlock the vault</string>
<string name="pref_encryption_summary">Encrypt the database and unlock it with a password or biometrics</string>
<string name="pref_biometrics_title">Biometric unlock</string>
<string name="pref_biometrics_summary">Allow biometric authentication to unlock the vault</string>
<string name="pref_set_password_title">Change password</string>
<string name="pref_set_password_summary">Set a new password which you will need to unlock your vault</string>
<string name="fingerprint_hint">Touch sensor</string>
<string name="fingerprint_not_recognized">Fingerprint not recognized. Try again.</string>
<string name="fingerprint_success">Fingerprint recognized</string>
<string name="choose_authentication_method">Security</string>
<string name="authentication_method_none">None</string>
<string name="authentication_method_none_description">You don\'t need a password to unlock the vault and it will not be encrypted. This option is not recommended.</string>
<string name="authentication_method_password">Password</string>
<string name="authentication_method_password_description">You need a password to unlock the vault.</string>
<string name="authentication_method_fingerprint">Fingerprint</string>
<string name="authentication_method_fingerprint_description">In addition to a password, fingerprints registered on this device can be used to unlock the vault.</string>
<string name="authentication_method_biometrics">Biometrics</string>
<string name="authentication_method_biometrics_description">In addition to a password, biometrics registered on this device, like a fingerprint or your face, can be used to unlock the vault.</string>
<string name="authentication_method_set_password">Password</string>
<string name="authentication_enter_password">Enter your password</string>
<string name="authentication">Unlock the vault</string>
@ -64,9 +60,10 @@
<string name="set_group">Please enter a group name</string>
<string name="set_number">Please enter a number</string>
<string name="set_password_confirm">Please confirm the password</string>
<string name="invalidated_fingerprint">A change in your device\'s security settings has been detected. Please go to \"Aegis -> Settings -> Fingerprint\" and re-add your fingerprint.</string>
<string name="invalidated_biometrics">A change in your device\'s security settings has been detected. Please go to \"Aegis -> Settings -> Biometrics\" and re-enable biometric unlock.</string>
<string name="unlock">Unlock</string>
<string name="biometrics">Biometrics</string>
<string name="advanced">Advanced</string>
<string name="seconds">seconds</string>
<string name="counter">Counter</string>
@ -75,9 +72,11 @@
<string name="scan">Scan QR code</string>
<string name="scan_image">Scan image</string>
<string name="enter_manually">Enter manually</string>
<string name="add_fingerprint">Add fingerprint</string>
<string name="add_biometric">Add biometric</string>
<string name="add_biometric_slot">Add biometric slot</string>
<string name="set_up_biometric">Set up biometric unlock</string>
<string name="add_password">Add password</string>
<string name="slots_warning">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.</string>
<string name="slots_warning">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.</string>
<string name="copy">Copy</string>
<string name="edit">Edit</string>
<string name="delete">Delete</string>
@ -89,7 +88,6 @@
<string name="unlock_vault_error">Couldn\'t unlock vault</string>
<string name="unlock_vault_error_description">Incorrect password. Make sure you didn\'t mistype your password.</string>
<string name="password_equality_error">Passwords should be identical and non-empty</string>
<string name="register_fingerprint">Register your fingerprint</string>
<string name="snackbar_authentication_method">Please select an authentication method</string>
<string name="encrypting_vault">Encrypting the vault</string>
<string name="delete_entry">Delete entry</string>
@ -132,7 +130,7 @@
<string name="export_database_location">The database has been exported to:</string>
<string name="export_warning">This action will export the database out of Aegis\' private storage.</string>
<string name="encryption_set_password_error">An error occurred while trying to set the password: </string>
<string name="encryption_enable_fingerprint_error">An error occurred while trying to enable fingerprint unlock: </string>
<string name="encryption_enable_biometrics_error">An error occurred while trying to enable biometric unlock</string>
<string name="no_cameras_available">No cameras available</string>
<string name="read_qr_error">An error occurred while trying to read the QR code</string>
<string name="authentication_method_raw">Raw</string>

View file

@ -11,8 +11,6 @@
</style>
<style name="Theme.Intro" parent="Theme.AppCompat.NoActionBar">
<item name="swirl_ridgeColor">@color/primary_text_inverted</item>
<item name="swirl_errorColor">@color/colorSwirlError</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
</style>
@ -35,9 +33,6 @@
<item name="iconColorPrimary">@color/icon_primary</item>
<item name="iconColorInverted">@color/icon_primary_inverted</item>
<item name="swirl_ridgeColor">@color/colorSwirlPrimary</item>
<item name="swirl_errorColor">@color/colorSwirlError</item>
<item name="actionModeStyle">@style/ActionModeStyle</item>
<item name="actionBarTheme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item>
<item name="alertDialogTheme">@style/DialogStyle</item>
@ -104,9 +99,6 @@
<item name="iconColorPrimary">@color/icon_primary_dark</item>
<item name="iconColorInverted">@color/icon_primary_dark_inverted</item>
<item name="swirl_ridgeColor">@color/colorSwirlPrimaryDark</item>
<item name="swirl_errorColor">@color/colorSwirlErrorDark</item>
<item name="actionModeStyle">@style/ActionModeStyle.Dark</item>
<item name="alertDialogTheme">@style/DialogStyle.Dark</item>

View file

@ -103,9 +103,9 @@
app:iconSpaceReserved="false"/>
<com.beemdevelopment.aegis.ui.preferences.SwitchPreference
android:key="pref_fingerprint"
android:title="@string/pref_fingerprint_title"
android:summary="@string/pref_fingerprint_summary"
android:key="pref_biometrics"
android:title="@string/pref_biometrics_title"
android:summary="@string/pref_biometrics_summary"
android:persistent="false"
app:iconSpaceReserved="false"/>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -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.

View file

@ -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,
its impossible for them to retrieve the contents without knowing the password.
<b>Fingerprint unlock</b>
<b>Biometric unlock</b>
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.
<b>Compatibility</b>
Aegis supports the HOTP and TOTP algorithms. These two algorithms are