"""
#
#
#**************************************************************
# 文件名 :add_sign_header_cann.py
# 版本号 :初稿
# 生成日期 :2025年11月25日
# 功能描述 :根据bios_check_cfg.xml配置文件,根据其中配置属性,对各文件进行制作cms签名并绑定
# 使用方法 :python add_bios_header.py $(DEVICE_RELEASE_DIR) $(DAVINCI_TOPDIR) $(PRODUCT_NAME) $(CHIP_NAME) $(signature_tag)
# 输入参数 :DEVICE_RELEASE_DIR:待签名文件的根目录
# DAVINCI_TOPDIR:工程根路径
# PRODUCT_NAME:待扫描的产品名
# CHIP_NAME:芯片名称
# signature_tag:是否需要数字签名
# 蓝区签名步骤 1、加esbc头; 2、生成ini文件; 3、进行签名(参数控制,暂不启动); 4、签名结果写入文件头
# 返回值 :0:成功,-1:失败
# 修改历史 :
# 日期 :2025年11月25日
# 修改内容 :创建文件
"""
from collections import namedtuple
from typing import Dict, Iterator, List, Tuple
import os
import sys
import shutil
import subprocess
import argparse
import xml.etree.ElementTree as ET
import common_log as COMM_LOG
THIS_FILE_NAME = __file__
THIS_FILE_PATH = os.path.realpath(THIS_FILE_NAME)
MY_PATH = os.path.dirname(THIS_FILE_PATH)
PATH_SEPARATOR = "/"
class AddHeaderConfig:
"""加头工具命令行配置类"""
def __init__(self, inputfile, output, version, fw_version, inputtype, tag,
rootrsa, subrsa, additional, sign_alg, encrypt_alg,
encrypt_type, nvcnt, rsatag, position, image_pack_version,
bist_flag):
self.input = inputfile
self.output = output
self.version = version
self.fw_version = fw_version
self.rootrsa = rootrsa
self.subrsa = subrsa
self.additional = additional
self.type = inputtype
self.tag = tag
self.sign_alg = sign_alg
self.encrypt_alg = encrypt_alg
self.encrypt_type = encrypt_type
self.nvcnt = nvcnt
self.rsatag = rsatag
self.position = position
self.image_pack_version = image_pack_version
self.bist_flag = bist_flag
AddNvcntHeaderConfig = \
namedtuple('AddNvcntHeaderConfig', ['inputfile', 'nvcnt'])
def read_xml(in_path):
'''
功能:读取XML
'''
tree = ET.ElementTree()
tree.parse(in_path)
return tree
def check_config_item(node) -> bool:
"""校验节点必需属性"""
if "input" not in node.attrib or "output" not in node.attrib:
COMM_LOG.cilog_error(THIS_FILE_NAME, "bios_check_cfg.xml config format is invalid")
return False
if "type" in node.attrib:
if "cms" in node.attrib["type"].split('/') and "tag" not in node.attrib:
COMM_LOG.cilog_error(THIS_FILE_NAME,
"when bios_check_cfg.xml has cms type, it must has 'tag' attribute")
return False
return True
def parse_item(node):
tag_type = ""
if "type" in node.attrib:
tag_type = node.attrib["type"]
sign_alg = "PKCSv1.5"
if "sign_alg" in node.attrib:
sign_alg = node.attrib["sign_alg"]
encrypt_alg = ""
if "encrypt_alg" in node.attrib:
encrypt_alg = node.attrib["encrypt_alg"]
encrypt_type = ""
if "encrypt_type" in node.attrib:
encrypt_type = node.attrib["encrypt_type"]
add_para = ""
if "additional" in node.attrib:
add_para = node.attrib["additional"]
add_tag = []
if "tag" in node.attrib:
add_tag = node.attrib["tag"]
nvcnt = ""
if "nvcnt" in node.attrib:
nvcnt = node.attrib["nvcnt"]
rsatag = ""
if "rsatag" in node.attrib:
rsatag = node.attrib["rsatag"]
position = ""
if "position" in node.attrib:
position = node.attrib["position"]
image_pack_version = '1.0'
if 'image_pack' in node.attrib:
image_pack_version = node.attrib["image_pack"]
rootrsa = "default_rsa_rootkey"
if "rootrsa" in node.attrib:
rootrsa = node.attrib["rootrsa"]
subrsa = "default_rsa_subkey"
if "subrsa" in node.attrib:
subrsa = node.attrib["subrsa"]
bist_flag = ""
if "bist_flag" in node.attrib:
bist_flag = node.attrib["bist_flag"]
cur_conf = AddHeaderConfig(node.attrib["input"], node.attrib["output"],
node.attrib["version"],
node.attrib.get("fw_version", ""), tag_type,
add_tag, rootrsa, subrsa, add_para, sign_alg,
encrypt_alg, encrypt_type, nvcnt, rsatag,
position, image_pack_version, bist_flag)
return cur_conf
def get_item_set(config_file, sign_file_dir, version) -> Tuple[int, Dict, List]:
"""
功能:解析xml配置文件
"""
item_size_set = {}
ini_size_set = {}
tree = read_xml(config_file)
origin_nodes = tree.findall("item")
ini_nodes = tree.findall("ini")
for node in origin_nodes:
if 'version' not in node.attrib:
node.attrib['version'] = version
if not check_config_item(node):
return -1, None, None, None
nodes = []
for node in origin_nodes:
input_file = os.path.join(sign_file_dir, node.attrib["input"])
if os.path.exists(input_file):
nodes.append(node)
else:
COMM_LOG.cilog_warning(THIS_FILE_NAME, "Image file:%s not exits!\n\t", input_file)
continue
for node in nodes:
cur_conf = parse_item(node)
item_size_set[cur_conf.input] = cur_conf
for ini_node in ini_nodes:
cur_conf = parse_item(ini_node)
ini_size_set[cur_conf.input] = cur_conf
nvcnt_configs = []
for node in nodes:
if "nvcnt" in node.attrib:
inputfile = os.path.join(sign_file_dir, node.attrib["input"])
nvcnt = node.attrib["nvcnt"]
config = AddNvcntHeaderConfig(inputfile, nvcnt)
nvcnt_configs.append(config)
return 0, item_size_set, ini_size_set, nvcnt_configs
def build_inifile(item_size_set, sign_file_dir, bios_tool_path,
sign_tmp_path, product_delivery_path, add_sign):
'''
功能:根据从bios_check_cfg.xml读取的配置,生成ini工具(ini_gen.py)的配置文件,
然后调用ini工具读取该配置文件生成每个文件对应的ini文件
输入:item_size_set:待制作ini镜像的清单、image_dir:镜像根路径、bios_tool_path:bios的ini制作工具路径
返回:-1:失败,0:成功
'''
cms_flag = False
if add_sign == "true":
inicfg = os.path.join(sign_tmp_path, "image_info.xml")
with open(inicfg, "w+", encoding='utf-8') as read_cfg:
read_cfg.write("<image_info>\n")
for (infile, conf_item) in list(item_size_set.items()):
inputfile = os.path.join(sign_file_dir, infile)
relative_path = inputfile.replace(product_delivery_path + PATH_SEPARATOR, "")
output_path = os.path.dirname(
os.path.join(sign_tmp_path, relative_path))
output_path = os.path.realpath(output_path)
if not os.path.isdir(output_path):
os.makedirs(output_path)
if "cms" in conf_item.type.split('/') and conf_item.tag != "" \
and os.path.isfile(inputfile):
cms_flag = True
read_cfg.write('<image path="%s" out="%s" tag="%s" ini_name="%s"/>\n'
% (inputfile, output_path, conf_item.tag, os.path.basename(infile)))
read_cfg.write("</image_info>\n")
gen_tool = os.path.join(bios_tool_path, "ini_gen.py")
cmd = "%s %s -in_xml %s" % (os.environ["HI_PYTHON"], gen_tool, inicfg)
if add_sign == "true" and cms_flag:
COMM_LOG.cilog_info(THIS_FILE_NAME, "------------------------------------")
COMM_LOG.cilog_info(THIS_FILE_NAME, "execute:%s", cmd)
ret = subprocess.getstatusoutput(cmd)
if ret[0] != 0:
COMM_LOG.cilog_error(THIS_FILE_NAME, "build inifile failed!\n\t%s", (ret[1]))
return -1
return 0
def build_sign(item_size_set, sign_file_dir, sign_tool_path, sign_tmp_path, root_dir, product_delivery_path):
'''
功能:制作签名文件
输入:para1:待签名的镜像清单、para2:镜像根路径、para3:签名工具路径、
para4:签名临时路径,实际将待签名文件拷贝到该目录下进行签名、 para5: davinci工程路径
para6: davinci镜像生成路径
返回:-1:失败,0:成功
'''
sign_dict = {}
sign_dict["cms"] = []
for (infile, conf_item) in list(item_size_set.items()):
input_path = os.path.join(sign_file_dir, infile)
if os.path.exists(input_path):
cmd = "ls {}".format(input_path)
ret = subprocess.getstatusoutput(cmd)
if ret[0] != 0:
COMM_LOG.cilog_warning(THIS_FILE_NAME, "can not find %s in %s \n\t%s", input_path, sign_file_dir,
ret[1])
continue
else:
COMM_LOG.cilog_error(THIS_FILE_NAME, "infile is not exist:%s", input_path)
return -1
for sign in conf_item.type.split('/'):
if sign in sign_dict:
sign_dict[sign].append(infile)
cmd = ''
for file in sign_dict["cms"]:
file_with_path = os.path.join(sign_file_dir, file)
relative_path = file_with_path.replace(("{}" + PATH_SEPARATOR).format(product_delivery_path), "")
file_sign_des = os.path.realpath(os.path.join(sign_tmp_path, relative_path))
sign_path = os.path.dirname(file_sign_des)
if not os.path.isdir(sign_path):
os.makedirs(sign_path)
if os.path.isfile(file_with_path):
COMM_LOG.cilog_info(THIS_FILE_NAME, "copy %s --> %s", file_with_path, file_sign_des)
shutil.copy(file_with_path, file_sign_des)
if not os.path.isfile(file_sign_des):
COMM_LOG.cilog_error(THIS_FILE_NAME, "copy %s --> %s fail",
file_with_path, file_sign_des)
return -1
else:
COMM_LOG.cilog_error(THIS_FILE_NAME, "can not find src:%s", file_with_path)
return -1
file_sign_des = "{}.ini".format(os.path.join(sign_path, os.path.basename(file)))
print(file_sign_des)
if not cmd:
cmd = "{} {} {} {}".format(os.environ["HI_PYTHON"], sign_tool_path, root_dir, file_sign_des)
else:
cmd = '{} {}'.format(cmd, file_sign_des)
COMM_LOG.cilog_info(THIS_FILE_NAME, "------------------------------------")
COMM_LOG.cilog_info(THIS_FILE_NAME, "execute:%s", cmd)
ret = subprocess.getstatusoutput(cmd)
if ret[0] != 0:
COMM_LOG.cilog_error(THIS_FILE_NAME, "make %s sign failed!\n\t%s", sign, ret[1])
return -1
COMM_LOG.cilog_info(THIS_FILE_NAME, "%s", ret[1])
return 0
def add_bios_esbc_header(root_dir, item_size_set, sign_file_dir):
'''
功能:需要做RSA签名绑定的镜像绑定esbc二级头
输入:para1:davinci工程路径、 para2: 待签名的镜像清单、 para3: 镜像根路径
返回:-1:失败,0:成功
'''
bios_esbc_header_tool_path = os.path.join(
root_dir, "scripts", "signtool", "esbc_header")
if not os.path.exists(bios_esbc_header_tool_path):
COMM_LOG.cilog_error(THIS_FILE_NAME, "bios esbc tool dir not exits")
return -1
for (input_filename, conf_item) in list(item_size_set.items()):
input_file = os.path.join(sign_file_dir, input_filename)
if conf_item.nvcnt:
cmd = f'{os.environ["HI_PYTHON"]} {os.path.join(bios_esbc_header_tool_path, "esbc_header.py")}'
cmd += f" -raw_img {input_file} -out_img {input_file}"
cmd += f" -version {conf_item.version} -nvcnt {conf_item.nvcnt} -tag {conf_item.tag}"
COMM_LOG.cilog_info(THIS_FILE_NAME, "------------------------------------")
COMM_LOG.cilog_info(THIS_FILE_NAME, "execute:%s", cmd)
ret = subprocess.getstatusoutput(cmd)
if ret[0] != 0:
COMM_LOG.cilog_error(THIS_FILE_NAME, "add %s esbc header failed!\n\t%s", input_file, ret[1])
return -1
else:
COMM_LOG.cilog_info(THIS_FILE_NAME, "%s don't need add esbc head!\n", input_file)
return 0
def convert_der_file(crl_file: str, der_file: str) -> int:
"""
将 PEM 格式的 CRL 文件转换为 DER 格式,并保存到临时目录。
返回值:
0 - 成功
1 - 失败(包括文件不存在、OpenSSL 未安装、转换失败等)
"""
try:
if not os.path.isfile(crl_file):
print(f"[ERROR] Input CRL file not found: {crl_file}")
return 1
cmd = f"openssl crl -in {crl_file} -outform DER -out {der_file}"
result = subprocess.getstatusoutput(cmd)
if result[0] != 0:
print(f"[ERROR] OpenSSL conversion failed: {result[1]}")
return 1
return 0
except Exception as e:
print(f"[ERROR] Unexpected error: {e}")
return 1
def add_bios_header(item_size_set, sign_file_dir, bios_tool_path, sign_tool_path, root_dir, add_sign):
"""
功能:生成每个镜像的签名并绑定
输入:para1:待签名的镜像清单
para2:镜像根路径
para3:bios的ini制作工具路径
para4:签名工具路径
para5:davinci工程路径
返回:-1:失败,0:成功
"""
sign_tmp_path = os.path.join(root_dir, "sign_tmp")
product_delivery_path = os.path.join(root_dir)
if not os.path.isdir(sign_tmp_path):
os.makedirs(sign_tmp_path)
ret_code = add_bios_esbc_header(root_dir, item_size_set, sign_file_dir)
if ret_code != 0:
return ret_code
ret_code = build_inifile(
item_size_set, sign_file_dir, bios_tool_path, sign_tmp_path, product_delivery_path, add_sign)
if ret_code != 0:
return ret_code
if add_sign == "true":
ret_code = build_sign(item_size_set, sign_file_dir, sign_tool_path, sign_tmp_path,
root_dir, product_delivery_path)
if ret_code != 0:
return ret_code
signature_path = os.path.join(root_dir, "scripts", "signtool", "signature")
crl_file = os.path.join(signature_path, "SWSCRL.crl")
der_file = os.path.join(signature_path, "SWSCRL.der")
if not os.path.exists(der_file):
convert_der_file(crl_file, der_file)
for (input, conf_item) in list(item_size_set.items()):
input_file = os.path.join(sign_file_dir, input)
relative_path = input_file.replace(("{}" + PATH_SEPARATOR).format(product_delivery_path), "")
sign_file = os.path.realpath(os.path.join(sign_tmp_path, relative_path))
sign_path = os.path.dirname(sign_file)
cmd = "{} {}".format(os.environ["HI_PYTHON"], os.path.join(bios_tool_path, "image_pack.py"))
add_cmd = conf_item.additional
if add_sign != "true" or conf_item.type == '':
cmd = cmd + " -raw_img %s -out_img %s -version %s -nvcnt %s -tag %s" \
% (input_file, input_file, conf_item.version, conf_item.nvcnt, conf_item.tag)
if conf_item.position != "":
cmd = cmd + " -position %s" % (conf_item.position)
elif add_sign == "true" and conf_item.type != "":
for sign in conf_item.type.split('/'):
cmd = cmd + " -raw_img %s -out_img %s -version %s -nvcnt %s -tag %s %s" \
% (input_file, input_file, conf_item.version, conf_item.nvcnt, conf_item.tag, add_cmd)
if sign == "cms":
ini_file = os.path.join(sign_path, os.path.basename(input))
cmd = cmd + " -cms %s.ini.p7s -ini %s.ini -crl %s -certtype 1 --addcms" \
% (ini_file, ini_file, der_file)
if conf_item.position != "":
cmd = cmd + " -position %s" % (conf_item.position)
else:
COMM_LOG.cilog_error(THIS_FILE_NAME,
"bios_check_cfg.xml config format is invalid, %s is not correct!,please check!",
input_file)
return -1
COMM_LOG.cilog_info(THIS_FILE_NAME, "------------------------------------")
COMM_LOG.cilog_info(THIS_FILE_NAME, "execute:%s", cmd)
ret = subprocess.getstatusoutput(cmd)
if ret[0] != 0:
COMM_LOG.cilog_error(THIS_FILE_NAME, "add %s header failed!\n\t%s", input_file, ret[1])
return -1
if os.path.isdir(sign_tmp_path):
shutil.rmtree(sign_tmp_path)
COMM_LOG.cilog_info(THIS_FILE_NAME, "add header to all bios image success!")
return 0
def check_params(params):
"""检查参数。"""
if not os.path.exists(params['config_file']):
COMM_LOG.cilog_error(THIS_FILE_NAME, "bios image header config file not exits:%s", params['config_file'])
return -1
if not os.path.exists(params['bios_tool_path']):
COMM_LOG.cilog_error(THIS_FILE_NAME, "biostool dir not exits")
return -1
print(params['sgn_tool_path'])
if not os.path.exists(params['sgn_tool_path']):
COMM_LOG.cilog_error(THIS_FILE_NAME, "sign tools script not exits")
return -1
return 0
def _define_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser()
parser.add_argument('sign_file_dir', help='device release dir')
parser.add_argument('sign_flag', help='sign flag', default='false')
parser.add_argument('--bios_check_cfg', help='default bios_check_cfg.xml', default='bios_check_cfg.xml')
parser.add_argument('--version', help='version')
parser.add_argument('--sign_script', help='sign、 script', default='')
return parser
def setenv():
"""设置环境变量。"""
if 'HI_PYTHON' not in os.environ:
os.environ['HI_PYTHON'] = os.path.basename(sys.executable)
def main(argv=None):
"""
主函数,检查输入参数及环境检查,并调用功能函数
"""
parser = _define_parser()
args = parser.parse_args()
sign_file_dir = args.sign_file_dir
add_sign = args.sign_flag
bios_check_cfg = args.bios_check_cfg if args.bios_check_cfg else 'bios_check_cfg.xml'
version = args.version
if add_sign == "false":
return 0
root_dir = os.path.dirname(os.path.dirname(MY_PATH))
config_file = os.path.join(root_dir, bios_check_cfg)
COMM_LOG.cilog_info(THIS_FILE_NAME, "config_file=" + config_file)
bios_tool_path = os.path.join(
root_dir, "scripts", "signtool", "image_pack")
sgn_tool_path = os.path.join(
root_dir, "scripts", "sign", "community_sign_build.py")
if hasattr(args, 'sign_script') and args.sign_script:
sgn_tool_path = args.sign_script
ret_code = check_params({
'config_file': config_file,
'bios_tool_path': bios_tool_path,
'sign_file_dir': sign_file_dir,
'sgn_tool_path': sgn_tool_path,
'add_sign': add_sign,
})
if ret_code != 0:
return ret_code
setenv()
ret_code, item_size_set, ini_size_set, nvcnt_configs = get_item_set(config_file, sign_file_dir, version)
if ret_code != 0:
return ret_code
ret_code = add_bios_header(item_size_set, sign_file_dir, bios_tool_path,
sgn_tool_path, root_dir, add_sign)
return ret_code
if __name__ == "__main__":
sys.exit(main())