diff --git a/README.rst b/README.md
similarity index 97%
rename from README.rst
rename to README.md
index 44faebd4..ba188e23 100644
--- a/README.rst
+++ b/README.md
@@ -161,3 +161,8 @@ script (modify version, etc., as needed)
This will produce an executable install app
``windows/Output/kkbsetup.exe``
+
+KeepKey Authenticator
+==============
+Documentation can be found [here](authenticator/AUTHENTICATOR.md).
+
diff --git a/authenticator/AUTHENTICATOR.md b/authenticator/AUTHENTICATOR.md
new file mode 100644
index 00000000..7b24919b
--- /dev/null
+++ b/authenticator/AUTHENTICATOR.md
@@ -0,0 +1,42 @@
+
+KeepKey Authenticator
+==============
+The KeepKey Authenticator is a standalone app intended to demo the authenticator feature.
+It enables the KeepKey device to perform as a hardware off-line one-time passcode (OTP) generator for two factor authentication using the TOPT algorithm. The KeepKey (optional) PIN, (optional) passphrase, and physical button gives it security features similar to other hardware authenticator products, but with better OTP display security.
+
+Build for MacOS - Intel
+-----------------------
+Make sure you have python-keepkey built and installed:
+
+ $ python python-keepkey/setup.py build install
+
+KeepKeyAuthenticator.app is built using py2app.
+If not already installed, install py2app with:
+
+ $ pip install py2app
+
+Change to the pytthon-keepkey/authenticator directory:
+
+ $ cd authenticator
+
+The setup file was created from the command line:
+
+ $ py2applet --make-setup KeepKeyAuthenticator.py authResources/*.png
+
+and then saved as setupAuth.py. NOTE: Creating the setup.py file in this manner will overwrite
+the existing setup.py file if one exists.
+
+Create the app:
+
+ $ python setup.py py2app
+
+Build for MacOS - M1
+--------------------
+from https://py2app.readthedocs.io/_/downloads/en/stable/pdf/
+
+M1 Macs and libraries not available for arm64 A lot of libraries are not yet available as arm64 or universal2 libraries. For applications using those libraries you can create an x86_64 (Intel) application instead:
+1. Create a new virtual environment and activate this
+2. Use arch -x86_64 python -mpip install ... to install libraries.
+ The arch command is necessary here to ensure that pip selects variants that are compatible with the x86_64 architecture instead of arm64.
+3. Use arch -x86_64 python setup.py py2app --arch x86_64 to build
+This results in an application bundle where the launcher is an x86_64 only binary, and where included C extensions and libraries are compatible with that architecture as well.
diff --git a/authenticator/AddAccDialog.ui b/authenticator/AddAccDialog.ui
new file mode 100644
index 00000000..512a46a8
--- /dev/null
+++ b/authenticator/AddAccDialog.ui
@@ -0,0 +1,110 @@
+
+
+ AddAccDialog
+
+
+
+ 0
+ 0
+ 450
+ 330
+
+
+
+ Add Account
+
+
+ background-color:rgb(40, 40, 40);
+
+
+ true
+
+
+
+
+ 125
+ 30
+ 200
+ 70
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 1px;
+border-style: solid;
+border-color: rgb(0, 255, 0);
+color: rgb(0, 255, 0);
+text-align:center;
+
+
+ Scan QR
+
+
+
+
+
+ 110
+ 110
+ 220
+ 45
+
+
+
+
+ 18
+
+
+
+ color: rgb(255, 255, 255);
+
+
+
+ Make sure QR code is fully visible on main screen
+
+
+ Qt::RichText
+
+
+ Qt::AlignHCenter|Qt::AlignTop
+
+
+ true
+
+
+
+
+
+ 125
+ 210
+ 200
+ 70
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 1px;
+border-style: solid;
+border-color: rgb(0, 255, 0);
+color: rgb(0, 255, 0);
+text-align:center;
+
+
+ Enter Manually
+
+
+
+
+
+
diff --git a/authenticator/AuthMain.ui b/authenticator/AuthMain.ui
new file mode 100644
index 00000000..58fdd371
--- /dev/null
+++ b/authenticator/AuthMain.ui
@@ -0,0 +1,685 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 772
+ 828
+
+
+
+ KeepKey Authenticator
+
+
+
+ kk-icon-gold.pngkk-icon-gold.png
+
+
+ background-color: rgb(1, 0, 0);
+
+
+
+
+
+ 30
+ 30
+ 200
+ 160
+
+
+
+
+
+
+ kk-icon-gold.png
+
+
+
+
+
+ 290
+ 30
+ 460
+ 80
+
+
+
+
+ 32
+
+
+
+ color: rgb(255, 255, 255);
+
+
+ Authenticator for the KeepKey Python client
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+ true
+
+
+
+
+
+ 290
+ 150
+ 150
+ 40
+
+
+
+
+ 24
+
+
+
+ color: rgb(55, 62, 255);
+
+
+ by markrypto
+
+
+
+
+
+ 30
+ 200
+ 720
+ 16
+
+
+
+ color: rgb(255, 255, 255)
+
+
+ QFrame::Plain
+
+
+ 1
+
+
+ Qt::Horizontal
+
+
+
+
+
+ 610
+ 120
+ 140
+ 70
+
+
+
+
+ 20
+
+
+
+ border-radius: 10px;
+background-color: rgb(255, 128, 4);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(255, 255, 255)
+
+
+ Connect
+KeepKey
+
+
+ true
+
+
+
+
+
+ 30
+ 220
+ 200
+ 50
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(103, 103, 103);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(255, 255, 255)
+
+
+ Add Account
+
+
+ true
+
+
+
+
+
+ 30
+ 480
+ 200
+ 50
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(103, 103, 103);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(255, 255, 255);
+QPushButton::pressed {
+ background-color: rgb(35, 40, 49);
+ border: 2px solid rgb(43, 50, 61);
+ }
+
+
+ test
+
+
+ true
+
+
+
+
+
+ 380
+ 220
+ 130
+ 45
+
+
+
+
+ 32
+
+
+
+ color: rgb(255, 255, 255);
+
+
+
+ Accounts
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+
+
+
+ 30
+ 300
+ 200
+ 50
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(103, 103, 103);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(255, 255, 255)
+
+
+ Remove Account
+
+
+ true
+
+
+
+
+
+ 380
+ 270
+ 351
+ 41
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ true
+
+
+ OTPButtonGroup
+
+
+
+
+
+ 525
+ 220
+ 220
+ 45
+
+
+
+
+ 18
+
+
+
+ color: rgb(255, 255, 255);
+
+
+
+ Click on account to generate OTP on KeepKey
+
+
+ Qt::RichText
+
+
+ Qt::AlignHCenter|Qt::AlignTop
+
+
+ true
+
+
+
+
+
+ 380
+ 320
+ 351
+ 41
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ true
+
+
+ OTPButtonGroup
+
+
+
+
+
+ 380
+ 370
+ 351
+ 41
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ true
+
+
+ OTPButtonGroup
+
+
+
+
+
+ 380
+ 420
+ 351
+ 41
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ true
+
+
+ OTPButtonGroup
+
+
+
+
+
+ 380
+ 470
+ 351
+ 41
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ true
+
+
+ OTPButtonGroup
+
+
+
+
+
+ 380
+ 520
+ 351
+ 41
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ true
+
+
+ OTPButtonGroup
+
+
+
+
+
+ 380
+ 570
+ 351
+ 41
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ true
+
+
+ OTPButtonGroup
+
+
+
+
+
+ 380
+ 620
+ 351
+ 41
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ true
+
+
+ OTPButtonGroup
+
+
+
+
+
+ 380
+ 670
+ 351
+ 41
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ true
+
+
+ OTPButtonGroup
+
+
+
+
+
+ 380
+ 720
+ 351
+ 41
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ true
+
+
+ OTPButtonGroup
+
+
+
+
+
+ 290
+ 110
+ 220
+ 40
+
+
+
+
+ 24
+
+
+
+ color: rgb(255, 255, 255);
+
+
+
+
+
+
+
+
+ 30
+ 380
+ 200
+ 50
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(103, 103, 103);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(255, 255, 255)
+
+
+ Wipe All Accounts
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/authenticator/GUImixin.py b/authenticator/GUImixin.py
new file mode 100644
index 00000000..6c0ad10c
--- /dev/null
+++ b/authenticator/GUImixin.py
@@ -0,0 +1,58 @@
+# This file is part of the KeepKey project.
+#
+# Copyright (C) 2022 markrypto (cryptoakorn@gmail.com)
+#
+# This library is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library. If not, see .
+#
+# The script has been modified for KeepKey Device.
+
+from keepkeylib import messages_pb2 as proto
+from KeepKeyAuthenticator import pingui_popup
+from KeepKeyAuthenticator import passphrase_popup
+
+class GuiUIMixin(object):
+ # qt gui interface
+
+ def __init__(self, *args, **kwargs):
+ super(GuiUIMixin, self).__init__(*args, **kwargs)
+ self.character_request_first_pass = True
+
+ def callback_ButtonRequest(self, msg):
+ # log("Sending ButtonAck for %s " % get_buttonrequest_value(msg.code))
+ return proto.ButtonAck()
+
+ # def callback_RecoveryMatrix(self, msg):
+ # pass
+
+ def callback_PinMatrixRequest(self, msg):
+ # get matrix position and ack
+ # pin = getPinDecoded()
+ pin = pingui_popup()
+ print('pin is ', pin)
+ if pin == 'E':
+ return proto.Cancel()
+ else:
+ return proto.PinMatrixAck(pin=pin)
+
+ def callback_PassphraseRequest(self, msg):
+ passphrase = passphrase_popup()
+ # passphrase = "YEJ@.h*B!KLAJLpQ!6AMfaQLdijAmZRXMe3@a_FqmNfpbAN4p"
+ return proto.PassphraseAck(passphrase=passphrase)
+
+
+ # def callback_CharacterRequest(self, msg):
+ # pass
+
+
+
diff --git a/authenticator/KKQRkey160.png b/authenticator/KKQRkey160.png
new file mode 100644
index 00000000..820ee0c1
Binary files /dev/null and b/authenticator/KKQRkey160.png differ
diff --git a/authenticator/KeepKeyAuthenticator.py b/authenticator/KeepKeyAuthenticator.py
new file mode 100644
index 00000000..e36377c2
--- /dev/null
+++ b/authenticator/KeepKeyAuthenticator.py
@@ -0,0 +1,734 @@
+# Copyright (C) 2023 markrypto
+#
+# This library is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library. If not, see .
+#
+# The script has been modified for KeepKey Device.
+
+__version__ = "0.5.0"
+
+from PyQt6 import QtWidgets, uic
+from PyQt6.QtWidgets import QFileDialog, QMenuBar,QMessageBox,QPushButton
+from PyQt6.QtGui import *
+from PyQt6 import QtCore
+import sys
+import os
+from PIL import Image
+from pyzbar.pyzbar import decode
+import qrcode
+import time
+from urllib.parse import urlparse
+from PIL import ImageGrab
+
+import binascii
+import urllib
+import time
+from datetime import datetime
+import sys
+import semver
+import usb1 as libusb
+
+from keepkeylib.transport_webusb import WebUsbTransport
+from keepkeylib.client import PinException, ProtocolMixin, BaseClient, CallException
+from keepkeylib import types_pb2 as types
+
+
+from authmain import Ui_MainWindow as Ui
+from pindialog import Ui_Dialog as PIN_Dialog
+from remaccdialog import Ui_RemAccDialog as RemAcc_Dialog
+from addaccdialog import Ui_AddAccDialog as AddAcc_Dialog
+from manualaddacc import Ui_ManualAddAccDialog as ManAddAcc_Dialog
+from passphrasedialog import Ui_PassphraseDialog as Passphrase_Dialog
+
+# logs for dev testing
+_test = False
+
+authErrs = ('Invalid PIN', 'PIN Cancelled', 'PIN expected', 'Auth secret unknown error',
+ 'Account name missing or too long, or seed/message string missing',
+ 'Authenticator secret can\'t be decoded', 'Authenticator secret seed too large',
+ 'passphrase incorrect for authdata')
+class kkClient:
+ def __init__(self):
+ self.client = None
+
+ def ConnectKK(self):
+ from GUImixin import GuiUIMixin
+ class KeepKeyClientAuth(ProtocolMixin, GuiUIMixin, BaseClient):
+ pass
+
+ # look for keepkey connection
+ # List all connected KeepKeys on USB
+ self.client = None
+ devices = WebUsbTransport.enumerate()
+
+ # Check whether we found any
+ if len(devices) == 0:
+ error_popup("No KeepKey found",
+"Ensure that\n1) KeepKey is plugged in\n2) There are no browser windows connected to KeepKey\nThen restart KeepKeyAuthenticator")
+ return
+
+ # Use first connected device
+ try:
+ transport = WebUsbTransport(devices[0])
+ except Exception as E:
+ if str(E) == "LIBUSB_ERROR_ACCESS [-3]":
+ error_popup("KeepKey already claimed on USB","Make sure all KeepKey apps and websites are closed")
+ else:
+ error_popup("Unknown connection error","Make sure all KeepKey apps and websites are closed")
+ return
+
+ # Creates object for manipulating KeepKey
+ self.client = KeepKeyClientAuth(transport)
+ self.requires_firmware("7.6.0")
+
+
+ def requires_firmware(self, ver_required):
+ ret = self.client.init_device()
+ features = self.client.features
+ version = "%s.%s.%s" % (features.major_version, features.minor_version, features.patch_version)
+ if semver.VersionInfo.parse(version) < semver.VersionInfo.parse(ver_required):
+ error_popup("Firmware Upgrade Needed",
+"Your KeepKey has v"+version+" Authenticator feature requires v"+ver_required)
+ self.client = None
+
+ def getClient(self):
+ return self.client
+
+ def closeClient(self):
+ if self.client != None:
+ self.client.close()
+ self.client=None
+
+def error_popup(errmsg, infotext):
+ # set up error/status message box popup
+ msg = QMessageBox()
+ msg.setWindowTitle("ERROR")
+ msg.setText(errmsg)
+ msg.setInformativeText(infotext)
+ x = msg.exec() # show message box
+
+class PIN_Dialog(PIN_Dialog):
+ def setupUi(self, Dialog):
+ super(PIN_Dialog, self).setupUi(Dialog)
+
+ self.pushButton_1.clicked.connect(lambda: self.addPinClick('1'))
+ self.pushButton_2.clicked.connect(lambda: self.addPinClick('2'))
+ self.pushButton_3.clicked.connect(lambda: self.addPinClick('3'))
+ self.pushButton_4.clicked.connect(lambda: self.addPinClick('4'))
+ self.pushButton_5.clicked.connect(lambda: self.addPinClick('5'))
+ self.pushButton_6.clicked.connect(lambda: self.addPinClick('6'))
+ self.pushButton_7.clicked.connect(lambda: self.addPinClick('7'))
+ self.pushButton_8.clicked.connect(lambda: self.addPinClick('8'))
+ self.pushButton_9.clicked.connect(lambda: self.addPinClick('9'))
+ self.pbUnlock.clicked.connect(lambda: self.returnPinAndClose(Dialog))
+ self.encodedPin = ''
+ self.unlockClicked = False
+
+
+ def addPinClick(self, clickPosition):
+ self.encodedPin+=clickPosition
+ if _test: print("encoded pin ", self.encodedPin)
+ self.lineEdit.setText(self.encodedPin)
+ return
+
+ def returnPinAndClose(self, Dialog):
+ if _test: print("unlock pressed")
+ self.unlockClicked = True
+ Dialog.close()
+
+ def getEncodedPin(self):
+ # return encoded PIN when unlock button pressed
+ return self.encodedPin
+
+ def getUnlockClicked(self):
+ # return encoded PIN when unlock button pressed
+ return self.unlockClicked
+
+def pingui_popup():
+ # set up PIN dialog
+ PINDialog = QtWidgets.QDialog()
+ PIN_ui = PIN_Dialog()
+ PIN_ui.setupUi(PINDialog)
+ PINDialog.show()
+ x = PINDialog.exec() # show pin dialog
+ if PIN_ui.getUnlockClicked() == True:
+ pin = PIN_ui.getEncodedPin()
+ if _test: print(pin)
+ return pin
+ else:
+ return 'E' # pin cancelled
+
+def passphrase_popup():
+ # set up passphrase dialog
+ passphraseDialog = QtWidgets.QDialog()
+ passphrase_ui = Passphrase_Dialog()
+ passphrase_ui.setupUi(passphraseDialog)
+ passphraseDialog.show()
+ x = passphraseDialog.exec() # show pin dialog
+ if passphrase_ui.getEnterClicked() == True:
+ passphrase = passphrase_ui.getPassphrase()
+ if _test: print(passphrase)
+ return passphrase
+ else:
+ return 'E' # passphrase canceled
+
+class Passphrase_Dialog(Passphrase_Dialog):
+ def __init__(self):
+ self.KKDisconnect = False
+ self.passphrase = None
+ self.enterClicked = False
+ return
+
+ def setupUi(self, Dialog):
+ super(Passphrase_Dialog, self).setupUi(Dialog)
+ self.Dialog = Dialog
+ self.EnterButton.clicked.connect(self.Enter)
+
+ def Enter(self):
+ self.passphrase = self.passphraseLineEdit.text()
+ self.enterClicked = True
+ self.Dialog.close()
+
+ def getEnterClicked(self):
+ return self.enterClicked
+
+ def getPassphrase(self):
+ return self.passphrase
+
+ def getKKDisconnect(self):
+ return self.KKDisconnect
+
+
+class RemAcc_Dialog(RemAcc_Dialog):
+ def __init__(self, client, authOps):
+ self.client = client
+ self.authOps = authOps
+ self.accounts = None
+ self.KKDisconnect = False
+ return
+
+ def setupUi(self, Dialog):
+ super(RemAcc_Dialog, self).setupUi(Dialog)
+ self.Dialog = Dialog
+ # Remove account button group
+ # Add IDs to buttons to make for easy access
+ bNum = 1
+ bList = self.RemoveAccButtonGroup.buttons()
+ for b in bList:
+ self.RemoveAccButtonGroup.setId(b, bNum)
+ bNum += 1
+
+ self.RemoveAccButtonGroup.idClicked.connect(self.RemoveAccount)
+ self.RemGetAccounts()
+
+ def RemGetAccounts(self):
+
+ self.accounts, fail = self.authOps.auth_accGet(self.client)
+ if fail in ('Invalid PIN', 'PIN Cancelled', 'PIN expected', 'usb err', 'Device not initialized'):
+ self.KKDisconnect = True
+ return
+
+ # reset button list
+ bList = self.RemoveAccButtonGroup.buttons()
+ for b in bList:
+ b.setText('')
+
+ if len(self.accounts) > 0:
+ # this forms monotonic button list of non-empty slots
+ self.RemAccButton = list()
+ bNum = 1
+ for acc in self.accounts:
+ if acc[1] == '':
+ continue;
+ else:
+ self.RemoveAccButtonGroup.button(bNum).setText(acc[1])
+ self.RemAccButton.append((str(bNum), acc))
+ bNum += 1
+
+ def RemoveAccount(self, id_):
+ # First check if this is an active button with an account
+ if self.RemoveAccButtonGroup.button(id_).text() == '':
+ return
+
+ if (self.client != None):
+ for ba in self.RemAccButton:
+ if ba[0] == str(id_): # button clicked is in button-account list
+ dom, acc = ba[1][1].split(':')
+ if _test: print(dom+" "+acc)
+ break
+ err = self.authOps.auth_accRem(self.client, dom, acc)
+ if err == 'noerr':
+ self.RemGetAccounts()
+ elif err == 'usb err':
+ self.KKDisconnect = True
+ self.Dialog.close()
+ return
+ else:
+ error_popup(err, '')
+ return
+
+ else:
+ error_popup('Keepkey not connected', '')
+
+ def getKKDisconnect(self):
+ return self.KKDisconnect
+
+class ManAddAcc_Dialog(ManAddAcc_Dialog):
+ def __init__(self, client, authOps):
+ self.client = client
+ self.authOps = authOps
+ self.KKDisconnect = False
+ self.secret = None
+ self.domain = None
+ self.account = None
+ return
+
+ def setupUi(self, Dialog):
+ super(ManAddAcc_Dialog, self).setupUi(Dialog)
+ self.Dialog = Dialog
+ self.AddButton.clicked.connect(self.ManualAdd)
+
+ def ManualAdd(self):
+
+ self.secret = self.SecretlineEdit.text()
+ self.domain = self.IssuerlineEdit.text()
+ self.account = self.AccNamelineEdit.text()
+ self.Dialog.close()
+
+ def getManAuth(self):
+ return self.domain, self.account, self.secret
+
+ def getKKDisconnect(self):
+ return self.KKDisconnect
+
+class AddAcc_Dialog(AddAcc_Dialog):
+ def __init__(self, client, authOps):
+ self.client = client
+ self.authOps = authOps
+ self.accounts = None
+ self.KKDisconnect = False
+ return
+
+ def setupUi(self, Dialog):
+ super(AddAcc_Dialog, self).setupUi(Dialog)
+ self.Dialog = Dialog
+ self.ScanQrButton.clicked.connect(self.QrScreencap)
+ self.EnterManuallyButton.clicked.connect(self.ManAddAcc)
+
+ def QrScreencap(self):
+ # grab fullscreen
+ self.im = ImageGrab.grab(include_layered_windows=True)
+ #self.im.save("fullscreen.png")
+
+ data = decode(self.im)
+ if (data == []):
+ error_popup("QR Code Error", "Could not read QR code")
+ return
+ rawdata = str(data[0][0]).replace("b'",'').replace("'","")
+ type1 = str(data[0][1])
+ if _test: print(rawdata)
+ if _test: print(type1)
+
+
+ unqData = urllib.parse.unquote(rawdata) # replace any %xx escapes with single char equivalent
+ # parse check
+ pUrl = urlparse(unqData)
+ if pUrl.scheme == 'otpauth' and pUrl.netloc == 'totp':
+ try:
+ secret = pUrl.query.split('=')[1].split('&')[0]
+ domain = pUrl.path.split('/')[1].split(':')[0]
+ account = pUrl.path.split('/')[1].split(':')[1]
+ except (IndexError, ValueError):
+ error_popup("QR Code Parse Error", ("Could not parse %s" % unqData))
+ return
+ else:
+ error_popup("QR Code Error", ("invalid otpauth url\n%s" % unqData))
+ return
+
+ self.KKAddAcc(self.client, secret, domain, account)
+ return
+
+ def KKAddAcc(self, client, secret, domain, account):
+ if _test: print(secret)
+ if _test: print(domain)
+ if _test: print(account)
+ if (client != None):
+ err = self.authOps.auth_accAdd(client, secret, domain, account)
+ if err == 'noerr':
+ self.Dialog.close()
+ return
+ elif err in authErrs:
+ error_popup(err, '')
+ else:
+ error_popup(err, '') #usb error
+ self.KKDisconnect = True
+ self.Dialog.close()
+
+ return
+ else:
+ error_popup('Keepkey not connected', '')
+ self.KKDisconnect = True
+ self.Dialog.close()
+
+ def ManAddAcc(self):
+ if self.client == None:
+ return
+
+ # set up manual add account dialog
+ ManAddAccDialog = QtWidgets.QDialog()
+ ManAddAcc_ui = ManAddAcc_Dialog(self.client, self.authOps)
+ ManAddAcc_ui.setupUi(ManAddAccDialog)
+ if ManAddAcc_ui.getKKDisconnect() == True:
+ self.KKDisconnect()
+ self.Dialog.close()
+ return
+ ManAddAccDialog.show()
+ x = ManAddAccDialog.exec() # show dialog
+ domain, account, secret = ManAddAcc_ui.getManAuth()
+ if None in (domain, account, secret):
+ error_popup('Must enter values for domain, account and secret', '')
+ else:
+ self.KKAddAcc(self.client, secret, domain, account)
+
+ self.Dialog.close()
+
+ def getKKDisconnect(self):
+ return self.KKDisconnect
+
+class Ui(Ui):
+ def __init__(self):
+ self.authOps = AuthClass()
+
+ def setupUi(self, MainWindow):
+ super(Ui, self).setupUi(MainWindow)
+ _translate = QtCore.QCoreApplication.translate
+
+ self.clientOps = kkClient()
+ versionText = "Version %s" % __version__
+ self.Version.setText(_translate("MainWindow", versionText))
+
+ self.ConnectKKButton.clicked.connect(self.KKConnect)
+ self.AddAccButton.clicked.connect(self.addAcc)
+ self.RemoveAccButton.clicked.connect(self.removeAcc)
+ self.WipeAccButton.clicked.connect(self.wipeData)
+ if _test:
+ self.testButton.clicked.connect(self.Test)
+ else:
+ self.testButton.deleteLater()
+
+ # OTP button group
+ # Add IDs to buttons to make for easy access
+ bNum = 1
+ bList = self.OTPButtonGroup.buttons()
+ for b in bList:
+ self.OTPButtonGroup.setId(b, bNum)
+ bNum += 1
+
+ # self.OTPButtonGroup.buttonClicked.connect(self.OtpGen)
+ self.OTPButtonGroup.idClicked.connect(self.OtpGen)
+
+ MainWindow.show()
+
+ def KKConnect(self):
+ self.accounts = None
+ _translate = QtCore.QCoreApplication.translate
+ self.clientOps.ConnectKK()
+ client = self.clientOps.getClient()
+ if (client != None):
+ self.ConnectKKButton.setStyleSheet("border-radius: 10px;\n"\
+ "background-color: rgb(35, 40, 49);\n"\
+ "border-width: 2px;\n"\
+ "border-style: solid;\n"\
+ "border-color: black;\n"\
+ "color: rgb(0, 255, 0)")
+ self.ConnectKKButton.setText(_translate("MainWindow", "KeepKey\nConnected"))
+ # get accounts if connected
+ self.getAccounts(client)
+ else:
+ self.KKDisconnect()
+
+ def KKDisconnect(self):
+ self.accounts = None
+ _translate = QtCore.QCoreApplication.translate
+ self.clientOps.closeClient()
+ self.ConnectKKButton.setStyleSheet("border-radius: 10px;\n"\
+ "background-color: rgb(255, 128, 4);\n"\
+ "border-width: 2px;\n"\
+ "border-style: solid;\n"\
+ "border-color: black;\n"\
+ "color: rgb(255, 255, 255)")
+ self.ConnectKKButton.setText(_translate("MainWindow", "Connect\nKeepKey"))
+ self.clearAccounts()
+
+ def getAccounts(self, client):
+ self.accounts, err = self.authOps.auth_accGet(client)
+ if _test:print("get accounts err: "+err)
+ if err in ('Invalid PIN', 'PIN Cancelled', 'PIN expected', 'usb err', 'Device not initialized'):
+ self.KKDisconnect()
+ return
+ if (err == 'passphrase incorrect for authdata'):
+ self.clearAccounts()
+ return
+
+ if _test: print(self.accounts)
+ # reset button list
+ bList = self.OTPButtonGroup.buttons()
+ for b in bList:
+ b.setText('')
+
+ if len(self.accounts) > 0:
+ # this forms monotonic button list of non-empty slots
+ self.otpButtonAcc = list()
+ bNum = 1
+ for acc in self.accounts:
+ if acc[1] == '':
+ continue
+ else:
+ self.OTPButtonGroup.button(bNum).setText(acc[1])
+ self.otpButtonAcc.append((str(bNum), acc))
+ bNum += 1
+
+ def clearAccounts(self):
+ self.accounts = None
+ bList = self.OTPButtonGroup.buttons()
+ for b in bList:
+ b.setText('')
+
+ def addAcc(self):
+ client = self.clientOps.getClient()
+ if client == None:
+ return
+
+ # set up add account dialog
+ AddAccDialog = QtWidgets.QDialog()
+ AddAcc_ui = AddAcc_Dialog(client, self.authOps)
+ AddAcc_ui.setupUi(AddAccDialog)
+ if AddAcc_ui.getKKDisconnect() == True:
+ self.KKDisconnect()
+ return
+ AddAccDialog.show()
+ x = AddAccDialog.exec() # show pin dialog
+ self.getAccounts(client)
+
+ def OtpGen(self, id_):
+ # First check if this is an active button with an account
+ if self.OTPButtonGroup.button(id_).text() == '':
+ return
+
+ #generate OTP
+ client = self.clientOps.getClient()
+
+ if (client != None):
+ for ba in self.otpButtonAcc:
+ if ba[0] == str(id_): # button clicked is in button-account list
+ dom, acc = ba[1][1].split(':')
+ if _test: print(dom+" "+acc)
+ break
+ err = self.authOps.auth_otp(client, dom, acc)
+ if err in authErrs:
+ error_popup(err, '')
+ elif err == 'usb err':
+ error_popup('usb error', '')
+ self.KKDisconnect()
+ return
+
+ else:
+ error_popup('Keepkey not connected', '')
+
+ def removeAcc(self):
+ client = self.clientOps.getClient()
+ if client == None:
+ return
+
+ # set up remove account dialog
+ RemAccDialog = QtWidgets.QDialog()
+ RemAcc_ui = RemAcc_Dialog(client, self.authOps)
+ RemAcc_ui.setupUi(RemAccDialog)
+ if RemAcc_ui.getKKDisconnect() == True:
+ self.KKDisconnect()
+ return
+ RemAccDialog.show()
+ x = RemAccDialog.exec() # show pin dialog
+ self.getAccounts(client)
+
+ def wipeData(self):
+ client = self.clientOps.getClient()
+
+ if (client != None):
+ try:
+ self.authOps.auth_wipe(client)
+ self.KKDisconnect() # disconnect and reestablish
+ except PinException as e:
+ error_popup("Invalid PIN", "")
+ return
+ else:
+ error_popup('Keepkey not connected', '')
+ return
+
+ def Test(self):
+ #test the keepkey function
+ client = self.clientOps.getClient()
+ if (client != None):
+ try:
+ self.authOps.auth_test(client)
+ self.getAccounts(client)
+ except PinException as e:
+ error_popup("Invalid PIN", "")
+ return
+ else:
+ error_popup('Keepkey not connected', '')
+
+class AuthClass:
+ def sendMsg(self, client, msg):
+ err = ''
+ retval = None
+ try:
+ retval = client.ping(msg)
+ except CallException as E:
+ err = E.args[1]
+
+ except libusb.USBErrorNoDevice:
+ err = "No KeepKey found"
+ except libusb.USBErrorTimeout:
+ err = "USB error timeout"
+ except libusb.USBError as error:
+ err = "USB error %r" % error
+
+ return retval, err
+
+
+ def auth_accAdd(self, client, secret, domain, account):
+ print(b'\x15' + bytes("initializeAuth:"+domain+":"+account+":"+secret, 'utf8'))
+ retval, err = self.sendMsg(client, msg = b'\x15' + bytes("initializeAuth:"+domain+":"+account+":"+secret, 'utf8'))
+ if err == 'Authenticator secret storage full':
+ error_popup(err, "Need to remove an account to add a new one to this KeepKey")
+ return err
+ elif err in authErrs:
+ error_popup(err, '')
+ return err
+ elif err != '':
+ error_popup(err, '')
+ return 'usb err'
+ return 'noerr'
+
+ def auth_otp(self, client, domain, account):
+ interval = 30 # 30 second interval
+ T0 = datetime.now().timestamp()
+ Tslice = int(T0/interval)
+ Tremain = interval - int((int(T0) - Tslice*30))
+ if _test: print(b'\x16' + bytes("generateOTPFrom:"+domain+":"+account+":", 'utf8') +
+ bytes(str(Tslice), 'utf8') + bytes(":" + str(Tremain), 'utf8'))
+ retval, err = self.sendMsg(client,
+ msg = b'\x16' + bytes("generateOTPFrom:"+domain+":"+account+":", 'utf8') +
+ bytes(str(Tslice), 'utf8') + bytes(":" + str(Tremain), 'utf8')
+ )
+ if err in authErrs:
+ error_popup(err, '')
+ return err
+ elif err != '':
+ error_popup(err, '')
+ return 'usb err'
+
+ if _test: print("OTP return: "+retval)
+
+ return 'noerr'
+
+ def auth_accGet(self, client):
+ ctr=0
+ accounts = list()
+ while True:
+ retval, err = self.sendMsg(client, msg = b'\x17' + bytes("getAccount:"+str(ctr), 'utf8'))
+ if err == '':
+ accounts.append([ctr, retval])
+ elif err == 'Account not found':
+ accounts.append([ctr, ''])
+ elif err == 'Slot request out of range':
+ break
+ elif err == 'Device not initialized':
+ error_popup(err, 'Initialize KeepKey prior to using authentication feature')
+ break
+ elif err in authErrs:
+ error_popup(err, '')
+ return accounts, err
+ else:
+ error_popup(err, '')
+ return accounts, 'usb err'
+
+ ctr+=1
+
+ return accounts, 'noerr'
+
+ def auth_accRem(self, client, domain, account):
+ retval, err = self.sendMsg(client, msg = b'\x18' + bytes("removeAccount:"+domain+":"+account, 'utf8'))
+ if err in authErrs:
+ error_popup(E.args[1], domain+":"+account)
+ elif err != '':
+ error_popup(err, '')
+ return 'usb err'
+
+ return 'noerr'
+
+ def auth_wipe(self, client):
+ retval, err = self.sendMsg(client, msg = b'\x19' + bytes("wipeAuthdata:", 'utf8'))
+ if err in authErrs:
+ error_popup(E.args[1])
+ elif err != '':
+ error_popup(err, '')
+ return 'usb err'
+
+ return 'noerr'
+
+ def auth_test(self, client):
+ # otpauth://totp/KeepKey:markrypto?secret=ZKLHM3W3XAHG4CBN&issuer=kk
+ for msg in (
+ b'\x15' + bytes("initializeAuth:"+"KeepKey"+":"+"markrypto"+":"+"ZKLHM3W3XAHG4CBN", 'utf8'),
+ b'\x15' + bytes("initializeAuth:"+"Shapeshift"+":"+"markrypto"+":"+"BASE32SECRET2345AB", 'utf8'),
+ b'\x15' + bytes("initializeAuth:"+"KeepKey"+":"+"markrypto2"+":"+"JBSWY3DPEHPK3PXP", 'utf8'),
+ # 160-bit key
+ b'\x15' + bytes("initializeAuth:"+"Google"+":"+"novicecoingroup"+":"+"liabmylfzm3qta2txzqgcunbp3y76pkb", 'utf8')
+ ):
+ retval, err = self.sendMsg(client, msg)
+ if err == 'Authenticator secret storage full':
+ error_popup(err, "Need to remove an account to add a new one to this KeepKey")
+ return err
+ elif err in authErrs:
+ error_popup(err, '')
+ return err
+ elif err != '':
+ error_popup(err, '')
+ return 'usb err'
+
+ return 'noerr'
+
+ # interval = 30 # 30 second interval
+ # #T0 = 1535317397
+ # T0 = 1536262427
+
+ # T = int(T0/interval).to_bytes(8, byteorder='big')
+ # retval = client.ping(
+ # msg = b'\x16' + bytes("generateOTPFrom:" + "python-test:", 'utf8') + binascii.hexlify(bytearray(T))
+ # )
+ # if _test: print(retval)
+ # if _test: print("should be 007767")
+
+def main():
+ app = QtWidgets.QApplication(sys.argv)
+ MainWindow = QtWidgets.QMainWindow()
+ ui = Ui()
+ ui.setupUi(MainWindow)
+ sys.exit(app.exec())
+
+if __name__ == '__main__':
+ main()
+
+
diff --git a/authenticator/ManualAddAcc.ui b/authenticator/ManualAddAcc.ui
new file mode 100644
index 00000000..d690b47e
--- /dev/null
+++ b/authenticator/ManualAddAcc.ui
@@ -0,0 +1,230 @@
+
+
+ ManualAddAccDialog
+
+
+
+ 0
+ 0
+ 498
+ 464
+
+
+
+ Add Account
+
+
+ background-color:rgb(40, 40, 40);
+
+
+ true
+
+
+
+
+ 50
+ 10
+ 401
+ 61
+
+
+
+
+ 18
+
+
+
+ color: rgb(255, 255, 255);
+
+
+
+ <html><head/><body><p>Add Account</p><p>Use manual entry if there is no QR code available.</p></body></html>
+
+
+ Qt::RichText
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+ true
+
+
+
+
+
+ 50
+ 130
+ 411
+ 25
+
+
+
+
+ 20
+
+
+
+ color: rgb(0, 255, 0);
+
+
+
+
+
+ 50
+ 100
+ 71
+ 21
+
+
+
+
+ 18
+
+
+
+ color: rgb(255, 255, 255);
+
+
+
+ <html><head/><body><p>Issuer</p></body></html>
+
+
+ Qt::RichText
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+ true
+
+
+
+
+
+ 50
+ 175
+ 121
+ 21
+
+
+
+
+ 18
+
+
+
+ color: rgb(255, 255, 255);
+
+
+
+ <html><head/><body><p>Account name</p></body></html>
+
+
+ Qt::RichText
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+ true
+
+
+
+
+
+ 50
+ 245
+ 91
+ 21
+
+
+
+
+ 18
+
+
+
+ color: rgb(255, 255, 255);
+
+
+
+ <html><head/><body><p>Secret key</p></body></html>
+
+
+ Qt::RichText
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+ true
+
+
+
+
+
+ 50
+ 330
+ 111
+ 70
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 1px;
+border-style: solid;
+border-color: rgb(0, 255, 0);
+color: rgb(0, 255, 0);
+text-align:center;
+
+
+ ADD
+
+
+
+
+
+ 50
+ 205
+ 411
+ 25
+
+
+
+
+ 20
+
+
+
+ color: rgb(0, 255, 0);
+
+
+
+
+
+ 50
+ 275
+ 411
+ 25
+
+
+
+
+ 20
+
+
+
+ color: rgb(0, 255, 0);
+
+
+
+
+
+
diff --git a/authenticator/PinUi.ui b/authenticator/PinUi.ui
new file mode 100644
index 00000000..c8ea7b15
--- /dev/null
+++ b/authenticator/PinUi.ui
@@ -0,0 +1,354 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 576
+ 634
+
+
+
+ PIN Entry
+
+
+ background-color: rgb(1, 0, 0);
+
+
+ true
+
+
+
+
+ 110
+ 140
+ 101
+ 91
+
+
+
+ border-radius: 10px;
+background-color: rgb(103, 103, 103);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+
+
+
+
+
+
+ circle-32.pngcircle-32.png
+
+
+
+ 32
+ 32
+
+
+
+
+
+
+ 110
+ 250
+ 101
+ 91
+
+
+
+ border-radius: 10px;
+background-color: rgb(103, 103, 103);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+
+
+
+ circle-32.pngcircle-32.png
+
+
+
+ 32
+ 32
+
+
+
+
+
+
+ 110
+ 360
+ 101
+ 91
+
+
+
+ border-radius: 10px;
+background-color: rgb(103, 103, 103);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+
+
+
+
+
+
+ circle-32.pngcircle-32.png
+
+
+
+ 32
+ 32
+
+
+
+
+
+
+ 240
+ 140
+ 101
+ 91
+
+
+
+ border-radius: 10px;
+background-color: rgb(103, 103, 103);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+
+
+
+ circle-32.pngcircle-32.png
+
+
+
+ 32
+ 32
+
+
+
+
+
+
+ 240
+ 250
+ 101
+ 91
+
+
+
+ border-radius: 10px;
+background-color: rgb(103, 103, 103);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+
+
+
+
+
+
+ circle-32.pngcircle-32.png
+
+
+
+ 32
+ 32
+
+
+
+
+
+
+ 240
+ 360
+ 101
+ 91
+
+
+
+ border-radius: 10px;
+background-color: rgb(103, 103, 103);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+
+
+
+
+
+
+ circle-32.pngcircle-32.png
+
+
+
+ 32
+ 32
+
+
+
+
+
+
+ 370
+ 140
+ 101
+ 91
+
+
+
+ border-radius: 10px;
+background-color: rgb(103, 103, 103);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+
+
+
+ circle-32.pngcircle-32.png
+
+
+
+ 32
+ 32
+
+
+
+
+
+
+ 370
+ 250
+ 101
+ 91
+
+
+
+ border-radius: 10px;
+background-color: rgb(103, 103, 103);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+
+
+
+
+
+
+ circle-32.pngcircle-32.png
+
+
+
+ 32
+ 32
+
+
+
+
+
+
+ 370
+ 360
+ 101
+ 91
+
+
+
+ border-radius: 10px;
+background-color: rgb(103, 103, 103);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+
+
+
+
+
+
+ circle-32.pngcircle-32.png
+
+
+
+ 32
+ 32
+
+
+
+
+
+
+ 60
+ 10
+ 441
+ 121
+
+
+
+ color: rgb(255, 255, 255);
+
+
+ <html><head/><body><p><span style=" font-size:24pt; font-weight:600;">Enter Your PIN</span></p><p><span style=" font-size:18pt; font-weight:600;">Use PIN layout shown on your device to find the </span></p><p><span style=" font-size:18pt; font-weight:600;">location to press on the PIN pad.</span></p></body></html>
+
+
+
+
+
+ 65
+ 550
+ 440
+ 60
+
+
+
+
+ 24
+
+
+
+ background-color: rgb(184, 155, 104);
+border-radius: 10px;
+color: rgb(255, 255,255);
+
+
+ Unlock
+
+
+
+
+
+ 65
+ 470
+ 440
+ 60
+
+
+
+
+ 24
+
+
+
+ background-color: rgb(1, 0, 0);
+border-radius: 10px;
+color: rgb(255, 255,255);
+border-width: 2px;
+border-style: solid;
+border-color: rgb(103, 103, 103);
+
+
+ 10
+
+
+ QLineEdit::Password
+
+
+
+
+
+
diff --git a/authenticator/RemAccDialog.ui b/authenticator/RemAccDialog.ui
new file mode 100644
index 00000000..11402a76
--- /dev/null
+++ b/authenticator/RemAccDialog.ui
@@ -0,0 +1,384 @@
+
+
+ RemAccDialog
+
+
+
+ 0
+ 0
+ 465
+ 540
+
+
+
+ Remove Account
+
+
+ background-color: rgb(1, 0, 0);
+
+
+ true
+
+
+
+
+ 50
+ 80
+ 350
+ 40
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ RemoveAccButtonGroup
+
+
+
+
+
+ 50
+ 20
+ 130
+ 45
+
+
+
+
+ 32
+
+
+
+ color: rgb(255, 255, 255);
+
+
+
+ Accounts
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+
+
+
+ 195
+ 20
+ 220
+ 45
+
+
+
+
+ 18
+
+
+
+ color: rgb(255, 255, 255);
+
+
+
+ Click on account to PERMANENTLY remove
+
+
+ Qt::RichText
+
+
+ Qt::AlignHCenter|Qt::AlignTop
+
+
+ true
+
+
+
+
+
+ 50
+ 125
+ 350
+ 40
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ RemoveAccButtonGroup
+
+
+
+
+
+ 50
+ 170
+ 350
+ 40
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ RemoveAccButtonGroup
+
+
+
+
+
+ 50
+ 215
+ 350
+ 40
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ RemoveAccButtonGroup
+
+
+
+
+
+ 50
+ 260
+ 350
+ 40
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ RemoveAccButtonGroup
+
+
+
+
+
+ 50
+ 305
+ 350
+ 40
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ RemoveAccButtonGroup
+
+
+
+
+
+ 50
+ 350
+ 350
+ 40
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ RemoveAccButtonGroup
+
+
+
+
+
+ 50
+ 395
+ 350
+ 40
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ RemoveAccButtonGroup
+
+
+
+
+
+ 50
+ 440
+ 350
+ 40
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ RemoveAccButtonGroup
+
+
+
+
+
+ 50
+ 485
+ 350
+ 40
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 2px;
+border-style: solid;
+border-color: black;
+color: rgb(0, 255, 0);
+text-align:left;
+
+
+
+
+
+ RemoveAccButtonGroup
+
+
+
+
+
+
+
+
+
diff --git a/authenticator/__init__.py b/authenticator/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/authenticator/addaccdialog.py b/authenticator/addaccdialog.py
new file mode 100644
index 00000000..0d79d588
--- /dev/null
+++ b/authenticator/addaccdialog.py
@@ -0,0 +1,74 @@
+# Form implementation generated from reading ui file 'AddAccDialog.ui'
+#
+# Created by: PyQt6 UI code generator 6.1.0
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic6 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+
+class Ui_AddAccDialog(object):
+ def setupUi(self, AddAccDialog):
+ AddAccDialog.setObjectName("AddAccDialog")
+ AddAccDialog.resize(450, 330)
+ AddAccDialog.setStyleSheet("background-color:rgb(40, 40, 40);")
+ AddAccDialog.setModal(True)
+ self.ScanQrButton = QtWidgets.QPushButton(AddAccDialog)
+ self.ScanQrButton.setGeometry(QtCore.QRect(125, 30, 200, 70))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.ScanQrButton.setFont(font)
+ self.ScanQrButton.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 1px;\n"
+"border-style: solid;\n"
+"border-color: rgb(0, 255, 0);\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:center;")
+ self.ScanQrButton.setObjectName("ScanQrButton")
+ self.QR_instructions = QtWidgets.QLabel(AddAccDialog)
+ self.QR_instructions.setGeometry(QtCore.QRect(110, 110, 220, 45))
+ font = QtGui.QFont()
+ font.setPointSize(18)
+ self.QR_instructions.setFont(font)
+ self.QR_instructions.setStyleSheet("color: rgb(255, 255, 255);\n"
+"")
+ self.QR_instructions.setTextFormat(QtCore.Qt.TextFormat.RichText)
+ self.QR_instructions.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop)
+ self.QR_instructions.setWordWrap(True)
+ self.QR_instructions.setObjectName("QR_instructions")
+ self.EnterManuallyButton = QtWidgets.QPushButton(AddAccDialog)
+ self.EnterManuallyButton.setGeometry(QtCore.QRect(125, 210, 200, 70))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.EnterManuallyButton.setFont(font)
+ self.EnterManuallyButton.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 1px;\n"
+"border-style: solid;\n"
+"border-color: rgb(0, 255, 0);\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:center;")
+ self.EnterManuallyButton.setObjectName("EnterManuallyButton")
+
+ self.retranslateUi(AddAccDialog)
+ QtCore.QMetaObject.connectSlotsByName(AddAccDialog)
+
+ def retranslateUi(self, AddAccDialog):
+ _translate = QtCore.QCoreApplication.translate
+ AddAccDialog.setWindowTitle(_translate("AddAccDialog", "Add Account"))
+ self.ScanQrButton.setText(_translate("AddAccDialog", "Scan QR"))
+ self.QR_instructions.setText(_translate("AddAccDialog", "Make sure QR code is fully visible on main screen"))
+ self.EnterManuallyButton.setText(_translate("AddAccDialog", "Enter Manually"))
+
+
+if __name__ == "__main__":
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+ AddAccDialog = QtWidgets.QDialog()
+ ui = Ui_AddAccDialog()
+ ui.setupUi(AddAccDialog)
+ AddAccDialog.show()
+ sys.exit(app.exec())
diff --git a/authenticator/authResources/circle-32.png b/authenticator/authResources/circle-32.png
new file mode 100644
index 00000000..0c6eea0e
Binary files /dev/null and b/authenticator/authResources/circle-32.png differ
diff --git a/authenticator/authResources/kk-icon-gold.png b/authenticator/authResources/kk-icon-gold.png
new file mode 100644
index 00000000..0d5b7531
Binary files /dev/null and b/authenticator/authResources/kk-icon-gold.png differ
diff --git a/authenticator/authmain.py b/authenticator/authmain.py
new file mode 100644
index 00000000..22e92aa4
--- /dev/null
+++ b/authenticator/authmain.py
@@ -0,0 +1,343 @@
+# Form implementation generated from reading ui file 'AuthMain.ui'
+#
+# Created by: PyQt6 UI code generator 6.1.0
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic6 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+
+class Ui_MainWindow(object):
+ def setupUi(self, MainWindow):
+ MainWindow.setObjectName("MainWindow")
+ MainWindow.resize(772, 828)
+ icon = QtGui.QIcon()
+ icon.addPixmap(QtGui.QPixmap("kk-icon-gold.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
+ MainWindow.setWindowIcon(icon)
+ MainWindow.setStyleSheet("background-color: rgb(1, 0, 0);")
+ self.centralwidget = QtWidgets.QWidget(MainWindow)
+ self.centralwidget.setObjectName("centralwidget")
+ self.kklogo = QtWidgets.QLabel(self.centralwidget)
+ self.kklogo.setGeometry(QtCore.QRect(30, 30, 200, 160))
+ self.kklogo.setText("")
+ self.kklogo.setPixmap(QtGui.QPixmap("kk-icon-gold.png"))
+ self.kklogo.setObjectName("kklogo")
+ self.TitleDesc = QtWidgets.QLabel(self.centralwidget)
+ self.TitleDesc.setGeometry(QtCore.QRect(290, 30, 460, 80))
+ font = QtGui.QFont()
+ font.setPointSize(32)
+ self.TitleDesc.setFont(font)
+ self.TitleDesc.setStyleSheet("color: rgb(255, 255, 255);")
+ self.TitleDesc.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
+ self.TitleDesc.setWordWrap(True)
+ self.TitleDesc.setObjectName("TitleDesc")
+ self.Author = QtWidgets.QLabel(self.centralwidget)
+ self.Author.setGeometry(QtCore.QRect(290, 150, 150, 40))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.Author.setFont(font)
+ self.Author.setStyleSheet("color: rgb(55, 62, 255);")
+ self.Author.setObjectName("Author")
+ self.line = QtWidgets.QFrame(self.centralwidget)
+ self.line.setGeometry(QtCore.QRect(30, 200, 720, 16))
+ self.line.setStyleSheet("color: rgb(255, 255, 255)")
+ self.line.setFrameShadow(QtWidgets.QFrame.Shadow.Plain)
+ self.line.setLineWidth(1)
+ self.line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
+ self.line.setObjectName("line")
+ self.ConnectKKButton = QtWidgets.QPushButton(self.centralwidget)
+ self.ConnectKKButton.setGeometry(QtCore.QRect(610, 120, 140, 70))
+ font = QtGui.QFont()
+ font.setPointSize(20)
+ self.ConnectKKButton.setFont(font)
+ self.ConnectKKButton.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(255, 128, 4);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(255, 255, 255)")
+ self.ConnectKKButton.setAutoDefault(True)
+ self.ConnectKKButton.setObjectName("ConnectKKButton")
+ self.AddAccButton = QtWidgets.QPushButton(self.centralwidget)
+ self.AddAccButton.setGeometry(QtCore.QRect(30, 220, 200, 50))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.AddAccButton.setFont(font)
+ self.AddAccButton.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(103, 103, 103);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(255, 255, 255)")
+ self.AddAccButton.setAutoDefault(True)
+ self.AddAccButton.setObjectName("AddAccButton")
+ self.testButton = QtWidgets.QPushButton(self.centralwidget)
+ self.testButton.setGeometry(QtCore.QRect(30, 480, 200, 50))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.testButton.setFont(font)
+ self.testButton.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(103, 103, 103);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(255, 255, 255);\n"
+"QPushButton::pressed { \n"
+" background-color: rgb(35, 40, 49);\n"
+" border: 2px solid rgb(43, 50, 61);\n"
+" }")
+ self.testButton.setAutoDefault(True)
+ self.testButton.setObjectName("testButton")
+ self.AccountList = QtWidgets.QLabel(self.centralwidget)
+ self.AccountList.setGeometry(QtCore.QRect(380, 220, 130, 45))
+ font = QtGui.QFont()
+ font.setPointSize(32)
+ self.AccountList.setFont(font)
+ self.AccountList.setStyleSheet("color: rgb(255, 255, 255);\n"
+"")
+ self.AccountList.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
+ self.AccountList.setObjectName("AccountList")
+ self.RemoveAccButton = QtWidgets.QPushButton(self.centralwidget)
+ self.RemoveAccButton.setGeometry(QtCore.QRect(30, 300, 200, 50))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.RemoveAccButton.setFont(font)
+ self.RemoveAccButton.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(103, 103, 103);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(255, 255, 255)")
+ self.RemoveAccButton.setAutoDefault(True)
+ self.RemoveAccButton.setObjectName("RemoveAccButton")
+ self.OTPAcc_1 = QtWidgets.QPushButton(self.centralwidget)
+ self.OTPAcc_1.setGeometry(QtCore.QRect(380, 270, 351, 41))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.OTPAcc_1.setFont(font)
+ self.OTPAcc_1.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.OTPAcc_1.setText("")
+ self.OTPAcc_1.setAutoDefault(True)
+ self.OTPAcc_1.setObjectName("OTPAcc_1")
+ self.OTPButtonGroup = QtWidgets.QButtonGroup(MainWindow)
+ self.OTPButtonGroup.setObjectName("OTPButtonGroup")
+ self.OTPButtonGroup.addButton(self.OTPAcc_1)
+ self.AccountList_2 = QtWidgets.QLabel(self.centralwidget)
+ self.AccountList_2.setGeometry(QtCore.QRect(525, 220, 220, 45))
+ font = QtGui.QFont()
+ font.setPointSize(18)
+ self.AccountList_2.setFont(font)
+ self.AccountList_2.setStyleSheet("color: rgb(255, 255, 255);\n"
+"")
+ self.AccountList_2.setTextFormat(QtCore.Qt.TextFormat.RichText)
+ self.AccountList_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop)
+ self.AccountList_2.setWordWrap(True)
+ self.AccountList_2.setObjectName("AccountList_2")
+ self.OTPAcc_2 = QtWidgets.QPushButton(self.centralwidget)
+ self.OTPAcc_2.setGeometry(QtCore.QRect(380, 320, 351, 41))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.OTPAcc_2.setFont(font)
+ self.OTPAcc_2.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.OTPAcc_2.setText("")
+ self.OTPAcc_2.setAutoDefault(True)
+ self.OTPAcc_2.setObjectName("OTPAcc_2")
+ self.OTPButtonGroup.addButton(self.OTPAcc_2)
+ self.OTPAcc_3 = QtWidgets.QPushButton(self.centralwidget)
+ self.OTPAcc_3.setGeometry(QtCore.QRect(380, 370, 351, 41))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.OTPAcc_3.setFont(font)
+ self.OTPAcc_3.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.OTPAcc_3.setText("")
+ self.OTPAcc_3.setAutoDefault(True)
+ self.OTPAcc_3.setObjectName("OTPAcc_3")
+ self.OTPButtonGroup.addButton(self.OTPAcc_3)
+ self.OTPAcc_4 = QtWidgets.QPushButton(self.centralwidget)
+ self.OTPAcc_4.setGeometry(QtCore.QRect(380, 420, 351, 41))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.OTPAcc_4.setFont(font)
+ self.OTPAcc_4.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.OTPAcc_4.setText("")
+ self.OTPAcc_4.setAutoDefault(True)
+ self.OTPAcc_4.setObjectName("OTPAcc_4")
+ self.OTPButtonGroup.addButton(self.OTPAcc_4)
+ self.OTPAcc_5 = QtWidgets.QPushButton(self.centralwidget)
+ self.OTPAcc_5.setGeometry(QtCore.QRect(380, 470, 351, 41))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.OTPAcc_5.setFont(font)
+ self.OTPAcc_5.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.OTPAcc_5.setText("")
+ self.OTPAcc_5.setAutoDefault(True)
+ self.OTPAcc_5.setObjectName("OTPAcc_5")
+ self.OTPButtonGroup.addButton(self.OTPAcc_5)
+ self.OTPAcc_6 = QtWidgets.QPushButton(self.centralwidget)
+ self.OTPAcc_6.setGeometry(QtCore.QRect(380, 520, 351, 41))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.OTPAcc_6.setFont(font)
+ self.OTPAcc_6.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.OTPAcc_6.setText("")
+ self.OTPAcc_6.setAutoDefault(True)
+ self.OTPAcc_6.setObjectName("OTPAcc_6")
+ self.OTPButtonGroup.addButton(self.OTPAcc_6)
+ self.OTPAcc_7 = QtWidgets.QPushButton(self.centralwidget)
+ self.OTPAcc_7.setGeometry(QtCore.QRect(380, 570, 351, 41))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.OTPAcc_7.setFont(font)
+ self.OTPAcc_7.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.OTPAcc_7.setText("")
+ self.OTPAcc_7.setAutoDefault(True)
+ self.OTPAcc_7.setObjectName("OTPAcc_7")
+ self.OTPButtonGroup.addButton(self.OTPAcc_7)
+ self.OTPAcc_8 = QtWidgets.QPushButton(self.centralwidget)
+ self.OTPAcc_8.setGeometry(QtCore.QRect(380, 620, 351, 41))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.OTPAcc_8.setFont(font)
+ self.OTPAcc_8.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.OTPAcc_8.setText("")
+ self.OTPAcc_8.setAutoDefault(True)
+ self.OTPAcc_8.setObjectName("OTPAcc_8")
+ self.OTPButtonGroup.addButton(self.OTPAcc_8)
+ self.OTPAcc_9 = QtWidgets.QPushButton(self.centralwidget)
+ self.OTPAcc_9.setGeometry(QtCore.QRect(380, 670, 351, 41))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.OTPAcc_9.setFont(font)
+ self.OTPAcc_9.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.OTPAcc_9.setText("")
+ self.OTPAcc_9.setAutoDefault(True)
+ self.OTPAcc_9.setObjectName("OTPAcc_9")
+ self.OTPButtonGroup.addButton(self.OTPAcc_9)
+ self.OTPAcc_10 = QtWidgets.QPushButton(self.centralwidget)
+ self.OTPAcc_10.setGeometry(QtCore.QRect(380, 720, 351, 41))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.OTPAcc_10.setFont(font)
+ self.OTPAcc_10.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.OTPAcc_10.setText("")
+ self.OTPAcc_10.setAutoDefault(True)
+ self.OTPAcc_10.setObjectName("OTPAcc_10")
+ self.OTPButtonGroup.addButton(self.OTPAcc_10)
+ self.Version = QtWidgets.QLabel(self.centralwidget)
+ self.Version.setGeometry(QtCore.QRect(290, 110, 220, 40))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.Version.setFont(font)
+ self.Version.setStyleSheet("color: rgb(255, 255, 255);")
+ self.Version.setText("")
+ self.Version.setObjectName("Version")
+ self.WipeAccButton = QtWidgets.QPushButton(self.centralwidget)
+ self.WipeAccButton.setGeometry(QtCore.QRect(30, 380, 200, 50))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.WipeAccButton.setFont(font)
+ self.WipeAccButton.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(103, 103, 103);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(255, 255, 255)")
+ self.WipeAccButton.setAutoDefault(True)
+ self.WipeAccButton.setObjectName("WipeAccButton")
+ MainWindow.setCentralWidget(self.centralwidget)
+ self.menubar = QtWidgets.QMenuBar(MainWindow)
+ self.menubar.setGeometry(QtCore.QRect(0, 0, 772, 21))
+ self.menubar.setObjectName("menubar")
+ MainWindow.setMenuBar(self.menubar)
+ self.statusbar = QtWidgets.QStatusBar(MainWindow)
+ self.statusbar.setObjectName("statusbar")
+ MainWindow.setStatusBar(self.statusbar)
+
+ self.retranslateUi(MainWindow)
+ QtCore.QMetaObject.connectSlotsByName(MainWindow)
+
+ def retranslateUi(self, MainWindow):
+ _translate = QtCore.QCoreApplication.translate
+ MainWindow.setWindowTitle(_translate("MainWindow", "KeepKey Authenticator"))
+ self.TitleDesc.setText(_translate("MainWindow", "Authenticator for the KeepKey Python client"))
+ self.Author.setText(_translate("MainWindow", "by markrypto"))
+ self.ConnectKKButton.setText(_translate("MainWindow", "Connect\n"
+"KeepKey"))
+ self.AddAccButton.setText(_translate("MainWindow", "Add Account"))
+ self.testButton.setText(_translate("MainWindow", "test"))
+ self.AccountList.setText(_translate("MainWindow", "Accounts"))
+ self.RemoveAccButton.setText(_translate("MainWindow", "Remove Account"))
+ self.AccountList_2.setText(_translate("MainWindow", "Click on account to generate OTP on KeepKey"))
+ self.WipeAccButton.setText(_translate("MainWindow", "Wipe All Accounts"))
+
+
+if __name__ == "__main__":
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+ MainWindow = QtWidgets.QMainWindow()
+ ui = Ui_MainWindow()
+ ui.setupUi(MainWindow)
+ MainWindow.show()
+ sys.exit(app.exec())
diff --git a/authenticator/circle-32.png b/authenticator/circle-32.png
new file mode 100644
index 00000000..0c6eea0e
Binary files /dev/null and b/authenticator/circle-32.png differ
diff --git a/authenticator/kk-icon-gold.png b/authenticator/kk-icon-gold.png
new file mode 100644
index 00000000..0d5b7531
Binary files /dev/null and b/authenticator/kk-icon-gold.png differ
diff --git a/authenticator/kkQRtest.png b/authenticator/kkQRtest.png
new file mode 100644
index 00000000..80c90f2b
Binary files /dev/null and b/authenticator/kkQRtest.png differ
diff --git a/authenticator/kkQRtest1.png b/authenticator/kkQRtest1.png
new file mode 100644
index 00000000..2eeb1e09
Binary files /dev/null and b/authenticator/kkQRtest1.png differ
diff --git a/authenticator/kkQRtest2.png b/authenticator/kkQRtest2.png
new file mode 100644
index 00000000..dbc71d2e
Binary files /dev/null and b/authenticator/kkQRtest2.png differ
diff --git a/authenticator/manualaddacc.py b/authenticator/manualaddacc.py
new file mode 100644
index 00000000..c6fc964f
--- /dev/null
+++ b/authenticator/manualaddacc.py
@@ -0,0 +1,117 @@
+# Form implementation generated from reading ui file 'ManualAddAcc.ui'
+#
+# Created by: PyQt6 UI code generator 6.1.0
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic6 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+
+class Ui_ManualAddAccDialog(object):
+ def setupUi(self, ManualAddAccDialog):
+ ManualAddAccDialog.setObjectName("ManualAddAccDialog")
+ ManualAddAccDialog.resize(498, 464)
+ ManualAddAccDialog.setStyleSheet("background-color:rgb(40, 40, 40);")
+ ManualAddAccDialog.setModal(True)
+ self.QR_instructions = QtWidgets.QLabel(ManualAddAccDialog)
+ self.QR_instructions.setGeometry(QtCore.QRect(50, 10, 401, 61))
+ font = QtGui.QFont()
+ font.setPointSize(18)
+ self.QR_instructions.setFont(font)
+ self.QR_instructions.setStyleSheet("color: rgb(255, 255, 255);\n"
+"")
+ self.QR_instructions.setTextFormat(QtCore.Qt.TextFormat.RichText)
+ self.QR_instructions.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
+ self.QR_instructions.setWordWrap(True)
+ self.QR_instructions.setObjectName("QR_instructions")
+ self.IssuerlineEdit = QtWidgets.QLineEdit(ManualAddAccDialog)
+ self.IssuerlineEdit.setGeometry(QtCore.QRect(50, 130, 411, 25))
+ font = QtGui.QFont()
+ font.setPointSize(20)
+ self.IssuerlineEdit.setFont(font)
+ self.IssuerlineEdit.setStyleSheet("color: rgb(0, 255, 0);")
+ self.IssuerlineEdit.setObjectName("IssuerlineEdit")
+ self.IssuerLabel = QtWidgets.QLabel(ManualAddAccDialog)
+ self.IssuerLabel.setGeometry(QtCore.QRect(50, 100, 71, 21))
+ font = QtGui.QFont()
+ font.setPointSize(18)
+ self.IssuerLabel.setFont(font)
+ self.IssuerLabel.setStyleSheet("color: rgb(255, 255, 255);\n"
+"")
+ self.IssuerLabel.setTextFormat(QtCore.Qt.TextFormat.RichText)
+ self.IssuerLabel.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
+ self.IssuerLabel.setWordWrap(True)
+ self.IssuerLabel.setObjectName("IssuerLabel")
+ self.AccNameLabel = QtWidgets.QLabel(ManualAddAccDialog)
+ self.AccNameLabel.setGeometry(QtCore.QRect(50, 175, 121, 21))
+ font = QtGui.QFont()
+ font.setPointSize(18)
+ self.AccNameLabel.setFont(font)
+ self.AccNameLabel.setStyleSheet("color: rgb(255, 255, 255);\n"
+"")
+ self.AccNameLabel.setTextFormat(QtCore.Qt.TextFormat.RichText)
+ self.AccNameLabel.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
+ self.AccNameLabel.setWordWrap(True)
+ self.AccNameLabel.setObjectName("AccNameLabel")
+ self.SecretKeyLabel = QtWidgets.QLabel(ManualAddAccDialog)
+ self.SecretKeyLabel.setGeometry(QtCore.QRect(50, 245, 91, 21))
+ font = QtGui.QFont()
+ font.setPointSize(18)
+ self.SecretKeyLabel.setFont(font)
+ self.SecretKeyLabel.setStyleSheet("color: rgb(255, 255, 255);\n"
+"")
+ self.SecretKeyLabel.setTextFormat(QtCore.Qt.TextFormat.RichText)
+ self.SecretKeyLabel.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
+ self.SecretKeyLabel.setWordWrap(True)
+ self.SecretKeyLabel.setObjectName("SecretKeyLabel")
+ self.AddButton = QtWidgets.QPushButton(ManualAddAccDialog)
+ self.AddButton.setGeometry(QtCore.QRect(50, 330, 111, 70))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.AddButton.setFont(font)
+ self.AddButton.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 1px;\n"
+"border-style: solid;\n"
+"border-color: rgb(0, 255, 0);\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:center;")
+ self.AddButton.setObjectName("AddButton")
+ self.AccNamelineEdit = QtWidgets.QLineEdit(ManualAddAccDialog)
+ self.AccNamelineEdit.setGeometry(QtCore.QRect(50, 205, 411, 25))
+ font = QtGui.QFont()
+ font.setPointSize(20)
+ self.AccNamelineEdit.setFont(font)
+ self.AccNamelineEdit.setStyleSheet("color: rgb(0, 255, 0);")
+ self.AccNamelineEdit.setObjectName("AccNamelineEdit")
+ self.SecretlineEdit = QtWidgets.QLineEdit(ManualAddAccDialog)
+ self.SecretlineEdit.setGeometry(QtCore.QRect(50, 275, 411, 25))
+ font = QtGui.QFont()
+ font.setPointSize(20)
+ self.SecretlineEdit.setFont(font)
+ self.SecretlineEdit.setStyleSheet("color: rgb(0, 255, 0);")
+ self.SecretlineEdit.setObjectName("SecretlineEdit")
+
+ self.retranslateUi(ManualAddAccDialog)
+ QtCore.QMetaObject.connectSlotsByName(ManualAddAccDialog)
+
+ def retranslateUi(self, ManualAddAccDialog):
+ _translate = QtCore.QCoreApplication.translate
+ ManualAddAccDialog.setWindowTitle(_translate("ManualAddAccDialog", "Add Account"))
+ self.QR_instructions.setText(_translate("ManualAddAccDialog", "
Add Account
Use manual entry if there is no QR code available.
"))
+ self.IssuerLabel.setText(_translate("ManualAddAccDialog", "Issuer
"))
+ self.AccNameLabel.setText(_translate("ManualAddAccDialog", "Account name
"))
+ self.SecretKeyLabel.setText(_translate("ManualAddAccDialog", "Secret key
"))
+ self.AddButton.setText(_translate("ManualAddAccDialog", "ADD"))
+
+
+if __name__ == "__main__":
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+ ManualAddAccDialog = QtWidgets.QDialog()
+ ui = Ui_ManualAddAccDialog()
+ ui.setupUi(ManualAddAccDialog)
+ ManualAddAccDialog.show()
+ sys.exit(app.exec())
diff --git a/authenticator/passphraseDialog.ui b/authenticator/passphraseDialog.ui
new file mode 100644
index 00000000..2f09cd5e
--- /dev/null
+++ b/authenticator/passphraseDialog.ui
@@ -0,0 +1,101 @@
+
+
+ PassphraseDialog
+
+
+
+ 0
+ 0
+ 902
+ 163
+
+
+
+ Enter Passphrase
+
+
+ background-color:rgb(40, 40, 40);
+
+
+ true
+
+
+
+
+ 50
+ 10
+ 101
+ 21
+
+
+
+
+ 18
+
+
+
+ color: rgb(255, 255, 255);
+
+
+
+ <html><head/><body><p>Passphrase:</p></body></html>
+
+
+ Qt::RichText
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+ true
+
+
+
+
+
+ 50
+ 40
+ 731
+ 25
+
+
+
+
+ 20
+
+
+
+ color: rgb(0, 255, 0);
+
+
+
+
+
+ 50
+ 80
+ 111
+ 70
+
+
+
+
+ 24
+
+
+
+ border-radius: 10px;
+background-color: rgb(1, 0, 0);
+border-width: 1px;
+border-style: solid;
+border-color: rgb(0, 255, 0);
+color: rgb(0, 255, 0);
+text-align:center;
+
+
+ Enter
+
+
+
+
+
+
diff --git a/authenticator/passphrasedialog.py b/authenticator/passphrasedialog.py
new file mode 100644
index 00000000..abc78e0d
--- /dev/null
+++ b/authenticator/passphrasedialog.py
@@ -0,0 +1,67 @@
+# Form implementation generated from reading ui file 'passphraseDialog.ui'
+#
+# Created by: PyQt6 UI code generator 6.1.0
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic6 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+
+class Ui_PassphraseDialog(object):
+ def setupUi(self, PassphraseDialog):
+ PassphraseDialog.setObjectName("PassphraseDialog")
+ PassphraseDialog.resize(902, 163)
+ PassphraseDialog.setStyleSheet("background-color:rgb(40, 40, 40);")
+ PassphraseDialog.setModal(True)
+ self.passphrase = QtWidgets.QLabel(PassphraseDialog)
+ self.passphrase.setGeometry(QtCore.QRect(50, 10, 101, 21))
+ font = QtGui.QFont()
+ font.setPointSize(18)
+ self.passphrase.setFont(font)
+ self.passphrase.setStyleSheet("color: rgb(255, 255, 255);\n"
+"")
+ self.passphrase.setTextFormat(QtCore.Qt.TextFormat.RichText)
+ self.passphrase.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
+ self.passphrase.setWordWrap(True)
+ self.passphrase.setObjectName("passphrase")
+ self.passphraseLineEdit = QtWidgets.QLineEdit(PassphraseDialog)
+ self.passphraseLineEdit.setGeometry(QtCore.QRect(50, 40, 731, 25))
+ font = QtGui.QFont()
+ font.setPointSize(20)
+ self.passphraseLineEdit.setFont(font)
+ self.passphraseLineEdit.setStyleSheet("color: rgb(0, 255, 0);")
+ self.passphraseLineEdit.setObjectName("passphraseLineEdit")
+ self.EnterButton = QtWidgets.QPushButton(PassphraseDialog)
+ self.EnterButton.setGeometry(QtCore.QRect(50, 80, 111, 70))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.EnterButton.setFont(font)
+ self.EnterButton.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 1px;\n"
+"border-style: solid;\n"
+"border-color: rgb(0, 255, 0);\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:center;")
+ self.EnterButton.setObjectName("EnterButton")
+
+ self.retranslateUi(PassphraseDialog)
+ QtCore.QMetaObject.connectSlotsByName(PassphraseDialog)
+
+ def retranslateUi(self, PassphraseDialog):
+ _translate = QtCore.QCoreApplication.translate
+ PassphraseDialog.setWindowTitle(_translate("PassphraseDialog", "Enter Passphrase"))
+ self.passphrase.setText(_translate("PassphraseDialog", "Passphrase:
"))
+ self.EnterButton.setText(_translate("PassphraseDialog", "Enter"))
+
+
+if __name__ == "__main__":
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+ PassphraseDialog = QtWidgets.QDialog()
+ ui = Ui_PassphraseDialog()
+ ui.setupUi(PassphraseDialog)
+ PassphraseDialog.show()
+ sys.exit(app.exec())
diff --git a/authenticator/pindialog.py b/authenticator/pindialog.py
new file mode 100644
index 00000000..41de6486
--- /dev/null
+++ b/authenticator/pindialog.py
@@ -0,0 +1,161 @@
+# Form implementation generated from reading ui file 'PinUi.ui'
+#
+# Created by: PyQt6 UI code generator 6.1.0
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic6 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Dialog(object):
+ def setupUi(self, Dialog):
+ Dialog.setObjectName("Dialog")
+ Dialog.resize(576, 634)
+ Dialog.setStyleSheet("background-color: rgb(1, 0, 0);")
+ Dialog.setModal(True)
+ self.pushButton_7 = QtWidgets.QPushButton(Dialog)
+ self.pushButton_7.setGeometry(QtCore.QRect(110, 140, 101, 91))
+ self.pushButton_7.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(103, 103, 103);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;")
+ self.pushButton_7.setText("")
+ icon = QtGui.QIcon()
+ icon.addPixmap(QtGui.QPixmap("circle-32.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
+ self.pushButton_7.setIcon(icon)
+ self.pushButton_7.setIconSize(QtCore.QSize(32, 32))
+ self.pushButton_7.setObjectName("pushButton_7")
+ self.pushButton_4 = QtWidgets.QPushButton(Dialog)
+ self.pushButton_4.setGeometry(QtCore.QRect(110, 250, 101, 91))
+ self.pushButton_4.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(103, 103, 103);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;")
+ self.pushButton_4.setIcon(icon)
+ self.pushButton_4.setIconSize(QtCore.QSize(32, 32))
+ self.pushButton_4.setObjectName("pushButton_4")
+ self.pushButton_1 = QtWidgets.QPushButton(Dialog)
+ self.pushButton_1.setGeometry(QtCore.QRect(110, 360, 101, 91))
+ self.pushButton_1.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(103, 103, 103);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;")
+ self.pushButton_1.setText("")
+ self.pushButton_1.setIcon(icon)
+ self.pushButton_1.setIconSize(QtCore.QSize(32, 32))
+ self.pushButton_1.setObjectName("pushButton_1")
+ self.pushButton_8 = QtWidgets.QPushButton(Dialog)
+ self.pushButton_8.setGeometry(QtCore.QRect(240, 140, 101, 91))
+ self.pushButton_8.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(103, 103, 103);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;")
+ self.pushButton_8.setIcon(icon)
+ self.pushButton_8.setIconSize(QtCore.QSize(32, 32))
+ self.pushButton_8.setObjectName("pushButton_8")
+ self.pushButton_5 = QtWidgets.QPushButton(Dialog)
+ self.pushButton_5.setGeometry(QtCore.QRect(240, 250, 101, 91))
+ self.pushButton_5.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(103, 103, 103);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;")
+ self.pushButton_5.setText("")
+ self.pushButton_5.setIcon(icon)
+ self.pushButton_5.setIconSize(QtCore.QSize(32, 32))
+ self.pushButton_5.setObjectName("pushButton_5")
+ self.pushButton_2 = QtWidgets.QPushButton(Dialog)
+ self.pushButton_2.setGeometry(QtCore.QRect(240, 360, 101, 91))
+ self.pushButton_2.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(103, 103, 103);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;")
+ self.pushButton_2.setText("")
+ self.pushButton_2.setIcon(icon)
+ self.pushButton_2.setIconSize(QtCore.QSize(32, 32))
+ self.pushButton_2.setObjectName("pushButton_2")
+ self.pushButton_9 = QtWidgets.QPushButton(Dialog)
+ self.pushButton_9.setGeometry(QtCore.QRect(370, 140, 101, 91))
+ self.pushButton_9.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(103, 103, 103);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;")
+ self.pushButton_9.setIcon(icon)
+ self.pushButton_9.setIconSize(QtCore.QSize(32, 32))
+ self.pushButton_9.setObjectName("pushButton_9")
+ self.pushButton_6 = QtWidgets.QPushButton(Dialog)
+ self.pushButton_6.setGeometry(QtCore.QRect(370, 250, 101, 91))
+ self.pushButton_6.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(103, 103, 103);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;")
+ self.pushButton_6.setText("")
+ self.pushButton_6.setIcon(icon)
+ self.pushButton_6.setIconSize(QtCore.QSize(32, 32))
+ self.pushButton_6.setObjectName("pushButton_6")
+ self.pushButton_3 = QtWidgets.QPushButton(Dialog)
+ self.pushButton_3.setGeometry(QtCore.QRect(370, 360, 101, 91))
+ self.pushButton_3.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(103, 103, 103);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;")
+ self.pushButton_3.setText("")
+ self.pushButton_3.setIcon(icon)
+ self.pushButton_3.setIconSize(QtCore.QSize(32, 32))
+ self.pushButton_3.setObjectName("pushButton_3")
+ self.label = QtWidgets.QLabel(Dialog)
+ self.label.setGeometry(QtCore.QRect(60, 10, 441, 121))
+ self.label.setStyleSheet("color: rgb(255, 255, 255);")
+ self.label.setObjectName("label")
+ self.pbUnlock = QtWidgets.QPushButton(Dialog)
+ self.pbUnlock.setGeometry(QtCore.QRect(65, 550, 440, 60))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.pbUnlock.setFont(font)
+ self.pbUnlock.setStyleSheet("background-color: rgb(184, 155, 104);\n"
+"border-radius: 10px;\n"
+"color: rgb(255, 255,255);")
+ self.pbUnlock.setObjectName("pbUnlock")
+ self.lineEdit = QtWidgets.QLineEdit(Dialog)
+ self.lineEdit.setGeometry(QtCore.QRect(65, 470, 440, 60))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.lineEdit.setFont(font)
+ self.lineEdit.setStyleSheet("background-color: rgb(1, 0, 0);\n"
+"border-radius: 10px;\n"
+"color: rgb(255, 255,255);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: rgb(103, 103, 103);")
+ self.lineEdit.setMaxLength(10)
+ self.lineEdit.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password)
+ self.lineEdit.setObjectName("lineEdit")
+
+ self.retranslateUi(Dialog)
+ QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+ def retranslateUi(self, Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Dialog.setWindowTitle(_translate("Dialog", "PIN Entry"))
+ self.label.setText(_translate("Dialog", "Enter Your PIN
Use PIN layout shown on your device to find the
location to press on the PIN pad.
"))
+ self.pbUnlock.setText(_translate("Dialog", "Unlock"))
+
+
+if __name__ == "__main__":
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+ Dialog = QtWidgets.QDialog()
+ ui = Ui_Dialog()
+ ui.setupUi(Dialog)
+ Dialog.show()
+ sys.exit(app.exec())
diff --git a/authenticator/remaccdialog.py b/authenticator/remaccdialog.py
new file mode 100644
index 00000000..4c5ee9a1
--- /dev/null
+++ b/authenticator/remaccdialog.py
@@ -0,0 +1,208 @@
+# Form implementation generated from reading ui file 'RemAccDialog.ui'
+#
+# Created by: PyQt6 UI code generator 6.1.0
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic6 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+
+class Ui_RemAccDialog(object):
+ def setupUi(self, RemAccDialog):
+ RemAccDialog.setObjectName("RemAccDialog")
+ RemAccDialog.resize(465, 540)
+ RemAccDialog.setStyleSheet("background-color: rgb(1, 0, 0);")
+ RemAccDialog.setModal(True)
+ self.RemAccButton_1 = QtWidgets.QPushButton(RemAccDialog)
+ self.RemAccButton_1.setGeometry(QtCore.QRect(50, 80, 350, 40))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.RemAccButton_1.setFont(font)
+ self.RemAccButton_1.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.RemAccButton_1.setText("")
+ self.RemAccButton_1.setObjectName("RemAccButton_1")
+ self.RemoveAccButtonGroup = QtWidgets.QButtonGroup(RemAccDialog)
+ self.RemoveAccButtonGroup.setObjectName("RemoveAccButtonGroup")
+ self.RemoveAccButtonGroup.addButton(self.RemAccButton_1)
+ self.AccountList = QtWidgets.QLabel(RemAccDialog)
+ self.AccountList.setGeometry(QtCore.QRect(50, 20, 130, 45))
+ font = QtGui.QFont()
+ font.setPointSize(32)
+ self.AccountList.setFont(font)
+ self.AccountList.setStyleSheet("color: rgb(255, 255, 255);\n"
+"")
+ self.AccountList.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
+ self.AccountList.setObjectName("AccountList")
+ self.AccountList_2 = QtWidgets.QLabel(RemAccDialog)
+ self.AccountList_2.setGeometry(QtCore.QRect(195, 20, 220, 45))
+ font = QtGui.QFont()
+ font.setPointSize(18)
+ self.AccountList_2.setFont(font)
+ self.AccountList_2.setStyleSheet("color: rgb(255, 255, 255);\n"
+"")
+ self.AccountList_2.setTextFormat(QtCore.Qt.TextFormat.RichText)
+ self.AccountList_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop)
+ self.AccountList_2.setWordWrap(True)
+ self.AccountList_2.setObjectName("AccountList_2")
+ self.RemAccButton_2 = QtWidgets.QPushButton(RemAccDialog)
+ self.RemAccButton_2.setGeometry(QtCore.QRect(50, 125, 350, 40))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.RemAccButton_2.setFont(font)
+ self.RemAccButton_2.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.RemAccButton_2.setText("")
+ self.RemAccButton_2.setObjectName("RemAccButton_2")
+ self.RemoveAccButtonGroup.addButton(self.RemAccButton_2)
+ self.RemAccButton_3 = QtWidgets.QPushButton(RemAccDialog)
+ self.RemAccButton_3.setGeometry(QtCore.QRect(50, 170, 350, 40))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.RemAccButton_3.setFont(font)
+ self.RemAccButton_3.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.RemAccButton_3.setText("")
+ self.RemAccButton_3.setObjectName("RemAccButton_3")
+ self.RemoveAccButtonGroup.addButton(self.RemAccButton_3)
+ self.RemAccButton_4 = QtWidgets.QPushButton(RemAccDialog)
+ self.RemAccButton_4.setGeometry(QtCore.QRect(50, 215, 350, 40))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.RemAccButton_4.setFont(font)
+ self.RemAccButton_4.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.RemAccButton_4.setText("")
+ self.RemAccButton_4.setObjectName("RemAccButton_4")
+ self.RemoveAccButtonGroup.addButton(self.RemAccButton_4)
+ self.RemAccButton_5 = QtWidgets.QPushButton(RemAccDialog)
+ self.RemAccButton_5.setGeometry(QtCore.QRect(50, 260, 350, 40))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.RemAccButton_5.setFont(font)
+ self.RemAccButton_5.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.RemAccButton_5.setText("")
+ self.RemAccButton_5.setObjectName("RemAccButton_5")
+ self.RemoveAccButtonGroup.addButton(self.RemAccButton_5)
+ self.RemAccButton_6 = QtWidgets.QPushButton(RemAccDialog)
+ self.RemAccButton_6.setGeometry(QtCore.QRect(50, 305, 350, 40))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.RemAccButton_6.setFont(font)
+ self.RemAccButton_6.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.RemAccButton_6.setText("")
+ self.RemAccButton_6.setObjectName("RemAccButton_6")
+ self.RemoveAccButtonGroup.addButton(self.RemAccButton_6)
+ self.RemAccButton_7 = QtWidgets.QPushButton(RemAccDialog)
+ self.RemAccButton_7.setGeometry(QtCore.QRect(50, 350, 350, 40))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.RemAccButton_7.setFont(font)
+ self.RemAccButton_7.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.RemAccButton_7.setText("")
+ self.RemAccButton_7.setObjectName("RemAccButton_7")
+ self.RemoveAccButtonGroup.addButton(self.RemAccButton_7)
+ self.RemAccButton_8 = QtWidgets.QPushButton(RemAccDialog)
+ self.RemAccButton_8.setGeometry(QtCore.QRect(50, 395, 350, 40))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.RemAccButton_8.setFont(font)
+ self.RemAccButton_8.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.RemAccButton_8.setText("")
+ self.RemAccButton_8.setObjectName("RemAccButton_8")
+ self.RemoveAccButtonGroup.addButton(self.RemAccButton_8)
+ self.RemAccButton_9 = QtWidgets.QPushButton(RemAccDialog)
+ self.RemAccButton_9.setGeometry(QtCore.QRect(50, 440, 350, 40))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.RemAccButton_9.setFont(font)
+ self.RemAccButton_9.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.RemAccButton_9.setText("")
+ self.RemAccButton_9.setObjectName("RemAccButton_9")
+ self.RemoveAccButtonGroup.addButton(self.RemAccButton_9)
+ self.RemAccButton_10 = QtWidgets.QPushButton(RemAccDialog)
+ self.RemAccButton_10.setGeometry(QtCore.QRect(50, 485, 350, 40))
+ font = QtGui.QFont()
+ font.setPointSize(24)
+ self.RemAccButton_10.setFont(font)
+ self.RemAccButton_10.setStyleSheet("border-radius: 10px;\n"
+"background-color: rgb(1, 0, 0);\n"
+"border-width: 2px;\n"
+"border-style: solid;\n"
+"border-color: black;\n"
+"color: rgb(0, 255, 0);\n"
+"text-align:left;")
+ self.RemAccButton_10.setText("")
+ self.RemAccButton_10.setObjectName("RemAccButton_10")
+ self.RemoveAccButtonGroup.addButton(self.RemAccButton_10)
+
+ self.retranslateUi(RemAccDialog)
+ QtCore.QMetaObject.connectSlotsByName(RemAccDialog)
+
+ def retranslateUi(self, RemAccDialog):
+ _translate = QtCore.QCoreApplication.translate
+ RemAccDialog.setWindowTitle(_translate("RemAccDialog", "Remove Account"))
+ self.AccountList.setText(_translate("RemAccDialog", "Accounts"))
+ self.AccountList_2.setText(_translate("RemAccDialog", "Click on account to PERMANENTLY remove"))
+
+
+if __name__ == "__main__":
+ import sys
+ app = QtWidgets.QApplication(sys.argv)
+ RemAccDialog = QtWidgets.QDialog()
+ ui = Ui_RemAccDialog()
+ ui.setupUi(RemAccDialog)
+ RemAccDialog.show()
+ sys.exit(app.exec())
diff --git a/authenticator/setup.py b/authenticator/setup.py
new file mode 100644
index 00000000..032bb5dc
--- /dev/null
+++ b/authenticator/setup.py
@@ -0,0 +1,19 @@
+"""
+This is a setup.py script generated by py2applet
+
+Usage:
+ python setup.py py2app
+"""
+
+from setuptools import setup
+
+APP = ['KeepKeyAuthenticator.py']
+DATA_FILES = ['authResources/circle-32.png', 'authResources/kk-icon-gold.png']
+OPTIONS = {}
+
+setup(
+ app=APP,
+ data_files=DATA_FILES,
+ options={'py2app': OPTIONS},
+ setup_requires=['py2app'],
+)
diff --git a/keepkeyctl b/keepkeyctl
index c500d970..f8830529 100755
--- a/keepkeyctl
+++ b/keepkeyctl
@@ -2,10 +2,10 @@
# Keepkey python client.
#
+# Copyright (C) 2023 markrypto
# Copyright (C) 2012-2016 Marek Palatinus
# Copyright (C) 2012-2016 Pavol Rusnak
# Copyright (C) 2016 Jochen Hoenicke
-# Copyright (C) 2022 markrypto
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
@@ -441,6 +441,9 @@ class Commands(object):
def set_label(self, args):
return self.client.apply_settings(label=args.label)
+ def set_pass(self, args):
+ return self.client.apply_settings(use_passphrase=args.passphrase_protection)
+
def clear_session(self, args):
return self.client.clear_session()
@@ -562,6 +565,7 @@ class Commands(object):
get_features.help = 'Retrieve device features and settings'
get_public_node.help = 'Get public node of given path'
set_label.help = 'Set new wallet label'
+ set_pass.help = 'Set passphrase protection'
clear_session.help = 'Clear session (remove cached PIN, passphrase, etc.)'
change_pin.help = 'Change new PIN or remove existing'
apply_policy.help = 'Apply a policy'
@@ -696,6 +700,10 @@ class Commands(object):
# (('-c', '--clear'), {'action': 'store_true', 'default': False})
)
+ set_pass.arguments = (
+ (('-r', '--passphrase-protection'), {'action': 'store_true', 'default': False}),
+ )
+
change_pin.arguments = (
(('-r', '--remove'), {'action': 'store_true', 'default': False}),
)
diff --git a/keepkeylib/client.py b/keepkeylib/client.py
index 7ded0f89..9d8b9ad2 100644
--- a/keepkeylib/client.py
+++ b/keepkeylib/client.py
@@ -1,5 +1,6 @@
# This file is part of the TREZOR project.
#
+# Copyright (C) 2022 markrypto
# Copyright (C) 2012-2016 Marek Palatinus
# Copyright (C) 2012-2016 Pavol Rusnak
# Copyright (C) 2016 Jochen Hoenicke
@@ -369,6 +370,9 @@ def __init__(self, *args, **kwargs):
# Use blank passphrase
self.set_passphrase('')
+ def setAutoButton(self, tf):
+ self.auto_button = tf
+
def close(self):
super(DebugLinkMixin, self).close()
if self.debug:
diff --git a/setup.py b/setup.py
index 813a2edf..2c457d02 100755
--- a/setup.py
+++ b/setup.py
@@ -4,7 +4,7 @@
setup(
name='keepkey',
version='7.0.3',
- author='TREZOR and KeepKey',
+ author='markrypto, KeepKey and TREZOR',
author_email='support@keepkey.com',
description='Python library for communicating with KeepKey Hardware Wallet',
url='https://github.com/keepkey/python-keepkey',
diff --git a/tests/otp.py b/tests/otp.py
new file mode 100644
index 00000000..15914c58
--- /dev/null
+++ b/tests/otp.py
@@ -0,0 +1,122 @@
+# This file is part of the KeepKey project.
+#
+# Copyright (C) markrypto
+#
+# This library is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library. If not, see .
+#
+# The script has been modified for KeepKey Device.
+
+import hashlib
+import hmac
+import base64
+import time
+
+
+### Define SharedSecret, Block size, hashing algorithm, TOTP length and frequency
+hash_algo = "sha1"
+B = 64
+# Generate a TOTP every 30 seconds
+F = 30
+# Shared Secret
+shared_secret = b'BASE32SECRET2345AB'
+# OTP Length
+Digits = 6
+key=base64.b32decode(shared_secret+b'='* (8 - len(shared_secret)%8))
+# Control excessive output to console
+debug = True
+def dbg(data):
+ if (debug):
+ print(data)
+
+# Prepare time - convert integer to byte
+def i2b_time(int_time):
+ return int_time.to_bytes(8, byteorder='big')
+
+### Implement the HMAC Algorithm. For details see the rfc2104.ipynb at
+# https://github.com/lordloh/OPT_algorithms/blob/master/rfc2104.ipynb
+
+def my_hmac(key, message):
+ #print(key.hex())
+ #print(message.hex())
+ trans_5C = bytes((x ^ 0x5C) for x in range(256))
+ trans_36 = bytes((x ^ 0x36) for x in range(256))
+ K_zpad=key.ljust(B,b'\0')
+ K_ipad=K_zpad.translate(trans_36)
+ K_opad=K_zpad.translate(trans_5C)
+ hash1 = hashlib.new(hash_algo, K_ipad+message).digest()
+ hmac_hash = hashlib.new(hash_algo, K_opad + hash1).digest()
+ return hmac_hash
+### Dynamic Truncation
+
+def dynamic_truncate(b_hash):
+ hash_len=len(b_hash)
+ int_hash = int.from_bytes(b_hash, byteorder='big')
+ #print("int_hash", int_hash)
+ offset = int_hash & 0xF
+ #print("offset", offset)
+ # Geterate a mask to get bytes from left to right of the hash
+ n_shift = 8*(hash_len-offset)-32
+ MASK = 0xFFFFFFFF << n_shift
+ hex_mask = "0x"+("{:0"+str(2*hash_len)+"x}").format(MASK)
+ P = (int_hash & MASK)>>n_shift # Get rid of left zeros
+ LSB_31 = P & 0x7FFFFFFF # Return only the lower 31 bits
+ return LSB_31
+
+# function wrapper to run the HOTP algorithm multiple times for different counter value
+def generate_TOTP(time_val):
+ # %30 seconds
+ T = i2b_time(int(time_val/F))
+ # Same algorithm as HOTP
+ hmac_hash = my_hmac(key,T)
+ #print("hmac_hash", hmac_hash.hex())
+ trc_hash = dynamic_truncate(hmac_hash) # Get truncated hash (int)
+ #print("trc_hash", trc_hash)
+ # Adjust TOTP length
+ TOTP = ("{:0"+str(Digits)+"}").format(trc_hash % (10**Digits))
+ #DEL dbg("\n***** ADJUST DIGITS *****\n"+str(trc_hash)+" % 10 ^ "+str(Digits)+"\nHOPT : "+HOTP)
+ return TOTP
+
+def main():
+
+ # Google Authenticator Compatibility (BASE-32)
+ # dbg("Key Base32 Decode :")
+ # dbg(key)
+
+ # Generate TOTP for current time
+ # T0 = int(time.time())
+ T0 = 1535317397
+ T1 = T0 + F # F seconds later...
+ myTOTP0=generate_TOTP(T0)
+ # myTOTP1=generate_TOTP(T1)
+
+ # Similarly, lets generate HOTPs for counter = 3..10 without a lot of output messages.
+ debug=False
+ # myTOTPs=[(generate_TOTP(x)) for x in range(T1+F,T1+F*5,F)]
+ # myTOTPs.insert(0,myTOTP0)
+ # myTOTPs.insert(1,myTOTP1)
+
+ #print(myTOTPs)
+ print(myTOTP0)
+ print(" ")
+
+ print(generate_TOTP(1536262427))
+
+
+ # T = 1535317397
+ # while True:
+ # print(T, " ", generate_TOTP(T))
+ # T = T + 30
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/test_msg_authfeature.py b/tests/test_msg_authfeature.py
new file mode 100644
index 00000000..e394f4da
--- /dev/null
+++ b/tests/test_msg_authfeature.py
@@ -0,0 +1,73 @@
+# Copyright (C) 2023 markrypto
+#
+# This library is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the License along with this library.
+# If not, see .
+
+import unittest
+import common
+import binascii
+
+from keepkeylib import messages_pb2 as proto
+from keepkeylib import types_pb2 as proto_types
+from keepkeylib.client import PinException, CallException
+
+
+class TestAuthFeature(common.KeepKeyTest):
+ def sendMsg(self, client, msg):
+ err = ''
+ retval = None
+ try:
+ retval = client.ping(msg=msg)
+ except CallException as E:
+ err = E.args[1]
+ return retval, err
+
+ def clearAuthData(self, client):
+ retval, err = self.sendMsg(client, b'\x19' + bytes("wipeAuthdata:", 'utf8'))
+ return err
+
+ def test_InitGetOTPClear(self):
+ if self.client.features.firmware_variant == "Emulator":
+ self.skipTest("Skip test in emulator, test on physical KeepKey")
+ return
+
+ self.requires_firmware("7.6.0")
+ self.setup_mnemonic_pin_passphrase()
+ self.client.clear_session()
+ self.clearAuthData(self.client)
+
+ for msg in (
+ b'\x15' + bytes("initializeAuth:"+"KeepKey"+":"+"markrypto"+":"+"ZKLHM3W3XAHG4CBN", 'utf8'),
+ b'\x15' + bytes("initializeAuth:"+"Shapeshift"+":"+"markrypto"+":"+"BASE32SECRET2345AB", 'utf8'),
+ b'\x15' + bytes("initializeAuth:"+"KeepKey"+":"+"markrypto2"+":"+"JBSWY3DPEHPK3PXP", 'utf8')
+ ):
+ retval, err = self.sendMsg(self.client, msg)
+ self.assertEqual(err, '')
+
+ T0 = 1535317397
+ interval = 30
+ Tslice = int(T0/interval)
+ Tremain = 7
+ for vector in (
+ (b'\x16' + bytes("generateOTPFrom:KeepKey:markrypto:", 'utf8') + bytes(str(Tslice), 'utf8') + bytes(":" + str(Tremain), 'utf8'), '910862'),
+ (b'\x16' + bytes("generateOTPFrom:Shapeshift:markrypto:", 'utf8') + bytes(str(Tslice), 'utf8') + bytes(":" + str(Tremain), 'utf8'), '280672'),
+ (b'\x16' + bytes("generateOTPFrom:KeepKey:markrypto2:", 'utf8') + bytes(str(Tslice), 'utf8') + bytes(":" + str(Tremain), 'utf8'), '660041')
+ ):
+ retval, err = self.sendMsg(self.client, vector[0])
+ self.assertEqual(err, '')
+ self.assertEqual(retval[:6], vector[1])
+
+ err = self.clearAuthData(self.client)
+ self.assertEqual(err, '')
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/test_msg_ethereum_cfunc.py b/tests/test_msg_ethereum_cfunc.py
deleted file mode 100644
index 7efbad50..00000000
--- a/tests/test_msg_ethereum_cfunc.py
+++ /dev/null
@@ -1,118 +0,0 @@
-# This file is part of the KEEPKEY project.
-#
-# Copyright (C) 2022 markrypto
-#
-# This library is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this library. If not, see .
-
-from base64 import b64encode
-import unittest
-import common
-import binascii
-import struct
-
-import keepkeylib.messages_pb2 as proto
-import keepkeylib.types_pb2 as proto_types
-from keepkeylib.client import CallException
-from keepkeylib.tools import int_to_big_endian
-
-
-# test contract function confirm when contract isn't recognized, e.g., gnosis safe proxy contracts
-
-class TestMsgEthereumCfunc(common.KeepKeyTest):
-
- def test_sign_execTx(self):
- self.requires_firmware("7.5.2")
- self.setup_mnemonic_nopin_nopassphrase()
-
- sig_v, sig_r, sig_s = self.client.ethereum_sign_tx(
- n=[2147483692,2147483708,2147483648,0,0],
- nonce=0xab,
- gas_price=0x24c988ac00,
- gas_limit=0x26249,
- value=0x0,
- to=binascii.unhexlify('c8fff0d944406a40475a0a8264328aac8d64927b'), # gnosis proxy
- address_type=0,
- chain_id=1,
- # The data below is generally broken into 32-byte chunks except for the function selector (4 bytes_ and
- # keccak signatures (4 bytes)
-
- # Function: execTransaction(address to, uint256 value, bytes data, uint8 operation, uint256 safeTxGas,
- # uint256 baseGas, uint256 gasPrice, address gasToken, address refundReceiver, bytes signatures)
- data=binascii.unhexlify('6a761202' + # execTransaction
- '000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' + # to address
- '0000000000000000000000000000000000000000000000000000000000000000' + # value in eth
- '0000000000000000000000000000000000000000000000000000000000000140' + # offset to data
- '0000000000000000000000000000000000000000000000000000000000000000' + # operation {Call, DelegateCall}
- '0000000000000000000000000000000000000000000000000000000000000000' + # safeTxGas
- '0000000000000000000000000000000000000000000000000000000000000000' + # baseGas
- '0000000000000000000000000000000000000000000000000000000000000000' + # gasPrice
- '0000000000000000000000000000000000000000000000000000000000000000' + # gasToken (0 if eth)
- '0000000000000000000000000000000000000000000000000000000000000000' + # refundReceiver of gas payment (0 if tx.origin)
- '00000000000000000000000000000000000000000000000000000000000001c0' + # offset to signatures data
- '0000000000000000000000000000000000000000000000000000000000000044' + # data: len of data
- 'a9059cbb000000000000000000000000b5bd898fadf4dc19313bc70c932bcee7' + # data payload bytes
- 'a90d9bb300000000000000000000000000000000000000000000000000000000' +
- '0ee6b28000000000000000000000000000000000000000000000000000000000' +
- '0000000000000000000000000000000000000000000000000000000000000041' + # signatures: len of signatures
- '00000000000000000000000021c9a94af76b59b171b32fd125a4edf0e9a2ad3e' + # signatures bytes
- '0000000000000000000000000000000000000000000000000000000000000000' +
- '0100000000000000000000000000000000000000000000000000000000000000')
- )
- self.assertEqual(sig_v, 37)
- self.assertEqual(binascii.hexlify(sig_r), '4ec68dfe39d7993e55366b305c65d235d4ecb3e0749b3b78830076f878c5b2c2')
- self.assertEqual(binascii.hexlify(sig_s), '2c270f8f4b39ba22c06786126df0f544e010ccd7fae621054caa05f6ae947bb8')
-
-
- sig_v, sig_r, sig_s = self.client.ethereum_sign_tx(
- n=[2147483692,2147483708,2147483648,0,0],
- nonce=0xab,
- gas_price=0x24c988ac00,
- gas_limit=0x26249,
- value=0x0,
- to=binascii.unhexlify('c8fff0d944406a40475a0a8264328aac8d64927b'), # gnosis proxy
- address_type=0,
- chain_id=1,
- # The data below is generally broken into 32-byte chunks except for the function selector (4 bytes_ and
- # keccak signatures (4 bytes)
-
- # Function: execTransaction(address to, uint256 value, bytes data, uint8 operation, uint256 safeTxGas,
- # uint256 baseGas, uint256 gasPrice, address gasToken, address refundReceiver, bytes signatures)
- data=binascii.unhexlify('6a761202' + # execTransaction
- '000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' + # to address
- '0000000000000000000000000000000000000000000000000000000000000000' + # value in eth
- '0000000000000000000000000000000000000000000000000000000000000140' + # offset to data
- '0000000000000000000000000000000000000000000000000000000000000000' + # operation {Call, DelegateCall}
- '0000000000000000000000000000000000000000000000000000000000126249' + # safeTxGas
- '0000000000000000000000000000000000000000000000000000000000026249' + # baseGas
- '0000000000000000000000000000000000000000000000000000000024c988ac' + # gasPrice
- '0000000000000000000000000000000000000000000000000000000000000000' + # gasToken (0 if eth)
- '0000000000000000000000000000000000000000000000000000000000000000' + # refundReceiver of gas payment (0 if tx.origin)
- '00000000000000000000000000000000000000000000000000000000000001c0' + # offset to signatures data
- '0000000000000000000000000000000000000000000000000000000000000044' + # data: len of data
- 'a9059cbb000000000000000000000000b5bd898fadf4dc19313bc70c932bcee7' + # data payload bytes
- 'a90d9bb300000000000000000000000000000000000000000000000000000000' +
- '0ee6b28000000000000000000000000000000000000000000000000000000000' +
- '0000000000000000000000000000000000000000000000000000000000000082' + # signatures: len of signatures
- '00000000000000000000000021c9a94af76b59b171b32fd125a4edf0e9a2ad3e' + # signatures bytes
- '0000000000000000000000000000000000000000000000000000000000000000' +
- '01abcdef10000000000000000021c9a94af76b59b171b32fd125a4edf0e9a2ad' +
- '3e00000000000000000000000000000000000000000000000000000000000000' +
- '0001000000000000000000000000000000000000000000000000000000000000')
- )
- self.assertEqual(sig_v, 38)
- self.assertEqual(binascii.hexlify(sig_r), '828dbc0c6002c89e9c4c0a9a0a8e8170fe552780f3c6811d1f7f110bf3056150')
- self.assertEqual(binascii.hexlify(sig_s), '0ba6f2d0e6e849cafab77e9316cfa2634abe79345dfd414ac773d1dd1caa7c66')
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_msg_ping.py b/tests/test_msg_ping.py
index 2522105f..1441ee1b 100644
--- a/tests/test_msg_ping.py
+++ b/tests/test_msg_ping.py
@@ -21,12 +21,13 @@
import time
import unittest
import common
+from datetime import datetime
+import binascii
from keepkeylib import messages_pb2 as proto
from keepkeylib import types_pb2 as proto_types
class TestPing(common.KeepKeyTest):
-
def test_ping(self):
self.setup_mnemonic_pin_passphrase()
self.client.clear_session()