diff --git a/examples/htool.c b/examples/htool.c index 22ec4c7..ff43ada 100644 --- a/examples/htool.c +++ b/examples/htool.c @@ -112,16 +112,65 @@ static int command_show_chipinfo(const struct htool_invocation* inv) { if (!dev) { return -1; } + struct hoth_response_chip_info response; int status = libhoth_chipinfo(dev, &response); if (status != 0) { return -1; } + printf("Chip Info:\n"); - printf("Hardware Identity: 0x%016llx\n", - (unsigned long long)response.hardware_identity); - printf("Hardware Category: %d\n", response.hardware_category); - printf("Info Variant: %lu\n", (unsigned long)response.info_variant); + if (response.version == 0) { + printf("Hardware Identity: 0x%016llx\n", + (unsigned long long)response.data.hoth_device_id.hardware_identity); + printf("Hardware Category: %u\n", + (unsigned)response.data.hoth_device_id.hardware_category); + printf("Info Variant: %lu\n", + (unsigned long)response.data.hoth_device_id.info_variant); + return 0; + } + + if (response.version != 1) { + printf("Unsupported chipinfo version: %u\n", response.version); + return -1; + } + + uint8_t* id = response.data.open_titan_device_id; + struct opentitan_device_id parsed_id; + if (parse_opentitan_device_id(id, &parsed_id) != 0) { + printf("Failed to parse OpenTitan Device ID\n"); + return -1; + } + + printf("OpenTitan iSerial: 0x"); + for (int i = 0; i < 32; i++) { + printf("%02x", id[i]); + } + printf("\nOpenTitan Device ID: 0x"); + for (int i = 20; i < 32; i++) { + printf("%02x", id[i]); + } + printf("\n\nGeneric Identifier:\n"); + + printf(" %-22s 0x%04x\n", "SI Creator ID:", parsed_id.creator_id); + printf(" %-22s 0x%04x\n", "Product ID:", parsed_id.product_id); + printf(" %-22s %u week %02u\n", "Device Date:", parsed_id.device_year, + parsed_id.device_week); + printf(" %-22s %u\n", "Lot Number:", parsed_id.lot_number); + printf(" %-22s %u\n", "Wafer Number:", parsed_id.wafer_number); + printf(" %-22s %u\n", "Wafer X Coord:", parsed_id.wafer_x); + printf(" %-22s %u\n", "Wafer Y Coord:", parsed_id.wafer_y); + printf(" %-22s 0x%02x\n", "Reserved DIN:", parsed_id.reserved_din); + printf(" %-22s 0x%08x\n", "Reserved:", parsed_id.reserved); + printf("\nSKU-specific Identifier:\n"); + printf(" %-22s %u\n", "Package ID:", parsed_id.package_id); + printf(" %-22s %u\n", "AST Config Version:", parsed_id.ast_config_version); + printf(" %-22s \"%s\"\n", "OTP ID:", parsed_id.otp_id); + printf(" %-22s %u\n", "OTP Version:", parsed_id.otp_version); + printf(" %-22s \"%s\"\n", "SKU ID String:", parsed_id.sku_id_string); + printf(" %-22s %u\n", + "SKU Specific Version:", parsed_id.sku_specific_version); + return 0; } @@ -215,6 +264,11 @@ static int command_authz_host_command_build( fprintf(stderr, "Failed to get chip ID. status=%d\n", status); return -1; } + if (chipinfo_resp.version != 0) { + fprintf(stderr, "Unsupported chipinfo version: %u\n", + chipinfo_resp.version); + return -1; + } struct hoth_authorized_command_get_nonce_response nonce_resp; status = libhoth_hostcmd_exec( @@ -227,8 +281,8 @@ static int command_authz_host_command_build( } struct hoth_authorized_command_request request = authz_command_build_request( - chipinfo_resp.hardware_identity, opcode, nonce_resp.supported_key_info, - nonce_resp.nonce); + chipinfo_resp.data.hoth_device_id.hardware_identity, opcode, + nonce_resp.supported_key_info, nonce_resp.nonce); authz_command_print_request(&request); return 0; } diff --git a/protocol/authz_record.c b/protocol/authz_record.c index 78205eb..5ecccac 100644 --- a/protocol/authz_record.c +++ b/protocol/authz_record.c @@ -53,8 +53,15 @@ int libhoth_authz_record_build(struct libhoth_device* dev, if (status != 0) { return -1; } - record->dev_id_0 = chipinfo_resp.hardware_identity & 0xfffffffful; - record->dev_id_1 = (chipinfo_resp.hardware_identity >> 32); + if (chipinfo_resp.version != 0) { + fprintf(stderr, "Unsupported chipinfo version: %u\n", + chipinfo_resp.version); + return -1; + } + record->dev_id_0 = + chipinfo_resp.data.hoth_device_id.hardware_identity & 0xfffffffful; + record->dev_id_1 = + (chipinfo_resp.data.hoth_device_id.hardware_identity >> 32); struct hoth_authz_record_get_nonce_response nonce_resp; status = libhoth_hostcmd_exec( diff --git a/protocol/authz_record_test.cc b/protocol/authz_record_test.cc index 1416e73..e8f0634 100644 --- a/protocol/authz_record_test.cc +++ b/protocol/authz_record_test.cc @@ -77,9 +77,9 @@ TEST_F(LibHothTest, authz_read_test) { } TEST_F(LibHothTest, authz_build_test) { - struct hoth_response_chip_info chipinfo = {}; - chipinfo.hardware_identity = 0xABCD; - chipinfo.hardware_identity |= (0x1234UL << 32); + struct libhoth_hoth_device_id hoth_data = {}; + hoth_data.hardware_identity = 0xABCD; + hoth_data.hardware_identity |= (0x1234UL << 32); struct hoth_authz_record_get_nonce_response nonce_resp = {}; nonce_resp.ro_supported_key_id = 1; @@ -98,7 +98,7 @@ TEST_F(LibHothTest, authz_build_test) { EXPECT_CALL(mock_, receive) .WillOnce( - DoAll(CopyResp(&chipinfo, sizeof(chipinfo)), Return(LIBHOTH_OK))) + DoAll(CopyResp(&hoth_data, sizeof(hoth_data)), Return(LIBHOTH_OK))) .WillOnce( DoAll(CopyResp(&nonce_resp, sizeof(nonce_resp)), Return(LIBHOTH_OK))); @@ -115,9 +115,9 @@ TEST_F(LibHothTest, authz_build_test) { } TEST_F(LibHothTest, authz_build_fail_test) { - struct hoth_response_chip_info chipinfo = {}; - chipinfo.hardware_identity = 0xABCD; - chipinfo.hardware_identity |= (0x1234UL << 32); + struct libhoth_hoth_device_id hoth_data = {}; + hoth_data.hardware_identity = 0xABCD; + hoth_data.hardware_identity |= (0x1234UL << 32); // key_id supported by RO and RW. These key_id's are expected to match one // another to successfully program an authorization record. key_id == 0 should @@ -140,7 +140,7 @@ TEST_F(LibHothTest, authz_build_fail_test) { EXPECT_CALL(mock_, receive) .WillOnce( - DoAll(CopyResp(&chipinfo, sizeof(chipinfo)), Return(LIBHOTH_OK))) + DoAll(CopyResp(&hoth_data, sizeof(hoth_data)), Return(LIBHOTH_OK))) .WillOnce( DoAll(CopyResp(&nonce_resp, sizeof(nonce_resp)), Return(LIBHOTH_OK))); @@ -151,9 +151,9 @@ TEST_F(LibHothTest, authz_build_fail_test) { } TEST_F(LibHothTest, authz_mismatch_key_id_test) { - struct hoth_response_chip_info chipinfo = {}; - chipinfo.hardware_identity = 0xABCD; - chipinfo.hardware_identity |= (0x1234UL << 32); + struct libhoth_hoth_device_id hoth_data = {}; + hoth_data.hardware_identity = 0xABCD; + hoth_data.hardware_identity |= (0x1234UL << 32); // key_id supported by RO and RW. These key_id's are expected to match one // another to successfully program an authorization record. key_id == 0 should @@ -176,7 +176,7 @@ TEST_F(LibHothTest, authz_mismatch_key_id_test) { EXPECT_CALL(mock_, receive) .WillOnce( - DoAll(CopyResp(&chipinfo, sizeof(chipinfo)), Return(LIBHOTH_OK))) + DoAll(CopyResp(&hoth_data, sizeof(hoth_data)), Return(LIBHOTH_OK))) .WillOnce( DoAll(CopyResp(&nonce_resp, sizeof(nonce_resp)), Return(LIBHOTH_OK))); @@ -187,9 +187,9 @@ TEST_F(LibHothTest, authz_mismatch_key_id_test) { } TEST_F(LibHothTest, authz_nonce_fail_test) { - struct hoth_response_chip_info chipinfo = {}; - chipinfo.hardware_identity = 0xABCD; - chipinfo.hardware_identity |= (0x1234UL << 32); + struct libhoth_hoth_device_id hoth_data = {}; + hoth_data.hardware_identity = 0xABCD; + hoth_data.hardware_identity |= (0x1234UL << 32); struct hoth_authz_record_get_nonce_response nonce_resp = {}; nonce_resp.ro_supported_key_id = 0; @@ -208,7 +208,7 @@ TEST_F(LibHothTest, authz_nonce_fail_test) { EXPECT_CALL(mock_, receive) .WillOnce( - DoAll(CopyResp(&chipinfo, sizeof(chipinfo)), Return(LIBHOTH_OK))) + DoAll(CopyResp(&hoth_data, sizeof(hoth_data)), Return(LIBHOTH_OK))) .WillOnce( DoAll(CopyResp(&nonce_resp, sizeof(nonce_resp)), Return(LIBHOTH_OK))); diff --git a/protocol/chipinfo.c b/protocol/chipinfo.c index 0effd35..fa7a10b 100644 --- a/protocol/chipinfo.c +++ b/protocol/chipinfo.c @@ -14,11 +14,69 @@ #include "chipinfo.h" +#include + #include "host_cmd.h" int libhoth_chipinfo(struct libhoth_device* dev, struct hoth_response_chip_info* chipinfo) { - return libhoth_hostcmd_exec( + uint8_t resp_buf[32]; // Max size for new format + size_t resp_size; + + int ret = libhoth_hostcmd_exec( dev, HOTH_CMD_BOARD_SPECIFIC_BASE + HOTH_PRV_CMD_HOTH_CHIP_INFO, - /*version=*/0, NULL, 0, chipinfo, sizeof(*chipinfo), NULL); + /*version=*/0, NULL, 0, resp_buf, sizeof(resp_buf), &resp_size); + + if (ret != 0) { + return ret; + } + + if (resp_size == 16) { + // Old format: structured data + chipinfo->version = 0; + memcpy(&chipinfo->data.hoth_device_id, resp_buf, 16); + } else if (resp_size == 32) { + // New format: OpenTitan Device ID + chipinfo->version = 1; + memcpy(chipinfo->data.open_titan_device_id, resp_buf, 32); + } else { + // Unexpected size + return HTOOL_ERROR_HOST_COMMAND_START + HOTH_RES_INVALID_PARAM; + } + + return 0; +} + +int parse_opentitan_device_id(const uint8_t* src, + struct opentitan_device_id* dst) { + if (src == NULL || dst == NULL) { + return -1; + } + + dst->creator_id = (uint16_t)(src[31]) | (src[30] << 8); + dst->product_id = (uint16_t)(src[29]) | (src[28] << 8); + dst->device_year = 2020 + (src[27] & 0x0F); + dst->device_week = (src[26] & 0x0F) * 10 + (src[27] >> 4); + dst->lot_number = + (src[25] >> 4) * 100 + (src[25] & 0x0F) * 10 + (src[26] >> 4); + dst->wafer_number = (src[24] >> 4) * 10 + (src[24] & 0x0F); + dst->wafer_x = + (src[22] & 0x0F) * 100 + (src[23] >> 4) * 10 + (src[23] & 0x0F); + dst->wafer_y = (src[21] >> 4) * 100 + (src[21] & 0x0F) * 10 + (src[22] >> 4); + dst->reserved_din = src[20]; + dst->reserved = (src[16] << 24) | (src[17] << 16) | (src[18] << 8) | src[19]; + dst->package_id = src[15]; + dst->ast_config_version = src[14]; + dst->otp_id[0] = src[12]; + dst->otp_id[1] = src[13]; + dst->otp_id[2] = '\0'; + dst->otp_version = src[11]; + dst->sku_id_string[0] = src[4]; + dst->sku_id_string[1] = src[5]; + dst->sku_id_string[2] = src[6]; + dst->sku_id_string[3] = src[7]; + dst->sku_id_string[4] = '\0'; + dst->sku_specific_version = src[0]; + + return 0; } diff --git a/protocol/chipinfo.h b/protocol/chipinfo.h index 051898b..a71bf68 100644 --- a/protocol/chipinfo.h +++ b/protocol/chipinfo.h @@ -24,13 +24,44 @@ extern "C" { #include "transports/libhoth_device.h" #define HOTH_PRV_CMD_HOTH_CHIP_INFO 0x0010 - -struct hoth_response_chip_info { +struct libhoth_hoth_device_id { uint64_t hardware_identity; uint16_t hardware_category; uint16_t reserved0; uint32_t info_variant; -} __attribute__((packed, aligned(4))); +}; + +struct opentitan_device_id { + uint16_t creator_id; + uint16_t product_id; + uint16_t device_year; + uint8_t device_week; + uint16_t lot_number; + uint8_t wafer_number; + uint16_t wafer_x; + uint16_t wafer_y; + uint8_t reserved_din; + uint32_t reserved; + uint8_t package_id; + uint8_t ast_config_version; + char otp_id[3]; + uint8_t otp_version; + char sku_id_string[5]; + uint8_t sku_specific_version; +}; + +int parse_opentitan_device_id(const uint8_t* src, + struct opentitan_device_id* dst); + +struct hoth_response_chip_info { + uint32_t version; // 0: old format, 1: OpenTitan + uint32_t reserved; + union { + struct libhoth_hoth_device_id hoth_device_id; // Old format (16 bytes) + uint8_t open_titan_device_id[32]; // New format (32 bytes) - OpenTitan + // Device ID + } data; +}; int libhoth_chipinfo(struct libhoth_device* dev, struct hoth_response_chip_info* chipinfo); diff --git a/protocol/chipinfo_test.cc b/protocol/chipinfo_test.cc index ae139b7..11d91a1 100644 --- a/protocol/chipinfo_test.cc +++ b/protocol/chipinfo_test.cc @@ -23,12 +23,39 @@ using ::testing::_; using ::testing::DoAll; using ::testing::Return; -TEST_F(LibHothTest, chipinfo_test) { - struct hoth_response_chip_info chipinfo_exp = {}; +TEST_F(LibHothTest, hoth_chipinfo_test) { + struct libhoth_hoth_device_id hoth_data = {}; + hoth_data.hardware_identity = 0xABCD1234; + hoth_data.hardware_category = 1234; + hoth_data.info_variant = 2; - chipinfo_exp.hardware_identity = 0xABCD1234; - chipinfo_exp.hardware_category = 1234; - chipinfo_exp.info_variant = 2; + EXPECT_CALL(mock_, send(_, + UsesCommand(HOTH_CMD_BOARD_SPECIFIC_BASE + + HOTH_PRV_CMD_HOTH_CHIP_INFO), + _)) + .WillOnce(Return(LIBHOTH_OK)); + + EXPECT_CALL(mock_, receive) + .WillOnce( + DoAll(CopyResp(&hoth_data, sizeof(hoth_data)), Return(LIBHOTH_OK))); + + struct hoth_response_chip_info chipinfo; + EXPECT_EQ(libhoth_chipinfo(&hoth_dev_, &chipinfo), LIBHOTH_OK); + + EXPECT_EQ(chipinfo.version, 0); + EXPECT_EQ(chipinfo.data.hoth_device_id.hardware_identity, + hoth_data.hardware_identity); + EXPECT_EQ(chipinfo.data.hoth_device_id.hardware_category, + hoth_data.hardware_category); + EXPECT_EQ(chipinfo.data.hoth_device_id.info_variant, hoth_data.info_variant); +} + +TEST_F(LibHothTest, opentitan_chipinfo_test) { + uint8_t opentitan_data[32]; + memcpy(opentitan_data, + "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF\x00" + "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\x00", + 32); EXPECT_CALL(mock_, send(_, UsesCommand(HOTH_CMD_BOARD_SPECIFIC_BASE + @@ -37,13 +64,62 @@ TEST_F(LibHothTest, chipinfo_test) { .WillOnce(Return(LIBHOTH_OK)); EXPECT_CALL(mock_, receive) - .WillOnce(DoAll(CopyResp(&chipinfo_exp, sizeof(chipinfo_exp)), + .WillOnce(DoAll(CopyResp(opentitan_data, sizeof(opentitan_data)), Return(LIBHOTH_OK))); struct hoth_response_chip_info chipinfo; EXPECT_EQ(libhoth_chipinfo(&hoth_dev_, &chipinfo), LIBHOTH_OK); - EXPECT_EQ(chipinfo_exp.hardware_identity, chipinfo.hardware_identity); - EXPECT_EQ(chipinfo_exp.hardware_category, chipinfo.hardware_category); - EXPECT_EQ(chipinfo_exp.info_variant, chipinfo.info_variant); + EXPECT_EQ(chipinfo.version, 1); + EXPECT_EQ(memcmp(chipinfo.data.open_titan_device_id, opentitan_data, 32), 0); +} + +TEST(ChipInfoTest, ParseOpenTitanDeviceId) { + uint8_t data[32] = {0}; + data[31] = 0x34; // creator_id low + data[30] = 0x12; // creator_id high -> 0x1234 + data[29] = 0x78; // product_id low + data[28] = 0x56; // product_id high -> 0x5678 + data[27] = 0x55; // year low nibble = 5 -> 2025, week low nibble = 5 + data[26] = 0x34; // lot high = 3, week high nibble = 4 -> week 45, lot = 123 + data[25] = 0x12; // lot low = 1, 2 -> lot = 123 + data[24] = 0x67; // wafer = 67 + data[23] = 0x89; // wafer_x = 89 (if high nibble of 22 is 0) + data[22] = 0x01; // wafer_x high = 1, wafer_y low = 0 + data[21] = 0x23; // wafer_y high = 2, lot low = 3 -> wafer_y = 230 + data[20] = 0xAA; // reserved_din + data[16] = 0x11; + data[17] = 0x22; + data[18] = 0x33; + data[19] = 0x44; + data[15] = 0xBB; // package_id + data[14] = 0xCC; // ast_config_version + data[13] = 'B'; // otp_id[1] + data[12] = 'A'; // otp_id[0] + data[11] = 0xDD; // otp_version + data[7] = '4'; // sku_id[3] + data[6] = '3'; // sku_id[2] + data[5] = '2'; // sku_id[1] + data[4] = '1'; // sku_id[0] + data[0] = 0xEE; // sku_specific_version + + struct opentitan_device_id parsed; + ASSERT_EQ(parse_opentitan_device_id(data, &parsed), 0); + + EXPECT_EQ(parsed.creator_id, 0x1234); + EXPECT_EQ(parsed.product_id, 0x5678); + EXPECT_EQ(parsed.device_year, 2025); + EXPECT_EQ(parsed.device_week, 45); + EXPECT_EQ(parsed.lot_number, 123); + EXPECT_EQ(parsed.wafer_number, 67); + EXPECT_EQ(parsed.wafer_x, 189); + EXPECT_EQ(parsed.wafer_y, 230); + EXPECT_EQ(parsed.reserved_din, 0xAA); + EXPECT_EQ(parsed.reserved, 0x11223344); + EXPECT_EQ(parsed.package_id, 0xBB); + EXPECT_EQ(parsed.ast_config_version, 0xCC); + EXPECT_STREQ(parsed.otp_id, "AB"); + EXPECT_EQ(parsed.otp_version, 0xDD); + EXPECT_STREQ(parsed.sku_id_string, "1234"); + EXPECT_EQ(parsed.sku_specific_version, 0xEE); }