Add fingerprint support

This commit is contained in:
Alexander Bakker 2017-08-13 23:38:38 +02:00
parent 1deb8910b0
commit bfe7a1bde0
9 changed files with 321 additions and 95 deletions

View file

@ -1,38 +1,84 @@
package me.impy.aegis; package me.impy.aegis;
import android.Manifest;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.TextView;
import java.lang.reflect.UndeclaredThrowableException; import java.lang.reflect.UndeclaredThrowableException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import me.impy.aegis.crypto.CryptoUtils; import me.impy.aegis.crypto.CryptoUtils;
import me.impy.aegis.crypto.KeyStoreHandle;
import me.impy.aegis.crypto.MasterKey; import me.impy.aegis.crypto.MasterKey;
import me.impy.aegis.crypto.slots.FingerprintSlot;
import me.impy.aegis.crypto.slots.PasswordSlot; import me.impy.aegis.crypto.slots.PasswordSlot;
import me.impy.aegis.crypto.slots.Slot; import me.impy.aegis.crypto.slots.Slot;
import me.impy.aegis.crypto.slots.SlotCollection; import me.impy.aegis.crypto.slots.SlotCollection;
import me.impy.aegis.finger.FingerprintUiHelper;
import me.impy.aegis.helpers.AuthHelper;
public class AuthActivity extends AppCompatActivity { public class AuthActivity extends AppCompatActivity implements FingerprintUiHelper.Callback {
public static final int RESULT_OK = 0; public static final int RESULT_OK = 0;
public static final int RESULT_EXCEPTION = 1; public static final int RESULT_EXCEPTION = 1;
private EditText textPassword; private EditText textPassword;
private SlotCollection slots;
private LinearLayout boxFingerprint;
private ImageView imgFingerprint;
private TextView textFingerprint;
private FingerprintUiHelper fingerHelper;
private Cipher fingerCipher;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_auth); setContentView(R.layout.activity_auth);
textPassword = (EditText) findViewById(R.id.text_password); textPassword = (EditText) findViewById(R.id.text_password);
boxFingerprint = (LinearLayout) findViewById(R.id.box_fingerprint);
imgFingerprint = (ImageView) findViewById(R.id.img_fingerprint);
textFingerprint = (TextView) findViewById(R.id.text_fingerprint);
Intent intent = getIntent(); Intent intent = getIntent();
final SlotCollection slots = (SlotCollection) intent.getSerializableExtra("slots"); slots = (SlotCollection) intent.getSerializableExtra("slots");
// 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
Context context = getApplicationContext();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
FingerprintManager manager = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) == PackageManager.PERMISSION_GRANTED && manager.isHardwareDetected()) {
if (slots.has(FingerprintSlot.class)) {
try {
KeyStoreHandle handle = new KeyStoreHandle();
if (handle.keyExists()) {
SecretKey key = handle.getKey();
fingerCipher = Slot.createCipher(key, Cipher.DECRYPT_MODE);
fingerHelper = new FingerprintUiHelper(manager, imgFingerprint, textFingerprint, this);
boxFingerprint.setVisibility(View.VISIBLE);
}
} catch (Exception e) {
throw new UndeclaredThrowableException(e);
}
}
}
}
Button button = (Button) findViewById(R.id.button_decrypt); Button button = (Button) findViewById(R.id.button_decrypt);
button.setOnClickListener(new View.OnClickListener() { button.setOnClickListener(new View.OnClickListener() {
@ -42,11 +88,13 @@ public class AuthActivity extends AppCompatActivity {
try { try {
if (slots.has(PasswordSlot.class)) { if (slots.has(PasswordSlot.class)) {
PasswordSlot slot = slots.find(PasswordSlot.class); PasswordSlot slot = slots.find(PasswordSlot.class);
char[] password = getPassword(true); char[] password = AuthHelper.getPassword(textPassword, true);
SecretKey key = slot.deriveKey(password); SecretKey key = slot.deriveKey(password);
CryptoUtils.zero(password); CryptoUtils.zero(password);
Cipher cipher = Slot.createCipher(key, Cipher.DECRYPT_MODE); Cipher cipher = Slot.createCipher(key, Cipher.DECRYPT_MODE);
masterKey = MasterKey.decryptSlot(slot, cipher); masterKey = MasterKey.decryptSlot(slot, cipher);
} else {
throw new RuntimeException();
} }
} catch (Exception e) { } catch (Exception e) {
// TODO: feedback // TODO: feedback
@ -54,31 +102,57 @@ public class AuthActivity extends AppCompatActivity {
} }
// send the master key back to the main activity // send the master key back to the main activity
Intent result = new Intent(); setKey(masterKey);
result.putExtra("key", masterKey);
setResult(RESULT_OK, result);
finish();
} }
}); });
} }
private void setKey(MasterKey key) {
// send the master key back to the main activity
Intent result = new Intent();
result.putExtra("key", key);
setResult(RESULT_OK, result);
finish();
}
@Override @Override
public void onBackPressed() { public void onBackPressed() {
// ignore back button presses // ignore back button presses
} }
private char[] getPassword(boolean clear) { @Override
char[] password = getEditTextChars(textPassword); public void onResume() {
if (clear) { super.onResume();
textPassword.getText().clear();
if (fingerHelper != null) {
fingerHelper.startListening(new FingerprintManager.CryptoObject(fingerCipher));
} }
return password;
} }
private static char[] getEditTextChars(EditText text) { @Override
Editable editable = text.getText(); public void onPause() {
char[] chars = new char[editable.length()]; super.onPause();
editable.getChars(0, editable.length(), chars, 0);
return chars; if (fingerHelper != null) {
fingerHelper.stopListening();
}
}
@Override
public void onAuthenticated() {
MasterKey key;
FingerprintSlot slot = slots.find(FingerprintSlot.class);
try {
key = new MasterKey(slot.getKey(fingerCipher));
} catch (Exception e) {
throw new UndeclaredThrowableException(e);
}
setKey(key);
}
@Override
public void onError() {
} }
} }

