#!/usr/bin/python3 ''' * keygen.py * * Copyright (C) 2021 wolfSSL Inc. * * This file is part of wolfBoot. * * wolfBoot is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * wolfBoot is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA ''' import sys,os,struct from wolfcrypt import ciphers AUTH_KEY_ED25519 = 0x01 AUTH_KEY_ECC256 = 0x02 AUTH_KEY_RSA2048 = 0x03 AUTH_KEY_RSA4096 = 0x04 AUTH_KEY_ED448 = 0x05 AUTH_KEY_ECC384 = 0x06 AUTH_KEY_ECC521 = 0x07 AUTH_KEY_RSA3072 = 0x08 #default sign algorithm value sign="ed25519" def usage(): print("Usage: %s [--ed25519 | --ed448 | --ecc256 | --ecc384 | --ecc521 | --rsa2048| --rsa3072 | --rsa4096] [ --force ] [-i pubkey0.der [-i pubkey1.der -i pubkey2.der ... -i pubkeyN.der]] [-i pubkey0.der [-i pubkey1.der -i pubkey2.der ... -i pubkeyN.der] [-keystoreDir dir]]n" % sys.argv[0]) parser.print_help() sys.exit(1) def dupsign(): print("") print("Error: only one algorithm must be specified.") print("") usage() def sign_key_type(name): if name == 'ed25519': return 'AUTH_KEY_ED25519' elif name == 'ed448': return 'AUTH_KEY_ED448' elif name == 'ecc256': return 'AUTH_KEY_ECC256' elif name == 'ecc384': return 'AUTH_KEY_ECC384' elif name == 'ecc521': return 'AUTH_KEY_ECC521' elif name == 'rsa2048': return 'AUTH_KEY_RSA2048' elif name == 'rsa3072': return 'AUTH_KEY_RSA3072' elif name == 'rsa4096': return 'AUTH_KEY_RSA4096' else: return 0 def sign_key_size(name): if name == 'ed25519': return 'KEYSTORE_PUBKEY_SIZE_ED25519' elif name == 'ed448': return 'KEYSTORE_PUBKEY_SIZE_ED448' elif name == 'ecc256': return 'KEYSTORE_PUBKEY_SIZE_ECC256' elif name == 'ecc384': return 'KEYSTORE_PUBKEY_SIZE_ECC384' elif name == 'ecc521': return 'KEYSTORE_PUBKEY_SIZE_ECC521' elif name == 'rsa2048': return 'KEYSTORE_PUBKEY_SIZE_RSA2048' elif name == 'rsa3072': return 'KEYSTORE_PUBKEY_SIZE_RSA3072' elif name == 'rsa4096': return 'KEYSTORE_PUBKEY_SIZE_RSA4096' else: return 0 def sign_key_size_literal(name): if name == 'ed25519': return 32 elif name == 'ed448': return 57 elif name == 'ecc256': return 64 elif name == 'ecc384': return 96 elif name == 'ecc521': return 132 elif name == 'rsa2048': return 320 elif name == 'rsa3072': return 448 elif name == 'rsa4096': return 576 else: return 0 def keystore_add(slot, pub, sz = 0): ktype = sign_key_type(sign) if (sz == 0): ksize = sign_key_size(sign) else: ksize = str(sz) pfile.write(Slot_hdr % (key_file, slot, ktype, ksize)) i = 0 for c in bytes(pub[0:-1]): pfile.write("0x%02X, " % c) i += 1 if (i % 8 == 0): pfile.write('\n\t\t\t') pfile.write("0x%02X" % pub[-1]) pfile.write(Pubkey_footer) pfile.write(Slot_footer) t = 0x8A8A8A8A m = 0xFFFFFFFF ks_struct = struct.pack("\n#include \"wolfboot/wolfboot.h\"\n#include \"keystore.h\"\n" \ "#ifdef WOLFBOOT_NO_SIGN\n\t#define NUM_PUBKEYS 0\n#else\n\n" \ "#if !defined(KEYSTORE_ANY) && (KEYSTORE_PUBKEY_SIZE != KEYSTORE_PUBKEY_SIZE_%s)\n\t" \ "#error Key algorithm mismatch. Remove old keys via 'make keysclean'\n" \ "#else\n" Store_hdr = "#define NUM_PUBKEYS %d\nconst struct keystore_slot PubKeys[NUM_PUBKEYS] = {\n\n" Slot_hdr = "\t /* Key associated to file '%s' */\n" Slot_hdr += "\t{\n\t\t.slot_id = %d,\n\t\t.key_type = %s,\n" Slot_hdr += "\t\t.part_id_mask = KEY_VERIFY_ALL,\n\t\t.pubkey_size = %s,\n" Slot_hdr += "\t\t.pubkey = {\n\t\t\t" Pubkey_footer = "\n\t\t}," Slot_footer = "\n\t},\n\n" Store_footer = '\n};\n\n' Keystore_API = "int keystore_num_pubkeys(void)\n" Keystore_API += "{\n" Keystore_API += " return NUM_PUBKEYS;\n" Keystore_API += "}\n\n" Keystore_API += "uint8_t *keystore_get_buffer(int id)\n" Keystore_API += "{\n" Keystore_API += " if (id >= keystore_num_pubkeys())\n" Keystore_API += " return (uint8_t *)0;\n" Keystore_API += " return (uint8_t *)PubKeys[id].pubkey;\n" Keystore_API += "}\n\n" Keystore_API += "int keystore_get_size(int id)\n" Keystore_API += "{\n" Keystore_API += " if (id >= keystore_num_pubkeys())\n" Keystore_API += " return -1;\n" Keystore_API += " return (int)PubKeys[id].pubkey_size;\n" Keystore_API += "}\n\n" Keystore_API += "uint32_t keystore_get_mask(int id)\n" Keystore_API += "{\n" Keystore_API += " if (id >= keystore_num_pubkeys())\n" Keystore_API += " return -1;\n" Keystore_API += " return (int)PubKeys[id].part_id_mask;\n" Keystore_API += "}\n\n" Keystore_API += "#endif /* Keystore public key size check */\n" Keystore_API += "#endif /* WOLFBOOT_NO_SIGN */\n" import argparse as ap parser = ap.ArgumentParser(prog='keygen.py', description='wolfBoot key generation tool') parser.add_argument('--ed25519', dest='ed25519', action='store_true') parser.add_argument('--ed448', dest='ed448', action='store_true') parser.add_argument('--ecc256', dest='ecc256', action='store_true') parser.add_argument('--ecc384', dest='ecc384', action='store_true') parser.add_argument('--ecc521', dest='ecc521', action='store_true') parser.add_argument('--rsa2048', dest='rsa2048', action='store_true') parser.add_argument('--rsa3072', dest='rsa3072', action='store_true') parser.add_argument('--rsa4096', dest='rsa4096', action='store_true') parser.add_argument('--force', dest='force', action='store_true') parser.add_argument('-i', dest='pubfile', nargs='+', action='extend') parser.add_argument('-g', dest='keyfile', nargs='+', action='extend') parser.add_argument('-keystoreDir', dest='storeDir', nargs='+', action='extend') print(" *** WARNING ***") print("Python key tools are now deprecated") print("and will be removed in future versions.") print("Please ensure that your scripts are using") print("the compiled C version of these tools") print("(e.g. by running 'make keytools').") print(" *** ******* ***") print("") args=parser.parse_args() if (type(args.storeDir) == list): pubkey_cfile = "".join(args.storeDir)+"/keystore.c" keystore_imgfile = "".join(args.storeDir)+"/keystore.der" else: pubkey_cfile = "src/keystore.c" keystore_imgfile = "keystore.der" key_files = args.keyfile pubkey_files = args.pubfile if pubkey_files == None: pubkey_files = [] if key_files == None: key_files = [] print("keys to import:") print(pubkey_files) print("keys to generate:") print(key_files) sign=None force=False if (args.ed25519): sign='ed25519' if (args.ed448): if sign is not None: dupsign() sign='ed448' if (args.ecc256): if sign is not None: dupsign() sign='ecc256' if (args.ecc384): if sign is not None: dupsign() sign='ecc384' if (args.ecc521): if sign is not None: dupsign() sign='ecc521' print("ecc521 keys are not yet supported!") sys.exit(1) if (args.rsa2048): if sign is not None: dupsign() sign='rsa2048' if (args.rsa3072): if sign is not None: dupsign() sign='rsa3072' if (args.rsa4096): if sign is not None: dupsign() sign='rsa4096' if sign is None: usage() force = args.force if pubkey_cfile[-2:] != '.c': print("** Warning: generated public key cfile does not have a '.c' extension") # Create/open public key c file print ("Output C file: " + pubkey_cfile) pfile = open(pubkey_cfile, "w") pfile.write(Cfile_Banner % sign.upper()) pfile.write(Store_hdr % (len(key_files) + len(pubkey_files))) ksfile = open(keystore_imgfile, "wb") pub_slot_index = 0 if pubkey_files != None: for pub_slot_index, key_file in enumerate(pubkey_files): print ("Public key slot: " + str(pub_slot_index)) print ("Selected cipher: " + sign) print ("Input public key: " + key_file) with open(key_file, 'rb') as f: key = f.read(4096) # if it's an ecc key and it's length is longer than the raw key we # need to parse it if (sign == 'ecc256' or sign == 'ecc384' or sign == 'ecc521') and len(key) > sign_key_size_literal(sign): eccKey = ciphers.EccPublic(key) key = eccKey.encode_key_raw() key = key[0] + key[1] keystore_add(pub_slot_index, key) pub_slot_index = len(pubkey_files) for slot_index_off, key_file in enumerate(key_files): slot_index = slot_index_off + pub_slot_index print ("Public key slot: " + str(slot_index)) print ("Selected cipher: " + sign) print ("Output Private key: " + key_file) print() if os.path.exists(key_file) and not force: choice = input("** Warning: key file already exist! Are you sure you want to "+ "generate a new key and overwrite the existing key? [Type 'Yes']: ") if (choice != "Yes"): print("Operation canceled.") sys.exit(2) if (sign == "ed25519"): ed = ciphers.Ed25519Private.make_key(32) priv,pub = ed.encode_key() print() print("Creating file " + key_file) with open(key_file, "wb") as f: f.write(priv) f.write(pub) f.close() keystore_add(slot_index, pub) if (sign == "ed448"): ed = ciphers.Ed448Private.make_key(57) priv,pub = ed.encode_key() print() print("Creating file " + key_file) with open(key_file, "wb") as f: f.write(priv) f.write(pub) f.close() keystore_add(slot_index, pub) if (sign[0:3] == 'ecc'): if (sign == "ecc256"): ec = ciphers.EccPrivate.make_key(32) ecc_pub_key_len = 64 qx,qy,d = ec.encode_key_raw() if (sign == "ecc384"): ec = ciphers.EccPrivate.make_key(48) ecc_pub_key_len = 96 qx,qy,d = ec.encode_key_raw() if (sign == "ecc521"): ec = ciphers.EccPrivate.make_key(66) ecc_pub_key_len = 132 qx,qy,d = ec.encode_key_raw() print() print("Creating file " + key_file) keystore_add(slot_index, bytes(qx) + bytes(qy)) with open(key_file, "wb") as f: f.write(qx) f.write(qy) f.write(d) f.close() if (sign == "rsa2048"): rsa = ciphers.RsaPrivate.make_key(2048) priv,pub = rsa.encode_key() print() print("Creating file " + key_file) with open(key_file, "wb") as f: f.write(priv) f.close() print("Creating file " + pubkey_cfile) keystore_add(slot_index, pub, len(pub)) if (sign == "rsa3072"): rsa = ciphers.RsaPrivate.make_key(3072) priv,pub = rsa.encode_key() print() print("Creating file " + key_file) with open(key_file, "wb") as f: f.write(priv) f.close() keystore_add(slot_index, pub, len(pub)) if (sign == "rsa4096"): rsa = ciphers.RsaPrivate.make_key(4096) if os.path.exists(key_file) and not force: choice = input("** Warning: key file already exist! Are you sure you want to "+ "generate a new key and overwrite the existing key? [Type 'Yes']: ") if (choice != "Yes"): print("Operation canceled.") sys.exit(2) priv,pub = rsa.encode_key() print() print("Creating file " + key_file) with open(key_file, "wb") as f: f.write(priv) f.close() keystore_add(slot_index, pub, len(pub)) pfile.write(Store_footer) pfile.write(Keystore_API) pfile.close()