unlocker: initial implementation of unlocking keyboard to perform security-sensitive actions
parent
5619ccbcd4
commit
a6c42b513f
|
|
@ -11,6 +11,7 @@ from PyQt5.QtGui import QFontDatabase
|
||||||
from PyQt5.QtWidgets import QHBoxLayout, QLineEdit, QToolButton, QPlainTextEdit, QProgressBar,QFileDialog, QDialog
|
from PyQt5.QtWidgets import QHBoxLayout, QLineEdit, QToolButton, QPlainTextEdit, QProgressBar,QFileDialog, QDialog
|
||||||
|
|
||||||
from basic_editor import BasicEditor
|
from basic_editor import BasicEditor
|
||||||
|
from unlocker import Unlocker
|
||||||
from util import tr, chunks, find_vial_devices
|
from util import tr, chunks, find_vial_devices
|
||||||
from vial_device import VialBootloader, VialKeyboard
|
from vial_device import VialBootloader, VialKeyboard
|
||||||
|
|
||||||
|
|
@ -141,6 +142,8 @@ class FirmwareFlasher(BasicEditor):
|
||||||
|
|
||||||
self.layout_restore = self.uid_restore = None
|
self.layout_restore = self.uid_restore = None
|
||||||
|
|
||||||
|
self.unlocker = Unlocker()
|
||||||
|
|
||||||
def rebuild(self, device):
|
def rebuild(self, device):
|
||||||
super().rebuild(device)
|
super().rebuild(device)
|
||||||
self.txt_logger.clear()
|
self.txt_logger.clear()
|
||||||
|
|
@ -191,7 +194,6 @@ class FirmwareFlasher(BasicEditor):
|
||||||
|
|
||||||
self.layout_restore = self.uid_restore = None
|
self.layout_restore = self.uid_restore = None
|
||||||
|
|
||||||
# TODO: this needs to switch to the secure assisted-reset feature before public release
|
|
||||||
if isinstance(self.device, VialKeyboard):
|
if isinstance(self.device, VialKeyboard):
|
||||||
# back up current layout
|
# back up current layout
|
||||||
self.log("Backing up current layout...")
|
self.log("Backing up current layout...")
|
||||||
|
|
@ -200,6 +202,8 @@ class FirmwareFlasher(BasicEditor):
|
||||||
# keep track of which keyboard we should restore saved layout to
|
# keep track of which keyboard we should restore saved layout to
|
||||||
self.uid_restore = self.device.keyboard.get_uid()
|
self.uid_restore = self.device.keyboard.get_uid()
|
||||||
|
|
||||||
|
self.unlocker.perform_unlock(self.device.keyboard)
|
||||||
|
|
||||||
self.log("Restarting in bootloader mode...")
|
self.log("Restarting in bootloader mode...")
|
||||||
self.device.keyboard.reset()
|
self.device.keyboard.reset()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@ CMD_VIAL_GET_SIZE = 0x01
|
||||||
CMD_VIAL_GET_DEFINITION = 0x02
|
CMD_VIAL_GET_DEFINITION = 0x02
|
||||||
CMD_VIAL_GET_ENCODER = 0x03
|
CMD_VIAL_GET_ENCODER = 0x03
|
||||||
CMD_VIAL_SET_ENCODER = 0x04
|
CMD_VIAL_SET_ENCODER = 0x04
|
||||||
|
CMD_VIAL_GET_LOCK = 0x05
|
||||||
|
CMD_VIAL_UNLOCK_START = 0x06
|
||||||
|
CMD_VIAL_UNLOCK_POLL = 0x07
|
||||||
|
|
||||||
# how much of a macro/keymap buffer we can read/write per packet
|
# how much of a macro/keymap buffer we can read/write per packet
|
||||||
BUFFER_FETCH_CHUNK = 28
|
BUFFER_FETCH_CHUNK = 28
|
||||||
|
|
@ -290,3 +293,14 @@ class Keyboard:
|
||||||
data = self.usb_send(self.dev, struct.pack("BB", CMD_VIA_VIAL_PREFIX, CMD_VIAL_GET_KEYBOARD_ID))
|
data = self.usb_send(self.dev, struct.pack("BB", CMD_VIA_VIAL_PREFIX, CMD_VIAL_GET_KEYBOARD_ID))
|
||||||
keyboard_id = data[4:12]
|
keyboard_id = data[4:12]
|
||||||
return keyboard_id
|
return keyboard_id
|
||||||
|
|
||||||
|
def get_lock(self):
|
||||||
|
data = self.usb_send(self.dev, struct.pack("BB", CMD_VIA_VIAL_PREFIX, CMD_VIAL_GET_LOCK))
|
||||||
|
return data[0]
|
||||||
|
|
||||||
|
def unlock_start(self):
|
||||||
|
self.usb_send(self.dev, struct.pack("BB", CMD_VIA_VIAL_PREFIX, CMD_VIAL_UNLOCK_START))
|
||||||
|
|
||||||
|
def unlock_poll(self):
|
||||||
|
data = self.usb_send(self.dev, struct.pack("BB", CMD_VIA_VIAL_PREFIX, CMD_VIAL_UNLOCK_POLL))
|
||||||
|
return data
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
import time
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QCoreApplication, Qt
|
||||||
|
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QProgressBar
|
||||||
|
|
||||||
|
from util import tr
|
||||||
|
|
||||||
|
|
||||||
|
class Unlocker(QWidget):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.keyboard = None
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
|
self.progress = QProgressBar()
|
||||||
|
|
||||||
|
layout.addWidget(QLabel(tr("Unlocker", "In order to proceed, the keyboard must be set into unlocked mode.\n"
|
||||||
|
"You should only perform this operation on computers that you trust.")))
|
||||||
|
layout.addWidget(QLabel(tr("Unlocker", "To exit this mode, you will need to replug the keyboard.")))
|
||||||
|
layout.addWidget(QLabel(tr("Unlocker", "Press and hold the following keys until the progress bar "
|
||||||
|
"below fills up:")))
|
||||||
|
|
||||||
|
# TODO: add image/text reference of keys user needs to hold
|
||||||
|
|
||||||
|
layout.addWidget(self.progress)
|
||||||
|
|
||||||
|
self.setLayout(layout)
|
||||||
|
self.setWindowFlag(Qt.Dialog)
|
||||||
|
|
||||||
|
def perform_unlock(self, keyboard):
|
||||||
|
# if it's already unlocked, don't need to do anything
|
||||||
|
if keyboard.get_lock() == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.progress.setMaximum(1)
|
||||||
|
self.progress.setValue(0)
|
||||||
|
|
||||||
|
self.show()
|
||||||
|
self.keyboard = keyboard
|
||||||
|
self.keyboard.unlock_start()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
data = self.keyboard.unlock_poll()
|
||||||
|
unlocked = data[0]
|
||||||
|
unlock_counter = data[2]
|
||||||
|
|
||||||
|
self.progress.setMaximum(max(self.progress.maximum(), unlock_counter))
|
||||||
|
self.progress.setValue(self.progress.maximum() - unlock_counter)
|
||||||
|
|
||||||
|
if unlocked == 1:
|
||||||
|
break
|
||||||
|
|
||||||
|
QCoreApplication.processEvents()
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
|
# ok all done, the keyboard is now set to insecure state
|
||||||
|
self.hide()
|
||||||
Loading…
Reference in New Issue