summaryrefslogtreecommitdiff
path: root/fw/hexnoise.py
diff options
context:
space:
mode:
Diffstat (limited to 'fw/hexnoise.py')
-rwxr-xr-xfw/hexnoise.py81
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()