summaryrefslogtreecommitdiff
path: root/pairing.py
blob: 39247550e232ab41af8eb16b89a29713cb162649 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#!/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, noise, debug=False):
        Gtk.Window.__init__(self, title='SecureHID pairing')
        self.noise = noise
        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('<b>Step 1</b>\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):
        for i in range(10):
            try:
                self.run_handshake()
                break
            except hexnoise.ProtocolError as e:
                print(e)

    def run_handshake(self):
        binding_incantation = self.noise.channel_binding_incantation()
        GLib.idle_add(self.label.set_markup,
                f'<b>Step 2</b>\n\nPerform channel binding ritual.\n'
                f'Enter the following incantation, then press enter.\n'
                f'<b>{binding_incantation}</b>')
        
        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))

        try:
            for user_input in self.noise.pairing_messages():
                GLib.idle_add(update_text, user_input)
            self.destroy()
        except noise.ProtocolError as e:
            GLib.idle_add(self.label.set_markup, f'<b>Error: {e}!</b>')


class StatusIcon(Gtk.StatusIcon):
    def __init__(self):
        Gtk.StatusIcon.__init__(self)
        self.set_tooltip_text('SecureHID connected')
        self.set_from_file('secureusb_icon.png')


def run_pairing_gui(serial, baudrate, debug=False):
    ser = serial.Serial(serial, baudrate)
    packetizer = hexnoise.Packetizer(serial, debug=debug)
    noise = hexnoise.NoiseEngine(packetizer, debug=debug)
    noise.perform_handshake()

    if not noise.paired:
        window = PairingWindow(noise, debug=debug)
        window.connect('destroy', Gtk.main_quit)
        window.show_all()
        Gtk.main()

    if self.noise.paired:
        input_runner = threading.Thread(target=noise.uinput_passthrough, daemon=True)
        input_runner.start()

        status_icon = StatusIcon()
        Gtk.main()

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

    run_pairing_gui(args.serial, args.baudrate, args.debug)