#!/usr/bin/env python3
import threading
import re
import serial
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, Pango, GLib
import hexnoise
class PairingWindow(Gtk.Window):
def __init__(self, serial, debug=False):
Gtk.Window.__init__(self, title='SecureHID pairing')
self.serial = serial
self.debug = debug
self.set_border_width(10)
self.set_default_size(600, 200)
self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
self.label = Gtk.Label()
self.label.set_line_wrap(True)
self.label.set_justify(Gtk.Justification.CENTER)
self.label.set_markup('Step 1\n\nContacting device...')
self.vbox.pack_start(self.label, True, True, 0)
self.entry = Gtk.Entry()
self.entry.set_editable(False)
self.vbox.pack_start(self.entry, True, True, 0)
self.add(self.vbox)
self.handshaker = threading.Thread(target=self.pair, daemon=True)
self.handshaker.start()
def pair(self):
self.packetizer = hexnoise.Packetizer(self.serial, debug=self.debug)
self.noise = hexnoise.NoiseEngine(self.packetizer, debug=self.debug)
for i in range(10):
try:
self.run_handshake()
break
except hexnoise.ProtocolError as e:
print(e)
def run_handshake(self):
self.noise.perform_handshake()
binding_incantation = self.noise.channel_binding_incantation()
self.label.set_markup(f'Step 2\n\nPerform channel binding ritual.\n'
f'Enter the following incantation, then press enter.\n'
f'{binding_incantation}')
def update_text(text):
self.entry.set_text(text)
self.entry.set_position(len(text))
clean = lambda s: re.sub('[^a-z0-9-]', '', s.lower())
if clean(binding_incantation).startswith(clean(text)):
color = 0.9, 1.0, 0.9 # light red
else:
color = 1.0, 0.9, 0.9 # light green
self.entry.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(*color, 1.0))
for user_input in self.noise.pairing_messages():
print(f'User input: "{user_input}"')
GLib.idle_add(update_text, user_input)
self.label.set_markup(f'Done!')
# FIXME demo
self.noise.uinput_passthrough()
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('serial')
parser.add_argument('baudrate')
parser.add_argument('-d', '--debug', action='store_true')
args = parser.parse_args()
ser = serial.Serial(args.serial, args.baudrate)
window = PairingWindow(ser, debug=args.debug)
window.connect('destroy', Gtk.main_quit)
window.show_all()
Gtk.main()