326 lines
16 KiB
Python
Raw Normal View History

2025-10-10 16:07:00 +08:00
#!/usr/bin/env python3
import logging
import sys
import shutil
from argparse import _MutuallyExclusiveGroup
from genericpath import exists
import click
from click_option_group import optgroup,RequiredMutuallyExclusiveOptionGroup
import json
import struct
from scripts import partition
from scripts.partition import *
from scripts.gen_partition import *
from scripts.gen_ppc import *
from scripts.gen_mpc import *
from scripts.gen_security import *
from scripts.gen_ota import *
from scripts.gen_code import gen_code
from scripts.bl1_sign import *
from scripts.bl2_sign import *
from scripts.pk_hash import *
from scripts.pack import *
from scripts.compress import *
from scripts import crc
from scripts import encrypt
from scripts import xts_aes
from scripts import generator
from scripts.sign import *
from scripts.steps import *
from scripts.solution_generator import *
from scripts.copy_json_data_csv import *
def set_debug(debug):
if debug:
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
stream_handler = logging.StreamHandler(sys.stdout)
stream_handler.setLevel(logging.DEBUG)
@click.group()
@click.version_option(version='1.0.0.0')
def cli():
"""Beken security tools for generating code, signing, encrypting and packing"""
pass
@cli.group("gen")
def gen():
"""Generate code from security config files"""
@cli.group("sign")
def sign():
"""BL1/BL2 signing commands"""
@cli.group("pack")
def pack():
"""Merge partitions, CRC/AES and packing"""
@cli.group("steps")
def steps():
"""Create downloadable bin step by step"""
@cli.group("pipeline")
def pipeline():
"""security solutions pipeline"""
@gen.command("partition")
@click.option("--partition_csv", type=click.Path(exists=True, dir_okay=False), required=False, default='partitions.csv', help="partition CSV file.")
@click.option("--ota_type", type=click.Choice(['OVERWRITE', 'XIP']), default='OVERWRITE', required=True, help="The OTA type.")
@click.option("--out_hdr_file", type=str, required=False, default='partition_gen.h', help="Output file")
@click.option("--out_layout_file", type=str, required=False, default='partition_layout.h', help="Output file")
@click.option("--debug", is_flag=True, help="Enable debug")
def gen_partition_command(partition_csv, ota_type, out_hdr_file, out_layout_file, debug):
"""gen partition header and layout file."""
set_debug(debug)
p = Partitions(partition_csv, ota_type)
gen_partitions_hdr_file(p, out_hdr_file)
gen_partitions_layout_file(p, out_layout_file)
@gen.command("ppc")
@click.option("--ppc_csv", type=click.Path(exists=True, dir_okay=False), required=False, default='ppc.csv', help="PPC CSV file.")
@click.option("--gpio_dev_csv", type=click.Path(exists=True, dir_okay=False), required=False, default='gpio_dev.csv', help="GPIO map control config csv file.")
@click.option("--outfile", type=str, required=False, default='_ppc.h', help="Output file")
@click.option("--debug", is_flag=True, help="Enable debug")
def gen_ppc_command(ppc_csv, gpio_dev_csv, outfile, debug):
"""gen ppc.h from ppc.csv and gpio_dev.csv."""
set_debug(debug)
gen_ppc_config_file(ppc_csv, gpio_dev_csv, outfile)
@gen.command("mpc")
@click.option("--mpc_csv", type=click.Path(exists=True, dir_okay=False), required=False, default='mpc.csv', help="MPC CSV file.")
@click.option("--outfile", type=str, required=False, default='_mpc.h', help="Output file")
@click.option("--debug", is_flag=True, help="Enable debug")
def gen_mpc_command(mpc_csv, outfile, debug):
"""gen mpc.h from mpc.csv."""
set_debug(debug)
gen_mpc_config_file(mpc_csv, outfile)
@gen.command("security")
@click.option("--security_csv", type=click.Path(exists=True, dir_okay=False), required=False, default='security.csv', help="Security CSV file.")
@click.option("--outfile", type=str, required=False, default='security.h', help="Output file")
@click.option("--debug", is_flag=True, help="Enable debug")
def gen_security_command(security_csv, outfile, debug):
"""gen security.h from security.csv."""
set_debug(debug)
gen_security_config_file(security_csv, outfile)
@gen.command("ota")
@click.option("--ota_csv", type=click.Path(exists=True, dir_okay=False), required=False, default='ota.csv', help="OTA CSV file.")
@click.option("--outfile", type=str, required=False, default='_ota.h', help="Output file")
@click.option("--debug", is_flag=True, help="Enable debug")
def gen_ota_command(ota_csv, outfile, debug):
"""gen ota.h from ota.csv."""
set_debug(debug)
gen_ota_config_file(ota_csv, outfile)
@gen.command("otp")
@click.option("--otp_csv", type=click.Path(exists=True, dir_okay=False), required=False, default='otp2.csv', help="OTP CSV file.")
@click.option("--outfile", type=str, required=False, default='_otp.h', help="Output file")
@click.option("--debug", is_flag=True, help="Enable debug")
def gen_ota_command(otp_csv, outfile, debug):
"""gen otp.h from otp.csv."""
set_debug(debug)
gen_otp_map_file()
@gen.command("otp_efuse")
@click.option("--flash_aes_type", type=click.Choice(['FIXED', 'RANDOM', 'NONE']), default='FIXED', required=True, help="Flash AES type.")
@click.option("--flash_aes_key", type=str, required=False, default=None, help="flash AES key.")
@click.option("--pubkey_pem_file", type=click.Path(exists=True, dir_okay=False), required=False, default='root_ec256_pubkey.pem', help="PEM secure boot public key file.")
@click.option("--secure_boot", is_flag=True, help="Enable secure boot")
@click.option("--outfile", type=str, required=False, default='otp_efuse_config.json', help="Output file")
@click.option("--debug", is_flag=True, help="Enable debug")
def gen_otp_efuse_command(flash_aes_type, flash_aes_key, pubkey_pem_file, secure_boot, outfile, debug):
"""gen otp_efuse_config.json from security csv files."""
set_debug(debug)
gen_otp_efuse_config_file(flash_aes_type, flash_aes_key, pubkey_pem_file, secure_boot, outfile)
@gen.command("all")
@click.option("--debug", is_flag=True, help="Enable debug")
def pack_command(debug):
"""generate all code from security csv files"""
set_debug(debug)
gen_code()
@pack.command("compress")
@click.option("--infile", type=click.Path(exists=True, dir_okay=False), required=False, default='primary_all_code_signed.bin', help="Binary to be compressed.")
@click.option("--outfile", type=str, required=False, default='otp_efuse_config.json', help="Output file")
@click.option("--debug", is_flag=True, help="Enable debug")
def compress_command(infile, outfile, debug):
"""compress OTA binary (overwrite only)"""
set_debug(debug)
compress_bin(infile, outfile)
@pack.command("extract_pk_hash")
@click.option("--bin", type=click.Path(exists=True, dir_okay=False), required=True, default='bootloader.bin', help="Binaries that contains public key hash.")
@click.option("--outfile", type=str, required=False, default='pk_hash.json', help="Output public key hash json")
@click.option("--debug", is_flag=True, help="Enable debug")
def pk_hash_command(bin, outfile, debug):
"""extract public key hash from binary"""
set_debug(debug)
extract_pk_hash(bin, outfile)
@pack.command("insert_pk_hash")
@click.option("--bin", type=click.Path(exists=True, dir_okay=False), required=True, default='bootloader.bin', help="Binaries that contains public key hash.")
@click.option("--pubkey_pem_file", type=click.Path(exists=True, dir_okay=False), required=False, default='root_ec256_pubkey.pem', help="PEM public key file.")
@click.option("--debug", is_flag=True, help="Enable debug")
def pk_hash_command(bin, pubkey_pem_file, debug):
"""insert public key hash from binary"""
set_debug(debug)
insert_pk_hash(bin, pubkey_pem_file)
@pack.command("get_pk_hash")
@click.option("--pubkey_pem_file", type=click.Path(exists=True, dir_okay=False), required=False, default='root_ec256_pubkey.pem', help="PEM public key file.")
@click.option("--debug", is_flag=True, help="Enable debug")
def pk_hash_command(pubkey_pem_file, debug):
"""insert public key hash from binary"""
set_debug(debug)
get_pk_hash(pubkey_pem_file)
@steps.command("get_app_bin_hash")
@click.option("--debug", is_flag=True, help="Enable debug")
def get_bin_hash_command(debug):
"""Get binaries hash for signing"""
set_debug(debug)
get_app_bin_hash()
@steps.command("sign_app_bin_hash")
@click.option("--debug", is_flag=True, help="Enable debug")
def sign_hash_command(debug):
"""Signing app binaries hash"""
set_debug(debug)
sign_app_bin_hash()
@steps.command("sign_from_app_sig")
@click.option("--debug", is_flag=True, help="Enable debug")
def sign_from_sig_command(debug):
"""Create signed app bin based on signature"""
set_debug(debug)
sign_from_app_sig()
@steps.command("get_ota_bin_hash")
@click.option("--debug", is_flag=True, help="Enable debug")
def get_ota_bin_hash_command(debug):
"""Get OTA binary hash"""
set_debug(debug)
get_ota_bin_hash()
@steps.command("sign_ota_bin_hash")
@click.option("--debug", is_flag=True, help="Enable debug")
def sign_ota_bin_hash_command(debug):
"""Create signature of OTA bin hash"""
set_debug(debug)
sign_ota_bin_hash()
@steps.command("sign_from_ota_sig")
@click.option("--debug", is_flag=True, help="Enable debug")
def sign_from_ota_sig_command(debug):
"""Create signed OTA bin from signature"""
set_debug(debug)
sign_from_ota_sig()
@steps.command("pack")
@click.option("--debug", is_flag=True, help="Enable debug")
def pack_command(debug):
"""Pack download bin"""
set_debug(debug)
steps_pack()
@pack.command("all")
@click.option("--debug", is_flag=True, help="Enable debug")
@click.option("--config_dir", type=click.Path(exists=True, dir_okay=True), required=False, default=None, help="configuration files dir")
def pack_command(debug, config_dir):
"""Pack downloadable bin in a single command"""
set_debug(debug)
pack_all(config_dir)
@sign.command("bl1_sign_hash")
@click.option("--privkey_pem_file", type=click.Path(exists=True, dir_okay=False), required=False, default='root_ec256_privkey.pem', help="PEM private key file.")
@click.option("--hash", type=str, default=None, required=False, help="HASH value.")
@click.option("--outfile", type=str, required=False, default='manifest_signature.json', help="Output file")
@click.option("--debug", is_flag=True, help="Enable debug")
def bl1_sign_hash_command(privkey_pem_file, hash, outfile, debug):
"""sign the hash via beken bootrom signing tool."""
set_debug(debug)
bl1_sign_hash(privkey_pem_file, hash, outfile)
@sign.command("bl1_sign")
@click.option("--action_type", type=click.Choice(['hash', 'sign', 'sign_from_sig']), default='sign', required=True, help="Sign action type.")
@click.option("--key_type", type=click.Choice(['ec256']), default='ec256', required=False, help="Sign algorithm, currently only support ec256.")
@click.option("--privkey_pem_file", type=click.Path(exists=True, dir_okay=False), required=False, default='root_ec256_privkey.pem', help="PEM private key file.")
@click.option("--pubkey_pem_file", type=click.Path(exists=True, dir_okay=False), required=False, default='root_ec256_pubkey.pem', help="PEM public key file.")
@click.option("--signature", type=str, required=False, default=None, help="Signature of manifest hash.")
@click.option("--bin_file", type=click.Path(exists=True, dir_okay=False), required=False, default='bl2.bin', help="Binary file to be signed.")
@click.option("--static_addr", type=str, required=True, default='0x0', help="BL2 static address.")
@click.option("--load_addr", type=str, required=True, default='0x28040000', help="BL2 load address.")
@click.option("--outfile", type=str, required=False, default='primary_manifest.bin', help="Output file")
@click.option("--debug", is_flag=True, help="Enable debug")
def bl1_sign_command(action_type, key_type, privkey_pem_file, pubkey_pem_file, signature, bin_file, static_addr, load_addr, outfile, debug):
"""sign or calculate binary hash for bl1."""
set_debug(debug)
bl1_sign(action_type, key_type, privkey_pem_file, pubkey_pem_file, signature, bin_file, static_addr, load_addr, outfile)
@sign.command("bl2_sign_hash")
@click.option("-k", "--privkey_pem_file", type=click.Path(exists=True, dir_okay=False), required=False, default='root_ec256_privkey.pem', help="PEM private key file.")
@click.option("-d", "--hash", type=str, default=None, required=False, help="HASH value.")
@click.option("-o", "--outfile", type=str, required=False, default='manifest_signature.json', help="Output file")
@click.option("--debug", is_flag=True, help="Enable debug")
def bl2_sign_hash_command(privkey_pem_file, hash, outfile, debug):
"""sign the hash via mcuboot imgtools."""
set_debug(debug)
bl2_sign_hash(privkey_pem_file, hash, outfile)
@sign.command("bl2_sign")
@click.option("--action_type", type=click.Choice(['hash', 'sign', 'sign_from_sig']), default='sign', required=True, help="Sign action type.")
@click.option("--key_type", type=click.Choice(['ec256']), default='ec256', required=False, help="Sign algorithm, currently only support ec256.")
@click.option("--privkey_pem_file", type=click.Path(exists=True, dir_okay=False), required=False, default='root_ec256_privkey.pem', help="PEM private key file.")
@click.option("--pubkey_pem_file", type=click.Path(exists=True, dir_okay=False), required=False, default='root_ec256_pubkey.pem', help="PEM public key file.")
@click.option("--signature", type=str, required=False, default=None, help="Signature of manifest hash.")
@click.option("--bin_file", type=click.Path(exists=True, dir_okay=False), required=False, default='bl2.bin', help="Binary file to be signed.")
@click.option("--partition_size", type=int, required=True, default=0, help="Partition size.")
@click.option("--version", type=str, required=True, default='0.0.1', help="Version.")
@click.option("--security_counter", type=int, required=True, default=0, help="Version.")
@click.option("--sign_outfile", type=str, required=False, default='signed.bin', help="Output signed bin")
@click.option("--hash_outfile", type=str, required=False, default='bl2_hash.json', help="Output bl2 hash json file")
@click.option("--debug", is_flag=True, help="Enable debug")
def bl2_sign_command(action_type, key_type, privkey_pem_file, pubkey_pem_file, signature, bin_file,
partition_size, version, security_counter, sign_outfile, hash_outfile, debug):
"""sign or calculate binary hash for bl2."""
set_debug(debug)
bl2_sign(action_type, key_type, privkey_pem_file, pubkey_pem_file, signature, bin_file,
partition_size, version, security_counter, sign_outfile, hash_outfile)
@pipeline.command("mock")
@click.option("--debug", is_flag=True, help="Enable debug")
@click.option("--outfolder", type=str, required=False, default='./key_output', help="Output folder path")
def mock_command(outfolder, debug):
"""generator mock data"""
set_debug(debug)
Mock(outfolder).generate_mock_data()
@pipeline.command("server_keys")
@click.option("--outfolder", type=str, required=False, default='./key_output', help="Output folder path")
@click.option("--debug", is_flag=True, help="Enable debug")
def server_keys_command(outfolder, debug):
"""generator a series keys and bin file"""
set_debug(debug)
generate_solution_files(outfolder)
@pipeline.command("process_keys_data")
@click.option("--srcfolder", type=str, required=False, default='./key_output', help="Output folder path")
@click.option("--dstfolder", type=str, required=False, default='./pack', help="Output folder path")
@click.option("--debug", is_flag=True, help="Enable debug")
def server_keys_command(srcfolder, dstfolder, debug):
"""generator a series keys and bin file"""
set_debug(debug)
keys_data_post_processing(srcfolder, dstfolder)
if __name__ == '__main__':
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
stream_handler = logging.StreamHandler(sys.stdout)
stream_handler.setLevel(logging.DEBUG)
cli()