Beispiel #1
0
 def passphrase_dialog(self, msg, confirm):
     # If confirm is true, require the user to enter the passphrase twice
     parent = self.top_level_window()
     d = WindowModalDialog(parent, _("Enter Passphrase"))
     if confirm:
         OK_button = OkButton(d)
         playout = PasswordLayout(msg=msg,
                                  kind=PW_PASSPHRASE,
                                  OK_button=OK_button)
         vbox = QVBoxLayout()
         vbox.addLayout(playout.layout())
         vbox.addLayout(Buttons(CancelButton(d), OK_button))
         d.setLayout(vbox)
         passphrase = playout.new_password() if d.exec_() else None
     else:
         pw = PasswordLineEdit()
         pw.setMinimumWidth(200)
         vbox = QVBoxLayout()
         vbox.addWidget(WWLabel(msg))
         vbox.addWidget(pw)
         vbox.addLayout(Buttons(CancelButton(d), OkButton(d)))
         d.setLayout(vbox)
         passphrase = pw.text() if d.exec_() else None
     self.passphrase = passphrase
     self.done.set()
Beispiel #2
0
    def __init__(self, parent):
        super(MatrixDialog, self).__init__(parent)
        self.setWindowTitle(_("Trezor Matrix Recovery"))
        self.num = 9
        self.loop = QEventLoop()

        vbox = QVBoxLayout(self)
        vbox.addWidget(WWLabel(MATRIX_RECOVERY))

        grid = QGridLayout()
        grid.setSpacing(0)
        self.char_buttons = []
        for y in range(3):
            for x in range(3):
                button = QPushButton('?')
                button.clicked.connect(
                    partial(self.process_key,
                            ord('1') + y * 3 + x))
                grid.addWidget(button, 3 - y, x)
                self.char_buttons.append(button)
        vbox.addLayout(grid)

        self.backspace_button = QPushButton("<=")
        self.backspace_button.clicked.connect(
            partial(self.process_key, Qt.Key_Backspace))
        self.cancel_button = QPushButton(_("Cancel"))
        self.cancel_button.clicked.connect(
            partial(self.process_key, Qt.Key_Escape))
        buttons = Buttons(self.backspace_button, self.cancel_button)
        vbox.addSpacing(40)
        vbox.addLayout(buttons)
        self.refresh()
        self.show()
Beispiel #3
0
    def cypherseed_dialog(self, window):
        self.warn_old_revealer()

        d = WindowModalDialog(window, "Encryption Dialog")
        d.setMinimumWidth(500)
        d.setMinimumHeight(210)
        d.setMaximumHeight(450)
        d.setContentsMargins(11, 11, 1, 1)
        self.c_dialog = d

        hbox = QHBoxLayout(d)
        self.vbox = QVBoxLayout()
        logo = QLabel()
        hbox.addWidget(logo)
        logo.setPixmap(QPixmap(icon_path('revealer.png')))
        logo.setAlignment(Qt.AlignLeft)
        hbox.addSpacing(16)
        self.vbox.addWidget(
            WWLabel("<b>" + _("Revealer Secret Backup Plugin") + "</b><br>" +
                    _("Ready to encrypt for revealer {}").format(
                        self.versioned_seed.version + '_' +
                        self.versioned_seed.checksum)))
        self.vbox.addSpacing(11)
        hbox.addLayout(self.vbox)
        grid = QGridLayout()
        self.vbox.addLayout(grid)

        cprint = QPushButton(_("Encrypt {}'s seed").format(self.wallet_name))
        cprint.setMaximumWidth(250)
        cprint.clicked.connect(partial(self.seed_img, True))
        self.vbox.addWidget(cprint)
        self.vbox.addSpacing(1)
        self.vbox.addWidget(
            WWLabel("<b>" + _("OR") + "</b> " +
                    _("type a custom alphanumerical secret below:")))
        self.text = ScanQRTextEdit(config=self.config)
        self.text.setTabChangesFocus(True)
        self.text.setMaximumHeight(70)
        self.text.textChanged.connect(self.customtxt_limits)
        self.vbox.addWidget(self.text)
        self.char_count = WWLabel("")
        self.char_count.setAlignment(Qt.AlignRight)
        self.vbox.addWidget(self.char_count)
        self.max_chars = WWLabel(
            "<font color='red'>" +
            _("This version supports a maximum of {} characters.").format(
                self.MAX_PLAINTEXT_LEN) + "</font>")
        self.vbox.addWidget(self.max_chars)
        self.max_chars.setVisible(False)
        self.ctext = QPushButton(_("Encrypt custom secret"))
        self.ctext.clicked.connect(self.t)
        self.vbox.addWidget(self.ctext)
        self.ctext.setEnabled(False)
        self.vbox.addSpacing(11)
        self.vbox.addLayout(Buttons(CloseButton(d)))
        return bool(d.exec_())
