Skip to content

Commit

Permalink
Implement fingerprint login method (#244)
Browse files Browse the repository at this point in the history
* Implement fingerprint login method

* Display a security warning

* Verify the identity before sensitive operation
  • Loading branch information
0140454 authored and m2049r committed Apr 24, 2018
1 parent 1115bbb commit a2d6ca0
Show file tree
Hide file tree
Showing 18 changed files with 588 additions and 134 deletions.
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />

<application
android:allowBackup="true"
Expand Down
49 changes: 45 additions & 4 deletions app/src/main/java/com/m2049r/xmrwallet/GenerateFragment.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@

package com.m2049r.xmrwallet;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TextInputLayout;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.Html;
import android.text.InputType;
import android.text.TextWatcher;
import android.view.KeyEvent;
Expand All @@ -32,21 +35,23 @@
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.TextView;

import com.m2049r.xmrwallet.util.KeyStoreHelper;
import com.m2049r.xmrwallet.util.RestoreHeight;
import com.m2049r.xmrwallet.widget.Toolbar;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.FingerprintHelper;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.KeyStoreHelper;
import com.m2049r.xmrwallet.util.RestoreHeight;
import com.m2049r.xmrwallet.widget.Toolbar;
import com.nulabinc.zxcvbn.Strength;
import com.nulabinc.zxcvbn.Zxcvbn;

import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;

import timber.log.Timber;

Expand All @@ -60,6 +65,7 @@ public class GenerateFragment extends Fragment {

private TextInputLayout etWalletName;
private TextInputLayout etWalletPassword;
private LinearLayout llFingerprintAuth;
private TextInputLayout etWalletAddress;
private TextInputLayout etWalletMnemonic;
private TextInputLayout etWalletViewKey;
Expand All @@ -80,6 +86,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,

etWalletName = (TextInputLayout) view.findViewById(R.id.etWalletName);
etWalletPassword = (TextInputLayout) view.findViewById(R.id.etWalletPassword);
llFingerprintAuth = (LinearLayout) view.findViewById(R.id.llFingerprintAuth);
etWalletMnemonic = (TextInputLayout) view.findViewById(R.id.etWalletMnemonic);
etWalletAddress = (TextInputLayout) view.findViewById(R.id.etWalletAddress);
etWalletViewKey = (TextInputLayout) view.findViewById(R.id.etWalletViewKey);
Expand Down Expand Up @@ -147,6 +154,30 @@ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
}
});

if (FingerprintHelper.isDeviceSupported(getContext())) {
llFingerprintAuth.setVisibility(View.VISIBLE);

final Switch swFingerprintAllowed = (Switch) llFingerprintAuth.getChildAt(0);
swFingerprintAllowed.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (!swFingerprintAllowed.isChecked()) return;

AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(Html.fromHtml(getString(R.string.generate_fingerprint_warn)))
.setCancelable(false)
.setPositiveButton(getString(R.string.label_ok), null)
.setNegativeButton(getString(R.string.label_cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
swFingerprintAllowed.setChecked(false);
}
})
.show();
}
});
}

