LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

ARM Trusted Firmware分析——镜像签名/加密/生成、解析/解密/验签

关键词:等等。

 

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\n", build_msg);
    NOTICE("Target platform: %s\n", 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\n");
        exit(1);
    }

    /* Initialize the keys */
    if (key_init() != 0) {
        ERROR("Cannot initialize keys\n");
        exit(1);
    }

    /* Initialize the new types and register OIDs for the extensions */
    if (ext_init() != 0) {
        ERROR("Cannot initialize extensions\n");
        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'\n", optarg);
                exit(1);
            }
            break;
        case 'b':
            key_size = get_key_size(optarg);
            if (key_size <= 0) {
                ERROR("Invalid key size '%s'\n", 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'\n", 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\n");
            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'\n", keys[i].desc);
            if (!key_create(&keys[i], key_alg, key_size)) {-------创建新的秘钥,key_alg指定算法,key_size指定长度。比如使用RSA 2048。
                ERROR("Error creating key '%s'\n", 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\n",
                            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\n",
                        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\n", 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("\n\n=====================================\n\n");
            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\n", __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\n", certs[i].fn);
            }
        }
    }

    /* Save keys */
    if (save_keys) {
        for (i = 0 ; i < num_keys ; i++) {
            if (!key_store(&keys[i])) {-----------------------------如果自定秘钥名称,保存。
                ERROR("Cannot save %s\n", 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\n", key->fn);
        }
    } else {
        ERROR("Key filename not specified\n");
    }

    return 0;
}

int key_create(key_t *key, int type, int key_bits)
{
    if (type >= KEY_ALG_MAX_NUM) {
        printf("Invalid key type\n");
        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\n");
        goto err;
    }

    if (!BN_set_word(e, RSA_F4)) {
        printf("Cannot assign RSA exponent\n");
        goto err;
    }

    rsa = RSA_new();----------------------------------------初始化一个RSA结构体。
    if (rsa == NULL) {
        printf("Cannot create RSA key\n");
        goto err;
    }

    if (!RSA_generate_key_ex(rsa, key_bits, e, NULL)) {-----生成一对RSA秘钥,保存在rsa中。key_bits指定RSA秘钥宽度,指数由e指定。
        printf("Cannot generate RSA key\n");
        goto err;
    }

    if (!EVP_PKEY_assign_RSA(key->key, rsa)) {---------------从rsa中提取EVP_PKEY。
        printf("Cannot assign RSA key\n");
        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\n", 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'\n", 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\n", 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\n"); return -1; } } if (strlen(nonce_string) != IV_STRING_SIZE) { ERROR("Unsupported IV size: %lu\n", 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\n"); return -1; } } ip_file = fopen(ip_name, "rb");---------------------------------------------以二进制读打开原始文件。 if (ip_file == NULL) { ERROR("Cannot read %s\n", ip_name); return -1; } op_file = fopen(op_name, "wb");---------------------------------------------以二进制读写打开输出文件。 if (op_file == NULL) { ERROR("Cannot write %s\n", op_name); fclose(ip_file); return -1; } ret = fseek(op_file, sizeof(struct fw_enc_hdr), SEEK_SET);-------------------跳过输出文件头。 if (ret) { ERROR("fseek failed\n"); goto out_file; } ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) { ERROR("EVP_CIPHER_CTX_new failed\n"); 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\n"); ret = -1; goto out; } ret = EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv); if (ret != 1) { ERROR("EVP_EncryptInit_ex failed\n"); 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\n"); 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\n"); 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\n"); 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\n"); 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] == '\0')
                create_usage(EXIT_FAILURE);

            desc = lookup_image_desc_from_uuid(&uuid);
            if (desc == NULL) {
                uuid_to_str(name, sizeof(name), &uuid);
                desc = new_image_desc(&uuid, name, "blob");
                add_image_desc(desc);
            }
            set_image_desc_action(desc, DO_PACK, filename);
            break;
        }
        default:
            create_usage(EXIT_FAILURE);
        }
    }
    argc -= optind;
    argv += optind;
    free(opts);

    if (argc == 0)
        create_usage(EXIT_SUCCESS);

    update_fip();

    pack_images(argv[0], toc_flags, align);
    return 0;
}

fiptool将所有的证书和镜像文件打包,首先生成fip的TOC头,然后是每个文件的TOC,最后是每个文件的二进制内容。

