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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM