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(-) 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('