static int pack_images(const char *filename, uint64_t toc_flags, unsigned long align)
{
    FILE *fp;
    image_desc_t *desc;
    fip_toc_header_t *toc_header;
    fip_toc_entry_t *toc_entry;
    char *buf;
    uint64_t entry_offset, buf_size, payload_size = 0, pad_size;
    size_t nr_images = 0;

    for (desc = image_desc_head; desc != NULL; desc = desc->next)
        if (desc->image != NULL)
            nr_images++;

    buf_size = sizeof(fip_toc_header_t) +
        sizeof(fip_toc_entry_t) * (nr_images + 1);----------FIP文件头大小,包括一个struct fip_toc_header,N+1个struct fip_toc_entry。
    buf = calloc(1, buf_size);
    if (buf == NULL)
        log_err("calloc");

    /* Build up header and ToC entries from the image table. */
    toc_header = (fip_toc_header_t *)buf;-------------------配置fip_toc_header。
    toc_header->name = TOC_HEADER_NAME;
    toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
    toc_header->flags = toc_flags;

    toc_entry = (fip_toc_entry_t *)(toc_header + 1);--------第一个struct fip_toc_entry。

    entry_offset = buf_size;
    for (desc = image_desc_head; desc != NULL; desc = desc->next) {
        image_t *image = desc->image;

        if (image == NULL)
            continue;
        payload_size += image->toc_e.size;
        entry_offset = (entry_offset + align - 1) & ~(align - 1);
        image->toc_e.offset_address = entry_offset;
        *toc_entry++ = image->toc_e;-----------------------遍历所有image_desc_head数据到toc_entry中,并更新offset_address和payload_size。
        entry_offset += image->toc_e.size;
    }

    /*
     * Append a null uuid entry to mark the end of ToC entries.
     * NOTE the offset address for the last toc_entry must match the fip
     * size.
     */
    memset(toc_entry, 0, sizeof(*toc_entry));--------------最后附加一个NULL toc_entry表示结束。
    toc_entry->offset_address = (entry_offset + align - 1) & ~(align - 1);

    /* Generate the FIP file. */
    fp = fopen(filename, "wb");
    if (fp == NULL)
        log_err("fopen %s", filename);

    if (verbose)
        log_dbgx("Metadata size: %zu bytes", buf_size);

    xfwrite(buf, buf_size, fp, filename);

    if (verbose)
        log_dbgx("Payload size: %zu bytes", payload_size);

    for (desc = image_desc_head; desc != NULL; desc = desc->next) {
        image_t *image = desc->image;

        if (image == NULL)
            continue;
if (fseek(fp, image->toc_e.offset_address, SEEK_SET))
            log_errx("Failed to set file position");

        xfwrite(image->buffer, image->toc_e.size, fp, filename);
    }

    if (fseek(fp, entry_offset, SEEK_SET))
        log_errx("Failed to set file position");

    pad_size = toc_entry->offset_address - entry_offset;
    while (pad_size--)
        fputc(0x0, fp);

    free(buf);
    fclose(fp);
    return 0;
}

1.3.3 fiptool使用

tools/fiptool/fiptool create 
--tos-fw-extra1 /home/al/edge10/tos/qemuv8/arm-trusted-firmware/build/qemu/release/bl32_extra1_enc.bin
--tos-fw-extra2 /home/al/edge10/tos/qemuv8/arm-trusted-firmware/build/qemu/release/bl32_extra2_enc.bin
--trusted-key-cert /home/al/edge10/tos/qemuv8/arm-trusted-firmware/build/qemu/release/trusted_key.crt
--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_enc.bin
--tos-fw /home/al/edge10/tos/qemuv8/arm-trusted-firmware/build/qemu/release/bl32_enc.bin
--nt-fw /home/al/edge10/tos/qemuv8/build/../edk2/Build/ArmVirtQemuKernel-AARCH64/DEBUG_GCC49/FV/QEMU_EFI.fd
/home/al/edge10/tos/qemuv8/arm-trusted-firmware/build/qemu/release/fip.bin

2. 启动流程中镜像解析、验签、解密

ATF通过REGISTER_IMG_PARSER_LIB()注册了镜像解析接口函数,通过REGISTER_CRYPTO_LIB()注册验签和解密接口函数。

2.1 镜像解析

ATF将镜像类型分为三类:IMG_RAW(Binary image)、IMG_PLAT(Platform specific format)、IMG_CERT(X509v3 certificate)

针对IMG_CERT类型镜像,通过REGISTER_IMG_PARSER_LIB()注册相关处理函数:init()进行mbedtls初始化;check_integrity()检查证书完整性;get_auth_param()从整数中提取参数。

/* Image parser library structure */
typedef struct img_parser_lib_desc_s {
    img_type_t img_type;
    const char *name;

    void (*init)(void);
    int (*check_integrity)(void *img, unsigned int img_len);
    int (*get_auth_param)(const auth_param_type_desc_t *type_desc,
            void *img, unsigned int img_len,
            void **param, unsigned int *param_len);
} img_parser_lib_desc_t;

/* Macro to register an image parser library */
#define REGISTER_IMG_PARSER_LIB(_type, _name, _init, _check_int, _get_param) \
    static const img_parser_lib_desc_t __img_parser_lib_desc_##_type \
    __section(".img_parser_lib_descs") __used = { \
        .img_type = _type, \
        .name = _name, \
        .init = _init, \
        .check_integrity = _check_int, \
        .get_auth_param = _get_param \
    }

REGISTER_IMG_PARSER_LIB(IMG_CERT, LIB_NAME, init, \
               check_integrity, get_auth_param);

提取参数的流程?

提取哪些参数,用途是什么?

当时是如何生成的?

2.2 镜像验签和解密

ATF中镜像验签、哈希、解密功能是通过libmbedtls实现的。通过REGISTER_CRYPTO_LIB()注册libmbedtls封装的验签、哈希、解密接口函数,对上层提供服务。

2.2.1 加解密库注册

镜像的解密和验签通过REGISTER_CRYPTO_LIB()注册init()初始化函数,verify_signature()验证签名,verify_hash()验证哈希、auth_decrypt()进行解密功能。

这些函数是对libmbedtls提供功能的封装。

REGISTER_CRYPTO_LIB(LIB_NAME, init, verify_signature, verify_hash,
            auth_decrypt);
/*
 * Cryptographic library descriptor
 */
typedef struct crypto_lib_desc_s {
    const char *name;

    /* Initialize library. This function is not expected to fail. All errors
     * must be handled inside the function, asserting or panicing in case of
     * a non-recoverable error */
    void (*init)(void);

    /* Verify a digital signature. Return one of the
     * 'enum crypto_ret_value' options */
    int (*verify_signature)(void *data_ptr, unsigned int data_len,
                void *sig_ptr, unsigned int sig_len,
                void *sig_alg, unsigned int sig_alg_len,
                void *pk_ptr, unsigned int pk_len);

    /* Verify a hash. Return one of the 'enum crypto_ret_value' options */
    int (*verify_hash)(void *data_ptr, unsigned int data_len,
               void *digest_info_ptr, unsigned int digest_info_len);

#if MEASURED_BOOT
    /* Calculate a hash. Return hash value */
    int (*calc_hash)(unsigned int alg, void *data_ptr,
             unsigned int data_len, unsigned char *output);
#endif /* MEASURED_BOOT */

    /*
     * Authenticated decryption. Return one of the
     * 'enum crypto_ret_value' options.
     */
    int (*auth_decrypt)(enum crypto_dec_algo dec_algo, void *data_ptr,
                size_t len, const void *key, unsigned int key_len,
                unsigned int key_flags, const void *iv,
                unsigned int iv_len, const void *tag,
                unsigned int tag_len);
} crypto_lib_desc_t;

关于mbedtls的初始化,通过mbedtls_init()进行内存分配和释放函数初始化,以及heap结构初始化。

static void init(void)
{
/* Initialize mbed TLS */
    mbedtls_init();
}

void mbedtls_init(void)
{
    static int ready;
    void *heap_addr;
    size_t heap_size = 0;
    int err;

    if (!ready) {
        if (atexit(cleanup))
            panic();

        err = plat_get_mbedtls_heap(&heap_addr, &heap_size);---------------获取当前系统提供的堆首地址和大小。

        /* Ensure heap setup is proper */
        if (err < 0) {
            ERROR("Mbed TLS failed to get a heap\n");
            panic();
        }
        assert(heap_size >= TF_MBEDTLS_HEAP_SIZE);

        /* Initialize the mbed TLS heap */
        mbedtls_memory_buffer_alloc_init(heap_addr, heap_size);-----------注册mbedtls内存分配接口mbedtls_calloc_func和mbedtls_free_func函数,以及初始化buffer_alloc_ctx数据结构heap。

#ifdef MBEDTLS_PLATFORM_SNPRINTF_ALT
        mbedtls_platform_set_snprintf(snprintf);--------------------------注册mbedtls_snprintf接口函数。
#endif
        ready = 1;
    }
}

2.2.2 ATF验签、哈希验证、解密接口

启动过程中的加解密模块负责镜像的验签、哈希验证、镜像解密。它依赖于一个加解密库进行相关流程,这里使用的是libmbedtls。

加解密模块API基本上将参数bypass到crypto_lib_desc对应的函数接口。

void crypto_mod_init(void)
{
/* Initialize the cryptographic library */
    crypto_lib_desc.init();
}

/*
 * Function to verify a digital signature
 *
 * Parameters:
 *
 *   data_ptr, data_len: signed data---------------------------------------将要被验签的数据。
 *   sig_ptr, sig_len: the digital signature-------------------------------数字签名指针和长度。
 *   sig_alg_ptr, sig_alg_len: the digital signature algorithm-------------签名算法,包括RSA和哈希算法。
 *   pk_ptr, pk_len: the public key----------------------------------------公钥指针和长度。
 */
