1*90c8c64dSAndroid Build Coastguard Worker /* 2*90c8c64dSAndroid Build Coastguard Worker * Copyright (C) 2015 The Android Open Source Project 3*90c8c64dSAndroid Build Coastguard Worker * 4*90c8c64dSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*90c8c64dSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*90c8c64dSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*90c8c64dSAndroid Build Coastguard Worker * 8*90c8c64dSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*90c8c64dSAndroid Build Coastguard Worker * 10*90c8c64dSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*90c8c64dSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*90c8c64dSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*90c8c64dSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*90c8c64dSAndroid Build Coastguard Worker * limitations under the License 15*90c8c64dSAndroid Build Coastguard Worker */ 16*90c8c64dSAndroid Build Coastguard Worker 17*90c8c64dSAndroid Build Coastguard Worker package com.example.android.asymmetricfingerprintdialog; 18*90c8c64dSAndroid Build Coastguard Worker 19*90c8c64dSAndroid Build Coastguard Worker import com.example.android.asymmetricfingerprintdialog.server.StoreBackend; 20*90c8c64dSAndroid Build Coastguard Worker import com.example.android.asymmetricfingerprintdialog.server.Transaction; 21*90c8c64dSAndroid Build Coastguard Worker 22*90c8c64dSAndroid Build Coastguard Worker import android.app.DialogFragment; 23*90c8c64dSAndroid Build Coastguard Worker import android.content.Context; 24*90c8c64dSAndroid Build Coastguard Worker import android.content.SharedPreferences; 25*90c8c64dSAndroid Build Coastguard Worker import android.hardware.fingerprint.FingerprintManager; 26*90c8c64dSAndroid Build Coastguard Worker import android.os.Bundle; 27*90c8c64dSAndroid Build Coastguard Worker import android.view.KeyEvent; 28*90c8c64dSAndroid Build Coastguard Worker import android.view.LayoutInflater; 29*90c8c64dSAndroid Build Coastguard Worker import android.view.View; 30*90c8c64dSAndroid Build Coastguard Worker import android.view.ViewGroup; 31*90c8c64dSAndroid Build Coastguard Worker import android.view.inputmethod.EditorInfo; 32*90c8c64dSAndroid Build Coastguard Worker import android.view.inputmethod.InputMethodManager; 33*90c8c64dSAndroid Build Coastguard Worker import android.widget.Button; 34*90c8c64dSAndroid Build Coastguard Worker import android.widget.CheckBox; 35*90c8c64dSAndroid Build Coastguard Worker import android.widget.EditText; 36*90c8c64dSAndroid Build Coastguard Worker import android.widget.ImageView; 37*90c8c64dSAndroid Build Coastguard Worker import android.widget.TextView; 38*90c8c64dSAndroid Build Coastguard Worker 39*90c8c64dSAndroid Build Coastguard Worker import java.io.IOException; 40*90c8c64dSAndroid Build Coastguard Worker import java.security.KeyFactory; 41*90c8c64dSAndroid Build Coastguard Worker import java.security.KeyStore; 42*90c8c64dSAndroid Build Coastguard Worker import java.security.KeyStoreException; 43*90c8c64dSAndroid Build Coastguard Worker import java.security.NoSuchAlgorithmException; 44*90c8c64dSAndroid Build Coastguard Worker import java.security.PublicKey; 45*90c8c64dSAndroid Build Coastguard Worker import java.security.SecureRandom; 46*90c8c64dSAndroid Build Coastguard Worker import java.security.Signature; 47*90c8c64dSAndroid Build Coastguard Worker import java.security.SignatureException; 48*90c8c64dSAndroid Build Coastguard Worker import java.security.cert.CertificateException; 49*90c8c64dSAndroid Build Coastguard Worker import java.security.spec.InvalidKeySpecException; 50*90c8c64dSAndroid Build Coastguard Worker import java.security.spec.X509EncodedKeySpec; 51*90c8c64dSAndroid Build Coastguard Worker 52*90c8c64dSAndroid Build Coastguard Worker import javax.inject.Inject; 53*90c8c64dSAndroid Build Coastguard Worker 54*90c8c64dSAndroid Build Coastguard Worker /** 55*90c8c64dSAndroid Build Coastguard Worker * A dialog which uses fingerprint APIs to authenticate the user, and falls back to password 56*90c8c64dSAndroid Build Coastguard Worker * authentication if fingerprint is not available. 57*90c8c64dSAndroid Build Coastguard Worker */ 58*90c8c64dSAndroid Build Coastguard Worker public class FingerprintAuthenticationDialogFragment extends DialogFragment 59*90c8c64dSAndroid Build Coastguard Worker implements TextView.OnEditorActionListener, FingerprintUiHelper.Callback { 60*90c8c64dSAndroid Build Coastguard Worker 61*90c8c64dSAndroid Build Coastguard Worker private Button mCancelButton; 62*90c8c64dSAndroid Build Coastguard Worker private Button mSecondDialogButton; 63*90c8c64dSAndroid Build Coastguard Worker private View mFingerprintContent; 64*90c8c64dSAndroid Build Coastguard Worker private View mBackupContent; 65*90c8c64dSAndroid Build Coastguard Worker private EditText mPassword; 66*90c8c64dSAndroid Build Coastguard Worker private CheckBox mUseFingerprintFutureCheckBox; 67*90c8c64dSAndroid Build Coastguard Worker private TextView mPasswordDescriptionTextView; 68*90c8c64dSAndroid Build Coastguard Worker private TextView mNewFingerprintEnrolledTextView; 69*90c8c64dSAndroid Build Coastguard Worker 70*90c8c64dSAndroid Build Coastguard Worker private Stage mStage = Stage.FINGERPRINT; 71*90c8c64dSAndroid Build Coastguard Worker 72*90c8c64dSAndroid Build Coastguard Worker private FingerprintManager.CryptoObject mCryptoObject; 73*90c8c64dSAndroid Build Coastguard Worker private FingerprintUiHelper mFingerprintUiHelper; 74*90c8c64dSAndroid Build Coastguard Worker private MainActivity mActivity; 75*90c8c64dSAndroid Build Coastguard Worker 76*90c8c64dSAndroid Build Coastguard Worker @Inject FingerprintUiHelper.FingerprintUiHelperBuilder mFingerprintUiHelperBuilder; 77*90c8c64dSAndroid Build Coastguard Worker @Inject InputMethodManager mInputMethodManager; 78*90c8c64dSAndroid Build Coastguard Worker @Inject SharedPreferences mSharedPreferences; 79*90c8c64dSAndroid Build Coastguard Worker @Inject StoreBackend mStoreBackend; 80*90c8c64dSAndroid Build Coastguard Worker 81*90c8c64dSAndroid Build Coastguard Worker @Inject FingerprintAuthenticationDialogFragment()82*90c8c64dSAndroid Build Coastguard Worker public FingerprintAuthenticationDialogFragment() {} 83*90c8c64dSAndroid Build Coastguard Worker 84*90c8c64dSAndroid Build Coastguard Worker @Override onCreate(Bundle savedInstanceState)85*90c8c64dSAndroid Build Coastguard Worker public void onCreate(Bundle savedInstanceState) { 86*90c8c64dSAndroid Build Coastguard Worker super.onCreate(savedInstanceState); 87*90c8c64dSAndroid Build Coastguard Worker 88*90c8c64dSAndroid Build Coastguard Worker // Do not create a new Fragment when the Activity is re-created such as orientation changes. 89*90c8c64dSAndroid Build Coastguard Worker setRetainInstance(true); 90*90c8c64dSAndroid Build Coastguard Worker setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Material_Light_Dialog); 91*90c8c64dSAndroid Build Coastguard Worker 92*90c8c64dSAndroid Build Coastguard Worker // We register a new user account here. Real apps should do this with proper UIs. 93*90c8c64dSAndroid Build Coastguard Worker enroll(); 94*90c8c64dSAndroid Build Coastguard Worker } 95*90c8c64dSAndroid Build Coastguard Worker 96*90c8c64dSAndroid Build Coastguard Worker @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)97*90c8c64dSAndroid Build Coastguard Worker public View onCreateView(LayoutInflater inflater, ViewGroup container, 98*90c8c64dSAndroid Build Coastguard Worker Bundle savedInstanceState) { 99*90c8c64dSAndroid Build Coastguard Worker getDialog().setTitle(getString(R.string.sign_in)); 100*90c8c64dSAndroid Build Coastguard Worker View v = inflater.inflate(R.layout.fingerprint_dialog_container, container, false); 101*90c8c64dSAndroid Build Coastguard Worker mCancelButton = (Button) v.findViewById(R.id.cancel_button); 102*90c8c64dSAndroid Build Coastguard Worker mCancelButton.setOnClickListener(new View.OnClickListener() { 103*90c8c64dSAndroid Build Coastguard Worker @Override 104*90c8c64dSAndroid Build Coastguard Worker public void onClick(View view) { 105*90c8c64dSAndroid Build Coastguard Worker dismiss(); 106*90c8c64dSAndroid Build Coastguard Worker } 107*90c8c64dSAndroid Build Coastguard Worker }); 108*90c8c64dSAndroid Build Coastguard Worker 109*90c8c64dSAndroid Build Coastguard Worker mSecondDialogButton = (Button) v.findViewById(R.id.second_dialog_button); 110*90c8c64dSAndroid Build Coastguard Worker mSecondDialogButton.setOnClickListener(new View.OnClickListener() { 111*90c8c64dSAndroid Build Coastguard Worker @Override 112*90c8c64dSAndroid Build Coastguard Worker public void onClick(View view) { 113*90c8c64dSAndroid Build Coastguard Worker if (mStage == Stage.FINGERPRINT) { 114*90c8c64dSAndroid Build Coastguard Worker goToBackup(); 115*90c8c64dSAndroid Build Coastguard Worker } else { 116*90c8c64dSAndroid Build Coastguard Worker verifyPassword(); 117*90c8c64dSAndroid Build Coastguard Worker } 118*90c8c64dSAndroid Build Coastguard Worker } 119*90c8c64dSAndroid Build Coastguard Worker }); 120*90c8c64dSAndroid Build Coastguard Worker mFingerprintContent = v.findViewById(R.id.fingerprint_container); 121*90c8c64dSAndroid Build Coastguard Worker mBackupContent = v.findViewById(R.id.backup_container); 122*90c8c64dSAndroid Build Coastguard Worker mPassword = (EditText) v.findViewById(R.id.password); 123*90c8c64dSAndroid Build Coastguard Worker mPassword.setOnEditorActionListener(this); 124*90c8c64dSAndroid Build Coastguard Worker mPasswordDescriptionTextView = (TextView) v.findViewById(R.id.password_description); 125*90c8c64dSAndroid Build Coastguard Worker mUseFingerprintFutureCheckBox = (CheckBox) 126*90c8c64dSAndroid Build Coastguard Worker v.findViewById(R.id.use_fingerprint_in_future_check); 127*90c8c64dSAndroid Build Coastguard Worker mNewFingerprintEnrolledTextView = (TextView) 128*90c8c64dSAndroid Build Coastguard Worker v.findViewById(R.id.new_fingerprint_enrolled_description); 129*90c8c64dSAndroid Build Coastguard Worker mFingerprintUiHelper = mFingerprintUiHelperBuilder.build( 130*90c8c64dSAndroid Build Coastguard Worker (ImageView) v.findViewById(R.id.fingerprint_icon), 131*90c8c64dSAndroid Build Coastguard Worker (TextView) v.findViewById(R.id.fingerprint_status), this); 132*90c8c64dSAndroid Build Coastguard Worker updateStage(); 133*90c8c64dSAndroid Build Coastguard Worker 134*90c8c64dSAndroid Build Coastguard Worker // If fingerprint authentication is not available, switch immediately to the backup 135*90c8c64dSAndroid Build Coastguard Worker // (password) screen. 136*90c8c64dSAndroid Build Coastguard Worker if (!mFingerprintUiHelper.isFingerprintAuthAvailable()) { 137*90c8c64dSAndroid Build Coastguard Worker goToBackup(); 138*90c8c64dSAndroid Build Coastguard Worker } 139*90c8c64dSAndroid Build Coastguard Worker return v; 140*90c8c64dSAndroid Build Coastguard Worker } 141*90c8c64dSAndroid Build Coastguard Worker 142*90c8c64dSAndroid Build Coastguard Worker @Override onResume()143*90c8c64dSAndroid Build Coastguard Worker public void onResume() { 144*90c8c64dSAndroid Build Coastguard Worker super.onResume(); 145*90c8c64dSAndroid Build Coastguard Worker if (mStage == Stage.FINGERPRINT) { 146*90c8c64dSAndroid Build Coastguard Worker mFingerprintUiHelper.startListening(mCryptoObject); 147*90c8c64dSAndroid Build Coastguard Worker } 148*90c8c64dSAndroid Build Coastguard Worker } 149*90c8c64dSAndroid Build Coastguard Worker setStage(Stage stage)150*90c8c64dSAndroid Build Coastguard Worker public void setStage(Stage stage) { 151*90c8c64dSAndroid Build Coastguard Worker mStage = stage; 152*90c8c64dSAndroid Build Coastguard Worker } 153*90c8c64dSAndroid Build Coastguard Worker 154*90c8c64dSAndroid Build Coastguard Worker @Override onPause()155*90c8c64dSAndroid Build Coastguard Worker public void onPause() { 156*90c8c64dSAndroid Build Coastguard Worker super.onPause(); 157*90c8c64dSAndroid Build Coastguard Worker mFingerprintUiHelper.stopListening(); 158*90c8c64dSAndroid Build Coastguard Worker } 159*90c8c64dSAndroid Build Coastguard Worker 160*90c8c64dSAndroid Build Coastguard Worker @Override onAttach(Context context)161*90c8c64dSAndroid Build Coastguard Worker public void onAttach(Context context) { 162*90c8c64dSAndroid Build Coastguard Worker super.onAttach(context); 163*90c8c64dSAndroid Build Coastguard Worker mActivity = (MainActivity) getActivity(); 164*90c8c64dSAndroid Build Coastguard Worker } 165*90c8c64dSAndroid Build Coastguard Worker 166*90c8c64dSAndroid Build Coastguard Worker /** 167*90c8c64dSAndroid Build Coastguard Worker * Sets the crypto object to be passed in when authenticating with fingerprint. 168*90c8c64dSAndroid Build Coastguard Worker */ setCryptoObject(FingerprintManager.CryptoObject cryptoObject)169*90c8c64dSAndroid Build Coastguard Worker public void setCryptoObject(FingerprintManager.CryptoObject cryptoObject) { 170*90c8c64dSAndroid Build Coastguard Worker mCryptoObject = cryptoObject; 171*90c8c64dSAndroid Build Coastguard Worker } 172*90c8c64dSAndroid Build Coastguard Worker 173*90c8c64dSAndroid Build Coastguard Worker /** 174*90c8c64dSAndroid Build Coastguard Worker * Switches to backup (password) screen. This either can happen when fingerprint is not 175*90c8c64dSAndroid Build Coastguard Worker * available or the user chooses to use the password authentication method by pressing the 176*90c8c64dSAndroid Build Coastguard Worker * button. This can also happen when the user had too many fingerprint attempts. 177*90c8c64dSAndroid Build Coastguard Worker */ goToBackup()178*90c8c64dSAndroid Build Coastguard Worker private void goToBackup() { 179*90c8c64dSAndroid Build Coastguard Worker mStage = Stage.PASSWORD; 180*90c8c64dSAndroid Build Coastguard Worker updateStage(); 181*90c8c64dSAndroid Build Coastguard Worker mPassword.requestFocus(); 182*90c8c64dSAndroid Build Coastguard Worker 183*90c8c64dSAndroid Build Coastguard Worker // Show the keyboard. 184*90c8c64dSAndroid Build Coastguard Worker mPassword.postDelayed(mShowKeyboardRunnable, 500); 185*90c8c64dSAndroid Build Coastguard Worker 186*90c8c64dSAndroid Build Coastguard Worker // Fingerprint is not used anymore. Stop listening for it. 187*90c8c64dSAndroid Build Coastguard Worker mFingerprintUiHelper.stopListening(); 188*90c8c64dSAndroid Build Coastguard Worker } 189*90c8c64dSAndroid Build Coastguard Worker 190*90c8c64dSAndroid Build Coastguard Worker /** 191*90c8c64dSAndroid Build Coastguard Worker * Enrolls a user to the fake backend. 192*90c8c64dSAndroid Build Coastguard Worker */ enroll()193*90c8c64dSAndroid Build Coastguard Worker private void enroll() { 194*90c8c64dSAndroid Build Coastguard Worker try { 195*90c8c64dSAndroid Build Coastguard Worker KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); 196*90c8c64dSAndroid Build Coastguard Worker keyStore.load(null); 197*90c8c64dSAndroid Build Coastguard Worker PublicKey publicKey = keyStore.getCertificate(MainActivity.KEY_NAME).getPublicKey(); 198*90c8c64dSAndroid Build Coastguard Worker // Provide the public key to the backend. In most cases, the key needs to be transmitted 199*90c8c64dSAndroid Build Coastguard Worker // to the backend over the network, for which Key.getEncoded provides a suitable wire 200*90c8c64dSAndroid Build Coastguard Worker // format (X.509 DER-encoded). The backend can then create a PublicKey instance from the 201*90c8c64dSAndroid Build Coastguard Worker // X.509 encoded form using KeyFactory.generatePublic. This conversion is also currently 202*90c8c64dSAndroid Build Coastguard Worker // needed on API Level 23 (Android M) due to a platform bug which prevents the use of 203*90c8c64dSAndroid Build Coastguard Worker // Android Keystore public keys when their private keys require user authentication. 204*90c8c64dSAndroid Build Coastguard Worker // This conversion creates a new public key which is not backed by Android Keystore and 205*90c8c64dSAndroid Build Coastguard Worker // thus is not affected by the bug. 206*90c8c64dSAndroid Build Coastguard Worker KeyFactory factory = KeyFactory.getInstance(publicKey.getAlgorithm()); 207*90c8c64dSAndroid Build Coastguard Worker X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKey.getEncoded()); 208*90c8c64dSAndroid Build Coastguard Worker PublicKey verificationKey = factory.generatePublic(spec); 209*90c8c64dSAndroid Build Coastguard Worker mStoreBackend.enroll("user", "password", verificationKey); 210*90c8c64dSAndroid Build Coastguard Worker } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | 211*90c8c64dSAndroid Build Coastguard Worker IOException | InvalidKeySpecException e) { 212*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 213*90c8c64dSAndroid Build Coastguard Worker } 214*90c8c64dSAndroid Build Coastguard Worker } 215*90c8c64dSAndroid Build Coastguard Worker 216*90c8c64dSAndroid Build Coastguard Worker /** 217*90c8c64dSAndroid Build Coastguard Worker * Checks whether the current entered password is correct, and dismisses the the dialog and lets 218*90c8c64dSAndroid Build Coastguard Worker * the activity know about the result. 219*90c8c64dSAndroid Build Coastguard Worker */ verifyPassword()220*90c8c64dSAndroid Build Coastguard Worker private void verifyPassword() { 221*90c8c64dSAndroid Build Coastguard Worker Transaction transaction = new Transaction("user", 1, new SecureRandom().nextLong()); 222*90c8c64dSAndroid Build Coastguard Worker if (!mStoreBackend.verify(transaction, mPassword.getText().toString())) { 223*90c8c64dSAndroid Build Coastguard Worker return; 224*90c8c64dSAndroid Build Coastguard Worker } 225*90c8c64dSAndroid Build Coastguard Worker if (mStage == Stage.NEW_FINGERPRINT_ENROLLED) { 226*90c8c64dSAndroid Build Coastguard Worker SharedPreferences.Editor editor = mSharedPreferences.edit(); 227*90c8c64dSAndroid Build Coastguard Worker editor.putBoolean(getString(R.string.use_fingerprint_to_authenticate_key), 228*90c8c64dSAndroid Build Coastguard Worker mUseFingerprintFutureCheckBox.isChecked()); 229*90c8c64dSAndroid Build Coastguard Worker editor.apply(); 230*90c8c64dSAndroid Build Coastguard Worker 231*90c8c64dSAndroid Build Coastguard Worker if (mUseFingerprintFutureCheckBox.isChecked()) { 232*90c8c64dSAndroid Build Coastguard Worker // Re-create the key so that fingerprints including new ones are validated. 233*90c8c64dSAndroid Build Coastguard Worker mActivity.createKeyPair(); 234*90c8c64dSAndroid Build Coastguard Worker mStage = Stage.FINGERPRINT; 235*90c8c64dSAndroid Build Coastguard Worker } 236*90c8c64dSAndroid Build Coastguard Worker } 237*90c8c64dSAndroid Build Coastguard Worker mPassword.setText(""); 238*90c8c64dSAndroid Build Coastguard Worker mActivity.onPurchased(null); 239*90c8c64dSAndroid Build Coastguard Worker dismiss(); 240*90c8c64dSAndroid Build Coastguard Worker } 241*90c8c64dSAndroid Build Coastguard Worker 242*90c8c64dSAndroid Build Coastguard Worker private final Runnable mShowKeyboardRunnable = new Runnable() { 243*90c8c64dSAndroid Build Coastguard Worker @Override 244*90c8c64dSAndroid Build Coastguard Worker public void run() { 245*90c8c64dSAndroid Build Coastguard Worker mInputMethodManager.showSoftInput(mPassword, 0); 246*90c8c64dSAndroid Build Coastguard Worker } 247*90c8c64dSAndroid Build Coastguard Worker }; 248*90c8c64dSAndroid Build Coastguard Worker updateStage()249*90c8c64dSAndroid Build Coastguard Worker private void updateStage() { 250*90c8c64dSAndroid Build Coastguard Worker switch (mStage) { 251*90c8c64dSAndroid Build Coastguard Worker case FINGERPRINT: 252*90c8c64dSAndroid Build Coastguard Worker mCancelButton.setText(R.string.cancel); 253*90c8c64dSAndroid Build Coastguard Worker mSecondDialogButton.setText(R.string.use_password); 254*90c8c64dSAndroid Build Coastguard Worker mFingerprintContent.setVisibility(View.VISIBLE); 255*90c8c64dSAndroid Build Coastguard Worker mBackupContent.setVisibility(View.GONE); 256*90c8c64dSAndroid Build Coastguard Worker break; 257*90c8c64dSAndroid Build Coastguard Worker case NEW_FINGERPRINT_ENROLLED: 258*90c8c64dSAndroid Build Coastguard Worker // Intentional fall through 259*90c8c64dSAndroid Build Coastguard Worker case PASSWORD: 260*90c8c64dSAndroid Build Coastguard Worker mCancelButton.setText(R.string.cancel); 261*90c8c64dSAndroid Build Coastguard Worker mSecondDialogButton.setText(R.string.ok); 262*90c8c64dSAndroid Build Coastguard Worker mFingerprintContent.setVisibility(View.GONE); 263*90c8c64dSAndroid Build Coastguard Worker mBackupContent.setVisibility(View.VISIBLE); 264*90c8c64dSAndroid Build Coastguard Worker if (mStage == Stage.NEW_FINGERPRINT_ENROLLED) { 265*90c8c64dSAndroid Build Coastguard Worker mPasswordDescriptionTextView.setVisibility(View.GONE); 266*90c8c64dSAndroid Build Coastguard Worker mNewFingerprintEnrolledTextView.setVisibility(View.VISIBLE); 267*90c8c64dSAndroid Build Coastguard Worker mUseFingerprintFutureCheckBox.setVisibility(View.VISIBLE); 268*90c8c64dSAndroid Build Coastguard Worker } 269*90c8c64dSAndroid Build Coastguard Worker break; 270*90c8c64dSAndroid Build Coastguard Worker } 271*90c8c64dSAndroid Build Coastguard Worker } 272*90c8c64dSAndroid Build Coastguard Worker 273*90c8c64dSAndroid Build Coastguard Worker @Override onEditorAction(TextView v, int actionId, KeyEvent event)274*90c8c64dSAndroid Build Coastguard Worker public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 275*90c8c64dSAndroid Build Coastguard Worker if (actionId == EditorInfo.IME_ACTION_GO) { 276*90c8c64dSAndroid Build Coastguard Worker verifyPassword(); 277*90c8c64dSAndroid Build Coastguard Worker return true; 278*90c8c64dSAndroid Build Coastguard Worker } 279*90c8c64dSAndroid Build Coastguard Worker return false; 280*90c8c64dSAndroid Build Coastguard Worker } 281*90c8c64dSAndroid Build Coastguard Worker 282*90c8c64dSAndroid Build Coastguard Worker @Override onAuthenticated()283*90c8c64dSAndroid Build Coastguard Worker public void onAuthenticated() { 284*90c8c64dSAndroid Build Coastguard Worker // Callback from FingerprintUiHelper. Let the activity know that authentication was 285*90c8c64dSAndroid Build Coastguard Worker // successful. 286*90c8c64dSAndroid Build Coastguard Worker mPassword.setText(""); 287*90c8c64dSAndroid Build Coastguard Worker Signature signature = mCryptoObject.getSignature(); 288*90c8c64dSAndroid Build Coastguard Worker // Include a client nonce in the transaction so that the nonce is also signed by the private 289*90c8c64dSAndroid Build Coastguard Worker // key and the backend can verify that the same nonce can't be used to prevent replay 290*90c8c64dSAndroid Build Coastguard Worker // attacks. 291*90c8c64dSAndroid Build Coastguard Worker Transaction transaction = new Transaction("user", 1, new SecureRandom().nextLong()); 292*90c8c64dSAndroid Build Coastguard Worker try { 293*90c8c64dSAndroid Build Coastguard Worker signature.update(transaction.toByteArray()); 294*90c8c64dSAndroid Build Coastguard Worker byte[] sigBytes = signature.sign(); 295*90c8c64dSAndroid Build Coastguard Worker if (mStoreBackend.verify(transaction, sigBytes)) { 296*90c8c64dSAndroid Build Coastguard Worker mActivity.onPurchased(sigBytes); 297*90c8c64dSAndroid Build Coastguard Worker dismiss(); 298*90c8c64dSAndroid Build Coastguard Worker } else { 299*90c8c64dSAndroid Build Coastguard Worker mActivity.onPurchaseFailed(); 300*90c8c64dSAndroid Build Coastguard Worker dismiss(); 301*90c8c64dSAndroid Build Coastguard Worker } 302*90c8c64dSAndroid Build Coastguard Worker } catch (SignatureException e) { 303*90c8c64dSAndroid Build Coastguard Worker throw new RuntimeException(e); 304*90c8c64dSAndroid Build Coastguard Worker } 305*90c8c64dSAndroid Build Coastguard Worker } 306*90c8c64dSAndroid Build Coastguard Worker 307*90c8c64dSAndroid Build Coastguard Worker @Override onError()308*90c8c64dSAndroid Build Coastguard Worker public void onError() { 309*90c8c64dSAndroid Build Coastguard Worker goToBackup(); 310*90c8c64dSAndroid Build Coastguard Worker } 311*90c8c64dSAndroid Build Coastguard Worker 312*90c8c64dSAndroid Build Coastguard Worker /** 313*90c8c64dSAndroid Build Coastguard Worker * Enumeration to indicate which authentication method the user is trying to authenticate with. 314*90c8c64dSAndroid Build Coastguard Worker */ 315*90c8c64dSAndroid Build Coastguard Worker public enum Stage { 316*90c8c64dSAndroid Build Coastguard Worker FINGERPRINT, 317*90c8c64dSAndroid Build Coastguard Worker NEW_FINGERPRINT_ENROLLED, 318*90c8c64dSAndroid Build Coastguard Worker PASSWORD 319*90c8c64dSAndroid Build Coastguard Worker } 320*90c8c64dSAndroid Build Coastguard Worker } 321