关键词:等等。
1. 镜像生成过程
生成fip.bin需要做如下工作工作:
- 编译certificates相关工具cert_create,生成证书。注意证书对应未加密的镜像。
- 如果需要加密,需要编译enctool工具encrypt_fw,用于对镜像文件进行加密,并加上加密头struct fw_enc_hdr。
- fiptool工具生成fip.bin,使用cert_create生成的证书,如加密使用encrypt_fw加密后的镜像,否则使用原始镜像,加上FIP的TOC和Entry等信息。
在Makefile和make_helpers/build_macro.mk中定义了fip.bin生成流程。
fip: ${BUILD_PLAT}/${FIP_NAME} ${BUILD_PLAT}/${FIP_NAME}: ${FIP_DEPS} ${FIPTOOL}----------生成fip.bin文件。 @echo "Arnoldlu: ${FIP_DEPS} ${FIPTOOL} create ${FIP_ARGS} $@" ${Q}${FIPTOOL} create ${FIP_ARGS} $@ ${Q}${FIPTOOL} info $@ @${ECHO_BLANK_LINE} @echo "Built $@ successfully" @${ECHO_BLANK_LINE}
certtool: ${CRTTOOL---------------------------生成cert_create工具。
.PHONY: ${CRTTOOL}
${CRTTOOL}:
echo "Arnoldlu: ${CRTTOOL}"
${Q}${MAKE} PLAT=${PLAT} USE_TBBR_DEFS=${USE_TBBR_DEFS} COT=${COT} OPENSSL_DIR=${OPENSSL_DIR} CRTTOOL=${CRTTOOL} --no-print-directory -C ${CRTTOOLPATH}
@${ECHO_BLANK_LINE}
@echo "Built $@ successfully"
@${ECHO_BLANK_LINE}
certificates: ${CRT_DEPS} ${CRTTOOL}-----------生成各证书。 @echo "Arnoldlu: ${CRTTOOL} ${CRT_ARGS}" ${Q}${CRTTOOL} ${CRT_ARGS} @${ECHO_BLANK_LINE} @echo "Built $@ successfully" @echo "Certificates can be found in ${BUILD_PLAT}" @${ECHO_BLANK_LINE} enctool: ${ENCTOOL}---------------------------生成encrypt_fw工具。 .PHONY: ${ENCTOOL} ${ENCTOOL}: ${Q}${MAKE} PLAT=${PLAT} BUILD_INFO=0 OPENSSL_DIR=${OPENSSL_DIR} ENCTOOL=${ENCTOOL} --no-print-directory -C ${ENCTOOLPATH} @${ECHO_BLANK_LINE} @echo "Built $@ successfully" @${ECHO_BLANK_LINE} define ENCRYPT_FW----------------------------对镜像进行加密。 $(2): $(1) enctool $$(ECHO) "Arnoldlu: ENC $$(ENCTOOL) $$(ENC_ARGS) -i $$< -o $$@" $$(Q)$$(ENCTOOL) $$(ENC_ARGS) -i $$< -o $$@ endef
1.1 证书生成工具cert_create
证书工具首先生成所有tbb_keys[]中的秘钥,然后生成tbb_certs[]中的证书,并保存为命令行中指定的文件名。
FIP中内容ID | struct auth_img_desc_s | 说明 | 文件名 | cert_create选项 | tbb_certs | tbb_keys |
TRUSTED_BOOT_FW_CERT_ID | trusted_boot_fw_cert | Trusted Boot Firmware BL2 certificate |
tb_fw.crt |
--tb-fw-cert | TRUSTED_BOOT_FW_CERT | ROT_KEY |
TRUSTED_KEY_CERT_ID |
trusted_key_cert |
Trusted key certificate |
trusted_key.crt |
--trusted-key-cert |
TRUSTED_KEY_CERT | ROT_KEY |
SOC_FW_KEY_CERT_ID | soc_fw_key_cert | SoC Firmware key certificate |
soc_fw_key.crt |
--soc-fw-key-cert | SOC_FW_KEY_CERT | TRUSTED_WORLD_KEY |
SOC_FW_CONTENT_CERT_ID | soc_fw_content_cert | SoC Firmware content certificate |
soc_fw_content.crt |
--soc-fw-cert | SOC_FW_CONTENT_CERT | SOC_FW_CONTENT_CERT_KEY |
TRUSTED_OS_FW_KEY_CERT_ID | trusted_os_fw_key_cert | Trusted OS Firmware key certificate |
tos_fw_key.crt |
--tos-fw-key-cert | TRUSTED_OS_FW_KEY_CERT | TRUSTED_WORLD_KEY |
TRUSTED_OS_FW_CONTENT_CERT_ID | trusted_os_fw_content_cert | Trusted OS Firmware content certificate |
tos_fw_content.crt |
--tos-fw-cert | TRUSTED_OS_FW_CONTENT_CERT | TRUSTED_OS_FW_CONTENT_CERT_KEY |
NON_TRUSTED_FW_KEY_CERT_ID | non_trusted_fw_key_cert | Non-Trusted Firmware key certificate |
nt_fw_key.crt |
--nt-fw-key-cert | NON_TRUSTED_FW_KEY_CERT | NON_TRUSTED_WORLD_KEY |
NON_TRUSTED_FW_CONTENT_CERT_ID | non_trusted_fw_content_cert | Non-Trusted Firmware content certificate |
nt_fw_content.crt |
--nt-fw-cert | NON_TRUSTED_FW_CONTENT_CERT | NON_TRUSTED_FW_CONTENT_CERT_KEY |
上述所有的证书都是为了认证镜像的,他们组合形成了认证链:
1.1.1 cert_create使用
cert_create工具指定生成秘钥的算法、长度、哈希,镜像文件,证书文件,RootKey等,输出一系列证书文件。
NOTICE: CoT Generation Tool: Built : 11:14:29, Jan 28 2021 NOTICE: Target platform: TBBR Generic The certificate generation tool loads the binary images and optionally the RSA keys, and outputs the key and content certificates properly signed to implement the chain of trust. If keys are provided, they must be in PEM format. Certificates are generated in DER format. Usage: ./cert_create [OPTIONS] Available options: -h,--help Print this message and exit -a,--key-alg <arg> Key algorithm: 'rsa' (default)- RSAPSS scheme as per PKCS#1 v2.1, 'ecdsa' -b,--key-size <arg> Key size (for supported algorithms). -s,--hash-alg <arg> Hash algorithm : 'sha256' (default), 'sha384', 'sha512' -k,--save-keys Save key pairs into files. Filenames must be provided -n,--new-keys Generate new key pairs if no key files are provided -p,--print-cert Print the certificates in the standard output --tb-fw-cert <arg> Trusted Boot FW Certificate (output file) --trusted-key-cert <arg> Trusted Key Certificate (output file) --scp-fw-key-cert <arg> SCP Firmware Key Certificate (output file) --scp-fw-cert <arg> SCP Firmware Content Certificate (output file) --soc-fw-key-cert <arg> SoC Firmware Key Certificate (output file) --soc-fw-cert <arg> SoC Firmware Content Certificate (output file) --tos-fw-key-cert <arg> Trusted OS Firmware Key Certificate (output file) --tos-fw-cert <arg> Trusted OS Firmware Content Certificate (output file) --nt-fw-key-cert <arg> Non-Trusted Firmware Key Certificate (output file) --nt-fw-cert <arg> Non-Trusted Firmware Content Certificate (output file) --sip-sp-cert <arg> SiP owned Secure Partition Content Certificate (output file) --fwu-cert <arg> Firmware Update Certificate (output file) --rot-key <arg> Root Of Trust key (input/output file) --trusted-world-key <arg> Trusted World key (input/output file) --non-trusted-world-key <arg> Non Trusted World key (input/output file) --scp-fw-key <arg> SCP Firmware Content Certificate key (input/output file) --soc-fw-key <arg> SoC Firmware Content Certificate key (input/output file) --tos-fw-key <arg> Trusted OS Firmware Content Certificate key (input/output file) --nt-fw-key <arg> Non Trusted Firmware Content Certificate key (input/output file) --tfw-nvctr <arg> Trusted Firmware Non-Volatile counter value --ntfw-nvctr <arg> Non-Trusted Firmware Non-Volatile counter value --tb-fw <arg> Trusted Boot Firmware image file --tb-fw-config <arg> Trusted Boot Firmware Config file --hw-config <arg> HW Config file --fw-config <arg> Firmware Config file --scp-fw <arg> SCP Firmware image file --soc-fw <arg> SoC AP Firmware image file --soc-fw-config <arg> SoC Firmware Config file --tos-fw <arg> Trusted OS image file --tos-fw-extra1 <arg> Trusted OS Extra1 image file --tos-fw-extra2 <arg> Trusted OS Extra2 image file --tos-fw-config <arg> Trusted OS Firmware Config file --nt-fw <arg> Non-Trusted World Bootloader image file --nt-fw-config <arg> Non Trusted OS Firmware Config file ... --scp-fwu-cfg <arg> SCP Firmware Update Config image file --ap-fwu-cfg <arg> AP Firmware Update Config image file --fwu <arg> Firmware Updater image file
如下一个实例:
tools/cert_create/cert_create
--tos-fw-extra1 /home/al/edge10/tos/qemuv8/build/../optee_os/out/arm/core/tee-pager_v2.bin
--tos-fw-extra2 /home/al/edge10/tos/qemuv8/build/../optee_os/out/arm/core/tee-pageable_v2.bin
-n----------如未指定秘钥,则生成新的秘钥。
--tfw-nvctr 0 --ntfw-nvctr 0--------------------------------------------------------Trusted Firmware/Non-Trusted Firmware的NV Counter都默认为0.
--trusted-key-cert /home/al/edge10/tos/qemuv8/arm-trusted-firmware/build/qemu/release/trusted_key.crt
--key-alg rsa --key-size 2048 --hash-alg sha256-------------------------------------签名算法使用rsa 2048,哈希算法使用sha256。
--rot-key /home/al/edge10/tos/qemuv8/build/../arm-trusted-firmware/plat/arm/board/common/rotpk/arm_rotprivk_rsa.pem
--tb-fw-cert /home/al/edge10/tos/qemuv8/arm-trusted-firmware/build/qemu/release/tb_fw.crt
--soc-fw-cert /home/al/edge10/tos/qemuv8/arm-trusted-firmware/build/qemu/release/soc_fw_content.crt
--soc-fw-key-cert /home/al/edge10/tos/qemuv8/arm-trusted-firmware/build/qemu/release/soc_fw_key.crt
--tos-fw-cert /home/al/edge10/tos/qemuv8/arm-trusted-firmware/build/qemu/release/tos_fw_content.crt
--tos-fw-key-cert /home/al/edge10/tos/qemuv8/arm-trusted-firmware/build/qemu/release/tos_fw_key.crt
--nt-fw-cert /home/al/edge10/tos/qemuv8/arm-trusted-firmware/build/qemu/release/nt_fw_content.crt
--nt-fw-key-cert /home/al/edge10/tos/qemuv8/arm-trusted-firmware/build/qemu/release/nt_fw_key.crt
--tb-fw /home/al/edge10/tos/qemuv8/arm-trusted-firmware/build/qemu/release/bl2.bin
--soc-fw /home/al/edge10/tos/qemuv8/arm-trusted-firmware/build/qemu/release/bl31.bin
--tos-fw /home/al/edge10/tos/qemuv8/build/../optee_os/out/arm/core/tee-header_v2.bin
--nt-fw /home/al/edge10/tos/qemuv8/build/../edk2/Build/ArmVirtQemuKernel-AARCH64/DEBUG_GCC49/FV/QEMU_EFI.fd
1.1.2 cert_create代码分析
证书相关数据tbb_certs[]通过key和tbb_keys[]关联,通过ext和tbb_ext[]关联。
1.1.2.1 秘钥数据结构
struct key_s定义了cert_create秘钥数据结构,id区别不同秘钥,秘钥存放于EVP_PKEY数据结构中。
typedef struct key_s { int id; /* Key id */ const char *opt; /* Command line option to specify a key */------------秘钥对应的命令行选项,决定下面的fn。 const char *help_msg; /* Help message */ const char *desc; /* Key description (debug purposes) */ char *fn; /* Filename to load/store the key */ EVP_PKEY *key; /* Key container */----------------------------------存放EVP_KEY秘钥。 } key_t;
通过REGISTER_KEYS()向系统注册了建立TBB所需要的秘钥,由tbb_keys[]指定,里面任何一个成员都对应一组RSA 2048秘钥。
/* Macro to register the keys used in the CoT */ #define REGISTER_KEYS(_keys) key_t *keys = &_keys[0]; const unsigned int num_keys = sizeof(_keys)/sizeof(_keys[0]) REGISTER_KEYS(tbb_keys); static key_t tbb_keys[] = { [ROT_KEY] = { .id = ROT_KEY, .opt = "rot-key", .help_msg = "Root Of Trust key (input/output file)", .desc = "Root Of Trust key" }, ... };
1.1.2.2 证书数据结构
struct cert_s是cert_create所使用的证书数据结构,包括id、x用于存放X509.v3格式证书等。
struct cert_s { int id; /* Unique identifier */ const char *opt; /* Command line option to pass filename */ const char *fn; /* Filename to save the certificate */ const char *cn; /* Subject CN (Company Name) */ const char *help_msg; /* Help message */ /* These fields must be defined statically */ int key; /* Key to be signed */----------------------------------和tbb_keys关联。 int issuer; /* Issuer certificate */ int ext[CERT_MAX_EXT]; /* Certificate extensions */ int num_ext; /* Number of extensions in the certificate */ X509 *x; /* X509 certificate container */ };
通过REGISTER_COT向系统注册了建立TBB所需要的证书。
/* Macro to register the certificates used in the CoT */ #define REGISTER_COT(_certs) cert_t *certs = &_certs[0]; const unsigned int num_certs = sizeof(_certs)/sizeof(_certs[0]) REGISTER_COT(tbb_certs); /* * Certificates used in the chain of trust * * The order of the certificates must follow the enumeration specified in * tbb_cert.h. All certificates are self-signed, so the issuer certificate * field points to itself. */ static cert_t tbb_certs[] = { [TRUSTED_BOOT_FW_CERT] = { .id = TRUSTED_BOOT_FW_CERT, .opt = "tb-fw-cert", .help_msg = "Trusted Boot FW Certificate (output file)", .fn = NULL, .cn = "Trusted Boot FW Certificate", .key = ROT_KEY, .issuer = TRUSTED_BOOT_FW_CERT, .ext = { TRUSTED_FW_NVCOUNTER_EXT, TRUSTED_BOOT_FW_HASH_EXT, TRUSTED_BOOT_FW_CONFIG_HASH_EXT, HW_CONFIG_HASH_EXT, FW_CONFIG_HASH_EXT }, .num_ext = 5 }, ... };
1.1.2.3 证书扩展数据结构
struct ext_s定义了帧数扩展数据结构,REGISTER_EXTENSIONS(tbb_ext)将支持的扩展注册到系统。
/* * This structure contains the relevant information to create the extensions * to be included in the certificates. This extensions will be used to * establish the chain of trust. */ typedef struct ext_s { const char *oid; /* OID of the extension */ const char *sn; /* Short name */ const char *ln; /* Long description */ const char *opt; /* Command line option to specify data */ const char *help_msg; /* Help message */ const char *arg; /* Argument passed from command line */ int asn1_type; /* OpenSSL ASN1 type of the extension data. * Supported types are: * - V_ASN1_INTEGER * - V_ASN1_OCTET_STRING */ int type; /* See ext_type_e */ /* Extension attributes (depends on extension type) */ union { int nvctr_type; /* See nvctr_type_e */ int key; /* Index into array of registered public keys */ } attr; int alias; /* In case OpenSSL provides an standard * extension of the same type, add the new * extension as an alias of this one */ X509V3_EXT_METHOD method; /* This field may be used to define a custom * function to print the contents of the * extension */ int optional; /* This field may be used optionally to exclude an image */ } ext_t; REGISTER_EXTENSIONS(tbb_ext);
/* Macro to register the extensions used in the CoT */
#define REGISTER_EXTENSIONS(_ext)
ext_t *extensions = &_ext[0];
const unsigned int num_extensions = sizeof(_ext)/sizeof(_ext[0])
static ext_t tbb_ext[] = { [TRUSTED_FW_NVCOUNTER_EXT] = { .oid = TRUSTED_FW_NVCOUNTER_OID, .opt = "tfw-nvctr", .help_msg = "Trusted Firmware Non-Volatile counter value", .sn = "TrustedWorldNVCounter", .ln = "Trusted World Non-Volatile counter", .asn1_type = V_ASN1_INTEGER, .type = EXT_TYPE_NVCOUNTER, .attr.nvctr_type = NVCTR_TYPE_TFW }, ... [TRUSTED_WORLD_PK_EXT] = { .oid = TRUSTED_WORLD_PK_OID, .sn = "TrustedWorldPublicKey", .ln = "Trusted World Public Key", .asn1_type = V_ASN1_OCTET_STRING, .type = EXT_TYPE_PKEY, .attr.key = TRUSTED_WORLD_KEY }, ... [TRUSTED_OS_FW_CONTENT_CERT_PK_EXT] = { .oid = TRUSTED_OS_FW_CONTENT_CERT_PK_OID, .sn = "TrustedOSFirmwareContentCertPK", .ln = "Trusted OS Firmware content certificate public key", .asn1_type = V_ASN1_OCTET_STRING, .type = EXT_TYPE_PKEY, .attr.key = TRUSTED_OS_FW_CONTENT_CERT_KEY }, [TRUSTED_OS_FW_HASH_EXT] = { .oid = TRUSTED_OS_FW_HASH_OID, .opt = "tos-fw", .help_msg = "Trusted OS image file", .sn = "TrustedOSHash", .ln = "Trusted OS hash (SHA256)", .asn1_type = V_ASN1_OCTET_STRING, .type = EXT_TYPE_HASH }, [TRUSTED_OS_FW_EXTRA1_HASH_EXT] = { .oid = TRUSTED_OS_FW_EXTRA1_HASH_OID, .opt = "tos-fw-extra1", .help_msg = "Trusted OS Extra1 image file", .sn = "TrustedOSExtra1Hash", .ln = "Trusted OS Extra1 hash (SHA256)", .asn1_type = V_ASN1_OCTET_STRING, .type = EXT_TYPE_HASH, .optional = 1 }, [TRUSTED_OS_FW_EXTRA2_HASH_EXT] = { .oid = TRUSTED_OS_FW_EXTRA2_HASH_OID, .opt = "tos-fw-extra2", .help_msg = "Trusted OS Extra2 image file", .sn = "TrustedOSExtra2Hash", .ln = "Trusted OS Extra2 hash (SHA256)", .asn1_type = V_ASN1_OCTET_STRING, .type = EXT_TYPE_HASH, .optional = 1 }, ... };
1.1.2.4 秘钥和证书生成
首先解析命令行参数, 逐个生成秘钥、证书及其扩展部分,并将证书保存成文件。
int main(int argc, char *argv[]) { STACK_OF(X509_EXTENSION) * sk; X509_EXTENSION *cert_ext = NULL; ext_t *ext; key_t *key; cert_t *cert; FILE *file; int i, j, ext_nid, nvctr; int c, opt_idx = 0; const struct option *cmd_opt; const char *cur_opt; unsigned int err_code; unsigned char md[SHA512_DIGEST_LENGTH]; unsigned int md_len; const EVP_MD *md_info; NOTICE("CoT Generation Tool: %s ", build_msg); NOTICE("Target platform: %s ", platform_msg); /* Set default options */ key_alg = KEY_ALG_RSA; hash_alg = HASH_ALG_SHA256; key_size = -1; /* Add common command line options */ for (i = 0; i < NUM_ELEM(common_cmd_opt); i++) { cmd_opt_add(&common_cmd_opt[i]); } /* Initialize the certificates */ if (cert_init() != 0) { ERROR("Cannot initialize certificates "); exit(1); } /* Initialize the keys */ if (key_init() != 0) { ERROR("Cannot initialize keys "); exit(1); } /* Initialize the new types and register OIDs for the extensions */ if (ext_init() != 0) { ERROR("Cannot initialize extensions "); exit(1); } /* Get the command line options populated during the initialization */ cmd_opt = cmd_opt_get_array(); while (1) { /* getopt_long stores the option index here. */ c = getopt_long(argc, argv, "a:b:hknps:", cmd_opt, &opt_idx); /* Detect the end of the options. */ if (c == -1) { break; } switch (c) { case 'a': key_alg = get_key_alg(optarg); if (key_alg < 0) { ERROR("Invalid key algorithm '%s' ", optarg); exit(1); } break; case 'b': key_size = get_key_size(optarg); if (key_size <= 0) { ERROR("Invalid key size '%s' ", optarg); exit(1); } break; case 'h': print_help(argv[0], cmd_opt); exit(0); case 'k': save_keys = 1; break; case 'n': new_keys = 1; break; case 'p': print_cert = 1; break; case 's': hash_alg = get_hash_alg(optarg); if (hash_alg < 0) { ERROR("Invalid hash algorithm '%s' ", optarg); exit(1); } break; case CMD_OPT_EXT: cur_opt = cmd_opt_get_name(opt_idx); ext = ext_get_by_opt(cur_opt); ext->arg = strdup(optarg); break; case CMD_OPT_KEY: cur_opt = cmd_opt_get_name(opt_idx); key = key_get_by_opt(cur_opt); key->fn = strdup(optarg);---------------------从命令行中获取相关key的文件名。 break; case CMD_OPT_CERT: cur_opt = cmd_opt_get_name(opt_idx); cert = cert_get_by_opt(cur_opt); cert->fn = strdup(optarg);--------------------从命令行中获取相关证书的文件名。 break; case '?': default: print_help(argv[0], cmd_opt); exit(1); } } /* Select a reasonable default key-size */ if (key_size == -1) { key_size = KEY_SIZES[key_alg][0]; } /* Check command line arguments */ check_cmd_params(); ... /* Load private keys from files (or generate new ones) */ for (i = 0 ; i < num_keys ; i++) {---------------------------遍历通过REGISTER_KEYS()注册的秘钥数组,这里是tbb_keys[]。首先尝试从文件中获取,如果没有则创建新的秘钥。 if (!key_new(&keys[i])) {------------------------------------EVP_PKEY_new()分配秘钥空间。 ERROR("Failed to allocate key container "); exit(1); } /* First try to load the key from disk */ if (key_load(&keys[i], &err_code)) {---------------------尝试从文件系统中加载秘钥。 /* Key loaded successfully */ continue; } ... /* File does not exist, could not be opened or no filename was * given */ if (new_keys) { /* Try to create a new key */ NOTICE("Creating new key for '%s' ", keys[i].desc); if (!key_create(&keys[i], key_alg, key_size)) {-------创建新的秘钥,key_alg指定算法,key_size指定长度。比如使用RSA 2048。 ERROR("Error creating key '%s' ", keys[i].desc); exit(1); } } else { ... } } /* Create the certificates */ for (i = 0 ; i < num_certs ; i++) {---------------------------遍历通过REGISTER_COT()创建的证书数组,这里是tbb_certs[]。 cert = &certs[i]; /* Create a new stack of extensions. This stack will be used * to create the certificate */ CHECK_NULL(sk, sk_X509_EXTENSION_new_null()); for (j = 0 ; j < cert->num_ext ; j++) {------------------遍历当前证书中ext[], ext = &extensions[cert->ext[j]]; /* Get OpenSSL internal ID for this extension */ CHECK_OID(ext_nid, ext->oid); /* * Three types of extensions are currently supported: * - EXT_TYPE_NVCOUNTER * - EXT_TYPE_HASH * - EXT_TYPE_PKEY */ switch (ext->type) { case EXT_TYPE_NVCOUNTER: if (ext->arg) { nvctr = atoi(ext->arg); CHECK_NULL(cert_ext, ext_new_nvcounter(ext_nid, EXT_CRIT, nvctr)); } break; case EXT_TYPE_HASH: if (ext->arg == NULL) { if (ext->optional) { /* Include a hash filled with zeros */ memset(md, 0x0, SHA512_DIGEST_LENGTH); } else { /* Do not include this hash in the certificate */ break; } } else { /* Calculate the hash of the file */ if (!sha_file(hash_alg, ext->arg, md)) { ERROR("Cannot calculate hash of %s ", ext->arg); exit(1); } } CHECK_NULL(cert_ext, ext_new_hash(ext_nid, EXT_CRIT, md_info, md, md_len)); break; case EXT_TYPE_PKEY: CHECK_NULL(cert_ext, ext_new_key(ext_nid, EXT_CRIT, keys[ext->attr.key].key)); break; default: ERROR("Unknown extension type '%d' in %s ", ext->type, cert->cn); exit(1); } /* Push the extension into the stack */ sk_X509_EXTENSION_push(sk, cert_ext); } /* Create certificate. Signed with corresponding key */ if (cert->fn && !cert_new(hash_alg, cert, VAL_DAYS, 0, sk)) { ERROR("Cannot create %s ", cert->cn); exit(1); } sk_X509_EXTENSION_free(sk); } /* Print the certificates */ if (print_cert) { for (i = 0 ; i < num_certs ; i++) { if (!certs[i].x) { continue; } printf(" ===================================== "); X509_print_fp(stdout, certs[i].x); } } /* Save created certificates to files */ for (i = 0 ; i < num_certs ; i++) { if (certs[i].x && certs[i].fn) {----------------------------如果指定证书名称,保存。 file = fopen(certs[i].fn, "w"); printf("Arnoldlu %s write cert to %s ", __func__, certs[i].fn); if (file != NULL) { i2d_X509_fp(file, certs[i].x);----------------------将certs[i].x保存到文件中,为X509.v3格式文件。 fclose(file); } else { ERROR("Cannot create file %s ", certs[i].fn); } } } /* Save keys */ if (save_keys) { for (i = 0 ; i < num_keys ; i++) { if (!key_store(&keys[i])) {-----------------------------如果自定秘钥名称,保存。 ERROR("Cannot save %s ", keys[i].desc); } } } #ifndef OPENSSL_NO_ENGINE ENGINE_cleanup(); #endif CRYPTO_cleanup_all_ex_data(); return 0; }
key_load()从文件中加载秘钥,key_store将秘钥保存到文件中,key_create()创建秘钥。
int key_init(void) { cmd_opt_t cmd_opt; key_t *key; unsigned int i; for (i = 0; i < num_keys; i++) { key = &keys[i]; if (key->opt != NULL) { cmd_opt.long_opt.name = key->opt; cmd_opt.long_opt.has_arg = required_argument; cmd_opt.long_opt.flag = NULL; cmd_opt.long_opt.val = CMD_OPT_KEY; cmd_opt.help_msg = key->help_msg; cmd_opt_add(&cmd_opt); } } return 0; } int key_load(key_t *key, unsigned int *err_code) { FILE *fp; EVP_PKEY *k; if (key->fn) { /* Load key from file */ fp = fopen(key->fn, "r"); if (fp) { k = PEM_read_PrivateKey(fp, &key->key, NULL, NULL);----------读取PEM格式的秘钥到key->key中。 fclose(fp); ... } else { ... } } else { ... } return 0; } int key_store(key_t *key) { FILE *fp; if (key->fn) { fp = fopen(key->fn, "w"); if (fp) { PEM_write_PrivateKey(fp, key->key, NULL, NULL, 0, NULL, NULL);--------------------------将PEM格式的key->key保存到文件中。 fclose(fp); return 1; } else { ERROR("Cannot create file %s ", key->fn); } } else { ERROR("Key filename not specified "); } return 0; } int key_create(key_t *key, int type, int key_bits) { if (type >= KEY_ALG_MAX_NUM) { printf("Invalid key type "); return 0; } if (key_create_fn[type]) { return key_create_fn[type](key, key_bits); } return 0; } static const key_create_fn_t key_create_fn[KEY_ALG_MAX_NUM] = { key_create_rsa, /* KEY_ALG_RSA */ #ifndef OPENSSL_NO_EC key_create_ecdsa, /* KEY_ALG_ECDSA */ #endif /* OPENSSL_NO_EC */ }; static int key_create_rsa(key_t *key, int key_bits) { BIGNUM *e; RSA *rsa = NULL; e = BN_new();------------------------------------------生成大数。 if (e == NULL) { printf("Cannot create RSA exponent "); goto err; } if (!BN_set_word(e, RSA_F4)) { printf("Cannot assign RSA exponent "); goto err; } rsa = RSA_new();----------------------------------------初始化一个RSA结构体。 if (rsa == NULL) { printf("Cannot create RSA key "); goto err; } if (!RSA_generate_key_ex(rsa, key_bits, e, NULL)) {-----生成一对RSA秘钥,保存在rsa中。key_bits指定RSA秘钥宽度,指数由e指定。 printf("Cannot generate RSA key "); goto err; } if (!EVP_PKEY_assign_RSA(key->key, rsa)) {---------------从rsa中提取EVP_PKEY。 printf("Cannot assign RSA key "); goto err; } BN_free(e);----------------------------------------------释放大数。 return 1; err: RSA_free(rsa); BN_free(e); return 0; }
cert_new()创建证书,cert_add_ext()添加证书扩展。
int cert_init(void) { cmd_opt_t cmd_opt; cert_t *cert; unsigned int i; for (i = 0; i < num_certs; i++) { cert = &certs[i]; cmd_opt.long_opt.name = cert->opt; cmd_opt.long_opt.has_arg = required_argument; cmd_opt.long_opt.flag = NULL; cmd_opt.long_opt.val = CMD_OPT_CERT; cmd_opt.help_msg = cert->help_msg; cmd_opt_add(&cmd_opt); } return 0; } int cert_new( int md_alg, cert_t *cert, int days, int ca, STACK_OF(X509_EXTENSION) * sk) { EVP_PKEY *pkey = keys[cert->key].key; cert_t *issuer_cert = &certs[cert->issuer]; EVP_PKEY *ikey = keys[issuer_cert->key].key; X509 *issuer = issuer_cert->x; X509 *x; X509_EXTENSION *ex; X509_NAME *name; ASN1_INTEGER *sno; int i, num, rc = 0; EVP_MD_CTX *mdCtx; EVP_PKEY_CTX *pKeyCtx = NULL; /* Create the certificate structure */ x = X509_new(); if (!x) { return 0; } /* If we do not have a key, use the issuer key (the certificate will * become self signed). This happens in content certificates. */ if (!pkey) { pkey = ikey; } /* If we do not have an issuer certificate, use our own (the certificate * will become self signed) */ if (!issuer) { issuer = x; } mdCtx = EVP_MD_CTX_create(); if (mdCtx == NULL) { ERR_print_errors_fp(stdout); goto END; } /* Sign the certificate with the issuer key */ if (!EVP_DigestSignInit(mdCtx, &pKeyCtx, get_digest(md_alg), NULL, ikey)) { ERR_print_errors_fp(stdout); goto END; } /* * Set additional parameters if issuing public key algorithm is RSA. * This is not required for ECDSA. */ if (EVP_PKEY_base_id(ikey) == EVP_PKEY_RSA) { if (!EVP_PKEY_CTX_set_rsa_padding(pKeyCtx, RSA_PKCS1_PSS_PADDING)) { ERR_print_errors_fp(stdout); goto END; } if (!EVP_PKEY_CTX_set_rsa_pss_saltlen(pKeyCtx, RSA_SALT_LEN)) { ERR_print_errors_fp(stdout); goto END; } if (!EVP_PKEY_CTX_set_rsa_mgf1_md(pKeyCtx, get_digest(md_alg))) { ERR_print_errors_fp(stdout); goto END; } } /* x509.v3 */ X509_set_version(x, 2); /* Random serial number */ sno = ASN1_INTEGER_new(); rand_serial(NULL, sno); X509_set_serialNumber(x, sno); ASN1_INTEGER_free(sno); X509_gmtime_adj(X509_get_notBefore(x), 0); X509_gmtime_adj(X509_get_notAfter(x), (long)60*60*24*days); X509_set_pubkey(x, pkey); /* Subject name */ name = X509_get_subject_name(x); X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (const unsigned char *)cert->cn, -1, -1, 0); X509_set_subject_name(x, name); /* Issuer name */ name = X509_get_issuer_name(x); X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (const unsigned char *)issuer_cert->cn, -1, -1, 0); X509_set_issuer_name(x, name); /* Add various extensions: standard extensions */ cert_add_ext(issuer, x, NID_subject_key_identifier, "hash"); cert_add_ext(issuer, x, NID_authority_key_identifier, "keyid:always"); if (ca) { cert_add_ext(issuer, x, NID_basic_constraints, "CA:TRUE"); cert_add_ext(issuer, x, NID_key_usage, "keyCertSign"); } else { cert_add_ext(issuer, x, NID_basic_constraints, "CA:FALSE"); } /* Add custom extensions */ if (sk != NULL) { num = sk_X509_EXTENSION_num(sk); for (i = 0; i < num; i++) { ex = sk_X509_EXTENSION_value(sk, i); X509_add_ext(x, ex, -1); } } if (!X509_sign_ctx(x, mdCtx)) { ERR_print_errors_fp(stdout); goto END; } /* X509 certificate signed successfully */ rc = 1; cert->x = x; END: EVP_MD_CTX_destroy(mdCtx); return rc; } int cert_add_ext(X509 *issuer, X509 *subject, int nid, char *value) { X509_EXTENSION *ex; X509V3_CTX ctx; /* No configuration database */ X509V3_set_ctx_nodb(&ctx); /* Set issuer and subject certificates in the context */ X509V3_set_ctx(&ctx, issuer, subject, NULL, NULL, 0); ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value); if (!ex) { ERR_print_errors_fp(stdout); return 0; } X509_add_ext(subject, ex, -1); X509_EXTENSION_free(ex); return 1; } const EVP_MD *get_digest(int alg) { switch (alg) { case HASH_ALG_SHA256: return EVP_sha256(); case HASH_ALG_SHA384: return EVP_sha384(); case HASH_ALG_SHA512: return EVP_sha512(); default: return NULL; } }
1.2 加密工具encrypt_fw
1.2.1 encrypt_fw使用
encrypt_fw的使用介绍如下:
The firmware encryption tool loads the binary image and
outputs encrypted binary image using an encryption key
provided as an input hex string.
Usage:
./arm-trusted-firmware/tools/encrypt_fw/encrypt_fw [OPTIONS]
Available options:
-h,--help Print this message and exit
-f,--fw-enc-status <arg> Firmware encryption status flag (with SSK=0 or BSSK=1).
-a,--key-alg <arg> Encryption key algorithm: 'gcm' (default)
-k,--key <arg> Encryption key (for supported algorithm).
-n,--nonce <arg> Nonce or Initialization Vector (for supported algorithm).
-i,--in <arg> Input filename to be encrypted.
-o,--out <arg> Encrypted output filename.
ATF在生成加密镜像的执行命令如下:
tools/encrypt_fw/encrypt_fw
-f 0
-k 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef--加密所使用的秘钥。
-n 1234567890abcdef12345678------------------------------------------加密所使用的Initialization Vector参数。
-i /home/al/edge10/tos/qemuv8/build/../optee_os/out/arm/core/tee-pager_v2.bin
-o /home/al/edge10/tos/qemuv8/arm-trusted-firmware/build/qemu/release/bl32_extra1_enc.bin
1.2.2 encrypt_fw代码分析
1.2.2.1 数据结构
struct fw_enc_hdr是加密文件头,共44字节。魔数为0xAA640001。
#define ENC_HEADER_MAGIC 0xAA640001U #define FW_ENC_STATUS_FLAG_MASK 0x1 enum fw_enc_status_t { FW_ENC_WITH_SSK = 0, FW_ENC_WITH_BSSK = 1, }; #define ENC_MAX_IV_SIZE 16U #define ENC_MAX_TAG_SIZE 16U #define ENC_MAX_KEY_SIZE 32U struct fw_enc_hdr { uint32_t magic;------------------------加密文件魔数。 uint16_t dec_algo;---------------------解密算法。 uint16_t flags; uint16_t iv_len;-----------------------iv大小。 uint16_t tag_len;----------------------tag大小。 uint8_t iv[ENC_MAX_IV_SIZE]; uint8_t tag[ENC_MAX_TAG_SIZE]; };
1.2.2.2 文件加密流程
encrypt_fw工具目前仅支持AES GCM加密算法,通过调用libopenssl库文件实现对文件的加密,并附上文件头struct fw_enc_hdr。
int main(int argc, char *argv[]) { int i, key_alg, ret; int c, opt_idx = 0; const struct option *cmd_opt; char *key = NULL; char *nonce = NULL; char *in_fn = NULL; char *out_fn = NULL; unsigned short fw_enc_status = 0; NOTICE("Firmware Encryption Tool: %s ", build_msg); /* Set default options */ key_alg = KEY_ALG_GCM; /* Add common command line options */ for (i = 0; i < NUM_ELEM(common_cmd_opt); i++) { cmd_opt_add(&common_cmd_opt[i]); } /* Get the command line options populated during the initialization */ cmd_opt = cmd_opt_get_array(); while (1) { /* getopt_long stores the option index here. */ c = getopt_long(argc, argv, "a:f:hi:k:n:o:", cmd_opt, &opt_idx); /* Detect the end of the options. */ if (c == -1) { break; } switch (c) { case 'a': key_alg = get_key_alg(optarg);--------------------------------------------目前仅支持gcm模式加密。 if (key_alg < 0) { ERROR("Invalid key algorithm '%s' ", optarg); exit(1); } break; case 'f': parse_fw_enc_status_flag(optarg, &fw_enc_status); break; case 'k': key = optarg; break; case 'i': in_fn = optarg; break; case 'o': out_fn = optarg; break; case 'n': nonce = optarg; break; case 'h': print_help(argv[0], cmd_opt); exit(0); case '?': default: print_help(argv[0], cmd_opt); exit(1); } } ... ret = encrypt_file(fw_enc_status, key_alg, key, nonce, in_fn, out_fn);-------------文件加密入口,参数包括加密算法、密钥、Initialization Vector等。。 CRYPTO_cleanup_all_ex_data(); return ret; }
static int get_key_alg(const char *key_alg_str) { int i; for (i = 0 ; i < NUM_ELEM(key_algs_str) ; i++) { if (strcmp(key_alg_str, key_algs_str[i]) == 0) { return i; } } return -1; } int encrypt_file(unsigned short fw_enc_status, int enc_alg, char *key_string, char *nonce_string, const char *ip_name, const char *op_name) { switch (enc_alg) { case KEY_ALG_GCM:-------------------------------------------------------------------使用GCM模式加密文件。 return gcm_encrypt(fw_enc_status, key_string, nonce_string, ip_name, op_name); default: return -1; } } static int gcm_encrypt(unsigned short fw_enc_status, char *key_string, char *nonce_string, const char *ip_name, const char *op_name) { FILE *ip_file; FILE *op_file; EVP_CIPHER_CTX *ctx; unsigned char data[BUFFER_SIZE], enc_data[BUFFER_SIZE]; unsigned char key[KEY_SIZE], iv[IV_SIZE], tag[TAG_SIZE]; int bytes, enc_len = 0, i, j, ret = 0; struct fw_enc_hdr header; memset(&header, 0, sizeof(struct fw_enc_hdr)); if (strlen(key_string) != KEY_STRING_SIZE) { ERROR("Unsupported key size: %lu ", strlen(key_string)); return -1; } for (i = 0, j = 0; i < KEY_SIZE; i++, j += 2) {-----------------------------KEY_SIZE为32字节。 if (sscanf(&key_string[j], "%02hhx", &key[i]) != 1) { ERROR("Incorrect key format "); return -1; } } if (strlen(nonce_string) != IV_STRING_SIZE) { ERROR("Unsupported IV size: %lu ", strlen(nonce_string)); return -1; } for (i = 0, j = 0; i < IV_SIZE; i++, j += 2) {------------------------------IV_SIZE为12字节。 if (sscanf(&nonce_string[j], "%02hhx", &iv[i]) != 1) { ERROR("Incorrect IV format "); return -1; } } ip_file = fopen(ip_name, "rb");---------------------------------------------以二进制读打开原始文件。 if (ip_file == NULL) { ERROR("Cannot read %s ", ip_name); return -1; } op_file = fopen(op_name, "wb");---------------------------------------------以二进制读写打开输出文件。 if (op_file == NULL) { ERROR("Cannot write %s ", op_name); fclose(ip_file); return -1; } ret = fseek(op_file, sizeof(struct fw_enc_hdr), SEEK_SET);-------------------跳过输出文件头。 if (ret) { ERROR("fseek failed "); goto out_file; } ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) { ERROR("EVP_CIPHER_CTX_new failed "); ret = -1; goto out_file; } ret = EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL); if (ret != 1) { ERROR("EVP_EncryptInit_ex failed "); ret = -1; goto out; } ret = EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv); if (ret != 1) { ERROR("EVP_EncryptInit_ex failed "); goto out; } while ((bytes = fread(data, 1, BUFFER_SIZE, ip_file)) != 0) {-----------------以256字节为单位对输入文件进行加密。 ret = EVP_EncryptUpdate(ctx, enc_data, &enc_len, data, bytes); if (ret != 1) { ERROR("EVP_EncryptUpdate failed "); ret = -1; goto out; } fwrite(enc_data, 1, enc_len, op_file); } ret = EVP_EncryptFinal_ex(ctx, enc_data, &enc_len); if (ret != 1) { ERROR("EVP_EncryptFinal_ex failed "); ret = -1; goto out; } ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, TAG_SIZE, tag);-----------获取GCM Tag,大小为16字节。 if (ret != 1) { ERROR("EVP_CIPHER_CTX_ctrl failed "); ret = -1; goto out; } header.magic = ENC_HEADER_MAGIC;-----------------------------------------------加密文件魔数0xaa6401。 header.flags |= fw_enc_status & FW_ENC_STATUS_FLAG_MASK;-----------------------加密文件flags,一般为0。 header.dec_algo = KEY_ALG_GCM;-------------------------------------------------加密算法类型。 header.iv_len = IV_SIZE;-------------------------------------------------------IV大小,0x0c字节。 header.tag_len = TAG_SIZE;-----------------------------------------------------Tag大小,0x10字节。 memcpy(header.iv, iv, IV_SIZE); memcpy(header.tag, tag, TAG_SIZE); ret = fseek(op_file, 0, SEEK_SET); if (ret) { ERROR("fseek failed "); goto out; } fwrite(&header, 1, sizeof(struct fw_enc_hdr), op_file); out: EVP_CIPHER_CTX_free(ctx); out_file: fclose(ip_file); fclose(op_file); /* * EVP_* APIs returns 1 as success but enctool considers * 0 as success. */ if (ret == 1) ret = 0; return ret; }
1.3 镜像生成工具fiptool
1.3.1 数据结构
fiptool工具支持查询镜像内容、生成/更新/移除镜像内容、解压镜像等功能。
/* Available subcommands. */ static cmd_t cmds[] = { { .name = "info", .handler = info_cmd, .usage = info_usage }, { .name = "create", .handler = create_cmd, .usage = create_usage }, { .name = "update", .handler = update_cmd, .usage = update_usage }, { .name = "unpack", .handler = unpack_cmd, .usage = unpack_usage }, { .name = "remove", .handler = remove_cmd, .usage = remove_usage }, { .name = "version", .handler = version_cmd, .usage = version_usage }, { .name = "help", .handler = help_cmd, .usage = NULL }, };
toc_entries定义了系统支持的所有内容入口,包括镜像和证书,使用uuid和cmdline_name进行关联。
/* The images used depends on the platform. */ toc_entry_t toc_entries[] = { { .name = "SCP Firmware Updater Configuration FWU SCP_BL2U", .uuid = UUID_TRUSTED_UPDATE_FIRMWARE_SCP_BL2U, .cmdline_name = "scp-fwu-cfg" }, ... { .name = NULL, .uuid = { {0} }, .cmdline_name = NULL, } };
struct fip_toc_header定义了FIP文件的头,struct fip_toc_entry定义了FIP文件中包含的每个内容的入口,通过fip_toc_entry可以解析出FIP中每个内容。
typedef struct fip_toc_header {
uint32_t name;
uint32_t serial_number;
uint64_t flags;
} fip_toc_header_t;
typedef struct fip_toc_entry {
uuid_t uuid;
uint64_t offset_address;
uint64_t size;
uint64_t flags;
} fip_toc_entry_t;
1.3.2 镜像生成create功能
create命令的handler是create_cmd()。
static int create_cmd(int argc, char *argv[]) { struct option *opts = NULL; size_t nr_opts = 0; unsigned long long toc_flags = 0; unsigned long align = 1; if (argc < 2) create_usage(EXIT_FAILURE); opts = fill_common_opts(opts, &nr_opts, required_argument); opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument, OPT_PLAT_TOC_FLAGS); opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN); opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b'); opts = add_opt(opts, &nr_opts, NULL, 0, 0); while (1) { int c, opt_index = 0; c = getopt_long(argc, argv, "b:", opts, &opt_index); if (c == -1) break; switch (c) { case OPT_TOC_ENTRY: { image_desc_t *desc; desc = lookup_image_desc_from_opt(opts[opt_index].name); set_image_desc_action(desc, DO_PACK, optarg); break; } case OPT_PLAT_TOC_FLAGS: parse_plat_toc_flags(optarg, &toc_flags); break; case OPT_ALIGN: align = get_image_align(optarg); break; case 'b': { char name[_UUID_STR_LEN + 1]; char filename[PATH_MAX] = { 0 }; uuid_t uuid = uuid_null; image_desc_t *desc; parse_blob_opt(optarg, &uuid, filename, sizeof(filename)); if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 || filename[0] == '