int crypto_mod_verify_signature(void *data_ptr, unsigned int data_len,
                void *sig_ptr, unsigned int sig_len,
                void *sig_alg_ptr, unsigned int sig_alg_len,
                void *pk_ptr, unsigned int pk_len)
{
return crypto_lib_desc.verify_signature(data_ptr, data_len,
                        sig_ptr, sig_len,
                        sig_alg_ptr, sig_alg_len,
                        pk_ptr, pk_len);
}

/*
 * Verify a hash by comparison
 *
 * Parameters:
 *
 *   data_ptr, data_len: data to be hashed----------------------------------将要被哈希的数据指针和长度。
 *   digest_info_ptr, digest_info_len: hash to be compared------------------哈希后的摘要指针和长度。
 */
int crypto_mod_verify_hash(void *data_ptr, unsigned int data_len,
               void *digest_info_ptr, unsigned int digest_info_len)
{
return crypto_lib_desc.verify_hash(data_ptr, data_len,
                       digest_info_ptr, digest_info_len);
}

/*
 * Authenticated decryption of data
 *
 * Parameters:
 *
 *   dec_algo: authenticated decryption algorithm--------------------------解密算法。
 *   data_ptr, len: data to be decrypted (inout param)---------------------将要被解密的加密数据。
 *   key, key_len, key_flags: symmetric decryption key---------------------解密秘钥、长度、标志位。
 *   iv, iv_len: initialization vector-------------------------------------初始化向量,随机化加密的一块数据。
 *   tag, tag_len: authentication tag--------------------------------------
 */
int crypto_mod_auth_decrypt(enum crypto_dec_algo dec_algo, void *data_ptr,
                size_t len, const void *key, unsigned int key_len,
                unsigned int key_flags, const void *iv,
                unsigned int iv_len, const void *tag,
                unsigned int tag_len)
{
return crypto_lib_desc.auth_decrypt(dec_algo, data_ptr, len, key,
                        key_len, key_flags, iv, iv_len, tag,
                        tag_len);
}

2.2.3 ATF和mbedtls之间验签、哈希、解密接口

ATF和mbedtls的验签、哈希、解密的接口分别是:verify_signature()、verify_hash()、auth_decrypt()。

2.2.3.1 mbedtls验签接口

verify_signature()负责对一段数据进行验签,需要的参数包括一段数据、及其对应的数字签名,还需要公钥、加密算法、摘要算法。

/*
 * Verify a signature.
 *
 * Parameters are passed using the DER encoding format following the ASN.1
 * structures detailed above.
 */
static int verify_signature(void *data_ptr, unsigned int data_len,
                void *sig_ptr, unsigned int sig_len,
                void *sig_alg, unsigned int sig_alg_len,
                void *pk_ptr, unsigned int pk_len)
{
    mbedtls_asn1_buf sig_oid, sig_params;
    mbedtls_asn1_buf signature;
    mbedtls_md_type_t md_alg;
    mbedtls_pk_type_t pk_alg;
    mbedtls_pk_context pk = {0};
    int rc;
    void *sig_opts = NULL;
    const mbedtls_md_info_t *md_info;
    unsigned char *p, *end;
    unsigned char hash[MBEDTLS_MD_MAX_SIZE];
/* Get pointers to signature OID and parameters */
    p = (unsigned char *)sig_alg;
    end = (unsigned char *)(p + sig_alg_len);
    rc = mbedtls_asn1_get_alg(&p, end, &sig_oid, &sig_params);----------------------------------从ANS1格式sig_alg中解析出签名OID和参数sig_params。
    if (rc != 0) {
        return CRYPTO_ERR_SIGNATURE;
    }

    /* Get the actual signature algorithm (MD + PK) */
    rc = mbedtls_x509_get_sig_alg(&sig_oid, &sig_params, &md_alg, &pk_alg, &sig_opts);----------从sig_oid和sig_params中解析出摘要算法、公钥类型等信息。
    if (rc != 0) {
        return CRYPTO_ERR_SIGNATURE;
    }

    /* Parse the public key */
    mbedtls_pk_init(&pk);-----------------------------------------------------------------------初始化一个mbedtls_pk_context容器。
    p = (unsigned char *)pk_ptr;
    end = (unsigned char *)(p + pk_len);
    rc = mbedtls_pk_parse_subpubkey(&p, end, &pk);----------------------------------------------从p中解析出公钥,并填充到pk上下文中。
    if (rc != 0) {
        rc = CRYPTO_ERR_SIGNATURE;
        goto end2;
    }

    /* Get the signature (bitstring) */
    p = (unsigned char *)sig_ptr;
    end = (unsigned char *)(p + sig_len);
    signature.tag = *p;
    rc = mbedtls_asn1_get_bitstring_null(&p, end, &signature.len);------------------------------从p指向的ASN.1数据结构中解析出有效数据,成功后p指向有效数据,len表示数据长度。
    if (rc != 0) {
        rc = CRYPTO_ERR_SIGNATURE;
        goto end1;
    }
    signature.p = p;-----------------------------------------------------------------------------此时p指向签名。

    /* Calculate the hash of the data */
    md_info = mbedtls_md_info_from_type(md_alg);
    if (md_info == NULL) {
        rc = CRYPTO_ERR_SIGNATURE;
        goto end1;
    }
    p = (unsigned char *)data_ptr;---------------------------------------------------------------至此已经获取到用于签名的所有信息:原始数据、签名、摘要算法、签名算法、公钥。
    rc = mbedtls_md(md_info, p, data_len, hash);-------------------------------------------------1.对数据生成摘要。
    if (rc != 0) {
        rc = CRYPTO_ERR_SIGNATURE;
        goto end1;
    }

    /* Verify the signature */
    rc = mbedtls_pk_verify_ext(pk_alg, sig_opts, &pk, md_alg, hash,
            mbedtls_md_get_size(md_info),
            signature.p, signature.len);---------------------------------------------------------2.使用签名算法对签名解密,获得摘要。然后比较摘要。如果成功,则签名有效。
    if (rc != 0) {
        rc = CRYPTO_ERR_SIGNATURE;
        goto end1;
    }

    /* Signature verification success */
    rc = CRYPTO_SUCCESS;

end1:
    mbedtls_pk_free(&pk);
end2:
    mbedtls_free(sig_opts);
return rc;
}