Beispiel #4
0
    def __init__(self, window, plugin, keystore, device_id):
        title = _("{} Settings").format(plugin.device)
        super(SettingsDialog, self).__init__(window, title)
        self.setMaximumWidth(540)

        devmgr = plugin.device_manager()
        config = devmgr.config
        handler = keystore.handler
        thread = keystore.thread
        hs_cols, hs_rows = (128, 64)

        def invoke_client(method, *args, **kw_args):
            unpair_after = kw_args.pop('unpair_after', False)

            def task():
                client = devmgr.client_by_id(device_id)
                if not client:
                    raise RuntimeError("Device not connected")
                if method:
                    getattr(client, method)(*args, **kw_args)
                if unpair_after:
                    devmgr.unpair_id(device_id)
                return client.features

            thread.add(task, on_success=update)

        def update(features):
            self.features = features
            set_label_enabled()
            if features.bootloader_hash:
                bl_hash = bh2u(features.bootloader_hash)
                bl_hash = "\n".join([bl_hash[:32], bl_hash[32:]])
            else:
                bl_hash = "N/A"
            noyes = [_("No"), _("Yes")]
            endis = [_("Enable Passphrases"), _("Disable Passphrases")]
            disen = [_("Disabled"), _("Enabled")]
            setchange = [_("Set a PIN"), _("Change PIN")]

            version = "%d.%d.%d" % (features.major_version,
                                    features.minor_version,
                                    features.patch_version)

            device_label.setText(features.label)
            pin_set_label.setText(noyes[features.pin_protection])
            passphrases_label.setText(disen[features.passphrase_protection])
            bl_hash_label.setText(bl_hash)
            label_edit.setText(features.label)
            device_id_label.setText(features.device_id)
            initialized_label.setText(noyes[features.initialized])
            version_label.setText(version)
            clear_pin_button.setVisible(features.pin_protection)
            clear_pin_warning.setVisible(features.pin_protection)
            pin_button.setText(setchange[features.pin_protection])
            pin_msg.setVisible(not features.pin_protection)
            passphrase_button.setText(endis[features.passphrase_protection])
            language_label.setText(features.language)

        def set_label_enabled():
            label_apply.setEnabled(label_edit.text() != self.features.label)

        def rename():
            invoke_client('change_label', label_edit.text())

        def toggle_passphrase():
            title = _("Confirm Toggle Passphrase Protection")
            currently_enabled = self.features.passphrase_protection
            if currently_enabled:
                msg = _("After disabling passphrases, you can only pair this "
                        "Electrum wallet if it had an empty passphrase.  "
                        "If its passphrase was not empty, you will need to "
                        "create a new wallet with the install wizard.  You "
                        "can use this wallet again at any time by re-enabling "
                        "passphrases and entering its passphrase.")
            else:
                msg = _("Your current Electrum wallet can only be used with "
                        "an empty passphrase.  You must create a separate "
                        "wallet with the install wizard for other passphrases "
                        "as each one generates a new set of addresses.")
            msg += "\n\n" + _("Are you sure you want to proceed?")
            if not self.question(msg, title=title):
                return
            invoke_client('toggle_passphrase', unpair_after=currently_enabled)

        def change_homescreen():
            filename = getOpenFileName(
                parent=self,
                title=_("Choose Homescreen"),
                config=config,
            )
            if not filename:
                return  # user cancelled

            if filename.endswith('.toif'):
                img = open(filename, 'rb').read()
                if img[:8] != b'TOIf\x90\x00\x90\x00':
                    handler.show_error(
                        'File is not a TOIF file with size of 144x144')
                    return
            else:
                from PIL import Image  # FIXME
                im = Image.open(filename)
                if im.size != (128, 64):
                    handler.show_error('Image must be 128 x 64 pixels')
                    return
                im = im.convert('1')
                pix = im.load()
                img = bytearray(1024)
                for j in range(64):
                    for i in range(128):
                        if pix[i, j]:
                            o = (i + j * 128)
                            img[o // 8] |= (1 << (7 - o % 8))
                img = bytes(img)
            invoke_client('change_homescreen', img)

        def clear_homescreen():
            invoke_client('change_homescreen', b'\x00')

        def set_pin():
            invoke_client('set_pin', remove=False)

        def clear_pin():
            invoke_client('set_pin', remove=True)

        def wipe_device():
            wallet = window.wallet
            if wallet and sum(wallet.get_balance()):
                title = _("Confirm Device Wipe")
                msg = _("Are you SURE you want to wipe the device?\n"
                        "Your wallet still has doriancoins in it!")
                if not self.question(
                        msg, title=title, icon=QMessageBox.Critical):
                    return
            invoke_client('wipe_device', unpair_after=True)

        def slider_moved():
            mins = timeout_slider.sliderPosition()
            timeout_minutes.setText(_("{:2d} minutes").format(mins))

        def slider_released():
            config.set_session_timeout(timeout_slider.sliderPosition() * 60)

        # Information tab
        info_tab = QWidget()
        info_layout = QVBoxLayout(info_tab)
        info_glayout = QGridLayout()
        info_glayout.setColumnStretch(2, 1)
        device_label = QLabel()
        pin_set_label = QLabel()
        passphrases_label = QLabel()
        version_label = QLabel()
        device_id_label = QLabel()
        bl_hash_label = QLabel()
        bl_hash_label.setWordWrap(True)
        language_label = QLabel()
        initialized_label = QLabel()
        rows = [
            (_("Device Label"), device_label),
            (_("PIN set"), pin_set_label),
            (_("Passphrases"), passphrases_label),
            (_("Firmware Version"), version_label),
            (_("Device ID"), device_id_label),
            (_("Bootloader Hash"), bl_hash_label),
            (_("Language"), language_label),
            (_("Initialized"), initialized_label),
        ]
        for row_num, (label, widget) in enumerate(rows):
            info_glayout.addWidget(QLabel(label), row_num, 0)
            info_glayout.addWidget(widget, row_num, 1)
        info_layout.addLayout(info_glayout)

        # Settings tab
        settings_tab = QWidget()
        settings_layout = QVBoxLayout(settings_tab)
        settings_glayout = QGridLayout()

        # Settings tab - Label
        label_msg = QLabel(
            _("Name this {}.  If you have multiple devices "
              "their labels help distinguish them.").format(plugin.device))
        label_msg.setWordWrap(True)
        label_label = QLabel(_("Device Label"))
        label_edit = QLineEdit()
        label_edit.setMinimumWidth(150)
        label_edit.setMaxLength(plugin.MAX_LABEL_LEN)
        label_apply = QPushButton(_("Apply"))
        label_apply.clicked.connect(rename)
        label_edit.textChanged.connect(set_label_enabled)
        settings_glayout.addWidget(label_label, 0, 0)
        settings_glayout.addWidget(label_edit, 0, 1, 1, 2)
        settings_glayout.addWidget(label_apply, 0, 3)
        settings_glayout.addWidget(label_msg, 1, 1, 1, -1)

        # Settings tab - PIN
        pin_label = QLabel(_("PIN Protection"))
        pin_button = QPushButton()
        pin_button.clicked.connect(set_pin)
        settings_glayout.addWidget(pin_label, 2, 0)
        settings_glayout.addWidget(pin_button, 2, 1)
        pin_msg = QLabel(
            _("PIN protection is strongly recommended.  "
              "A PIN is your only protection against someone "
              "stealing your doriancoins if they obtain physical "
              "access to your {}.").format(plugin.device))
        pin_msg.setWordWrap(True)
        pin_msg.setStyleSheet("color: red")
        settings_glayout.addWidget(pin_msg, 3, 1, 1, -1)

        # Settings tab - Homescreen
        homescreen_label = QLabel(_("Homescreen"))
        homescreen_change_button = QPushButton(_("Change..."))
        homescreen_clear_button = QPushButton(_("Reset"))
        homescreen_change_button.clicked.connect(change_homescreen)
        try:
            import PIL
        except ImportError:
            homescreen_change_button.setDisabled(True)
            homescreen_change_button.setToolTip(
                _("Required package 'PIL' is not available - Please install it or use the Trezor website instead."
                  ))
        homescreen_clear_button.clicked.connect(clear_homescreen)
        homescreen_msg = QLabel(
            _("You can set the homescreen on your "
              "device to personalize it.  You must "
              "choose a {} x {} monochrome black and "
              "white image.").format(hs_cols, hs_rows))
        homescreen_msg.setWordWrap(True)
        settings_glayout.addWidget(homescreen_label, 4, 0)
        settings_glayout.addWidget(homescreen_change_button, 4, 1)
        settings_glayout.addWidget(homescreen_clear_button, 4, 2)
        settings_glayout.addWidget(homescreen_msg, 5, 1, 1, -1)

        # Settings tab - Session Timeout
        timeout_label = QLabel(_("Session Timeout"))
        timeout_minutes = QLabel()
        timeout_slider = QSlider(Qt.Horizontal)
        timeout_slider.setRange(1, 60)
        timeout_slider.setSingleStep(1)
        timeout_slider.setTickInterval(5)
        timeout_slider.setTickPosition(QSlider.TicksBelow)
        timeout_slider.setTracking(True)
        timeout_msg = QLabel(
            _("Clear the session after the specified period "
              "of inactivity.  Once a session has timed out, "
              "your PIN and passphrase (if enabled) must be "
              "re-entered to use the device."))
        timeout_msg.setWordWrap(True)
        timeout_slider.setSliderPosition(config.get_session_timeout() // 60)
        slider_moved()
        timeout_slider.valueChanged.connect(slider_moved)
        timeout_slider.sliderReleased.connect(slider_released)
        settings_glayout.addWidget(timeout_label, 6, 0)
        settings_glayout.addWidget(timeout_slider, 6, 1, 1, 3)
        settings_glayout.addWidget(timeout_minutes, 6, 4)
        settings_glayout.addWidget(timeout_msg, 7, 1, 1, -1)
        settings_layout.addLayout(settings_glayout)
        settings_layout.addStretch(1)

        # Advanced tab
        advanced_tab = QWidget()
        advanced_layout = QVBoxLayout(advanced_tab)
        advanced_glayout = QGridLayout()

        # Advanced tab - clear PIN
        clear_pin_button = QPushButton(_("Disable PIN"))
        clear_pin_button.clicked.connect(clear_pin)
        clear_pin_warning = QLabel(
            _("If you disable your PIN, anyone with physical access to your "
              "{} device can spend your doriancoins.").format(plugin.device))
        clear_pin_warning.setWordWrap(True)
        clear_pin_warning.setStyleSheet("color: red")
        advanced_glayout.addWidget(clear_pin_button, 0, 2)
        advanced_glayout.addWidget(clear_pin_warning, 1, 0, 1, 5)

        # Advanced tab - toggle passphrase protection
        passphrase_button = QPushButton()
        passphrase_button.clicked.connect(toggle_passphrase)
        passphrase_msg = WWLabel(PASSPHRASE_HELP)
        passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN)
        passphrase_warning.setStyleSheet("color: red")
        advanced_glayout.addWidget(passphrase_button, 3, 2)
        advanced_glayout.addWidget(passphrase_msg, 4, 0, 1, 5)
        advanced_glayout.addWidget(passphrase_warning, 5, 0, 1, 5)

        # Advanced tab - wipe device
        wipe_device_button = QPushButton(_("Wipe Device"))
        wipe_device_button.clicked.connect(wipe_device)
        wipe_device_msg = QLabel(
            _("Wipe the device, removing all data from it.  The firmware "
              "is left unchanged."))
        wipe_device_msg.setWordWrap(True)
        wipe_device_warning = QLabel(
            _("Only wipe a device if you have the recovery seed written down "
              "and the device wallet(s) are empty, otherwise the doriancoins "
              "will be lost forever."))
        wipe_device_warning.setWordWrap(True)
        wipe_device_warning.setStyleSheet("color: red")
        advanced_glayout.addWidget(wipe_device_button, 6, 2)
        advanced_glayout.addWidget(wipe_device_msg, 7, 0, 1, 5)
        advanced_glayout.addWidget(wipe_device_warning, 8, 0, 1, 5)
        advanced_layout.addLayout(advanced_glayout)
        advanced_layout.addStretch(1)

        tabs = QTabWidget(self)
        tabs.addTab(info_tab, _("Information"))
        tabs.addTab(settings_tab, _("Settings"))
        tabs.addTab(advanced_tab, _("Advanced"))
        dialog_vbox = QVBoxLayout(self)
        dialog_vbox.addWidget(tabs)
        dialog_vbox.addLayout(Buttons(CloseButton(self)))

        # Update information
        invoke_client(None)
Beispiel #5
0
    def request_trezor_init_settings(self, wizard, method, device_id):
        vbox = QVBoxLayout()
        next_enabled = True

        devmgr = self.device_manager()
        client = devmgr.client_by_id(device_id)
        if not client:
            raise Exception(_("The device was disconnected."))
        model = client.get_trezor_model()
        fw_version = client.client.version
        capabilities = client.client.features.capabilities
        have_shamir = Capability.Shamir in capabilities

        # label
        label = QLabel(_("Enter a label to name your device:"))
        name = QLineEdit()
        hl = QHBoxLayout()
        hl.addWidget(label)
        hl.addWidget(name)
        hl.addStretch(1)
        vbox.addLayout(hl)

        # Backup type
        gb_backuptype = QGroupBox()
        hbox_backuptype = QHBoxLayout()
        gb_backuptype.setLayout(hbox_backuptype)
        vbox.addWidget(gb_backuptype)
        gb_backuptype.setTitle(_('Select backup type:'))
        bg_backuptype = QButtonGroup()

        rb_single = QRadioButton(gb_backuptype)
        rb_single.setText(_('Single seed (BIP39)'))
        bg_backuptype.addButton(rb_single)
        bg_backuptype.setId(rb_single, BackupType.Bip39)
        hbox_backuptype.addWidget(rb_single)
        rb_single.setChecked(True)

        rb_shamir = QRadioButton(gb_backuptype)
        rb_shamir.setText(_('Shamir'))
        bg_backuptype.addButton(rb_shamir)
        bg_backuptype.setId(rb_shamir, BackupType.Slip39_Basic)
        hbox_backuptype.addWidget(rb_shamir)
        rb_shamir.setEnabled(Capability.Shamir in capabilities)
        rb_shamir.setVisible(False)  # visible with "expert settings"

        rb_shamir_groups = QRadioButton(gb_backuptype)
        rb_shamir_groups.setText(_('Super Shamir'))
        bg_backuptype.addButton(rb_shamir_groups)
        bg_backuptype.setId(rb_shamir_groups, BackupType.Slip39_Advanced)
        hbox_backuptype.addWidget(rb_shamir_groups)
        rb_shamir_groups.setEnabled(Capability.ShamirGroups in capabilities)
        rb_shamir_groups.setVisible(False)  # visible with "expert settings"

        # word count
        word_count_buttons = {}

        gb_numwords = QGroupBox()
        hbox1 = QHBoxLayout()
        gb_numwords.setLayout(hbox1)
        vbox.addWidget(gb_numwords)
        gb_numwords.setTitle(_("Select seed/share length:"))
        bg_numwords = QButtonGroup()
        for count in (12, 18, 20, 24, 33):
            rb = QRadioButton(gb_numwords)
            word_count_buttons[count] = rb
            rb.setText(_("{:d} words").format(count))
            bg_numwords.addButton(rb)
            bg_numwords.setId(rb, count)
            hbox1.addWidget(rb)
            rb.setChecked(True)

        def configure_word_counts():
            if model == "1":
                checked_wordcount = 24
            else:
                checked_wordcount = 12

            if method == TIM_RECOVER:
                if have_shamir:
                    valid_word_counts = (12, 18, 20, 24, 33)
                else:
                    valid_word_counts = (12, 18, 24)
            elif rb_single.isChecked():
                valid_word_counts = (12, 18, 24)
                gb_numwords.setTitle(_('Select seed length:'))
            else:
                valid_word_counts = (20, 33)
                checked_wordcount = 20
                gb_numwords.setTitle(_('Select share length:'))

            word_count_buttons[checked_wordcount].setChecked(True)
            for c, btn in word_count_buttons.items():
                btn.setVisible(c in valid_word_counts)

        bg_backuptype.buttonClicked.connect(configure_word_counts)
        configure_word_counts()

        # set up conditional visibility:
        # 1. backup_type is only visible when creating new seed
        gb_backuptype.setVisible(method == TIM_NEW)
        # 2. word_count is not visible when recovering on TT
        if method == TIM_RECOVER and model != "1":
            gb_numwords.setVisible(False)

        # PIN
        cb_pin = QCheckBox(_('Enable PIN protection'))
        cb_pin.setChecked(True)
        vbox.addWidget(WWLabel(RECOMMEND_PIN))
        vbox.addWidget(cb_pin)

        # "expert settings" button
        expert_vbox = QVBoxLayout()
        expert_widget = QWidget()
        expert_widget.setLayout(expert_vbox)
        expert_widget.setVisible(False)
        expert_button = QPushButton(_("Show expert settings"))

        def show_expert_settings():
            expert_button.setVisible(False)
            expert_widget.setVisible(True)
            rb_shamir.setVisible(True)
            rb_shamir_groups.setVisible(True)

        expert_button.clicked.connect(show_expert_settings)
        vbox.addWidget(expert_button)

        # passphrase
        passphrase_msg = WWLabel(PASSPHRASE_HELP_SHORT)
        passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN)
        passphrase_warning.setStyleSheet("color: red")
        cb_phrase = QCheckBox(_('Enable passphrases'))
        cb_phrase.setChecked(False)
        expert_vbox.addWidget(passphrase_msg)
        expert_vbox.addWidget(passphrase_warning)
        expert_vbox.addWidget(cb_phrase)

        # ask for recovery type (random word order OR matrix)
        bg_rectype = None
        if method == TIM_RECOVER and model == '1':
            gb_rectype = QGroupBox()
            hbox_rectype = QHBoxLayout()
            gb_rectype.setLayout(hbox_rectype)
            expert_vbox.addWidget(gb_rectype)
            gb_rectype.setTitle(_("Select recovery type:"))
            bg_rectype = QButtonGroup()

            rb1 = QRadioButton(gb_rectype)
            rb1.setText(_('Scrambled words'))
            bg_rectype.addButton(rb1)
            bg_rectype.setId(rb1, RecoveryDeviceType.ScrambledWords)
            hbox_rectype.addWidget(rb1)
            rb1.setChecked(True)

            rb2 = QRadioButton(gb_rectype)
            rb2.setText(_('Matrix'))
            bg_rectype.addButton(rb2)
            bg_rectype.setId(rb2, RecoveryDeviceType.Matrix)
            hbox_rectype.addWidget(rb2)

        # no backup
        cb_no_backup = None
        if method == TIM_NEW:
            cb_no_backup = QCheckBox(f'''{_('Enable seedless mode')}''')
            cb_no_backup.setChecked(False)
            if (model == '1' and fw_version >= (1, 7, 1)
                    or model == 'T' and fw_version >= (2, 0, 9)):
                cb_no_backup.setToolTip(SEEDLESS_MODE_WARNING)
            else:
                cb_no_backup.setEnabled(False)
                cb_no_backup.setToolTip(_('Firmware version too old.'))
            expert_vbox.addWidget(cb_no_backup)

        vbox.addWidget(expert_widget)
        wizard.exec_layout(vbox, next_enabled=next_enabled)

        return TrezorInitSettings(
            word_count=bg_numwords.checkedId(),
            label=name.text(),
            pin_enabled=cb_pin.isChecked(),
            passphrase_enabled=cb_phrase.isChecked(),
            recovery_type=bg_rectype.checkedId() if bg_rectype else None,
            backup_type=bg_backuptype.checkedId(),
            no_backup=cb_no_backup.isChecked() if cb_no_backup else False,
        )
Beispiel #6
0
    def setup_dialog(self, window):
        self.wallet = window.parent().wallet
        self.update_wallet_name(self.wallet)
        self.user_input = False

        self.d = WindowModalDialog(window, "Setup Dialog")
        self.d.setMinimumWidth(500)
        self.d.setMinimumHeight(210)
        self.d.setMaximumHeight(320)
        self.d.setContentsMargins(11, 11, 1, 1)

        self.hbox = QHBoxLayout(self.d)
        vbox = QVBoxLayout()
        logo = QLabel()
        self.hbox.addWidget(logo)
        logo.setPixmap(QPixmap(icon_path('revealer.png')))
        logo.setAlignment(Qt.AlignLeft)
        self.hbox.addSpacing(16)
        vbox.addWidget(
            WWLabel(
                "<b>" + _("Revealer Secret Backup Plugin") + "</b><br>" +
                _("To encrypt your backup, first we need to load some noise.")
                + "<br/>"))
        vbox.addSpacing(7)
        bcreate = QPushButton(_("Create a new Revealer"))
        bcreate.setMaximumWidth(181)
        bcreate.setDefault(True)
        vbox.addWidget(bcreate, Qt.AlignCenter)
        self.load_noise = ScanQRTextEdit(config=self.config)
        self.load_noise.setTabChangesFocus(True)
        self.load_noise.textChanged.connect(self.on_edit)
        self.load_noise.setMaximumHeight(33)
        self.hbox.addLayout(vbox)
        vbox.addWidget(
            WWLabel(
                _("or type an existing revealer code below and click 'next':"))
        )
        vbox.addWidget(self.load_noise)
        vbox.addSpacing(3)
        self.next_button = QPushButton(_("Next"), self.d)
        self.next_button.setEnabled(False)
        vbox.addLayout(Buttons(self.next_button))
        self.next_button.clicked.connect(self.d.close)
        self.next_button.clicked.connect(
            partial(self.cypherseed_dialog, window))
        vbox.addWidget(
            QLabel("<b>" + _("Warning") + "</b>: " + _(
                "Each revealer should be used only once."
            ) + "<br>" + _(
                "more information at <a href=\"https://revealer.cc/faq\">https://revealer.cc/faq</a>"
            )))

        def mk_digital():
            try:
                self.make_digital(self.d)
            except Exception:
                self.logger.exception('')
            else:
                self.cypherseed_dialog(window)

        bcreate.clicked.connect(mk_digital)
        return bool(self.d.exec_())