View file

@ -1,42 +1,57 @@
package me.impy.aegis; package me.impy.aegis;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle; import android.os.Bundle;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.text.Editable;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.github.paolorotolo.appintro.ISlidePolicy; import com.github.paolorotolo.appintro.ISlidePolicy;
import com.github.paolorotolo.appintro.ISlideSelectionListener; import com.github.paolorotolo.appintro.ISlideSelectionListener;
import java.security.InvalidKeyException; import java.lang.reflect.UndeclaredThrowableException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import me.impy.aegis.crypto.CryptoUtils; import me.impy.aegis.crypto.CryptoUtils;
import me.impy.aegis.crypto.KeyStoreHandle;
import me.impy.aegis.crypto.slots.FingerprintSlot;
import me.impy.aegis.crypto.slots.PasswordSlot; import me.impy.aegis.crypto.slots.PasswordSlot;
import me.impy.aegis.crypto.slots.Slot; import me.impy.aegis.crypto.slots.Slot;
import me.impy.aegis.finger.FingerprintUiHelper;
import me.impy.aegis.helpers.AuthHelper;
public class CustomAuthenticatedSlide extends Fragment implements ISlidePolicy, ISlideSelectionListener { public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiHelper.Callback, ISlidePolicy, ISlideSelectionListener {
private int cryptType; private int cryptType;
private EditText textPassword; private EditText textPassword;
private EditText textPasswordConfirm; private EditText textPasswordConfirm;
private int bgColor; private int bgColor;
private LinearLayout boxFingerprint;
private ImageView imgFingerprint;
private TextView textFingerprint;
private FingerprintUiHelper fingerHelper;
private KeyStoreHandle storeHandle;
private Cipher fingerCipher;
private boolean fingerAuthenticated;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_authenticated_slide, container, false); final View view = inflater.inflate(R.layout.fragment_authenticated_slide, container, false);
textPassword = (EditText) view.findViewById(R.id.text_password); textPassword = (EditText) view.findViewById(R.id.text_password);
textPasswordConfirm = (EditText) view.findViewById(R.id.text_password_confirm); textPasswordConfirm = (EditText) view.findViewById(R.id.text_password_confirm);
boxFingerprint = (LinearLayout) view.findViewById(R.id.box_fingerprint);
imgFingerprint = (ImageView) view.findViewById(R.id.img_fingerprint);
textFingerprint = (TextView) view.findViewById(R.id.text_fingerprint);
view.findViewById(R.id.main).setBackgroundColor(bgColor); view.findViewById(R.id.main).setBackgroundColor(bgColor);
return view; return view;
} }
@ -50,45 +65,16 @@ public class CustomAuthenticatedSlide extends Fragment implements ISlidePolicy,
return cryptType; return cryptType;
} }
public Cipher getCipher(PasswordSlot slot, int mode) public Cipher getCipher(Slot slot) throws Exception {
throws InvalidKeySpecException, NoSuchAlgorithmException, if (slot instanceof PasswordSlot) {
InvalidKeyException, NoSuchPaddingException { char[] password = AuthHelper.getPassword(textPassword, true);
char[] password = getPassword(true);
byte[] salt = CryptoUtils.generateSalt(); byte[] salt = CryptoUtils.generateSalt();
SecretKey key = slot.deriveKey(password, salt, CryptoUtils.CRYPTO_SCRYPT_N, CryptoUtils.CRYPTO_SCRYPT_r, CryptoUtils.CRYPTO_SCRYPT_p); SecretKey key = ((PasswordSlot)slot).deriveKey(password, salt, CryptoUtils.CRYPTO_SCRYPT_N, CryptoUtils.CRYPTO_SCRYPT_r, CryptoUtils.CRYPTO_SCRYPT_p);
CryptoUtils.zero(password); CryptoUtils.zero(password);
return Slot.createCipher(key, Cipher.ENCRYPT_MODE);
return Slot.createCipher(key, mode); } else if (slot instanceof FingerprintSlot) {
} return fingerCipher;
} else {
private char[] getPassword(boolean clear) {
char[] password = getEditTextChars(textPassword);
if (clear) {
textPassword.getText().clear();
}
return password;
}
private static char[] getEditTextChars(EditText text) {
Editable editable = text.getText();
char[] chars = new char[editable.length()];
editable.getChars(0, editable.length(), chars, 0);
return chars;
}
@Override
public void onSlideSelected() {
Intent intent = getActivity().getIntent();
cryptType = intent.getIntExtra("cryptType", 1337);
switch(cryptType) {
case CustomAuthenticationSlide.CRYPT_TYPE_NONE:
break;
case CustomAuthenticationSlide.CRYPT_TYPE_PASS:
break;
case CustomAuthenticationSlide.CRYPT_TYPE_FINGER:
break;
default:
throw new RuntimeException(); throw new RuntimeException();
} }
} }
@ -97,8 +83,57 @@ public class CustomAuthenticatedSlide extends Fragment implements ISlidePolicy,
bgColor = color; bgColor = color;
} }
@Override
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();
}
// TODO: consider regenerating the key if it exists
if (!storeHandle.keyExists()) {
key = storeHandle.generateKey(true);
} else {
key = storeHandle.getKey();
}
} catch (Exception e) {
throw new UndeclaredThrowableException(e);
}
if (fingerHelper == null) {
FingerprintManager fingerManager = (FingerprintManager) getContext().getSystemService(Context.FINGERPRINT_SERVICE);
fingerHelper = new FingerprintUiHelper(fingerManager, imgFingerprint, textFingerprint, this);
}
try {
fingerCipher = Slot.createCipher(key, Cipher.ENCRYPT_MODE);
} catch (Exception e) {
throw new UndeclaredThrowableException(e);
}
fingerHelper.startListening(new FingerprintManager.CryptoObject(fingerCipher));
break;
default:
throw new RuntimeException();
}
}
@Override @Override
public void onSlideDeselected() { public void onSlideDeselected() {
if (fingerHelper != null) {
fingerAuthenticated = false;
boxFingerprint.setVisibility(View.INVISIBLE);
fingerHelper.stopListening();
}
} }
@Override @Override
@ -106,15 +141,13 @@ public class CustomAuthenticatedSlide extends Fragment implements ISlidePolicy,
switch(cryptType) { switch(cryptType) {
case CustomAuthenticationSlide.CRYPT_TYPE_NONE: case CustomAuthenticationSlide.CRYPT_TYPE_NONE:
return true; return true;
case CustomAuthenticationSlide.CRYPT_TYPE_PASS:
char[] password = getEditTextChars(textPassword);
char[] passwordConfirm = getEditTextChars(textPasswordConfirm);
boolean equal = password.length != 0 && Arrays.equals(password, passwordConfirm);
CryptoUtils.zero(password);
CryptoUtils.zero(passwordConfirm);
return equal;
case CustomAuthenticationSlide.CRYPT_TYPE_FINGER: case CustomAuthenticationSlide.CRYPT_TYPE_FINGER:
if (!fingerAuthenticated) {
return false; return false;
}
// intentional fallthrough
case CustomAuthenticationSlide.CRYPT_TYPE_PASS:
return AuthHelper.arePasswordsEqual(textPassword, textPasswordConfirm);
default: default:
throw new RuntimeException(); throw new RuntimeException();
} }
@ -122,10 +155,29 @@ public class CustomAuthenticatedSlide extends Fragment implements ISlidePolicy,
@Override @Override
public void onUserIllegallyRequestedNextPage() { public void onUserIllegallyRequestedNextPage() {
String message;
if (!AuthHelper.arePasswordsEqual(textPassword, textPasswordConfirm)) {
message = "Passwords should be equal and non-empty";
} else if (!fingerAuthenticated) {
message = "Register your fingerprint";
} else {
return;
}
View view = getView(); View view = getView();
if (view != null) { if (view != null) {
Snackbar snackbar = Snackbar.make(getView(), "Passwords should be equal and non-empty", Snackbar.LENGTH_LONG); Snackbar snackbar = Snackbar.make(getView(), message, Snackbar.LENGTH_LONG);
snackbar.show(); snackbar.show();
} }
} }
@Override
public void onAuthenticated() {
fingerAuthenticated = true;
}
@Override
public void onError() {
}
} }

