예제 #1
0
 def createLogDisplay(self):
     logTextDisplay = QTextEdit()
     logTextDisplay.setFont(GETFONT('Fixed', 8))
     w, h = relaxedSizeNChar(logTextDisplay, 68)[0], int(12 * 8.2)
     logTextDisplay.setMinimumWidth(w)
     logTextDisplay.setMinimumHeight(h)
     logTextDisplay.setReadOnly(True)
     return logTextDisplay
 def createLogDisplay(self):
    logTextDisplay = QTextEdit()
    logTextDisplay.setFont(GETFONT('Fixed', 8))
    w,h = relaxedSizeNChar(logTextDisplay, 68)[0], int(12 * 8.2)
    logTextDisplay.setMinimumWidth(w)
    logTextDisplay.setMinimumHeight(h)
    logTextDisplay.setReadOnly(True)
    return logTextDisplay
예제 #3
0
class LedgerAuthDialog(QDialog):
    def __init__(self, handler, data):
        '''Ask user for 2nd factor authentication. Support text, security card and paired mobile methods.
        Use last method from settings, but support new pairing and downgrade.
        '''
        QDialog.__init__(self, handler.top_level_window())
        self.handler = handler
        self.txdata = data
        self.idxs = self.txdata[
            'keycardData'] if self.txdata['confirmationType'] > 1 else ''
        self.setMinimumWidth(600)
        self.setWindowTitle(_("Ledger Wallet Authentication"))
        self.cfg = copy.deepcopy(self.handler.win.wallet.get_keystore().cfg)
        self.dongle = self.handler.win.wallet.get_keystore().get_client(
        ).dongle
        self.ws = None
        self.pin = ''

        self.devmode = self.getDevice2FAMode()
        if self.devmode == 0x11 or self.txdata['confirmationType'] == 1:
            self.cfg['mode'] = 0

        vbox = QVBoxLayout()
        self.setLayout(vbox)

        def on_change_mode(idx):
            if idx < 2 and self.ws:
                self.ws.stop()
                self.ws = None
            self.cfg[
                'mode'] = 0 if self.devmode == 0x11 else idx if idx > 0 else 1
            if self.cfg['mode'] > 1 and self.cfg['pair'] and not self.ws:
                self.req_validation()
            if self.cfg['mode'] > 0:
                self.handler.win.wallet.get_keystore().cfg = self.cfg
                self.handler.win.wallet.save_keystore()
            self.update_dlg()

        def add_pairing():
            self.do_pairing()

        def return_pin():
            self.pin = self.pintxt.text(
            ) if self.txdata['confirmationType'] == 1 else self.cardtxt.text()
            if self.cfg['mode'] == 1:
                self.pin = ''.join(chr(int(str(i), 16)) for i in self.pin)
            self.accept()

        self.modebox = QWidget()
        modelayout = QHBoxLayout()
        self.modebox.setLayout(modelayout)
        modelayout.addWidget(QLabel(_("Method:")))
        self.modes = QComboBox()
        modelayout.addWidget(self.modes, 2)
        self.addPair = QPushButton(_("Pair"))
        self.addPair.setMaximumWidth(60)
        modelayout.addWidget(self.addPair)
        modelayout.addStretch(1)
        self.modebox.setMaximumHeight(50)
        vbox.addWidget(self.modebox)

        self.populate_modes()
        self.modes.currentIndexChanged.connect(on_change_mode)
        self.addPair.clicked.connect(add_pairing)

        self.helpmsg = QTextEdit()
        self.helpmsg.setStyleSheet(
            "QTextEdit { background-color: lightgray; }")
        self.helpmsg.setReadOnly(True)
        vbox.addWidget(self.helpmsg)

        self.pinbox = QWidget()
        pinlayout = QHBoxLayout()
        self.pinbox.setLayout(pinlayout)
        self.pintxt = QLineEdit()
        self.pintxt.setEchoMode(2)
        self.pintxt.setMaxLength(4)
        self.pintxt.returnPressed.connect(return_pin)
        pinlayout.addWidget(QLabel(_("Enter PIN:")))
        pinlayout.addWidget(self.pintxt)
        pinlayout.addWidget(QLabel(_("NOT DEVICE PIN - see above")))
        pinlayout.addStretch(1)
        self.pinbox.setVisible(self.cfg['mode'] == 0)
        vbox.addWidget(self.pinbox)

        self.cardbox = QWidget()
        card = QVBoxLayout()
        self.cardbox.setLayout(card)
        self.addrtext = QTextEdit()
        self.addrtext.setStyleSheet(
            "QTextEdit { color:blue; background-color:lightgray; padding:15px 10px; border:none; font-size:20pt; }"
        )
        self.addrtext.setReadOnly(True)
        self.addrtext.setMaximumHeight(120)
        card.addWidget(self.addrtext)

        def pin_changed(s):
            if len(s) < len(self.idxs):
                i = self.idxs[len(s)]
                addr = self.txdata['address']
                addr = addr[:i] + '<u><b>' + addr[i:i +
                                                  1] + '</u></b>' + addr[i +
                                                                         1:]
                self.addrtext.setHtml(str(addr))
            else:
                self.addrtext.setHtml(_("Press Enter"))

        pin_changed('')
        cardpin = QHBoxLayout()
        cardpin.addWidget(QLabel(_("Enter PIN:")))
        self.cardtxt = QLineEdit()
        self.cardtxt.setEchoMode(2)
        self.cardtxt.setMaxLength(len(self.idxs))
        self.cardtxt.textChanged.connect(pin_changed)
        self.cardtxt.returnPressed.connect(return_pin)
        cardpin.addWidget(self.cardtxt)
        cardpin.addWidget(QLabel(_("NOT DEVICE PIN - see above")))
        cardpin.addStretch(1)
        card.addLayout(cardpin)
        self.cardbox.setVisible(self.cfg['mode'] == 1)
        vbox.addWidget(self.cardbox)

        self.pairbox = QWidget()
        pairlayout = QVBoxLayout()
        self.pairbox.setLayout(pairlayout)
        pairhelp = QTextEdit(helpTxt[5])
        pairhelp.setStyleSheet("QTextEdit { background-color: lightgray; }")
        pairhelp.setReadOnly(True)
        pairlayout.addWidget(pairhelp, 1)
        self.pairqr = QRCodeWidget()
        pairlayout.addWidget(self.pairqr, 4)
        self.pairbox.setVisible(False)
        vbox.addWidget(self.pairbox)
        self.update_dlg()

        if self.cfg['mode'] > 1 and not self.ws:
            self.req_validation()

    def populate_modes(self):
        self.modes.blockSignals(True)
        self.modes.clear()
        self.modes.addItem(
            _("Summary Text PIN (requires dongle replugging)"
              ) if self.txdata['confirmationType'] ==
            1 else _("Summary Text PIN is Disabled"))
        if self.txdata['confirmationType'] > 1:
            self.modes.addItem(_("Security Card Challenge"))
            if not self.cfg['pair']:
                self.modes.addItem(_("Mobile - Not paired"))
            else:
                self.modes.addItem(_("Mobile - %s") % self.cfg['pair'][1])
        self.modes.blockSignals(False)

    def update_dlg(self):
        self.modes.setCurrentIndex(self.cfg['mode'])
        self.modebox.setVisible(True)
        self.addPair.setText(
            _("Pair") if not self.cfg['pair'] else _("Re-Pair"))
        self.addPair.setVisible(self.txdata['confirmationType'] > 2)
        self.helpmsg.setText(
            helpTxt[self.cfg['mode'] if self.cfg['mode'] < 2 else 2 if self.
                    cfg['pair'] else 4])
        self.helpmsg.setMinimumHeight(180 if self.txdata['confirmationType'] ==
                                      1 else 100)
        self.pairbox.setVisible(False)
        self.helpmsg.setVisible(True)
        self.pinbox.setVisible(self.cfg['mode'] == 0)
        self.cardbox.setVisible(self.cfg['mode'] == 1)
        self.pintxt.setFocus(
            True) if self.cfg['mode'] == 0 else self.cardtxt.setFocus(True)
        self.setMaximumHeight(200)

    def do_pairing(self):
        rng = os.urandom(16)
        pairID = rng.encode('hex') + hashlib.sha256(rng).digest()[0].encode(
            'hex')
        self.pairqr.setData(pairID)
        self.modebox.setVisible(False)
        self.helpmsg.setVisible(False)
        self.pinbox.setVisible(False)
        self.cardbox.setVisible(False)
        self.pairbox.setVisible(True)
        self.pairqr.setMinimumSize(300, 300)
        if self.ws:
            self.ws.stop()
        self.ws = LedgerWebSocket(self, pairID)
        self.ws.pairing_done.connect(self.pairing_done)
        self.ws.start()

    def pairing_done(self, data):
        if data is not None:
            self.cfg['pair'] = [data['pairid'], data['name'], data['platform']]
            self.cfg['mode'] = 2
            self.handler.win.wallet.get_keystore().cfg = self.cfg
            self.handler.win.wallet.save_keystore()
        self.pin = 'paired'
        self.accept()

    def req_validation(self):
        if self.cfg['pair'] and 'secureScreenData' in self.txdata:
            if self.ws:
                self.ws.stop()
            self.ws = LedgerWebSocket(self, self.cfg['pair'][0], self.txdata)
            self.ws.req_updated.connect(self.req_updated)
            self.ws.start()

    def req_updated(self, pin):
        if pin == 'accepted':
            self.helpmsg.setText(helpTxt[3])
        else:
            self.pin = str(pin)
            self.accept()

    def getDevice2FAMode(self):
        apdu = [0xe0, 0x24, 0x01, 0x00, 0x00, 0x01]  # get 2fa mode
        try:
            mode = self.dongle.exchange(bytearray(apdu))
            return mode
        except BTChipException, e:
            debug_msg('Device getMode Failed')
        return 0x11
