# Copyright (c) 2015-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. # Number of static hash and HMAC state blocks to allocate. # Numbers pulled out of a hat, just testing. STATIC_HASH_STATE_BLOCKS = 10 STATIC_HMAC_STATE_BLOCKS = 4 STATIC_PKEY_STATE_BLOCKS = 6 INC = hal.h hal_internal.h LIB = libhal.a OBJ = errorstrings.o rsa.o ecdsa.o asn1.o ${CORE_OBJ} ${IO_OBJ} ${RPC_OBJ} ${KS_OBJ} CORE_OBJ := core.o csprng.o hash.o aes_keywrap.o pbkdf2.o modexp.o USAGE = "usage: make [IO_BUS=eim|i2c|fmc] [RPC_CLIENT=local|remote|mixed] [RPC_SERVER=yes] [KS=mmap|volatile|flash]" # I/O bus to the FPGA # # IO_BUS = eim | i2c | fmc # eim: EIM bus from Novena # i2c: older I2C bus from Novena # fmc: FMC bus from dev-bridge board IO_BUS ?= eim ifeq (${IO_BUS},eim) IO_OBJ = hal_io_eim.o novena-eim.o else ifeq (${IO_BUS},i2c) IO_OBJ = hal_io_i2c.o else ifeq (${IO_BUS},fmc) IO_OBJ = hal_io_fmc.o endif # If we're building for STM32, position-independent code leads to some # hard-to-debug function pointer errors. OTOH, if we're building for Linux # (even on the Novena), we want to make it possible to build a shared library. ifneq (${IO_BUS},fmc) CFLAGS += -fPIC endif # RPC_CLIENT = local | remote | mixed # local: Build for Novena or dev-bridge, access FPGA cores directly. # remote: Build for other host, communicate with RPC server. # mixed: Do hashing locally in software, other functions remotely. # # RPC_SERVER = yes # # RPC_TRANSPORT = loopback | serial # loopback: communicate over loopback socket on Novena # serial: communicate over USB in serial pass-through mode RPC_CORE_OBJ = rpc_hash.o rpc_misc.o rpc_pkey.o ifdef RPC_SERVER RPC_SERVER_OBJ = rpc_server.o rpc_api.o ${RPC_CORE_OBJ} RPC_TRANSPORT ?= serial endif ifdef RPC_CLIENT RPC_CLIENT_OBJ = rpc_client.o rpc_api.o ifeq (${RPC_CLIENT},local) RPC_CLIENT_OBJ += ${RPC_CORE_OBJ} else CFLAGS += -DHAL_RSA_USE_MODEXP=0 RPC_TRANSPORT ?= serial ifeq (${RPC_CLIENT},mixed) RPC_CLIENT_OBJ += rpc_hash.o hash.o endif ifndef RPC_SERVER # If we're only building a remote RPC client lib, don't include # the modules that access the FPGA cores. CORE_OBJ := IO_OBJ := endif endif endif ifdef RPC_TRANSPORT RPC_TRANSPORT_OBJ = xdr.o ifeq (${RPC_TRANSPORT},loopback) ifdef RPC_SERVER RPC_TRANSPORT_OBJ += rpc_server_loopback.o endif ifdef RPC_CLIENT RPC_TRANSPORT_OBJ += rpc_client_loopback.o endif else ifeq (${RPC_TRANSPORT},serial) RPC_TRANSPORT_OBJ += slip.o ifdef RPC_SERVER RPC_TRANSPORT_OBJ += rpc_server_serial.o endif ifdef RPC_CLIENT RPC_TRANSPORT_OBJ += rpc_client_serial.o endif endif endif RPC_OBJ = ${RPC_SERVER_OBJ} ${RPC_CLIENT_OBJ} ${RPC_TRANSPORT_OBJ} # RPC client locality, for rpc_client.c. This has to be kept in sync with # hal_internal.h. Yeah, it's ugly, but the C preprocessor can only # compare integers, not strings. ifeq (${RPC_CLIENT},local) RPC_CLIENT_FLAG = 0 else ifeq (${RPC_CLIENT},remote) RPC_CLIENT_FLAG = 1 else ifeq (${RPC_CLIENT},mixed) RPC_CLIENT_FLAG = 2 endif ifdef RPC_CLIENT_FLAG CFLAGS += -DRPC_CLIENT=${RPC_CLIENT_FLAG} endif # The mmap and flash keystore implementations are both server code. # # The volatile keystore (conventional memory) is client code, to # support using the same API for things like PKCS #11 "session" objects. # # Default at the moment is mmap, since that should work on the Novena # and we haven't yet written the flash code for the bridge board. KS_OBJ = ks.o KS ?= mmap ifeq (${KS},mmap) KS_OBJ += ks_mmap.o else ifeq (${KS},volatile) KS_OBJ += ks_volatile.o else ifeq (${KS},flash) KS_OBJ += ks_flash.o endif TFMDIR := $(abspath ../thirdparty/libtfm) CFLAGS += -g3 -Wall -std=c99 -I${TFMDIR} LDFLAGS := -g3 -L${TFMDIR} -ltfm CFLAGS += -DHAL_STATIC_HASH_STATE_BLOCKS=${STATIC_HASH_STATE_BLOCKS} CFLAGS += -DHAL_STATIC_HMAC_STATE_BLOCKS=${STATIC_HMAC_STATE_BLOCKS} CFLAGS += -DHAL_STATIC_PKEY_STATE_BLOCKS=${STATIC_PKEY_STATE_BLOCKS} all: ${LIB} cd tests; ${MAKE} CFLAGS='${CFLAGS} -I..' LDFLAGS='${LDFLAGS}' $@ ifneq (${CORE_OBJ},) cd utils; ${MAKE} CFLAGS='${CFLAGS} -I..' LDFLAGS='${LDFLAGS}' $@ endif client: ${MAKE} RPC_CLIENT=remote mixed: ${MAKE} RPC_CLIENT=mixed server: ${MAKE} RPC_SERVER=yes loopback: ${MAKE} RPC_CLIENT=remote RPC_SERVER=yes RPC_TRANSPORT=loopback ${OBJ}: ${INC} ${LIB}: ${OBJ} ${AR} rcs $@ $^ asn1.o rsa.o ecdsa.o: asn1_internal.h ecdsa.o: ecdsa_curves.h novena-eim.o hal_io_eim.o: novena-eim.h slip.o rpc_client_serial.o rpc_server_serial.o: slip_internal.h test: all export RPC_CLIENT RPC_SERVER cd tests; ${MAKE} -k $@ clean: rm -f *.o ${LIB} cd tests; ${MAKE} $@ cd utils; ${MAKE} $@ distclean: clean rm -f TAGS tags: TAGS TAGS: *.[ch] tests/*.[ch] utils/*.[ch] etags $^ '>
f948d67
#!/usr/bin/env python3
#
# 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 transfer a new firmware to the bootloader.
Example usage:
./bin/dfu path/to/firmware.bin
Then reset the Cryptech Alpha board.
"""
import os
import sys
import time
import struct
import serial
import argparse
from binascii import crc32, hexlify
CHUNK_SIZE = 4096
def parse_args():
"""
Parse the command line arguments
"""
parser = argparse.ArgumentParser(description = "Device firmware upgrader",
add_help = True,
formatter_class = argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument('--verbose',
dest='verbose',
action='store_true', default=False,
help='Verbose operation',
)
parser.add_argument('--device',
dest='device',
default='/dev/ttyUSB0',
help='Name of management port USB serial device',
)
# positional argument(s)
parser.add_argument('filename')
return parser.parse_args()
def _write(dst, data):
for i in range(len(data)):
dst.write(data[i:i+1])
time.sleep(0.1)
if len(data) == 4:
print("Wrote 0x{}".format(hexlify(data).decode("ascii")))
else:
print("Wrote {!r}".format(data))
def _read(dst, verbose=False):
res = b''
while True:
x = dst.read(1)
if not x:
break
res += x
if res and verbose:
print("Read {!r}".format(res))
return res
def send_file(filename, args):
s = os.stat(filename)
size = s.st_size
src = open(filename, 'rb')
chunk_size = CHUNK_SIZE
print("Waiting for contact with the bootloader on device {!s}, reset the CrypTech Alpha...".format(args.device))
while True:
try:
dst = serial.Serial(args.device, 921600, timeout=0.1)
except serial.SerialException:
time.sleep(0.2)
continue
dst.write(b'\r')
response = _read(dst, args.verbose)
if 'OK' in response:
dst.timeout=2
break
print('\nUploading firmware\n')
crc = 0
counter = 0
# 1. Write size of file (4 bytes)
_write(dst, struct.pack('<I', size))
_read(dst, args.verbose)
# 2. Write file contents while calculating CRC-32
while True:
data = src.read(chunk_size)
if not data:
break
dst.write(data)
print("Wrote {!s} bytes (chunk {!s}/{!s})".format(len(data), counter, int(size / chunk_size)))
# read ACK (a counter of number of 4k chunks received)
while True:
ack_bytes = dst.read(4)
if len(ack_bytes) == 4:
break
print('ERROR: Did not receive an ACK, got {!r}'.format(ack_bytes))
dst.write(b'\r') # eventually get back to the CLI prompt
ack = struct.unpack('<I', ack_bytes)[0]
if ack != counter + 1:
print('ERROR: Did not receive the expected counter as ACK (got {!r}/{!r}, not {!r})'.format(ack, ack_bytes, counter))
flush = dst.read(100)
print('FLUSH data: {!r}'.format(flush))
return False
counter += 1
crc = crc32(data, crc) & 0xffffffff
_read(dst, args.verbose)
# 3. Write CRC-32 (4 bytes)
_write(dst, struct.pack('<I', crc))
if args.verbose:
print('\nFinished - this command might hang now depending on the firmware loaded')
_read(dst, args.verbose)
else:
print('\nFinished uploading firmware')
src.close()
dst.close()
return True
def main(args):
send_file(args.filename, args)
return True
if __name__ == '__main__':
try:
args = parse_args()
if main(args):
sys.exit(0)
sys.exit(1)
except KeyboardInterrupt:
pass