diff options
Diffstat (limited to 'fw/hexnoise.py')
-rwxr-xr-x | fw/hexnoise.py | 81 |
1 files changed, 66 insertions, 15 deletions
diff --git a/fw/hexnoise.py b/fw/hexnoise.py index 549f583..b0217e2 100755 --- a/fw/hexnoise.py +++ b/fw/hexnoise.py @@ -2,7 +2,9 @@ import time import enum +import os import sys +import binascii from contextlib import contextmanager, suppress, wraps import hashlib import secrets @@ -58,11 +60,12 @@ class Packetizer: packet = self.ser.read_until(b'\0') data = cobs.decode(packet[:-1]) + pkt_type, data = PacketType(data[0]), data[1:] + if self.debug: - print(f'\033[93mReceived {len(data)} bytes\033[0m') + print(f'\033[93mReceived {len(data)} bytes, packet type {pkt_type.name} ({pkt_type.value})\033[0m') hexdump(print, data, self.width) - pkt_type, data = PacketType(data[0]), data[1:] if pkt_type is PacketType.COMM_ERROR: raise ProtocolError('Device-side serial communication error') elif pkt_type is PacketType.CRYPTO_ERROR: @@ -253,6 +256,7 @@ class NoiseEngine: msg_type, payload = self.packetizer.receive_packet() rtype, data = self._decrypt(payload) + print(f'Received report {rtype.name} ({rtype.value})') if rtype is ReportType.PAIRING_SUCCESS: self.connected, self.paired = True, True elif rtype is ReportType.PAIRING_START: @@ -334,7 +338,7 @@ class NoiseEngine: raise ProtocolError('Device-side pairing error') # FIXME find better exception subclass here else: - raise ProtocolError('Invalid report type') + raise ProtocolError(f'Invalid report type {msg_type}') def uinput_passthrough(self): mouse_foo = [uinput.BTN_LEFT, uinput.BTN_RIGHT, uinput.BTN_MIDDLE, @@ -351,7 +355,6 @@ class NoiseEngine: raise ValueError('Unsupported report length', report_len) modbyte, _reserved, *keycodes = report - import binascii keys = { *KeyMapper.map_modifiers(modbyte), *KeyMapper.map_regulars(keycodes) } if self.debug: print('Emitting:', keys) @@ -366,7 +369,6 @@ class NoiseEngine: elif msg_type is ReportType.MOUSE: if report_len < 3 or report_len > 8: raise ValueError('Unsupported report length', report_len) - import binascii _report_type, buttons, a, b, c, w, _2 = report x = ((b&0x0f)<<8) | a y = ((b&0xf0) >> 4) | (c<<4) @@ -393,22 +395,71 @@ if __name__ == '__main__': parser.add_argument('baudrate') parser.add_argument('-w', '--width', type=int, default=16, help='Number of bytes to display in one line') parser.add_argument('-d', '--debug', action='store_true') + parser.add_argument('-r', '--random-key', action='store_true', help='Use random key instead of persisted key') args = parser.parse_args() + if args.random_key: + host_key_private = NoiseEngine.generate_private_key_x25519() + + else: + XDG_CONFIG_HOME = os.environ.get('XDG_CONFIG_HOME') or os.path.join(os.path.expandvars('$HOME'), '.config', 'secure_hid') + if not os.path.isdir(XDG_CONFIG_HOME): + os.mkdir(XDG_CONFIG_HOME) + + private_key_file = os.path.join(XDG_CONFIG_HOME, 'host_key.pem') + if not os.path.isfile(private_key_file): + with open(private_key_file, 'w') as f: + f.write(binascii.hexlify(hexnoise.NoiseEngine.generate_private_key_x25519()).decode()) + + with open(private_key_file) as f: + host_key_private = binascii.unhexlify(f.read()) + ser = serial.Serial(args.serial, args.baudrate) packetizer = Packetizer(ser, debug=args.debug, width=args.width) - temp_priv_key = NoiseEngine.generate_private_key_x25519() - noise = NoiseEngine(temp_priv_key, packetizer, debug=args.debug) + noise = NoiseEngine(host_key_private, packetizer, debug=args.debug) noise.perform_handshake() - print('Handshake channel binding incantation:') - print(noise.channel_binding_incantation()) + if noise.connected and not noise.paired: + print('Handshake channel binding incantation:') + channel_binding_incantation = noise.channel_binding_incantation() + print(channel_binding_incantation) + + def term_match(user_input, template): + out = '' + words_u = user_input.split(' ') + words_t = [ w for w in template.split(' ') if w != 'and' ] + + for u_word in words_u: + if 'and'.startswith(u_word) or not u_word: + out += ' ' + u_word + continue + + if not words_t: + out += '\033[91m' + continue + + t_word, *words_t = words_t + if not t_word.startswith(u_word.strip(',')): + out += ' \033[91m' + u_word + + else: + out += ' ' + u_word + + return '\033[92m' + out[1:] + '\033[0m' + + for user_input in noise.pairing_messages(): + if not args.debug: + print('\033[2K\r', end='') + matched = term_match(user_input, channel_binding_incantation) + print('Pairing input:', matched, end='' if not args.debug else '\n', flush=True) + print() + print('Pairing success') + + elif noise.connected and noise.paired: + print('Successfully re-connected') - for user_input in noise.pairing_messages(): - if not args.debug: - print('\033[2K\r', end='') - print('Pairing input:', user_input, end='' if not args.debug else '\n', flush=True) - print() - print('Pairing success') + else: + print('Could not connect') + sys.exit(1) noise.uinput_passthrough() |