View file

@ -13,11 +13,9 @@ import android.support.v4.app.Fragment;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.RadioButton; import android.widget.RadioButton;
import android.widget.RadioGroup; import android.widget.RadioGroup;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import com.github.paolorotolo.appintro.ISlidePolicy; import com.github.paolorotolo.appintro.ISlidePolicy;
@ -53,9 +51,6 @@ public class CustomAuthenticationSlide extends Fragment implements ISlidePolicy
break; break;
case R.id.rb_fingerprint: case R.id.rb_fingerprint:
id = CRYPT_TYPE_FINGER; id = CRYPT_TYPE_FINGER;
// TODO: remove this
group.clearCheck();
Toast.makeText(context, "Fingerprint is not supported yet", Toast.LENGTH_SHORT).show();
break; break;
default: default:
throw new RuntimeException(); throw new RuntimeException();

View file

@ -15,6 +15,7 @@ import javax.crypto.Cipher;
import me.impy.aegis.crypto.CryptResult; import me.impy.aegis.crypto.CryptResult;
import me.impy.aegis.crypto.MasterKey; import me.impy.aegis.crypto.MasterKey;
import me.impy.aegis.crypto.slots.FingerprintSlot;
import me.impy.aegis.crypto.slots.PasswordSlot; import me.impy.aegis.crypto.slots.PasswordSlot;
import me.impy.aegis.crypto.slots.SlotCollection; import me.impy.aegis.crypto.slots.SlotCollection;
import me.impy.aegis.db.Database; import me.impy.aegis.db.Database;
@ -107,13 +108,13 @@ public class IntroActivity extends AppIntro {
} }
} }
SlotCollection slots = databaseFile.getSlots();
if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_NONE) { if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
try { try {
// encrypt the master key with a key derived from the user's password // encrypt the master key with a key derived from the user's password
// and add it to the list of slots // and add it to the list of slots
SlotCollection slots = databaseFile.getSlots();
PasswordSlot slot = new PasswordSlot(); PasswordSlot slot = new PasswordSlot();
Cipher cipher = authenticatedSlide.getCipher(slot, Cipher.ENCRYPT_MODE); Cipher cipher = authenticatedSlide.getCipher(slot);
masterKey.encryptSlot(slot, cipher); masterKey.encryptSlot(slot, cipher);
slots.add(slot); slots.add(slot);
} catch (Exception e) { } catch (Exception e) {
@ -122,8 +123,18 @@ public class IntroActivity extends AppIntro {
} }
} }
if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_FINGER) { if (cryptType == CustomAuthenticationSlide.CRYPT_TYPE_FINGER) {
// TODO try {
// encrypt the master key with the fingerprint key
// and add it to the list of slots
FingerprintSlot slot = new FingerprintSlot();
Cipher cipher = authenticatedSlide.getCipher(slot);
masterKey.encryptSlot(slot, cipher);
slots.add(slot);
} catch (Exception e) {
setException(e);
return;
}
} }
// finally, save the database // finally, save the database