예제 #4
0
 def createEditor(self, parent, option, index):
     editor = QTextEdit(parent)
     editor.setMinimumHeight(24)
     return editor
class PluginObject(object):

   tabName = 'Pass Phrase Finder'
   maxVersion = '0.93.99'
   
   #############################################################################
   def __init__(self, main):
      self.searchThread = None
      self.passPhraseFinder = None
      self.isSearchOver = False
      
      def updateResults(resultStr):
         self.resultStr += resultStr
         
      def updateDisplay():
         if len(self.resultStr) > 0:
            self.resultsDisplay.append(self.resultStr)
            self.resultStr = ''
            self.resultsDisplay.moveCursor(QtGui.QTextCursor.End)
            self.resultsDisplay.repaint()
         if self.isSearchOver:
            endSearch()
      
      # Call this from another thread to end the search
      def terminateSearch():
         self.isSearchOver = True
      
      # Call this from the main thread to end the search
      def endSearch():
         self.main.extraHeartbeatAlways.remove(updateDisplay)
         self.searchButton.setEnabled(True)
         self.stopButton.setEnabled(False)
         # If the thread is still searching tell the pass phrase finder to stop
         if self.passPhraseFinder and self.searchThread  and not self.searchThread.isFinished():
            self.passPhraseFinder.isStopped = True
            
      def searchForPassphrase():
         # Get the selected wallet from the main screen
         wlt = self.getSelectedWlt()
         if wlt and not wlt.watchingOnly and wlt.isLocked:
            self.resultStr = ''
            self.passPhraseFinder = PassPhraseFinder(wlt)
            self.resultsDisplay.setText(QString(''))
            self.main.extraHeartbeatAlways.append(updateDisplay)
            if len(self.segOrdStrSet) > 0:
               # From self.segOrdStrList, create a list of lists of indexes that describe the segment orderings to search
               # In other words convert all of the strings in orderings list to lists of integers
               segOrdIntListList = []
               for ordStr in self.segOrdStrSet:
                  # The indexes provided by the users are 1 based, and the list indexes ought to be 0 based
                  segOrdIntListList.append([int(indexStr)-1 for indexStr in ordStr.split(',')])
               self.searchThread = PyBackgroundThread(self.passPhraseFinder.searchForPassPhrase, 
                  [segDef.getSegList() for segDef in self.segDefList], 
                  segOrdIntListList, 
                  updateResults,
                  terminateSearch )
               # Reset the isSearchOver flag
               self.isSearchOver = False
               self.searchThread.start()
               
               # Disable search button adn enabled stop button
               self.stopButton.setEnabled(True)
               self.searchButton.setEnabled(False)
            else:
               QMessageBox.warning(self.main, tr('Invalid'), tr("""
                  There are no valid segment combinations to search.
                  Please add at least one segment and ordering to search."""), QMessageBox.Ok)
         else:
            QMessageBox.warning(self.main, tr('Invalid'), tr("""
               No valid wallet is selected. Please select a locked
               non-watching-only from Available Wallets."""), QMessageBox.Ok)

      def addKnownSegment():
         dlgEnterSegment = DlgEnterSegment(main, main)
         if dlgEnterSegment.exec_():
            segmentText = str(dlgEnterSegment.editSegment.text())
            if len(segmentText)>0:
               self.segDefList.append(KnownSeg(segmentText))
               self.segDefTableModel.updateSegList(self.segDefList)
      
      def addUnknownCaseSegment():
         dlgEnterSegment = DlgEnterSegment(main, main)
         if dlgEnterSegment.exec_():
            segmentText = str(dlgEnterSegment.editSegment.text())
            if len(segmentText)>0:
               self.segDefList.append(UnknownCaseSeg(segmentText))
               self.segDefTableModel.updateSegList(self.segDefList)
               
      def addUnknownOrderSegment():
         dlgEnterSegment = DlgEnterSegment(main, main, isUnknownOrder=True)
         if dlgEnterSegment.exec_():
            segmentText = str(dlgEnterSegment.editSegment.text())
            minLen = int(str(dlgEnterSegment.minSelector.currentText()))
            maxLen = int(str(dlgEnterSegment.maxSelector.currentText()))
            if len(segmentText)>0:
               self.segDefList.append(UnknownSeg(segmentText, minLen, maxLen))
               self.segDefTableModel.updateSegList(self.segDefList)

      def addOrdering():
         if len(self.segDefList) > 0:
            dlgSpecifyOrdering = DlgSpecifyOrdering(main, main, len(self.segDefList))     
            if dlgSpecifyOrdering.exec_():
               self.segOrdStrSet.add(str(dlgSpecifyOrdering.parseOrderingList()).strip('[]'))
               self.updateOrderingListBox()
         else:
            QMessageBox.warning(self.main, tr('Not Ready'), tr("""
               No segments have been entered. You must enter some segments before you can order them."""), QMessageBox.Ok)

      
      self.main = main
      self.segDefList = []
      self.segOrdStrSet = set()
      segmentHeader = QRichLabel(tr("""<b>Build segments for pass phrase search: </b>"""), doWrap=False)
      self.knownButton = QPushButton("Add Known Segment")
      self.unknownCaseButton = QPushButton("Add Unknown Case Segment")
      self.unknownOrderButton = QPushButton("Add Unknown Order Segment")
      self.main.connect(self.knownButton, SIGNAL('clicked()'), addKnownSegment)
      self.main.connect(self.unknownCaseButton, SIGNAL('clicked()'), addUnknownCaseSegment)
      self.main.connect(self.unknownOrderButton, SIGNAL('clicked()'), addUnknownOrderSegment)
      topRow =  makeHorizFrame([segmentHeader, self.knownButton, self.unknownCaseButton, self.unknownOrderButton, 'stretch'])
      
      self.segDefTableModel = SegDefDisplayModel()
      self.segDefTableView = QTableView()
      self.segDefTableView.setModel(self.segDefTableModel)
      self.segDefTableView.setSelectionBehavior(QTableView.SelectRows)
      self.segDefTableView.setSelectionMode(QTableView.SingleSelection)
      self.segDefTableView.verticalHeader().setDefaultSectionSize(20)
      self.segDefTableView.verticalHeader().hide()
      
      h = tightSizeNChar(self.segDefTableView, 1)[1]
      self.segDefTableView.setMinimumHeight(2 * (1.3 * h))
      self.segDefTableView.setMaximumHeight(10 * (1.3 * h))      
      initialColResize(self.segDefTableView, [.1, .2, .4, .1, .1, .1])

      self.segDefTableView.customContextMenuRequested.connect(self.showSegContextMenu)
      self.segDefTableView.setContextMenuPolicy(Qt.CustomContextMenu)
      
      segmentOrderingsHeader = QRichLabel(tr("""<b>Specify orderings for pass phrase search: </b>"""), doWrap=False)
      self.addOrderingButton = QPushButton("Add Ordering")
      
      
      self.main.connect(self.addOrderingButton, SIGNAL('clicked()'), addOrdering)
      orderingButtonPanel = makeHorizFrame([segmentOrderingsHeader, self.addOrderingButton, 'stretch'])

      self.segOrdListBox  = QListWidget()
      
      self.segOrdListBox.customContextMenuRequested.connect(self.showOrdContextMenu)
      self.segOrdListBox.setContextMenuPolicy(Qt.CustomContextMenu)
      
      
      self.searchButton = QPushButton("Search")
      self.main.connect(self.searchButton, SIGNAL('clicked()'), searchForPassphrase)
      self.stopButton = QPushButton("Stop Searching")
      self.stopButton.setEnabled(False)
      self.main.connect(self.stopButton, SIGNAL('clicked()'), endSearch)
      totalSearchLabel = QRichLabel(tr("""<b>Total Passphrase Tries To Search: </b>"""), doWrap=False)
      self.totalSearchTriesDisplay = QLineEdit()
      self.totalSearchTriesDisplay.setReadOnly(True)
      self.totalSearchTriesDisplay.setText(QString('0'))
      self.totalSearchTriesDisplay.setFont(GETFONT('Fixed'))
      self.totalSearchTriesDisplay.setMinimumWidth(tightSizeNChar(self.totalSearchTriesDisplay, 6)[0])
      self.totalSearchTriesDisplay.setMaximumWidth(tightSizeNChar(self.totalSearchTriesDisplay, 12)[0])
      searchButtonPanel = makeHorizFrame([self.searchButton, self.stopButton, 'stretch', totalSearchLabel,  self.totalSearchTriesDisplay])
      
      self.resultsDisplay = QTextEdit()
      self.resultsDisplay.setReadOnly(True)
      self.resultsDisplay.setFont(GETFONT('Fixed'))
      self.resultsDisplay.setMinimumHeight(100)
      self.searchPanel = makeVertFrame([topRow, self.segDefTableView, orderingButtonPanel,
             self.segOrdListBox, searchButtonPanel, self.resultsDisplay, 'stretch'])
      # Now set the scrollarea widget to the layout
      self.tabToDisplay = QScrollArea()
      self.tabToDisplay.setWidgetResizable(True)
      self.tabToDisplay.setWidget(self.searchPanel)
   
   def getSelectedWlt(self):
      wlt = None
      selectedWltList = self.main.walletsView.selectedIndexes()
      if len(selectedWltList)>0:
            row = selectedWltList[0].row()
            wltID = str(self.main.walletsView.model().index(row, WLTVIEWCOLS.ID).data().toString())
            wlt = self.main.walletMap[wltID]
      return wlt
   
   def showSegContextMenu(self):
      menu = QMenu(self.segDefTableView)
      if len(self.segDefTableView.selectedIndexes())==0:
         return

      row = self.segDefTableView.selectedIndexes()[0].row()
      deleteSegMenuItem = menu.addAction("Delete Segment")
      action = menu.exec_(QCursor.pos())
      
      if action == deleteSegMenuItem:
         self.deleteSegRow(row)
         
   def showOrdContextMenu(self):
      menu = QMenu(self.segOrdListBox)
      if len(self.segOrdListBox.selectedItems())==0:
         return

      item = self.segOrdListBox.currentItem()
      deleteOrdMenuItem = menu.addAction("Delete Ordering")
      action = menu.exec_(QCursor.pos())
      
      if action == deleteOrdMenuItem:
         self.deleteOrdItem(item)
         
   def deleteSegRow(self, row):
      self.segDefList.remove(self.segDefList[row])
      self.segDefTableModel.updateSegList(self.segDefList)
      self.segOrdStrSet.clear()
      self.updateOrderingListBox()
   
         
   def deleteOrdItem(self, ordItem):
      ordText = str(ordItem.text())
      self.segOrdStrSet.remove(ordText)
      self.updateOrderingListBox()
   
   
   def getTabToDisplay(self):
      return self.tabToDisplay

   def updateOrderingListBox(self):
      self.segOrdListBox.clear()
      segOrdList = list(self.segOrdStrSet)
      segOrdList.sort()
      totalTries = 0
      for ordStr in segOrdList:
         self.segOrdListBox.addItem(QListWidgetItem(ordStr))
         totalTries += self.calculateTries(ordStr)
      if totalTries > BILLION_INT:
         self.totalSearchTriesDisplay.setText(OVER_BILLION_STR)
      else:         
         self.totalSearchTriesDisplay.setText(str(totalTries))
      
   def calculateTries(self, ordStr):
      ordIntList = [int(indexStr) for indexStr in ordStr.split(',')]
      totalTries = 1
      # Multiply each of the totals of segment instance together.
      for ordInt in ordIntList:
         totalTries *= self.segDefList[ordInt-1].getSegListLen()
      return totalTries
 def createEditor(self, parent, option, index):
     editor = QTextEdit(parent)
     editor.setMinimumHeight(24)
     return editor