sig_alg和sig_ptr都是ASN.1格式数据,其中sig_alg包含了摘要算法和公钥解密算法,sig_ptr包含了摘要。pk_ptr包含了公钥。

2.2.3.2 mbedtls哈希接口

mbedtls支持的哈希算法用mbedtls_md_info_t结构体表示,包括算法名称、算法类型、摘要长度等。

typedef enum {
...
    MBEDTLS_MD_SHA256,    /**< The SHA-256 message digest. */...
} mbedtls_md_type_t;

struct mbedtls_md_info_t
{
    /** Name of the message digest */
    const char * name;

    /** Digest identifier */
    mbedtls_md_type_t type;

    /** Output length of the digest function in bytes */
    unsigned char size;

    /** Block length of the digest function in bytes */
    unsigned char block_size;
};

verify_hash()接口从传入的参数digest_info_ptr中解析出摘要和哈希算法,然后使用哈希算法对数据data_ptr进行摘要处理,最后比较前后两个摘要。

static int verify_hash(void *data_ptr, unsigned int data_len,
               void *digest_info_ptr, unsigned int digest_info_len)
{
    mbedtls_asn1_buf hash_oid, params;
    mbedtls_md_type_t md_alg;
    const mbedtls_md_info_t *md_info;
    unsigned char *p, *end, *hash;
    unsigned char data_hash[MBEDTLS_MD_MAX_SIZE];
    size_t len;
    int rc;
/* Digest info should be an MBEDTLS_ASN1_SEQUENCE */
    p = (unsigned char *)digest_info_ptr;
    end = p + digest_info_len;
    rc = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
                  MBEDTLS_ASN1_SEQUENCE);-------------------------------------获取成功后,p指向获取的数据内容,len表示获取内容的长度。
    if (rc != 0) {
        return CRYPTO_ERR_HASH;
    }

    /* Get the hash algorithm */
    rc = mbedtls_asn1_get_alg(&p, end, &hash_oid, &params);--------------------获取哈希算法数据。
    if (rc != 0) {
        return CRYPTO_ERR_HASH;
    }

    rc = mbedtls_oid_get_md_alg(&hash_oid, &md_alg);---------------------------hash_oid转换成哈希算法类型。
    if (rc != 0) {
        return CRYPTO_ERR_HASH;
    }

    md_info = mbedtls_md_info_from_type(md_alg);-------------------------------根据哈希算法类型,获取mbedtls对应结构体。
    if (md_info == NULL) {
        return CRYPTO_ERR_HASH;
    }

    /* Hash should be octet string type */
    rc = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING);-------根据tag获取原数据,这里是hash摘要。
    if (rc != 0) {
        return CRYPTO_ERR_HASH;
    }

    /* Length of hash must match the algorithm's size */
    if (len != mbedtls_md_get_size(md_info)) {---------------------------------检查摘要长度是否和算法要求摘要长度一致。
        return CRYPTO_ERR_HASH;
    }
    hash = p;

    /* Calculate the hash of the data */
    p = (unsigned char *)data_ptr;
    rc = mbedtls_md(md_info, p, data_len, data_hash);---------------------------使用哈希算法计算传入的数据的哈希值。
    if (rc != 0) {
        return CRYPTO_ERR_HASH;
    }

    /* Compare values */
    rc = memcmp(data_hash, hash, mbedtls_md_get_size(md_info));-----------------对前后两个哈希摘要进行比较。
    if (rc != 0) {
        return CRYPTO_ERR_HASH;
    }
