New host-side arch working

This commit is contained in:
jaseg 2018-11-12 23:18:45 +09:00
parent d2c9b00b36
commit af15c38a05
8 changed files with 485 additions and 886 deletions

View file

@ -1,260 +1,34 @@
ADJECTIVES = [
"wrathful", # 0
"worthy", # 1
"weird", # 2
"warm", # 3
"volatile", # 4
"veiled", # 5
"vacuous", # 6
"useless", # 7
"upset", # 8
"unsoiled", # 9
"unsightly", # 10
"unpronounceable", # 11
"unfriendly", # 12
"unfree", # 13
"unfit", # 14
"unfaithful", # 15
"unchaste", # 16
"unbroken", # 17
"unbound", # 18
"unblessed", # 19
"unbefitting", # 20
"unaltered", # 21
"unabused", # 22
"unable", # 23
"ugly", # 24
"tongued", # 25
"thorny", # 26
"thirsty", # 27
"thick", # 28
"terminal", # 29
"ten-sided", # 30
"teeming", # 31
"tangerine", # 32
"taken", # 33
"substantial", # 34
"stupefying", # 35
"stringy", # 36
"strange", # 37
"stillborn", # 38
"sticky", # 39
"stagnant", # 40
"spongy", # 41
"sour", # 42
"soul-destroying", # 43
"smoldering", # 44
"smitten", # 45
"slain", # 46
"six-sided", # 47
"shifting", # 48
"shadowy", # 49
"severed", # 50
"seven-sided", # 51
"serene", # 52
"salty", # 53
"rust-red", # 54
"royal", # 55
"rotten", # 56
"riddled", # 57
"resentful", # 58
"regrettable", # 59
"reeking", # 60
"rare", # 61
"rank", # 62
"rancid", # 63
"quiescent", # 64
"putrid", # 65
"putrid", # 66
"putrescent", # 67
"prehistoric", # 68
"predatory", # 69
"predaceous", # 70
"porous", # 71
"poisonous", # 72
"pierced", # 73
"phlegmatic", # 74
"petrifying", # 75
"pessimal", # 76
"pathetic", # 77
"odorless", # 78
"oddish", # 79
"obsessed", # 80
"obscene", # 81
"numb", # 82
"nine-sided", # 83
"nasty", # 84
"mysterious", # 85
"mute", # 86
"musky", # 87
"morose", # 88
"moribund", # 89
"moldy", # 90
"miasmic", # 91
"material", # 92
"many-lobed", # 93
"malodorous", # 94
"malign", # 95
"maimed", # 96
"luminescent", # 97
"low-cut", # 98
"lousy", # 99
"live", # 100
"limp", # 101
"lifeless", # 102
"leering", # 103
"leaky", # 104
"layered", # 105
"latent", # 106
"lackluster", # 107
"jagged", # 108
"irregular", # 109
"iridescent", # 110
"intangible", # 111
"infinite", # 112
"inept", # 113
"incomprehensible", # 114
"in-between", # 115
"improper", # 116
"idle", # 117
"hunted", # 118
"hideous", # 119
"heavy", # 120
"hairy", # 121
"guilty", # 122
"grotesque", # 123
"grey", # 124
"greedy", # 125
"gory", # 126
"gorgeous", # 127
"gooey", # 128
"golden-brown", # 129
"golden", # 130
"ghastly", # 131
"frostbitten", # 132
"fresh-cut", # 133
"freakish", # 134
"frantic", # 135
"fossilized", # 136
"formless", # 137
"formidable", # 138
"floccose", # 139
"five-lobed", # 140
"firstborn", # 141
"filthy", # 142
"fickle", # 143
"fetid", # 144
"fertile", # 145
"fearful", # 146
"fatal", # 147
"familiar", # 148
"fallen", # 149
"fallacious", # 150
"faint", # 151
"faceless", # 152
"extinct", # 153
"esoteric", # 154
"errant", # 155
"emergent", # 156
"elastic", # 157
"eight-sided", # 158
"eerie", # 159
"ebon", # 160
"dysphoric", # 161
"dying", # 162
"dumb", # 163
"dull-purple", # 164
"dull", # 165
"dull", # 166
"dull", # 167
"dormant", # 168
"doomed", # 169
"disfigured", # 170
"dirty", # 171
"defenseless", # 172
"deep-pink", # 173
"deep", # 174
"deconsecrated", # 175
"deathlike", # 176
"deadly", # 177
"dead", # 178
"dark-blue", # 179
"dark", # 180
"curly", # 181
"curious", # 182
"cured", # 183
"cunning", # 184
"crystalline", # 185
"cryptic", # 186
"crying", # 187
"crumbly", # 188
"crimson", # 189
"crested", # 190
"creepy", # 191
"crazy", # 192
"corrupt", # 193
"corporeal", # 194
"contemptible", # 195
"contained", # 196
"concrete", # 197
"cloudy", # 198
"chopped", # 199
"chained", # 200
"caustic", # 201
"catholic", # 202
"cathartic", # 203
"captive", # 204
"cancerous", # 205
"cabalistic", # 206
"burnt", # 207
"buoyant", # 208
"bronze-red", # 209
"bronze", # 210
"broken", # 211
"bright-red", # 212
"breathless", # 213
"bound", # 214
"bound", # 215
"bottomless", # 216
"bony", # 217
"bodiless", # 218
"blue-lilac", # 219
"blue", # 220
"bloody", # 221
"bloodthirsty", # 222
"bloodsucking", # 223
"bloodstained", # 224
"bloodcurdling", # 225
"blonde", # 226
"blistered", # 227
"blank", # 228
"bitter", # 229
"bilgy", # 230
"bewitched", # 231
"befouled", # 232
"beardless", # 233
"bastardly", # 234
"barbed", # 235
"baleful", # 236
"balding", # 237
"awkward", # 238
"awful", # 239
"atrocious", # 240
"arcane", # 241
"appalling", # 242
"antic", # 243
"anonymous", # 244
"angry", # 245
"ample", # 246
"ambiguous", # 247
"amber-green", # 248
"amber", # 249
"aghast", # 250
"activated", # 251
"acidic", # 252
"abused", # 253
"abstruse", # 254
"abject", # 255
]
ADJECTIVES = '''
wrathful worthy weird warm volatile veiled vacuous useless
upset unsoiled unsightly unpronounceable unfriendly unfree unfit unfaithful
unchaste unbroken unbound unblessed unbefitting unaltered unabused unable
ugly tongued thorny thirsty thick terminal ten-sided teeming
tangerine taken substantial stupefying stringy strange stillborn sticky
stagnant spongy sour soul-destroying smoldering smitten slain six-sided
shifting shadowy severed seven-sided serene salty rust-red royal
rotten riddled resentful regrettable reeking rare rank rancid
quiescent putrid putrid putrescent prehistoric predatory predaceous porous
poisonous pierced phlegmatic petrifying pessimal pathetic odorless oddish
obsessed obscene numb nine-sided nasty mysterious mute musky
morose moribund moldy miasmic material many-lobed malodorous malign
maimed luminescent low-cut lousy live limp lifeless leering
leaky layered latent lackluster jagged irregular iridescent intangible
infinite inept incomprehensible in-between improper idle hunted hideous
heavy hairy guilty grotesque grey greedy gory gorgeous
gooey golden-brown golden ghastly frostbitten fresh-cut freakish frantic
fossilized formless formidable floccose five-lobed firstborn filthy fickle
fetid fertile fearful fatal familiar fallen fallacious faint
faceless extinct esoteric errant emergent elastic eight-sided eerie
ebon dysphoric dying dumb dull-purple dull dull dull
dormant doomed disfigured dirty defenseless deep-pink deep deconsecrated
deathlike deadly dead dark-blue dark curly curious cured
cunning crystalline cryptic crying crumbly crimson crested creepy
crazy corrupt corporeal contemptible contained concrete cloudy chopped
chained caustic catholic cathartic captive cancerous cabalistic burnt
buoyant bronze-red bronze broken bright-red breathless bound bound
bottomless bony bodiless blue-lilac blue bloody bloodthirsty bloodsucking
bloodstained bloodcurdling blonde blistered blank bitter bilgy bewitched
befouled beardless bastardly barbed baleful balding awkward awful
atrocious arcane appalling antic anonymous angry ample ambiguous
amber-green amber aghast activated acidic abused abstruse abject
'''.split()