예제 #7
0
class LedgerAuthDialog(QDialog):
    def __init__(self, handler, data):
        '''Ask user for 2nd factor authentication. Support text, security card and paired mobile methods.
        Use last method from settings, but support new pairing and downgrade.
        '''
        QDialog.__init__(self, handler.top_level_window())
        self.handler = handler
        self.txdata = data
        self.idxs = self.txdata['keycardData'] if self.txdata['confirmationType'] > 1 else ''
        self.setMinimumWidth(600)
        self.setWindowTitle(_("Ledger Wallet Authentication"))
        self.cfg = copy.deepcopy(self.handler.win.wallet.get_keystore().cfg)
        self.dongle = self.handler.win.wallet.get_keystore().get_client().dongle
        self.ws = None
        self.pin = ''
        
        self.devmode = self.getDevice2FAMode()
        if self.devmode == 0x11 or self.txdata['confirmationType'] == 1:
            self.cfg['mode'] = 0
        
        vbox = QVBoxLayout()
        self.setLayout(vbox)
        
        def on_change_mode(idx):
            if idx < 2 and self.ws:
                self.ws.stop()
                self.ws = None
            self.cfg['mode'] = 0 if self.devmode == 0x11 else idx if idx > 0 else 1
            if self.cfg['mode'] > 1 and self.cfg['pair'] and not self.ws:
                self.req_validation()
            if self.cfg['mode'] > 0:
                self.handler.win.wallet.get_keystore().cfg = self.cfg
                self.handler.win.wallet.save_keystore()
            self.update_dlg()
        def add_pairing():
            self.do_pairing()
        def return_pin():
            self.pin = self.pintxt.text() if self.txdata['confirmationType'] == 1 else self.cardtxt.text() 
            if self.cfg['mode'] == 1:
                self.pin = ''.join(chr(int(str(i),16)) for i in self.pin)
            self.accept()
        
        self.modebox = QWidget()
        modelayout = QHBoxLayout()
        self.modebox.setLayout(modelayout)
        modelayout.addWidget(QLabel(_("Method:")))
        self.modes = QComboBox()
        modelayout.addWidget(self.modes, 2)
        self.addPair = QPushButton(_("Pair"))
        self.addPair.setMaximumWidth(60)
        modelayout.addWidget(self.addPair)
        modelayout.addStretch(1)
        self.modebox.setMaximumHeight(50)
        vbox.addWidget(self.modebox)
        
        self.populate_modes()
        self.modes.currentIndexChanged.connect(on_change_mode)
        self.addPair.clicked.connect(add_pairing)
        
        self.helpmsg = QTextEdit()
        self.helpmsg.setStyleSheet("QTextEdit { background-color: lightgray; }")
        self.helpmsg.setReadOnly(True)
        vbox.addWidget(self.helpmsg)
        
        self.pinbox = QWidget()
        pinlayout = QHBoxLayout()
        self.pinbox.setLayout(pinlayout)
        self.pintxt = QLineEdit()
        self.pintxt.setEchoMode(2)
        self.pintxt.setMaxLength(4)
        self.pintxt.returnPressed.connect(return_pin)
        pinlayout.addWidget(QLabel(_("Enter PIN:")))
        pinlayout.addWidget(self.pintxt)
        pinlayout.addWidget(QLabel(_("NOT DEVICE PIN - see above")))
        pinlayout.addStretch(1)
        self.pinbox.setVisible(self.cfg['mode'] == 0)
        vbox.addWidget(self.pinbox)
                    
        self.cardbox = QWidget()
        card = QVBoxLayout()
        self.cardbox.setLayout(card)
        self.addrtext = QTextEdit()
        self.addrtext.setStyleSheet("QTextEdit { color:blue; background-color:lightgray; padding:15px 10px; border:none; font-size:20pt; }")
        self.addrtext.setReadOnly(True)
        self.addrtext.setMaximumHeight(120)
        card.addWidget(self.addrtext)
        
        def pin_changed(s):
            if len(s) < len(self.idxs):
                i = self.idxs[len(s)]
                addr = self.txdata['address']
                addr = addr[:i] + '<u><b>' + addr[i:i+1] + '</u></b>' + addr[i+1:]
                self.addrtext.setHtml(str(addr))
            else:
                self.addrtext.setHtml(_("Press Enter"))
                
        pin_changed('')    
        cardpin = QHBoxLayout()
        cardpin.addWidget(QLabel(_("Enter PIN:")))
        self.cardtxt = QLineEdit()
        self.cardtxt.setEchoMode(2)
        self.cardtxt.setMaxLength(len(self.idxs))
        self.cardtxt.textChanged.connect(pin_changed)
        self.cardtxt.returnPressed.connect(return_pin)
        cardpin.addWidget(self.cardtxt)
        cardpin.addWidget(QLabel(_("NOT DEVICE PIN - see above")))
        cardpin.addStretch(1)
        card.addLayout(cardpin)
        self.cardbox.setVisible(self.cfg['mode'] == 1)
        vbox.addWidget(self.cardbox)
        
        self.pairbox = QWidget()
        pairlayout = QVBoxLayout()
        self.pairbox.setLayout(pairlayout)
        pairhelp = QTextEdit(helpTxt[5])
        pairhelp.setStyleSheet("QTextEdit { background-color: lightgray; }")
        pairhelp.setReadOnly(True)
        pairlayout.addWidget(pairhelp, 1)
        self.pairqr = QRCodeWidget()
        pairlayout.addWidget(self.pairqr, 4)
        self.pairbox.setVisible(False)
        vbox.addWidget(self.pairbox)
        self.update_dlg()
        
        if self.cfg['mode'] > 1 and not self.ws:
            self.req_validation()
        
    def populate_modes(self):
        self.modes.blockSignals(True)
        self.modes.clear()
        self.modes.addItem(_("Summary Text PIN (requires dongle replugging)") if self.txdata['confirmationType'] == 1 else _("Summary Text PIN is Disabled"))
        if self.txdata['confirmationType'] > 1:
            self.modes.addItem(_("Security Card Challenge"))
            if not self.cfg['pair']:
                self.modes.addItem(_("Mobile - Not paired")) 
            else:
                self.modes.addItem(_("Mobile - %s") % self.cfg['pair'][1]) 
        self.modes.blockSignals(False)
        
    def update_dlg(self):
        self.modes.setCurrentIndex(self.cfg['mode'])
        self.modebox.setVisible(True)
        self.addPair.setText(_("Pair") if not self.cfg['pair'] else _("Re-Pair"))
        self.addPair.setVisible(self.txdata['confirmationType'] > 2)
        self.helpmsg.setText(helpTxt[self.cfg['mode'] if self.cfg['mode'] < 2 else 2 if self.cfg['pair'] else 4])
        self.helpmsg.setMinimumHeight(180 if self.txdata['confirmationType'] == 1 else 100)
        self.pairbox.setVisible(False)
        self.helpmsg.setVisible(True)
        self.pinbox.setVisible(self.cfg['mode'] == 0)
        self.cardbox.setVisible(self.cfg['mode'] == 1)
        self.pintxt.setFocus(True) if self.cfg['mode'] == 0 else self.cardtxt.setFocus(True)
        self.setMaximumHeight(200)
        
    def do_pairing(self):
        rng = os.urandom(16)
        pairID = rng.encode('hex') + hashlib.sha256(rng).digest()[0].encode('hex')
        self.pairqr.setData(pairID)
        self.modebox.setVisible(False)
        self.helpmsg.setVisible(False)
        self.pinbox.setVisible(False)
        self.cardbox.setVisible(False)
        self.pairbox.setVisible(True)
        self.pairqr.setMinimumSize(300,300)
        if self.ws:
            self.ws.stop()
        self.ws = LedgerWebSocket(self, pairID)
        self.ws.pairing_done.connect(self.pairing_done)
        self.ws.start() 
               
    def pairing_done(self, data):
        if data is not None:
            self.cfg['pair'] = [ data['pairid'], data['name'], data['platform'] ]
            self.cfg['mode'] = 2
            self.handler.win.wallet.get_keystore().cfg = self.cfg
            self.handler.win.wallet.save_keystore()
        self.pin = 'paired'
        self.accept()
    
    def req_validation(self):
        if self.cfg['pair'] and 'secureScreenData' in self.txdata:
            if self.ws:
                self.ws.stop()
            self.ws = LedgerWebSocket(self, self.cfg['pair'][0], self.txdata)
            self.ws.req_updated.connect(self.req_updated)
            self.ws.start()
              
    def req_updated(self, pin):
        if pin == 'accepted':
            self.helpmsg.setText(helpTxt[3])
        else:
            self.pin = str(pin)
            self.accept()
        
    def getDevice2FAMode(self):
        apdu = [0xe0, 0x24, 0x01, 0x00, 0x00, 0x01] # get 2fa mode
        try:
            mode = self.dongle.exchange( bytearray(apdu) )
            return mode
        except BTChipException, e:
            debug_msg('Device getMode Failed')
        return 0x11