mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-05-14 22:12:55 +00:00
Add fingerprint support
This commit is contained in:
parent
1deb8910b0
commit
bfe7a1bde0
9 changed files with 321 additions and 95 deletions
|
@ -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() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,46 +65,17 @@ 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 = ((PasswordSlot)slot).deriveKey(password, salt, CryptoUtils.CRYPTO_SCRYPT_N, CryptoUtils.CRYPTO_SCRYPT_r, CryptoUtils.CRYPTO_SCRYPT_p);
|
||||||
SecretKey key = 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);
|
||||||
|
} else if (slot instanceof FingerprintSlot) {
|
||||||
return Slot.createCipher(key, mode);
|
return fingerCipher;
|
||||||
}
|
} else {
|
||||||
|
throw new RuntimeException();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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:
|
||||||
return false;
|
if (!fingerAuthenticated) {
|
||||||
|
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() {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
@ -147,4 +144,4 @@ public class FingerprintUiHelper extends FingerprintManager.AuthenticationCallba
|
||||||
|
|
||||||
void onError();
|
void onError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
38
app/src/main/java/me/impy/aegis/helpers/AuthHelper.java
Normal file
38
app/src/main/java/me/impy/aegis/helpers/AuthHelper.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue