summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--adjectives.py294
-rw-r--r--hexdump.py27
-rwxr-xr-xhexnoise.py452
-rw-r--r--keymap.py176
-rw-r--r--nouns.py294
-rwxr-xr-xpairing.py27
-rw-r--r--src/demo.c83
-rw-r--r--src/packet_interface.h22
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()
diff --git a/keymap.py b/keymap.py
index efef507..e69de29 100644
--- a/keymap.py
+++ b/keymap.py
@@ -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
diff --git a/nouns.py b/nouns.py
index 27d1b91..602e93c 100644
--- a/nouns.py
+++ b/nouns.py
@@ -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()
+
diff --git a/src/demo.c b/src/demo.c
index 8febe9b..6c519e1 100644
--- a/src/demo.c
+++ b/src/demo.c
@@ -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];