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