package com.tangem.presentation.activity; import android.Manifest; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Dialog; import android.app.KeyguardManager; import android.content.pm.PackageManager; import android.hardware.fingerprint.FingerprintManager; import android.os.Build; import android.os.Bundle; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyPermanentlyInvalidatedException; import android.security.keystore.KeyProperties; import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.CheckBox; import android.widget.TextView; import android.widget.Toast; import com.tangem.data.network.task.save_pin.ConfirmWithFingerprintTask; import com.tangem.domain.wallet.FingerprintHelper; import com.tangem.domain.wallet.PINStorage; import com.tangem.wallet.R; import java.io.IOException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; public class SavePINActivity extends AppCompatActivity implements FingerprintHelper.FingerprintHelperListener { private TextView tvPIN; private CheckBox chkUseFingerprint; public ConfirmWithFingerprintTask mConfirmWithFingerprintTask; private KeyStore keyStore; private Cipher cipher; public FingerprintManager fingerprintManager; public FingerprintManager.CryptoObject cryptoObject; public FingerprintHelper fingerprintHelper; private boolean UsePIN2 = false; private enum OnConfirmAction {Save, DeleteEncryptedAndSave, Delete} private OnConfirmAction onConfirmAction; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_save_pin); MainActivity.commonInit(getApplicationContext()); UsePIN2 = getIntent().getBooleanExtra("PIN2", false); tvPIN = findViewById(R.id.pin); OnClickListener onButtonNClick = view -> tvPIN.setText(String.format("%s%s", tvPIN.getText(), ((Button) view).getText())); if (UsePIN2) { ((TextView) findViewById(R.id.pin_prompt)).setText(R.string.enter_pin2_and_use_fingerprint_to_save_it); } else { ((TextView) findViewById(R.id.pin_prompt)).setText(R.string.enter_pin_and_use_fingerprint_to_save_it); } chkUseFingerprint = findViewById(R.id.chkUseFingerprint); if (UsePIN2) { chkUseFingerprint.setChecked(true); chkUseFingerprint.setEnabled(false); } else { chkUseFingerprint.setChecked(PINStorage.haveEncryptedPIN()); chkUseFingerprint.setEnabled(true); } Button btn0 = findViewById(R.id.btn0); btn0.setOnClickListener(onButtonNClick); Button btn1 = findViewById(R.id.btn1); btn1.setOnClickListener(onButtonNClick); Button btn2 = findViewById(R.id.btn2); btn2.setOnClickListener(onButtonNClick); Button btn3 = findViewById(R.id.btn3); btn3.setOnClickListener(onButtonNClick); Button btn4 = findViewById(R.id.btn4); btn4.setOnClickListener(onButtonNClick); Button btn5 = findViewById(R.id.btn5); btn5.setOnClickListener(onButtonNClick); Button btn6 = findViewById(R.id.btn6); btn6.setOnClickListener(onButtonNClick); Button btn7 = findViewById(R.id.btn7); btn7.setOnClickListener(onButtonNClick); Button btn8 = findViewById(R.id.btn8); btn8.setOnClickListener(onButtonNClick); Button btn9 = findViewById(R.id.btn9); btn9.setOnClickListener(onButtonNClick); Button btnBS = findViewById(R.id.btnBackspace); btnBS.setOnClickListener(view -> { String S = tvPIN.getText().toString(); if (S.length() > 0) { tvPIN.setText(S.substring(0, S.length() - 1)); } }); Button btnSave = findViewById(R.id.btnSavePIN); btnSave.setOnClickListener(view -> doSavePIN()); Button btnDelete = findViewById(R.id.btnDeletePIN); btnDelete.setOnClickListener(view -> doDeletePIN()); } @Override protected void onPause() { super.onPause(); if (fingerprintHelper != null) fingerprintHelper.cancel(); if (mConfirmWithFingerprintTask != null) mConfirmWithFingerprintTask.cancel(true); } @Override protected void onStop() { super.onStop(); if (fingerprintHelper != null) fingerprintHelper.cancel(); if (mConfirmWithFingerprintTask != null) mConfirmWithFingerprintTask.cancel(true); } private void doSavePIN() { if (mConfirmWithFingerprintTask != null) { return; } tvPIN.setError(null); String pin = tvPIN.getText().toString(); boolean cancel = false; View focusView = null; if (TextUtils.isEmpty(pin)) { tvPIN.setError(getString(R.string.error_empty_pin)); focusView = tvPIN; cancel = true; } if (cancel) { focusView.requestFocus(); } else { if (UsePIN2) { if (!testFingerPrintSettings()) { tvPIN.postDelayed(this::finish, 2000); return; } onConfirmAction = OnConfirmAction.Save; mConfirmWithFingerprintTask = new ConfirmWithFingerprintTask(SavePINActivity.this); mConfirmWithFingerprintTask.execute((Void) null); } else { if (chkUseFingerprint.isChecked() || PINStorage.haveEncryptedPIN()) { if (!testFingerPrintSettings()) { tvPIN.postDelayed(this::finish, 2000); return; } if (chkUseFingerprint.isChecked()) { onConfirmAction = OnConfirmAction.Save; } else { onConfirmAction = OnConfirmAction.DeleteEncryptedAndSave; } // Show a progress spinner, and kick off a background task to // perform the user login attempt. //showProgress(true); mConfirmWithFingerprintTask = new ConfirmWithFingerprintTask(SavePINActivity.this); mConfirmWithFingerprintTask.execute((Void) null); } else { PINStorage.savePIN(tvPIN.getText().toString()); finish(); } } } } private void doDeletePIN() { if (UsePIN2) { if (PINStorage.haveEncryptedPIN2()) { if (!testFingerPrintSettings()) { tvPIN.postDelayed(this::finish, 2000); return; } onConfirmAction = OnConfirmAction.Delete; mConfirmWithFingerprintTask = new ConfirmWithFingerprintTask(SavePINActivity.this); mConfirmWithFingerprintTask.execute((Void) null); } else { tvPIN.setText(""); finish(); } } else { if (chkUseFingerprint.isChecked() || PINStorage.haveEncryptedPIN()) { if (!testFingerPrintSettings()) { tvPIN.postDelayed(this::finish, 2000); return; } onConfirmAction = OnConfirmAction.Delete; mConfirmWithFingerprintTask = new ConfirmWithFingerprintTask(SavePINActivity.this); mConfirmWithFingerprintTask.execute((Void) null); } else { tvPIN.setText(""); PINStorage.deletePIN(); finish(); } } } @Override public void authenticationFailed(String error) { print(error); } @TargetApi(Build.VERSION_CODES.M) @Override public void authenticationSucceeded(FingerprintManager.AuthenticationResult result) { print("Authentication succeeded!"); cipher = result.getCryptoObject().getCipher(); switch (onConfirmAction) { case Save: String textToEncrypt = tvPIN.getText().toString(); if (UsePIN2) { PINStorage.saveEncryptedPIN2(cipher, textToEncrypt); } else { PINStorage.saveEncryptedPIN(cipher, textToEncrypt); } print(R.string.pin_save_success); break; case Delete: if (UsePIN2) { PINStorage.deleteEncryptedPIN2(); } else { PINStorage.deleteEncryptedPIN(); PINStorage.deletePIN(); } tvPIN.setText(""); break; case DeleteEncryptedAndSave: if (UsePIN2) { PINStorage.deleteEncryptedPIN2(); PINStorage.saveEncryptedPIN2(cipher, tvPIN.getText().toString()); } else { PINStorage.deleteEncryptedPIN(); PINStorage.savePIN(tvPIN.getText().toString()); } break; } dFingerPrintConfirmation.dismiss(); finish(); } public void print(String text) { // Log.e("FP", text); } public void print(int id) { print(getString(id)); } @SuppressLint("NewApi") private boolean testFingerPrintSettings() { print("Testing Fingerprint Settings"); KeyguardManager keyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE); fingerprintManager = (FingerprintManager) getSystemService(FINGERPRINT_SERVICE); if (!keyguardManager.isKeyguardSecure()) { print("User hasn't enabled Lock Screen"); Toast.makeText(getBaseContext(), "User hasn't enabled Lock Screen", Toast.LENGTH_LONG).show(); return false; } if (ActivityCompat.checkSelfPermission(this, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) { print("User hasn't granted permission to use Fingerprint"); Toast.makeText(getBaseContext(), "User hasn't granted permission to use Fingerprint", Toast.LENGTH_LONG).show(); return false; } if (!fingerprintManager.hasEnrolledFingerprints()) { print("User hasn't registered any fingerprints"); Toast.makeText(getBaseContext(), "User hasn't registered any fingerprints", Toast.LENGTH_LONG).show(); return false; } print("Fingerprint authentication is set.\n"); return true; } public boolean getKeyStore() { print("Getting keystore..."); try { keyStore = KeyStore.getInstance(RequestPINActivity.KEYSTORE); keyStore.load(null); // Create empty keystore return true; } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) { e.printStackTrace(); } return false; } @TargetApi(Build.VERSION_CODES.M) public boolean createNewKey(boolean forceCreate) { print("Creating new key..."); try { if (forceCreate) keyStore.deleteEntry(RequestPINActivity.KEY_ALIAS); if (!keyStore.containsAlias(RequestPINActivity.KEY_ALIAS)) { KeyGenerator generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, RequestPINActivity.KEYSTORE); generator.init(new KeyGenParameterSpec.Builder(RequestPINActivity.KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_CBC) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) .setUserAuthenticationRequired(true) .build() ); generator.generateKey(); print("Key created."); } else print("Key exists."); return true; } catch (Exception e) { print(e.getMessage()); } return false; } public boolean getCipher() { print("Getting cipher..."); try { cipher = Cipher.getInstance( KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7); return true; } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { e.printStackTrace(); } return false; } @TargetApi(Build.VERSION_CODES.M) public boolean initCipher(int mode) { print("Initializing cipher..."); try { keyStore.load(null); SecretKey keyspec = (SecretKey) keyStore.getKey(RequestPINActivity.KEY_ALIAS, null); if (mode == Cipher.ENCRYPT_MODE) { cipher.init(mode, keyspec); } else { byte[] iv = PINStorage.loadEncryptedIV(); IvParameterSpec ivspec = new IvParameterSpec(iv); cipher.init(mode, keyspec, ivspec); } return true; } catch (KeyPermanentlyInvalidatedException e) { e.printStackTrace(); createNewKey(true); // Retry after clearing entry } catch (Exception e) { e.printStackTrace(); } return false; } @TargetApi(Build.VERSION_CODES.M) public boolean initCryptObject() { print("Initializing crypt object..."); try { cryptoObject = new FingerprintManager.CryptoObject(cipher); return true; } catch (Exception e) { e.printStackTrace(); } return false; } public Dialog dFingerPrintConfirmation = null; public void CreateFingerPrintConfirmationDialog() { // final AlertDialogWrapper.Builder b = new AlertDialogWrapper.Builder(this); // switch (onConfirmAction) // { // case Save: // if( UsePIN2) { // b.setTitle("Confirm new PIN2 saving "); // }else{ // b.setTitle("Confirm new PIN saving "); // } // break; // case Delete: // if( UsePIN2 ) { // b.setTitle("Confirm PIN2 deleting"); // }else{ // b.setTitle("Confirm PIN deleting"); // } // break; // case DeleteEncryptedAndSave: // if( UsePIN2 ) { // b.setTitle("Confirm deleting old saved PIN2"); // }else { // b.setTitle("Confirm deleting old saved PIN"); // } // break; // } // View view = getLayoutInflater().inflate(R.layout.dialog_fingerprint_confirmation, null); // b.setView(view); // dFingerPrintConfirmation = b.show(); // dFingerPrintConfirmation.setOnCancelListener(new DialogInterface.OnCancelListener() { // @Override // public void onCancel(DialogInterface dialog) { // fingerprintHelper.cancel(); // print("Cancel fingerprint confirmation"); // } // }); } }