Beispiel #7
0
class Plugin(RevealerPlugin):

    MAX_PLAINTEXT_LEN = 189  # chars

    def __init__(self, parent, config, name):
        RevealerPlugin.__init__(self, parent, config, name)
        self.base_dir = os.path.join(config.electrum_path(), 'revealer')

        if self.config.get('calibration_h') is None:
            self.config.set_key('calibration_h', 0)
        if self.config.get('calibration_v') is None:
            self.config.set_key('calibration_v', 0)

        self.calibration_h = self.config.get('calibration_h')
        self.calibration_v = self.config.get('calibration_v')

        self.f_size = QSize(1014 * 2, 642 * 2)
        self.abstand_h = 21
        self.abstand_v = 34
        self.calibration_noise = int('10' * 128)
        self.rawnoise = False
        make_dir(self.base_dir)

        self.extension = False

    @hook
    def create_status_bar(self, parent):
        b = StatusBarButton(read_QIcon('revealer.png'),
                            "Revealer " + _("secret backup utility"),
                            partial(self.setup_dialog, parent))
        parent.addPermanentWidget(b)

    def requires_settings(self):
        return True

    def settings_widget(self, window):
        return EnterButton(_('Printer Calibration'),
                           partial(self.calibration_dialog, window))

    def password_dialog(self, msg=None, parent=None):
        from electrum_dsv.gui.qt.password_dialog import PasswordDialog
        parent = parent or self
        d = PasswordDialog(parent, msg)
        return d.run()

    def get_seed(self):
        password = None
        if self.wallet.has_keystore_encryption():
            password = self.password_dialog(parent=self.d.parent())
            if not password:
                raise UserCancelled()

        keystore = self.wallet.get_keystore()
        if not keystore or not keystore.has_seed():
            return
        self.extension = bool(keystore.get_passphrase(password))
        return keystore.get_seed(password)

    def setup_dialog(self, window):
        self.wallet = window.parent().wallet
        self.update_wallet_name(self.wallet)
        self.user_input = False

        self.d = WindowModalDialog(window, "Setup Dialog")
        self.d.setMinimumWidth(500)
        self.d.setMinimumHeight(210)
        self.d.setMaximumHeight(320)
        self.d.setContentsMargins(11, 11, 1, 1)

        self.hbox = QHBoxLayout(self.d)
        vbox = QVBoxLayout()
        logo = QLabel()
        self.hbox.addWidget(logo)
        logo.setPixmap(QPixmap(icon_path('revealer.png')))
        logo.setAlignment(Qt.AlignLeft)
        self.hbox.addSpacing(16)
        vbox.addWidget(
            WWLabel(
                "<b>" + _("Revealer Secret Backup Plugin") + "</b><br>" +
                _("To encrypt your backup, first we need to load some noise.")
                + "<br/>"))
        vbox.addSpacing(7)
        bcreate = QPushButton(_("Create a new Revealer"))
        bcreate.setMaximumWidth(181)
        bcreate.setDefault(True)
        vbox.addWidget(bcreate, Qt.AlignCenter)
        self.load_noise = ScanQRTextEdit(config=self.config)
        self.load_noise.setTabChangesFocus(True)
        self.load_noise.textChanged.connect(self.on_edit)
        self.load_noise.setMaximumHeight(33)
        self.hbox.addLayout(vbox)
        vbox.addWidget(
            WWLabel(
                _("or type an existing revealer code below and click 'next':"))
        )
        vbox.addWidget(self.load_noise)
        vbox.addSpacing(3)
        self.next_button = QPushButton(_("Next"), self.d)
        self.next_button.setEnabled(False)
        vbox.addLayout(Buttons(self.next_button))
        self.next_button.clicked.connect(self.d.close)
        self.next_button.clicked.connect(
            partial(self.cypherseed_dialog, window))
        vbox.addWidget(
            QLabel("<b>" + _("Warning") + "</b>: " + _(
                "Each revealer should be used only once."
            ) + "<br>" + _(
                "more information at <a href=\"https://revealer.cc/faq\">https://revealer.cc/faq</a>"
            )))

        def mk_digital():
            try:
                self.make_digital(self.d)
            except Exception:
                self.logger.exception('')
            else:
                self.cypherseed_dialog(window)

        bcreate.clicked.connect(mk_digital)
        return bool(self.d.exec_())

    def get_noise(self):
        text = self.load_noise.text()
        return ''.join(text.split()).lower()

    def on_edit(self):
        txt = self.get_noise()
        versioned_seed = self.get_versioned_seed_from_user_input(txt)
        if versioned_seed:
            self.versioned_seed = versioned_seed
        self.user_input = bool(versioned_seed)
        self.next_button.setEnabled(bool(versioned_seed))

    def make_digital(self, dialog):
        self.make_rawnoise(True)
        self.bdone(dialog)
        self.d.close()

    def get_path_to_revealer_file(self, ext: str = '') -> str:
        version = self.versioned_seed.version
        code_id = self.versioned_seed.checksum
        filename = self.filename_prefix + version + "_" + code_id + ext
        path = os.path.join(self.base_dir, filename)
        return os.path.normcase(os.path.abspath(path))

    def get_path_to_calibration_file(self):
        path = os.path.join(self.base_dir, 'calibration.pdf')
        return os.path.normcase(os.path.abspath(path))

    def bcrypt(self, dialog):
        self.rawnoise = False
        version = self.versioned_seed.version
        code_id = self.versioned_seed.checksum
        dialog.show_message(''.join([
            _("{} encrypted for Revealer {}_{} saved as PNG and PDF at: ").
            format(self.was, version, code_id), "<b>",
            self.get_path_to_revealer_file(), "</b>", "<br/>", "<br/>", "<b>",
            _("Always check your backups.")
        ]),
                            rich_text=True)
        dialog.close()

    def ext_warning(self, dialog):
        dialog.show_message(''.join([
            "<b>",
            _("Warning"), ": </b>",
            _("your seed extension will <b>not</b> be included in the encrypted backup."
              )
        ]),
                            rich_text=True)
        dialog.close()

    def bdone(self, dialog):
        version = self.versioned_seed.version
        code_id = self.versioned_seed.checksum
        dialog.show_message(''.join([
            _("Digital Revealer ({}_{}) saved as PNG and PDF at:").format(
                version, code_id), "<br/>", "<b>",
            self.get_path_to_revealer_file(), '</b>'
        ]),
                            rich_text=True)

    def customtxt_limits(self):
        txt = self.text.text()
        self.max_chars.setVisible(False)
        self.char_count.setText(f"({len(txt)}/{self.MAX_PLAINTEXT_LEN})")
        if len(txt) > 0:
            self.ctext.setEnabled(True)
        if len(txt) > self.MAX_PLAINTEXT_LEN:
            self.text.setPlainText(txt[:self.MAX_PLAINTEXT_LEN])
            self.max_chars.setVisible(True)

    def t(self):
        self.txt = self.text.text()
        self.seed_img(is_seed=False)

    def warn_old_revealer(self):
        if self.versioned_seed.version == '0':
            link = "https://revealer.cc/revealer-warning-and-upgrade/"
            self.d.show_warning((
                "<b>{warning}: </b>{ver0}<br>"
                "{url}<br>"
                "{risk}"
            ).format(
                warning=_("Warning"),
                ver0=
                _("Revealers starting with 0 are not secure due to a vulnerability."
                  ),
                url=_("More info at: {}").format(
                    f'<a href="{link}">{link}</a>'),
                risk=_("Proceed at your own risk.")),
                                rich_text=True)

    def cypherseed_dialog(self, window):
        self.warn_old_revealer()

        d = WindowModalDialog(window, "Encryption Dialog")
        d.setMinimumWidth(500)
        d.setMinimumHeight(210)
        d.setMaximumHeight(450)
        d.setContentsMargins(11, 11, 1, 1)
        self.c_dialog = d

        hbox = QHBoxLayout(d)
        self.vbox = QVBoxLayout()
        logo = QLabel()
        hbox.addWidget(logo)
        logo.setPixmap(QPixmap(icon_path('revealer.png')))
        logo.setAlignment(Qt.AlignLeft)
        hbox.addSpacing(16)
        self.vbox.addWidget(
            WWLabel("<b>" + _("Revealer Secret Backup Plugin") + "</b><br>" +
                    _("Ready to encrypt for revealer {}").format(
                        self.versioned_seed.version + '_' +
                        self.versioned_seed.checksum)))
        self.vbox.addSpacing(11)
        hbox.addLayout(self.vbox)
        grid = QGridLayout()
        self.vbox.addLayout(grid)

        cprint = QPushButton(_("Encrypt {}'s seed").format(self.wallet_name))
        cprint.setMaximumWidth(250)
        cprint.clicked.connect(partial(self.seed_img, True))
        self.vbox.addWidget(cprint)
        self.vbox.addSpacing(1)
        self.vbox.addWidget(
            WWLabel("<b>" + _("OR") + "</b> " +
                    _("type a custom alphanumerical secret below:")))
        self.text = ScanQRTextEdit(config=self.config)
        self.text.setTabChangesFocus(True)
        self.text.setMaximumHeight(70)
        self.text.textChanged.connect(self.customtxt_limits)
        self.vbox.addWidget(self.text)
        self.char_count = WWLabel("")
        self.char_count.setAlignment(Qt.AlignRight)
        self.vbox.addWidget(self.char_count)
        self.max_chars = WWLabel(
            "<font color='red'>" +
            _("This version supports a maximum of {} characters.").format(
                self.MAX_PLAINTEXT_LEN) + "</font>")
        self.vbox.addWidget(self.max_chars)
        self.max_chars.setVisible(False)
        self.ctext = QPushButton(_("Encrypt custom secret"))
        self.ctext.clicked.connect(self.t)
        self.vbox.addWidget(self.ctext)
        self.ctext.setEnabled(False)
        self.vbox.addSpacing(11)
        self.vbox.addLayout(Buttons(CloseButton(d)))
        return bool(d.exec_())

    def update_wallet_name(self, name):
        self.wallet_name = str(name)

    def seed_img(self, is_seed=True):

        if is_seed:
            try:
                cseed = self.get_seed()
            except UserCancelled:
                return
            except InvalidPassword as e:
                self.d.show_error(str(e))
                return
            if not cseed:
                self.d.show_message(_("This wallet has no seed"))
                return
            txt = cseed.upper()
        else:
            txt = self.txt.upper()

        img = QImage(self.SIZE[0], self.SIZE[1], QImage.Format_Mono)
        bitmap = QBitmap.fromImage(img, Qt.MonoOnly)
        bitmap.fill(Qt.white)
        painter = QPainter()
        painter.begin(bitmap)
        QFontDatabase.addApplicationFont(
            os.path.join(os.path.dirname(__file__), 'SourceSansPro-Bold.otf'))
        if len(txt) < 102:
            fontsize = 15
            linespace = 15
            max_letters = 17
            max_lines = 6
            max_words = 3
        else:
            fontsize = 12
            linespace = 10
            max_letters = 21
            max_lines = 9
            max_words = int(max_letters / 4)

        font = QFont('Source Sans Pro', fontsize, QFont.Bold)
        font.setLetterSpacing(QFont.PercentageSpacing, 100)
        font.setPixelSize(fontsize)
        painter.setFont(font)
        seed_array = txt.split(' ')

        for n in range(max_lines):
            nwords = max_words
            temp_seed = seed_array[:nwords]
            while len(' '.join(map(str, temp_seed))) > max_letters:
                nwords = nwords - 1
                temp_seed = seed_array[:nwords]
            painter.drawText(
                QRect(0, linespace * n, self.SIZE[0], self.SIZE[1]),
                Qt.AlignHCenter, ' '.join(map(str, temp_seed)))
            del seed_array[:nwords]

        painter.end()
        img = bitmap.toImage()
        if (self.rawnoise == False):
            self.make_rawnoise()

        self.make_cypherseed(img, self.rawnoise, False, is_seed)
        return img

    def make_rawnoise(self, create_revealer=False):
        if not self.user_input:
            self.versioned_seed = self.gen_random_versioned_seed()
        assert self.versioned_seed
        w, h = self.SIZE
        rawnoise = QImage(w, h, QImage.Format_Mono)

        noise_map = self.get_noise_map(self.versioned_seed)
        for (x, y), pixel in noise_map.items():
            rawnoise.setPixel(x, y, pixel)

        self.rawnoise = rawnoise
        if create_revealer:
            self.make_revealer()

    def make_calnoise(self):
        random.seed(self.calibration_noise)
        w, h = self.SIZE
        rawnoise = QImage(w, h, QImage.Format_Mono)
        for x in range(w):
            for y in range(h):
                rawnoise.setPixel(x, y, random.randint(0, 1))
        self.calnoise = self.pixelcode_2x2(rawnoise)

    def make_revealer(self):
        revealer = self.pixelcode_2x2(self.rawnoise)
        revealer.invertPixels()
        revealer = QBitmap.fromImage(revealer)
        revealer = revealer.scaled(self.f_size, Qt.KeepAspectRatio)
        revealer = self.overlay_marks(revealer)

        self.filename_prefix = 'revealer_'
        revealer.save(self.get_path_to_revealer_file('.png'))
        self.toPdf(QImage(revealer))
        QDesktopServices.openUrl(
            QUrl.fromLocalFile(self.get_path_to_revealer_file('.pdf')))

    def make_cypherseed(self, img, rawnoise, calibration=False, is_seed=True):
        img = img.convertToFormat(QImage.Format_Mono)
        p = QPainter()
        p.begin(img)
        p.setCompositionMode(26)  #xor
        p.drawImage(0, 0, rawnoise)
        p.end()
        cypherseed = self.pixelcode_2x2(img)
        cypherseed = QBitmap.fromImage(cypherseed)
        cypherseed = cypherseed.scaled(self.f_size, Qt.KeepAspectRatio)
        cypherseed = self.overlay_marks(cypherseed, True, calibration)

        if not is_seed:
            self.filename_prefix = 'custom_secret_'
            self.was = _('Custom secret')
        else:
            self.filename_prefix = self.wallet_name + '_seed_'
            self.was = self.wallet_name + ' ' + _('seed')
            if self.extension:
                self.ext_warning(self.c_dialog)

        if not calibration:
            self.toPdf(QImage(cypherseed))
            QDesktopServices.openUrl(
                QUrl.fromLocalFile(self.get_path_to_revealer_file('.pdf')))
            cypherseed.save(self.get_path_to_revealer_file('.png'))
            self.bcrypt(self.c_dialog)
        return cypherseed

    def calibration(self):
        img = QImage(self.SIZE[0], self.SIZE[1], QImage.Format_Mono)
        bitmap = QBitmap.fromImage(img, Qt.MonoOnly)
        bitmap.fill(Qt.black)
        self.make_calnoise()
        img = self.overlay_marks(
            self.calnoise.scaledToHeight(self.f_size.height()), False, True)
        self.calibration_pdf(img)
        QDesktopServices.openUrl(
            QUrl.fromLocalFile(self.get_path_to_calibration_file()))
        return img

    def toPdf(self, image):
        printer = QPrinter()
        printer.setPaperSize(QSizeF(210, 297), QPrinter.Millimeter)
        printer.setResolution(600)
        printer.setOutputFormat(QPrinter.PdfFormat)
        printer.setOutputFileName(self.get_path_to_revealer_file('.pdf'))
        printer.setPageMargins(0, 0, 0, 0, 6)
        painter = QPainter()
        painter.begin(printer)

        delta_h = round(image.width() / self.abstand_v)
        delta_v = round(image.height() / self.abstand_h)

        size_h = 2028 + ((int(self.calibration_h) * 2028 /
                          (2028 -
                           (delta_h * 2) + int(self.calibration_h))) / 2)
        size_v = 1284 + ((int(self.calibration_v) * 1284 /
                          (1284 -
                           (delta_v * 2) + int(self.calibration_v))) / 2)

        image = image.scaled(size_h, size_v)

        painter.drawImage(553, 533, image)
        wpath = QPainterPath()
        wpath.addRoundedRect(QRectF(553, 533, size_h, size_v), 19, 19)
        painter.setPen(QPen(Qt.black, 1))
        painter.drawPath(wpath)
        painter.end()

    def calibration_pdf(self, image):
        printer = QPrinter()
        printer.setPaperSize(QSizeF(210, 297), QPrinter.Millimeter)
        printer.setResolution(600)
        printer.setOutputFormat(QPrinter.PdfFormat)
        printer.setOutputFileName(self.get_path_to_calibration_file())
        printer.setPageMargins(0, 0, 0, 0, 6)

        painter = QPainter()
        painter.begin(printer)
        painter.drawImage(553, 533, image)
        font = QFont('Source Sans Pro', 10, QFont.Bold)
        painter.setFont(font)
        painter.drawText(254, 277, _("Calibration sheet"))
        font = QFont('Source Sans Pro', 7, QFont.Bold)
        painter.setFont(font)
        painter.drawText(600, 2077, _("Instructions:"))
        font = QFont('Source Sans Pro', 7, QFont.Normal)
        painter.setFont(font)
        painter.drawText(
            700, 2177,
            _("1. Place this paper on a flat and well iluminated surface."))
        painter.drawText(
            700, 2277,
            _("2. Align your Revealer borderlines to the dashed lines on the top and left."
              ))
        painter.drawText(
            700, 2377,
            _("3. Press slightly the Revealer against the paper and read the numbers that best "
              "match on the opposite sides. "))
        painter.drawText(700, 2477, _("4. Type the numbers in the software"))
        painter.end()

    def pixelcode_2x2(self, img):
        result = QImage(img.width() * 2,
                        img.height() * 2, QImage.Format_ARGB32)
        white = qRgba(255, 255, 255, 0)
        black = qRgba(0, 0, 0, 255)

        for x in range(img.width()):
            for y in range(img.height()):
                c = img.pixel(QPoint(x, y))
                colors = QColor(c).getRgbF()
                if colors[0]:
                    result.setPixel(x * 2 + 1, y * 2 + 1, black)
                    result.setPixel(x * 2, y * 2 + 1, white)
                    result.setPixel(x * 2 + 1, y * 2, white)
                    result.setPixel(x * 2, y * 2, black)

                else:
                    result.setPixel(x * 2 + 1, y * 2 + 1, white)
                    result.setPixel(x * 2, y * 2 + 1, black)
                    result.setPixel(x * 2 + 1, y * 2, black)
                    result.setPixel(x * 2, y * 2, white)
        return result

    def overlay_marks(self, img, is_cseed=False, calibration_sheet=False):
        border_color = Qt.white
        base_img = QImage(self.f_size.width(), self.f_size.height(),
                          QImage.Format_ARGB32)
        base_img.fill(border_color)
        img = QImage(img)

        painter = QPainter()
        painter.begin(base_img)

        total_distance_h = round(base_img.width() / self.abstand_v)
        dist_v = round(total_distance_h) / 2
        dist_h = round(total_distance_h) / 2

        img = img.scaledToWidth(base_img.width() - (2 * (total_distance_h)))
        painter.drawImage(total_distance_h, total_distance_h, img)

        #frame around image
        pen = QPen(Qt.black, 2)
        painter.setPen(pen)

        #horz
        painter.drawLine(0, total_distance_h, base_img.width(),
                         total_distance_h)
        painter.drawLine(0,
                         base_img.height() - (total_distance_h),
                         base_img.width(),
                         base_img.height() - (total_distance_h))
        #vert
        painter.drawLine(total_distance_h, 0, total_distance_h,
                         base_img.height())
        painter.drawLine(base_img.width() - (total_distance_h), 0,
                         base_img.width() - (total_distance_h),
                         base_img.height())

        #border around img
        border_thick = 6
        Rpath = QPainterPath()
        Rpath.addRect(
            QRectF((total_distance_h) + (border_thick / 2),
                   (total_distance_h) + (border_thick / 2),
                   base_img.width() - ((total_distance_h) * 2) -
                   ((border_thick) - 1),
                   (base_img.height() -
                    ((total_distance_h)) * 2) - ((border_thick) - 1)))
        pen = QPen(Qt.black, border_thick)
        pen.setJoinStyle(Qt.MiterJoin)

        painter.setPen(pen)
        painter.drawPath(Rpath)

        Bpath = QPainterPath()
        Bpath.addRect(
            QRectF((total_distance_h), (total_distance_h),
                   base_img.width() - ((total_distance_h) * 2),
                   (base_img.height() - ((total_distance_h)) * 2)))
        pen = QPen(Qt.black, 1)
        painter.setPen(pen)
        painter.drawPath(Bpath)

        pen = QPen(Qt.black, 1)
        painter.setPen(pen)
        painter.drawLine(0,
                         base_img.height() / 2, total_distance_h,
                         base_img.height() / 2)
        painter.drawLine(base_img.width() / 2, 0,
                         base_img.width() / 2, total_distance_h)

        painter.drawLine(base_img.width() - total_distance_h,
                         base_img.height() / 2, base_img.width(),
                         base_img.height() / 2)
        painter.drawLine(base_img.width() / 2, base_img.height(),
                         base_img.width() / 2,
                         base_img.height() - total_distance_h)

        #print code
        f_size = 37
        QFontDatabase.addApplicationFont(
            os.path.join(os.path.dirname(__file__), 'DejaVuSansMono-Bold.ttf'))
        font = QFont("DejaVu Sans Mono", f_size - 11, QFont.Bold)
        font.setPixelSize(35)
        painter.setFont(font)

        if not calibration_sheet:
            if is_cseed:  #its a secret
                painter.setPen(QPen(Qt.black, 1, Qt.DashDotDotLine))
                painter.drawLine(0, dist_v, base_img.width(), dist_v)
                painter.drawLine(dist_h, 0, dist_h, base_img.height())
                painter.drawLine(0,
                                 base_img.height() - dist_v, base_img.width(),
                                 base_img.height() - (dist_v))
                painter.drawLine(base_img.width() - (dist_h), 0,
                                 base_img.width() - (dist_h),
                                 base_img.height())

                painter.drawImage(
                    ((total_distance_h)) + 11, ((total_distance_h)) + 11,
                    QImage(icon_path('electrumb.png')).scaledToWidth(
                        2.1 * (total_distance_h), Qt.SmoothTransformation))

                painter.setPen(QPen(Qt.white, border_thick * 8))
                painter.drawLine(
                    base_img.width() - ((total_distance_h)) -
                    (border_thick * 8) / 2 - (border_thick / 2) - 2,
                    (base_img.height() - ((total_distance_h))) -
                    ((border_thick * 8) / 2) - (border_thick / 2) - 2,
                    base_img.width() - ((total_distance_h)) -
                    (border_thick * 8) / 2 - (border_thick / 2) - 2 - 77,
                    (base_img.height() - ((total_distance_h))) -
                    ((border_thick * 8) / 2) - (border_thick / 2) - 2)
                painter.setPen(QColor(0, 0, 0, 255))
                painter.drawText(
                    QRect(
                        0,
                        base_img.height() - 107,
                        base_img.width() - total_distance_h - border_thick -
                        11,
                        base_img.height() - total_distance_h - border_thick),
                    Qt.AlignRight, self.versioned_seed.version + '_' +
                    self.versioned_seed.checksum)
                painter.end()

            else:  # revealer

                painter.setPen(QPen(border_color, 17))
                painter.drawLine(0, dist_v, base_img.width(), dist_v)
                painter.drawLine(dist_h, 0, dist_h, base_img.height())
                painter.drawLine(0,
                                 base_img.height() - dist_v, base_img.width(),
                                 base_img.height() - (dist_v))
                painter.drawLine(base_img.width() - (dist_h), 0,
                                 base_img.width() - (dist_h),
                                 base_img.height())

                painter.setPen(QPen(Qt.black, 2))
                painter.drawLine(0, dist_v, base_img.width(), dist_v)
                painter.drawLine(dist_h, 0, dist_h, base_img.height())
                painter.drawLine(0,
                                 base_img.height() - dist_v, base_img.width(),
                                 base_img.height() - (dist_v))
                painter.drawLine(base_img.width() - (dist_h), 0,
                                 base_img.width() - (dist_h),
                                 base_img.height())
                logo = QImage(icon_path('revealer_c.png')).scaledToWidth(
                    1.3 * (total_distance_h))
                painter.drawImage((total_distance_h) + (border_thick),
                                  ((total_distance_h)) + (border_thick), logo,
                                  Qt.SmoothTransformation)

                #frame around logo
                painter.setPen(QPen(Qt.black, border_thick))
                painter.drawLine(
                    total_distance_h + border_thick,
                    total_distance_h + logo.height() + 3 * (border_thick / 2),
                    total_distance_h + logo.width() + border_thick,
                    total_distance_h + logo.height() + 3 * (border_thick / 2))
                painter.drawLine(
                    logo.width() + total_distance_h + 3 * (border_thick / 2),
                    total_distance_h + (border_thick),
                    total_distance_h + logo.width() + 3 * (border_thick / 2),
                    total_distance_h + logo.height() + (border_thick))

                #frame around code/qr
                qr_size = 179

                painter.drawLine((base_img.width() - ((total_distance_h)) -
                                  (border_thick / 2) - 2) - qr_size,
                                 (base_img.height() - ((total_distance_h))) -
                                 ((border_thick * 8)) - (border_thick / 2) - 2,
                                 (base_img.width() / 2 +
                                  (total_distance_h / 2) - border_thick -
                                  (border_thick * 8) / 2) - qr_size,
                                 (base_img.height() - ((total_distance_h))) -
                                 ((border_thick * 8)) - (border_thick / 2) - 2)

                painter.drawLine(
                    (base_img.width() / 2 +
                     (total_distance_h / 2) - border_thick -
                     (border_thick * 8) / 2) - qr_size,
                    (base_img.height() - ((total_distance_h))) -
                    ((border_thick * 8)) - (border_thick / 2) - 2,
                    base_img.width() / 2 + (total_distance_h / 2) -
                    border_thick - (border_thick * 8) / 2 - qr_size,
                    ((base_img.height() - ((total_distance_h))) -
                     (border_thick / 2) - 2))

                painter.setPen(QPen(Qt.white, border_thick * 8))
                painter.drawLine(
                    base_img.width() - ((total_distance_h)) -
                    (border_thick * 8) / 2 - (border_thick / 2) - 2,
                    (base_img.height() - ((total_distance_h))) -
                    ((border_thick * 8) / 2) - (border_thick / 2) - 2,
                    base_img.width() / 2 + (total_distance_h / 2) -
                    border_thick - qr_size,
                    (base_img.height() - ((total_distance_h))) -
                    ((border_thick * 8) / 2) - (border_thick / 2) - 2)

                painter.setPen(QColor(0, 0, 0, 255))
                painter.drawText(
                    QRect(((base_img.width() / 2) + 21) - qr_size,
                          base_img.height() - 107,
                          base_img.width() - total_distance_h - border_thick -
                          93,
                          base_img.height() - total_distance_h - border_thick),
                    Qt.AlignLeft,
                    self.versioned_seed.get_ui_string_version_plus_seed())
                painter.drawText(
                    QRect(
                        0,
                        base_img.height() - 107,
                        base_img.width() - total_distance_h - border_thick -
                        3 - qr_size,
                        base_img.height() - total_distance_h - border_thick),
                    Qt.AlignRight, self.versioned_seed.checksum)

                # draw qr code
                qr_qt = self.paintQR(
                    self.versioned_seed.get_ui_string_version_plus_seed() +
                    self.versioned_seed.checksum)
                target = QRectF(base_img.width() - 65 - qr_size,
                                base_img.height() - 65 - qr_size, qr_size,
                                qr_size)
                painter.drawImage(target, qr_qt)
                painter.setPen(QPen(Qt.black, 4))
                painter.drawLine(base_img.width() - 65 - qr_size,
                                 base_img.height() - 65 - qr_size,
                                 base_img.width() - 65 - qr_size,
                                 (base_img.height() - ((total_distance_h))) -
                                 ((border_thick * 8)) - (border_thick / 2) - 4)
                painter.drawLine(base_img.width() - 65 - qr_size,
                                 base_img.height() - 65 - qr_size,
                                 base_img.width() - 65,
                                 base_img.height() - 65 - qr_size)
                painter.end()

        else:  # calibration only
            painter.end()
            cal_img = QImage(self.f_size.width() + 100,
                             self.f_size.height() + 100, QImage.Format_ARGB32)
            cal_img.fill(Qt.white)

            cal_painter = QPainter()
            cal_painter.begin(cal_img)
            cal_painter.drawImage(0, 0, base_img)

            #black lines in the middle of border top left only
            cal_painter.setPen(QPen(Qt.black, 1, Qt.DashDotDotLine))
            cal_painter.drawLine(0, dist_v, base_img.width(), dist_v)
            cal_painter.drawLine(dist_h, 0, dist_h, base_img.height())

            pen = QPen(Qt.black, 2, Qt.DashDotDotLine)
            cal_painter.setPen(pen)
            n = 15

            cal_painter.setFont(QFont("DejaVu Sans Mono", 21, QFont.Bold))
            for x in range(-n, n):
                #lines on bottom (vertical calibration)
                cal_painter.drawLine((((base_img.width()) / (n * 2)) *
                                      (x)) + (base_img.width() / 2) - 13,
                                     x + 2 + base_img.height() - (dist_v),
                                     (((base_img.width()) / (n * 2)) *
                                      (x)) + (base_img.width() / 2) + 13,
                                     x + 2 + base_img.height() - (dist_v))

                num_pos = 9
                if x > 9: num_pos = 17
                if x < 0: num_pos = 20
                if x < -9: num_pos = 27

                cal_painter.drawText((((base_img.width()) / (n * 2)) *
                                      (x)) + (base_img.width() / 2) - num_pos,
                                     50 + base_img.height() - (dist_v), str(x))

                #lines on the right (horizontal calibrations)

                cal_painter.drawLine(
                    x + 2 + (base_img.width() - (dist_h)),
                    ((base_img.height() / (2 * n)) * (x)) +
                    (base_img.height() / n) + (base_img.height() / 2) - 13,
                    x + 2 + (base_img.width() - (dist_h)),
                    ((base_img.height() / (2 * n)) * (x)) +
                    (base_img.height() / n) + (base_img.height() / 2) + 13)

                cal_painter.drawText(30 + (base_img.width() - (dist_h)),
                                     ((base_img.height() / (2 * n)) *
                                      (x)) + (base_img.height() / 2) + 13,
                                     str(x))

            cal_painter.end()
            base_img = cal_img

        return base_img

    def paintQR(self, data):
        if not data:
            return
        qr = qrcode.QRCode()
        qr.add_data(data)
        matrix = qr.get_matrix()
        k = len(matrix)
        border_color = Qt.white
        base_img = QImage(k * 5, k * 5, QImage.Format_ARGB32)
        base_img.fill(border_color)
        qrpainter = QPainter()
        qrpainter.begin(base_img)
        boxsize = 5
        size = k * boxsize
        left = (base_img.width() - size) / 2
        top = (base_img.height() - size) / 2
        qrpainter.setBrush(Qt.black)
        qrpainter.setPen(Qt.black)

        for r in range(k):
            for c in range(k):
                if matrix[r][c]:
                    qrpainter.drawRect(left + c * boxsize, top + r * boxsize,
                                       boxsize - 1, boxsize - 1)
        qrpainter.end()
        return base_img

    def calibration_dialog(self, window):
        d = WindowModalDialog(window,
                              _("Revealer - Printer calibration settings"))

        d.setMinimumSize(100, 200)

        vbox = QVBoxLayout(d)
        vbox.addWidget(
            QLabel(''.join([
                "<br/>",
                _("If you have an old printer, or want optimal precision"),
                "<br/>",
                _("print the calibration pdf and follow the instructions "),
                "<br/>",
                "<br/>",
            ])))
        self.calibration_h = self.config.get('calibration_h')
        self.calibration_v = self.config.get('calibration_v')
        cprint = QPushButton(_("Open calibration pdf"))
        cprint.clicked.connect(self.calibration)
        vbox.addWidget(cprint)

        vbox.addWidget(QLabel(_('Calibration values:')))
        grid = QGridLayout()
        vbox.addLayout(grid)
        grid.addWidget(QLabel(_('Right side')), 0, 0)
        horizontal = QLineEdit()
        horizontal.setText(str(self.calibration_h))
        grid.addWidget(horizontal, 0, 1)

        grid.addWidget(QLabel(_('Bottom')), 1, 0)
        vertical = QLineEdit()
        vertical.setText(str(self.calibration_v))
        grid.addWidget(vertical, 1, 1)

        vbox.addStretch()
        vbox.addSpacing(13)
        vbox.addLayout(Buttons(CloseButton(d), OkButton(d)))

        if not d.exec_():
            return

        self.calibration_h = int(Decimal(horizontal.text()))
        self.config.set_key('calibration_h', self.calibration_h)
        self.calibration_v = int(Decimal(vertical.text()))
        self.config.set_key('calibration_v', self.calibration_v)