return CRYPTO_SUCCESS;
}

2.2.3.3 mbedtls镜像解密

mbedtls_gcm_context是GCM上下文数据结构,除了包括GCM特有数据外,还包括mbedtls通用上下文mbedtls_cipher_context_t数据结构。

typedef struct mbedtls_gcm_context
{
    mbedtls_cipher_context_t cipher_ctx;  /*!< The cipher context used. */
    uint64_t HL[16];                      /*!< Precalculated HTable low. */
    uint64_t HH[16];                      /*!< Precalculated HTable high. */
    uint64_t len;                         /*!< The total length of the encrypted data. */
    uint64_t add_len;                     /*!< The total length of the additional data. */
    unsigned char base_ectr[16];          /*!< The first ECTR for tag. */
    unsigned char y[16];                  /*!< The Y working value. */
    unsigned char buf[16];                /*!< The buf working value. */
    int mode;                             /*!< The operation to perform:
                                               #MBEDTLS_GCM_ENCRYPT or
                                               #MBEDTLS_GCM_DECRYPT. */
}

typedef struct mbedtls_cipher_context_t
{
    /** Information about the associated cipher. */
    const mbedtls_cipher_info_t *cipher_info;

    /** Key length to use. */
    int key_bitlen;

    /** Operation that the key of the context has been
     * initialized for.
     */
    mbedtls_operation_t operation;

#if defined(MBEDTLS_CIPHER_MODE_WITH_PADDING)
    /** Padding functions to use, if relevant for
     * the specific cipher mode.
     */
    void (*add_padding)( unsigned char *output, size_t olen, size_t data_len );
    int (*get_padding)( unsigned char *input, size_t ilen, size_t *data_len );
#endif

    /** Buffer for input that has not been processed yet. */
    unsigned char unprocessed_data[MBEDTLS_MAX_BLOCK_LENGTH];

    /** Number of Bytes that have not been processed yet. */
    size_t unprocessed_len;

    /** Current IV or NONCE_COUNTER for CTR-mode, data unit (or sector) number
     * for XTS-mode. */
    unsigned char iv[MBEDTLS_MAX_IV_LENGTH];

    /** IV size in Bytes, for ciphers with variable-length IVs. */
    size_t iv_size;

    /** The cipher-specific context. */
    void *cipher_ctx;
...
} mbedtls_cipher_context_t;

auth_decrypt()根据解密算法选择入口函数,比如aes_gcm_decrypt()对数据进行解密。所使用的其他参数包括密钥key、初始化向量iv、标志tag。

static int auth_decrypt(enum crypto_dec_algo dec_algo, void *data_ptr,
            size_t len, const void *key, unsigned int key_len,
            unsigned int key_flags, const void *iv,
            unsigned int iv_len, const void *tag,
            unsigned int tag_len)
{
    int rc;

    assert((key_flags & ENC_KEY_IS_IDENTIFIER) == 0);
switch (dec_algo) {
    case CRYPTO_GCM_DECRYPT:
        rc = aes_gcm_decrypt(data_ptr, len, key, key_len, iv, iv_len,
                     tag, tag_len);
        if (rc != 0)
            return rc;
        break;
    default:
        return CRYPTO_ERR_DECRYPTION;
    }

return CRYPTO_SUCCESS;
}
  #define DEC_OP_BUF_SIZE 128