if (type.equals(TYPE_NEW)) {
etWalletPassword.getEditText().setImeOptions(EditorInfo.IME_ACTION_DONE);
etWalletPassword.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
Expand Down Expand Up @@ -424,6 +455,7 @@ private void generateWallet() {

String name = etWalletName.getEditText().getText().toString();
String password = etWalletPassword.getEditText().getText().toString();
boolean fingerprintAuthAllowed = ((Switch) llFingerprintAuth.getChildAt(0)).isChecked();

// create the real wallet password
String crazyPass = KeyStoreHelper.getCrazyPass(getActivity(), password);
Expand All @@ -433,11 +465,17 @@ private void generateWallet() {

if (type.equals(TYPE_NEW)) {
bGenerate.setEnabled(false);
if (fingerprintAuthAllowed) {
KeyStoreHelper.saveWalletUserPass(getActivity(), name, password);
}
activityCallback.onGenerate(name, crazyPass);
} else if (type.equals(TYPE_SEED)) {
if (!checkMnemonic()) return;
String seed = etWalletMnemonic.getEditText().getText().toString();
bGenerate.setEnabled(false);
if (fingerprintAuthAllowed) {
KeyStoreHelper.saveWalletUserPass(getActivity(), name, password);
}
activityCallback.onGenerate(name, crazyPass, seed, height);
} else if (type.equals(TYPE_KEY) || type.equals(TYPE_VIEWONLY)) {
if (checkAddress() && checkViewKey() && checkSpendKey()) {
Expand All @@ -448,6 +486,9 @@ private void generateWallet() {
if (type.equals(TYPE_KEY)) {
spendKey = etWalletSpendKey.getEditText().getText().toString();
}
if (fingerprintAuthAllowed) {
KeyStoreHelper.saveWalletUserPass(getActivity(), name, password);
}
activityCallback.onGenerate(name, crazyPass, address, viewKey, spendKey, height);
}
}
Expand Down
56 changes: 50 additions & 6 deletions app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import android.support.design.widget.TextInputLayout;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.Html;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
Expand All @@ -38,18 +39,21 @@
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;

import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.util.KeyStoreHelper;
import com.m2049r.xmrwallet.widget.Toolbar;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.FingerprintHelper;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.KeyStoreHelper;
import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor;
import com.m2049r.xmrwallet.widget.Toolbar;

import java.io.File;
import java.security.KeyStoreException;

import timber.log.Timber;

Expand Down Expand Up @@ -369,12 +373,21 @@ protected void onPreExecute() {

@Override
protected Boolean doInBackground(String... params) {
if (params.length != 3) return false;
if (params.length != 4) return false;
File walletFile = Helper.getWalletFile(getActivity(), params[0]);
String oldPassword = params[1];
String userPassword = params[2];
boolean fingerprintAuthAllowed = Boolean.valueOf(params[3]);
newPassword = KeyStoreHelper.getCrazyPass(getActivity(), userPassword);
return changeWalletPassword(newPassword);
boolean success = changeWalletPassword(newPassword);
if (success) {
if (fingerprintAuthAllowed) {
KeyStoreHelper.saveWalletUserPass(getActivity(), walletName, userPassword);
} else {
KeyStoreHelper.removeWalletUserPass(getActivity(), walletName);
}
}
return success;
}

@Override
Expand Down Expand Up @@ -410,6 +423,37 @@ public AlertDialog createChangePasswordDialog() {
final TextInputLayout etPasswordB = (TextInputLayout) promptsView.findViewById(R.id.etWalletPasswordB);
etPasswordB.setHint(getString(R.string.prompt_changepwB, walletName));

LinearLayout llFingerprintAuth = (LinearLayout) promptsView.findViewById(R.id.llFingerprintAuth);
final Switch swFingerprintAllowed = (Switch) llFingerprintAuth.getChildAt(0);
if (FingerprintHelper.isDeviceSupported(getActivity())) {
llFingerprintAuth.setVisibility(View.VISIBLE);

swFingerprintAllowed.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (!swFingerprintAllowed.isChecked()) return;

AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(Html.fromHtml(getString(R.string.generate_fingerprint_warn)))
.setCancelable(false)
.setPositiveButton(getString(R.string.label_ok), null)
.setNegativeButton(getString(R.string.label_cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
swFingerprintAllowed.setChecked(false);
}
})
.show();
}
});

try {
swFingerprintAllowed.setChecked(FingerprintHelper.isFingerprintAuthAllowed(walletName));
} catch (KeyStoreException ex) {
ex.printStackTrace();
}
}

etPasswordA.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
Expand Down Expand Up @@ -483,7 +527,7 @@ public void onClick(View view) {
} else if (!newPasswordA.equals(newPasswordB)) {
etPasswordB.setError(getString(R.string.generate_bad_passwordB));
} else if (newPasswordA.equals(newPasswordB)) {
new AsyncChangePassword().execute(walletName, walletPassword, newPasswordA);
new AsyncChangePassword().execute(walletName, walletPassword, newPasswordA, Boolean.toString(swFingerprintAllowed.isChecked()));
Helper.hideKeyboardAlways(getActivity());
openDialog.dismiss();
openDialog = null;
Expand All @@ -505,7 +549,7 @@ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
} else if (!newPasswordA.equals(newPasswordB)) {
etPasswordB.setError(getString(R.string.generate_bad_passwordB));
} else if (newPasswordA.equals(newPasswordB)) {
new AsyncChangePassword().execute(walletName, walletPassword, newPasswordA);
new AsyncChangePassword().execute(walletName, walletPassword, newPasswordA, Boolean.toString(swFingerprintAllowed.isChecked()));
Helper.hideKeyboardAlways(getActivity());
openDialog.dismiss();
openDialog = null;
Expand Down
Loading

0 comments on commit a2d6ca0

Please sign in to comment.