diff --git a/cli/main.c b/cli/main.c index 843ace67..d4f11ad7 100644 --- a/cli/main.c +++ b/cli/main.c @@ -2346,6 +2346,7 @@ static int fw_read(int argc, char **argv) int bl2; int key; int pmap; + int debug_token; int no_progress_bar; } cfg = { .out_fd = -1 @@ -2364,9 +2365,11 @@ static int fw_read(int argc, char **argv) {"bl2", 'b', "", CFG_NONE, &cfg.bl2, no_argument, "read the BL2 partiton instead of the main firmware"}, {"key", 'k', "", CFG_NONE, &cfg.key, no_argument, - "read the key manifest partiton instead of the main firmware - Gen5 and below only"}, + "read the key manifest partiton instead of the main firmware"}, {"pmap", 'm', "", CFG_NONE, &cfg.pmap, no_argument, "read the partition map instead of the main firmware - Gen6"}, + {"token", 't', "", CFG_NONE, &cfg.debug_token, no_argument, + "read the debug token instead of the main firmware - Gen6"}, {"no-progress", 'p', "", CFG_NONE, &cfg.no_progress_bar, no_argument, "don't print progress to stdout"}, {NULL}}; @@ -2374,11 +2377,14 @@ static int fw_read(int argc, char **argv) argconfig_parse(argc, argv, CMD_DESC_FW_READ, opts, &cfg, sizeof(cfg)); if (!switchtec_is_gen6(cfg.dev) && cfg.pmap) { - fprintf(stderr, "Getting the parition map is only available on Gen6 Switchtec device!\n"); + fprintf(stderr, "Getting the parition map is only available \ + on Gen6 Switchtec device!\n"); return -1; } - if (switchtec_is_gen6(cfg.dev) && cfg.key) { - fprintf(stderr, "Getting the key manifest is not available on Gen6 Switchtec device!\n"); + + if (!switchtec_is_gen6(cfg.dev) && cfg.debug_token) { + fprintf(stderr, "Getting the debug token is only available \ + on Gen6 Switchtec device!\n"); return -1; } @@ -2405,9 +2411,13 @@ static int fw_read(int argc, char **argv) fw_typ_gen6 = SWITCHTEC_IMG_PART_TYPE_BL2; } else if (cfg.key) { inf = cfg.inactive ? sum->key.inactive : sum->key.active; + fw_typ_gen6 = SWITCHTEC_IMG_PART_TYPE_KMT; } else if (cfg.pmap) { inf = cfg.inactive ? sum->map.inactive : sum->map.active; fw_typ_gen6 = SWITCHTEC_IMG_PART_TYPE_MAP; + } else if (cfg.debug_token) { + inf = cfg.inactive ? sum->token.inactive : sum->token.active; + fw_typ_gen6 = SWITCHTEC_IMG_PART_TYPE_DEBUG_TOKEN; } else { inf = cfg.inactive ? sum->img.inactive : sum->img.active; fw_typ_gen6 = SWITCHTEC_IMG_PART_TYPE_FW; @@ -2446,11 +2456,12 @@ static int fw_read(int argc, char **argv) fw_slot = (fw_slot % 2) ? 0 : 1; progress_start(); if (cfg.no_progress_bar) - ret = switchtec_fw_img_get(cfg.dev, cfg.out_fd, fw_typ_gen6, - fw_slot, NULL); + ret = switchtec_fw_img_get(cfg.dev, cfg.out_fd, + fw_typ_gen6, fw_slot, NULL); else - ret = switchtec_fw_img_get(cfg.dev, cfg.out_fd, fw_typ_gen6, - fw_slot, progress_update); + ret = switchtec_fw_img_get(cfg.dev, cfg.out_fd, + fw_typ_gen6, fw_slot, + progress_update); } else { ret = switchtec_fw_img_write_hdr(cfg.out_fd, inf); if (ret < 0) { @@ -2460,11 +2471,11 @@ static int fw_read(int argc, char **argv) progress_start(); if (cfg.no_progress_bar) - ret = switchtec_fw_body_read_fd(cfg.dev, cfg.out_fd, - inf, NULL); + ret = switchtec_fw_body_read_fd(cfg.dev, cfg.out_fd, + inf, NULL); else ret = switchtec_fw_body_read_fd(cfg.dev, cfg.out_fd, - inf, progress_update); + inf, progress_update); } switchtec_fw_part_summary_free(sum); progress_finish(cfg.no_progress_bar); diff --git a/cli/mfg.c b/cli/mfg.c index 023f3055..07f9f260 100644 --- a/cli/mfg.c +++ b/cli/mfg.c @@ -511,7 +511,7 @@ static int info(int argc, char **argv) printf("Secure Unlock Version: \t\t\t0x%08x\n", sn_info.ver_sec_unlock); } - if (phase_id == SWITCHTEC_BOOT_PHASE_BL2) { + if (!switchtec_is_gen6(cfg.dev) && phase_id == SWITCHTEC_BOOT_PHASE_BL2) { printf("\nOther secure settings are only shown in the BL1 or Main Firmware phase.\n\n"); return 0; } @@ -1342,6 +1342,7 @@ static int debug_unlock(int argc, char **argv) int ret; struct switchtec_pubkey pubk; struct switchtec_signature sig; + struct switchtec_gen6_token token; const char *desc = CMD_DESC_DEBUG_UNLOCK "\n\n" "This command unlocks the EJTAG port, Command Line " @@ -1355,6 +1356,8 @@ static int debug_unlock(int argc, char **argv) unsigned long serial; FILE *sig_fimg; char *sig_file; + FILE *tkn_fimg; + char *tkn_file; } cfg = { .unlock_version = 0xffff, }; @@ -1376,6 +1379,10 @@ static int debug_unlock(int argc, char **argv) .value_addr=&cfg.sig_fimg, .argument_type=required_argument, .help="signature file"}, + {"token_file", 't', .cfg_type=CFG_FILE_R, + .value_addr=&cfg.tkn_fimg, + .argument_type=required_argument, + .help="token file - Gen6 only"}, {NULL} }; @@ -1405,6 +1412,18 @@ static int debug_unlock(int argc, char **argv) return -1; } + if (cfg.tkn_file == NULL && switchtec_is_gen6(cfg.dev)) { + fprintf(stderr, + "Token file must be set for Gen6 devices using this command!\n"); + return -1; + } + + if(cfg.tkn_file != NULL && !switchtec_is_gen6(cfg.dev)) { + fprintf(stderr, + "Ignoring token file parameter, this device is not Gen6!\n"); + cfg.tkn_file = NULL; + } + ret = switchtec_read_pubk_file(cfg.pubkey_fimg, &pubk); fclose(cfg.pubkey_fimg); @@ -1423,8 +1442,19 @@ static int debug_unlock(int argc, char **argv) return -3; } + if (switchtec_is_gen6(cfg.dev)) { + ret = switchtec_read_token_file(cfg.tkn_fimg, &token); + fclose(cfg.tkn_fimg); + + if (ret) { + fprintf(stderr, "Invalid token file %s!\n", + cfg.tkn_file); + return -3; + } + } + ret = switchtec_dbg_unlock(cfg.dev, cfg.serial, cfg.unlock_version, - &pubk, &sig); + &pubk, &sig, &token); if (ret) switchtec_perror("mfg dbg-unlock"); @@ -1577,6 +1607,10 @@ static int debug_unlock_token(int argc, char **argv) "Generate token for signature file required for command 'mfg debug-unlock' (default)"}, {"UNLOCK_VERSION_UPDATE", TOKEN_VERSION_UPDATE, "Generate token for signature file required for command 'mfg debug-lock-update'"}, + {"GEN6_STATIC_TOKEN", GEN6_TOKEN_STATIC, + "Generate static token for signature file required for command 'mfg debug-unlock' on Gen6 devices"}, + {"GEN6_EPHEMERAL_TOKEN", GEN6_TOKEN_EPHEMERAL, + "Generate ephemeral token for signature file required for command 'mfg debug-unlock' on Gen6 devices"}, {} }; @@ -1604,30 +1638,64 @@ static int debug_unlock_token(int argc, char **argv) argconfig_parse(argc, argv, desc, opts, &cfg, sizeof(cfg)); - ret = switchtec_sn_ver_get(cfg.dev, &sn_info); - if (ret) { - switchtec_perror("mfg debug unlock token"); - return ret; - } + if (switchtec_is_gen6(cfg.dev)) + { + if (cfg.type != GEN6_TOKEN_STATIC && + cfg.type != GEN6_TOKEN_EPHEMERAL) { + fprintf(stderr, + "On Gen6 devices, only GEN6_STATIC_TOKEN \ + and GEN6_EPHEMERAL_TOKEN types are supported.\n"); + return -1; + } + + struct switchtec_gen6_token token; + + ret = switchtec_dbg_unlock_get_token_gen6(cfg.dev, &token, cfg.type); + if (ret) { + switchtec_perror("mfg debug unlock token"); + return ret; + } - token.serial = htole32(sn_info.chip_serial); + ret = write(cfg.out_fd, &token, sizeof(token)); + if(ret <= 0) { + switchtec_perror("mfg debug gen6 unlock token"); + return ret; + } - if (cfg.type == TOKEN_RESOURCE_UNLOCK) { - token.id = htole32(1); - token.version = htole32(sn_info.ver_sec_unlock); + fprintf(stderr, "\nToken data saved to %s\n", cfg.out_filename); + close(cfg.out_fd); } else { - token.id = htole32(2); - token.version = htole32(sn_info.ver_sec_unlock) + 1; - } + if (cfg.type == GEN6_TOKEN_STATIC || + cfg.type == GEN6_TOKEN_EPHEMERAL) { + fprintf(stderr, + "Gen6 types are not supported on this device.\n"); + return -1; + } + ret = switchtec_sn_ver_get(cfg.dev, &sn_info); + if (ret) { + switchtec_perror("mfg debug unlock token"); + return ret; + } - ret = write(cfg.out_fd, &token, sizeof(token)); - if(ret <= 0) { - switchtec_perror("mfg debug unlock token"); - return ret; - } + token.serial = htole32(sn_info.chip_serial); - fprintf(stderr, "\nToken data saved to %s\n", cfg.out_filename); - close(cfg.out_fd); + if (cfg.type == TOKEN_RESOURCE_UNLOCK) { + token.id = htole32(1); + token.version = htole32(sn_info.ver_sec_unlock); + } else { + token.id = htole32(2); + token.version = htole32(sn_info.ver_sec_unlock) + 1; + } + + ret = write(cfg.out_fd, &token, sizeof(token)); + if(ret <= 0) { + switchtec_perror("mfg debug unlock token"); + return ret; + } + + fprintf(stderr, "\nToken data saved to %s\n", cfg.out_filename); + close(cfg.out_fd); + } return 0; } diff --git a/inc/switchtec/mfg.h b/inc/switchtec/mfg.h index a8a3a0fe..fc4b4dd9 100644 --- a/inc/switchtec/mfg.h +++ b/inc/switchtec/mfg.h @@ -34,6 +34,10 @@ #define SWITCHTEC_KMSK_NUM_MAX 10 #define SWITCHTEC_KMSK_NUM_GEN6 12 #define SWITCHTEC_KMSK_LEN_DWORDS (SWITCHTEC_KMSK_LEN / 4) +#define SWITCHTEC_GEN6_TOKEN_LEN 104 + +#define OTP_MULTI_DWORD_UID_UNIQUEID_DWORDS 16 +#define OTP_MULTI_DWORD_CUSTOMER_PSID0_DWORDS 4 #define SWITCHTEC_SECURITY_SPI_RATE_MAX_NUM 16 @@ -208,6 +212,20 @@ struct switchtec_security_cfg_state { struct switchtec_attestation_state attn_state; }; +/** + * @brief Supported KMT Signature Formats. Value stored in KMT Prefix in 4 bit field. + */ +enum kmt_signature_types_e +{ + KMT_SIG_FORMAT_CRC = 0, + KMT_SIG_FORMAT_RSA3KSHA2 = 1, + KMT_SIG_FORMAT_RSA4KSHA2 = 2, + KMT_SIG_FORMAT_ECDSAP384SHA2 = 3, + KMT_SIG_FORMAT_ECDSAP521SHA2 = 4, + KMT_SIG_FORMAT_DILITHIUM5 = 5, + KMT_SIG_FORMAT_MAX +}; + enum switchtec_otp_key_status { UNPROGRAMMED = 0x00, PROGRAMMED = 0x01, @@ -259,6 +277,17 @@ enum switchtec_bl2_recovery_mode { SWITCHTEC_BL2_RECOVERY_I2C_AND_XMODEM = 3 }; +#define TOKEN_RESOURCE_UNLOCK 0 +#define TOKEN_VERSION_UPDATE 1 +#define GEN6_TOKEN_STATIC 2 +#define GEN6_TOKEN_EPHEMERAL 3 + +enum secure_token_get_types_e { + SECURE_TOKEN_GET_TYPE_STATIC = 0, + SECURE_TOKEN_GET_TYPE_EPHEMERAL = 1, + SECURE_TOKEN_GET_TYPE_MAX +}; + struct switchtec_kmsk { uint8_t kmsk[SWITCHTEC_KMSK_LEN]; }; @@ -272,6 +301,10 @@ struct switchtec_signature{ uint8_t signature[SWITCHTEC_SIG_LEN]; }; +struct switchtec_gen6_token{ + uint8_t token[SWITCHTEC_GEN6_TOKEN_LEN]; +}; + struct switchtec_uds { unsigned char uds[SWITCHTEC_UDS_LEN]; }; @@ -312,12 +345,16 @@ int switchtec_secure_state_set(struct switchtec_dev *dev, int switchtec_dbg_unlock(struct switchtec_dev *dev, uint32_t serial, uint32_t ver_sec_unlock, struct switchtec_pubkey *public_key, - struct switchtec_signature *signature); + struct switchtec_signature *signature, + struct switchtec_gen6_token *token); int switchtec_dbg_unlock_version_update(struct switchtec_dev *dev, uint32_t serial, uint32_t ver_sec_unlock, struct switchtec_pubkey *public_key, struct switchtec_signature *signature); +int switchtec_dbg_unlock_get_token_gen6(struct switchtec_dev *dev, + struct switchtec_gen6_token *token, + int token_type); int switchtec_read_sec_cfg_file(struct switchtec_dev *dev, FILE *setting_file, struct switchtec_security_cfg_set *set); @@ -325,6 +362,7 @@ int switchtec_read_pubk_file(FILE *pubk_file, struct switchtec_pubkey *pubk); int switchtec_read_kmsk_file(FILE *kmsk_file, struct switchtec_kmsk *kmsk); int switchtec_read_signature_file(FILE *sig_file, struct switchtec_signature *sigature); +int switchtec_read_token_file(FILE *tkn_file, struct switchtec_gen6_token *token); int switchtec_read_uds_file(FILE *uds_file, struct switchtec_uds *uds); int switchtec_security_state_has_kmsk(struct switchtec_security_cfg_state *state, diff --git a/inc/switchtec/mrpc.h b/inc/switchtec/mrpc.h index d7d1b744..9d3e95c1 100644 --- a/inc/switchtec/mrpc.h +++ b/inc/switchtec/mrpc.h @@ -250,6 +250,13 @@ enum mrpc_sub_cmd { MRPC_DBG_UNLOCK_DATA = 1, MRPC_DBG_UNLOCK_UPDATE = 2, + MRPC_GEN6_DBG_UNLOCK_PKEY = 0, + MRPC_GEN6_DBG_UNLOCK_SIG = 1, + MRPC_GEN6_DBG_UNLOCK_STATIC = 2, + MRPC_GEN6_DBG_SEC_VER_UPDATE = 3, + MRPC_GEN6_DBG_UNLOCK_STATIC_DISABLE = 4, + MRPC_GEN6_DBG_UNLOCK_TOKEN_GET = 5, + MRPC_KMSK_ENTRY_SET_PKEY = 0, MRPC_KMSK_ENTRY_SET_SIG = 1, MRPC_KMSK_ENTRY_SET_KMSK = 2, diff --git a/inc/switchtec/switchtec.h b/inc/switchtec/switchtec.h index 9529d4ab..42e70570 100644 --- a/inc/switchtec/switchtec.h +++ b/inc/switchtec/switchtec.h @@ -327,7 +327,7 @@ struct switchtec_fw_image_info { struct switchtec_fw_part_summary { struct switchtec_fw_part_type { struct switchtec_fw_image_info *active, *inactive; - } boot, map, img, cfg, nvlog, seeprom, key, bl2, riot; + } boot, map, img, cfg, nvlog, seeprom, key, bl2, riot, token; struct switchtec_fw_image_info *mult_cfg; diff --git a/lib/mfg.c b/lib/mfg.c index 0ad8e936..5940ba19 100644 --- a/lib/mfg.c +++ b/lib/mfg.c @@ -1189,6 +1189,56 @@ static int dbg_unlock_send_pubkey(struct switchtec_dev *dev, return switchtec_mfg_cmd(dev, cmd_id, &cmd, sizeof(cmd), NULL, 0); } +static int dbg_unlock_send_pubkey_gen6(struct switchtec_dev *dev, + struct switchtec_pubkey *public_key, + uint32_t cmd_id) +{ + struct public_key_cmd_gen6 { + uint8_t subcmd; + uint8_t reserved[3]; + uint32_t total_len; + uint32_t total_crc; + uint32_t data_len; + uint32_t offset; + uint8_t pub_key[SWITCHTEC_PUB_KEY_LEN]; + } cmd = {}; + + cmd.subcmd = MRPC_GEN6_DBG_UNLOCK_PKEY; + memcpy(cmd.pub_key, public_key->pubkey, SWITCHTEC_PUB_KEY_LEN); + cmd.total_len = htole32(SWITCHTEC_PUB_KEY_LEN); + cmd.total_crc = htole32(crc32(cmd.pub_key, SWITCHTEC_PUB_KEY_LEN, 0, 1, 1)); + cmd.data_len = htole32(SWITCHTEC_PUB_KEY_LEN); + cmd.offset = htole32(0); + + return switchtec_mfg_cmd(dev, cmd_id, &cmd, sizeof(cmd), NULL, 0); +} + +static int dbg_unlock_send_sig_gen6(struct switchtec_dev *dev, + struct switchtec_signature *signature, + uint32_t cmd_id) +{ + struct sig_cmd_gen6 { + uint8_t subcmd; + uint8_t sig_type; + uint8_t reserved[2]; + uint32_t total_len; + uint32_t total_crc; + uint32_t data_len; + uint32_t offset; + uint8_t signature[SWITCHTEC_SIG_LEN]; + } cmd = {}; + + cmd.subcmd = MRPC_GEN6_DBG_UNLOCK_SIG; + cmd.sig_type = KMT_SIG_FORMAT_RSA4KSHA2; + memcpy(cmd.signature, signature->signature, SWITCHTEC_SIG_LEN); + cmd.total_len = htole32(SWITCHTEC_SIG_LEN); + cmd.total_crc = htole32(crc32(cmd.signature, SWITCHTEC_SIG_LEN, 0, 1, 1)); + cmd.data_len = htole32(SWITCHTEC_SIG_LEN); + cmd.offset = htole32(0); + + return switchtec_mfg_cmd(dev, cmd_id, &cmd, sizeof(cmd), NULL, 0); +} + /** * @brief Unlock firmware debug features * @param[in] dev Switchtec device handle @@ -1201,9 +1251,36 @@ static int dbg_unlock_send_pubkey(struct switchtec_dev *dev, int switchtec_dbg_unlock(struct switchtec_dev *dev, uint32_t serial, uint32_t ver_sec_unlock, struct switchtec_pubkey *public_key, - struct switchtec_signature *signature) + struct switchtec_signature *signature, + struct switchtec_gen6_token *token) { int ret; + + if (switchtec_is_gen6(dev)) + { + struct unlock_cmd_gen6 { + uint8_t subcmd; + uint8_t rsvd[3]; + uint8_t token[SWITCHTEC_GEN6_TOKEN_LEN]; + } cmd = {}; + + uint32_t cmd_id; + cmd_id = MRPC_DBG_UNLOCK_GEN6; + + ret = dbg_unlock_send_pubkey_gen6(dev, public_key, cmd_id); + if (ret) + return ret; + + ret = dbg_unlock_send_sig_gen6(dev, signature, cmd_id); + if (ret) + return ret; + + cmd.subcmd = MRPC_GEN6_DBG_UNLOCK_STATIC; + memcpy(cmd.token, token->token, SWITCHTEC_GEN6_TOKEN_LEN); + + return switchtec_mfg_cmd(dev, cmd_id, &cmd, sizeof(cmd), NULL, 0); + } + struct unlock_cmd { uint8_t subcmd; uint8_t rsvd[3]; @@ -1227,6 +1304,49 @@ int switchtec_dbg_unlock(struct switchtec_dev *dev, uint32_t serial, return switchtec_mfg_cmd(dev, cmd_id, &cmd, sizeof(cmd), NULL, 0); } +/** + * @brief Get gen6 token + * @param[in] dev Switchtec device handle + * @param[in] token pointer to downloaded token + * @param[in] token_type type of token to download + * + * @return 0 on success, error code on failure + */ +int switchtec_dbg_unlock_get_token_gen6(struct switchtec_dev *dev, + struct switchtec_gen6_token *token, + int token_type) +{ + int ret; + + struct get_unlock_token_cmd_gen6 { + uint8_t subcmd; + uint8_t token_type; + uint8_t rsvd[2]; + } cmd = {}; + + uint32_t cmd_id; + cmd_id = MRPC_DBG_UNLOCK_GEN6; + + struct get_unlock_token_reply_gen6 { + uint8_t token[SWITCHTEC_GEN6_TOKEN_LEN]; + } reply; + + cmd.subcmd = MRPC_GEN6_DBG_UNLOCK_TOKEN_GET; + + if(token_type == GEN6_TOKEN_STATIC) + cmd.token_type = SECURE_TOKEN_GET_TYPE_STATIC; + else + cmd.token_type = SECURE_TOKEN_GET_TYPE_EPHEMERAL; + + ret = switchtec_mfg_cmd(dev, cmd_id, &cmd, sizeof(cmd), &reply, + sizeof(reply)); + if (ret) + return ret; + + memcpy(&token->token, &reply, SWITCHTEC_GEN6_TOKEN_LEN); + return 0; +} + /** * @brief Update firmware debug secure unlock version number * @param[in] dev Switchtec device handle @@ -1748,6 +1868,23 @@ int switchtec_read_signature_file(FILE *sig_file, return 0; } +/** + * @brief Read token data from token file + * @param[in] tkn_file Token file + * @param[out] token Token data + * @return 0 on success, error code on failure + */ +int switchtec_read_token_file(FILE *tkn_file, struct switchtec_gen6_token *token) +{ + ssize_t rlen; + + rlen = fread(token->token, 1, SWITCHTEC_GEN6_TOKEN_LEN, tkn_file); + if (rlen < SWITCHTEC_GEN6_TOKEN_LEN) + return -EBADF; + + return 0; +} + /** * @brief Read UDS data from UDS file * @param[in] uds_file UDS file diff --git a/lib/platform/gasops.c b/lib/platform/gasops.c index 5b998e4f..a1e73ff6 100644 --- a/lib/platform/gasops.c +++ b/lib/platform/gasops.c @@ -67,6 +67,7 @@ static const struct no_retry_struct gasop_noretry_cmds[] = { [MRPC_SECURE_STATE_SET_GEN5] = {1, 0, NULL}, [MRPC_BOOTUP_RESUME_GEN5] = {1, 0, NULL}, [MRPC_DBG_UNLOCK_GEN5] = {1, 0, NULL}, + [MRPC_DBG_UNLOCK_GEN6] = {1, 0, NULL}, [MRPC_FW_TX_GEN5] = {1, 1, fw_toggle_noretry_subcmds}, }; static const int gasop_noretry_cmds_count = sizeof(gasop_noretry_cmds) /