tpassword_dialog.py - electrum - Electrum Bitcoin wallet
HTML git clone https://git.parazyd.org/electrum
DIR Log
DIR Files
DIR Refs
DIR Submodules
---
tpassword_dialog.py (11097B)
---
1 #!/usr/bin/env python
2 #
3 # Electrum - lightweight Bitcoin client
4 # Copyright (C) 2013 ecdsa@github
5 #
6 # Permission is hereby granted, free of charge, to any person
7 # obtaining a copy of this software and associated documentation files
8 # (the "Software"), to deal in the Software without restriction,
9 # including without limitation the rights to use, copy, modify, merge,
10 # publish, distribute, sublicense, and/or sell copies of the Software,
11 # and to permit persons to whom the Software is furnished to do so,
12 # subject to the following conditions:
13 #
14 # The above copyright notice and this permission notice shall be
15 # included in all copies or substantial portions of the Software.
16 #
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 # SOFTWARE.
25
26 import re
27 import math
28 from functools import partial
29
30 from PyQt5.QtCore import Qt
31 from PyQt5.QtGui import QPixmap
32 from PyQt5.QtWidgets import QLineEdit, QLabel, QGridLayout, QVBoxLayout, QCheckBox
33
34 from electrum.i18n import _
35 from electrum.plugin import run_hook
36
37 from .util import (icon_path, WindowModalDialog, OkButton, CancelButton, Buttons,
38 PasswordLineEdit)
39
40
41 def check_password_strength(password):
42
43 '''
44 Check the strength of the password entered by the user and return back the same
45 :param password: password entered by user in New Password
46 :return: password strength Weak or Medium or Strong
47 '''
48 password = password
49 n = math.log(len(set(password)))
50 num = re.search("[0-9]", password) is not None and re.match("^[0-9]*$", password) is None
51 caps = password != password.upper() and password != password.lower()
52 extra = re.match("^[a-zA-Z0-9]*$", password) is None
53 score = len(password)*( n + caps + num + extra)/20
54 password_strength = {0:"Weak",1:"Medium",2:"Strong",3:"Very Strong"}
55 return password_strength[min(3, int(score))]
56
57
58 PW_NEW, PW_CHANGE, PW_PASSPHRASE = range(0, 3)
59
60
61 class PasswordLayout(object):
62
63 titles = [_("Enter Password"), _("Change Password"), _("Enter Passphrase")]
64
65 def __init__(self, msg, kind, OK_button, wallet=None, force_disable_encrypt_cb=False):
66 self.wallet = wallet
67
68 self.pw = PasswordLineEdit()
69 self.new_pw = PasswordLineEdit()
70 self.conf_pw = PasswordLineEdit()
71 self.kind = kind
72 self.OK_button = OK_button
73
74 vbox = QVBoxLayout()
75 label = QLabel(msg + "\n")
76 label.setWordWrap(True)
77
78 grid = QGridLayout()
79 grid.setSpacing(8)
80 grid.setColumnMinimumWidth(0, 150)
81 grid.setColumnMinimumWidth(1, 100)
82 grid.setColumnStretch(1,1)
83
84 if kind == PW_PASSPHRASE:
85 vbox.addWidget(label)
86 msgs = [_('Passphrase:'), _('Confirm Passphrase:')]
87 else:
88 logo_grid = QGridLayout()
89 logo_grid.setSpacing(8)
90 logo_grid.setColumnMinimumWidth(0, 70)
91 logo_grid.setColumnStretch(1,1)
92
93 logo = QLabel()
94 logo.setAlignment(Qt.AlignCenter)
95
96 logo_grid.addWidget(logo, 0, 0)
97 logo_grid.addWidget(label, 0, 1, 1, 2)
98 vbox.addLayout(logo_grid)
99
100 m1 = _('New Password:') if kind == PW_CHANGE else _('Password:')
101 msgs = [m1, _('Confirm Password:')]
102 if wallet and wallet.has_password():
103 grid.addWidget(QLabel(_('Current Password:')), 0, 0)
104 grid.addWidget(self.pw, 0, 1)
105 lockfile = "lock.png"
106 else:
107 lockfile = "unlock.png"
108 logo.setPixmap(QPixmap(icon_path(lockfile))
109 .scaledToWidth(36, mode=Qt.SmoothTransformation))
110
111 grid.addWidget(QLabel(msgs[0]), 1, 0)
112 grid.addWidget(self.new_pw, 1, 1)
113
114 grid.addWidget(QLabel(msgs[1]), 2, 0)
115 grid.addWidget(self.conf_pw, 2, 1)
116 vbox.addLayout(grid)
117
118 # Password Strength Label
119 if kind != PW_PASSPHRASE:
120 self.pw_strength = QLabel()
121 grid.addWidget(self.pw_strength, 3, 0, 1, 2)
122 self.new_pw.textChanged.connect(self.pw_changed)
123
124 self.encrypt_cb = QCheckBox(_('Encrypt wallet file'))
125 self.encrypt_cb.setEnabled(False)
126 grid.addWidget(self.encrypt_cb, 4, 0, 1, 2)
127 self.encrypt_cb.setVisible(kind != PW_PASSPHRASE)
128
129 def enable_OK():
130 ok = self.new_pw.text() == self.conf_pw.text()
131 OK_button.setEnabled(ok)
132 self.encrypt_cb.setEnabled(ok and bool(self.new_pw.text())
133 and not force_disable_encrypt_cb)
134 self.new_pw.textChanged.connect(enable_OK)
135 self.conf_pw.textChanged.connect(enable_OK)
136
137 self.vbox = vbox
138
139 def title(self):
140 return self.titles[self.kind]
141
142 def layout(self):
143 return self.vbox
144
145 def pw_changed(self):
146 password = self.new_pw.text()
147 if password:
148 colors = {"Weak":"Red", "Medium":"Blue", "Strong":"Green",
149 "Very Strong":"Green"}
150 strength = check_password_strength(password)
151 label = (_("Password Strength") + ": " + "<font color="
152 + colors[strength] + ">" + strength + "</font>")
153 else:
154 label = ""
155 self.pw_strength.setText(label)
156
157 def old_password(self):
158 if self.kind == PW_CHANGE:
159 return self.pw.text() or None
160 return None
161
162 def new_password(self):
163 pw = self.new_pw.text()
164 # Empty passphrases are fine and returned empty.
165 if pw == "" and self.kind != PW_PASSPHRASE:
166 pw = None
167 return pw
168
169 def clear_password_fields(self):
170 for field in [self.pw, self.new_pw, self.conf_pw]:
171 field.clear()
172
173
174 class PasswordLayoutForHW(object):
175
176 def __init__(self, msg, wallet=None):
177 self.wallet = wallet
178
179 vbox = QVBoxLayout()
180 label = QLabel(msg + "\n")
181 label.setWordWrap(True)
182
183 grid = QGridLayout()
184 grid.setSpacing(8)
185 grid.setColumnMinimumWidth(0, 150)
186 grid.setColumnMinimumWidth(1, 100)
187 grid.setColumnStretch(1,1)
188
189 logo_grid = QGridLayout()
190 logo_grid.setSpacing(8)
191 logo_grid.setColumnMinimumWidth(0, 70)
192 logo_grid.setColumnStretch(1,1)
193
194 logo = QLabel()
195 logo.setAlignment(Qt.AlignCenter)
196
197 logo_grid.addWidget(logo, 0, 0)
198 logo_grid.addWidget(label, 0, 1, 1, 2)
199 vbox.addLayout(logo_grid)
200
201 if wallet and wallet.has_storage_encryption():
202 lockfile = "lock.png"
203 else:
204 lockfile = "unlock.png"
205 logo.setPixmap(QPixmap(icon_path(lockfile))
206 .scaledToWidth(36, mode=Qt.SmoothTransformation))
207
208 vbox.addLayout(grid)
209
210 self.encrypt_cb = QCheckBox(_('Encrypt wallet file'))
211 grid.addWidget(self.encrypt_cb, 1, 0, 1, 2)
212
213 self.vbox = vbox
214
215 def title(self):
216 return _("Toggle Encryption")
217
218 def layout(self):
219 return self.vbox
220
221
222 class ChangePasswordDialogBase(WindowModalDialog):
223
224 def __init__(self, parent, wallet):
225 WindowModalDialog.__init__(self, parent)
226 is_encrypted = wallet.has_storage_encryption()
227 OK_button = OkButton(self)
228
229 self.create_password_layout(wallet, is_encrypted, OK_button)
230
231 self.setWindowTitle(self.playout.title())
232 vbox = QVBoxLayout(self)
233 vbox.addLayout(self.playout.layout())
234 vbox.addStretch(1)
235 vbox.addLayout(Buttons(CancelButton(self), OK_button))
236 self.playout.encrypt_cb.setChecked(is_encrypted)
237
238 def create_password_layout(self, wallet, is_encrypted, OK_button):
239 raise NotImplementedError()
240
241
242 class ChangePasswordDialogForSW(ChangePasswordDialogBase):
243
244 def __init__(self, parent, wallet):
245 ChangePasswordDialogBase.__init__(self, parent, wallet)
246 if not wallet.has_password():
247 self.playout.encrypt_cb.setChecked(True)
248
249 def create_password_layout(self, wallet, is_encrypted, OK_button):
250 if not wallet.has_password():
251 msg = _('Your wallet is not protected.')
252 msg += ' ' + _('Use this dialog to add a password to your wallet.')
253 else:
254 if not is_encrypted:
255 msg = _('Your bitcoins are password protected. However, your wallet file is not encrypted.')
256 else:
257 msg = _('Your wallet is password protected and encrypted.')
258 msg += ' ' + _('Use this dialog to change your password.')
259 self.playout = PasswordLayout(msg=msg,
260 kind=PW_CHANGE,
261 OK_button=OK_button,
262 wallet=wallet,
263 force_disable_encrypt_cb=not wallet.can_have_keystore_encryption())
264
265 def run(self):
266 try:
267 if not self.exec_():
268 return False, None, None, None
269 return True, self.playout.old_password(), self.playout.new_password(), self.playout.encrypt_cb.isChecked()
270 finally:
271 self.playout.clear_password_fields()
272
273
274 class ChangePasswordDialogForHW(ChangePasswordDialogBase):
275
276 def __init__(self, parent, wallet):
277 ChangePasswordDialogBase.__init__(self, parent, wallet)
278
279 def create_password_layout(self, wallet, is_encrypted, OK_button):
280 if not is_encrypted:
281 msg = _('Your wallet file is NOT encrypted.')
282 else:
283 msg = _('Your wallet file is encrypted.')
284 msg += '\n' + _('Note: If you enable this setting, you will need your hardware device to open your wallet.')
285 msg += '\n' + _('Use this dialog to toggle encryption.')
286 self.playout = PasswordLayoutForHW(msg)
287
288 def run(self):
289 if not self.exec_():
290 return False, None
291 return True, self.playout.encrypt_cb.isChecked()
292
293
294 class PasswordDialog(WindowModalDialog):
295
296 def __init__(self, parent=None, msg=None):
297 msg = msg or _('Please enter your password')
298 WindowModalDialog.__init__(self, parent, _("Enter Password"))
299 self.pw = pw = PasswordLineEdit()
300 vbox = QVBoxLayout()
301 vbox.addWidget(QLabel(msg))
302 grid = QGridLayout()
303 grid.setSpacing(8)
304 grid.addWidget(QLabel(_('Password')), 1, 0)
305 grid.addWidget(pw, 1, 1)
306 vbox.addLayout(grid)
307 vbox.addLayout(Buttons(CancelButton(self), OkButton(self)))
308 self.setLayout(vbox)
309 run_hook('password_dialog', pw, grid, 1)
310
311 def run(self):
312 try:
313 if not self.exec_():
314 return
315 return self.pw.text()
316 finally:
317 self.pw.clear()