From 2334c43a39dcc7eba00811c36a9bc3362177a10b Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Tue, 5 Jul 2016 14:40:09 -0400 Subject: Add horrible kludge of a script to probe USB ports and report which ones look like console and RPC ports for the HSM. --- projects/hsm/cryptech_probe | 129 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100755 projects/hsm/cryptech_probe (limited to 'projects/hsm/cryptech_probe') diff --git a/projects/hsm/cryptech_probe b/projects/hsm/cryptech_probe new file mode 100755 index 0000000..16a2a8f --- /dev/null +++ b/projects/hsm/cryptech_probe @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# +# Copyright (c) 2016, NORDUnet A/S All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# - Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# - Neither the name of the NORDUnet nor the names of its contributors may +# be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +Utility to probe USB serial port(s) trying to figure out which one(s) +we have plugged in today. +""" + +import sys +import time +import argparse +import serial.tools.list_ports_posix + +parser = argparse.ArgumentParser(formatter_class = argparse.ArgumentDefaultsHelpFormatter) +parser.add_argument("-v", "--verbose", action = "store_true", help = "blather about what we're doing") +args = parser.parse_args() + +SLIP_END = chr(0300) # Indicates end of SLIP packet +SLIP_ESC = chr(0333) # Indicates byte stuffing +SLIP_ESC_END = chr(0334) # ESC ESC_END means END data byte +SLIP_ESC_ESC = chr(0335) # ESC ESC_ESC means ESC data byte + +Control_U = chr(0025) # Console: clear line +Control_M = chr(0015) # Console: end of line + +RPC_query = chr(0) * 8 # client_handle = 0, function code = RPC_FUNC_GET_VERSION +RPC_reply = chr(0) * 12 # opcode = RPC_FUNC_GET_VERSION, client_handle = 0, valret = HAL_OK + +# This is the query string we send to each USB port we find. It's +# intended to be relatively harmless, at least for either of the HSM +# ports: the final Control-U should prevent the console from trying to +# interpret the RPC command, and the SLIP_END markers should cause +# the RPC server to treat the ASCII control characters as noise. +# +# Yes, this is a total kludge. Useful identifiers for the USB ports +# are are on the wish list for a future revision of the hardware, but +# for the moment, we do what we can with what we have. + +probe_string = SLIP_END + Control_U + SLIP_END + RPC_query + SLIP_END + Control_U + Control_M + +def looks_like_console(response): + # Check whether we got a known console prompt. + return any(prompt in response for prompt in ("Username:", "Password:", "cryptech>")) + + +def looks_like_rpc(response): + # Check whether we got something that looks like the response to an RPC version query. + # We skip over the version value itself, as it might change, but we check that it's + # terminated properly. This is fragile, and will need to handle SLIP decoding if + # we ever bump one of the version fields up into the range where the SLIP control + # characters live, but it will do for the moment. + try: + return response[response.index(SLIP_END + RPC_reply) + len(SLIP_END + RPC_reply) + 4] == SLIP_END + except ValueError: + return False + except IndexError: + return False + + +rpc_hints = None + +ports = dict((port, None) + for port, desc, hwid in serial.tools.list_ports_posix.comports() + if "VID:PID=0403:6014" in hwid) + +if not ports: + sys.exit("Couldn't find any likely USB ports") + +if args.verbose: + print "Candidate USB ports:", ", ".join(ports) + +for port in ports: + while True: + try: + ports[port] = serial.Serial(port, 921600, timeout=0.1) + break + except serial.SerialException: + time.sleep(0.2) + +for port in ports: + # Do we really need to dole out characters one at a time here? + # Dunno, but this works well enough. + for c in probe_string: + ports[port].write(c) + time.sleep(0.1) + +time.sleep(1) + +for port in ports: + s = "" + while True: + c = ports[port].read(1) + if len(c) > 0: + s += c + else: + break + if args.verbose: + print "Received from {}: {!r} ({})".format(port, s, ":".join("{:02x}".format(ord(c)) for c in s)) + if looks_like_console(s): + print "{} looks like the Cryptech HSM console port".format(port) + if looks_like_rpc(s): + print "{} looks like the Cryptech HSM RPC port".format(port) -- cgit v1.2.3 From 40e903c8e0a925df1c67eec6fbd1be6ffccd0f09 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Thu, 7 Jul 2016 15:05:37 -0400 Subject: Rewrite and add cleanup sequences to avoid confusing the RPC server. --- projects/hsm/cryptech_probe | 88 +++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 43 deletions(-) (limited to 'projects/hsm/cryptech_probe') diff --git a/projects/hsm/cryptech_probe b/projects/hsm/cryptech_probe index 16a2a8f..bc798bc 100755 --- a/projects/hsm/cryptech_probe +++ b/projects/hsm/cryptech_probe @@ -38,8 +38,15 @@ import time import argparse import serial.tools.list_ports_posix +class positive_integer(int): + def __init__(self, value): + if self <= 0: + raise ValueError + parser = argparse.ArgumentParser(formatter_class = argparse.ArgumentDefaultsHelpFormatter) -parser.add_argument("-v", "--verbose", action = "store_true", help = "blather about what we're doing") +parser.add_argument("-v", "--verbose", action = "store_true", help = "blather about what we're doing") +parser.add_argument("--no-cleanup", action = "store_true", help = "don't send cleanup sequences after probing") +parser.add_argument("--read-size", type = positive_integer, help = "size of read buffer", default = 1024) args = parser.parse_args() SLIP_END = chr(0300) # Indicates end of SLIP packet @@ -65,30 +72,8 @@ RPC_reply = chr(0) * 12 # opcode = RPC_FUNC_GET_VERSION, client_handle = probe_string = SLIP_END + Control_U + SLIP_END + RPC_query + SLIP_END + Control_U + Control_M -def looks_like_console(response): - # Check whether we got a known console prompt. - return any(prompt in response for prompt in ("Username:", "Password:", "cryptech>")) - - -def looks_like_rpc(response): - # Check whether we got something that looks like the response to an RPC version query. - # We skip over the version value itself, as it might change, but we check that it's - # terminated properly. This is fragile, and will need to handle SLIP decoding if - # we ever bump one of the version fields up into the range where the SLIP control - # characters live, but it will do for the moment. - try: - return response[response.index(SLIP_END + RPC_reply) + len(SLIP_END + RPC_reply) + 4] == SLIP_END - except ValueError: - return False - except IndexError: - return False - - -rpc_hints = None - -ports = dict((port, None) - for port, desc, hwid in serial.tools.list_ports_posix.comports() - if "VID:PID=0403:6014" in hwid) +ports = [port for port, desc, hwid in serial.tools.list_ports_posix.comports() + if "VID:PID=0403:6014" in hwid] if not ports: sys.exit("Couldn't find any likely USB ports") @@ -97,33 +82,50 @@ if args.verbose: print "Candidate USB ports:", ", ".join(ports) for port in ports: + while True: try: - ports[port] = serial.Serial(port, 921600, timeout=0.1) + tty = serial.Serial(port, 921600, timeout=0.1) break except serial.SerialException: time.sleep(0.2) -for port in ports: - # Do we really need to dole out characters one at a time here? - # Dunno, but this works well enough. for c in probe_string: - ports[port].write(c) + tty.write(c) time.sleep(0.1) -time.sleep(1) - -for port in ports: - s = "" - while True: - c = ports[port].read(1) - if len(c) > 0: - s += c - else: - break + response = tty.read(args.read_size) if args.verbose: - print "Received from {}: {!r} ({})".format(port, s, ":".join("{:02x}".format(ord(c)) for c in s)) - if looks_like_console(s): + print "Received from {}: {!r} ({})".format(port, response, ":".join("{:02x}".format(ord(c)) for c in response)) + + # Check whether we got a known console prompt. + + is_cty = any(prompt in response for prompt in ("Username:", "Password:", "cryptech>")) + + # Check whether we got something that looks like the response to an RPC version query. + # We skip over the version value itself, as it might change, but we check that it's + # terminated properly. This is fragile, and will need to handle SLIP decoding if + # we ever bump one of the version fields up into the range where the SLIP control + # characters live, but it will do for the moment. + + try: + is_hsm = response[response.index(SLIP_END + RPC_reply) + len(SLIP_END + RPC_reply) + 4] == SLIP_END + except ValueError: + is_hsm = False + except IndexError: + is_hsm = False + + if is_cty: print "{} looks like the Cryptech HSM console port".format(port) - if looks_like_rpc(s): + if is_hsm: print "{} looks like the Cryptech HSM RPC port".format(port) + + if (is_cty or is_hsm) and not args.no_cleanup: + if is_cty: + tty.write(Control_U) + if is_hsm: + tty.write(SLIP_END) + while tty.read(args.read_size): + pass + + tty.close() -- cgit v1.2.3 From 41b72119bc23b942af5d7d1b7a7a704f2ab8aa5b Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Thu, 7 Jul 2016 21:11:47 -0400 Subject: Add cryptech_miniterm; tweak cryptech_probe to write environment variables like ssh-agent. --- projects/hsm/cryptech_probe | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) (limited to 'projects/hsm/cryptech_probe') diff --git a/projects/hsm/cryptech_probe b/projects/hsm/cryptech_probe index bc798bc..14dae01 100755 --- a/projects/hsm/cryptech_probe +++ b/projects/hsm/cryptech_probe @@ -30,7 +30,9 @@ """ Utility to probe USB serial port(s) trying to figure out which one(s) -we have plugged in today. +we have plugged in today. stdout is environment variable settings, +suitable for use in bash with "eval `cryptech_probe`"; all other output +goes to stderr. """ import sys @@ -44,9 +46,10 @@ class positive_integer(int): raise ValueError parser = argparse.ArgumentParser(formatter_class = argparse.ArgumentDefaultsHelpFormatter) -parser.add_argument("-v", "--verbose", action = "store_true", help = "blather about what we're doing") -parser.add_argument("--no-cleanup", action = "store_true", help = "don't send cleanup sequences after probing") -parser.add_argument("--read-size", type = positive_integer, help = "size of read buffer", default = 1024) +parser.add_argument("-v", "--verbose", action = "store_true", help = "produce human-readable output") +parser.add_argument("-d", "--debug", action = "store_true", help = "blather about what we're doing") +parser.add_argument("--no-cleanup", action = "store_true", help = "don't send cleanup sequences after probing") +parser.add_argument("--read-buffer-size", type = positive_integer, help = "size of read buffer", default = 1024) args = parser.parse_args() SLIP_END = chr(0300) # Indicates end of SLIP packet @@ -78,8 +81,10 @@ ports = [port for port, desc, hwid in serial.tools.list_ports_posix.comports() if not ports: sys.exit("Couldn't find any likely USB ports") -if args.verbose: - print "Candidate USB ports:", ", ".join(ports) +if args.debug: + sys.stderr.write("Candidate USB ports: {}\n".format(", ".join(ports))) + +env = {} for port in ports: @@ -94,9 +99,9 @@ for port in ports: tty.write(c) time.sleep(0.1) - response = tty.read(args.read_size) - if args.verbose: - print "Received from {}: {!r} ({})".format(port, response, ":".join("{:02x}".format(ord(c)) for c in response)) + response = tty.read(args.read_buffer_size) + if args.debug: + sys.stderr.write("Received from {}: {!r} ({})\n".format(port, response, ":".join("{:02x}".format(ord(c)) for c in response))) # Check whether we got a known console prompt. @@ -115,17 +120,26 @@ for port in ports: except IndexError: is_hsm = False + if is_cty and args.verbose: + sys.stderr.write("{} looks like the Cryptech HSM console port\n".format(port)) + + if is_hsm and args.verbose: + sys.stderr.write("{} looks like the Cryptech HSM RPC port\n".format(port)) + if is_cty: - print "{} looks like the Cryptech HSM console port".format(port) + env.update(CRYPTECH_CTY_CLIENT_SERIAL_DEVICE = port) + if is_hsm: - print "{} looks like the Cryptech HSM RPC port".format(port) + env.update(CRYPTECH_RPC_CLIENT_SERIAL_DEVICE = port) if (is_cty or is_hsm) and not args.no_cleanup: if is_cty: tty.write(Control_U) if is_hsm: tty.write(SLIP_END) - while tty.read(args.read_size): + while tty.read(args.read_buffer_size): pass tty.close() + +sys.stdout.write("export {}\n".format(" ".join("{}='{}'".format(var, env[var]) for var in sorted(env)))) -- cgit v1.2.3