27
hexdump.py Normal file
View file

@ -0,0 +1,27 @@
import string
import time
startup = time.time()
def _print_line(write, ts, line, width=16):
h,m,s,ms = int(ts//3600), int((ts//60)%60), int(ts%60), int((ts%1.0) * 1000)
timestamp = f'{h: 3d}:{m:02d}:{s:02d}:{ms:03d}'
line = list(line) + [None]*(width-len(line))
hexcol = '\033[0m'
col = lambda b, s: s if b != 0 else f'\033[91m{s}{hexcol}'
hexfmt = ' '.join(
' '.join(col(b, f'{b:02x}') if b is not None else ' ' for b in line[i*8:i*8+8])
for i in range(1 + (len(line)-1)//8))
asciifmt = ''.join(chr(c) if c is not None and chr(c) in string.printable and c>=0x20 else '.' for c in line)
write(f'\033[38;5;244m{timestamp} {hexcol}{hexfmt} \033[38;5;244m|\033[92m{asciifmt}\033[38;5;244m|\033[0m', flush=True, end='')
def hexdump(write, packet, width=16):
ts = time.time()
while len(packet) > width:
chunk, packet = packet[:width], packet[width:]
_print_line(write, ts-startup, chunk, width=width)
write()
_print_line(write, ts-startup, packet, width=width)
write()

View file

@ -1,33 +1,18 @@
#!/usr/bin/env python3
import time
import string
import enum
import sys
from contextlib import contextmanager, suppress, wraps
import serial
from cobs import cobs
import uinput
from noise.connection import NoiseConnection, Keypair
from noise.exceptions import NoiseInvalidMessage
def _print_line(write, ts, line, width=16):
h,m,s,ms = int(ts//3600), int((ts//60)%60), int(ts%60), int((ts%1.0) * 1000)
timestamp = f'{h: 3d}:{m:02d}:{s:02d}:{ms:03d}'
line = list(line) + [None]*(width-len(line))
hexcol = '\033[0m'
col = lambda b, s: s if b != 0 else f'\033[91m{s}{hexcol}'
hexfmt = ' '.join(
' '.join(col(b, f'{b:02x}') if b is not None else ' ' for b in line[i*8:i*8+8])
for i in range(1 + (len(line)-1)//8))
asciifmt = ''.join(chr(c) if c is not None and chr(c) in string.printable and c>=0x20 else '.' for c in line)
write(f'\033[38;5;244m{timestamp} {hexcol}{hexfmt} \033[38;5;244m|\033[92m{asciifmt}\033[38;5;244m|\033[0m', flush=True, end='')
startup = time.time()
def hexdump(write, packet, width=16):
ts = time.time()
while len(packet) > width:
chunk, packet = packet[:width], packet[width:]
_print_line(write, ts-startup, chunk, width=width)
write()
_print_line(write, ts-startup, packet, width=width)
write()
import keymap
from hexdump import hexdump
class PacketType(enum.Enum):
_RESERVED = 0
@ -39,147 +24,308 @@ class ReportType(enum.Enum):
_RESERVED = 0
KEYBOARD = 1
MOUSE = 2
PAIRING = 3 # keyboard in disguise
PAIRING_INPUT = 3
PAIRING_SUCESS = 4
PAIRING_ERROR = 5
def send_packet(ser, pkt_type, data, width=16):
print(f'\033[93mSending {len(data)} bytes, packet type {pkt_type.name} ({pkt_type.value})\033[0m')
hexdump(print, data, width)
data = bytes([pkt_type.value]) + data
encoded = cobs.encode(data) + b'\0'
ser.write(encoded)
ser.flushOutput()
class Packetizer:
def __init__(self, serial, debug=False, width=16):
self.ser, self.debug, self.width = serial, debug, width
self.ser.write(b'\0') # COBS synchronization
def receive_packet(ser, width=16):
packet = ser.read_until(b'\0')
data = cobs.decode(packet[:-1])
#print(f'\033[93mReceived {len(data)} bytes\033[0m')
#hexdump(print, data, width)
return data[0], data[1:]
def send_packet(self, pkt_type, data):
if self.debug:
print(f'\033[93mSending {len(data)} bytes, packet type {pkt_type.name} ({pkt_type.value})\033[0m')
hexdump(print, data, self.width)
data = bytes([pkt_type.value]) + data
encoded = cobs.encode(data) + b'\0'
self.ser.write(encoded)
self.ser.flushOutput()
def receive_packet(self):
packet = self.ser.read_until(b'\0')
data = cobs.decode(packet[:-1])
if self.debug:
print(f'\033[93mReceived {len(data)} bytes\033[0m')
hexdump(print, data, self.width)
return PacketType(data[0]), data[1:]
class KeyMapper:
Keycode = enum.Enum('Keycode', start=0, names='''
KEY_NONE _RESERVED_0x01 _RESERVED_0x02 _RESERVED_0x03 KEY_A KEY_B KEY_C KEY_D
KEY_E KEY_F KEY_G KEY_H KEY_I KEY_J KEY_K KEY_L
KEY_M KEY_N KEY_O KEY_P KEY_Q KEY_R KEY_S KEY_T
KEY_U KEY_V KEY_W KEY_X KEY_Y KEY_Z KEY_1 KEY_2
KEY_3 KEY_4 KEY_5 KEY_6 KEY_7 KEY_8 KEY_9 KEY_0
KEY_ENTER KEY_ESC KEY_BACKSPACE KEY_TAB KEY_SPACE KEY_MINUS KEY_EQUAL KEY_LEFTBRACE
KEY_RIGHTBRACE KEY_BACKSLASH KEY_HASH KEY_SEMICOLON KEY_APOSTROPHE KEY_GRAVE KEY_COMMA KEY_DOT
KEY_SLASH KEY_CAPSLOCK KEY_F1 KEY_F2 KEY_F3 KEY_F4 KEY_F5 KEY_F6
KEY_F7 KEY_F8 KEY_F9 KEY_F10 KEY_F11 KEY_F12 KEY_SYSRQ KEY_SCROLLLOCK
KEY_PAUSE KEY_INSERT KEY_HOME KEY_PAGEUP KEY_DELETE KEY_END KEY_PAGEDOWN KEY_RIGHT
KEY_LEFT KEY_DOWN KEY_UP KEY_NUMLOCK KEY_KPSLASH KEY_KPASTERISK KEY_KPMINUS KEY_KPPLUS
KEY_KPENTER KEY_KP1 KEY_KP2 KEY_KP3 KEY_KP4 KEY_KP5 KEY_KP6 KEY_KP7
KEY_KP8 KEY_KP9 KEY_KP0 KEY_KPDOT KEY_102ND KEY_COMPOSE KEY_POWER KEY_KPEQUAL
KEY_F13 KEY_F14 KEY_F15 KEY_F16 KEY_F17 KEY_F18 KEY_F19 KEY_F20
KEY_F21 KEY_F22 KEY_F23 KEY_F24 KEY_OPEN KEY_HELP KEY_PROPS KEY_FRONT
KEY_STOP KEY_AGAIN KEY_UNDO KEY_CUT KEY_COPY KEY_PASTE KEY_FIND KEY_MUTE
KEY_VOLUMEUP KEY_VOLUMEDOWN _RESERVED_0x82 _RESERVED_0x83 _RESERVED_0x84 KEY_KPCOMMA _RESERVED_0x86 KEY_RO
KEY_KATAKANAHIRAGANA KEY_YEN KEY_HENKAN KEY_MUHENKAN KEY_KPJPCOMMA _RESERVED_0x8D _RESERVED_0x8E _RESERVED_0x8F
KEY_HANGEUL KEY_HANJA KEY_KATAKANA KEY_HIRAGANA KEY_ZENKAKUHANKAKU _RESERVED_0x95 _RESERVED_0x96 _RESERVED_0x97
_RESERVED_0x98 _RESERVED_0x99 _RESERVED_0x9A _RESERVED_0x9B _RESERVED_0x9C _RESERVED_0x9D _RESERVED_0x9E _RESERVED_0x9F
_RESERVED_0xA0 _RESERVED_0xA1 _RESERVED_0xA2 _RESERVED_0xA3 _RESERVED_0xA4 _RESERVED_0xA5 _RESERVED_0xA6 _RESERVED_0xA7
_RESERVED_0xA8 _RESERVED_0xA9 _RESERVED_0xAA _RESERVED_0xAB _RESERVED_0xAC _RESERVED_0xAD _RESERVED_0xAE _RESERVED_0xAF
_RESERVED_0xB0 _RESERVED_0xB1 _RESERVED_0xB2 _RESERVED_0xB3 _RESERVED_0xB4 _RESERVED_0xB5 KEY_KPLEFTPAREN KEY_KPRIGHTPAREN
_RESERVED_0xB8 _RESERVED_0xB9 _RESERVED_0xBA _RESERVED_0xBB _RESERVED_0xBC _RESERVED_0xBD _RESERVED_0xBE _RESERVED_0xBF
_RESERVED_0xC0 _RESERVED_0xC1 _RESERVED_0xC2 _RESERVED_0xC3 _RESERVED_0xC4 _RESERVED_0xC5 _RESERVED_0xC6 _RESERVED_0xC7
_RESERVED_0xC8 _RESERVED_0xC9 _RESERVED_0xCA _RESERVED_0xCB _RESERVED_0xCC _RESERVED_0xCD _RESERVED_0xCE _RESERVED_0xCF
_RESERVED_0xD0 _RESERVED_0xD1 _RESERVED_0xD2 _RESERVED_0xD3 _RESERVED_0xD4 _RESERVED_0xD5 _RESERVED_0xD6 _RESERVED_0xD7
_RESERVED_0xD8 _RESERVED_0xD9 _RESERVED_0xDA _RESERVED_0xDB _RESERVED_0xDC _RESERVED_0xDD _RESERVED_0xDE _RESERVED_0xDF
_RESERVED_0xE0 _RESERVED_0xE1 _RESERVED_0xE2 _RESERVED_0xE3 _RESERVED_0xE4 _RESERVED_0xE5 _RESERVED_0xE6 _RESERVED_0xE7
_RESERVED_0xE8 _RESERVED_0xE9 _RESERVED_0xEA _RESERVED_0xEB _RESERVED_0xEC _RESERVED_0xED _RESERVED_0xEE _RESERVED_0xEF
_RESERVED_0xF0 _RESERVED_0xF1 _RESERVED_0xF2 _RESERVED_0xF3 _RESERVED_0xF4 _RESERVED_0xF5 _RESERVED_0xF6 _RESERVED_0xF7
_RESERVED_0xF8 _RESERVED_0xF9 _RESERVED_0xFA _RESERVED_0xFB _RESERVED_0xFC _RESERVED_0xFD _RESERVED_0xFE _RESERVED_0xFF
''')
MODIFIERS = [ uinput.ev.KEY_LEFTCTRL, uinput.ev.KEY_LEFTSHIFT, uinput.ev.KEY_LEFTALT, uinput.ev.KEY_LEFTMETA,
uinput.ev.KEY_RIGHTCTRL, uinput.ev.KEY_RIGHTSHIFT, uinput.ev.KEY_RIGHTALT, uinput.ev.KEY_RIGHTMETA ]
ALL_KEYS = [ v for k, v in uinput.ev.__dict__.items() if k.startswith('KEY_') ]
REGULAR_MAP = { kc.value: getattr(uinput.ev, kc.name) for kc in Keycode if hasattr(uinput.ev, kc.name) }
@classmethod
def map_modifiers(kls, val):
return [ mod for i, mod in enumerate(kls.MODIFIERS) if val & (1<<i) ]
@classmethod
def map_regulars(kls, keycodes):
return [ kls.REGULAR_MAP[kc] for kc in keycodes if kc != 0 and kc in kls.REGULAR_MAP ]
class Magic:
@classmethod
def map_bytes_to_incantation(kls, data):
elems = [ f'{kls.ADJECTIVES[a]} {kls.NOUNS[b]}' for a, b in zip(data[0::2], data[1::2]) ]
nfirst = ", ".join(elems[:-1])
return f'{nfirst} and {elems[-1]}'
ADJECTIVES = '''
wrathful worthy weird warm volatile veiled vacuous useless
upset unsoiled unsightly unpronounceable unfriendly unfree unfit unfaithful
unchaste unbroken unbound unblessed unbefitting unaltered unabused unable
ugly tongued thorny thirsty thick terminal ten-sided teeming
tangerine taken substantial stupefying stringy strange stillborn sticky
stagnant spongy sour soul-destroying smoldering smitten slain six-sided
shifting shadowy severed seven-sided serene salty rust-red royal
rotten riddled resentful regrettable reeking rare rank rancid
quiescent putrid putrid putrescent prehistoric predatory predaceous porous
poisonous pierced phlegmatic petrifying pessimal pathetic odorless oddish
obsessed obscene numb nine-sided nasty mysterious mute musky
morose moribund moldy miasmic material many-lobed malodorous malign
maimed luminescent low-cut lousy live limp lifeless leering
leaky layered latent lackluster jagged irregular iridescent intangible
infinite inept incomprehensible in-between improper idle hunted hideous
heavy hairy guilty grotesque grey greedy gory gorgeous
gooey golden-brown golden ghastly frostbitten fresh-cut freakish frantic
fossilized formless formidable floccose five-lobed firstborn filthy fickle
fetid fertile fearful fatal familiar fallen fallacious faint
faceless extinct esoteric errant emergent elastic eight-sided eerie
ebon dysphoric dying dumb dull-purple dull dull dull
dormant doomed disfigured dirty defenseless deep-pink deep deconsecrated
deathlike deadly dead dark-blue dark curly curious cured
cunning crystalline cryptic crying crumbly crimson crested creepy
crazy corrupt corporeal contemptible contained concrete cloudy chopped
chained caustic catholic cathartic captive cancerous cabalistic burnt
buoyant bronze-red bronze broken bright-red breathless bound bound
bottomless bony bodiless blue-lilac blue bloody bloodthirsty bloodsucking
bloodstained bloodcurdling blonde blistered blank bitter bilgy bewitched
befouled beardless bastardly barbed baleful balding awkward awful
atrocious arcane appalling antic anonymous angry ample ambiguous
amber-green amber aghast activated acidic abused abstruse abject
'''.split()
NOUNS = '''
yolk writing wrath wound worm wings whistle watchdog
waste vomit vermin variation underachievement tusk troll trick
transplant transgression tooth tongue tickle tick thorn thistle
thing terror tentacle tease surrender surge sucker substance
storm stone stew stalk squid sprout sponge spill
spider sphere spectacle speck spawn soul solution snout
snake smell sloth slime slice sleeper slave sinew
shell shape seizure seed schism scam scale sainthood
root robe roach rinse remains relay rejuvenation realization
reaction ransom pupa pride prey predator potion pornography
polyp plum pleasure pitch pigeon phenomenon pest periwinkle
percolation parasite pair oyster orphan orgasm organism orchid
object nail mushroom murder mucus movement mother mold
mist mildew metal mesh meddling mayhem masterpiece masonry
mask manhood maggot lust loop living_thing liquor liquid
lining laceration knife kitten kiss jumper jest instrument
injustice injury influence indulgence incursion impulse imago hound
horn hook hoof heirloom heart hawk hare hair
gulp guardian grass goat gnat gluttony glowworm gasp
game fusion fungus frustration frog foul foot food
fog foal fluke fluff flower flicker flea flattery
flask flare firefly finger filtration female feeder feather
fart fang failure face fabrication extract exodus evil
envy enema embryo egress echo eater ear dwarf
dust drop draft domestication distortion dew depravity deity
death daughter dash dagger culture crutch crow critter
creeper creation crab corruption cocoon claw chip child
cell catch carving carrot carnival cancer butterfly burn
buildup brush brew bottle boot book bone blunder
blot blood blink bite bird benthos beak basket
bark ball baby axolotl ashes artifact arson armor
apparition antenna alms alienation advent adornment abomination abandonment
'''.split()
class NoiseEngine:
def __init__(self, packetizer, debug=False):
self.debug = debug
self.packetizer = packetizer
self.static_local = bytes([ # FIXME
0xbb, 0xdb, 0x4c, 0xdb, 0xd3, 0x09, 0xf1, 0xa1, 0xf2, 0xe1, 0x45, 0x69, 0x67, 0xfe, 0x28, 0x8c,
0xad, 0xd6, 0xf7, 0x12, 0xd6, 0x5d, 0xc7, 0xb7, 0x79, 0x3d, 0x5e, 0x63, 0xda, 0x6b, 0x37, 0x5b
])
self.proto = NoiseConnection.from_name(b'Noise_XX_25519_ChaChaPoly_BLAKE2s')
self.proto.set_as_initiator()
self.proto.set_keypair_from_private_bytes(Keypair.STATIC, self.static_local)
self.proto.start_handshake()
self.packetizer.send_packet(PacketType.INITIATE_HANDSHAKE, b'')
self.debug_print('Handshake started')
@wraps(print)
def debug_print(self, *args, **kwargs):
if self.debug:
print(*args, **kwargs)
def perform_handshake(self):
while True:
if self.proto.handshake_finished:
break
self.packetizer.send_packet(PacketType.HANDSHAKE, self.proto.write_message())
if self.proto.handshake_finished:
break
pkt_type, payload = self.packetizer.receive_packet()
if pkt_type is PacketType.HANDSHAKE:
self.proto.read_message(payload)
else:
raise ValueError(f'Incorrect packet type {pkt_type}. Ignoring since this is only test code.')
if self.debug:
print('Handshake finished, handshake hash:')
hexdump(print, self.proto.get_handshake_hash(), args.width)
def channel_binding_incantation(self):
hhash = self.proto.get_handshake_hash()
return '\n'.join(Magic.map_bytes_to_incantation(hhash[i:i+8]) for i in range(0, 16, 8))
def receive_loop(self):
while True:
try:
pkt_type, received = self.packetizer.receive_packet()
except Exception as e:
self.debug_print('Invalid framing:', e)
if pkt_type is not PacketType.DATA:
raise UserWarning(f'Unexpected packet type {pkt_type}. Ignoring.')
continue
rtype, data = self._decrypt(received)
if self.debug:
print(f'Decrypted packet {rtype} ({rtype.value}):')
hexdump(print, data, args.width)
yield rtype, data
def _decrypt(self, received):
try:
data = self.proto.decrypt(received)
return ReportType(data[0]), data[1:]
except NoiseInvalidMessage as e:
self.debug_print('Invalid noise message', e)
for i in range(3):
with self._nonce_lookahead() as set_nonce:
set_nonce(i)
data = self.proto.decrypt(received)
return ReportType(data[0]), data[1:]
else:
self.debug_print(' Unrecoverable.')
raise e
self.debug_print(f' Recovered. n={n}')
@contextmanager
def _nonce_lookahead(self):
nold = self.proto.noise_protocol.cipher_state_decrypt.n
def setter(n):
self.proto.noise_protocol.cipher_state_decrypt.n = nold + n
with suppress(NoiseInvalMessage):
yield setter
proto.noise_protocol.cipher_state_decrypt.n = nold
def pairing_messages(self):
user_input = ''
for msg_type, payload in self.receive_loop():
if msg_type == ReportType.PAIRING_INPUT:
ch = chr(payload[0])
if ch == '\b':
user_input = user_input[:-1]
else:
user_input += ch
yield user_input
elif msg_type == ReportType.PAIRING_SUCESS:
break
elif msg_type == ReportType.PAIRING_ERROR:
raise ValueError('Device-side pairing error') # FIXME find better exception subclass here
else:
raise ValueError('Invalid report type')
def uinput_passthrough(self):
with uinput.Device(KeyMapper.ALL_KEYS) as ui:
old_kcs = set()
for msg_type, payload in noise.receive_loop():
if msg_type == ReportType.KEYBOARD:
modbyte, _reserved, *keycodes = payload
keys = { *KeyMapper.map_modifiers(modbyte), *KeyMapper.map_regulars(keycodes) }
if args.debug:
print('Emitting:', keys)
for key in keys - old_kcs:
ui.emit(key, 1, syn=False)
for key in old_kcs - keys:
ui.emit(key, 0, syn=False)
ui.syn()
old_kcs = keys
elif msg_type == ReportType.MOUSE:
# FIXME unhandled
pass
if __name__ == '__main__':
import argparse
import serial
parser = argparse.ArgumentParser()
parser.add_argument('serial')
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')
args = parser.parse_args()
ser = serial.Serial(args.serial, args.baudrate)
ser.write(b'\0') # COBS synchronization
packetizer = Packetizer(ser, debug=args.debug, width=args.width)
noise = NoiseEngine(packetizer, debug=args.debug)
noise.perform_handshake()
import uinput
ALL_KEYS = [ v for k, v in uinput.ev.__dict__.items() if k.startswith('KEY_') ]
MODIFIERS = [
uinput.ev.KEY_LEFTCTRL,
uinput.ev.KEY_LEFTSHIFT,
uinput.ev.KEY_LEFTALT,
uinput.ev.KEY_LEFTMETA,
uinput.ev.KEY_RIGHTCTRL,
uinput.ev.KEY_RIGHTSHIFT,
uinput.ev.KEY_RIGHTALT,
uinput.ev.KEY_RIGHTMETA,
]
map_modifiers = lambda x: [ mod for i, mod in enumerate(MODIFIERS) if x & (1<<i) ]
import keymap
map_regular = { v: getattr(uinput.ev, k) for k, v in keymap.__dict__.items() if k.startswith('KEY_') }
map_regulars = lambda keycodes: [ map_regular[kc] for kc in keycodes if kc != 0 and kc in map_regular ]
from noise.connection import NoiseConnection, Keypair
from noise.exceptions import NoiseInvalidMessage
STATIC_LOCAL = bytes([
0xbb, 0xdb, 0x4c, 0xdb, 0xd3, 0x09, 0xf1, 0xa1,
0xf2, 0xe1, 0x45, 0x69, 0x67, 0xfe, 0x28, 0x8c,
0xad, 0xd6, 0xf7, 0x12, 0xd6, 0x5d, 0xc7, 0xb7,
0x79, 0x3d, 0x5e, 0x63, 0xda, 0x6b, 0x37, 0x5b
])
proto = NoiseConnection.from_name(b'Noise_XX_25519_ChaChaPoly_BLAKE2s')
proto.set_as_initiator()
proto.set_keypair_from_private_bytes(Keypair.STATIC, STATIC_LOCAL)
proto.start_handshake()
send_packet(ser, PacketType.INITIATE_HANDSHAKE, b'', args.width)
print('Handshake started')
while True:
if proto.handshake_finished:
break
send_packet(ser, PacketType.HANDSHAKE, proto.write_message(), args.width)
if proto.handshake_finished:
break
pkt_type, payload = receive_packet(ser, args.width)
if pkt_type == PacketType.HANDSHAKE.value:
proto.read_message(payload)
else:
print(f'Incorrect packet type {pkt_type}. Ignoring since this is only test code.')
print('Handshake finished, handshake hash:')
hexdump(print, proto.get_handshake_hash(), args.width)
from nouns import NOUNS
from adjectives import ADJECTIVES
def map_bytes_to_incantation(data):
elems = [ f'{ADJECTIVES[a]} {NOUNS[b]}' for a, b in zip(data[0::2], data[1::2]) ]
nfirst = ", ".join(elems[:-1])
return f'{nfirst} and {elems[-1]}'
print('Handshake channel binding incantation:')
hhash = proto.get_handshake_hash()
print(' ' + map_bytes_to_incantation(hhash[:8 ]))
print(' ' + map_bytes_to_incantation(hhash[ 8:16 ]))
print(' ' + map_bytes_to_incantation(hhash[ 16:24 ]))
print(' ' + map_bytes_to_incantation(hhash[ 24:]))
print(noise.channel_binding_incantation())
old_kcs = set()
def noise_rx(received, ui):
global old_kcs
data = proto.decrypt(received)
#print('Decrypted data:')
#hexdump(print, data, args.width)
rtype, rlen, *report = data
if rtype != 1 or rlen != 8:
return
modbyte, _reserved, *keycodes = report
keys = map_modifiers(modbyte) + map_regulars(keycodes)
print('Emitting:', keys)
keyset = set(keys)
for key in keyset - old_kcs:
ui.emit(key, 1, syn=False)
for key in old_kcs - keyset:
ui.emit(key, 0, syn=False)
ui.syn()
old_kcs = keyset
with uinput.Device(ALL_KEYS) as ui:
while True:
try:
pkt_type, received = receive_packet(ser, args.width)
if pkt_type != PacketType.DATA.value:
print(f'Unexpected packet type {pkt_type}. Ignoring.')
continue
try:
noise_rx(received, ui)
except NoiseInvalidMessage as e:
orig_n = proto.noise_protocol.cipher_state_decrypt.n
print('Invalid noise message', e)
for n in [orig_n+1, orig_n+2, orig_n+3]:
try:
proto.noise_protocol.cipher_state_decrypt.n = n
noise_rx(received, ui)
print(f' Recovered. n={n}')
break
except NoiseInvalidMessage as e:
pass
else:
print(' Unrecoverable.')
proto.noise_protocol.cipher_state_decrypt.n = orig_n
except Exception as e:
print('Invalid framing:', e)
for user_input in noise.pairing_messages():
print('\033[2K\r', end='')
print('Pairing input:', user_input, end='', flush=True)
print()
print('Pairing success')
noise.uinput_passthrough()

176
keymap.py
View file

@ -1,176 +0,0 @@
KEY_RESERVED = 0x00
KEY_A = 0x04
KEY_B = 0x05
KEY_C = 0x06
KEY_D = 0x07
KEY_E = 0x08
KEY_F = 0x09
KEY_G = 0x0a
KEY_H = 0x0b
KEY_I = 0x0c
KEY_J = 0x0d
KEY_K = 0x0e
KEY_L = 0x0f
KEY_M = 0x10
KEY_N = 0x11
KEY_O = 0x12
KEY_P = 0x13
KEY_Q = 0x14
KEY_R = 0x15
KEY_S = 0x16
KEY_T = 0x17
KEY_U = 0x18
KEY_V = 0x19
KEY_W = 0x1a
KEY_X = 0x1b
KEY_Y = 0x1c
KEY_Z = 0x1d
KEY_1 = 0x1e
KEY_2 = 0x1f
KEY_3 = 0x20
KEY_4 = 0x21
KEY_5 = 0x22
KEY_6 = 0x23
KEY_7 = 0x24
KEY_8 = 0x25
KEY_9 = 0x26
KEY_0 = 0x27
KEY_ENTER = 0x28
KEY_ESC = 0x29
KEY_BACKSPACE = 0x2a
KEY_TAB = 0x2b
KEY_SPACE = 0x2c
KEY_MINUS = 0x2d
KEY_EQUAL = 0x2e
KEY_LEFTBRACE = 0x2f
KEY_RIGHTBRACE = 0x30
KEY_BACKSLASH = 0x31
KEY_SEMICOLON = 0x33
KEY_APOSTROPHE = 0x34
KEY_GRAVE = 0x35
KEY_COMMA = 0x36
KEY_DOT = 0x37
KEY_SLASH = 0x38
KEY_CAPSLOCK = 0x39
KEY_F1 = 0x3a
KEY_F2 = 0x3b
KEY_F3 = 0x3c
KEY_F4 = 0x3d
KEY_F5 = 0x3e
KEY_F6 = 0x3f
KEY_F7 = 0x40
KEY_F8 = 0x41
KEY_F9 = 0x42
KEY_F10 = 0x43
KEY_F11 = 0x44
KEY_F12 = 0x45
KEY_SYSRQ = 0x46
KEY_SCROLLLOCK = 0x47
KEY_PAUSE = 0x48
KEY_INSERT = 0x49
KEY_HOME = 0x4a
KEY_PAGEUP = 0x4b
KEY_DELETE = 0x4c
KEY_END = 0x4d
KEY_PAGEDOWN = 0x4e
KEY_RIGHT = 0x4f
KEY_LEFT = 0x50
KEY_DOWN = 0x51
KEY_UP = 0x52
KEY_NUMLOCK = 0x53
KEY_KPSLASH = 0x54
KEY_KPASTERISK = 0x55
KEY_KPMINUS = 0x56
KEY_KPPLUS = 0x57
KEY_KPENTER = 0x58
KEY_KP1 = 0x59
KEY_KP2 = 0x5a
KEY_KP3 = 0x5b
KEY_KP4 = 0x5c
KEY_KP5 = 0x5d
KEY_KP6 = 0x5e
KEY_KP7 = 0x5f
KEY_KP8 = 0x60
KEY_KP9 = 0x61
KEY_KP0 = 0x62
KEY_KPDOT = 0x63
KEY_102ND = 0x64
KEY_COMPOSE = 0x65
KEY_POWER = 0x66
KEY_KPEQUAL = 0x67
KEY_F13 = 0x68
KEY_F14 = 0x69
KEY_F15 = 0x6a
KEY_F16 = 0x6b
KEY_F17 = 0x6c
KEY_F18 = 0x6d
KEY_F19 = 0x6e
KEY_F20 = 0x6f
KEY_F21 = 0x70
KEY_F22 = 0x71
KEY_F23 = 0x72
KEY_F24 = 0x73
KEY_OPEN = 0x74
KEY_HELP = 0x75
KEY_PROPS = 0x76
KEY_FRONT = 0x77
KEY_STOP = 0x78
KEY_AGAIN = 0x79
KEY_UNDO = 0x7a
KEY_CUT = 0x7b
KEY_COPY = 0x7c
KEY_PASTE = 0x7d
KEY_FIND = 0x7e
KEY_MUTE = 0x7f
KEY_VOLUMEUP = 0x80
KEY_VOLUMEDOWN = 0x81
KEY_KPCOMMA = 0x85
KEY_RO = 0x87
KEY_KATAKANAHIRAGANA = 0x88
KEY_YEN = 0x89
KEY_HENKAN = 0x8a
KEY_MUHENKAN = 0x8b
KEY_KPJPCOMMA = 0x8c
KEY_HANGEUL = 0x90
KEY_HANJA = 0x91
KEY_KATAKANA = 0x92
KEY_HIRAGANA = 0x93
KEY_ZENKAKUHANKAKU = 0x94
KEY_KPLEFTPAREN = 0xb6
KEY_KPRIGHTPAREN = 0xb7
KEY_LEFTCTRL = 0xe0
KEY_LEFTSHIFT = 0xe1
KEY_LEFTALT = 0xe2
KEY_LEFTMETA = 0xe3
KEY_RIGHTCTRL = 0xe4
KEY_RIGHTSHIFT = 0xe5
KEY_RIGHTALT = 0xe6
KEY_RIGHTMETA = 0xe7
KEY_PLAYPAUSE = 0xe8
KEY_STOPCD = 0xe9
KEY_PREVIOUSSONG = 0xea
KEY_NEXTSONG = 0xeb
KEY_EJECTCD = 0xec
KEY_VOLUMEUP = 0xed
KEY_VOLUMEDOWN = 0xee
KEY_MUTE = 0xef
KEY_WWW = 0xf0
KEY_BACK = 0xf1
KEY_FORWARD = 0xf2
KEY_STOP = 0xf3
KEY_FIND = 0xf4
KEY_SCROLLUP = 0xf5
KEY_SCROLLDOWN = 0xf6
KEY_EDIT = 0xf7
KEY_SLEEP = 0xf8
KEY_COFFEE = 0xf9
KEY_REFRESH = 0xfa
KEY_CALC = 0xfb

294
nouns.py
View file

@ -1,260 +1,34 @@
NOUNS = [
"yolk", # 0
"writing", # 1
"wrath", # 2
"wound", # 3
"worm", # 4
"wings", # 5
"whistle", # 6
"watchdog", # 7
"waste", # 8
"vomit", # 9
"vermin", # 10
"variation", # 11
"underachievement", # 12
"tusk", # 13
"troll", # 14
"trick", # 15
"transplant", # 16
"transgression", # 17
"tooth", # 18
"tongue", # 19
"tickle", # 20
"tick", # 21
"thorn", # 22
"thistle", # 23
"thing", # 24
"terror", # 25
"tentacle", # 26
"tease", # 27
"surrender", # 28
"surge", # 29
"sucker", # 30
"substance", # 31
"storm", # 32
"stone", # 33
"stew", # 34
"stalk", # 35
"squid", # 36
"sprout", # 37
"sponge", # 38
"spill", # 39
"spider", # 40
"sphere", # 41
"spectacle", # 42
"speck", # 43
"spawn", # 44
"soul", # 45
"solution", # 46
"snout", # 47
"snake", # 48
"smell", # 49
"sloth", # 50
"slime", # 51
"slice", # 52
"sleeper", # 53
"slave", # 54
"sinew", # 55
"shell", # 56
"shape", # 57
"seizure", # 58
"seed", # 59
"schism", # 60
"scam", # 61
"scale", # 62
"sainthood", # 63
"root", # 64
"robe", # 65
"roach", # 66
"rinse", # 67
"remains", # 68
"relay", # 69
"rejuvenation", # 70
"realization", # 71
"reaction", # 72
"ransom", # 73
"pupa", # 74
"pride", # 75
"prey", # 76
"predator", # 77
"potion", # 78
"pornography", # 79
"polyp", # 80
"plum", # 81
"pleasure", # 82
"pitch", # 83
"pigeon", # 84
"phenomenon", # 85
"pest", # 86
"periwinkle", # 87
"percolation", # 88
"parasite", # 89
"pair", # 90
"oyster", # 91
"orphan", # 92
"orgasm", # 93
"organism", # 94
"orchid", # 95
"object", # 96
"nail", # 97
"mushroom", # 98
"murder", # 99
"mucus", # 100
"movement", # 101
"mother", # 102
"mold", # 103
"mist", # 104
"mildew", # 105
"metal", # 106
"mesh", # 107
"meddling", # 108
"mayhem", # 109
"masterpiece", # 110
"masonry", # 111
"mask", # 112
"manhood", # 113
"maggot", # 114
"lust", # 115
"loop", # 116
"living_thing", # 117
"liquor", # 118
"liquid", # 119
"lining", # 120
"laceration", # 121
"knife", # 122
"kitten", # 123
"kiss", # 124
"jumper", # 125
"jest", # 126
"instrument", # 127
"injustice", # 128
"injury", # 129
"influence", # 130
"indulgence", # 131
"incursion", # 132
"impulse", # 133
"imago", # 134
"hound", # 135
"horn", # 136
"hook", # 137
"hoof", # 138
"heirloom", # 139
"heart", # 140
"hawk", # 141
"hare", # 142
"hair", # 143
"gulp", # 144
"guardian", # 145
"grass", # 146
"goat", # 147
"gnat", # 148
"gluttony", # 149
"glowworm", # 150
"gasp", # 151
"game", # 152
"fusion", # 153
"fungus", # 154
"frustration", # 155
"frog", # 156
"foul", # 157
"foot", # 158
"food", # 159
"fog", # 160
"foal", # 161
"fluke", # 162
"fluff", # 163
"flower", # 164
"flicker", # 165
"flea", # 166
"flattery", # 167
"flask", # 168
"flare", # 169
"firefly", # 170
"finger", # 171
"filtration", # 172
"female", # 173
"feeder", # 174
"feather", # 175
"fart", # 176
"fang", # 177
"failure", # 178
"face", # 179
"fabrication", # 180
"extract", # 181
"exodus", # 182
"evil", # 183
"envy", # 184
"enema", # 185
"embryo", # 186
"egress", # 187
"echo", # 188
"eater", # 189
"ear", # 190
"dwarf", # 191
"dust", # 192
"drop", # 193
"draft", # 194
"domestication", # 195
"distortion", # 196
"dew", # 197
"depravity", # 198
"deity", # 199
"death", # 200
"daughter", # 201
"dash", # 202
"dagger", # 203
"culture", # 204
"crutch", # 205
"crow", # 206
"critter", # 207
"creeper", # 208
"creation", # 209
"crab", # 210
"corruption", # 211
"cocoon", # 212
"claw", # 213
"chip", # 214
"child", # 215
"cell", # 216
"catch", # 217
"carving", # 218
"carrot", # 219
"carnival", # 220
"cancer", # 221
"butterfly", # 222
"burn", # 223
"buildup", # 224
"brush", # 225
"brew", # 226
"bottle", # 227
"boot", # 228
"book", # 229
"bone", # 230
"blunder", # 231
"blot", # 232
"blood", # 233
"blink", # 234
"bite", # 235
"bird", # 236
"benthos", # 237
"beak", # 238
"basket", # 239
"bark", # 240
"ball", # 241
"baby", # 242
"axolotl", # 243
"ashes", # 244
"artifact", # 245
"arson", # 246
"armor", # 247
"apparition", # 248
"antenna", # 249
"alms", # 250
"alienation", # 251
"advent", # 252
"adornment", # 253
"abomination", # 254
"abandonment", # 255
]
NOUNS = '''
yolk writing wrath wound worm wings whistle watchdog
waste vomit vermin variation underachievement tusk troll trick
transplant transgression tooth tongue tickle tick thorn thistle
thing terror tentacle tease surrender surge sucker substance
storm stone stew stalk squid sprout sponge spill
spider sphere spectacle speck spawn soul solution snout
snake smell sloth slime slice sleeper slave sinew
shell shape seizure seed schism scam scale sainthood
root robe roach rinse remains relay rejuvenation realization
reaction ransom pupa pride prey predator potion pornography
polyp plum pleasure pitch pigeon phenomenon pest periwinkle
percolation parasite pair oyster orphan orgasm organism orchid
object nail mushroom murder mucus movement mother mold
mist mildew metal mesh meddling mayhem masterpiece masonry
mask manhood maggot lust loop living_thing liquor liquid
lining laceration knife kitten kiss jumper jest instrument
injustice injury influence indulgence incursion impulse imago hound
horn hook hoof heirloom heart hawk hare hair
gulp guardian grass goat gnat gluttony glowworm gasp
game fusion fungus frustration frog foul foot food
fog foal fluke fluff flower flicker flea flattery
flask flare firefly finger filtration female feeder feather
fart fang failure face fabrication extract exodus evil
envy enema embryo egress echo eater ear dwarf
dust drop draft domestication distortion dew depravity deity
death daughter dash dagger culture crutch crow critter
creeper creation crab corruption cocoon claw chip child
cell catch carving carrot carnival cancer butterfly burn
buildup brush brew bottle boot book bone blunder
blot blood blink bite bird benthos beak basket
bark ball baby axolotl ashes artifact arson armor
apparition antenna alms alienation advent adornment abomination abandonment
'''.split()

27
pairing.py Executable file
View file

@ -0,0 +1,27 @@
#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class PairingWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title='SecureHID pairing')
self.set_border_width(10)
self.set_default_size(400, 100)
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\nSearching for device')
self.vbox.add(self.label)
self.add(self.vbox)
if __name__ == '__main__':
window = PairingWindow()
window.connect('destroy', Gtk.main_quit)
window.show_all()
Gtk.main()

View file

@ -184,6 +184,7 @@ int pairing_check(struct NoiseState *st, const char *buf) {
}
void pairing_input(uint8_t modbyte, uint8_t keycode) {
char ch = 0;
uint8_t level = modbyte & MOD_XSHIFT ? LEVEL_SHIFT : LEVEL_NONE;
switch (keycode) {
case KEY_ENTER:
@ -191,8 +192,17 @@ void pairing_input(uint8_t modbyte, uint8_t keycode) {
if (!pairing_check(&noise_state, pairing_buf)) {
persist_remote_key(&noise_state);
/* FIXME write key to backup memory */
uint8_t response = REPORT_PAIRING_SUCCESS;
if (send_encrypted_message(&noise_state, &response, sizeof(response)))
LOG_PRINTF("Error sending pairing response packet\n");
} else {
/* FIXME sound alarm */
uint8_t response = REPORT_PAIRING_ERROR;
if (send_encrypted_message(&noise_state, &response, sizeof(response)))
LOG_PRINTF("Error sending pairing response packet\n");
}
break;
@ -200,17 +210,18 @@ void pairing_input(uint8_t modbyte, uint8_t keycode) {
if (pairing_buf_pos > 0)
pairing_buf_pos--;
pairing_buf[pairing_buf_pos] = '\0'; /* FIXME debug */
ch = '\b';
break;
default:
for (size_t i=0; keycode_mapping[i].kc != KEY_NONE; i++) {
if (keycode_mapping[i].kc == keycode) {
char ch = keycode_mapping[i].ch[level];
/* FIXME send decoded char to host here instead of raw hid report to reduce buggability */
ch = keycode_mapping[i].ch[level];
if (!(('a' <= ch && ch <= 'z') ||
('A' <= ch && ch <= 'Z') ||
('0' <= ch && ch <= '9') ||
(ch == ' ')))
(ch == ' ') ||
(ch == '-')))
break; /* ignore special chars */
if (pairing_buf_pos < sizeof(pairing_buf)-1) /* allow for terminating null byte */ {
@ -218,14 +229,28 @@ void pairing_input(uint8_t modbyte, uint8_t keycode) {
pairing_buf[pairing_buf_pos] = '\0'; /* FIXME debug */
} else {
LOG_PRINTF("Pairing confirmation user input buffer full\n");
/* FIXME return error to host? */
uint8_t response = REPORT_PAIRING_ERROR;
if (send_encrypted_message(&noise_state, &response, sizeof(response)))
LOG_PRINTF("Error sending pairing response packet\n");
}
break;
}
}
break;
}
LOG_PRINTF("Input: %s\n", pairing_buf);
if (ch) {
LOG_PRINTF("Input: %s\n", pairing_buf);
struct hid_report_packet pkt = {
.type = REPORT_PAIRING_INPUT,
.pairing_input = { .c = ch }
};
if (send_encrypted_message(&noise_state, (uint8_t *)&pkt, sizeof(pkt))) {
LOG_PRINTF("Error sending pairing input packet\n");
return;
}
}
}
void pairing_parse_report(struct hid_report *buf, uint8_t len) {
@ -249,44 +274,36 @@ void pairing_parse_report(struct hid_report *buf, uint8_t len) {
memcpy(old_keycodes, buf->keycodes, 6);
}
static void hid_in_message_handler(uint8_t device_id, const uint8_t *data, uint32_t length)
{
if (length < 4) {
LOG_PRINTF("HID report too short\n");
return;
}
if (length > 8) {
LOG_PRINTF("HID report too long\n");
static void hid_in_message_handler(uint8_t device_id, const uint8_t *data, uint32_t length) {
if (length < 4 || length > 8) {
LOG_PRINTF("HID report length must be 4 < len < 8, is %d bytes\n", length);
return;
}
//LOG_PRINTF("Sending event %02X %02X %02X %02X\n", data[0], data[1], data[2], data[3]);
struct hid_report_packet pkt = {
.len = length,
.report = {0}
};
memcpy(pkt.report, data, length);
int type = hid_get_type(device_id);
if (type == HID_TYPE_KEYBOARD) {
if (noise_state.handshake_state == HANDSHAKE_DONE_UNKNOWN_HOST) {
pkt.type = PAIRING;
pairing_parse_report((struct hid_report *)data, length);
} else {
pkt.type = HID_KEYBOARD_REPORT;
}
} else if (type == HID_TYPE_MOUSE) {
if (noise_state.handshake_state == HANDSHAKE_DONE_UNKNOWN_HOST) {
LOG_PRINTF("Not sending HID mouse report during pairing\n");
return;
} else {
pkt.type = HID_MOUSE_REPORT;
}
} else {
if (type != HID_TYPE_KEYBOARD && type != HID_TYPE_MOUSE) {
LOG_PRINTF("Unsupported HID report type %x\n", type);
return;
}
if (noise_state.handshake_state == HANDSHAKE_DONE_UNKNOWN_HOST) {
if (type == HID_TYPE_KEYBOARD)
pairing_parse_report((struct hid_report *)data, length);
else
LOG_PRINTF("Not sending HID mouse report during pairing\n");
return;
}
struct hid_report_packet pkt = {
.type = type == HID_TYPE_KEYBOARD ? REPORT_KEYBOARD : REPORT_MOUSE,
.report = {
.len = length,
.report = {0}
}
};
memcpy(pkt.report.report, data, length);
if (send_encrypted_message(&noise_state, (uint8_t *)&pkt, sizeof(pkt))) {
LOG_PRINTF("Error sending HID report packet\n");
return;

View file

@ -14,18 +14,28 @@ enum control_packet_types {
};
enum packet_types {
_PACKET_RESERVED = 0,
HID_KEYBOARD_REPORT = 1,
HID_MOUSE_REPORT = 2,
PAIRING = 3,
_REPORT_RESERVED = 0,
REPORT_KEYBOARD= 1,
REPORT_MOUSE= 2,
REPORT_PAIRING_INPUT = 3,
REPORT_PAIRING_SUCCESS = 4,
REPORT_PAIRING_ERROR = 5,
};
struct hid_report_packet {
uint8_t type;
uint8_t len;
uint8_t report[8];
union {
struct {
uint8_t len;
uint8_t report[8];
} report;
struct {
char c;
} pairing_input;
};
} __attribute__((__packed__));
struct control_packet {
uint8_t type;
uint8_t payload[0];