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

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