PageRenderTime 23ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/wallet/src/de/schildbach/wallet/ui/EncryptKeysDialogFragment.java

https://gitlab.com/yenny.prathivi/bitcoin-wallet
Java | 383 lines | 292 code | 72 blank | 19 comment | 25 complexity | 3070256b816a757050543ea04e90271a MD5 | raw file
  1. /*
  2. * Copyright 2014-2015 the original author or authors.
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. package de.schildbach.wallet.ui;
  18. import java.security.SecureRandom;
  19. import javax.annotation.Nullable;
  20. import org.bitcoinj.crypto.KeyCrypter;
  21. import org.bitcoinj.crypto.KeyCrypterException;
  22. import org.bitcoinj.crypto.KeyCrypterScrypt;
  23. import org.bitcoinj.wallet.Protos;
  24. import org.bitcoinj.wallet.Wallet;
  25. import org.slf4j.Logger;
  26. import org.slf4j.LoggerFactory;
  27. import org.spongycastle.crypto.params.KeyParameter;
  28. import android.app.Activity;
  29. import android.app.AlertDialog;
  30. import android.app.Dialog;
  31. import android.app.DialogFragment;
  32. import android.app.FragmentManager;
  33. import android.content.DialogInterface;
  34. import android.content.DialogInterface.OnShowListener;
  35. import android.graphics.Typeface;
  36. import android.os.Bundle;
  37. import android.os.Handler;
  38. import android.os.HandlerThread;
  39. import android.os.Process;
  40. import android.text.Editable;
  41. import android.text.TextWatcher;
  42. import android.view.LayoutInflater;
  43. import android.view.View;
  44. import android.view.View.OnClickListener;
  45. import android.widget.Button;
  46. import android.widget.CheckBox;
  47. import android.widget.EditText;
  48. import android.widget.TextView;
  49. import com.google.protobuf.ByteString;
  50. import de.schildbach.wallet.WalletApplication;
  51. import de.schildbach.wallet_test.R;
  52. /**
  53. * @author Andreas Schildbach
  54. */
  55. public class EncryptKeysDialogFragment extends DialogFragment
  56. {
  57. private static final int SCRYPT_ITERATIONS = 4096;
  58. private static final String FRAGMENT_TAG = EncryptKeysDialogFragment.class.getName();
  59. public static void show(final FragmentManager fm)
  60. {
  61. final DialogFragment newFragment = new EncryptKeysDialogFragment();
  62. newFragment.show(fm, FRAGMENT_TAG);
  63. }
  64. private AbstractWalletActivity activity;
  65. private WalletApplication application;
  66. private Wallet wallet;
  67. @Nullable
  68. private AlertDialog dialog;
  69. private View oldPasswordGroup;
  70. private EditText oldPasswordView;
  71. private EditText newPasswordView;
  72. private View badPasswordView;
  73. private TextView passwordStrengthView;
  74. private CheckBox showView;
  75. private Button positiveButton, negativeButton;
  76. private final Handler handler = new Handler();
  77. private HandlerThread backgroundThread;
  78. private Handler backgroundHandler;
  79. private enum State
  80. {
  81. INPUT, CRYPTING, DONE
  82. }
  83. private State state = State.INPUT;
  84. private static final Logger log = LoggerFactory.getLogger(EncryptKeysDialogFragment.class);
  85. private final TextWatcher textWatcher = new TextWatcher()
  86. {
  87. @Override
  88. public void onTextChanged(final CharSequence s, final int start, final int before, final int count)
  89. {
  90. badPasswordView.setVisibility(View.INVISIBLE);
  91. updateView();
  92. }
  93. @Override
  94. public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after)
  95. {
  96. }
  97. @Override
  98. public void afterTextChanged(final Editable s)
  99. {
  100. }
  101. };
  102. @Override
  103. public void onAttach(final Activity activity)
  104. {
  105. super.onAttach(activity);
  106. this.activity = (AbstractWalletActivity) activity;
  107. this.application = (WalletApplication) activity.getApplication();
  108. this.wallet = application.getWallet();
  109. }
  110. @Override
  111. public void onCreate(final Bundle savedInstanceState)
  112. {
  113. super.onCreate(savedInstanceState);
  114. backgroundThread = new HandlerThread("backgroundThread", Process.THREAD_PRIORITY_BACKGROUND);
  115. backgroundThread.start();
  116. backgroundHandler = new Handler(backgroundThread.getLooper());
  117. }
  118. @Override
  119. public Dialog onCreateDialog(final Bundle savedInstanceState)
  120. {
  121. final View view = LayoutInflater.from(activity).inflate(R.layout.encrypt_keys_dialog, null);
  122. oldPasswordGroup = view.findViewById(R.id.encrypt_keys_dialog_password_old_group);
  123. oldPasswordView = (EditText) view.findViewById(R.id.encrypt_keys_dialog_password_old);
  124. oldPasswordView.setText(null);
  125. newPasswordView = (EditText) view.findViewById(R.id.encrypt_keys_dialog_password_new);
  126. newPasswordView.setText(null);
  127. badPasswordView = view.findViewById(R.id.encrypt_keys_dialog_bad_password);
  128. passwordStrengthView = (TextView) view.findViewById(R.id.encrypt_keys_dialog_password_strength);
  129. showView = (CheckBox) view.findViewById(R.id.encrypt_keys_dialog_show);
  130. final DialogBuilder builder = new DialogBuilder(activity);
  131. builder.setTitle(R.string.encrypt_keys_dialog_title);
  132. builder.setView(view);
  133. builder.setPositiveButton(R.string.button_ok, null); // dummy, just to make it show
  134. builder.setNegativeButton(R.string.button_cancel, null);
  135. builder.setCancelable(false);
  136. final AlertDialog dialog = builder.create();
  137. dialog.setCanceledOnTouchOutside(false);
  138. dialog.setOnShowListener(new OnShowListener()
  139. {
  140. @Override
  141. public void onShow(final DialogInterface d)
  142. {
  143. positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
  144. negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
  145. positiveButton.setTypeface(Typeface.DEFAULT_BOLD);
  146. positiveButton.setOnClickListener(new OnClickListener()
  147. {
  148. @Override
  149. public void onClick(final View v)
  150. {
  151. handleGo();
  152. }
  153. });
  154. oldPasswordView.addTextChangedListener(textWatcher);
  155. newPasswordView.addTextChangedListener(textWatcher);
  156. showView = (CheckBox) dialog.findViewById(R.id.encrypt_keys_dialog_show);
  157. showView.setOnCheckedChangeListener(new ShowPasswordCheckListener(newPasswordView, oldPasswordView));
  158. showView.setChecked(true);
  159. EncryptKeysDialogFragment.this.dialog = dialog;
  160. updateView();
  161. }
  162. });
  163. return dialog;
  164. }
  165. @Override
  166. public void onResume()
  167. {
  168. super.onResume();
  169. updateView();
  170. }
  171. @Override
  172. public void onDismiss(final DialogInterface dialog)
  173. {
  174. this.dialog = null;
  175. oldPasswordView.removeTextChangedListener(textWatcher);
  176. newPasswordView.removeTextChangedListener(textWatcher);
  177. showView.setOnCheckedChangeListener(null);
  178. wipePasswords();
  179. super.onDismiss(dialog);
  180. }
  181. @Override
  182. public void onDestroy()
  183. {
  184. backgroundThread.getLooper().quit();
  185. super.onDestroy();
  186. }
  187. private void handleGo()
  188. {
  189. final boolean isEncrypted = wallet.isEncrypted();
  190. final String oldPassword = oldPasswordView.getText().toString().trim();
  191. final String password = newPasswordView.getText().toString().trim();
  192. state = State.CRYPTING;
  193. updateView();
  194. backgroundHandler.post(new Runnable()
  195. {
  196. @Override
  197. public void run()
  198. {
  199. final byte[] salt = new byte[KeyCrypterScrypt.SALT_LENGTH];
  200. new SecureRandom().nextBytes(salt);
  201. final KeyCrypter keyCrypter = new KeyCrypterScrypt(Protos.ScryptParameters.newBuilder().setSalt(ByteString.copyFrom(salt))
  202. .setN(SCRYPT_ITERATIONS).build());
  203. final KeyParameter oldKey = isEncrypted ? wallet.getKeyCrypter().deriveKey(oldPassword) : null;
  204. final KeyParameter newKey = password.isEmpty() ? null : keyCrypter.deriveKey(password);
  205. handler.post(new Runnable()
  206. {
  207. @Override
  208. public void run()
  209. {
  210. try
  211. {
  212. if (oldKey != null)
  213. wallet.decrypt(oldKey);
  214. if (newKey != null)
  215. wallet.encrypt(keyCrypter, newKey);
  216. application.backupWallet();
  217. state = State.DONE;
  218. updateView();
  219. log.info("spending password set or changed");
  220. delayedDismiss();
  221. }
  222. catch (final KeyCrypterException x)
  223. {
  224. badPasswordView.setVisibility(View.VISIBLE);
  225. state = State.INPUT;
  226. updateView();
  227. oldPasswordView.requestFocus();
  228. log.info("remove or change of spending password failed");
  229. }
  230. }
  231. private void delayedDismiss()
  232. {
  233. handler.postDelayed(new Runnable()
  234. {
  235. @Override
  236. public void run()
  237. {
  238. dismiss();
  239. }
  240. }, 2000);
  241. }
  242. });
  243. }
  244. });
  245. }
  246. private void wipePasswords()
  247. {
  248. oldPasswordView.setText(null);
  249. newPasswordView.setText(null);
  250. }
  251. private void updateView()
  252. {
  253. if (dialog == null)
  254. return;
  255. final boolean hasOldPassword = !oldPasswordView.getText().toString().trim().isEmpty();
  256. final boolean hasPassword = !newPasswordView.getText().toString().trim().isEmpty();
  257. oldPasswordGroup.setVisibility(wallet.isEncrypted() ? View.VISIBLE : View.GONE);
  258. oldPasswordView.setEnabled(state == State.INPUT);
  259. newPasswordView.setEnabled(state == State.INPUT);
  260. final int passwordLength = newPasswordView.getText().length();
  261. passwordStrengthView.setVisibility(state == State.INPUT && passwordLength > 0 ? View.VISIBLE : View.INVISIBLE);
  262. if (passwordLength < 4)
  263. {
  264. passwordStrengthView.setText(R.string.encrypt_keys_dialog_password_strength_weak);
  265. passwordStrengthView.setTextColor(getResources().getColor(R.color.fg_password_strength_weak));
  266. }
  267. else if (passwordLength < 6)
  268. {
  269. passwordStrengthView.setText(R.string.encrypt_keys_dialog_password_strength_fair);
  270. passwordStrengthView.setTextColor(getResources().getColor(R.color.fg_password_strength_fair));
  271. }
  272. else if (passwordLength < 8)
  273. {
  274. passwordStrengthView.setText(R.string.encrypt_keys_dialog_password_strength_good);
  275. passwordStrengthView.setTextColor(getResources().getColor(R.color.fg_less_significant));
  276. }
  277. else
  278. {
  279. passwordStrengthView.setText(R.string.encrypt_keys_dialog_password_strength_strong);
  280. passwordStrengthView.setTextColor(getResources().getColor(R.color.fg_password_strength_strong));
  281. }
  282. showView.setEnabled(state == State.INPUT);
  283. if (state == State.INPUT)
  284. {
  285. if (wallet.isEncrypted())
  286. {
  287. positiveButton.setText(hasPassword ? R.string.button_edit : R.string.button_remove);
  288. positiveButton.setEnabled(hasOldPassword);
  289. }
  290. else
  291. {
  292. positiveButton.setText(R.string.button_set);
  293. positiveButton.setEnabled(hasPassword);
  294. }
  295. negativeButton.setEnabled(true);
  296. }
  297. else if (state == State.CRYPTING)
  298. {
  299. positiveButton.setText(newPasswordView.getText().toString().trim().isEmpty() ? R.string.encrypt_keys_dialog_state_decrypting
  300. : R.string.encrypt_keys_dialog_state_encrypting);
  301. positiveButton.setEnabled(false);
  302. negativeButton.setEnabled(false);
  303. }
  304. else if (state == State.DONE)
  305. {
  306. positiveButton.setText(R.string.encrypt_keys_dialog_state_done);
  307. positiveButton.setEnabled(false);
  308. negativeButton.setEnabled(false);
  309. }
  310. }
  311. }