URI: 
       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()