package piuk.blockchain.android.ui; /*Client ID: 381130279932.apps.googleusercontent.com Redirect URIs: urn:ietf:wg:oauth:2.0:oob http://localhost Application type: Android Package name: com.ultimasquare.pinview Certificate fingerprint (SHA1): 86:F2:4D:FD:34:98:BF:0C:47:94:34:D4:8C:68:A3:84:B7:D7:B2:0F Deep Linking: Disabled*/ import java.lang.ref.WeakReference; import java.security.SecureRandom; import java.util.ArrayList; import java.util.List; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.spongycastle.util.encoders.Hex; import piuk.blockchain.android.EventListeners; import piuk.blockchain.android.MyWallet; import piuk.blockchain.android.Constants; import piuk.blockchain.android.R; import piuk.blockchain.android.SuccessCallback; import piuk.blockchain.android.WalletApplication; //import piuk.blockchain.android.ui.dialogs.RekeyWalletDialog; import piuk.blockchain.android.ui.dialogs.RequestPasswordDialog; //import piuk.blockchain.android.ui.dialogs.WelcomeDialog; import piuk.blockchain.android.util.WalletUtils; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences.Editor; import android.os.Bundle; import android.preference.PreferenceManager; import android.view.Menu; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; public class PinEntryActivity extends AbstractWalletActivity { public static boolean active = false; public static final int UNKNOWN = 1; public static final int BEGIN_SETUP = 1; public static final int CONFIRM_PIN_SETUP = 2; public static final int BEGIN_CHECK_PIN = 4; public static final int FINISHING_SETUP = 3; public static final int VALIDATING_PIN = 5; public static final int PBKDF2Iterations = 2000; private static List<WeakReference<PinEntryActivity>> fragmentRefs = new ArrayList<WeakReference<PinEntryActivity>>(); private EventListeners.EventListener eventListener = new EventListeners.EventListener() { @Override public String getDescription() { return "Pinentry Listener"; } @Override public void onWalletDidChange() { begin(); } }; public static void beginAll() { for (WeakReference<PinEntryActivity> fragmentRef : fragmentRefs) { if (fragmentRef != null && fragmentRef.get() != null) { try { ((PinEntryActivity)fragmentRef.get()).begin(); } catch (Exception e) { e.printStackTrace(); } } } } private static final String WebROOT = "https://"+Constants.BLOCKCHAIN_DOMAIN+"/pin-store"; int stage = UNKNOWN; String previousEntered; String userEntered; String userPin="8888"; final int PIN_LENGTH = 4; boolean keyPadLockedFlag = false; Context appContext; TextView titleView; TextView pinBox0; TextView pinBox1; TextView pinBox2; TextView pinBox3; TextView [] pinBoxArray; TextView statusView; Button button0; Button button1; Button button2; Button button3; Button button4; Button button5; Button button6; Button button7; Button button8; Button button9; Button button10; Button buttonDelete; Button buttonBlank; public static JSONObject apiGetValue(String key, String pin) throws Exception { StringBuilder args = new StringBuilder(); args.append("key=" + key); args.append("&pin="+ pin); args.append("&method=get"); String response = WalletUtils.postURL(WebROOT, args.toString()); if (response == null || response.length() == 0) throw new Exception("Invalid Server Response"); try { return (JSONObject) new JSONParser().parse(response); } catch (ParseException e) { throw new Exception("Invalid Server Response"); } } public static JSONObject apiStoreKey(String key, String value, String pin) throws Exception { StringBuilder args = new StringBuilder(); args.append("key=" + key); args.append("&value=" + value); args.append("&pin="+pin); args.append("&method=put"); String response = WalletUtils.postURL(WebROOT, args.toString()); if (response == null || response.length() == 0) throw new Exception("Invalid Server Response"); try { return (JSONObject) new JSONParser().parse(response); } catch (ParseException e) { throw new Exception("Invalid Server Response"); } } /* *************** public static void clearPrefValues(WalletApplication application) throws Exception { Editor editor = PreferenceManager.getDefaultSharedPreferences(application).edit(); editor.remove("pin_kookup_key"); editor.remove("encrypted_password"); if (!editor.commit()) { throw new Exception("Error Saving Preferences"); } } */ private void disableKeyPad(boolean enabled) { button0.setEnabled(!enabled); button1.setEnabled(!enabled); button2.setEnabled(!enabled); button3.setEnabled(!enabled); button4.setEnabled(!enabled); button5.setEnabled(!enabled); button6.setEnabled(!enabled); button7.setEnabled(!enabled); button8.setEnabled(!enabled); button9.setEnabled(!enabled); buttonDelete.setEnabled(!enabled); buttonBlank.setEnabled(!enabled); } public void validatePIN(final String PIN) { disableKeyPad(true); statusView.setText("Validating PIN"); final Activity activity = this; new Thread(new Runnable() { public void run() { String pin_lookup_key = PreferenceManager.getDefaultSharedPreferences(application).getString("pin_kookup_key", null); String encrypted_password = PreferenceManager.getDefaultSharedPreferences(application).getString("encrypted_password", null); try { final JSONObject response = apiGetValue(pin_lookup_key, PIN); String decryptionKey = (String) response.get("success"); if (decryptionKey != null) { application.didEncounterFatalPINServerError = false; String password = MyWallet.decrypt(encrypted_password, decryptionKey, PBKDF2Iterations); application.checkIfWalletHasUpdatedAndFetchTransactions(password, new SuccessCallback() { @Override public void onSuccess() { handler.post(new Runnable() { public void run() { Toast.makeText(application, "PIN Verified", Toast.LENGTH_SHORT).show(); disableKeyPad(false); /* if (application.needsWalletRekey()) { RekeyWalletDialog.show(getSupportFragmentManager(), application, new SuccessCallback() { @Override public void onSuccess() { finish(); } @Override public void onFail() { finish(); } }); } else { finish(); } */ finish(); } }); } @Override public void onFail() { handler.post(new Runnable() { public void run() { disableKeyPad(false); Toast.makeText(application, R.string.toast_wallet_decryption_failed, Toast.LENGTH_LONG) .show(); /* ******************* try { clearPrefValues(application); } catch (Exception e) { e.printStackTrace(); } */ begin(); } }); } }); } else if (response.get("error") != null) { //Even though we received an error it is a valid response //So no fatal application.didEncounterFatalPINServerError = false; //"code" == 2 means the PIN is incorrect if (!response.containsKey("code") || ((Number)response.get("code")).intValue() != 2) { /* ********************** clearPrefValues(application); */ } handler.post(new Runnable() { public void run() { disableKeyPad(false); Toast.makeText(application, (String)response.get("error"), Toast.LENGTH_SHORT).show(); begin(); } }); } else { throw new Exception("Unknown Error"); } } catch (final Exception e) { e.printStackTrace(); application.didEncounterFatalPINServerError = true; handler.post(new Runnable() { public void run() { try { disableKeyPad(false); AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setCancelable(false); builder.setMessage(R.string.pin_server_error_description); builder.setTitle(R.string.pin_server_error); builder.setPositiveButton(R.string.try_again, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { validatePIN(PIN); dialog.dismiss(); begin(); } }); builder.setNegativeButton(R.string.pin_server_error_enter_password_manually, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); RequestPasswordDialog.show( getSupportFragmentManager(), new SuccessCallback() { public void onSuccess() { finish(); } public void onFail() { Toast.makeText(application, R.string.password_incorrect, Toast.LENGTH_LONG).show(); begin(); } }, RequestPasswordDialog.PasswordTypeMain); } }); AlertDialog dialog = builder.create(); dialog.show(); begin(); } catch (Exception e) { e.printStackTrace(); } } }); } } }).start(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); fragmentRefs.add(new WeakReference<PinEntryActivity>(this)); EventListeners.addEventListener(eventListener); appContext = this; userEntered = ""; setContentView(R.layout.activity_pin_entry_view); buttonDelete = (Button) findViewById(R.id.buttonDeleteBack); buttonDelete.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { if (keyPadLockedFlag == true) { return; } if (userEntered.length()>0) { userEntered = userEntered.substring(0,userEntered.length()-1); pinBoxArray[userEntered.length()].setText(""); } } } ); titleView = (TextView)findViewById(R.id.titleBox); pinBox0 = (TextView)findViewById(R.id.pinBox0); pinBox1 = (TextView)findViewById(R.id.pinBox1); pinBox2 = (TextView)findViewById(R.id.pinBox2); pinBox3 = (TextView)findViewById(R.id.pinBox3); pinBoxArray = new TextView[PIN_LENGTH]; pinBoxArray[0] = pinBox0; pinBoxArray[1] = pinBox1; pinBoxArray[2] = pinBox2; pinBoxArray[3] = pinBox3; statusView = (TextView) findViewById(R.id.statusMessage); View.OnClickListener pinButtonHandler = new View.OnClickListener() { public void onClick(View v) { if (keyPadLockedFlag == true) { return; } Button pressedButton = (Button)v; if (userEntered.length() < PIN_LENGTH) { final String PIN = userEntered + pressedButton.getText(); userEntered = PIN; //Update pin boxes pinBoxArray[userEntered.length()-1].setText("8"); if (userEntered.length() == PIN_LENGTH) { if (stage == BEGIN_CHECK_PIN) { stage = VALIDATING_PIN; validatePIN(PIN); } if (stage == BEGIN_SETUP) { previousEntered = userEntered; clear(); titleView.setText("Confirm PIN"); statusView.setText("Please renter the PIN code"); stage = CONFIRM_PIN_SETUP; } else if (stage == CONFIRM_PIN_SETUP) { if (previousEntered.equals(userEntered)) { stage = FINISHING_SETUP; statusView.setText("Saving PIN. Please Wait."); disableKeyPad(true); new Thread(new Runnable() { public void run() { try { byte[] bytes = new byte[16]; SecureRandom random = new SecureRandom(); random.nextBytes(bytes); final String key = new String(Hex.encode(bytes), "UTF-8"); random.nextBytes(bytes); final String value = new String(Hex.encode(bytes), "UTF-8"); final JSONObject response = apiStoreKey(key, value, PIN); if (response.get("success") != null) { application.didEncounterFatalPINServerError = false; handler.post(new Runnable() { public void run() { try { Editor editor = PreferenceManager.getDefaultSharedPreferences(application).edit(); editor.putString("pin_kookup_key", key); editor.putString("encrypted_password", MyWallet.encrypt(application.getRemoteWallet().getTemporyPassword(), value, PBKDF2Iterations)); if (!editor.commit()) { throw new Exception("Error Saving Preferences"); } Toast.makeText(application, R.string.toast_pin_saved, Toast.LENGTH_SHORT) .show(); finish(); disableKeyPad(false); } catch (Exception e) { e.printStackTrace(); Toast.makeText(application, e.getLocalizedMessage(), Toast.LENGTH_LONG) .show(); begin(); disableKeyPad(false); } } }); } else if (response.get("error") != null) { application.didEncounterFatalPINServerError = false; handler.post(new Runnable() { public void run() { Toast.makeText(application, (String) response.get("error"), Toast.LENGTH_LONG) .show(); disableKeyPad(false); begin(); } }); } else { throw new Exception("Unknown Error"); } } catch (final Exception e) { application.didEncounterFatalPINServerError = true; e.printStackTrace(); finish(); } } }).start(); } else { statusView.setText("PIN does not match"); begin(); } } } } else { //Roll over pinBoxArray[0].setText(""); pinBoxArray[1].setText(""); pinBoxArray[2].setText(""); pinBoxArray[3].setText(""); userEntered = ""; statusView.setText(""); userEntered = userEntered + pressedButton.getText(); //Update pin boxes pinBoxArray[userEntered.length()-1].setText("8"); } } }; button0 = (Button)findViewById(R.id.button0); button0.setOnClickListener(pinButtonHandler); button1 = (Button)findViewById(R.id.button1); button1.setOnClickListener(pinButtonHandler); button2 = (Button)findViewById(R.id.button2); button2.setOnClickListener(pinButtonHandler); button3 = (Button)findViewById(R.id.button3); button3.setOnClickListener(pinButtonHandler); button4 = (Button)findViewById(R.id.button4); button4.setOnClickListener(pinButtonHandler); button5 = (Button)findViewById(R.id.button5); button5.setOnClickListener(pinButtonHandler); button6 = (Button)findViewById(R.id.button6); button6.setOnClickListener(pinButtonHandler); button7 = (Button)findViewById(R.id.button7); button7.setOnClickListener(pinButtonHandler); button8 = (Button)findViewById(R.id.button8); button8.setOnClickListener(pinButtonHandler); button9 = (Button)findViewById(R.id.button9); button9.setOnClickListener(pinButtonHandler); buttonDelete = (Button)findViewById(R.id.buttonDeleteBack); buttonBlank = (Button)findViewById(R.id.buttonBlank); } public void begin() { final Activity activity = this; disableKeyPad(false); clear(); RequestPasswordDialog.hide(); // WelcomeDialog.hide(); String pin_lookup_key = PreferenceManager.getDefaultSharedPreferences(this).getString("pin_kookup_key", null); String encrypted_password = PreferenceManager.getDefaultSharedPreferences(this).getString("encrypted_password", null); if (pin_lookup_key == null || encrypted_password == null || application.decryptionErrors > 0) { titleView.setText("Create PIN"); statusView.setText("Please create a new PIN code"); stage = BEGIN_SETUP; if (application.getRemoteWallet() == null || application.decryptionErrors > 0) { if (application.decryptionErrors <= 1 && application.getGUID() != null && application.getSharedKey() != null) { RequestPasswordDialog.show( getSupportFragmentManager(), new SuccessCallback() { public void onSuccess() { statusView.setText("Password Ok. Please create a PIN."); } public void onFail() { // WelcomeDialog.show(getSupportFragmentManager(), activity, (WalletApplication)getApplication()); } }, RequestPasswordDialog.PasswordTypeMain); } else { // WelcomeDialog.show(getSupportFragmentManager(), activity, (WalletApplication)getApplication()); } } } else { titleView.setText("Enter PIN"); stage = BEGIN_CHECK_PIN; } } @Override protected void onResume() { super.onResume(); begin(); } @Override protected void onDestroy() { super.onDestroy(); EventListeners.removeEventListener(eventListener); } @Override public void onStart() { super.onStart(); active = true; begin(); } @Override public void onStop() { super.onStop(); active = false; } public void clear() { statusView.setText(""); //Roll over pinBoxArray[0].setText(""); pinBoxArray[1].setText(""); pinBoxArray[2].setText(""); pinBoxArray[3].setText(""); userEntered = ""; } @Override public void onBackPressed() { // TODO Auto-generated method stub //App not allowed to go back to Parent activity until correct pin entered. return; //super.onBackPressed(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. //getMenuInflater().inflate(R.menu.activity_pin_entry_view, menu); return true; } }