Beispiel #8
0
    def request_safe_t_init_settings(self, wizard, method, device):
        vbox = QVBoxLayout()
        next_enabled = True
        label = QLabel(_("Enter a label to name your device:"))
        name = QLineEdit()
        hl = QHBoxLayout()
        hl.addWidget(label)
        hl.addWidget(name)
        hl.addStretch(1)
        vbox.addLayout(hl)

        def clean_text(widget):
            text = widget.toPlainText().strip()
            return ' '.join(text.split())

        if method in [TIM_NEW, TIM_RECOVER]:
            gb = QGroupBox()
            hbox1 = QHBoxLayout()
            gb.setLayout(hbox1)
            vbox.addWidget(gb)
            gb.setTitle(_("Select your seed length:"))
            bg = QButtonGroup()
            for i, count in enumerate([12, 18, 24]):
                rb = QRadioButton(gb)
                rb.setText(_("{:d} words").format(count))
                bg.addButton(rb)
                bg.setId(rb, i)
                hbox1.addWidget(rb)
                rb.setChecked(True)
            cb_pin = QCheckBox(_('Enable PIN protection'))
            cb_pin.setChecked(True)
        else:
            text = QTextEdit()
            text.setMaximumHeight(60)
            if method == TIM_MNEMONIC:
                msg = _("Enter your BIP39 mnemonic:")
            else:
                msg = _("Enter the master private key beginning with xprv:")

                def set_enabled():
                    from electrum_dsv.bip32 import is_xprv
                    wizard.next_button.setEnabled(is_xprv(clean_text(text)))

                text.textChanged.connect(set_enabled)
                next_enabled = False

            vbox.addWidget(QLabel(msg))
            vbox.addWidget(text)
            pin = QLineEdit()
            pin.setValidator(QRegExpValidator(QRegExp('[1-9]{0,9}')))
            pin.setMaximumWidth(100)
            hbox_pin = QHBoxLayout()
            hbox_pin.addWidget(QLabel(_("Enter your PIN (digits 1-9):")))
            hbox_pin.addWidget(pin)
            hbox_pin.addStretch(1)

        if method in [TIM_NEW, TIM_RECOVER]:
            vbox.addWidget(WWLabel(RECOMMEND_PIN))
            vbox.addWidget(cb_pin)
        else:
            vbox.addLayout(hbox_pin)

        passphrase_msg = WWLabel(PASSPHRASE_HELP_SHORT)
        passphrase_warning = WWLabel(PASSPHRASE_NOT_PIN)
        passphrase_warning.setStyleSheet("color: red")
        cb_phrase = QCheckBox(_('Enable passphrases'))
        cb_phrase.setChecked(False)
        vbox.addWidget(passphrase_msg)
        vbox.addWidget(passphrase_warning)
        vbox.addWidget(cb_phrase)

        wizard.exec_layout(vbox, next_enabled=next_enabled)

        if method in [TIM_NEW, TIM_RECOVER]:
            item = bg.checkedId()
            pin = cb_pin.isChecked()
        else:
            item = ' '.join(str(clean_text(text)).split())
            pin = str(pin.text())

        return (item, name.text(), pin, cb_phrase.isChecked())