diff options
-rw-r--r-- | adjectives.py | 294 | ||||
-rw-r--r-- | hexdump.py | 27 | ||||
-rwxr-xr-x | hexnoise.py | 452 | ||||
-rw-r--r-- | keymap.py | 176 | ||||
-rw-r--r-- | nouns.py | 294 | ||||
-rwxr-xr-x | pairing.py | 27 | ||||
-rw-r--r-- | src/demo.c | 83 | ||||
-rw-r--r-- | src/packet_interface.h | 22 |
8 files changed, 487 insertions, 888 deletions
diff --git a/adjectives.py b/adjectives.py index 78641ce..6d7deb0 100644 --- a/adjectives.py +++ b/adjectives.py @@ -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() diff --git a/hexdump.py b/hexdump.py new file mode 100644 index 0000000..943d8e4 --- /dev/null +++ b/hexdump.py @@ -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() + diff --git a/hexnoise.py b/hexnoise.py index 6b0aad8..8745159 100755 --- a/hexnoise.py +++ b/hexnoise.py @@ -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 - -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() - -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:] + PAIRING_INPUT = 3 + PAIRING_SUCESS = 4 + PAIRING_ERROR = 5 + +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 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:])) - - 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) + print(noise.channel_binding_incantation()) + + 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() @@ -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 @@ -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() diff --git a/pairing.py b/pairing.py new file mode 100755 index 0000000..8af78d9 --- /dev/null +++ b/pairing.py @@ -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() + @@ -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;
+ 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 {
- pkt.type = HID_KEYBOARD_REPORT;
- }
- } else if (type == HID_TYPE_MOUSE) {
- if (noise_state.handshake_state == HANDSHAKE_DONE_UNKNOWN_HOST) {
+ else
LOG_PRINTF("Not sending HID mouse report during pairing\n");
- return;
- } else {
- pkt.type = HID_MOUSE_REPORT;
- }
- } else {
- LOG_PRINTF("Unsupported HID report type %x\n", type);
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;
diff --git a/src/packet_interface.h b/src/packet_interface.h index 9e1327d..638405a 100644 --- a/src/packet_interface.h +++ b/src/packet_interface.h @@ -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]; |