static int aes_gcm_decrypt(void *data_ptr, size_t len, const void *key,
               unsigned int key_len, const void *iv,
               unsigned int iv_len, const void *tag,
               unsigned int tag_len)
{
    mbedtls_gcm_context ctx;
    mbedtls_cipher_id_t cipher = MBEDTLS_CIPHER_ID_AES;
    unsigned char buf[DEC_OP_BUF_SIZE];
    unsigned char tag_buf[CRYPTO_MAX_TAG_SIZE];
    unsigned char *pt = data_ptr;
    size_t dec_len;
    int diff, i, rc;

    mbedtls_gcm_init(&ctx);----------------------------------------------------分配一个GCM解密上下文结构体。

    rc = mbedtls_gcm_setkey(&ctx, cipher, key, key_len * 8);-------------------设置密码算法和解密密钥到ctx上下文中。
    if (rc != 0) {
        rc = CRYPTO_ERR_DECRYPTION;
        goto exit_gcm;
    }

    rc = mbedtls_gcm_starts(&ctx, MBEDTLS_GCM_DECRYPT, iv, iv_len, NULL, 0);---进行GCM解密前准备工作,设置iv和tag。
    if (rc != 0) {
        rc = CRYPTO_ERR_DECRYPTION;
        goto exit_gcm;
    }

    while (len > 0) {
        dec_len = MIN(sizeof(buf), len);---------------------------------------待解密数据可能小于buf大小,不需要补齐。默认大小为128字节。

        rc = mbedtls_gcm_update(&ctx, dec_len, pt, buf);-----------------------以dec_len为单位进行解密,数据来源于pt,解密后的数据存放于buf中。
        if (rc != 0) {
            rc = CRYPTO_ERR_DECRYPTION;
            goto exit_gcm;
        }

        memcpy(pt, buf, dec_len);----------------------------------------------将解密后的数据覆盖原始加密数据。
        pt += dec_len;
        len -= dec_len;
    }

    rc = mbedtls_gcm_finish(&ctx, tag_buf, sizeof(tag_buf));-------------------完成GCM解密工作,并生成认证tag数据。
    if (rc != 0) {
        rc = CRYPTO_ERR_DECRYPTION;
        goto exit_gcm;
    }

    /* Check tag in "constant-time" */
    for (diff = 0, i = 0; i < tag_len; i++)-------------------------------------对原始tag和解密tag进行比较。
        diff |= ((const unsigned char *)tag)[i] ^ tag_buf[i];

    if (diff != 0) {
        rc = CRYPTO_ERR_DECRYPTION;
        goto exit_gcm;
    }

    /* GCM decryption success */
    rc = CRYPTO_SUCCESS;

exit_gcm:
    mbedtls_gcm_free(&ctx);
return rc;
}

GCM解密算法基于mbedtls通用解密算法框架,输入包括加密数据、解密算法、秘钥、初始化向量、tag,解密过程中以一定长度为单位逐个解密,输出的解密数据覆盖原始加密数据。完成解密操作。

2.2.4 镜像验签流程

int auth_mod_verify_img(unsigned int img_id,
            void *img_ptr,
            unsigned int img_len)
{
    const auth_img_desc_t *img_desc = NULL;
    const auth_method_desc_t *auth_method = NULL;
    void *param_ptr;
    unsigned int param_len;
    int rc, i;

/* Get the image descriptor from the chain of trust */
    img_desc = FCONF_GET_PROPERTY(tbbr, cot, img_id);-------------------------根据img_id,使用tbbr_cot_getter()从cot_desc_ptr[]中找到对应auth_img_desc_t。

    /* Ask the parser to check the image integrity */
    rc = img_parser_check_integrity(img_desc->img_type, img_ptr, img_len);----检查镜像文件完整性,如果是IMG_RAW返回OK;如果是IMG_CERT,调用check_integrity进行X509.v3证书格式检查。
    return_if_error(rc);

    /* Authenticate the image using the methods indicated in the image
     * descriptor. */
    if (img_desc->img_auth_methods == NULL)
        return 1;
    for (i = 0 ; i < AUTH_METHOD_NUM ; i++) {
        auth_method = &img_desc->img_auth_methods[i];
switch (auth_method->type) {
        case AUTH_METHOD_NONE:
            rc = 0;
            break;
        case AUTH_METHOD_HASH:------------------------------------------------镜像类型是哈希,调用auth_hash()进行认证。
            rc = auth_hash(&auth_method->param.hash,
                    img_desc, img_ptr, img_len);
            break;
        case AUTH_METHOD_SIG:-------------------------------------------------镜像类型是签名,调用auth_signature()进行认证。
            rc = auth_signature(&auth_method->param.sig,
                    img_desc, img_ptr, img_len);
            break;
        case AUTH_METHOD_NV_CTR:----------------------------------------------镜像类型是NV Counter,调用auth_nvctr()进行认证。
            rc = auth_nvctr(&auth_method->param.nv_ctr,
                    img_desc, img_ptr, img_len);
            break;
        default:
            /* Unknown authentication method */
            rc = 1;
            break;
        }
        return_if_error(rc);
    }

    /* Extract the parameters indicated in the image descriptor to
     * authenticate the children images. */
    if (img_desc->authenticated_data != NULL) {
        for (i = 0 ; i < COT_MAX_VERIFIED_PARAMS ; i++) {
            if (img_desc->authenticated_data[i].type_desc == NULL) {
                continue;
            }
/* Get the parameter from the image parser module */
            rc = img_parser_get_auth_param(img_desc->img_type,
                    img_desc->authenticated_data[i].type_desc,
                    img_ptr, img_len, &param_ptr, &param_len);
            return_if_error(rc);

            /* Check parameter size */
            if (param_len > img_desc->authenticated_data[i].data.len) {
                return 1;
            }

            /* Copy the parameter for later use */
            memcpy((void *)img_desc->authenticated_data[i].data.ptr,
                    (void *)param_ptr, param_len);
        }
    }

    /* Mark image as authenticated */
    auth_img_flags[img_desc->img_id] |= IMG_FLAG_AUTHENTICATED;
return 0;
}

 

 

