Pairing and fingerprint checking works nicely now
This commit is contained in:
parent
b84de745fa
commit
66f9e82c5c
6 changed files with 240 additions and 20 deletions
88
USB_icon.svg
Normal file
88
USB_icon.svg
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.0"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24.000001 24"
|
||||
id="Layer_1"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="USB_icon.svg"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)"
|
||||
inkscape:export-filename="/home/user/ref/libusbhost/secureusb_icon.png"
|
||||
inkscape:export-xdpi="1280"
|
||||
inkscape:export-ydpi="1280"><metadata
|
||||
id="metadata7"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1030"
|
||||
id="namedview5"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:zoom="22.627417"
|
||||
inkscape:cx="6.8983878"
|
||||
inkscape:cy="13.626763"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="50"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="Layer_1" /><defs
|
||||
id="defs1337" />
|
||||
<path
|
||||
d="M 9.1909916,1.0365848 6.7543191,5.2559831 H 8.4925226 V 15.009993 L 4.0559868,10.810749 C 3.7695331,10.453379 3.568585,9.9857932 3.5574545,9.5048004 c 0,-1.9458109 -5.065e-4,-1.7888256 -0.00136,-2.2141627 0.8214146,-0.2883098 1.414308,-1.0627543 1.414308,-1.983421 0,-1.1647037 -0.9451206,-2.1099103 -2.1102466,-2.1099103 -1.1656325,0 -2.11041486,0.9451205 -2.11041486,2.1099103 0,0.9206667 0.59255516,1.6951112 1.41329516,1.983421 l -5.905e-4,2.1728424 c 0,0.9446159 0.5182659,1.9344279 1.1258318,2.5644249 -0.018039,-0.01719 -0.037271,-0.03508 3.365e-4,10e-4 0.014994,0.01332 4.7065449,4.455256 4.7065449,4.455256 0.2860334,0.356613 0.4857161,0.823945 0.4971849,1.304601 v 8.752206 c -1.6117146,0.323388 -2.8260028,1.746635 -2.8260028,3.45389 0,1.946487 1.5778153,3.524302 3.5237111,3.524302 1.9464859,0 3.5243859,-1.577815 3.5243859,-3.524302 0,-1.707592 -1.2153,-3.130838 -2.8283611,-3.454226 v -8.709455 c 0,-0.0061 3.365e-4,-0.01224 0,-0.01856 v -5.301631 c 0.012235,-0.479729 0.2121621,-0.94647 0.4985321,-1.302745 0,0 4.691197,-4.4409224 4.706376,-4.4546676 0.03771,-0.035583 0.01805,-0.017872 3.38e-4,-3.365e-4 0.60748,-0.6299968 1.125408,-1.6202312 1.125408,-2.565013 L 16.215975,3.454345 h 1.414729 V -0.76611257 H 13.410639 V 3.4543809 h 1.412874 c 0,0 -0.0015,-1.7403408 -0.0015,0.7750052 -0.01102,0.4810766 -0.211742,0.9491696 -0.498196,1.306372 L 9.8863699,9.7359362 V 5.2559831 h 1.7409841 z"
|
||||
id="path1334"
|
||||
inkscape:connector-curvature="0"
|
||||
style="stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
inkscape:transform-center-x="3.8484292"
|
||||
inkscape:transform-center-y="16.5482"
|
||||
sodipodi:nodetypes="cccccccssscccccccssscscccccccccccccccccc" />
|
||||
<g
|
||||
id="g4661"
|
||||
transform="matrix(0.95637878,0,0,0.95637878,49.375726,8.0349764)"
|
||||
inkscape:export-xdpi="1280"
|
||||
inkscape:export-ydpi="1280"><rect
|
||||
ry="1.6849748"
|
||||
y="6.75"
|
||||
x="-39.125"
|
||||
height="9.8125"
|
||||
width="12.375"
|
||||
id="rect4608"
|
||||
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.8904658;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /><rect
|
||||
ry="3.9323201"
|
||||
y="0.68743151"
|
||||
x="-36.86982"
|
||||
height="9.4375687"
|
||||
width="7.8646402"
|
||||
id="rect4610"
|
||||
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2.69644809;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /><g
|
||||
transform="matrix(0.93546514,0,0,0.93546514,-1.0854549,1.4271834)"
|
||||
id="g4654"><circle
|
||||
style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.72307682;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="path4627"
|
||||
cx="-34.051041"
|
||||
cy="8.8928328"
|
||||
r="1.84375" /><path
|
||||
style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m -34.754167,8.8303324 h 1.40625 l 1.140625,4.8258756 c 0.125,0.616281 -0.508719,1.140625 -1.140625,1.140625 h -1.40625 c -0.631906,0 -1.25,-0.508719 -1.140625,-1.140625 z"
|
||||
id="rect4629"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccsscc" /></g></g><path
|
||||
style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M -21.203125 -21.580078 L -21.203125 45.580078 L 45.203125 45.580078 L 45.203125 -21.580078 L -21.203125 -21.580078 z M 0 0 L 24 0 L 24 24 L 0 24 L 0 0 z "
|
||||
id="rect816" /></svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
26
hexnoise.py
26
hexnoise.py
|
|
@ -4,6 +4,8 @@ import time
|
|||
import enum
|
||||
import sys
|
||||
from contextlib import contextmanager, suppress, wraps
|
||||
import hashlib
|
||||
import secrets
|
||||
|
||||
import serial
|
||||
from cobs import cobs
|
||||
|
|
@ -195,20 +197,33 @@ class Magic:
|
|||
'''.split()
|
||||
|
||||
class NoiseEngine:
|
||||
def __init__(self, packetizer, debug=False):
|
||||
def __init__(self, host_key, 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.static_local = host_key
|
||||
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.handshake = self.proto.noise_protocol.handshake_state # save for later because someone didn't think
|
||||
self.paired = False
|
||||
self.connected = False
|
||||
|
||||
@property
|
||||
def remote_fingerprint(self):
|
||||
''' Return the SHA-256 hash of the remote static key (rs). This can be used to fingerprint the remote party. '''
|
||||
return hashlib.sha256(self.handshake.rs.public_bytes).hexdigest()
|
||||
|
||||
@classmethod
|
||||
def generate_private_key_x25519(kls):
|
||||
# This is taken from noise-c's reference implementation. This would not be needed had not cryptography/hazmat
|
||||
# decided noone would ever need serialized x25519 private keys and noiseprotocol stopped just short of implementing
|
||||
# key generation (who'd need that anyway, amiright?) -.-
|
||||
key = list(secrets.token_bytes(32))
|
||||
key[0] &= 0xF8
|
||||
key[31] = (key[31] & 0x7F) | 0x40
|
||||
return bytes(key)
|
||||
|
||||
@wraps(print)
|
||||
def debug_print(self, *args, **kwargs):
|
||||
if self.debug:
|
||||
|
|
@ -306,6 +321,7 @@ class NoiseEngine:
|
|||
yield user_input
|
||||
|
||||
elif msg_type is ReportType.PAIRING_SUCCESS:
|
||||
self.paired = True
|
||||
break
|
||||
|
||||
elif msg_type is ReportType.PAIRING_ERROR:
|
||||
|
|
|
|||
80
pairing.py
80
pairing.py
|
|
@ -1,6 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
import threading
|
||||
import binascii
|
||||
import re
|
||||
import os
|
||||
|
||||
import serial
|
||||
import gi
|
||||
|
|
@ -14,6 +16,7 @@ class PairingWindow(Gtk.Window):
|
|||
Gtk.Window.__init__(self, title='SecureHID pairing')
|
||||
self.noise = noise
|
||||
self.debug = debug
|
||||
self.trusted = False
|
||||
|
||||
self.set_border_width(10)
|
||||
self.set_default_size(600, 200)
|
||||
|
|
@ -30,6 +33,16 @@ class PairingWindow(Gtk.Window):
|
|||
self.entry.set_editable(False)
|
||||
self.vbox.pack_start(self.entry, True, True, 0)
|
||||
|
||||
self.confirm_button = Gtk.Button(label='Trust this device')
|
||||
self.confirm_button.connect('clicked', self.confirm_trust)
|
||||
self.confirm_button.set_sensitive(False)
|
||||
self.abort_button = Gtk.Button(label='Abort')
|
||||
self.abort_button.connect('clicked', lambda _foo: self.destroy())
|
||||
self.bbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
|
||||
self.bbox.pack_start(self.confirm_button, True, True, 0)
|
||||
self.bbox.pack_start(self.abort_button, True, True, 0)
|
||||
self.vbox.pack_start(self.bbox, True, True, 0)
|
||||
|
||||
self.add(self.vbox)
|
||||
|
||||
self.handshaker = threading.Thread(target=self.pair, daemon=True)
|
||||
|
|
@ -64,9 +77,19 @@ class PairingWindow(Gtk.Window):
|
|||
try:
|
||||
for user_input in self.noise.pairing_messages():
|
||||
GLib.idle_add(update_text, user_input)
|
||||
self.destroy()
|
||||
except noise.ProtocolError as e:
|
||||
GLib.idle_add(self.label.set_markup, f'<b>Error: {e}!</b>')
|
||||
|
||||
GLib.idle_add(self.finish_pairing)
|
||||
except hexnoise.ProtocolError as e:
|
||||
GLib.idle_add(self.label.set_markup, f'<b>Error: {e}</b>')
|
||||
|
||||
def finish_pairing(self):
|
||||
self.label.set_markup(f'<b>Step 3</b>\n\nConfirm pairing.\n'
|
||||
f'In case the device did not sound an alarm just now, confirm pairing now using the button below.')
|
||||
self.confirm_button.set_sensitive(True)
|
||||
|
||||
def confirm_trust(self, _foo):
|
||||
self.trusted = True
|
||||
self.destroy()
|
||||
|
||||
|
||||
class StatusIcon(Gtk.StatusIcon):
|
||||
|
|
@ -75,12 +98,30 @@ class StatusIcon(Gtk.StatusIcon):
|
|||
self.set_tooltip_text('SecureHID connected')
|
||||
self.set_from_file('secureusb_icon.png')
|
||||
|
||||
def run_pairing_gui(port, baudrate, debug=False):
|
||||
XDG_CONFIG_HOME = os.environ.get('XDG_CONFIG_HOME') or os.path.join(os.path.expandvars('$HOME'), '.config', 'secure_hid')
|
||||
if not os.path.isdir(XDG_CONFIG_HOME):
|
||||
os.mkdir(XDG_CONFIG_HOME)
|
||||
|
||||
def run_pairing_gui(serial, baudrate, debug=False):
|
||||
ser = serial.Serial(serial, baudrate)
|
||||
packetizer = hexnoise.Packetizer(serial, debug=debug)
|
||||
noise = hexnoise.NoiseEngine(packetizer, debug=debug)
|
||||
private_key_file = os.path.join(XDG_CONFIG_HOME, 'host_key.pem')
|
||||
if not os.path.isfile(private_key_file):
|
||||
with open(private_key_file, 'w') as f:
|
||||
f.write(binascii.hexlify(hexnoise.NoiseEngine.generate_private_key_x25519()).decode())
|
||||
|
||||
known_devices_file = os.path.join(XDG_CONFIG_HOME, 'known_devices')
|
||||
if not os.path.isfile(known_devices_file):
|
||||
with open(known_devices_file, 'w') as f:
|
||||
f.write('# This file contains the hex-encoded SHA-256 fingerprints of the X25519 keys of all trusted SecureHID devices\n')
|
||||
|
||||
with open(private_key_file) as f:
|
||||
host_key_private = binascii.unhexlify(f.read())
|
||||
|
||||
ser = serial.Serial(port, baudrate)
|
||||
packetizer = hexnoise.Packetizer(ser, debug=debug)
|
||||
noise = hexnoise.NoiseEngine(host_key_private, packetizer, debug=debug)
|
||||
noise.perform_handshake()
|
||||
print('Connected.')
|
||||
print('Device fingerprint:', noise.remote_fingerprint)
|
||||
|
||||
if not noise.paired:
|
||||
window = PairingWindow(noise, debug=debug)
|
||||
|
|
@ -88,12 +129,27 @@ def run_pairing_gui(serial, baudrate, debug=False):
|
|||
window.show_all()
|
||||
Gtk.main()
|
||||
|
||||
if self.noise.paired:
|
||||
input_runner = threading.Thread(target=noise.uinput_passthrough, daemon=True)
|
||||
input_runner.start()
|
||||
if not window.trusted:
|
||||
raise SystemError('User abort')
|
||||
|
||||
status_icon = StatusIcon()
|
||||
Gtk.main()
|
||||
if not noise.paired:
|
||||
raise SystemError('Unknown noise error')
|
||||
|
||||
with open(known_devices_file, 'a') as f:
|
||||
f.write(noise.remote_fingerprint)
|
||||
|
||||
else:
|
||||
with open(known_devices_file) as f:
|
||||
known_devices = [ l.strip() for l in f.readlines() if not l[0] == '#' ]
|
||||
|
||||
if noise.remote_fingerprint not in known_devices:
|
||||
raise ValueError('Remote host is untrusted but seems to trust us.')
|
||||
|
||||
input_runner = threading.Thread(target=noise.uinput_passthrough, daemon=True)
|
||||
input_runner.start()
|
||||
|
||||
status_icon = StatusIcon()
|
||||
Gtk.main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
|
|
|||
BIN
secureusb_icon.png
Normal file
BIN
secureusb_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
46
src/demo.c
46
src/demo.c
|
|
@ -175,7 +175,6 @@ int pairing_check(struct NoiseState *st, const char *buf) {
|
|||
|
||||
if (strncasecmp(p, "and", plen)) { /* ignore "and" */
|
||||
int num = -1;
|
||||
/* FIXME ignore "and", ignore commata and dots */
|
||||
for (int i=0; i<256; i++) {
|
||||
if ((!strncasecmp(p, adjectives[i], plen) && plen == strlen(adjectives[i]))
|
||||
|| (!strncasecmp(p, nouns[i], plen) && plen == strlen(nouns[i] ))) {
|
||||
|
|
@ -237,7 +236,7 @@ void pairing_input(uint8_t modbyte, uint8_t keycode) {
|
|||
case KEY_BACKSPACE:
|
||||
if (pairing_buf_pos > 0)
|
||||
pairing_buf_pos--;
|
||||
pairing_buf[pairing_buf_pos] = '\0'; /* FIXME debug */
|
||||
pairing_buf[pairing_buf_pos] = '\0';
|
||||
ch = '\b';
|
||||
break;
|
||||
|
||||
|
|
@ -250,7 +249,7 @@ void pairing_input(uint8_t modbyte, uint8_t keycode) {
|
|||
|
||||
if (pairing_buf_pos < sizeof(pairing_buf)-1) /* allow for terminating null byte */ {
|
||||
pairing_buf[pairing_buf_pos++] = ch;
|
||||
pairing_buf[pairing_buf_pos] = '\0'; /* FIXME debug */
|
||||
pairing_buf[pairing_buf_pos] = '\0';
|
||||
} else {
|
||||
LOG_PRINTF("Pairing confirmation user input buffer full\n");
|
||||
|
||||
|
|
@ -356,6 +355,42 @@ struct dma_usart_file debug_out_s = {
|
|||
};
|
||||
struct dma_usart_file *debug_out = &debug_out_s;
|
||||
|
||||
/* FIXME start unsafe debug code */
|
||||
void usart1_isr(void) {
|
||||
if (USART1_SR & USART_SR_ORE) { /* Overrun handling */
|
||||
LOG_PRINTF("USART1 data register overrun\n");
|
||||
/* Clear interrupt flag */
|
||||
return (void)USART1_DR;
|
||||
}
|
||||
|
||||
uint8_t data = USART1_DR; /* This automatically acknowledges the IRQ */
|
||||
for (size_t i=0; keycode_mapping[i].kc != KEY_NONE; i++) {
|
||||
struct hid_report report = {0};
|
||||
if (keycode_mapping[i].ch[0] == data)
|
||||
report.modifiers = 0;
|
||||
else if (keycode_mapping[i].ch[1] == data)
|
||||
report.modifiers = MOD_LSHIFT;
|
||||
else continue;
|
||||
|
||||
report.keycodes[0] = keycode_mapping[i].kc;
|
||||
pairing_parse_report(&report, 8);
|
||||
break;
|
||||
}
|
||||
LOG_PRINTF(" %02x ", data);
|
||||
if (data == 0x7f) {
|
||||
struct hid_report report = {.modifiers=0, .keycodes={KEY_BACKSPACE, 0}};
|
||||
pairing_parse_report(&report, 8);
|
||||
} else if (data == '\r') {
|
||||
struct hid_report report = {.modifiers=0, .keycodes={KEY_ENTER, 0}};
|
||||
pairing_parse_report(&report, 8);
|
||||
LOG_PRINTF("\n");
|
||||
}
|
||||
|
||||
struct hid_report report = {0};
|
||||
pairing_parse_report(&report, 8);
|
||||
}
|
||||
/* end unsafe debug code */
|
||||
|
||||
void DMA_ISR(DEBUG_USART_DMA_NUM, DEBUG_USART_DMA_STREAM_NUM)(void) {
|
||||
TRACING_SET(TR_DEBUG_OUT_DMA_IRQ);
|
||||
if (dma_get_interrupt_flag(debug_out->dma, debug_out->stream, DMA_FEIF)) {
|
||||
|
|
@ -384,6 +419,11 @@ int main(void)
|
|||
|
||||
#ifdef USART_DEBUG
|
||||
usart_dma_init(debug_out);
|
||||
/* FIXME start unsafe debug code */
|
||||
usart_enable_rx_interrupt(debug_out->usart);
|
||||
nvic_enable_irq(NVIC_USART1_IRQ);
|
||||
nvic_set_priority(NVIC_USART1_IRQ, 3<<4);
|
||||
/* end unsafe debug code */
|
||||
#endif
|
||||
|
||||
usart_dma_init(usart2_out);
|
||||
|
|
|
|||
20
src/tracing.h
Normal file
20
src/tracing.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef __TRACING_H__
|
||||
#define __TRACING_H__
|
||||
|
||||
#include <libopencm3/stm32/gpio.h>
|
||||
|
||||
#define TRACING_SET(i) gpio_set(GPIOD, (1<<i))
|
||||
#define TRACING_CLEAR(i) gpio_clear(GPIOD, (1<<i))
|
||||
|
||||
enum tracing_channels {
|
||||
TR_HID_MESSAGE_HANDLER = 0,
|
||||
TR_DEBUG_OUT_DMA_IRQ = 1,
|
||||
TR_HOST_IF_DMA_IRQ = 2,
|
||||
TR_HOST_IF_USART_IRQ = 3,
|
||||
TR_USBH_POLL = 4,
|
||||
TR_HOST_PKT_HANDLER = 5,
|
||||
TR_NOISE_HANDSHAKE = 6,
|
||||
TR_RNG = 7,
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue