From d7e7dbc990aca690c069060bd39a168ffbd28e64 Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Thu, 30 Jun 2016 22:10:01 -0400 Subject: RPC wire format now includes client handle in all requests, and opcode and client handle in all responses. --- projects/hsm/hsm.c | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) (limited to 'projects/hsm') diff --git a/projects/hsm/hsm.c b/projects/hsm/hsm.c index 5758f05..9cc6e9a 100644 --- a/projects/hsm/hsm.c +++ b/projects/hsm/hsm.c @@ -148,6 +148,7 @@ static void dispatch_thread(void const *args) while (1) { memset(&ibuf, 0, sizeof(ibuf)); memset(&obuf, 0, sizeof(obuf)); + obuf.len = sizeof(obuf.buf); /* Wait for access to the uart */ osMutexWait(dispatch_mutex, osWaitForever); @@ -161,16 +162,12 @@ static void dispatch_thread(void const *args) /* Let the next thread take the mutex */ osThreadYield(); - /* Copy client ID from request to response */ - memcpy(obuf.buf, ibuf.buf, 4); - obuf.len = sizeof(obuf.buf) - 4; - /* Process the request */ - hal_rpc_server_dispatch(ibuf.buf + 4, ibuf.len - 4, obuf.buf + 4, &obuf.len); + hal_rpc_server_dispatch(ibuf.buf, ibuf.len, obuf.buf, &obuf.len); /* Send the response */ osMutexWait(uart_mutex, osWaitForever); - hal_error_t ret = hal_rpc_sendto(obuf.buf, obuf.len + 4, NULL); + hal_error_t ret = hal_rpc_sendto(obuf.buf, obuf.len, NULL); osMutexRelease(uart_mutex); if (ret != HAL_OK) Error_Handler(); @@ -207,15 +204,6 @@ int main() { stm_init(); -#ifdef TARGET_CRYPTECH_DEV_BRIDGE - /* Wait six seconds to not upset the Novena at boot. */ - led_on(LED_BLUE); - for (int i = 0; i < 12; i++) { - osDelay(500); - led_toggle(LED_BLUE); - } - led_off(LED_BLUE); -#endif led_on(LED_GREEN); /* Prepare FMC interface. */ fmc_init(); @@ -232,13 +220,6 @@ int main() dispatch_mutex = osMutexCreate(osMutex(dispatch_mutex)); rpc_sem = osSemaphoreCreate(osSemaphore(rpc_sem), 0); -#ifdef TARGET_CRYPTECH_ALPHA - /* Launch other threads: - * - admin thread on USART1 - * - csprng warm-up thread? - */ -#endif - if (hal_rpc_server_init() != HAL_OK) Error_Handler(); @@ -258,5 +239,9 @@ int main() /* Start the non-blocking receive */ HAL_UART_Receive_IT(&huart_user, (uint8_t *)&uart_rx, 1); + /* Launch other threads: + * - csprng warm-up thread? + */ + return cli_main(); } -- cgit v1.2.3 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 +++++++++++++++++++++++++++++++++++++++++++ projects/hsm/cryptech_upload | 4 +- 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100755 projects/hsm/cryptech_probe (limited to 'projects/hsm') 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) diff --git a/projects/hsm/cryptech_upload b/projects/hsm/cryptech_upload index 722e37b..66fbe7d 100755 --- a/projects/hsm/cryptech_upload +++ b/projects/hsm/cryptech_upload @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # # Copyright (c) 2016, NORDUnet A/S All rights reserved. # @@ -27,9 +27,11 @@ # 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 upload new a firmware image or FPGA bitstream """ + import os import sys import time -- cgit v1.2.3 From 7edf6494434008d6c0330d413ccdc00eeb956cd9 Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Wed, 6 Jul 2016 14:48:50 -0400 Subject: Small improvements to try to suck less. --- projects/hsm/cryptech_upload | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'projects/hsm') diff --git a/projects/hsm/cryptech_upload b/projects/hsm/cryptech_upload index 66fbe7d..8bb6ebb 100755 --- a/projects/hsm/cryptech_upload +++ b/projects/hsm/cryptech_upload @@ -116,8 +116,8 @@ def _execute(dst, cmd): pin = getpass.getpass('SO PIN: ') _write(dst, pin + '\r') prompt = _read(dst) - if not prompt.endswith('> '): - #sys.stderr.write('Device does not seem to be ready for a file transfer (got {!r})\n'.format(prompt)) + if not prompt.endswith(('> ', '# ')): + print('Device does not seem to be ready for a file transfer (got {!r})'.format(prompt)) return prompt _write(dst, cmd + '\r') response = _read(dst) @@ -142,14 +142,18 @@ def send_file(filename, args, dst): print 'Access denied' return False if not 'OK' in response: - sys.stderr.write('Device did not accept the upload command (got {!r})\n'.format(response)) + print('Device did not accept the upload command (got {!r})'.format(response)) return False crc = 0 counter = 0 # 1. Write size of file (4 bytes) _write(dst, struct.pack(' Date: Wed, 6 Jul 2016 14:49:06 -0400 Subject: Add 'show fpga status' and 'show fpga cores'. --- projects/hsm/mgmt-fpga.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'projects/hsm') diff --git a/projects/hsm/mgmt-fpga.c b/projects/hsm/mgmt-fpga.c index b74392e..45bd33c 100644 --- a/projects/hsm/mgmt-fpga.c +++ b/projects/hsm/mgmt-fpga.c @@ -133,10 +133,37 @@ static int cmd_fpga_reset_registers(struct cli_def *cli, const char *command, ch return CLI_OK; } +static int cmd_fpga_show_status(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + cli_print(cli, "FPGA has %sloaded a bitstream", fpgacfg_check_done() ? "":"NOT "); + return CLI_OK; +} + +static int cmd_fpga_show_cores(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + const hal_core_t *core; + const hal_core_info_t *info; + + for (core = hal_core_iterate(NULL); core != NULL; core = hal_core_iterate(core)) { + info = hal_core_info(core); + cli_print(cli, "%04x: %8.8s %4.4s", + (unsigned int)info->base, info->name, info->version); + } + + return CLI_OK; +} + void configure_cli_fpga(struct cli_def *cli) { /* fpga */ cli_command_root(fpga); + + cli_command_branch(fpga, show); + /* show fpga status*/ + cli_command_node(fpga_show, status, "Show status about the FPGA"); + /* show fpga cores*/ + cli_command_node(fpga_show, cores, "Show FPGA core names and versions"); + /* fpga reset */ cli_command_node(fpga, reset, "Reset FPGA (config reset)"); /* fpga reset registers */ -- cgit v1.2.3 From 0bd9801146ae69f09d4ed980a0f4546da6a86150 Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Wed, 6 Jul 2016 14:49:25 -0400 Subject: Cleanup - remove demo filetransfer command. --- projects/hsm/mgmt-misc.c | 19 ------------------- 1 file changed, 19 deletions(-) (limited to 'projects/hsm') diff --git a/projects/hsm/mgmt-misc.c b/projects/hsm/mgmt-misc.c index 67bc875..15b2d13 100644 --- a/projects/hsm/mgmt-misc.c +++ b/projects/hsm/mgmt-misc.c @@ -44,13 +44,6 @@ extern uint32_t update_crc(uint32_t crc, uint8_t *buf, int len); -static volatile uint32_t demo_crc = 0; - -static int _count_bytes_callback(uint8_t *buf, size_t len) { - demo_crc = update_crc(demo_crc, buf, len); - return 1; -} - int cli_receive_data(struct cli_def *cli, uint8_t *buf, size_t len, cli_data_callback data_callback) { uint32_t filesize = 0, crc = 0, my_crc = 0, counter = 0; @@ -109,16 +102,6 @@ int cli_receive_data(struct cli_def *cli, uint8_t *buf, size_t len, cli_data_cal return CLI_OK; } -static int cmd_filetransfer(struct cli_def *cli, const char *command, char *argv[], int argc) -{ - uint8_t buf[FILETRANSFER_UPLOAD_CHUNK_SIZE]; - - demo_crc = 0; - cli_receive_data(cli, &buf[0], sizeof(buf), _count_bytes_callback); - cli_print(cli, "Demo CRC is: %li/0x%x", demo_crc, (unsigned int) demo_crc); - return CLI_OK; -} - static int cmd_reboot(struct cli_def *cli, const char *command, char *argv[], int argc) { cli_print(cli, "\n\n\nRebooting\n\n\n"); @@ -130,8 +113,6 @@ static int cmd_reboot(struct cli_def *cli, const char *command, char *argv[], in void configure_cli_misc(struct cli_def *cli) { - /* filetransfer */ - cli_command_root_node(filetransfer, "Test file transfering"); /* reboot */ cli_command_root_node(reboot, "Reboot the STM32"); } -- cgit v1.2.3 From adb5eef77e646b7d063bdacccf883bb9ce6b2ade Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Wed, 6 Jul 2016 15:10:14 -0400 Subject: Add uart_set_default() to make debugging output easier. --- projects/hsm/hsm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'projects/hsm') diff --git a/projects/hsm/hsm.c b/projects/hsm/hsm.c index 9cc6e9a..f4683b3 100644 --- a/projects/hsm/hsm.c +++ b/projects/hsm/hsm.c @@ -129,7 +129,7 @@ void HAL_UART2_RxCpltCallback(UART_HandleTypeDef *huart) hal_error_t hal_serial_send_char(uint8_t c) { - return (uart_send_char(c) == 0) ? HAL_OK : HAL_ERROR_RPC_TRANSPORT; + return (uart_send_char2(STM_UART_USER, c) == 0) ? HAL_OK : HAL_ERROR_RPC_TRANSPORT; } hal_error_t hal_serial_recv_char(uint8_t *cp) @@ -203,6 +203,7 @@ static uint8_t *sdram_malloc(size_t size) int main() { stm_init(); + uart_set_default(STM_UART_MGMT); led_on(LED_GREEN); /* Prepare FMC interface. */ -- 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') 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 61abfd73c36364a08901634a828c2e397e9c9010 Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Thu, 7 Jul 2016 15:48:30 -0400 Subject: Default to one RPC worker thread. --- projects/hsm/hsm.c | 103 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 43 deletions(-) (limited to 'projects/hsm') diff --git a/projects/hsm/hsm.c b/projects/hsm/hsm.c index f4683b3..e941abf 100644 --- a/projects/hsm/hsm.c +++ b/projects/hsm/hsm.c @@ -1,7 +1,7 @@ /* - * rpc_server.c - * ------------ - * Remote procedure call server-side private API implementation. + * hsm.c + * ---------------- + * Main module for the HSM project. * * Copyright (c) 2016, NORDUnet A/S All rights reserved. * @@ -33,20 +33,17 @@ */ /* - * This is the main RPC server moddule. It creates a new thread to deal - * with each request, to prevent a long-running request (e.g. RSA keygen) - * from blocking independent requests from other clients. This has a - * number of consequences. We can't do a blocking receive in the main - * thread, because that prevents the dispatch thread from transmitting the - * response (because they both want to lock the UART - see - * stm32f4xx_hal_uart.c). So we have to do a non-blocking receive with a - * callback routine. But we can't create a thread from the callback - * routine, because it's in the context of an ISR, so we raise a semaphore - * for the main thread to create the dispatch thread. + * This is the main RPC server module. At the moment, it has a single + * worker thread to handle RPC requests, while the main thread handles CLI + * activity. The design allows for multiple worker threads to handle + * concurrent RPC requests from multiple clients (muxed through a daemon + * on the host). */ #include +/* Rename both CMSIS HAL_OK and libhal HAL_OK to disambiguate */ +#define HAL_OK CMSIS_HAL_OK #include "cmsis_os.h" #include "stm-init.h" @@ -57,46 +54,44 @@ #include "mgmt-cli.h" -/* stm32f4xx_hal_def.h and hal.h both define HAL_OK as an enum value */ -#define HAL_OK HAL_OKAY - +#undef HAL_OK +#define HAL_OK LIBHAL_OK #include "hal.h" #include "hal_internal.h" #include "slip_internal.h" #include "xdr_internal.h" - -/* RPC buffers. For each active RPC, there will be two - input and output. - */ +#undef HAL_OK #ifndef NUM_RPC_TASK -/* An arbitrary number, but we don't expect to have more than 8 concurrent - * RPC requests. +/* Just one RPC task for now. More will require active resource management + * of at least the FPGA cores. */ -#define NUM_RPC_TASK 8 +#define NUM_RPC_TASK 1 #endif #ifndef TASK_STACK_SIZE /* Define an absurdly large task stack, because some pkey operation use a - * lot of stack variables. + * lot of stack variables. This has to go in SDRAM, because it exceeds the + * total RAM on the ARM. */ #define TASK_STACK_SIZE 200*1024 #endif #ifndef MAX_PKT_SIZE -/* Another arbitrary number, more or less driven by the 4096-bit RSA +/* An arbitrary number, more or less driven by the 4096-bit RSA * keygen test. */ #define MAX_PKT_SIZE 4096 #endif -/* The thread entry point takes a single void* argument, so we bundle the - * packet buffer and length arguments together. +/* RPC buffers. For each active RPC, there will be two - input and output. */ typedef struct { size_t len; uint8_t buf[MAX_PKT_SIZE]; } rpc_buffer_t; +#if NUM_RPC_TASK > 1 /* A mutex to arbitrate concurrent UART transmits, from RPC responses. */ osMutexId uart_mutex; @@ -106,6 +101,7 @@ osMutexDef(uart_mutex); */ osMutexId dispatch_mutex; osMutexDef(dispatch_mutex); +#endif /* Semaphore to inform the dispatch thread that there's a new RPC request. */ @@ -116,6 +112,12 @@ static volatile uint8_t uart_rx; /* current character received from UART */ static rpc_buffer_t * volatile rbuf; /* current RPC input buffer */ /* Callback for HAL_UART_Receive_IT(). + * With multiple worker threads, we can't do a blocking receive, because + * that prevents other threads from sending RPC responses (because they + * both want to lock the UART - see stm32f4xx_hal_uart.c). So we have to + * do a non-blocking receive with a callback routine. + * Even with only one worker thread, context-switching to the CLI thread + * causes HAL_UART_Receive to miss input. */ void HAL_UART2_RxCpltCallback(UART_HandleTypeDef *huart) { @@ -127,21 +129,21 @@ void HAL_UART2_RxCpltCallback(UART_HandleTypeDef *huart) HAL_UART_Receive_IT(huart, (uint8_t *)&uart_rx, 1); } -hal_error_t hal_serial_send_char(uint8_t c) -{ - return (uart_send_char2(STM_UART_USER, c) == 0) ? HAL_OK : HAL_ERROR_RPC_TRANSPORT; -} - hal_error_t hal_serial_recv_char(uint8_t *cp) { /* return the character from HAL_UART_Receive_IT */ *cp = uart_rx; - return HAL_OK; + return LIBHAL_OK; +} + +hal_error_t hal_serial_send_char(uint8_t c) +{ + return (uart_send_char2(STM_UART_USER, c) == 0) ? LIBHAL_OK : HAL_ERROR_RPC_TRANSPORT; } /* Thread entry point for the RPC request handler. */ -static void dispatch_thread(void const *args) +void dispatch_thread(void const *args) { rpc_buffer_t ibuf, obuf; @@ -150,26 +152,34 @@ static void dispatch_thread(void const *args) memset(&obuf, 0, sizeof(obuf)); obuf.len = sizeof(obuf.buf); +#if NUM_RPC_TASK > 1 /* Wait for access to the uart */ osMutexWait(dispatch_mutex, osWaitForever); +#endif /* Wait for the complete rpc request */ rbuf = &ibuf; osSemaphoreWait(rpc_sem, osWaitForever); +#if NUM_RPC_TASK > 1 /* Let the next thread handle the next request */ osMutexRelease(dispatch_mutex); /* Let the next thread take the mutex */ osThreadYield(); +#endif /* Process the request */ hal_rpc_server_dispatch(ibuf.buf, ibuf.len, obuf.buf, &obuf.len); /* Send the response */ +#if NUM_RPC_TASK > 1 osMutexWait(uart_mutex, osWaitForever); +#endif hal_error_t ret = hal_rpc_sendto(obuf.buf, obuf.len, NULL); +#if NUM_RPC_TASK > 1 osMutexRelease(uart_mutex); - if (ret != HAL_OK) +#endif + if (ret != LIBHAL_OK) Error_Handler(); } } @@ -217,14 +227,18 @@ int main() */ hal_core_iterate(NULL); - uart_mutex = osMutexCreate(osMutex(uart_mutex)); - dispatch_mutex = osMutexCreate(osMutex(dispatch_mutex)); - rpc_sem = osSemaphoreCreate(osSemaphore(rpc_sem), 0); +#if NUM_RPC_TASK > 1 + if ((uart_mutex = osMutexCreate(osMutex(uart_mutex))) == NULL || + (dispatch_mutex = osMutexCreate(osMutex(dispatch_mutex)) == NULL) + Error_Handler(); +#endif + if ((rpc_sem = osSemaphoreCreate(osSemaphore(rpc_sem), 0)) == NULL) + Error_Handler(); - if (hal_rpc_server_init() != HAL_OK) + if (hal_rpc_server_init() != LIBHAL_OK) Error_Handler(); - /* Create the rpc dispatch threads */ + /* Create the rpc dispatch worker threads. */ for (int i = 0; i < NUM_RPC_TASK; ++i) { osThreadDef_t *ot = &thread_def[i]; ot->pthread = dispatch_thread; @@ -237,11 +251,14 @@ int main() Error_Handler(); } - /* Start the non-blocking receive */ - HAL_UART_Receive_IT(&huart_user, (uint8_t *)&uart_rx, 1); + /* Start the non-blocking receive. Do this after launching the worker + * thread(s), because that's where rbuf is initialized. + */ + if (HAL_UART_Receive_IT(&huart_user, (uint8_t *)&uart_rx, 1) != CMSIS_HAL_OK) + Error_Handler(); - /* Launch other threads: - * - csprng warm-up thread? + /* Launch other threads (csprng warm-up thread?) + * Wait for FPGA_DONE interrupt. */ return cli_main(); -- cgit v1.2.3 From b3bce363f25d1a23dc7276bcb7fcbd11ccf591f2 Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Thu, 7 Jul 2016 20:09:51 -0400 Subject: Drop bad request packets. Also, if the UART receive callback fails to re-enable receive (because dispatch_thread is in the middle of transmitting a response), signal dispatch_thread to re-enable receive after it's done. --- projects/hsm/hsm.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) (limited to 'projects/hsm') diff --git a/projects/hsm/hsm.c b/projects/hsm/hsm.c index e941abf..3186bc5 100644 --- a/projects/hsm/hsm.c +++ b/projects/hsm/hsm.c @@ -110,6 +110,7 @@ osSemaphoreDef(rpc_sem); static volatile uint8_t uart_rx; /* current character received from UART */ static rpc_buffer_t * volatile rbuf; /* current RPC input buffer */ +static volatile int reenable_recv = 1; /* Callback for HAL_UART_Receive_IT(). * With multiple worker threads, we can't do a blocking receive, because @@ -122,11 +123,19 @@ static rpc_buffer_t * volatile rbuf; /* current RPC input buffer */ void HAL_UART2_RxCpltCallback(UART_HandleTypeDef *huart) { int complete; - hal_slip_recv_char(rbuf->buf, &rbuf->len, sizeof(rbuf->buf), &complete); + + if (hal_slip_recv_char(rbuf->buf, &rbuf->len, sizeof(rbuf->buf), &complete) != LIBHAL_OK) + Error_Handler(); + if (complete) - osSemaphoreRelease(rpc_sem); + if (osSemaphoreRelease(rpc_sem) != osOK) + Error_Handler(); - HAL_UART_Receive_IT(huart, (uint8_t *)&uart_rx, 1); + if (HAL_UART_Receive_IT(huart, (uint8_t *)&uart_rx, 1) != CMSIS_HAL_OK) + /* We may have collided with a transmit. + * Signal the dispatch_thread to try again. + */ + reenable_recv = 1; } hal_error_t hal_serial_recv_char(uint8_t *cp) @@ -157,6 +166,13 @@ void dispatch_thread(void const *args) osMutexWait(dispatch_mutex, osWaitForever); #endif + /* Start receiving, or re-enable after failing in the callback. */ + if (reenable_recv) { + if (HAL_UART_Receive_IT(&huart_user, (uint8_t *)&uart_rx, 1) != CMSIS_HAL_OK) + Error_Handler(); + reenable_recv = 0; + } + /* Wait for the complete rpc request */ rbuf = &ibuf; osSemaphoreWait(rpc_sem, osWaitForever); @@ -169,7 +185,12 @@ void dispatch_thread(void const *args) #endif /* Process the request */ - hal_rpc_server_dispatch(ibuf.buf, ibuf.len, obuf.buf, &obuf.len); + if (hal_rpc_server_dispatch(ibuf.buf, ibuf.len, obuf.buf, &obuf.len) != LIBHAL_OK) + /* If hal_rpc_server_dispatch failed with an XDR error, it + * probably means the request packet was garbage. In any case, we + * have nothing to transmit. + */ + continue; /* Send the response */ #if NUM_RPC_TASK > 1 @@ -251,12 +272,6 @@ int main() Error_Handler(); } - /* Start the non-blocking receive. Do this after launching the worker - * thread(s), because that's where rbuf is initialized. - */ - if (HAL_UART_Receive_IT(&huart_user, (uint8_t *)&uart_rx, 1) != CMSIS_HAL_OK) - Error_Handler(); - /* Launch other threads (csprng warm-up thread?) * Wait for FPGA_DONE interrupt. */ -- 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_miniterm | 45 ++++++++++++++++++++++++++++++++++++++++++ projects/hsm/cryptech_probe | 38 ++++++++++++++++++++++++----------- projects/hsm/cryptech_upload | 2 +- 3 files changed, 72 insertions(+), 13 deletions(-) create mode 100755 projects/hsm/cryptech_miniterm (limited to 'projects/hsm') diff --git a/projects/hsm/cryptech_miniterm b/projects/hsm/cryptech_miniterm new file mode 100755 index 0000000..b8ea3b1 --- /dev/null +++ b/projects/hsm/cryptech_miniterm @@ -0,0 +1,45 @@ +#!/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 run PySerial's "miniterm" with default settings suitable +for talking to the Cryptech Alpha's console port. +""" + +import serial.tools.miniterm +import sys +import os + +default_port = os.getenv("CRYPTECH_CTY_CLIENT_SERIAL_DEVICE") +default_baud = os.getenv("CRYPTECH_CTY_CLIENT_SERIAL_SPEED", 921600) + +sys.exit(serial.tools.miniterm.main(default_port = default_port, + default_baudrate = int(default_baud))) + 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)))) diff --git a/projects/hsm/cryptech_upload b/projects/hsm/cryptech_upload index 8bb6ebb..7590b38 100755 --- a/projects/hsm/cryptech_upload +++ b/projects/hsm/cryptech_upload @@ -57,7 +57,7 @@ def parse_args(): parser.add_argument('-d', '--device', dest='device', - default='/dev/ttyUSB0', + default=os.getenv('CRYPTECH_CTY_CLIENT_SERIAL_DEVICE', '/dev/ttyUSB0'), help='Name of management port USB serial device', ) -- cgit v1.2.3 From df9e82b28aad97664cba1fc238bc4f2563c1de7c Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Fri, 8 Jul 2016 14:26:34 -0400 Subject: Add upload from firmware tarball, gussie up command parser, add dire warnings. Command parser now enforces little things like mutually-exclusive required options so we warn users who attempt something silly. Preferred source for uploads is now the firmware tarball installed along with the client software; we still support uploading from an explictly-specified source file, but one must now say "-i file". Updating the bootloader is dangerous, we now say so and also require an additional option before we'll even attempt it. For the record, while testing this I did manage to brick my Alpha and had to use an ST-LINK to recover, exactly as predicted by the new dire warning. --- projects/hsm/cryptech_upload | 204 ++++++++++++++++++++++++++++--------------- 1 file changed, 136 insertions(+), 68 deletions(-) (limited to 'projects/hsm') diff --git a/projects/hsm/cryptech_upload b/projects/hsm/cryptech_upload index 7590b38..6d6d4f7 100755 --- a/projects/hsm/cryptech_upload +++ b/projects/hsm/cryptech_upload @@ -29,7 +29,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ -Utility to upload new a firmware image or FPGA bitstream +Utility to upload a new firmware image or FPGA bitstream. """ import os @@ -37,8 +37,11 @@ import sys import time import struct import serial -import argparse import getpass +import os.path +import tarfile +import argparse +import platform from binascii import crc32 @@ -50,35 +53,51 @@ def parse_args(): """ Parse the command line arguments """ - parser = argparse.ArgumentParser(description = "File uploader", - add_help = True, + + share_directory = "/usr/share" if platform.system() == "Linux" else "/usr/local/share" + + default_tarball = os.path.join(share_directory, "cryptech-alpha-firmware.tar.gz") + + if not os.path.exists(default_tarball): + default_tarball = None + + parser = argparse.ArgumentParser(description = __doc__, formatter_class = argparse.ArgumentDefaultsHelpFormatter, ) - parser.add_argument('-d', '--device', - dest='device', - default=os.getenv('CRYPTECH_CTY_CLIENT_SERIAL_DEVICE', '/dev/ttyUSB0'), - help='Name of management port USB serial device', + parser.add_argument("-d", "--device", + default = os.getenv("CRYPTECH_CTY_CLIENT_SERIAL_DEVICE", "/dev/ttyUSB0"), + help = "Name of management port USB serial device", ) - parser.add_argument('--fpga', - dest='fpga', - action='store_true', default=False, - help='Upload FPGA bitstream', - ) - parser.add_argument('--firmware', - dest='firmware', - action='store_true', default=False, - help='Upload firmware image', + parser.add_argument("--firmware-tarball", + type = argparse.FileType("rb"), + default = default_tarball, + help = "Location of firmware tarball", ) - parser.add_argument('--bootloader', - dest='bootloader', - action='store_true', default=False, - help='Upload bootloader image', + + actions = parser.add_mutually_exclusive_group(required = True) + actions.add_argument("--fpga", + action = "store_true", + help = "Upload FPGA bitstream", + ) + actions.add_argument("--firmware", "--hsm", + action = "store_true", + help = "Upload HSM firmware image", + ) + actions.add_argument("--bootloader", + action = "store_true", + help = "Upload bootloader image (dangerous!)", + ) + + parser.add_argument("--simon-says-whack-my-bootloader", + action = "store_true", + help = "Confirm that you really want to risk bricking the HSM", ) - # positional argument(s) - parser.add_argument('filename') + parser.add_argument("-i", "--explicit-image", + type = argparse.FileType("rb"), + help = "Explicit source image file for upload, overrides firmware tarball") return parser.parse_args() @@ -86,13 +105,13 @@ def parse_args(): def _write(dst, data): dst.write(data) #if len(data) == 4: - # print("Wrote 0x{!s}".format(data.encode('hex'))) + # print("Wrote 0x{!s}".format(data.encode("hex"))) #else: # print("Wrote {!r}".format(data)) def _read(dst): - res = '' + res = "" x = dst.read(1) while not x: x = dst.read(1) @@ -106,54 +125,51 @@ pin = None def _execute(dst, cmd): global pin - _write(dst, '\r') + _write(dst, "\r") prompt = _read(dst) - if prompt.endswith('Username: '): - _write(dst, 'so\r') + if prompt.endswith("Username: "): + _write(dst, "so\r") prompt = _read(dst) - if prompt.endswith('Password: '): + if prompt.endswith("Password: "): if not pin: - pin = getpass.getpass('SO PIN: ') - _write(dst, pin + '\r') + pin = getpass.getpass("SO PIN: ") + _write(dst, pin + "\r") prompt = _read(dst) - if not prompt.endswith(('> ', '# ')): - print('Device does not seem to be ready for a file transfer (got {!r})'.format(prompt)) + if not prompt.endswith(("> ", "# ")): + print("Device does not seem to be ready for a file transfer (got {!r})".format(prompt)) return prompt - _write(dst, cmd + '\r') + _write(dst, cmd + "\r") response = _read(dst) return response -def send_file(filename, args, dst): - s = os.stat(filename) - size = s.st_size - src = open(filename, 'rb') +def send_file(src, size, args, dst): if args.fpga: chunk_size = FPGA_CHUNK_SIZE - response = _execute(dst, 'fpga bitstream upload') + response = _execute(dst, "fpga bitstream upload") elif args.firmware: chunk_size = FIRMWARE_CHUNK_SIZE - response = _execute(dst, 'firmware upload') - if 'Rebooting' in response: - response = _execute(dst, 'firmware upload') + response = _execute(dst, "firmware upload") + if "Rebooting" in response: + response = _execute(dst, "firmware upload") elif args.bootloader: chunk_size = FIRMWARE_CHUNK_SIZE - response = _execute(dst, 'bootloader upload') - if 'Access denied' in response: - print 'Access denied' + response = _execute(dst, "bootloader upload") + if "Access denied" in response: + print "Access denied" return False - if not 'OK' in response: - print('Device did not accept the upload command (got {!r})'.format(response)) + if not "OK" in response: + print("Device did not accept the upload command (got {!r})".format(response)) return False crc = 0 counter = 0 # 1. Write size of file (4 bytes) - _write(dst, struct.pack('