/*
 * Authenticate by digital signature
 *
 * This function implements 'AUTH_METHOD_SIG'. To authenticate an image using
 * this method, the image must contain:
 *
 *   - Data to be signed
 *   - Signature
 *   - Signature algorithm
 *
 * We rely on the image parser module to extract this data from the image.
 * The parent image must contain:
 *
 *   - Public key (or a hash of it)
 *
 * If the parent image contains only a hash of the key, we will try to obtain
 * the public key from the image itself (i.e. self-signed certificates). In that
 * case, the signature verification is considered just an integrity check and
 * the authentication is established by calculating the hash of the key and
 * comparing it with the hash obtained from the parent.
 *
 * If the image has no parent (NULL), it means it has to be authenticated using
 * the ROTPK stored in the platform. Again, this ROTPK could be the key itself
 * or a hash of it.
 *
 * Return: 0 = success, Otherwise = error
 */
static int auth_signature(const auth_method_param_sig_t *param,
              const auth_img_desc_t *img_desc,
              void *img, unsigned int img_len)
{
    void *data_ptr, *pk_ptr, *pk_hash_ptr, *sig_ptr, *sig_alg_ptr;
    unsigned int data_len, pk_len, pk_hash_len, sig_len, sig_alg_len;
    unsigned int flags = 0;
    int rc = 0;
/* Get the data to be signed from current image */
    rc = img_parser_get_auth_param(img_desc->img_type, param->data,
            img, img_len, &data_ptr, &data_len);-------------------------根据img_desc->img_type和param->data,从img中解析出数据到data_ptr中。
    return_if_error(rc);

    /* Get the signature from current image */
    rc = img_parser_get_auth_param(img_desc->img_type, param->sig,
            img, img_len, &sig_ptr, &sig_len);---------------------------根据img_desc->img_type和param->sig,从img中解析出签名到sig_ptr中。
    return_if_error(rc);

    /* Get the signature algorithm from current image */
    rc = img_parser_get_auth_param(img_desc->img_type, param->alg,
            img, img_len, &sig_alg_ptr, &sig_alg_len);-------------------根据img_desc->img_type和param->alg,从img中解析出签名算法到sig_alg_ptr,包括哈希摘要算法和签名算法。
    return_if_error(rc);

    /* Get the public key from the parent. If there is no parent (NULL),
     * the certificate has been signed with the ROTPK, so we have to get
     * the PK from the platform */
    if (img_desc->parent) {
        rc = auth_get_param(param->pk, img_desc->parent,
                &pk_ptr, &pk_len);---------------------------------------从CoT父关系img_desc->parent中获取,公钥pk_ptr。
    } else {
        rc = plat_get_rotpk_info(param->pk->cookie, &pk_ptr, &pk_len,
                &flags);-------------------------------------------------如果CoT父关系为NULL,则从ROTPK中获取。
    }
    return_if_error(rc);

    if (flags & (ROTPK_IS_HASH | ROTPK_NOT_DEPLOYED)) {
        /* If the PK is a hash of the key or if the ROTPK is not
           deployed on the platform, retrieve the key from the image */
        pk_hash_ptr = pk_ptr;
        pk_hash_len = pk_len;
        rc = img_parser_get_auth_param(img_desc->img_type,
                    param->pk, img, img_len,
                    &pk_ptr, &pk_len);
        return_if_error(rc);

        /* Ask the crypto module to verify the signature */
        rc = crypto_mod_verify_signature(data_ptr, data_len,
                         sig_ptr, sig_len,
                         sig_alg_ptr, sig_alg_len,
                         pk_ptr, pk_len);
        return_if_error(rc);

        if (flags & ROTPK_NOT_DEPLOYED) {
            NOTICE("ROTPK is not deployed on platform. "
                "Skipping ROTPK verification.\n");
        } else {
            /* Ask the crypto-module to verify the key hash */
            rc = crypto_mod_verify_hash(pk_ptr, pk_len,
                    pk_hash_ptr, pk_hash_len);
        }
    } else {
        /* Ask the crypto module to verify the signature */
        rc = crypto_mod_verify_signature(data_ptr, data_len,
                         sig_ptr, sig_len,
                         sig_alg_ptr, sig_alg_len,
                         pk_ptr, pk_len);
    }
return rc;
}

 

posted on 2021-02-10 00:00  ArnoldLu  阅读(6745)  评论(0编辑  收藏  举报

导航