View file

@ -25,7 +25,7 @@ public abstract class Slot implements Serializable {
// getKey decrypts the encrypted master key in this slot with the given key and returns it. // getKey decrypts the encrypted master key in this slot with the given key and returns it.
public SecretKey getKey(Cipher cipher) throws BadPaddingException, IllegalBlockSizeException { public SecretKey getKey(Cipher cipher) throws BadPaddingException, IllegalBlockSizeException {
byte[] decryptedKeyBytes = cipher.doFinal(_encryptedMasterKey); byte[] decryptedKeyBytes = cipher.doFinal(_encryptedMasterKey);
SecretKey decryptedKey = new SecretKeySpec(decryptedKeyBytes, CryptoUtils.CRYPTO_CIPHER_RAW); SecretKey decryptedKey = new SecretKeySpec(decryptedKeyBytes, CryptoUtils.CRYPTO_CIPHER_AEAD);
CryptoUtils.zero(decryptedKeyBytes); CryptoUtils.zero(decryptedKeyBytes);
return decryptedKey; return decryptedKey;
} }

View file

@ -1,4 +1,5 @@
package me.impy.aegis.finger; // 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 * Copyright (C) 2015 The Android Open Source Project
@ -16,6 +17,8 @@ package me.impy.aegis.finger;
* limitations under the License * limitations under the License
*/ */
package me.impy.aegis.finger;
import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager;
import android.os.Build; import android.os.Build;
import android.os.CancellationSignal; import android.os.CancellationSignal;
@ -109,8 +112,6 @@ public class FingerprintUiHelper extends FingerprintManager.AuthenticationCallba
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
mErrorTextView.removeCallbacks(mResetErrorTextRunnable); mErrorTextView.removeCallbacks(mResetErrorTextRunnable);
mIcon.setImageResource(R.drawable.ic_fingerprint_success); mIcon.setImageResource(R.drawable.ic_fingerprint_success);
mErrorTextView.setTextColor(
mErrorTextView.getResources().getColor(R.color.success_color, null));
mErrorTextView.setText( mErrorTextView.setText(
mErrorTextView.getResources().getString(R.string.fingerprint_success)); mErrorTextView.getResources().getString(R.string.fingerprint_success));
mIcon.postDelayed(new Runnable() { mIcon.postDelayed(new Runnable() {
@ -124,8 +125,6 @@ public class FingerprintUiHelper extends FingerprintManager.AuthenticationCallba
private void showError(CharSequence error) { private void showError(CharSequence error) {
mIcon.setImageResource(R.drawable.ic_fingerprint_error); mIcon.setImageResource(R.drawable.ic_fingerprint_error);
mErrorTextView.setText(error); mErrorTextView.setText(error);
mErrorTextView.setTextColor(
mErrorTextView.getResources().getColor(R.color.warning_color, null));
mErrorTextView.removeCallbacks(mResetErrorTextRunnable); mErrorTextView.removeCallbacks(mResetErrorTextRunnable);
mErrorTextView.postDelayed(mResetErrorTextRunnable, ERROR_TIMEOUT_MILLIS); mErrorTextView.postDelayed(mResetErrorTextRunnable, ERROR_TIMEOUT_MILLIS);
} }
@ -133,8 +132,6 @@ public class FingerprintUiHelper extends FingerprintManager.AuthenticationCallba
private Runnable mResetErrorTextRunnable = new Runnable() { private Runnable mResetErrorTextRunnable = new Runnable() {
@Override @Override
public void run() { public void run() {
mErrorTextView.setTextColor(
mErrorTextView.getResources().getColor(R.color.hint_color, null));
mErrorTextView.setText( mErrorTextView.setText(
mErrorTextView.getResources().getString(R.string.fingerprint_hint)); mErrorTextView.getResources().getString(R.string.fingerprint_hint));
mIcon.setImageResource(R.drawable.ic_fp_40px); mIcon.setImageResource(R.drawable.ic_fp_40px);

View file

@ -0,0 +1,38 @@
package me.impy.aegis.helpers;
import android.text.Editable;
import android.widget.EditText;
import java.util.Arrays;
import me.impy.aegis.crypto.CryptoUtils;
public class AuthHelper {
private AuthHelper() {
}
public static char[] getPassword(EditText text, boolean clear) {
char[] password = getEditTextChars(text);
if (clear) {
text.getText().clear();
}
return password;
}
public static char[] getEditTextChars(EditText text) {
Editable editable = text.getText();
char[] chars = new char[editable.length()];
editable.getChars(0, editable.length(), chars, 0);
return chars;
}
public static boolean arePasswordsEqual(EditText text1, EditText text2) {
char[] password = getEditTextChars(text1);
char[] passwordConfirm = getEditTextChars(text2);
boolean equal = password.length != 0 && Arrays.equals(password, passwordConfirm);
CryptoUtils.zero(password);
CryptoUtils.zero(passwordConfirm);
return equal;
}
}

View file

@ -8,8 +8,8 @@
tools:context="me.impy.aegis.AuthActivity"> tools:context="me.impy.aegis.AuthActivity">
<LinearLayout <LinearLayout
android:orientation="vertical" android:orientation="vertical"
android:layout_width="344dp" android:layout_width="match_parent"
android:layout_height="495dp" android:layout_height="wrap_content"
android:layout_margin="32dp" android:layout_margin="32dp"
tools:layout_editor_absoluteY="8dp" tools:layout_editor_absoluteY="8dp"
tools:layout_editor_absoluteX="8dp"> tools:layout_editor_absoluteX="8dp">
@ -40,5 +40,26 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Decrypt" /> android:text="Decrypt" />
</LinearLayout> </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="invisible">
<ImageView
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_gravity="center_vertical"
android:layout_marginLeft="15dp"
android:text="Touch sensor"/>
</LinearLayout>
</LinearLayout> </LinearLayout>
</android.support.design.widget.CoordinatorLayout> </android.support.design.widget.CoordinatorLayout>

View file

@ -17,7 +17,7 @@
<LinearLayout <LinearLayout
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:layout_marginTop="12dp"> android:layout_marginTop="12dp">
<TextView <TextView
@ -61,4 +61,42 @@
</LinearLayout> </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="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">
<ImageView
android:id="@+id/img_fingerprint"
android:layout_width="60dp"
android:layout_height="60dp" />
<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="Touch sensor"/>
</LinearLayout>
</LinearLayout>
</LinearLayout> </LinearLayout>