unlocker: initial implementation of unlocking keyboard to perform security-sensitive actions

main
Ilya Zhuravlev 2020-12-27 08:03:45 -05:00
parent 5619ccbcd4
commit a6c42b513f
3 changed files with 79 additions and 1 deletions

View File

@ -11,6 +11,7 @@ from PyQt5.QtGui import QFontDatabase
from PyQt5.QtWidgets import QHBoxLayout, QLineEdit, QToolButton, QPlainTextEdit, QProgressBar,QFileDialog, QDialog
from basic_editor import BasicEditor
from unlocker import Unlocker
from util import tr, chunks, find_vial_devices
from vial_device import VialBootloader, VialKeyboard
@ -141,6 +142,8 @@ class FirmwareFlasher(BasicEditor):
self.layout_restore = self.uid_restore = None
self.unlocker = Unlocker()
def rebuild(self, device):
super().rebuild(device)
self.txt_logger.clear()
@ -191,7 +194,6 @@ class FirmwareFlasher(BasicEditor):
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):
# back 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
self.uid_restore = self.device.keyboard.get_uid()
self.unlocker.perform_unlock(self.device.keyboard)
self.log("Restarting in bootloader mode...")
self.device.keyboard.reset()

View File

@ -27,6 +27,9 @@ CMD_VIAL_GET_SIZE = 0x01
CMD_VIAL_GET_DEFINITION = 0x02
CMD_VIAL_GET_ENCODER = 0x03
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
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))
keyboard_id = data[4:12]
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

View File

@ -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()