diff --git a/migcvm-agent/CMakeLists.txt b/migcvm-agent/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ddaf1dcdb4021b97c77828c42042945726ba3089 --- /dev/null +++ b/migcvm-agent/CMakeLists.txt @@ -0,0 +1,46 @@ +cmake_minimum_required(VERSION 3.5.1) +project(migcvm-agent) + +set(CMAKE_C_FLAGS "-fPIE ${CMAKE_C_FLAGS}") + +add_subdirectory(src/attest) +add_subdirectory(src/migcvm_tsi) + +set(SOURCES src/migcvm-agent.c + src/socket/host_socket_agent.c) + +add_executable(${PROJECT_NAME} ${SOURCES}) + +target_include_directories(${PROJECT_NAME} PRIVATE + rats-tls/src/include + inc/attest/ + inc/socket/ + inc/migcvm_tsi +) + +target_link_libraries(${PROJECT_NAME} PRIVATE + attest + migcvm-tsi +) + +add_executable(tsi-controller + debug/tsi-controller.c) + +target_include_directories(tsi-controller PRIVATE + inc/migcvm_tsi + rats-tls/src/include) + +target_link_libraries(tsi-controller PRIVATE migcvm-tsi) + + +add_executable(socket-tool + debug/socket-tool.c) + +target_include_directories(socket-tool PRIVATE + inc/socket + rats-tls/src/include) + +target_link_libraries(socket-tool PRIVATE) + +install(TARGETS ${PROJECT_NAME} tsi-controller socket-tool + DESTINATION ${RATS_TLS_INSTALL_BIN_PATH}) diff --git a/migcvm-agent/README.md b/migcvm-agent/README.md new file mode 100644 index 0000000000000000000000000000000000000000..329c03a3de1375cad2213212815263e7f28019d9 --- /dev/null +++ b/migcvm-agent/README.md @@ -0,0 +1,165 @@ +# MIGVM Agent + +## +1. 下载安装编译依赖 +yum install virtCCA_sdk virtCCA_sdk-devel + +QCBOR +```bash +git clone https://github.com/laurencelundblade/QCBOR.git -b v1.2 +cd QCBOR +make +make install +cd - +``` + +t_cose +```bash +git clone https://github.com/laurencelundblade/t_cose.git -b v1.1.2 +cd t_cose +cmake -S . -B build -DCRYPTO_PROVIDER=OpenSSL +cmake --build build +cmake --install build +cd - +``` + +libcbor +```bash +git clone https://github.com/PJK/libcbor.git +cd libcbor +cmake -S . -B build +cmake --build build +cmake --install build +cd - +``` + +rats-tls(该仓库须放置于当前migcvm-agent目录下) +```bash +git clone https://github.com/inclavare-containers/rats-tls.git +cd rats-tls +git reset --hard 40f7b78403d75d13b1a372c769b2600f62b02692 +git apply ../../attestation/rats-tls/*.patch +bash build.sh -s -r -c -v gcc +cp -rf output/lib/rats-tls /usr/lib/ +cp -rfL output/lib/rats-tls/librats_tls.so.0 /lib64/ +cd - +``` + +2. 编译 +```bash +chmod +x build.sh +./build.sh +``` + +3. 部署 + +将以下编译产物部署至CVM +```bash +cp build/migcvm-agent ${CVM_PATH}/home/ +``` + +4. 运行 +源机密虚机 +```bash +./migcvm-agent +``` + +目的机密虚机 +```bash +./migcvm-agent -r ${RIM} +``` + +宿主机 +```bash +cd build +./socket-tool -c ${SRC_CID} -p ${SRC_VSOCK_PORT} -t 1 -i ${SRC_IP} +./socket-tool -c ${DST_CID} -p ${DST_VSOCK_PORT} -t 2 -i ${SRC_IP} +``` + +## +``` +. + CMakeLists.txt # CMake + build.sh # + debug/ # + socket-tool.c # + tsi-test.c # TSI + migcvm_tsi/ # TSI + migcvm_tsi.c + tsi.h + src/ # migcvm-agent + xxx +``` + +## +DH +1. xxxxxx + +## +- CMake (>= 3.10) +- GNU Make +- C (GCCClang) + +--- + +# MIGVM Agent + +## Overview +The MIGVM Agent is a virtual machine migration agent that provides socket communication and TSI (Trusted Service Interface) capabilities. + +## Build Instructions +1. Clone the repository +2. Run the build script: +```bash +chmod +x build.sh +./build.sh +``` + +The executable will be generated in the `build` directory as `migvm_agent`. + +### Debug Mode +To build in debug mode, add the `--debug` parameter: +```bash +./build.sh --debug +``` + +Debug tool (socket-send) can be built separately: +```bash +./build.sh --build-debug-tool +``` + +## Run Instructions +Execute the agent with: +```bash +./build/migvm_agent +``` + +## Directory Structure +``` +. + CMakeLists.txt # CMake configuration + build.sh # Build automation script + migvm_agent.c # Main application + debug/ # Debug tools + socket-send.c # Socket test tool + tsi-test.c # TSI test tool + migcvm_tsi/ # TSI implementation + migcvm_tsi.c + tsi.h + socket_agent/ # Socket communication + host_socket_agent.c + socket_agent.h +``` + +## Secure Communication +The agent uses DH key exchange to establish secure channels: +1. xxxxxx + +## Dependencies +- CMake (>= 3.10) +- GNU Make +- C compiler (GCC or Clang) + +## License +This project is licensed under the MIT License. + diff --git a/migcvm-agent/build.sh b/migcvm-agent/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..a738eac6bf698a7199ce4f70e98672107840d668 --- /dev/null +++ b/migcvm-agent/build.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +# +BUILD_TYPE="Release" +BUILD_DEBUG_TOOL=false +CLEAN=false + +for arg in "$@"; do + case $arg in + --debug) + BUILD_TYPE="Debug" + ;; + --build-debug-tool) + BUILD_DEBUG_TOOL=true + ;; + --clean) + CLEAN=true + ;; + *) + echo "Unknown argument: $arg" + exit 1 + ;; + esac +done + +# +if [[ $CLEAN == true ]]; then + echo "Cleaning build directory..." + rm -rf build + exit 1 +fi + +# +mkdir -p build +cd build || exit + +# cmake +echo "Running cmake with build type: ${BUILD_TYPE}..." +cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} .. || { + echo "CMake failed" + exit 1 +} + +# +echo "Building project..." +if [[ $BUILD_DEBUG_TOOL == true ]]; then + echo "Building debug tool only..." + make socket-tool tsi-controller || { + echo "Debug tool build failed" + exit 1 + } + echo "Debug tool built successfully! Executable is located at: build/socket-send" +elif [[ $BUILD_TYPE == "Debug" ]]; then + echo "Building in debug mode (both main agent and debug tool)..." + make migcvm-agent socket-tool tsi-controller || { + echo "Build failed" + exit 1 + } + echo "Build successful! Executables are located at: build/migvm_agent and build/socket-send" +else + make migcvm-agent || { + echo "Build failed" + exit 1 + } + echo "Build successful! Executable is located at: build/migvm_agent" +fi + diff --git a/migcvm-agent/debug/socket-tool.c b/migcvm-agent/debug/socket-tool.c new file mode 100644 index 0000000000000000000000000000000000000000..ab57f976b68063d93631f19feea00fb57d89e476 --- /dev/null +++ b/migcvm-agent/debug/socket-tool.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define INT_MAX ((int)(~0U >> 1)) +#define INT_MIN (-INT_MAX - 1) + +#define DEFAULT_TARGET_PORT 9000 +#define MAX_PAYLOAD_SIZE 256 +#define PAYLOAD_TYPE_CHAR 0 + +#pragma pack(push, 1) +struct socket_msg { + char cmd[16]; + unsigned int payload_type; + unsigned int payload_len; + union { + char char_payload[MAX_PAYLOAD_SIZE]; + unsigned long long ull_payload; + } payload; +}; +#pragma pack(pop) + +typedef struct { + unsigned int target_cid; + unsigned int target_port; + int tool_type; + int action; + char *mig_ip; +} socket_tool_args; + +static void payload_encode_char(struct socket_msg *msg, const char *in) +{ + msg->payload_type = PAYLOAD_TYPE_CHAR; + strncpy(msg->payload.char_payload, in, MAX_PAYLOAD_SIZE); + msg->payload_len = strlen(in) + 1; +} + +static void payload_encode(struct socket_msg *msg, const char *in) +{ + payload_encode_char(msg, in); +} + +static void payload_decode(const struct socket_msg *msg, char *out) +{ + if (msg->payload_type == PAYLOAD_TYPE_CHAR) { + strncpy(out, msg->payload.char_payload, msg->payload_len); + } +} + +static int parse_int(const char *str) +{ + char *endptr; + errno = 0; + if (str == NULL) { + fprintf(stderr, "str is null\n"); + } + long val = strtol(str, &endptr, 10); + if (errno == ERANGE || val > INT_MAX || val < INT_MIN) { + fprintf(stderr, "Value out of range\n"); + return -1; + } + if (*endptr != '\0') { + fprintf(stderr, "Invalid input: non-numeric character detected\n"); + return -1; + } + return (int) val; +} + +static int parse_input_socket_tool_args(int argc, char **argv, socket_tool_args *args) +{ + int opt; + char *rim = NULL; + char *const short_options = "c:p:t:i:"; + struct option long_options[] = { + { "cid", required_argument, NULL, 'c' }, + { "port", required_argument, NULL, 'p' }, + { "type", required_argument, NULL, 't' }, + { "mig-ip", required_argument, NULL, 'i' }, + { 0, 0, 0, 0 } + }; + + args->target_cid = 0; + args->target_port = DEFAULT_TARGET_PORT; + args->tool_type = 0; + + do { + opt = getopt_long(argc, argv, short_options, long_options, NULL); + switch (opt) { + case 'c': + args->target_cid = (unsigned int)parse_int(optarg); + break; + case 'p': + args->target_port = (unsigned int)parse_int(optarg); + break; + case 't': + args->action = parse_int(optarg); + break; + case 'i': + args->mig_ip = optarg; + break; + case -1: + break; + default: + puts("This is a socket-tool"); + puts(" Usage:\n\n" + " socket-tool [arguments]\n\n" + " Options:\n\n" + " --cid/-c value set the cid of socket\n" + " --port/-p value set the port of socket\n" + " --type/-t value set the type of target mig agent, server 1/agent 2\n" + " --mig-ip/-i value set the target migcvm ip\n"); + return -1; + } + } while (opt != -1); + + return 0; +} + +#define SERVER_RATS_TLS 1 +#define CLIENT_RATS_TLS 2 +static int vsock_send(socket_tool_args *args) +{ + int sockfd; + struct socket_msg msg; + struct sockaddr_vm sa = { + .svm_family = AF_VSOCK, + .svm_cid = args->target_cid, + .svm_port = args->target_port + }; + + /* setup socket */ + sockfd = socket(AF_VSOCK, SOCK_STREAM, 0); + if (sockfd < 0) { + perror("socket creation failed"); + exit(EXIT_FAILURE); + } + printf("Connected to VSOCK service at CID=%d, Port=%d\n", sa.svm_cid, sa.svm_port); + /* connect server */ + if (connect(sockfd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { + perror("connect failed"); + char error_message[256] = {0}; + strerror_r(errno, error_message, sizeof(error_message)); + printf("failed: %s (errno=%d)\n", error_message, errno); + close(sockfd); + exit(EXIT_FAILURE); + } + memset(&msg, 0, sizeof(msg)); + /* send msg */ + switch (args->action) { + case SERVER_RATS_TLS: + strncpy(msg.cmd, "s", sizeof(msg.cmd)); + payload_encode(&msg, args->mig_ip); + break; + case CLIENT_RATS_TLS: + strncpy(msg.cmd, "c", sizeof(msg.cmd)); + payload_encode(&msg, args->mig_ip); + break; + default: + printf("Unknown action: %d\n", args->action); + close(sockfd); + return -1; + } + + printf("Message sent: cmd = %s payload = %s\n", msg.cmd, msg.payload); + if (send(sockfd, &msg, sizeof(msg), 0) < 0) { + perror("send failed"); + close(sockfd); + exit(EXIT_FAILURE); + } + + close(sockfd); + return 0; +} + +int main(int argc, char **argv) +{ + socket_tool_args input_args = {0}; + if (parse_input_socket_tool_args(argc, argv, &input_args)) { + printf("Error parsing input args.\n"); + return 1; + } + + printf("cid: %u port:%u tool_type:%d\n", input_args.target_cid, input_args.target_port, input_args.tool_type); + return vsock_send(&input_args); +} + diff --git a/migcvm-agent/debug/tsi-controller.c b/migcvm-agent/debug/tsi-controller.c new file mode 100644 index 0000000000000000000000000000000000000000..191e2f98496ebe4a7aae0a6bfa56b36f5a8c78e8 --- /dev/null +++ b/migcvm-agent/debug/tsi-controller.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "migcvm_tsi.h" + +#define MIN_REQUIRED_ARGS 4 +#define GUEST_RD_BASE 16 +#define GET_MIGRATION_INFO 1 +#define BIND_MIGRATION_SLOT 2 +#define SET_MIGRATION_SLOT 3 +#define GET_MIGRATION_BINDED_RDS 4 + +static int switch_request_function(tsi_ctx *ctx, int function_to_test, + migration_info_t *attest_info, + virtcca_mig_info_t *migvm_info) +{ + int ret; + switch (function_to_test) { + case GET_MIGRATION_INFO: + printf("Testing get_migration_info...\n"); + ret = get_migration_info_and_mask(ctx, migvm_info, attest_info); + if (ret == TSI_SUCCESS) { + printf("get_migration_info succeeded\n"); + } else { + printf("get_migration_info failed with error: 0x%08x\n", ret); + tsi_free_ctx(ctx); + return -1; + } + break; + + case BIND_MIGRATION_SLOT: + // Parse rd value if provided + printf("Testing set_migration_bind_slot with guest_rd=0x%llx...\n", migvm_info->guest_rd); + attest_info->slot_status = SLOT_IS_BINDED; + attest_info->set_key = false; + ret = set_migration_bind_slot_and_mask(ctx, migvm_info, attest_info); + if (ret == TSI_SUCCESS) { + printf("set_migration_bind_slot succeeded\n"); + } else { + printf("set_migration_bind_slot failed with error: 0x%08x\n", ret); + tsi_free_ctx(ctx); + return -1; + } + break; + + case SET_MIGRATION_SLOT: + // Parse rd value if provided + printf("Testing set_migration_bind_slot with guest_rd=0x%llx...\n", migvm_info->guest_rd); + attest_info->slot_status = SLOT_IS_READY; + attest_info->set_key = true; + ret = set_migration_bind_slot_and_mask(ctx, migvm_info, attest_info); + if (ret == TSI_SUCCESS) { + printf("set_migration_bind_slot succeeded\n"); + } else { + printf("set_migration_bind_slot failed with error: 0x%08x\n", ret); + tsi_free_ctx(ctx); + return -1; + } + break; + + case GET_MIGRATION_BINDED_RDS: + printf("Testing get_migration_binded_rds...\n"); + ret = get_migration_binded_rds(ctx, migvm_info, attest_info); + if (ret == TSI_SUCCESS) { + printf("get_migration_binded_rds succeeded\n"); + } else { + printf("get_migration_binded_rds failed with error: 0x%08x\n", ret); + tsi_free_ctx(ctx); + return -1; + } + break; + default: + printf("Invalid function selection: %d\n", function_to_test); + tsi_free_ctx(ctx); + return -1; + } + return ret; +} + +int main(int argc, char *argv[]) +{ + tsi_ctx *ctx = NULL; + virtcca_mig_info_t *migvm_info = NULL; + migration_info_t *attest_info = NULL; + pending_guest_rd_t *pending_guest_rds = NULL; + int ret; + unsigned long long guest_rd = 0; + unsigned long long msk_value = 0; + int function_to_test = 0; /* 1 for get_migration_info, 2 for set_migration_bind_slot */ + + /* Parse command line arguments */ + if (argc < MIN_REQUIRED_ARGS) { + printf("Usage: %s \n", argv[0]); + printf(" function: 1 for get_migration_info, 2 for set_migration_bind_slot\n"); + printf(" guest_rd: guest_rd value in hex (e.g., 0x20000000)\n"); + printf(" msk: msk value in hex (e.g., 0x111)\n"); + return -1; + } + + function_to_test = atoi(argv[1]); + guest_rd = strtoull(argv[2], NULL, 0); + msk_value = strtoull(argv[3], NULL, 0); + printf("using guest_rd: 0x%llx\n", guest_rd); + printf("using msk: 0x%llx\n", msk_value); + + /* Initialize TSI context */ + ctx = tsi_new_ctx(); + if (ctx == NULL) { + printf("Failed to create TSI context\n"); + return -1; + } + + printf("using guest_rd: 0x%llx\n", guest_rd); + migvm_info = (virtcca_mig_info_t *)malloc(sizeof(virtcca_mig_info_t)); + if (migvm_info == NULL) { + printf("Failed to allocate memory for migvm_info: out of memory\n"); + tsi_free_ctx(ctx); + return -1; + } + migvm_info->guest_rd = guest_rd; + attest_info = (migration_info_t *)malloc(sizeof(migration_info_t)); + pending_guest_rds = (pending_guest_rd_t *)malloc(sizeof(pending_guest_rd_t)); + if (!pending_guest_rds) { + perror("Failed to allocate pending_guest_rds"); + return -1; + } + attest_info->msk[0] = msk_value; + attest_info->pending_guest_rds = pending_guest_rds; + ret = switch_request_function(ctx, function_to_test, attest_info, migvm_info); + free(migvm_info); + free(attest_info); + free(pending_guest_rds); + return ret; +} \ No newline at end of file diff --git a/migcvm-agent/inc/attest/binary_blob.h b/migcvm-agent/inc/attest/binary_blob.h new file mode 100644 index 0000000000000000000000000000000000000000..964e5e5e6f17302bbb8777a1abafa0d18a0167a6 --- /dev/null +++ b/migcvm-agent/inc/attest/binary_blob.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef BINARY_BLOB_H +#define BINARY_BLOB_H + +#include +#include +#include + +#define BYTES_PER_LINE 16 + +typedef struct { + uint8_t* data; + size_t length; + size_t base_address; +} binary_blob_t; + +/* binary data manipulation function */ +bool binary_blob_init(binary_blob_t* blob, uint8_t* data, size_t length, size_t base); +uint16_t binary_blob_get_uint16(const binary_blob_t* blob, size_t* pos); +uint8_t binary_blob_get_uint8(const binary_blob_t* blob, size_t* pos); +uint32_t binary_blob_get_uint32(const binary_blob_t* blob, size_t* pos); +uint64_t binary_blob_get_uint64(const binary_blob_t* blob, size_t* pos); +void binary_blob_get_bytes(const binary_blob_t* blob, size_t* pos, size_t count, uint8_t* out); +void binary_blob_dump(const binary_blob_t* blob); + + +#endif /* BINARY_BLOB_H */ \ No newline at end of file diff --git a/migcvm-agent/inc/attest/common.h b/migcvm-agent/inc/attest/common.h new file mode 100644 index 0000000000000000000000000000000000000000..953d13363c8a41c14cb0d2e5d3cfe5dd4064fbf1 --- /dev/null +++ b/migcvm-agent/inc/attest/common.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include "ima_measure.h" + +#ifndef COMMON_H +#define COMMON_H + +#define CHALLENGE_SIZE (64U) +#define MAX_MEASUREMENT_HEX_SIZE (MAX_MEASUREMENT_SIZE * 2) +#define MAX 4096 +#define MAX_LOG 0x200000 /* 2MB */ +#define PORT 7220 +#define VERIFY_SUCCESS 0 +#define VERIFY_FAILED 1 +#define VERIFY_CONTINUE 2 + +enum MSG_ID { + DEVICE_CERT_MSG_ID = 0x1001, + ATTEST_MSG_ID, + CCEL_ACPI_TABLE_ID, + CCEL_EVENT_LOG_ID, + VERIFY_SUCCESS_MSG_ID, + VERIFY_FAILED_MSG_ID, + VERIFY_REM_MSG_ID +}; + +typedef struct { + uint32_t ip; + uint16_t port; + uint8_t measurement[MAX_MEASUREMENT_SIZE]; + uint32_t meas_len; + uint8_t challenge[CHALLENGE_SIZE]; +} client_args; + +typedef struct { + uint32_t ip; + uint16_t port; +} server_args; + +#endif /* COMMON_H */ diff --git a/migcvm-agent/inc/attest/config.h b/migcvm-agent/inc/attest/config.h new file mode 100644 index 0000000000000000000000000000000000000000..9ffd2921981d6fa2c30c5989ecaa584caeb83e5a --- /dev/null +++ b/migcvm-agent/inc/attest/config.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#include + +typedef struct { + const char* ccel_file; /* CCEL file path */ + const char* event_log_file; /* Event log file path */ + const char* rem_file; /* REM file path */ + const char* json_file; /* JSON reference file path */ +} config_t; + +extern config_t g_config; + +#endif /* CONFIG_H */ \ No newline at end of file diff --git a/migcvm-agent/inc/attest/event_log.h b/migcvm-agent/inc/attest/event_log.h new file mode 100644 index 0000000000000000000000000000000000000000..48aee9e36237a9244c5bb82bd712711d572b2809 --- /dev/null +++ b/migcvm-agent/inc/attest/event_log.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef EVENT_LOG_H +#define EVENT_LOG_H + +#include +#include "rem.h" +#include "binary_blob.h" +#include "hash_defs.h" +#include "utils.h" + +/* SHA256 digest length (32 bytes) */ +#define SHA256_DIGEST_LENGTH 32 + +/* Add hash algorithm definitions */ +#define TPM_ALG_ERROR 0x0 +#define TPM_ALG_RSA 0x1 +#define TPM_ALG_SHA1 0x4 +#define TPM_ALG_SHA256 0xB +#define TPM_ALG_SHA384 0xC +#define TPM_ALG_SHA512 0xD +#define TPM_ALG_ECDSA 0x18 + +/* Algorithm information structure */ +typedef struct { + uint16_t algoid; + uint16_t digestsize; +} algorithm_info_t; + +/* Event type definitions */ +typedef enum { + EV_PREBOOT_CERT = 0x0, + EV_POST_CODE = 0x1, + EV_UNUSED = 0x2, + EV_NO_ACTION = 0x3, + EV_SEPARATOR = 0x4, + EV_ACTION = 0x5, + EV_EVENT_TAG = 0x6, + EV_S_CRTM_CONTENTS = 0x7, + EV_S_CRTM_VERSION = 0x8, + EV_CPU_MICROCODE = 0x9, + EV_PLATFORM_CONFIG_FLAGS = 0xa, + EV_TABLE_OF_DEVICES = 0xb, + EV_COMPACT_HASH = 0xc, + EV_IPL = 0xd, + EV_IPL_PARTITION_DATA = 0xe, + EV_NONHOST_CODE = 0xf, + EV_NONHOST_CONFIG = 0x10, + EV_NONHOST_INFO = 0x11, + EV_OMIT_BOOT_DEVICE_EVENTS = 0x12, + + /* TCG EFI Platform Specification For TPM Family 1.1 or 1.2 */ + EV_EFI_EVENT_BASE = 0x80000000, + EV_EFI_VARIABLE_DRIVER_CONFIG = EV_EFI_EVENT_BASE + 0x1, + EV_EFI_VARIABLE_BOOT = EV_EFI_EVENT_BASE + 0x2, + EV_EFI_BOOT_SERVICES_APPLICATION = EV_EFI_EVENT_BASE + 0x3, + EV_EFI_BOOT_SERVICES_DRIVER = EV_EFI_EVENT_BASE + 0x4, + EV_EFI_RUNTIME_SERVICES_DRIVER = EV_EFI_EVENT_BASE + 0x5, + EV_EFI_GPT_EVENT = EV_EFI_EVENT_BASE + 0x6, + EV_EFI_ACTION = EV_EFI_EVENT_BASE + 0x7, + EV_EFI_PLATFORM_FIRMWARE_BLOB = EV_EFI_EVENT_BASE + 0x8, + EV_EFI_HANDOFF_TABLES = EV_EFI_EVENT_BASE + 0x9, + EV_EFI_VARIABLE_AUTHORITY = EV_EFI_EVENT_BASE + 0xe0 +} event_type_t; + +typedef struct { + uint32_t rem_index; + uint32_t event_type; + uint32_t digest_count; + uint16_t* alg_ids; /* Algorithm ID for each digest */ + uint8_t* digests; /* Data for all digests */ + uint32_t event_size; + uint8_t* event; + uint32_t algorithms_number; /* Number of algorithms */ + algorithm_info_t* algorithms; /* Array of algorithm information */ +} event_log_entry_t; + +typedef struct { + binary_blob_t blob; + size_t log_base; + size_t log_length; + rem_t rems[REM_COUNT]; +} event_log_t; + +/* Event log operation functions */ +bool event_log_init(event_log_t* log, size_t base, size_t length); +bool event_log_process(event_log_t* log); +bool event_log_replay(event_log_t* log); +void event_log_dump(event_log_t* log); + +/* Internal function declarations */ +bool process_event_log_entry(event_log_t* log, size_t* pos, event_log_entry_t* entry); + +#endif /* EVENT_LOG_H */ \ No newline at end of file diff --git a/migcvm-agent/inc/attest/firmware_state.h b/migcvm-agent/inc/attest/firmware_state.h new file mode 100644 index 0000000000000000000000000000000000000000..8a2f8675fc9e56c22572f7d17c28624fba6bab2f --- /dev/null +++ b/migcvm-agent/inc/attest/firmware_state.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef FIRMWARE_STATE_H +#define FIRMWARE_STATE_H + +#include +#include +#include "event_log.h" + +/* EFI state structure */ +typedef struct { + uint8_t* image_hash; + uint32_t image_hash_size; + char* image_path; +} efi_image_t; + +typedef struct { + efi_image_t* images; + uint32_t image_count; +} efi_state_t; + +/* GRUB conf state structure */ +typedef struct { + uint8_t* config_hash; + uint32_t config_hash_size; +} grub_state_t; + +/* Linux kernel state structure */ +typedef struct { + uint8_t* kernel_hash; + uint32_t kernel_hash_size; + uint8_t* initrd_hash; + uint32_t initrd_hash_size; +} linux_kernel_state_t; + +/* Firmware log state structure */ +typedef struct { + efi_state_t* efi; + grub_state_t* grub; + linux_kernel_state_t* linux_kernel; + event_log_entry_t* raw_events; + uint32_t raw_events_count; + uint16_t hash_algo; +} firmware_log_state_t; + +/* JSON parsing state */ +typedef struct kernel_version_data { + char* version; + char* kernel; + char* initramfs; +} kernel_version_t; + +typedef struct { + char* grub; + char* grub_cfg; + kernel_version_t* kernels; + int kernel_count; + char* hash_alg; +} firmware_reference_t; + +/* ACPI table parsing structure */ +typedef struct { + uint8_t revision; + uint8_t checksum; + char oem_id[6]; + uint8_t cc_type; + uint8_t cc_subtype; + uint64_t log_length; + uint64_t log_address; +} acpi_table_info_t; + +/* Function declarations */ +firmware_log_state_t* firmware_log_state_create(event_log_t* log); +void firmware_log_state_free(firmware_log_state_t* state); +bool firmware_log_state_extract(event_log_t* log, firmware_log_state_t* state); +void firmware_log_state_print(const firmware_log_state_t* state); + +#endif /* FIRMWARE_STATE_H */ \ No newline at end of file diff --git a/migcvm-agent/inc/attest/hash_defs.h b/migcvm-agent/inc/attest/hash_defs.h new file mode 100644 index 0000000000000000000000000000000000000000..343fa2043b0050734dabc8817718415e2f0c3bf1 --- /dev/null +++ b/migcvm-agent/inc/attest/hash_defs.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef HASH_DEFS_H +#define HASH_DEFS_H + +#include + +/* Use OpenSSL definitions, but add our own if they don't exist */ +#ifndef SHA1_DIGEST_LENGTH +#define SHA1_DIGEST_LENGTH 20 +#endif +#ifndef SHA256_DIGEST_LENGTH +#define SHA256_DIGEST_LENGTH 32 +#endif +#ifndef SHA512_DIGEST_LENGTH +#define SHA512_DIGEST_LENGTH 64 +#endif + +#endif /* HASH_DEFS_H */ \ No newline at end of file diff --git a/migcvm-agent/inc/attest/ima_measure.h b/migcvm-agent/inc/attest/ima_measure.h new file mode 100644 index 0000000000000000000000000000000000000000..3028d044025c90a7df5cfc2ce5d31476de14a8c2 --- /dev/null +++ b/migcvm-agent/inc/attest/ima_measure.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef IMA_MEASURE_H +#define IMA_MEASURE_H + +#include + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +#define TCG_EVENT_NAME_LEN_MAX 255 +#define IMA_TEMPLATE_FIELD_ID_MAX_LEN 16 +#define IMA_TEMPLATE_NUM_FIELDS_MAX 15 +#define CRYPTO_MAX_ALG_NAME 64 +#define IMA_MAX_HASH_SIZE 64 +#define MAX_CMD_LEN 1024 +#define IMA_TEMPLATE_DATA_MAX_LEN (IMA_MAX_HASH_SIZE + PATH_MAX) +#define OPENSSL_1_1_0 0x10100000L + +#define IMA_TEMPLATE_IMA_NAME "ima" +#define IMA_TEMPLATE_IMA_FMT "d|n" + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) + +#define ERROR_ENTRY_PARSING 1 +#define ERROR_FIELD_NOT_FOUND 2 + +/* server */ +#define MIGCVM_PORT 1234 +#define DEFAULT_IP "0.0.0.0" /* Listern to connections from any ip */ +#define TOKEN "ATTESTATION_PASS" +#define REQUEST_IMA_LOG "REQUEST_IMA_LOG" +#define SERVER_IMA_MEASUREMENTS_PATH "/sys/kernel/security/ima/binary_runtime_measurements" +#define CLIENT_IMA_MEASUREMENTS_PATH "binary_runtime_measurements" +#define IMA_READ_BLCOK_SIZE 1024 +#define ENABLE_FDE_TOKEN "ENABLE_FDE_TOKEN" +#define MAX_PASSWD_LEN 32 +#define MAX 4096 +#define SERVER_CCEL_ACPI_TABLE_PATH "/sys/firmware/acpi/tables/CCEL" +#define SERVER_CCEL_EVENT_LOG_PATH "/sys/firmware/acpi/tables/data/CCEL" +#define KEY_FILE_PATH "/root/rootfs_key.bin" + +/* client */ +#define SHA256_SIZE 32 +#define SHA512_SIZE 64 +#define MAX_MEASUREMENT_SIZE SHA512_SIZE +#define MAX_IMA_LOG_SIZE (1024 * 1024 * 1024) +#define MAX_LOG 0x200000 /* 2MB */ +#define CLIENT_CCEL_ACPI_TABLE_PATH "./ccel.bin" +#define CLIENT_CCEL_EVENT_LOG_PATH "./event_log.bin" +#define HASH_STR_LENGTH 64 + +int ima_measure(const void *reference_pcr_value, size_t reference_pcr_value_len, + char *digest_list_file, int validate, int verify, int target_pcr_index); + +#endif \ No newline at end of file diff --git a/migcvm-agent/inc/attest/platform_verify.h b/migcvm-agent/inc/attest/platform_verify.h new file mode 100644 index 0000000000000000000000000000000000000000..e92350eae5e1d7a746e02f139637cbed7480be40 --- /dev/null +++ b/migcvm-agent/inc/attest/platform_verify.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef PLATFORM_VERIFY_H +#define PLATFORM_VERIFY_H + +#include +#include +#include +#include +#include +#include "token_parse.h" + +/* + * Reference value structure corresponding to measure_value in JSON file + */ +typedef struct { + char firmware_name[64]; /* Firmware name, e.g. "ipu", "imu" */ + char measurement[128]; /* Measurement value in hex string */ + char firmware_version[32]; /* Firmware version, e.g. "21.21.0" */ + char hash_algorithm[16]; /* Hash algorithm, e.g. "sha256" */ +} sw_component_ref_t; + +typedef struct { + sw_component_ref_t *sw_components; /* Software component reference values array */ + size_t sw_comp_count; /* Number of software components */ +} platform_ref_values_t; + +/* + * Load platform reference values from JSON file + * + * @param json_file_path JSON file path + * @param ref_values Output reference values structure + * @return true on success, false on failure + */ +bool load_platform_ref_values(const char *json_file_path, platform_ref_values_t *ref_values); + +/* + * Verify sw-components in platform token + * + * @param platform_claims Platform claims parsed from token + * @param ref_values Reference values + * @return true on verification success, false on verification failure + */ +bool verify_platform_sw_components(const platform_claims_t *platform_claims, + const platform_ref_values_t *ref_values); +void free_platform_ref_values(platform_ref_values_t *ref_values); +#endif /* PLATFORM_VERIFY_H */ \ No newline at end of file diff --git a/migcvm-agent/inc/attest/rem.h b/migcvm-agent/inc/attest/rem.h new file mode 100644 index 0000000000000000000000000000000000000000..25e2e566fc22daa131083168377bb06e7af9d01d --- /dev/null +++ b/migcvm-agent/inc/attest/rem.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef REM_H +#define REM_H + +#include +#include + +#define REM_COUNT 4 +#define REM_LENGTH_BYTES 32 + +typedef struct { + uint8_t data[REM_LENGTH_BYTES]; +} rem_t; + +/* REM operation function */ +bool rem_init(rem_t* rem); +bool rem_compare(const rem_t* rem1, const rem_t* rem2); +void rem_dump(const rem_t* rem); + +#endif /* REM_H */ \ No newline at end of file diff --git a/migcvm-agent/inc/attest/token_parse.h b/migcvm-agent/inc/attest/token_parse.h new file mode 100644 index 0000000000000000000000000000000000000000..08ed12ca336296daf31149c97ae905a5781752f3 --- /dev/null +++ b/migcvm-agent/inc/attest/token_parse.h @@ -0,0 +1,182 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef TOKEN_PARSE_H +#define TOKEN_PARSE_H + +#include "qcbor/qcbor.h" +#include "qcbor/qcbor_spiffy_decode.h" + +#define ATTEST_MAX_TOKEN_SIZE 4096 + +#define TAG_CCA_TOKEN (399) +#define CCA_PLAT_TOKEN (44234) +#define CCA_CVM_TOKEN (44241) + +#define CCA_CVM_CHALLENGE (10) +#define CCA_CVM_PERSONALIZATION_VALUE (44235) +#define CCA_CVM_HASH_ALGO_ID (44236) +#define CCA_CVM_PUB_KEY (44237) +#define CCA_CVM_INITIAL_MEASUREMENT (44238) +#define CCA_CVM_EXTENSIBLE_MEASUREMENTS (44239) +#define CCA_CVM_EXTED_MEAS_SLOTS_NUM (4) +#define CCA_CVM_PUB_KEY_HASH_ALGO_ID (44240) + +/* +/* Platform token related constants +*/ +#define CCA_PLATFORM_PROFILE (265) +#define CCA_PLATFORM_CHALLENGE (10) +#define CCA_PLATFORM_IMPLEMENTATION_ID (2396) +#define CCA_PLATFORM_INSTANCE_ID (256) +#define CCA_PLATFORM_CONFIG (2401) +#define CCA_PLATFORM_LIFESTYLE (2395) +#define CCA_PLATFORM_SW_COMPONENTS (2399) +#define CCA_PLATFORM_VERIFICATION_SERVICE (2400) +#define CCA_PLATFORM_HASH_ALGO_ID (2402) + +#define CCA_PLATFORM_SW_COMPONENT_TYPE (1) +#define CCA_PLATFORM_SW_COMPONENT_MEASUREMENT_VALUE (2) +#define CCA_PLATFORM_SW_COMPONENT_VERSION (4) +#define CCA_PLATFORM_SW_COMPONENT_SIGNER_ID (5) +#define CCA_PLATFORM_SW_COMPONENT_ALGORITHM_ID (6) + +#define CCA_BYTE_SIZE_32 (32) +#define CCA_BYTE_SIZE_48 (48) +#define CCA_BYTE_SIZE_64 (64) + +#define CCA_BYTE_SIZE_33 (33) +#define CCA_BYTE_SIZE_65 (65) +#define CCA_BYTE_SIZE_97 (97) +#define CCA_BYTE_SIZE_133 (133) + +#define CCA_BYTE_SIZE_550 (550) + +#define VIRTCCA_SUCCESS (0) +#define VIRTCCA_ERROR (1) + +#define CCA_CVM_CLAIM_CNT (7) +/* +/* Platform claims count +*/ +#define CCA_PLAT_CLAIM_CNT (8) +#define CCA_PLAT_SW_CLAIM_CNT (2) + +typedef struct q_useful_buf_c qbuf_t; + +/* + * DEN0137 Realm Management Monitor Specification (1.0-eac5) + * + * CCA attestation token { // Tag: 399 (cca-token-collection) + * CVM token { // 44241 + * COSE_Sign1 envelop { // 18 (COSE_Sign1) + * Protected headers + * Unprotected headers + * CVM token claim map { // Payload + * challenge // 10 + * rpv // 44235 + * rim // 44238 + * rem[4] // 44239 + * cvm_hash_algo_id // 44236 + * pub_key // 44237 + * pub_key_hash_algo_id // 44240 + * } + * Signature(RAK) + * } + * } + * CCA platform token { // 44234 + * COSE_Sign1 envelop { // 18 (COSE_Sign1) + * Protected headers + * Unprotected headers + * Platform token claim map { // Payload + * profile // 265 + * challenge // 10 + * implementation_id // 2396 (PSA imple id) + * instance_id // 256 (EAT ueid) + * config // 2401 + * lifecycle // 2395 + * sw_components { // 2399 + * sw_component { + * type + * measurement + * version + * signer_id + * hash_algo_id + * } + * } + * verification_service // 2400 + * hash-algo-id // 2402 + * } + * Signature(IAK) + * } + * } + * } +*/ + +typedef struct { + qbuf_t component_type; /* t */ + qbuf_t measurement; /* b */ + qbuf_t version; /* t */ + qbuf_t signer_id; /* b */ + qbuf_t hash_algo_id; /* t */ +} sw_comp_claims_t; + +typedef struct { + qbuf_t challenge; /* 10 */ + qbuf_t rpv; /* 44235 */ + qbuf_t rim; /* 44238 */ + qbuf_t rem[4]; /* 44239 */ + qbuf_t hash_algo_id; /* 44236 */ + qbuf_t pub_key; /* 44237 */ + qbuf_t pub_key_hash_algo_id; /* 44240 */ +} cvm_claims_t; + +/* +/* Platform claims structure +*/ +typedef struct { + qbuf_t profile; /* t 265 */ + qbuf_t challenge; /* b 10 */ + qbuf_t implementation_id; /* b 2396 (PSA imple) */ + qbuf_t instance_id; /* b 256 (EAT ueid) */ + qbuf_t config; /* b 2401 */ + uint64_t lifecycle; /* i 2395 */ + sw_comp_claims_t *sw_components; /* 2399 */ + uint64_t sw_comp_cnts; + qbuf_t verification_service; /* t 2400 */ + qbuf_t hash_algo_id; /* t 2402 */ +} platform_claims_t; + +typedef struct { + qbuf_t p_headers; + qbuf_t np_headers; + qbuf_t payload; + qbuf_t signature; +} cose_sign1_envelop_t; + +/* +/* Complete CCA token structure with both CVM and Platform tokens +*/ +typedef struct { + cose_sign1_envelop_t cvm_envelop; + qbuf_t cvm_cose; + cvm_claims_t cvm_token; + cose_sign1_envelop_t platform_envelop; + qbuf_t platform_cose; + platform_claims_t platform_token; +} cca_token_t; + +#define MAX_TOKEN_SIZE (4096U) + +typedef struct { + uint8_t buf[MAX_TOKEN_SIZE]; + size_t buf_size; +} cca_token_buf_t; + +uint64_t parse_cca_attestation_token(cca_token_t *token, + uint8_t *raw_token, size_t raw_token_size); +void print_cca_attestation_token(const cca_token_t *token); +void print_cca_attestation_token_raw(const cca_token_t *token); + +#endif /* TOKEN_PARSE_H */ \ No newline at end of file diff --git a/migcvm-agent/inc/attest/token_validate.h b/migcvm-agent/inc/attest/token_validate.h new file mode 100644 index 0000000000000000000000000000000000000000..4494059fea89b588e6a33162c00aec7e8b573998 --- /dev/null +++ b/migcvm-agent/inc/attest/token_validate.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef TOKEN_VALIDATE_H +#define TOKEN_VALIDATE_H + +#include +#include "t_cose/q_useful_buf.h" +#include "token_parse.h" + +/* Store the virtCCA certs downloaded from HUAWEI PKI */ +/* RSA certificate chain URLs (legacy) */ +#define DEFAULT_ROOT_CERT_URL "\"https://download.huawei.com/dl/download.do?actionFlag=download&nid=PKI1000000002&partNo=3001&mid=SUP_PKI\"" +#define DEFAULT_SUB_CERT_URL "\"https://download.huawei.com/dl/download.do?actionFlag=download&nid=PKI1000000040&partNo=3001&mid=SUP_PKI\"" + +/* ECC P-521 certificate chain URLs */ +#define ECCP521_ROOT_CERT_URL "\"https://download.huawei.com/dl/download.do?actionFlag=download&nid=PKI1100000224&partNo=3001&mid=SUP_PKI\"" +#define ECCP521_SUB_CERT_URL "\"https://download.huawei.com/dl/download.do?actionFlag=download&nid=PKI1100000225&partNo=3001&mid=SUP_PKI\"" + +/* SM2 certificate chain URLs */ +#define SM2_ROOT_CERT_URL "\"https://download.huawei.com/dl/download.do?actionFlag=download&nid=PKI1100000204&partNo=3001&mid=SUP_PKI\"" +#define SM2_SUB_CERT_URL "\"https://download.huawei.com/dl/download.do?actionFlag=download&nid=PKI1100000226&partNo=3001&mid=SUP_PKI\"" + +#define DEFAULT_CERT_PEM_PREFIX "." +/* RSA certificate filenames (legacy) */ +#define DEFAULT_ROOT_CERT_PEM_FILENAME "root_cert.pem" +#define DEFAULT_SUB_CERT_PEM_FILENAME "sub_cert.pem" + +/* ECC P-521 certificate filenames */ +#define ECCP521_ROOT_CERT_PEM_FILENAME "eccp521_root_cert.pem" +#define ECCP521_SUB_CERT_PEM_FILENAME "eccp521_sub_cert.pem" + +/* SM2 certificate filenames */ +#define SM2_ROOT_CERT_PEM_FILENAME "sm2_root_cert.pem" +#define SM2_SUB_CERT_PEM_FILENAME "sm2_sub_cert.pem" + +#define DEFAULT_AIK_CERT_PEM_FILENAME "aik_cert.pem" +#define MAX_FILE_NAME_SIZE 100 +#define MAX_FILE_PATH_SIZE 1000 +#define MAX_URL_SIZE 1000 + +typedef struct { + char cert_path_prefix[MAX_FILE_PATH_SIZE]; + char root_cert_filename[MAX_FILE_NAME_SIZE]; + char sub_cert_filename[MAX_FILE_NAME_SIZE]; + char aik_cert_filename[MAX_FILE_NAME_SIZE]; + char root_cert_url[MAX_URL_SIZE]; + char sub_cert_url[MAX_URL_SIZE]; +} cert_info_t; + +/* Certificate type enumeration */ +typedef enum { + CERT_TYPE_RSA = 0, + CERT_TYPE_ECC_P521 = 1, + CERT_TYPE_SM2 = 2, + CERT_TYPE_UNKNOWN = -1 +} cert_type_t; + +/* +/* Certificate type detection and configuration functions +*/ +cert_type_t detect_aik_cert_type(const char *aik_cert_path); +void configure_cert_info_by_type(cert_info_t *cert_info, cert_type_t cert_type); + +bool validate_aik_cert_chain(X509 *x509_aik, X509 *x509_sub, X509 *x509_root); + +bool verify_cvm_cose_sign(qbuf_t signed_cose, qbuf_t pub_key); + +/* +/* Platform token validation functions +*/ +bool verify_pubkhash_challenge(qbuf_t pub_key, qbuf_t challenge, qbuf_t algorithm); + +bool verify_plat_cose_sign(qbuf_t signed_cose, X509 *x509_aik); + +/* +/* Complete CCA token signature verification +*/ +bool verify_cca_token_signatures(cert_info_t *cert_info, + qbuf_t plat_cose, + qbuf_t cvm_cose, + qbuf_t cvm_pub_key, + qbuf_t plat_challenge, + qbuf_t cvm_pub_key_algo); + +#endif /* TOKEN_VALIDATE_H */ diff --git a/migcvm-agent/inc/attest/utils.h b/migcvm-agent/inc/attest/utils.h new file mode 100644 index 0000000000000000000000000000000000000000..2c795c8fef7598bc6e7d0d347e211a3b295064f2 --- /dev/null +++ b/migcvm-agent/inc/attest/utils.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef UTILS_H +#define UTILS_H + +#include +#include +#include + +int hex_to_bytes(unsigned char *in, size_t in_len, unsigned char *out, size_t *out_len); +int download_cert_pem(const char *prefix, const char *filename, const char *url); +int file_exists(const char *prefix, const char *filename); + +/* File handling functions */ +uint8_t* read_file_data(const char* filename, size_t* out_size); +char* read_text_file(const char* filename, size_t* out_size); +bool save_file_data(const char *file_name, const unsigned char *data, size_t size); + +#endif /* UTILS_H */ diff --git a/migcvm-agent/inc/attest/verify.h b/migcvm-agent/inc/attest/verify.h new file mode 100644 index 0000000000000000000000000000000000000000..9a92cd9305efd4f238cdd7827cfd6b3b46f9e9cf --- /dev/null +++ b/migcvm-agent/inc/attest/verify.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef VERIFY_H +#define VERIFY_H + +#include +#include "rem.h" +#include "event_log.h" +#include "firmware_state.h" + +/* Internal function declarations */ +bool read_token_rem(rem_t rems[REM_COUNT]); +void verify_single_rem(int rem_index, const rem_t* rem1, const rem_t* rem2); + +/** + * @brief Verify firmware state hash values + * + * @param json_file JSON file path + * @param state Firmware state + * @return true Verification successful + * @return false Verification failed + */ +bool verify_firmware_state(const char* json_file, const firmware_log_state_t* state); + + +#endif /* VERIFY_H */ \ No newline at end of file diff --git a/migcvm-agent/inc/migcvm_tsi/migcvm_tsi.h b/migcvm-agent/inc/migcvm_tsi/migcvm_tsi.h new file mode 100644 index 0000000000000000000000000000000000000000..8075a2be5bc9d90dbd572a5987a8f61afac57098 --- /dev/null +++ b/migcvm-agent/inc/migcvm_tsi/migcvm_tsi.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef MIGCVM_TSI_H +#define MIGCVM_TSI_H +#include +#include + +#define TSI_MAGIC 'T' +#define TSI_SUCCESS 0 + +/* Measurement Related Defination */ +#define TMI_HASH_ALGO_SHA256 0 +#define TMI_HASH_ALGO_SHA512 1 +#define TMI_HASH_ALGO_SM3 2 + +#define MAX_BIND_VM (16U) + +enum slot_status { + SLOT_IS_EMPTY = 0, + SLOT_IS_BINDED, + SLOT_IS_READY +}; + +enum TSI_ERROR { + NULL_INPUT = 0x00010001, /* NULL pointer. */ + INVALID_PARAM, /* Invalid param. */ + INSUFFICIENT_BUFFER_LEN, /* Insufficient buffer space. */ + NO_DEVICE_FILE, /* The TSI device file does not exist. */ + TSI_ERROR, /* TSI error. */ +}; + +typedef struct { + int fd; +} tsi_ctx; + +/* + * @brief Init ctx. + * @return TSI context + */ +tsi_ctx *tsi_new_ctx(void); + +/* + * @brief Free ctx. + * @param ctx [IN] TSI context + */ +void tsi_free_ctx(tsi_ctx *ctx); + +#define MAX_MEASUREMENT_SIZE (64U) +#define MEASUREMENT_SLOT_NR (5U) +typedef struct pending_guest_rd_s { + unsigned long long guest_rd[MAX_BIND_VM]; +} pending_guest_rd_t; + +typedef struct migration_info { + /* Algorithm to use for measurements */ + unsigned long long measurement_algo; + /* cvm measurement */ + unsigned char measurement[MEASUREMENT_SLOT_NR][MAX_MEASUREMENT_SIZE]; + bool set_key; + unsigned short slot_status; + unsigned long long msk[4]; + pending_guest_rd_t *pending_guest_rds; +} migration_info_t; + +typedef struct virtcca_migvm_info { + enum Ops { + OP_MIGRATE_GET_ATTR = 0, + OP_MIGRATE_SET_SLOT, + OP_MIGRATE_PEEK_RDS + } ops; + migration_info_t *mig_info; /* if ops == OP_MIGRATE_GET_ATTR, the size is sizeof(content) */ + unsigned long long guest_rd; /* if ops == OP_MIGRATE_SET_SLOT, the size is sizeof(guest_rd) */ + unsigned long size; +} virtcca_mig_info_t; + +#define TMM_GET_MIGRATION_INFO _IOWR(TSI_MAGIC, 3, struct virtcca_migvm_info) + +int get_migration_info_and_mask(tsi_ctx *ctx, virtcca_mig_info_t *migvm_info, migration_info_t *attest_info); +int set_migration_bind_slot_and_mask(tsi_ctx *ctx, virtcca_mig_info_t *migvm_info, migration_info_t *attest_info); +int get_migration_binded_rds(tsi_ctx *ctx, virtcca_mig_info_t *migvm_info, migration_info_t *attest_info); +#endif \ No newline at end of file diff --git a/migcvm-agent/inc/socket/socket_agent.h b/migcvm-agent/inc/socket/socket_agent.h new file mode 100644 index 0000000000000000000000000000000000000000..fde01297cb664a0ca2001ce4c6972fe64d075fff --- /dev/null +++ b/migcvm-agent/inc/socket/socket_agent.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef MIGCVM_SOCKET_AGENT_H +#define MIGCVM_SOCKET_AGENT_H +#include +#include + +#define HOST_AGENT_PORT 9000 +#define MIGCVM_CID VMADDR_CID_ANY +#define MAX_PAYLOAD_SIZE 256 + +#define PAYLOAD_TYPE_CHAR 0 +#define PAYLOAD_TYPE_ULL 1 +#pragma pack(push, 1) + +struct socket_msg { + char cmd[16]; + unsigned int payload_type; + unsigned int payload_len; + union { + char char_payload[MAX_PAYLOAD_SIZE]; + unsigned long long ull_payload; + } payload; +}; +#pragma pack(pop) + +typedef struct { + int agent_type; + + char *attester_type; + char *verifier_type; + char *tls_type; + char *crypto_type; + char *srv_ip; + uint16_t port; + char *digest_file; + rats_tls_log_level_t log_level; + bool mutual; + bool provide_endorsements; + bool use_firmware; + bool dump_eventlog; + char *ref_json_file; + bool use_fde; + char* rootfs_key_file; + bool verify_platform_components; + char* platform_ref_json_file; +} mig_agent_args; + +struct socket_agent_cfg { + mig_agent_args *args; + unsigned long cid; + unsigned long port; + int backlog; +}; + +int socket_agent_start(const struct socket_agent_cfg *cfg); + +typedef void (*socket_msg_handler)(const struct socket_msg *msg, int conn_fd, mig_agent_args *args); + +int socket_agent_start_with_handler(const struct socket_agent_cfg *cfg, + socket_msg_handler handler); + +void payload_encode(struct socket_msg *msg, const char *in); +void payload_decode(const struct socket_msg *msg, void *out); +#endif \ No newline at end of file diff --git a/migcvm-agent/src/attest/CMakeLists.txt b/migcvm-agent/src/attest/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..fb6bbadd2c459e2594af767948a0a76f1bac6952 --- /dev/null +++ b/migcvm-agent/src/attest/CMakeLists.txt @@ -0,0 +1,36 @@ +add_library(attest STATIC + token_parse.c + token_validate.c + platform_verify.c + ima_measure.c + event_log.c + rem.c + firmware_state.c + binary_blob.c + verify.c + config.c + utils.c +) + +target_include_directories(attest PRIVATE + ${CMAKE_SOURCE_DIR}/rats-tls/src/include + ${CMAKE_SOURCE_DIR}/inc/attest/ + /usr/local/include/qcbor + /usr/include + /usr/local/lib + /usr/include/openssl + /usr/local/include/t_cose +) + +find_package(OpenSSL REQUIRED) +find_library(RATS_TLS_LIB rats_tls PATHS /usr/lib/rats-tls/) +find_library(QCBOR_LIB qcbor) +find_library(T_COSE_LIB t_cose) + +target_link_libraries(attest PRIVATE + m + OpenSSL::Crypto + ${T_COSE_LIB} + ${QCBOR_LIB} + ${RATS_TLS_LIB} +) diff --git a/migcvm-agent/src/attest/binary_blob.c b/migcvm-agent/src/attest/binary_blob.c new file mode 100644 index 0000000000000000000000000000000000000000..f200988ee9971e7f298a7e4e10d27e7def906919 --- /dev/null +++ b/migcvm-agent/src/attest/binary_blob.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include "binary_blob.h" +#include "utils.h" + +bool binary_blob_init(binary_blob_t* blob, uint8_t* data, size_t length, size_t base) +{ + if (!blob || !data || length == 0) { + return false; + } + + blob->data = data; + blob->length = length; + blob->base_address = base; + + return true; +} + +static bool check_boundary(const binary_blob_t* blob, size_t* pos, size_t size) +{ + if (!blob || !pos || *pos + size > blob->length) { + printf("DEBUG: Boundary check failed: pos=%zu, size=%zu, blob_length=%zu\n", + *pos, size, blob ? blob->length : 0); + return false; + } + return true; +} + +uint16_t binary_blob_get_uint16(const binary_blob_t* blob, size_t* pos) +{ + uint16_t value = 0; + + if (!check_boundary(blob, pos, sizeof(uint16_t))) { + return 0; + } + + /* Little-endian read */ + value = (uint16_t)blob->data[*pos] | + ((uint16_t)blob->data[*pos + 1] << 8); + + *pos += sizeof(uint16_t); + return value; +} + +uint8_t binary_blob_get_uint8(const binary_blob_t* blob, size_t* pos) +{ + if (!check_boundary(blob, pos, sizeof(uint8_t))) { + return 0; + } + + uint8_t value = blob->data[*pos]; + *pos += sizeof(uint8_t); + return value; +} + +uint32_t binary_blob_get_uint32(const binary_blob_t* blob, size_t* pos) +{ + uint32_t value = 0; + if (!check_boundary(blob, pos, sizeof(uint32_t))) { + return 0; + } + + memcpy(&value, &blob->data[*pos], sizeof(uint32_t)); + *pos += sizeof(uint32_t); + return value; +} + +uint64_t binary_blob_get_uint64(const binary_blob_t* blob, size_t* pos) +{ + uint64_t value = 0; + + if (!check_boundary(blob, pos, sizeof(uint64_t))) { + return 0; + } + + /* Little-endian read */ + for (int i = 0; i < 8; i++) { + value |= ((uint64_t)blob->data[*pos + i] << (i * 8)); + } + + *pos += sizeof(uint64_t); + return value; +} + +void binary_blob_get_bytes(const binary_blob_t* blob, size_t* pos, size_t count, uint8_t* out) +{ + if (!check_boundary(blob, pos, count) || !out) { + memset(out, 0, count); + return; + } + + memcpy(out, &blob->data[*pos], count); + *pos += count; +} + +void binary_blob_dump(const binary_blob_t* blob) +{ + if (!blob || !blob->data) { + return; + } + + printf("Binary Data:\n"); + printf("Base Address: 0x%zX\n", blob->base_address); + printf("Length: %zu bytes\n\n", blob->length); + + char ascii_buf[17] = {0}; + size_t i; + + for (i = 0; i < blob->length; i++) { + /* Check if encountered consecutive 0xFF */ + if (blob->data[i] == 0xFF) { + size_t j; + for (j = i + 1; j < blob->length && j < i + 32; j++) { + if (blob->data[j] != 0xFF) break; + } + if (j == i + 32) { + /* Print ASCII part of the last line */ + if (i % 16 != 0) { + for (size_t k = i % 16; k < 16; k++) { + printf(" "); + } + printf(" %s\n", ascii_buf); + } + printf("\n[Remaining data omitted - all 0xFF]\n"); + return; + } + } + + if (i % 16 == 0) { + if (i > 0) { + printf(" %s\n", ascii_buf); + } + printf("%08zX ", blob->base_address + i); + memset(ascii_buf, 0, sizeof(ascii_buf)); + } + + printf("%02X ", blob->data[i]); + ascii_buf[i % 16] = isprint(blob->data[i]) ? blob->data[i] : '.'; + } + + /* Print last line */ + if (i % 16 != 0) { + for (size_t j = i % 16; j < 16; j++) { + printf(" "); + } + } + printf(" %s\n", ascii_buf); +} \ No newline at end of file diff --git a/migcvm-agent/src/attest/config.c b/migcvm-agent/src/attest/config.c new file mode 100644 index 0000000000000000000000000000000000000000..444e54cbc7f2c87b7dbc7b68527d95bd357f4dd8 --- /dev/null +++ b/migcvm-agent/src/attest/config.c @@ -0,0 +1,15 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include "config.h" + +#define CCEL_ACPI_TABLE_PATH "./ccel.bin" +#define CCEL_EVENT_LOG_PATH "./event_log.bin" + +/* Global configuration variable definition */ +config_t g_config = { + .ccel_file = CCEL_ACPI_TABLE_PATH, + .event_log_file = CCEL_EVENT_LOG_PATH, + .json_file = NULL /* Will be set from command line */ +}; \ No newline at end of file diff --git a/migcvm-agent/src/attest/event_log.c b/migcvm-agent/src/attest/event_log.c new file mode 100644 index 0000000000000000000000000000000000000000..343047b68a9bd971a720b92cc2f3b90d96a20762 --- /dev/null +++ b/migcvm-agent/src/attest/event_log.c @@ -0,0 +1,528 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include "event_log.h" +#include "config.h" +#include "firmware_state.h" +/* Event log header magic number */ +#define EVENT_LOG_MAGIC 0xFFFFFFFF + + +/* Function declarations */ +static const char* get_event_type_string(uint32_t type); +static void print_hex_dump(const uint8_t* data, size_t length, size_t base_addr); +static void update_rem(rem_t* rem, const uint8_t* digest); +static const char* get_algorithm_string(uint16_t algoid); +static int get_digest_size(uint16_t algoid); + +/* Event log entry structure */ +typedef struct { + uint32_t magic; + uint32_t type; + uint32_t digest_count; + uint8_t* digests; + uint32_t event_size; + uint8_t* event; +} event_log_header_t; + +bool process_event_log_entry(event_log_t* log, size_t* pos, + event_log_entry_t* entry) +{ + if (!log || !pos || !entry || *pos >= log->blob.length) { + return false; + } + + /* Initialize entry */ + memset(entry, 0, sizeof(event_log_entry_t)); + + /* Save initial position */ + size_t initial_pos = *pos; + + /* Read event header */ + uint32_t register_index = binary_blob_get_uint32(&log->blob, pos); + entry->event_type = binary_blob_get_uint32(&log->blob, pos); + + /* Check if reached end of file */ + if (register_index == EVENT_LOG_MAGIC && + entry->event_type == EVENT_LOG_MAGIC) { + return false; + } + + /* Decrease register_index by 1 to ensure REM index starts from 0 */ + entry->rem_index = register_index > 0 ? register_index - 1 : 0; + + /* Read digest count */ + entry->digest_count = binary_blob_get_uint32(&log->blob, pos); + + /* Special handling for EV_NO_ACTION event */ + if (entry->event_type == EV_NO_ACTION) { + /* Skip 20 bytes of digest */ + *pos += 20; + + /* Read Spec ID Event03 string (24 bytes) */ + uint8_t spec_id[24]; + binary_blob_get_bytes(&log->blob, pos, 24, spec_id); + + /* Read algorithm count */ + entry->algorithms_number = binary_blob_get_uint32(&log->blob, pos); + + /* Read algorithm information */ + entry->algorithms = (algorithm_info_t*)malloc(entry->algorithms_number * sizeof(algorithm_info_t)); + if (!entry->algorithms) { + return false; + } + + for (uint32_t i = 0; i < entry->algorithms_number; i++) { + entry->algorithms[i].algoid = binary_blob_get_uint16(&log->blob, pos); + entry->algorithms[i].digestsize = binary_blob_get_uint16(&log->blob, pos); + } + + /* Read vendor information size and skip */ + uint8_t vendorsize = binary_blob_get_uint8(&log->blob, pos); + *pos += vendorsize; + + /* Set event size */ + entry->event_size = *pos - initial_pos; + if (entry->event_size > 0) { + entry->event = (uint8_t*)malloc(entry->event_size); + if (entry->event) { + memcpy(entry->event, log->blob.data + initial_pos, entry->event_size); + } + } + + return true; + } + + /* Process other type events */ + if (entry->digest_count > 0) { + entry->digests = (uint8_t*)malloc(entry->digest_count * SHA256_DIGEST_LENGTH); + entry->alg_ids = (uint16_t*)malloc(entry->digest_count * sizeof(uint16_t)); + if (!entry->digests || !entry->alg_ids) { + free(entry->digests); + free(entry->alg_ids); + return false; + } + + /* Read each digest */ + for (uint32_t i = 0; i < entry->digest_count; i++) { + /* Read algorithm ID */ + entry->alg_ids[i] = binary_blob_get_uint16(&log->blob, pos); + + /* Read digest data */ + binary_blob_get_bytes(&log->blob, pos, SHA256_DIGEST_LENGTH, + entry->digests + i * SHA256_DIGEST_LENGTH); + } + } + + /* Read event data size */ + uint32_t event_data_size = binary_blob_get_uint32(&log->blob, pos); + + /* Read event data */ + if (event_data_size > 0) { + if (event_data_size > 10240) { + free(entry->digests); + free(entry->alg_ids); + entry->digests = NULL; + entry->alg_ids = NULL; + return false; + } + } + + /* Set total event size (including header, digest, and event data) */ + entry->event_size = *pos - initial_pos + event_data_size; + + /* Allocate and save complete event data */ + if (entry->event_size > 0) { + entry->event = (uint8_t*)malloc(entry->event_size); + if (!entry->event) { + free(entry->digests); + free(entry->alg_ids); + entry->digests = NULL; + entry->alg_ids = NULL; + return false; + } + + /* Copy header and digest data */ + memcpy(entry->event, log->blob.data + initial_pos, *pos - initial_pos); + + /* Copy event data */ + if (event_data_size > 0) { + binary_blob_get_bytes(&log->blob, pos, event_data_size, + entry->event + (*pos - initial_pos)); + } + } + + return true; +} + +static void update_rem(rem_t* rem, const uint8_t* digest) +{ + uint8_t hash[SHA256_DIGEST_LENGTH]; + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + if (!ctx) { + printf("Error: Failed to create EVP context\n"); + return; + } + + if (EVP_DigestInit_ex(ctx, EVP_sha256(), NULL) != 1) { + printf("Error: Failed to initialize SHA256\n"); + EVP_MD_CTX_free(ctx); + return; + } + + /* Update REM data */ + if (EVP_DigestUpdate(ctx, rem->data, REM_LENGTH_BYTES) != 1) { + printf("Error: Failed to update REM data\n"); + EVP_MD_CTX_free(ctx); + return; + } + + /* Update digest data */ + if (EVP_DigestUpdate(ctx, digest, SHA256_DIGEST_LENGTH) != 1) { + printf("Error: Failed to update digest data\n"); + EVP_MD_CTX_free(ctx); + return; + } + + /* Get final hash value */ + unsigned int hash_len; + if (EVP_DigestFinal_ex(ctx, hash, &hash_len) != 1) { + printf("Error: Failed to finalize hash\n"); + EVP_MD_CTX_free(ctx); + return; + } + + /* Copy hash value to REM */ + memcpy(rem->data, hash, REM_LENGTH_BYTES); + + EVP_MD_CTX_free(ctx); +} + +bool event_log_init(event_log_t* log, size_t base, size_t length) +{ + if (!log) { + return false; + } + + /* Read event log data from file */ + size_t file_size; + uint8_t* data = read_file_data(g_config.event_log_file, &file_size); + if (!data) { + return false; + } + + /* Initialize binary blob */ + if (!binary_blob_init(&log->blob, data, file_size, base)) { + printf("Error: Failed to initialize binary blob\n"); + free(data); + return false; + } + + log->log_base = base; + log->log_length = file_size; + + /* Initialize all REM values to 0 */ + for (int i = 0; i < REM_COUNT; i++) { + rem_init(&log->rems[i]); + } + + return true; +} + +static void print_hex_dump(const uint8_t* data, size_t length, size_t base_addr) +{ + char ascii_buf[17] = {0}; + + for (size_t i = 0; i < length; i++) { + if (i % 16 == 0) { + if (i > 0) { + printf(" %s\n", ascii_buf); + } + if (base_addr) { + printf("%08zX ", base_addr + i); + } else { + printf("%08zX ", i); + } + memset(ascii_buf, 0, sizeof(ascii_buf)); + } + printf("%02X ", data[i]); + ascii_buf[i % 16] = isprint(data[i]) ? data[i] : '.'; + } + + /* Process last line */ + if (length % 16 != 0) { + /* Pad with spaces */ + for (size_t i = length % 16; i < 16; i++) { + printf(" "); + } + } + printf(" %s\n", ascii_buf); +} + +/* Move function definitions here */ +static const char* get_event_type_string(uint32_t type) +{ + switch (type) { + case EV_PREBOOT_CERT: return "EV_PREBOOT_CERT"; + case EV_POST_CODE: return "EV_POST_CODE"; + case EV_UNUSED: return "EV_UNUSED"; + case EV_NO_ACTION: return "EV_NO_ACTION"; + case EV_SEPARATOR: return "EV_SEPARATOR"; + case EV_ACTION: return "EV_ACTION"; + case EV_EVENT_TAG: return "EV_EVENT_TAG"; + case EV_S_CRTM_CONTENTS: return "EV_S_CRTM_CONTENTS"; + case EV_S_CRTM_VERSION: return "EV_S_CRTM_VERSION"; + case EV_CPU_MICROCODE: return "EV_CPU_MICROCODE"; + case EV_PLATFORM_CONFIG_FLAGS: return "EV_PLATFORM_CONFIG_FLAGS"; + case EV_TABLE_OF_DEVICES: return "EV_TABLE_OF_DEVICES"; + case EV_COMPACT_HASH: return "EV_COMPACT_HASH"; + case EV_IPL: return "EV_IPL"; + case EV_IPL_PARTITION_DATA: return "EV_IPL_PARTITION_DATA"; + case EV_NONHOST_CODE: return "EV_NONHOST_CODE"; + case EV_NONHOST_CONFIG: return "EV_NONHOST_CONFIG"; + case EV_NONHOST_INFO: return "EV_NONHOST_INFO"; + case EV_OMIT_BOOT_DEVICE_EVENTS: return "EV_OMIT_BOOT_DEVICE_EVENTS"; + case EV_EFI_VARIABLE_DRIVER_CONFIG: return "EV_EFI_VARIABLE_DRIVER_CONFIG"; + case EV_EFI_VARIABLE_BOOT: return "EV_EFI_VARIABLE_BOOT"; + case EV_EFI_BOOT_SERVICES_APPLICATION: return "EV_EFI_BOOT_SERVICES_APPLICATION"; + case EV_EFI_BOOT_SERVICES_DRIVER: return "EV_EFI_BOOT_SERVICES_DRIVER"; + case EV_EFI_RUNTIME_SERVICES_DRIVER: return "EV_EFI_RUNTIME_SERVICES_DRIVER"; + case EV_EFI_GPT_EVENT: return "EV_EFI_GPT_EVENT"; + case EV_EFI_ACTION: return "EV_EFI_ACTION"; + case EV_EFI_PLATFORM_FIRMWARE_BLOB: return "EV_EFI_PLATFORM_FIRMWARE_BLOB"; + case EV_EFI_HANDOFF_TABLES: return "EV_EFI_HANDOFF_TABLES"; + case EV_EFI_VARIABLE_AUTHORITY: return "EV_EFI_VARIABLE_AUTHORITY"; + default: return "UNKNOWN"; + } +} + +/* Add function to get algorithm name */ +static const char* get_algorithm_string(uint16_t algoid) +{ + switch(algoid) { + case TPM_ALG_ERROR: return "TPM_ALG_ERROR"; + case TPM_ALG_RSA: return "TPM_ALG_RSA"; + case TPM_ALG_SHA1: return "TPM_ALG_SHA1"; + case TPM_ALG_SHA256: return "TPM_ALG_SHA256"; + case TPM_ALG_SHA384: return "TPM_ALG_SHA384"; + case TPM_ALG_SHA512: return "TPM_ALG_SHA512"; + case TPM_ALG_ECDSA: return "TPM_ALG_ECDSA"; + default: return "UNKNOWN"; + } +} + +/* Add function to get digest length */ +static int get_digest_size(uint16_t algoid) +{ + switch(algoid) { + case TPM_ALG_SHA1: return 20; + case TPM_ALG_SHA256: return 32; + case TPM_ALG_SHA384: return 48; + case TPM_ALG_SHA512: return 64; + default: return 0; + } +} + +bool event_log_process(event_log_t* log) +{ + if (!log) { + return false; + } + + printf("=> Read Event Log Data - Address: 0x%zX(0x%zX)\n", + log->log_base, log->log_length); + + size_t pos = 0; + event_log_entry_t entry = {0}; + int entry_count = 0; + + while (pos < log->blob.length) { + if (!process_event_log_entry(log, &pos, &entry)) { + break; + } + + printf("\n==== VCCA Event Log Entry - %d [0x%zX] ====\n", + entry_count, log->log_base + pos - entry.event_size); + /* REM index */ + printf("REM : %d\n", entry.rem_index); + printf("Type : 0x%X (%s)\n", entry.event_type, + get_event_type_string(entry.event_type)); + printf("Length : %d\n", entry.event_size); + + if (entry.event_type == 0x3) { /* EV_NO_ACTION */ + printf("Algorithms Number : %d\n", entry.algorithms_number); + for (uint32_t i = 0; i < entry.algorithms_number; i++) { + printf(" Algorithms[0x%X] Size: %d\n", + entry.algorithms[i].algoid, + entry.algorithms[i].digestsize * 8); + } + } + + if (entry.digest_count > 0) { + printf("Algorithms ID : %d (%s)\n", + entry.alg_ids[0], + get_algorithm_string(entry.alg_ids[0])); + + int digest_size = get_digest_size(entry.alg_ids[0]); + if (digest_size > 0) { + printf("Digest[0] :\n"); + print_hex_dump(entry.digests, digest_size, 0); + } + } + + if (entry.event_size > 0 && entry.event) { + printf("RAW DATA: ----------------------------------------------\n"); + print_hex_dump(entry.event, entry.event_size, + log->log_base + pos - entry.event_size); + printf("RAW DATA: ----------------------------------------------\n"); + } + + /* Free memory */ + if (entry.digests) { + free(entry.digests); + entry.digests = NULL; + } + if (entry.alg_ids) { + free(entry.alg_ids); + entry.alg_ids = NULL; + } + if (entry.event) { + free(entry.event); + entry.event = NULL; + } + if (entry.algorithms) { + free(entry.algorithms); + entry.algorithms = NULL; + } + entry_count++; + } + + return true; +} + +bool event_log_replay(event_log_t* log) +{ + if (!log) { + return false; + } + + size_t pos = 0; + event_log_entry_t entry = {0}; + bool success = true; + + printf("\n=> Replay Rolling Hash - REM\n"); + + /* Initialize all REM values to 0 */ + for (int i = 0; i < REM_COUNT; i++) { + rem_init(&log->rems[i]); + } + + /* Create firmware state object */ + firmware_log_state_t* firmware_state = firmware_log_state_create(log); + if (!firmware_state) { + printf("Error: Failed to create firmware state\n"); + return false; + } + + /* First pass: Process EV_NO_ACTION events */ + while (pos < log->blob.length) { + if (!process_event_log_entry(log, &pos, &entry)) { + break; + } + + /* Check if reached end of file */ + if (entry.rem_index == EVENT_LOG_MAGIC && + entry.event_type == EVENT_LOG_MAGIC) { + break; + } + + if (entry.event_type == 0x3) { /* EV_NO_ACTION */ + if (entry.rem_index < REM_COUNT && entry.digest_count > 0) { + update_rem(&log->rems[entry.rem_index], entry.digests); + } + } + + /* Free memory */ + if (entry.digests) { + free(entry.digests); + entry.digests = NULL; + } + if (entry.event) { + free(entry.event); + entry.event = NULL; + } + } + + /* Reset position */ + pos = 0; + + /* Second pass: Process other events */ + while (pos < log->blob.length) { + if (!process_event_log_entry(log, &pos, &entry)) { + break; + } + + /* Check if reached end of file */ + if (entry.rem_index == EVENT_LOG_MAGIC && + entry.event_type == EVENT_LOG_MAGIC) { + break; + } + + if (entry.event_type != 0x3) { /* Non EV_NO_ACTION */ + if (entry.rem_index < REM_COUNT && entry.digest_count > 0) { + update_rem(&log->rems[entry.rem_index], entry.digests); + } + } + + /* Free memory */ + if (entry.digests) { + free(entry.digests); + entry.digests = NULL; + } + if (entry.event) { + free(entry.event); + entry.event = NULL; + } + } + + /* Print final REM values */ + for (int i = 0; i < REM_COUNT; i++) { + printf("\n==== REM[%d] ====\n", i); + print_hex_dump(log->rems[i].data, REM_LENGTH_BYTES, 0); + } + + /* Extract firmware state information */ + if (!firmware_log_state_extract(log, firmware_state)) { + printf("Warning: Failed to extract complete firmware state\n"); + success = false; + } else { + /* Print firmware state information */ + firmware_log_state_print(firmware_state); + } + + /* Free firmware state object */ + firmware_log_state_free(firmware_state); + + return success; +} + +void event_log_dump(event_log_t* log) +{ + if (!log) { + return; + } + + printf("Event log base: 0x%zX, length: 0x%zX\n", log->log_base, log->log_length); + printf("Actual data size: %zu bytes\n\n", log->blob.length); + + event_log_process(log); + printf("\n"); /* Add empty line */ + event_log_replay(log); +} \ No newline at end of file diff --git a/migcvm-agent/src/attest/firmware_state.c b/migcvm-agent/src/attest/firmware_state.c new file mode 100644 index 0000000000000000000000000000000000000000..54aa32aed3e6217c88ac61c7a2b134b579618ac9 --- /dev/null +++ b/migcvm-agent/src/attest/firmware_state.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include +#include +#include +#include "event_log.h" +#include "firmware_state.h" + +/* Helper function: Extract EFI image information from event data */ +static bool extract_efi_image(event_log_entry_t* entry, efi_image_t* image) +{ + if (!entry || !image || !entry->digests || entry->digest_count == 0) { + return false; + } + + /* Only extract image hash */ + image->image_hash_size = SHA256_DIGEST_LENGTH; + image->image_hash = (uint8_t*)malloc(image->image_hash_size); + if (!image->image_hash) { + return false; + } + memcpy(image->image_hash, entry->digests, image->image_hash_size); + return true; +} + +/* Helper function: Extract GRUB information from event data */ +static bool extract_grub_info(event_log_entry_t* entry, grub_state_t* grub) +{ + if (!entry || !grub || !entry->event || entry->event_size == 0) { + return false; + } + + /* Check if it's a grub.cfg file */ + const char* grub_cfg = "grub.cfg"; + bool is_grub_cfg = false; + for (uint32_t i = 0; i < entry->event_size - strlen(grub_cfg); i++) { + if (memcmp(entry->event + i, grub_cfg, strlen(grub_cfg)) == 0) { + is_grub_cfg = true; + break; + } + } + + if (!is_grub_cfg) { + return false; + } + + /* Extract configuration hash */ + if (entry->digest_count > 0 && entry->digests) { + grub->config_hash_size = SHA256_DIGEST_LENGTH; + grub->config_hash = (uint8_t*)malloc(grub->config_hash_size); + if (!grub->config_hash) { + return false; + } + memcpy(grub->config_hash, entry->digests, grub->config_hash_size); + return true; + } + + return false; +} + +/* Helper function: Print hexadecimal data */ +static void print_hex(const uint8_t* data, size_t len) +{ + for (size_t i = 0; i < len; i++) { + printf("%02x", data[i]); + } +} + +/* Helper function: Search for substring in data */ +static const uint8_t* find_substring(const uint8_t* data, size_t data_len, const char* substr, size_t substr_len) +{ + if (!data || !substr || data_len < substr_len) { + return NULL; + } + + for (size_t i = 0; i <= data_len - substr_len; i++) { + if (memcmp(data + i, substr, substr_len) == 0) { + return data + i; + } + } + return NULL; +} + +/* Print firmware state information */ +void firmware_log_state_print(const firmware_log_state_t* state) +{ + if (!state) { + printf("Firmware state is empty\n"); + return; + } + + printf("\n============ Firmware Log State ============\n"); + printf("Hash Algorithm: 0x%x\n", state->hash_algo); + + /* Print EFI state */ + if (state->efi) { + printf("\n----- EFI State -----\n"); + printf("Image Count: %u\n", state->efi->image_count); + for (uint32_t i = 0; i < state->efi->image_count; i++) { + printf("\nImage[%u]:\n", i); + if (state->efi->images[i].image_hash) { + printf(" Hash: "); + print_hex(state->efi->images[i].image_hash, state->efi->images[i].image_hash_size); + printf("\n"); + } + } + } + + /* Print GRUB conf state */ + if (state->grub) { + printf("\n----- GRUB conf State -----\n"); + if (state->grub->config_hash) { + printf("Config Hash: "); + print_hex(state->grub->config_hash, state->grub->config_hash_size); + printf("\n"); + } + } + + /* Print Linux kernel state */ + if (state->linux_kernel) { + printf("\n----- Linux Kernel State -----\n"); + if (state->linux_kernel->kernel_hash) { + printf("Kernel Hash: "); + print_hex(state->linux_kernel->kernel_hash, state->linux_kernel->kernel_hash_size); + printf("\n"); + } + if (state->linux_kernel->initrd_hash) { + printf("Initrd Hash: "); + print_hex(state->linux_kernel->initrd_hash, state->linux_kernel->initrd_hash_size); + printf("\n"); + } + } + + printf("\n=====================================\n"); +} + +/* Create firmware log state */ +firmware_log_state_t* firmware_log_state_create(event_log_t* log) +{ + firmware_log_state_t* state = (firmware_log_state_t*)calloc(1, sizeof(firmware_log_state_t)); + if (!state) { + return NULL; + } + + state->efi = (efi_state_t*)calloc(1, sizeof(efi_state_t)); + state->grub = (grub_state_t*)calloc(1, sizeof(grub_state_t)); + state->linux_kernel = (linux_kernel_state_t*)calloc(1, sizeof(linux_kernel_state_t)); + + if (!state->efi || !state->grub || !state->linux_kernel) { + firmware_log_state_free(state); + return NULL; + } + + return state; +} + +/* Free firmware log state */ +void firmware_log_state_free(firmware_log_state_t* state) +{ + if (!state) { + return; + } + + if (state->efi) { + for (uint32_t i = 0; i < state->efi->image_count; i++) { + free(state->efi->images[i].image_hash); + } + free(state->efi->images); + free(state->efi); + } + + if (state->grub) { + free(state->grub->config_hash); + free(state->grub); + } + + if (state->linux_kernel) { + free(state->linux_kernel->kernel_hash); + free(state->linux_kernel->initrd_hash); + free(state->linux_kernel); + } + + free(state->raw_events); + free(state); +} + +/* Extract firmware log state */ +bool firmware_log_state_extract(event_log_t* log, firmware_log_state_t* state) +{ + if (!log || !state) { + return false; + } + + size_t pos = 0; + event_log_entry_t entry; + uint32_t efi_image_count = 0; + bool has_grub_info = false; + bool has_kernel_info = false; + bool has_initrd_info = false; + + /* First pass: Count EFI images */ + while (process_event_log_entry(log, &pos, &entry)) { + if (entry.event_type == EV_EFI_BOOT_SERVICES_APPLICATION) { + efi_image_count++; + } + } + + /* Allocate EFI image array */ + if (efi_image_count > 0) { + state->efi->images = (efi_image_t*)calloc(efi_image_count, sizeof(efi_image_t)); + if (!state->efi->images) { + return false; + } + } + + /* Reset position */ + pos = 0; + uint32_t current_efi_index = 0; + + /* Second pass: Extract detailed information */ + while (process_event_log_entry(log, &pos, &entry)) { + /* Process EFI images */ + if (entry.event_type == EV_EFI_BOOT_SERVICES_APPLICATION) { + if (extract_efi_image(&entry, &state->efi->images[current_efi_index])) { + current_efi_index++; + } + /* Process GRUB configuration file */ + } else if (entry.event_type == EV_IPL && !has_grub_info) { + if (extract_grub_info(&entry, state->grub)) { + has_grub_info = true; + } + /* Process kernel and initrd */ + } else if (entry.event_type == EV_IPL) { + /* Check if it's a kernel file path */ + if (!has_kernel_info && entry.event && entry.event_size > 0) { + const char* kernel_path = "/vmlinuz-"; + if (find_substring(entry.event, entry.event_size, kernel_path, strlen(kernel_path)) && + !find_substring(entry.event, entry.event_size, "grub_cmd:", strlen("grub_cmd:"))) { + if (entry.digest_count > 0 && entry.digests) { + state->linux_kernel->kernel_hash_size = SHA256_DIGEST_LENGTH; + state->linux_kernel->kernel_hash = (uint8_t*)malloc(state->linux_kernel->kernel_hash_size); + if (state->linux_kernel->kernel_hash) { + memcpy(state->linux_kernel->kernel_hash, entry.digests, + state->linux_kernel->kernel_hash_size); + has_kernel_info = true; + } + } + } + /* Check if it's an initrd file path */ + } else if (!has_initrd_info && entry.event && entry.event_size > 0) { + const char* initrd_path = "/initramfs-"; + if (find_substring(entry.event, entry.event_size, initrd_path, strlen(initrd_path)) && + !find_substring(entry.event, entry.event_size, "grub_cmd:", strlen("grub_cmd:"))) { + if (entry.digest_count > 0 && entry.digests) { + state->linux_kernel->initrd_hash_size = SHA256_DIGEST_LENGTH; + state->linux_kernel->initrd_hash = (uint8_t*)malloc(state->linux_kernel->initrd_hash_size); + if (state->linux_kernel->initrd_hash) { + memcpy(state->linux_kernel->initrd_hash, entry.digests, + state->linux_kernel->initrd_hash_size); + has_initrd_info = true; + } + } + } + } + } + + /* Release memory */ + if (entry.digests) { + free(entry.digests); + entry.digests = NULL; + } + if (entry.event) { + free(entry.event); + entry.event = NULL; + } + if (entry.algorithms) { + free(entry.algorithms); + entry.algorithms = NULL; + } + } + + /* Update state information */ + state->efi->image_count = current_efi_index; + state->hash_algo = TPM_ALG_SHA256; /* Currently fixed to use SHA256 */ + + return true; +} \ No newline at end of file diff --git a/migcvm-agent/src/attest/ima_measure.c b/migcvm-agent/src/attest/ima_measure.c new file mode 100644 index 0000000000000000000000000000000000000000..df56a0d8b3236e1aaf35431ffdde0ed53ad6a276 --- /dev/null +++ b/migcvm-agent/src/attest/ima_measure.c @@ -0,0 +1,546 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ima_measure.h" + +/* +* Define a constant for aggregating all PCRs, if needed for other use cases, +* though for the current problem, we will target a specific PCR. +*/ +#define IMA_AGGREGATE_ALL_PCRS -1 + +/* static int verify_sig; */ +/* static unsigned int no_sigs, unknown_keys, invalid_sigs; */ + +static u_int8_t zero[SHA_DIGEST_LENGTH]; +static u_int8_t fox[SHA_DIGEST_LENGTH]; + +static int display_digest(u_int8_t *digest, u_int32_t digestlen) +{ + int i; + + for (i = 0; i < digestlen; i++) { + printf("%02x", (*(digest + i) & 0xff)); + } + return 0; +} + +static int ima_eventdigest_parse(u_int8_t *buffer, u_int32_t buflen, u_int8_t *file_digest, u_int32_t *file_digest_len) +{ + if (buflen < SHA_DIGEST_LENGTH) { + printf("invalid len %u\n", buflen); + return -1; + } + return display_digest(buffer, SHA_DIGEST_LENGTH); +} + +static int ima_eventdigest_ng_parse(u_int8_t *buffer, u_int32_t buflen, u_int8_t *file_digest, u_int32_t *file_digest_len) +{ + char hash_algo[CRYPTO_MAX_ALG_NAME + 1] = { 0 }; + int algo_len; + const EVP_MD *md; + int digest_len; + + if (buflen > CRYPTO_MAX_ALG_NAME + 1) { + printf("invalid algo name\n"); + return ERROR_ENTRY_PARSING; + } + + algo_len = strnlen((char *)buffer, buflen); /* format: algo + ':' + '\0' */ + if (algo_len <= 1) { + printf("Hash algorithm name invalid\n"); + return ERROR_ENTRY_PARSING; + } + algo_len--; + printf("%s", buffer); + memcpy(hash_algo, buffer, algo_len); + + md = EVP_get_digestbyname(hash_algo); + if (md == NULL) { + printf("Unknown hash algorithm '%s'\n", hash_algo); + return ERROR_ENTRY_PARSING; + } + + digest_len = EVP_MD_size(md); + + if (algo_len + 2 + digest_len != buflen) { + printf("Field length mismatch, current: %d, expected: %d\n", + algo_len + 2 + digest_len, buflen); + return ERROR_ENTRY_PARSING; + } + + if (digest_len > IMA_MAX_HASH_SIZE) { + printf("Hash digest too long.\n"); + return ERROR_ENTRY_PARSING; + } + *file_digest_len = digest_len; + memcpy(file_digest, buffer + algo_len + 2, digest_len); + + return display_digest(buffer + algo_len + 2, digest_len); +} + +static int ima_parse_string(u_int8_t *buffer, u_int32_t buflen) +{ + char *str; + + /* some callers include the terminating null in the + * buflen, others don't (eg. 'd'). + */ + str = calloc(buflen + 1, sizeof(u_int8_t)); + if (str == NULL) { + printf("Out of memory\n"); + return -ENOMEM; + } + + memcpy(str, buffer, buflen); + printf("%s", str); + free(str); + return 0; +} + +static int ima_eventname_parse(u_int8_t *buffer, u_int32_t buflen, u_int8_t *file_digest, u_int32_t *file_digest_len) +{ + if (buflen > TCG_EVENT_NAME_LEN_MAX + 1) { + printf("Event name too long\n"); + return -1; + } + + return ima_parse_string(buffer, buflen); +} + +static int ima_eventname_ng_parse(u_int8_t *buffer, u_int32_t buflen, u_int8_t *file_digest, u_int32_t *file_digest_len) +{ + return ima_parse_string(buffer, buflen); +} + +static int ima_eventsig_parse(u_int8_t *buffer, u_int32_t buflen, u_int8_t *file_digest, u_int32_t *file_digest_len) +{ + return display_digest(buffer, buflen); +} + +/* IMA template field definition */ +struct ima_template_field { + const char field_id[IMA_TEMPLATE_FIELD_ID_MAX_LEN]; + int (*field_parse) (u_int8_t *buffer, u_int32_t buflen, u_int8_t *file_digest, u_int32_t *file_digest_len); +}; + +/* IMA template descriptor definition */ +struct ima_template_desc { + char *name; + char *fmt; + int num_fields; + struct ima_template_field **fields; +}; + +static struct ima_template_desc defined_templates[] = { + {.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT}, + {.name = "ima-ng",.fmt = "d-ng|n-ng"}, + {.name = "ima-sig",.fmt = "d-ng|n-ng|sig"}, +}; + +static struct ima_template_field supported_fields[] = { + {.field_id = "d",.field_parse = ima_eventdigest_parse}, + {.field_id = "n",.field_parse = ima_eventname_parse}, + {.field_id = "d-ng",.field_parse = ima_eventdigest_ng_parse}, + {.field_id = "n-ng",.field_parse = ima_eventname_ng_parse}, + {.field_id = "sig",.field_parse = ima_eventsig_parse}, +}; + +struct event { + struct { + u_int32_t pcr; + u_int8_t digest[SHA_DIGEST_LENGTH]; + u_int32_t name_len; + } header; + char name[TCG_EVENT_NAME_LEN_MAX + 1]; + struct ima_template_desc *template_desc; /* template descriptor */ + u_int32_t template_data_len; + u_int8_t *template_data; /* template related data */ + u_int8_t file_digest[IMA_MAX_HASH_SIZE]; + u_int32_t file_digest_len; +}; + +static int parse_template_data(struct event *template) +{ + int offset = 0, result = 0; + int i, j, is_ima_template; + char *template_fmt, *template_fmt_ptr, *f; + u_int32_t digest_len; + u_int8_t *digest; + + is_ima_template = strcmp(template->name, "ima") == 0 ? 1 : 0; + template->template_desc = NULL; + + for (i = 0; i < ARRAY_SIZE(defined_templates); i++) { + if (strcmp(template->name, + defined_templates[i].name) == 0) { + template->template_desc = defined_templates + i; + break; + } + } + + if (template->template_desc == NULL) { + i = ARRAY_SIZE(defined_templates) - 1; + template->template_desc = defined_templates + i; + template->template_desc->fmt = template->name; + } + + template_fmt = strdup(template->template_desc->fmt); + if (template_fmt == NULL) { + printf("Out of memory\n"); + return -ENOMEM; + } + + template_fmt_ptr = template_fmt; + for (i = 0; (f = strsep(&template_fmt_ptr, "|")) != NULL; i++) { + struct ima_template_field *field = NULL; + u_int32_t field_len = 0; + + for (j = 0; j < ARRAY_SIZE(supported_fields); j++) { + if (!strcmp(f, supported_fields[j].field_id)) { + field = supported_fields + j; + break; + } + } + + if (field == NULL) { + result = ERROR_FIELD_NOT_FOUND; + printf("Field '%s' not supported\n", f); + goto out; + } + + if (is_ima_template && strcmp(f, "d") == 0) + field_len = SHA_DIGEST_LENGTH; + else if (is_ima_template && strcmp(f, "n") == 0) + field_len = strlen(template->template_data + offset); + else { + memcpy(&field_len, template->template_data + offset, + sizeof(u_int32_t)); + offset += sizeof(u_int32_t); + } + + if (offset >= template->template_data_len || + field_len > template->template_data_len - offset) { + printf("offset or field len is invalid\n"); + goto out; + } + result = field->field_parse(template->template_data + offset, + field_len, template->file_digest, + &template->file_digest_len); + if (result) { + printf("Parsing of '%s' field failed, result: %d\n", + f, result); + goto out; + } + + /* ToDo: add verifiy ima-sig template */ + + offset += field_len; + printf(" "); + } +out: + free(template_fmt); + return result; +} + +static int read_template_data(struct event *template, FILE *fp) +{ + int len, is_ima_template; + + is_ima_template = strcmp(template->name, "ima") == 0 ? 1 : 0; + if (!is_ima_template) { + if (fread(&template->template_data_len, sizeof(u_int32_t), 1, fp) != 1 || + template->template_data_len > IMA_TEMPLATE_DATA_MAX_LEN) { + printf("ERROR: read length faild or length is invalid\n"); + return -EINVAL; + } + len = template->template_data_len; + } else { + template->template_data_len = SHA_DIGEST_LENGTH + + TCG_EVENT_NAME_LEN_MAX + 1; + /* + * Read the digest only as the event name length + * is not known in advance. + */ + len = SHA_DIGEST_LENGTH; + } + + template->template_data = calloc(template->template_data_len, + sizeof(u_int8_t)); + if (template->template_data == NULL) { + printf("ERROR: out of memory\n"); + return -ENOMEM; + } + + if (fread(template->template_data, len, 1, fp) != 1) { + printf("ERROR: read template data failed\n"); + goto free; + } + + if (is_ima_template) { /* finish 'ima' template data read */ + u_int32_t field_len; + if (fread(&field_len, sizeof(u_int32_t), 1, fp) != 1 || field_len > TCG_EVENT_NAME_LEN_MAX) { + printf("ERROR: read template data failed\n"); + goto free; + } + if (fread(template->template_data + SHA_DIGEST_LENGTH, field_len, 1, fp) != 1) { + printf("ERROR: read digest failed\n"); + goto free; + } + } + return 0; + +free: + free(template->template_data); + return -1; +} + +static int verify_template_hash(struct event *template_digest) +{ + int rc; + u_int8_t local_digest[SHA_DIGEST_LENGTH] = {0}; /* Renamed to avoid conflict */ + unsigned int len = SHA_DIGEST_LENGTH; /* Use unsigned int for EVP_Digest length parameter */ + + rc = memcmp(fox, template_digest, sizeof(fox)); + if (rc != 0) { + EVP_Digest(template_digest->template_data, template_digest->template_data_len, + local_digest, &len, EVP_sha1(), NULL); + rc = memcmp(local_digest, template_digest->header.digest, len); + if (rc != 0) + printf("- %s\n", "failed"); + } + return rc != 0 ? 1 : 0 ; +} + +static int check_one_template(struct event *template, FILE *fp, char *digest_list_file, + u_int8_t event_template_data_hash[SHA256_DIGEST_LENGTH], bool verify) +{ + int ret = -1; + char digest_hex[MAX_CMD_LEN * 2] = {0}; + int hash_failed = 0; + int i; + unsigned int len = SHA256_DIGEST_LENGTH; /* Use unsigned int for EVP_Digest length parameter */ + + display_digest(template->header.digest, SHA_DIGEST_LENGTH); + memset(template->name, 0, sizeof(template->name)); + if (template->header.name_len > TCG_EVENT_NAME_LEN_MAX || + fread(template->name, template->header.name_len, 1, fp) == 0) { + RTLS_ERR("Reading name failed\n"); + return -1; + } + printf(" %s ", template->name); + + if (read_template_data(template, fp) < 0) { + RTLS_ERR("Reading of measurement entry failed\n"); + return -1; + } + + if (parse_template_data(template) != 0) { + RTLS_ERR("Parsing of measurement entry failed\n"); + goto free; + } + + for (i = 0; i < template->file_digest_len; i++) { + sprintf(digest_hex + i * 2, "%02x", (*(template->file_digest + i) & 0xff)); + } + char cmd_str[MAX_CMD_LEN] = {0}; + if (template->file_digest_len * 2 + strlen("grep -E -i \"^$\" > /dev/null") + strlen(digest_list_file) >= MAX_CMD_LEN) { + RTLS_ERR("Digest list file name too long.\n"); + goto free; + } + sprintf(cmd_str, "grep -E -i \"^%s$\" %s > /dev/null", digest_hex, digest_list_file); + if (system(cmd_str) != 0) { + RTLS_ERR("Failed to verify file hash.\n"); + goto free; + } + + if (verify) { + if (verify_template_hash(template) != 0) { + hash_failed++; + } + } + + if (EVP_Digest(template->template_data, template->template_data_len, + event_template_data_hash, &len, EVP_sha256(), NULL) != 1) { + RTLS_ERR("Failed to calculate SHA256 hash of template data.\n"); + goto free; + } + ret = hash_failed; + +free: + free(template->template_data); + return ret; +} + +/* + * calculate the SHA1 aggregate-pcr value based on the + * IMA runtime binary measurements. + * + * --validate: forces validation of the aggregrate pcr value + * for an invalidated PCR. Replace all entries in the + * runtime binary measurement list with 0x00 hash values, + * which indicate the PCR was invalidated, either for + * "a time of measure, time of use"(ToMToU) error, or a + * file open for read was already open for write, with + * 0xFF's hash value, when calculating the aggregate + * pcr value. + * + * --verify: for all IMA template entries in the runtime binary + * measurement list, calculate the template hash value + * and compare it with the actual template hash value. + * + * For records with a signature, verify the file data + * hash against the file signature. + * + * Return the number of incorrect hash measurements + * and signatures. + * + * template info: list #, PCR-register #, template hash, template name + * IMA info: IMA hash, filename hint + * + * Ouput: displays the aggregate-pcr value + * Return code: if verification enabled, returns number of verification + * errors. + */ +int ima_measure(const void *reference_pcr_value, size_t reference_pcr_value_len, + char *digest_list_file, int validate, int verify, int target_pcr_index) +{ + int ret = 0; + FILE *fp; + struct event template; + u_int8_t calculated_pcr_aggregate[SHA256_DIGEST_LENGTH]; + u_int8_t event_data_hash[SHA256_DIGEST_LENGTH]; + u_int8_t pcr_extension_buffer[SHA256_DIGEST_LENGTH * 2]; + int count = 0; + int hash_failed = 0; + unsigned int tmp_len = SHA256_DIGEST_LENGTH; + + fp = fopen(CLIENT_IMA_MEASUREMENTS_PATH, "r"); + if (!fp) { + RTLS_ERR("fn: %s\n", CLIENT_IMA_MEASUREMENTS_PATH); + perror("Unable to open file"); + return -1; + } + + memset(calculated_pcr_aggregate, 0, SHA256_DIGEST_LENGTH); + memset(event_data_hash, 0, SHA256_DIGEST_LENGTH); + memset(pcr_extension_buffer, 0, SHA256_DIGEST_LENGTH * 2); + memset(zero, 0, SHA_DIGEST_LENGTH); + memset(fox, 0xff, SHA_DIGEST_LENGTH); + + RTLS_INFO("### PCR HASH TEMPLATE-NAME\n"); + +#if OPENSSL_VERSION_NUMBER <= OPENSSL_1_1_0 + OpenSSL_add_all_digests(); +#endif + + while (fread(&template.header, sizeof(template.header), 1, fp)) { + RTLS_INFO("%3d %03u ", count++, template.header.pcr); + + + /* + * check_one_template verifies the file digest against the digest_list_file + * and calculates SHA256 hash of template.template_data into event_data_hash. + * It also performs template hash verification if 'verify' is true, updating hash_failed. + */ + int check_ret = check_one_template(&template, fp, digest_list_file, event_data_hash, verify); + if (check_ret < 0) { + RTLS_ERR("check_one_template failed for an event\n"); + ret = -1; + break; + } + hash_failed += check_ret; + RTLS_INFO("\n"); + + + /* + * Extend simulated PCR with new template digest only if the event's PCR + * matches the target_pcr_index. + * IMA_AGGREGATE_ALL_PCRS can be used to revert to old behavior if needed. + */ + if (target_pcr_index == IMA_AGGREGATE_ALL_PCRS || template.header.pcr == (u_int32_t)target_pcr_index) { + if (validate) { + + /* + * This logic was present for the 'validate' flag regarding 0x00 or 0xFF hashes. + * It seems specific to how template.header.digest is handled, not event_data_hash. + * For PCR extension, we use event_data_hash (hash of template_data). + * The original code extended 'pcr' with 'digest' (which was H(template_data)). + * The original TOMTOU logic: + * if (memcmp(template.header.digest, zero, SHA_DIGEST_LENGTH) == 0) + * memset(template.header.digest, 0xFF, SHA_DIGEST_LENGTH); + * This part seems to modify template.header.digest if 'validate' is true, + * but PCR extension uses the hash of template_data. + * This might need careful review if template.header.digest is also meant to be extended. + * Based on standard PCR extension, it's H(event_data). + * The provided log shows 'digest' in check_one_template comes from template_data. + */ + } + + memcpy(pcr_extension_buffer, calculated_pcr_aggregate, SHA256_DIGEST_LENGTH); + memcpy(pcr_extension_buffer + SHA256_DIGEST_LENGTH, event_data_hash, SHA256_DIGEST_LENGTH); + + if (EVP_Digest(pcr_extension_buffer, 2 * SHA256_DIGEST_LENGTH, calculated_pcr_aggregate, &tmp_len, EVP_sha256(), NULL) != 1) { + RTLS_ERR("EVP_Digest for PCR extension failed\n"); + ret = -1; + break; + } + } + } + +#if OPENSSL_VERSION_NUMBER <= OPENSSL_1_1_0 + EVP_cleanup(); +#endif + fclose(fp); + + if (ret < 0) + return ret; + + RTLS_INFO("PCRAggr (re-calculated for PCR %d): ", target_pcr_index == IMA_AGGREGATE_ALL_PCRS ? 10 : target_pcr_index); + display_digest(calculated_pcr_aggregate, SHA256_DIGEST_LENGTH); + RTLS_INFO("\n"); + + if (verify) { + RTLS_INFO("Template hash verification (within check_one_template): %s, failures is %d \n", !hash_failed ? "Success" : "Failed", hash_failed); + } + + + /* + * 'validate' here means to compare the calculated_pcr_aggregate with the reference_pcr_value. + */ + if (validate) { + if (reference_pcr_value == NULL || reference_pcr_value_len != SHA256_DIGEST_LENGTH) { + RTLS_ERR("Invalid reference PCR value or length for validation.\n"); + return -1; + } + ret = memcmp(calculated_pcr_aggregate, reference_pcr_value, SHA256_DIGEST_LENGTH); + RTLS_INFO("Verifying if calculated PCR for index %d matches reference value: %s \n", + target_pcr_index, (ret == 0) ? "Success" : "Failed"); + if (ret != 0) { + return 1; + } + } + + + /* + * If verify was true and there were hash_failed, this could be considered a failure. + * However, the original code returns 0 if 'validate' passes or is false, + * and 1 if 'validate' fails. Errors are < 0. + * We will maintain that: 0 for success (including validate pass), 1 for validate fail, <0 for errors. + * The 'hash_failed' count is informational via logs. + */ + return 0; +} \ No newline at end of file diff --git a/migcvm-agent/src/attest/platform_verify.c b/migcvm-agent/src/attest/platform_verify.c new file mode 100644 index 0000000000000000000000000000000000000000..4ee87319b958773148f4f029d2de50b793fbe98f --- /dev/null +++ b/migcvm-agent/src/attest/platform_verify.c @@ -0,0 +1,381 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include "platform_verify.h" +#include +#include + +static char* extract_json_string_value(const char* json, const char* key) +{ + char* value = NULL; + char search_key[128]; + snprintf(search_key, sizeof(search_key), "\"%s\":", key); + + char* pos = strstr(json, search_key); + if (pos) { + pos = strchr(pos + strlen(search_key), '"'); + if (pos) { + pos++; + char* end = strchr(pos, '"'); + if (end) { + size_t len = end - pos; + value = (char*)malloc(len + 1); + if (value) { + strncpy(value, pos, len); + value[len] = '\0'; + } + } + } + } + return value; +} + +static char* read_file_content(const char* file_path) +{ + FILE* file = fopen(file_path, "r"); + if (!file) { + printf("Failed to open file: %s\n", file_path); + return NULL; + } + + fseek(file, 0, SEEK_END); + long file_size = ftell(file); + fseek(file, 0, SEEK_SET); + + if (file_size <= 0) { + printf("Invalid file size: %ld\n", file_size); + fclose(file); + return NULL; + } + + char* content = (char*)malloc(file_size + 1); + if (!content) { + printf("Failed to allocate memory for file content\n"); + fclose(file); + return NULL; + } + + size_t read_size = fread(content, 1, file_size, file); + content[read_size] = '\0'; + fclose(file); + + return content; +} + +static bool parse_measure_values(const char* json_content, platform_ref_values_t* ref_values) +{ + char* measure_start = strstr(json_content, "\"measure_value\""); + if (!measure_start) { + printf("measure_value not found in JSON\n"); + return false; + } + + char* array_start = strchr(measure_start, '['); + if (!array_start) { + printf("measure_value array start '[' not found\n"); + return false; + } + + char* array_end = strchr(array_start, ']'); + if (!array_end) { + printf("measure_value array end ']' not found\n"); + return false; + } + + size_t object_count = 0; + char* pos = array_start; + while (pos < array_end && (pos = strchr(pos, '{')) != NULL) { + object_count++; + pos++; + } + + if (object_count == 0) { + printf("No objects found in measure_value array\n"); + return false; + } + + ref_values->sw_components = (sw_component_ref_t*)malloc(object_count * sizeof(sw_component_ref_t)); + if (!ref_values->sw_components) { + printf("Failed to allocate memory for sw_components\n"); + return false; + } + ref_values->sw_comp_count = object_count; + + pos = array_start; + size_t index = 0; + while (pos < array_end && index < object_count) { + char* obj_start = strchr(pos, '{'); + if (!obj_start) { + break; + } + + char* obj_end = strchr(obj_start, '}'); + if (!obj_end) { + break; + } + + size_t obj_len = obj_end - obj_start + 1; + char* obj_content = (char*)malloc(obj_len + 1); + if (!obj_content) { + printf("Failed to allocate memory for object content\n"); + break; + } + + strncpy(obj_content, obj_start, obj_len); + obj_content[obj_len] = '\0'; + + char* firmware_name = extract_json_string_value(obj_content, "firware_name"); + char* measurement = extract_json_string_value(obj_content, "measurement"); + char* firmware_version = extract_json_string_value(obj_content, "firware_version"); + char* hash_algorithm = extract_json_string_value(obj_content, "hash_algorithm"); + + if (firmware_name && measurement && firmware_version && hash_algorithm) { + strncpy(ref_values->sw_components[index].firmware_name, firmware_name, + sizeof(ref_values->sw_components[index].firmware_name) - 1); + strncpy(ref_values->sw_components[index].measurement, measurement, + sizeof(ref_values->sw_components[index].measurement) - 1); + strncpy(ref_values->sw_components[index].firmware_version, firmware_version, + sizeof(ref_values->sw_components[index].firmware_version) - 1); + strncpy(ref_values->sw_components[index].hash_algorithm, hash_algorithm, + sizeof(ref_values->sw_components[index].hash_algorithm) - 1); + + index++; + } else { + printf("Failed to parse component %zu - missing required fields\n", index); + } + + if (firmware_name) { + free(firmware_name); + } + if (measurement) { + free(measurement); + } + if (firmware_version) { + free(firmware_version); + } + if (hash_algorithm) { + free(hash_algorithm); + } + free(obj_content); + + pos = obj_end + 1; + } + + ref_values->sw_comp_count = index; + return index > 0; +} + +bool load_platform_ref_values(const char *json_file_path, platform_ref_values_t *ref_values) +{ + if (!json_file_path || !ref_values) { + printf("Invalid parameters\n"); + return false; + } + + memset(ref_values, 0, sizeof(platform_ref_values_t)); + + char* json_content = read_file_content(json_file_path); + if (!json_content) { + return false; + } + + bool result = parse_measure_values(json_content, ref_values); + + free(json_content); + return result; +} + +static bool compare_hex_strings(const char* hex1, const char* hex2) +{ + if (!hex1 || !hex2) { + return false; + } + + size_t len1 = strlen(hex1); + size_t len2 = strlen(hex2); + if (len1 != len2) { + return false; + } + + for (size_t i = 0; i < len1; i++) { + if (tolower(hex1[i]) != tolower(hex2[i])) { + return false; + } + } + + return true; +} + +static char* qbuf_to_hex_string(const qbuf_t* buf) +{ + if (!buf || !buf->ptr || buf->len == 0) { + return NULL; + } + + char* hex_str = (char*)malloc(buf->len * 2 + 1); + if (!hex_str) { + return NULL; + } + + for (size_t i = 0; i < buf->len; i++) { + sprintf(hex_str + (i * 2), "%02x", ((unsigned char*)buf->ptr)[i]); + } + hex_str[buf->len * 2] = '\0'; + + return hex_str; +} + +static char* qbuf_to_string(const qbuf_t* buf) +{ + if (!buf || !buf->ptr || buf->len == 0) { + return NULL; + } + + char* str = (char*)malloc(buf->len + 1); + if (!str) { + return NULL; + } + + memcpy(str, buf->ptr, buf->len); + str[buf->len] = '\0'; + + return str; +} + +static bool verify_single_sw_component(const sw_comp_claims_t* token_comp, + const sw_component_ref_t* ref_comp, + const char* component_name, + uint64_t component_index) +{ + bool match = true; + bool has_mismatch = false; + + char* token_type = qbuf_to_string(&token_comp->component_type); + if (!token_type) { + printf("Component %lu (%s): Failed to extract component type from token\n", + component_index, component_name); + return false; + } + + if (strcmp(token_type, ref_comp->firmware_name) != 0) { + if (!has_mismatch) { + printf("Component %lu (%s) verification FAILED:\n", component_index, component_name); + has_mismatch = true; + } + printf(" Component Type: MISMATCH (token: %s, ref: %s)\n", + token_type, ref_comp->firmware_name); + match = false; + } + free(token_type); + + char* token_measurement = qbuf_to_hex_string(&token_comp->measurement); + if (!token_measurement) { + printf("Component %lu (%s): Failed to extract measurement from token\n", + component_index, component_name); + return false; + } + + if (!compare_hex_strings(token_measurement, ref_comp->measurement)) { + if (!has_mismatch) { + printf("Component %lu (%s) verification FAILED:\n", component_index, component_name); + has_mismatch = true; + } + printf(" Measurement: MISMATCH\n"); + printf(" Token: %s\n", token_measurement); + printf(" Reference: %s\n", ref_comp->measurement); + match = false; + } + free(token_measurement); + + char* token_version = qbuf_to_string(&token_comp->version); + if (!token_version) { + printf("Component %lu (%s): Failed to extract version from token\n", + component_index, component_name); + return false; + } + + if (strcmp(token_version, ref_comp->firmware_version) != 0) { + if (!has_mismatch) { + printf("Component %lu (%s) verification FAILED:\n", component_index, component_name); + has_mismatch = true; + } + printf(" Version: MISMATCH (token: %s, ref: %s)\n", + token_version, ref_comp->firmware_version); + match = false; + } + free(token_version); + + return match; +} + +bool verify_platform_sw_components(const platform_claims_t *platform_claims, + const platform_ref_values_t *ref_values) +{ + if (!platform_claims || !ref_values) { + printf("Invalid parameters for sw-components verification\n"); + return false; + } + + if (!platform_claims->sw_components || platform_claims->sw_comp_cnts == 0) { + printf("No software components found in platform token\n"); + return false; + } + + if (!ref_values->sw_components || ref_values->sw_comp_count == 0) { + printf("No reference values loaded\n"); + return false; + } + + bool overall_result = true; + size_t matched_count = 0; + + /* + * iterate through each component in token, + * find matching component in reference values + */ + for (uint64_t i = 0; i < platform_claims->sw_comp_cnts; i++) { + const sw_comp_claims_t* token_comp = &platform_claims->sw_components[i]; + + char* token_type = qbuf_to_string(&token_comp->component_type); + if (!token_type) { + printf("Failed to extract component type from token component %lu\n", i); + overall_result = false; + continue; + } + + bool found_match = false; + for (size_t j = 0; j < ref_values->sw_comp_count; j++) { + const sw_component_ref_t* ref_comp = &ref_values->sw_components[j]; + + if (strcmp(token_type, ref_comp->firmware_name) == 0) { + if (verify_single_sw_component(token_comp, ref_comp, token_type, i)) { + matched_count++; + found_match = true; + } else { + overall_result = false; + } + break; + } + } + + if (!found_match) { + printf("Component %lu (%s) verification FAILED:\n", i, token_type); + printf(" No matching reference component found for: %s\n", token_type); + overall_result = false; + } + + free(token_type); + } + + return overall_result; +} + +void free_platform_ref_values(platform_ref_values_t *ref_values) +{ + if (ref_values && ref_values->sw_components) { + free(ref_values->sw_components); + ref_values->sw_components = NULL; + ref_values->sw_comp_count = 0; + } +} \ No newline at end of file diff --git a/migcvm-agent/src/attest/rem.c b/migcvm-agent/src/attest/rem.c new file mode 100644 index 0000000000000000000000000000000000000000..c74d3bfbf47933d4705038aa4e340c97a18f3818 --- /dev/null +++ b/migcvm-agent/src/attest/rem.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ +#include +#include +#include "rem.h" + +bool rem_init(rem_t* rem) +{ + if (!rem) return false; + memset(rem->data, 0, REM_LENGTH_BYTES); + return true; +} + +bool rem_compare(const rem_t* rem1, const rem_t* rem2) +{ + if (!rem1 || !rem2) return false; + return memcmp(rem1->data, rem2->data, REM_LENGTH_BYTES) == 0; +} + +void rem_dump(const rem_t* rem) +{ + if (!rem) { + return; + } + + for (int i = 0; i < REM_LENGTH_BYTES; i++) { + printf("%02x", rem->data[i]); + } + printf("\n"); +} \ No newline at end of file diff --git a/migcvm-agent/src/attest/token_parse.c b/migcvm-agent/src/attest/token_parse.c new file mode 100644 index 0000000000000000000000000000000000000000..52cf32061e5218a7a96ba403eb5d0210126d4a55 --- /dev/null +++ b/migcvm-agent/src/attest/token_parse.c @@ -0,0 +1,803 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ +#include "token_parse.h" +#include +#include +#include +#include + +static bool check_item_type_size(const QCBORItem *item, int data_type, + const int *sizes, int num_sizes) +{ + if (item->uDataType != data_type) { + return false; + } + if (sizes != NULL && num_sizes > 0) { + for (int i = 0; i < num_sizes; ++i) { + if (item->val.string.len == sizes[i]) { + return true; + } + } + return false; + } else { + return true; + } +} + +static inline bool check_item_array_count(const QCBORItem *item, int count) +{ + if (item->uDataType != QCBOR_TYPE_ARRAY || count != item->val.uCount) { + return false; + } else { + return true; + } +} + +static inline bool check_item_bstring_size(const QCBORItem *item, const int *sizes, int num_sizes) +{ + return check_item_type_size(item, QCBOR_TYPE_BYTE_STRING, sizes, num_sizes); +} + +static inline bool check_item_tstring_size(const QCBORItem *item, const int *sizes, int num_sizes) +{ + return check_item_type_size(item, QCBOR_TYPE_TEXT_STRING, sizes, num_sizes); +} + +/* +/* Parse software component claims for platform token +*/ +static uint16_t parse_claims_sw_comp(sw_comp_claims_t *claim, + QCBORDecodeContext *decode_context, + QCBORItem *item) +{ + uint64_t claim_cnt = 0; + uint64_t map_item_count; + + QCBORDecode_VGetNext(decode_context, item); + if (item->uDataType != QCBOR_TYPE_MAP) { + printf("Attestation token error formatting: Invalid software component claim map format\n"); + return VIRTCCA_ERROR; + } + + map_item_count = item->val.uCount; + + for (int i = 0; i < map_item_count; i++) { + QCBORDecode_VGetNext(decode_context, item); + if (item->uLabelType == QCBOR_TYPE_INT64) { + claim_cnt = claim_cnt + 1; + switch (item->label.int64) { + case CCA_PLATFORM_SW_COMPONENT_MEASUREMENT_VALUE: + if (!check_item_bstring_size(item, NULL, 0)) { + printf("Software component measurement value is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->measurement = item->val.string; + break; + + case CCA_PLATFORM_SW_COMPONENT_SIGNER_ID: + if (!check_item_bstring_size(item, NULL, 0)) { + printf("Software component measurement signer is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->signer_id = item->val.string; + break; + + case CCA_PLATFORM_SW_COMPONENT_TYPE: + claim_cnt = claim_cnt - 1; + if (!check_item_tstring_size(item, NULL, 0)) { + printf("Software component type is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->component_type = item->val.string; + break; + + case CCA_PLATFORM_SW_COMPONENT_VERSION: + claim_cnt = claim_cnt - 1; + if (!check_item_tstring_size(item, NULL, 0)) { + printf("Software component version is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->version = item->val.string; + break; + + case CCA_PLATFORM_SW_COMPONENT_ALGORITHM_ID: + claim_cnt = claim_cnt - 1; + if (!check_item_tstring_size(item, NULL, 0)) { + printf("Software component algorithm is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->hash_algo_id = item->val.string; + break; + + default: + claim_cnt = claim_cnt - 1; + break; + } + } else { + printf("Un-supported label type %d\n", item->uLabelType); + return VIRTCCA_ERROR; + } + } + + if (claim_cnt != CCA_PLAT_SW_CLAIM_CNT) { + printf("Software component number of claims is incorrect\n"); + return VIRTCCA_ERROR; + } + return VIRTCCA_SUCCESS; +} + + +static uint64_t parse_platform_claims(platform_claims_t *claim, + qbuf_t *raw) +{ + QCBORItem item; + QCBORDecodeContext decode_context; + QCBORError ret = QCBOR_SUCCESS; + sw_comp_claims_t *comps; + int claim_cnt = 0; + + QCBORDecode_Init(&decode_context, *raw, QCBOR_DECODE_MODE_NORMAL); + QCBORDecode_VGetNext(&decode_context, &item); + if (item.uDataType != QCBOR_TYPE_MAP) { + printf("Attestation token error formatting: Invalid platform claim map format\n"); + return VIRTCCA_ERROR; + } + + while (ret == QCBOR_SUCCESS) { + QCBORDecode_VGetNext(&decode_context, &item); + ret = QCBORDecode_GetError(&decode_context); + if (ret != QCBOR_SUCCESS) { + break; + } + + if (item.uLabelType == QCBOR_TYPE_INT64) { + claim_cnt = claim_cnt + 1; + switch (item.label.int64) { + case CCA_PLATFORM_PROFILE: + if (!check_item_tstring_size(&item, NULL, 0)) { + printf("Platform token profile is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->profile = item.val.string; + break; + + case CCA_PLATFORM_CHALLENGE: + if (!check_item_bstring_size(&item, + (int[]){CCA_BYTE_SIZE_32, CCA_BYTE_SIZE_48, CCA_BYTE_SIZE_64}, 3)) { + printf("Platform challenge is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->challenge = item.val.string; + break; + + case CCA_PLATFORM_IMPLEMENTATION_ID: + if (!check_item_bstring_size(&item, (int[]){CCA_BYTE_SIZE_32}, 1)) { + printf("Platform implementation id is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->implementation_id = item.val.string; + break; + + case CCA_PLATFORM_INSTANCE_ID: + if (!check_item_bstring_size(&item, (int[]){CCA_BYTE_SIZE_33}, 1)) { + printf("Platform instance id is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->instance_id = item.val.string; + break; + + case CCA_PLATFORM_CONFIG: + if (!check_item_bstring_size(&item, NULL, 0)) { + printf("Platform config is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->config = item.val.string; + break; + + case CCA_PLATFORM_LIFESTYLE: + if (item.uDataType != QCBOR_TYPE_INT64) { + printf("Platform lifestyle is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->lifecycle = item.val.int64; + break; + + case CCA_PLATFORM_SW_COMPONENTS: + if (item.uDataType != QCBOR_TYPE_ARRAY) { + printf("Software components is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->sw_comp_cnts = item.val.uCount; + comps = (sw_comp_claims_t *)malloc(claim->sw_comp_cnts * + sizeof(sw_comp_claims_t)); + for (int i = 0; i < claim->sw_comp_cnts; i++) { + if (parse_claims_sw_comp(&comps[i], &decode_context, &item)) { + printf("Failed parsing software component %d\n", i); + } + } + claim->sw_components = comps; + break; + + case CCA_PLATFORM_VERIFICATION_SERVICE: + claim_cnt = claim_cnt - 1; + if (!check_item_tstring_size(&item, NULL, 0)) { + printf("Platform token verification-service is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->verification_service = item.val.string; + break; + + case CCA_PLATFORM_HASH_ALGO_ID: + if (!check_item_tstring_size(&item, NULL, 0)) { + printf("Platform token hash algo is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->hash_algo_id = item.val.string; + break; + + default: + claim_cnt = claim_cnt - 1; + break; + } + } else { + printf("Un-supported label type %d\n", item.uLabelType); + return VIRTCCA_ERROR; + } + } + + if (ret == QCBOR_ERR_NO_MORE_ITEMS) { + if (claim_cnt != CCA_PLAT_CLAIM_CNT) { + printf("Number of platform claims %d is incorrect\n", claim_cnt); + return VIRTCCA_ERROR; + } else { + return VIRTCCA_SUCCESS; + } + } else { + return VIRTCCA_ERROR; + } +} + +static uint64_t parse_cvm_claims(cvm_claims_t *claim, + qbuf_t *raw) +{ + QCBORItem item; + QCBORDecodeContext decode_context; + QCBORError ret = QCBOR_SUCCESS; + int claim_cnt = 0; + + QCBORDecode_Init(&decode_context, *raw, QCBOR_DECODE_MODE_NORMAL); + QCBORDecode_EnterBstrWrapped(&decode_context, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, NULL); + QCBORDecode_VGetNext(&decode_context, &item); + if (item.uDataType != QCBOR_TYPE_MAP) { + printf("Attestation token error formatting: Invalid cvm claim map format\n"); + return VIRTCCA_ERROR; + } + + while (ret == QCBOR_SUCCESS) { + QCBORDecode_VGetNext(&decode_context, &item); + ret = QCBORDecode_GetError(&decode_context); + if (ret != QCBOR_SUCCESS) { + break; + } + + if (item.uLabelType == QCBOR_TYPE_INT64) { + claim_cnt = claim_cnt + 1; + switch (item.label.int64) { + case CCA_CVM_CHALLENGE: + if (!check_item_bstring_size(&item, (int[]){CCA_BYTE_SIZE_64}, 1)) { + printf("cVM challenge is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->challenge = item.val.string; + break; + + case CCA_CVM_PERSONALIZATION_VALUE: + if (!check_item_bstring_size(&item, (int[]){CCA_BYTE_SIZE_64}, 1)) { + printf("cVM personalization value is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->rpv = item.val.string; + break; + + case CCA_CVM_INITIAL_MEASUREMENT: + if (!check_item_bstring_size(&item, + (int[]){CCA_BYTE_SIZE_32, CCA_BYTE_SIZE_48, CCA_BYTE_SIZE_64}, 3)) { + printf("cVM initial measurement is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->rim = item.val.string; + break; + + case CCA_CVM_EXTENSIBLE_MEASUREMENTS: + if (check_item_array_count(&item, CCA_CVM_EXTED_MEAS_SLOTS_NUM)) { + for (int i = 0; i < CCA_CVM_EXTED_MEAS_SLOTS_NUM; i++) { + QCBORDecode_VGetNext(&decode_context, &item); + if (check_item_bstring_size(&item, + (int[]){CCA_BYTE_SIZE_32, CCA_BYTE_SIZE_48, CCA_BYTE_SIZE_64}, 3)) { + claim->rem[i] = item.val.string; + } else { + printf("cVM extensible measurement is not in expected format\n"); + return VIRTCCA_ERROR; + } + } + } else { + printf("cVM extensible measurement is not in expected format\n"); + return VIRTCCA_ERROR; + } + break; + + case CCA_CVM_PUB_KEY: + /* + /* Support both RSA (550 bytes) and ECC (133 bytes) public keys for backward compatibility + */ + if (!check_item_bstring_size(&item, (int[]){CCA_BYTE_SIZE_133, CCA_BYTE_SIZE_550}, 2)) { + printf("cVM public key is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->pub_key = item.val.string; + break; + + case CCA_CVM_HASH_ALGO_ID: + if (!check_item_tstring_size(&item, NULL, 0)) { + printf("cVM hash algo is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->hash_algo_id = item.val.string; + break; + + case CCA_CVM_PUB_KEY_HASH_ALGO_ID: + if (!check_item_tstring_size(&item, NULL, 0)) { + printf("cVM public key hash algo is not in expected format\n"); + return VIRTCCA_ERROR; + } + claim->pub_key_hash_algo_id = item.val.string; + break; + + default: + claim_cnt = claim_cnt - 1; + break; + } + } else { + printf("Un-supported label type %d\n", item.uLabelType); + return VIRTCCA_ERROR; + } + } + + QCBORDecode_ExitBstrWrapped(&decode_context); + + if (ret == QCBOR_ERR_NO_MORE_ITEMS) { + if (claim_cnt != CCA_CVM_CLAIM_CNT) { + printf("Number of cVM claims %d is incorrect\n", claim_cnt); + return VIRTCCA_ERROR; + } else { + return VIRTCCA_SUCCESS; + } + } else { + return VIRTCCA_ERROR; + } +} + +static uint64_t parse_cose_sign1(cose_sign1_envelop_t *envelop, + qbuf_t *raw) +{ + QCBORItem item; + QCBORDecodeContext decode_context; + QCBORError ret; + + QCBORDecode_Init(&decode_context, *raw, QCBOR_DECODE_MODE_NORMAL); + QCBORDecode_VGetNext(&decode_context, &item); + + if (item.uDataType != QCBOR_TYPE_ARRAY || item.val.uCount != 4 || + QCBORDecode_GetNthTag(&decode_context, &item, 0) != CBOR_TAG_COSE_SIGN1) { + printf("Attestation token error formatting: Cannot get COSE_SIGN1 envelop\n"); + return VIRTCCA_ERROR; + } + + QCBORDecode_VGetNext(&decode_context, &item); + envelop->p_headers = item.val.string; + + QCBORDecode_VGetNext(&decode_context, &item); + envelop->np_headers = item.val.string; + + QCBORDecode_VGetNext(&decode_context, &item); + envelop->payload = item.val.string; + + QCBORDecode_VGetNext(&decode_context, &item); + envelop->signature = item.val.string; + + QCBORDecode_VGetNext(&decode_context, &item); + ret = QCBORDecode_Finish(&decode_context); + if (QCBOR_ERR_NO_MORE_ITEMS != ret) { + printf("Unexpected return code %d\n", ret); + return VIRTCCA_ERROR; + } + + return VIRTCCA_SUCCESS; +} + +uint64_t parse_cca_attestation_token(cca_token_t *token, uint8_t *raw_token, size_t raw_token_size) +{ + uint64_t status = VIRTCCA_SUCCESS; + QCBORItem item; + QCBORDecodeContext decode_context; + + qbuf_t raw_cca_token; + qbuf_t raw_platform_envelop; + qbuf_t raw_cvm_envelop; + errno = EIO; + QCBORError ret; + + /* + /* Initialize platform token fields to NULL/0 for backward compatibility + */ + memset(&token->platform_envelop, 0, sizeof(token->platform_envelop)); + memset(&token->platform_token, 0, sizeof(token->platform_token)); + token->platform_cose.ptr = NULL; + token->platform_cose.len = 0; + + raw_cca_token.ptr = raw_token; + raw_cca_token.len = raw_token_size; + + QCBORDecode_Init(&decode_context, raw_cca_token, QCBOR_DECODE_MODE_NORMAL); + QCBORDecode_VGetNext(&decode_context, &item); + /* + /* Updated to support both CVM-only and CVM+Platform token formats + */ + if (item.uDataType != QCBOR_TYPE_MAP || + (item.val.uCount != 1 && item.val.uCount != 2) || + QCBORDecode_GetNthTag(&decode_context, &item, 0) != TAG_CCA_TOKEN) { + printf("Attestation token error formatting: This may not be CCA token\n"); + return VIRTCCA_ERROR; + } + + /* + /* First try to parse platform token if present + */ + QCBORDecode_VGetNext(&decode_context, &item); + if (item.uDataType == QCBOR_TYPE_BYTE_STRING && + item.label.int64 == CCA_PLAT_TOKEN) { + raw_platform_envelop = item.val.string; + token->platform_cose = raw_platform_envelop; + status = parse_cose_sign1(&token->platform_envelop, + &raw_platform_envelop); + if (status != VIRTCCA_SUCCESS) { + printf("Failed to decode platform COSE_Sign1 envelop\n"); + return status; + } + + status = parse_platform_claims(&token->platform_token, &token->platform_envelop.payload); + if (status != VIRTCCA_SUCCESS) { + printf("Failed to parse platform claim map\n"); + return status; + } + + QCBORDecode_VGetNext(&decode_context, &item); + } + + /* + /* Parse CVM token + */ + if (item.uDataType != QCBOR_TYPE_BYTE_STRING || + item.label.int64 != CCA_CVM_TOKEN) { + printf("Attestation token error formatting: Cannot get CVM token\n"); + return VIRTCCA_ERROR; + } + + raw_cvm_envelop = item.val.string; + token->cvm_cose = raw_cvm_envelop; + status = parse_cose_sign1(&token->cvm_envelop, + &raw_cvm_envelop); + if (status != VIRTCCA_SUCCESS) { + printf("Failed to decode cvm COSE_Sign1 envelop\n"); + return status; + } + + status = parse_cvm_claims(&token->cvm_token, &token->cvm_envelop.payload); + if (status != VIRTCCA_SUCCESS) { + printf("Failed to parse cvm claim map\n"); + return status; + } + + QCBORDecode_VGetNext(&decode_context, &item); + ret = QCBORDecode_Finish(&decode_context); + if (QCBOR_ERR_NO_MORE_ITEMS != ret) { + printf("Unexpected return code %d\n", ret); + return ret; + } + + return VIRTCCA_SUCCESS; +} + +void print_cca_attestation_token_raw(const cca_token_t *token) +{ + const uint8_t *_p; + int _l; + printf("CCA attestation token\n"); + printf("CVM token\n"); + + printf("\tProtected headers: "); + _p = token->cvm_envelop.p_headers.ptr; + _l = token->cvm_envelop.p_headers.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n"); + + printf("\tUn-Protected headers: "); + _p = token->cvm_envelop.np_headers.ptr; + _l = token->cvm_envelop.np_headers.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n"); + + printf("\tPayload: "); + _p = token->cvm_envelop.payload.ptr; + _l = token->cvm_envelop.payload.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n"); + + printf("\tSignature: "); + _p = token->cvm_envelop.signature.ptr; + _l = token->cvm_envelop.signature.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n\n"); + + /* + /* Print platform token if present + */ + if (token->platform_cose.ptr != NULL && token->platform_cose.len > 0) { + printf("Platform token\n"); + + printf("\tProtected headers: "); + _p = token->platform_envelop.p_headers.ptr; + _l = token->platform_envelop.p_headers.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n"); + + printf("\tUn-Protected headers: "); + _p = token->platform_envelop.np_headers.ptr; + _l = token->platform_envelop.np_headers.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n"); + + printf("\tPayload: "); + _p = token->platform_envelop.payload.ptr; + _l = token->platform_envelop.payload.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n"); + + printf("\tSignature: "); + _p = token->platform_envelop.signature.ptr; + _l = token->platform_envelop.signature.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n\n"); + } +} + +void print_cca_attestation_token(const cca_token_t *token) +{ + const uint8_t *_p; + int _l; + + printf("Parsed CCA attestation token\n"); + printf("CVM token\n"); + + printf("\tChallenge: "); + _p = token->cvm_token.challenge.ptr; + _l = token->cvm_token.challenge.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n"); + + printf("\tRPV: "); + _p = token->cvm_token.rpv.ptr; + _l = token->cvm_token.rpv.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n"); + + printf("\tRIM: "); + _p = token->cvm_token.rim.ptr; + _l = token->cvm_token.rim.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n"); + + printf("\tREM[0]: "); + _p = token->cvm_token.rem[0].ptr; + _l = token->cvm_token.rem[0].len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n"); + + printf("\tREM[1]: "); + _p = token->cvm_token.rem[1].ptr; + _l = token->cvm_token.rem[1].len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n"); + + printf("\tREM[2]: "); + _p = token->cvm_token.rem[2].ptr; + _l = token->cvm_token.rem[2].len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n"); + + printf("\tREM[3]: "); + _p = token->cvm_token.rem[3].ptr; + _l = token->cvm_token.rem[3].len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n"); + + printf("\tHash Algo ID: "); + _p = token->cvm_token.hash_algo_id.ptr; + _l = token->cvm_token.hash_algo_id.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%c", _p[i]); + } + printf("\n"); + + printf("\tPublic Key: "); + _p = token->cvm_token.pub_key.ptr; + _l = token->cvm_token.pub_key.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n"); + + printf("\tPublic Key Hash Algo ID: "); + _p = token->cvm_token.pub_key_hash_algo_id.ptr; + _l = token->cvm_token.pub_key_hash_algo_id.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%c", _p[i]); + } + printf("\n"); + + /* + /* Print platform token details if present + */ + if (token->platform_cose.ptr != NULL && token->platform_cose.len > 0) { + printf("\nPlatform token\n"); + printf("\tProfile: "); + _p = token->platform_token.profile.ptr; + _l = token->platform_token.profile.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%c", _p[i]); + } + printf("\n"); + + printf("\tChallenge: "); + _p = token->platform_token.challenge.ptr; + _l = token->platform_token.challenge.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n"); + + printf("\tImplementation ID: "); + _p = token->platform_token.implementation_id.ptr; + _l = token->platform_token.implementation_id.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n"); + + printf("\tInstance ID: "); + _p = token->platform_token.instance_id.ptr; + _l = token->platform_token.instance_id.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n"); + + printf("\tConfig: "); + _p = token->platform_token.config.ptr; + _l = token->platform_token.config.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%02x", _p[i]); + } + printf("\n"); + + printf("\tLifecycle: %ld\n", token->platform_token.lifecycle); + + printf("\tSoftware Components (%ld):\n", token->platform_token.sw_comp_cnts); + for (int i = 0; i < token->platform_token.sw_comp_cnts; i++) { + printf("\t\tIndex %d:\n", i); + + printf("\t\tComponent Type: "); + _p = token->platform_token.sw_components[i].component_type.ptr; + _l = token->platform_token.sw_components[i].component_type.len; + if (_p != NULL && _l > 0) { + for (unsigned int j = 0U; j < _l; ++j) { + printf("%c", _p[j]); + } + } else { + printf("(not present)"); + } + printf("\n"); + + printf("\t\tMeasurement: "); + _p = token->platform_token.sw_components[i].measurement.ptr; + _l = token->platform_token.sw_components[i].measurement.len; + for (unsigned int j = 0U; j < _l; ++j) { + printf("%02x", _p[j]); + } + printf("\n"); + + printf("\t\tVersion: "); + _p = token->platform_token.sw_components[i].version.ptr; + _l = token->platform_token.sw_components[i].version.len; + if (_p != NULL && _l > 0) { + for (unsigned int j = 0U; j < _l; ++j) { + printf("%c", _p[j]); + } + } else { + printf("(not present)"); + } + printf("\n"); + + printf("\t\tSigner ID: "); + _p = token->platform_token.sw_components[i].signer_id.ptr; + _l = token->platform_token.sw_components[i].signer_id.len; + for (unsigned int j = 0U; j < _l; ++j) { + printf("%02x", _p[j]); + } + printf("\n"); + + printf("\t\tHash Algo ID: "); + _p = token->platform_token.sw_components[i].hash_algo_id.ptr; + _l = token->platform_token.sw_components[i].hash_algo_id.len; + if (_p != NULL && _l > 0) { + for (unsigned int j = 0U; j < _l; ++j) { + printf("%c", _p[j]); + } + } else { + printf("(not present)"); + } + printf("\n"); + } + + printf("\tVerification Service: "); + _p = token->platform_token.verification_service.ptr; + _l = token->platform_token.verification_service.len; + if (_p != NULL && _l > 0) { + for (unsigned int i = 0U; i < _l; ++i) { + printf("%c", _p[i]); + } + } else { + printf("(not present)"); + } + printf("\n"); + + printf("\tHash Algo ID: "); + _p = token->platform_token.hash_algo_id.ptr; + _l = token->platform_token.hash_algo_id.len; + for (unsigned int i = 0U; i < _l; ++i) { + printf("%c", _p[i]); + } + printf("\n"); + } +} diff --git a/migcvm-agent/src/attest/token_validate.c b/migcvm-agent/src/attest/token_validate.c new file mode 100644 index 0000000000000000000000000000000000000000..3a72454ed17ae04439b7f297107b5f767bcd7ca7 --- /dev/null +++ b/migcvm-agent/src/attest/token_validate.c @@ -0,0 +1,953 @@ +/* Copyright (c) 2022 Intel Corporation + * Copyright (c) 2020-2022 Alibaba Cloud + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "token_validate.h" +#include +#include +#include +#include +#include +#include "utils.h" +#include +#include +#include +#include +#include +#include +#include "t_cose/t_cose_common.h" +#include "t_cose/t_cose_sign1_verify.h" +#include "qcbor/qcbor_decode.h" + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +/* +/* Forward declarations for DER to COSE conversion functions +*/ +static bool ecdsa_signature_der_to_cose(const unsigned char *der_sig, size_t der_len, + unsigned char *cose_sig, size_t *cose_len, + int curve_nid); +static bool signature_is_der_format(const unsigned char *sig, size_t sig_len); +static int get_curve_nid_from_key(EVP_PKEY *pkey); + +/* +/* Certificate type detection function +/* Detects the type of certificate (RSA, ECC P-521, SM2) based on AIK certificate +*/ +cert_type_t detect_aik_cert_type(const char *aik_cert_path) +{ + char fullpath[PATH_MAX] = {0}; + FILE *fp = NULL; + X509 *cert = NULL; + EVP_PKEY *pkey = NULL; + cert_type_t cert_type = CERT_TYPE_UNKNOWN; + + if (!aik_cert_path) { + printf("Invalid AIK certificate path\n"); + return CERT_TYPE_UNKNOWN; + } + + /* Construct full path for AIK certificate */ + if (strstr(aik_cert_path, "/") != NULL) { + /* Path already contains directory separator */ + snprintf(fullpath, sizeof(fullpath), "%s", aik_cert_path); + } else { + /* Construct path with default prefix */ + snprintf(fullpath, sizeof(fullpath), "%s/%s", DEFAULT_CERT_PEM_PREFIX, aik_cert_path); + } + + fp = fopen(fullpath, "r"); + if (!fp) { + printf("Cannot open AIK certificate file: %s\n", fullpath); + return CERT_TYPE_UNKNOWN; + } + + cert = PEM_read_X509(fp, NULL, NULL, NULL); + if (!cert) { + printf("Failed to read X509 certificate from: %s\n", fullpath); + fclose(fp); + return CERT_TYPE_UNKNOWN; + } + + pkey = X509_get_pubkey(cert); + if (!pkey) { + printf("Failed to extract public key from certificate\n"); + X509_free(cert); + fclose(fp); + return CERT_TYPE_UNKNOWN; + } + + int key_type = EVP_PKEY_base_id(pkey); + + if (key_type == EVP_PKEY_RSA) { + cert_type = CERT_TYPE_RSA; + printf("Detected AIK certificate type: RSA\n"); + } else if (key_type == EVP_PKEY_EC) { + EC_KEY *ec_key = EVP_PKEY_get1_EC_KEY(pkey); + if (ec_key) { + const EC_GROUP *group = EC_KEY_get0_group(ec_key); + if (group) { + int curve_nid = EC_GROUP_get_curve_name(group); + if (curve_nid == NID_secp521r1) { + cert_type = CERT_TYPE_ECC_P521; + printf("Detected AIK certificate type: ECC P-521\n"); + } else { + printf("Detected ECC certificate with unsupported curve (NID: %d)\n", curve_nid); + cert_type = CERT_TYPE_UNKNOWN; + } + } + EC_KEY_free(ec_key); + } + } else { + /* Check for SM2 - this would need specific SM2 detection logic */ + printf("Detected certificate with key type: %d (checking for SM2)\n", key_type); + /* For now, assume SM2 detection would be added here */ + /* This is a placeholder - actual SM2 detection would need SM2-specific logic */ + cert_type = CERT_TYPE_UNKNOWN; + } + + EVP_PKEY_free(pkey); + X509_free(cert); + fclose(fp); + + return cert_type; +} + +/* +/* Configure certificate info structure based on detected certificate type +/* Sets appropriate URLs and filenames for certificate chain verification +*/ +void configure_cert_info_by_type(cert_info_t *cert_info, cert_type_t cert_type) +{ + if (!cert_info) { + printf("Invalid cert_info parameter\n"); + return; + } + + /* Set common values */ + strcpy(cert_info->cert_path_prefix, DEFAULT_CERT_PEM_PREFIX); + strcpy(cert_info->aik_cert_filename, DEFAULT_AIK_CERT_PEM_FILENAME); + + switch (cert_type) { + case CERT_TYPE_RSA: + printf("Configuring certificate chain for RSA\n"); + strcpy(cert_info->root_cert_filename, DEFAULT_ROOT_CERT_PEM_FILENAME); + strcpy(cert_info->sub_cert_filename, DEFAULT_SUB_CERT_PEM_FILENAME); + strcpy(cert_info->root_cert_url, DEFAULT_ROOT_CERT_URL); + strcpy(cert_info->sub_cert_url, DEFAULT_SUB_CERT_URL); + break; + + case CERT_TYPE_ECC_P521: + printf("Configuring certificate chain for ECC P-521\n"); + strcpy(cert_info->root_cert_filename, ECCP521_ROOT_CERT_PEM_FILENAME); + strcpy(cert_info->sub_cert_filename, ECCP521_SUB_CERT_PEM_FILENAME); + strcpy(cert_info->root_cert_url, ECCP521_ROOT_CERT_URL); + strcpy(cert_info->sub_cert_url, ECCP521_SUB_CERT_URL); + break; + + case CERT_TYPE_SM2: + printf("Configuring certificate chain for SM2\n"); + strcpy(cert_info->root_cert_filename, SM2_ROOT_CERT_PEM_FILENAME); + strcpy(cert_info->sub_cert_filename, SM2_SUB_CERT_PEM_FILENAME); + strcpy(cert_info->root_cert_url, SM2_ROOT_CERT_URL); + strcpy(cert_info->sub_cert_url, SM2_SUB_CERT_URL); + break; + + case CERT_TYPE_UNKNOWN: + default: + printf("Warning: Unknown certificate type, using RSA defaults\n"); + strcpy(cert_info->root_cert_filename, DEFAULT_ROOT_CERT_PEM_FILENAME); + strcpy(cert_info->sub_cert_filename, DEFAULT_SUB_CERT_PEM_FILENAME); + strcpy(cert_info->root_cert_url, DEFAULT_ROOT_CERT_URL); + strcpy(cert_info->sub_cert_url, DEFAULT_SUB_CERT_URL); + break; + } +} + +/* +/* Calculate SHA digest for challenge verification +*/ +static bool digest_sha(const void *msg, size_t msg_len, + const char *algorithm, + unsigned char *md_value, unsigned int *md_len) +{ + EVP_MD_CTX *mdctx; + const EVP_MD *md; + bool ret = false; + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + /* For OpenSSL versions < 1.1.0 */ + OpenSSL_add_all_digests(); +#endif + + md = EVP_get_digestbyname(algorithm); + if (!md) { + printf("Unknown algorithm %s\n", algorithm); + goto cleanup; + } + + mdctx = EVP_MD_CTX_new(); + if (!mdctx) { + printf("Failed to create MD context\n"); + goto cleanup; + } + + if (EVP_DigestInit_ex(mdctx, md, NULL) != 1) { + printf("Failed to initialize digest\n"); + goto cleanup; + } + + if (EVP_DigestUpdate(mdctx, msg, msg_len) != 1) { + printf("Failed to update digest\n"); + goto cleanup; + } + + if (EVP_DigestFinal_ex(mdctx, md_value, md_len) != 1) { + printf("Failed to finalize digest\n"); + goto cleanup; + } + + ret = true; + +cleanup: + if (mdctx) { + EVP_MD_CTX_free(mdctx); + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + /* For OpenSSL versions < 1.1.0 */ + EVP_cleanup(); +#endif + + return ret; +} + +/* +/* Enhanced initialization for signing key to support both RSA and ECC +*/ +static enum t_cose_err_t init_signing_key(struct t_cose_key *key_pair, + struct q_useful_buf_c pub_key) +{ + enum t_cose_err_t ret; + EVP_PKEY *pkey = NULL; + const unsigned char *pub_key_ptr = pub_key.ptr; + + pkey = d2i_PUBKEY(NULL, &pub_key_ptr, pub_key.len); + if (pkey == NULL) { + /* + /* If DER format fails, try raw ECC public key format + /* For P-521: 1 byte (0x04) + 66 bytes (x) + 66 bytes (y) = 133 bytes + */ + if (pub_key.len == 133 && ((unsigned char*)pub_key.ptr)[0] == 0x04) { + EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1); + if (!group) { + printf("Failed to create P-521 curve\n"); + ret = T_COSE_ERR_FAIL; + goto done; + } + + EC_POINT *point = EC_POINT_new(group); + if (!point) { + printf("Failed to create EC point\n"); + EC_GROUP_free(group); + ret = T_COSE_ERR_FAIL; + goto done; + } + + if (EC_POINT_oct2point(group, point, pub_key.ptr, pub_key.len, NULL) != 1) { + printf("Failed to convert raw data to EC point\n"); + EC_POINT_free(point); + EC_GROUP_free(group); + ret = T_COSE_ERR_FAIL; + goto done; + } + + EC_KEY *ec_key = EC_KEY_new(); + if (!ec_key || + EC_KEY_set_group(ec_key, group) != 1 || + EC_KEY_set_public_key(ec_key, point) != 1) { + printf("Failed to create EC_KEY\n"); + if (ec_key) { + EC_KEY_free(ec_key); + } + EC_POINT_free(point); + EC_GROUP_free(group); + ret = T_COSE_ERR_FAIL; + goto done; + } + + pkey = EVP_PKEY_new(); + if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, ec_key) != 1) { + printf("Failed to create EVP_PKEY from EC_KEY\n"); + if (pkey) { + EVP_PKEY_free(pkey); + } + EC_KEY_free(ec_key); + EC_POINT_free(point); + EC_GROUP_free(group); + ret = T_COSE_ERR_FAIL; + goto done; + } + + EC_KEY_free(ec_key); + EC_POINT_free(point); + EC_GROUP_free(group); + } else { + printf("Failed to load pubkey in any supported format\n"); + ret = T_COSE_ERR_FAIL; + goto done; + } + } + + key_pair->k.key_ptr = pkey; + key_pair->crypto_lib = T_COSE_CRYPTO_LIB_OPENSSL; + ret = T_COSE_SUCCESS; + +done: + return ret; +} + +static void free_signing_key(struct t_cose_key key_pair) +{ + EVP_PKEY_free(key_pair.k.key_ptr); +} + +static bool read_x509_from_pem(const char *prefix, const char *filename, X509 **x509_cert) +{ + char fullpath[PATH_MAX] = {0}; + FILE *pFile = NULL; + + snprintf(fullpath, sizeof(fullpath), "%s/%s", prefix, filename); + pFile = fopen(fullpath, "re"); + if (!pFile) { + printf("Cannot open pem file %s", fullpath); + return false; + } + + if (!PEM_read_X509(pFile, x509_cert, NULL, NULL)) { + printf("Failed to read x509 from file: %s\n", fullpath); + fclose(pFile); + return false; + } + + fclose(pFile); + return true; +} + +static bool x509_validate_signature(X509 *child_cert, X509 *intermediate_cert, X509 *parent_cert) +{ + bool ret = false; + X509_STORE *store = NULL; + X509_STORE_CTX *store_ctx = NULL; + + store = X509_STORE_new(); + if (!store) + goto err; + + if (X509_STORE_add_cert(store, parent_cert) != 1) { + printf("Failed to add parent_cert to x509_store\n"); + goto err; + } + + if (intermediate_cert) { + if (X509_STORE_add_cert(store, intermediate_cert) != 1) { + printf("Failed to add intermediate_cert to x509_store\n"); + goto err; + } + } + + store_ctx = X509_STORE_CTX_new(); + if (!store_ctx) { + printf("Failed to create x509_store_context\n"); + goto err; + } + + /* + /* Pass the store (parent and intermediate cert) and child cert (need + /* to be verified) into the store context + */ + if (X509_STORE_CTX_init(store_ctx, store, child_cert, NULL) != 1) { + printf("Failed to initialize 509_store_context\n"); + goto err; + } + + X509_STORE_CTX_set_cert(store_ctx, child_cert); + + ret = X509_verify_cert(store_ctx); + if (ret != 1) { + printf("Failed to verify x509 cert: %s\n", + X509_verify_cert_error_string(X509_STORE_CTX_get_error(store_ctx))); + goto err; + } + ret = true; + +err: + if (store_ctx) { + X509_STORE_CTX_free(store_ctx); + } + if (store) { + X509_STORE_free(store); + } + return ret; +} + +bool validate_aik_cert_chain(X509 *x509_aik, X509 *x509_sub, X509 *x509_root) +{ + bool ret; + + if (x509_aik == NULL || x509_sub == NULL || x509_root == NULL) { + return false; + } + + ret = x509_validate_signature(x509_root, NULL, x509_root); + if (!ret) { + printf("Failed to validate signature of x509_root cert\n"); + return ret; + } + + ret = x509_validate_signature(x509_sub, NULL, x509_root); + if (!ret) { + printf("Failed to validate signature of x509_sub cert\n"); + return ret; + } + + ret = x509_validate_signature(x509_aik, x509_sub, x509_root); + if (!ret) { + printf("Failed to validate signature of x509_aik cert\n"); + return ret; + } + + return ret; +} + +/* +/* Verify public key hash challenge +*/ +bool verify_pubkhash_challenge(qbuf_t pub_key, qbuf_t challenge, qbuf_t algorithm) +{ + unsigned char pubkey_hash[EVP_MAX_MD_SIZE]; + unsigned int pubkey_hash_len; + char algo[10]; + + if (strncmp("sha-256", algorithm.ptr, algorithm.len) == 0) { + memcpy(algo, "sha256", sizeof("sha256")); + } else if (strncmp("sha-512", algorithm.ptr, algorithm.len) == 0) { + memcpy(algo, "sha512", sizeof("sha512")); + } else { + printf("Unsupported sha algorithm\n"); + return false; + } + + if (!digest_sha(pub_key.ptr, pub_key.len, algo, pubkey_hash, &pubkey_hash_len)) { + printf("Failed to calculate the hash value\n"); + return false; + } + + if (memcmp(pubkey_hash, challenge.ptr, pubkey_hash_len) != 0) { + return false; + } + + return true; +} + +/* +/* Enhanced platform COSE signature verification with DER-to-COSE conversion support +/* Same DER format conversion capability as CVM token verification +*/ +bool verify_plat_cose_sign(qbuf_t signed_cose, X509 *x509_aik) +{ + qbuf_t payload; + enum t_cose_err_t ret; + struct t_cose_key key_pair; + struct t_cose_sign1_verify_ctx verify_ctx; + EVP_PKEY *pkey; + bool conversion_performed = false; + unsigned char *converted_cose_data = NULL; + size_t converted_cose_len = 0; + qbuf_t final_signed_cose = signed_cose; + + pkey = X509_get_pubkey(x509_aik); + if (!pkey) { + printf("Failed to extract pub-key from aik_cert\n"); + return false; + } + + int key_type = EVP_PKEY_base_id(pkey); + + key_pair.k.key_ptr = pkey; + key_pair.crypto_lib = T_COSE_CRYPTO_LIB_OPENSSL; + + if (key_type == EVP_PKEY_EC) { + QCBORDecodeContext decode_context; + QCBORItem item; + QCBORDecode_Init(&decode_context, signed_cose, QCBOR_DECODE_MODE_NORMAL); + + QCBORDecode_VGetNext(&decode_context, &item); + if (item.uDataType == QCBOR_TYPE_ARRAY && item.val.uCount == 4) { + QCBORDecode_VGetNext(&decode_context, &item); + QCBORDecode_VGetNext(&decode_context, &item); + QCBORDecode_VGetNext(&decode_context, &item); + QCBORDecode_VGetNext(&decode_context, &item); + + if (item.uDataType == QCBOR_TYPE_BYTE_STRING) { + const unsigned char *signature_data = item.val.string.ptr; + size_t signature_len = item.val.string.len; + + if (signature_is_der_format(signature_data, signature_len)) { + int curve_nid = get_curve_nid_from_key(pkey); + if (curve_nid > 0) { + int field_size = (curve_nid == NID_secp521r1) ? 66 : + (curve_nid == NID_secp384r1) ? 48 : 32; + unsigned char *cose_signature = malloc(field_size * 2); + size_t cose_sig_len = field_size * 2; + + if (ecdsa_signature_der_to_cose(signature_data, signature_len, + cose_signature, &cose_sig_len, curve_nid)) { + converted_cose_len = signed_cose.len - signature_len + cose_sig_len; + converted_cose_data = malloc(converted_cose_len); + + if (converted_cose_data) { + size_t prefix_len = (unsigned char*)signature_data - (unsigned char*)signed_cose.ptr; + memcpy(converted_cose_data, signed_cose.ptr, prefix_len); + + converted_cose_data[prefix_len - 1] = cose_sig_len; + memcpy(converted_cose_data + prefix_len, cose_signature, cose_sig_len); + + size_t remaining_len = signed_cose.len - prefix_len - signature_len; + if (remaining_len > 0) { + memcpy(converted_cose_data + prefix_len + cose_sig_len, + (unsigned char*)signature_data + signature_len, remaining_len); + } + + final_signed_cose.ptr = converted_cose_data; + final_signed_cose.len = converted_cose_len; + conversion_performed = true; + } + } + + free(cose_signature); + } + } + } + } + } + + t_cose_sign1_verify_init(&verify_ctx, 0); + t_cose_sign1_set_verification_key(&verify_ctx, key_pair); + + ret = t_cose_sign1_verify(&verify_ctx, final_signed_cose, &payload, NULL); + + if (converted_cose_data) { + free(converted_cose_data); + } + + if (ret != T_COSE_SUCCESS) { + printf("Platform token signature verification failed with t_cose error: %d\n", ret); + return false; + } + + return true; +} + +/* +/* Helper function: Convert DER format ECDSA signature to COSE format (r||s) +/* For P-521: DER format (~139 bytes) COSE format (132 bytes) +*/ +static bool ecdsa_signature_der_to_cose(const unsigned char *der_sig, size_t der_len, + unsigned char *cose_sig, size_t *cose_len, + int curve_nid) +{ + ECDSA_SIG *sig = NULL; + const BIGNUM *r = NULL; + const BIGNUM *s = NULL; + int field_size; + bool ret = false; + + switch (curve_nid) { + case NID_secp521r1: /* P-521 */ + field_size = 66; /* (521 + 7) / 8 = 66 bytes */ + break; + case NID_secp384r1: /* P-384 */ + field_size = 48; /* (384 + 7) / 8 = 48 bytes */ + break; + case NID_X9_62_prime256v1: /* P-256 */ + field_size = 32; /* (256 + 7) / 8 = 32 bytes */ + break; + default: + printf("Unsupported curve for DER to COSE conversion\n"); + return false; + } + + if (*cose_len < field_size * 2) { + printf("COSE signature buffer too small\n"); + return false; + } + + /* Parse DER signature */ + const unsigned char *p = der_sig; + sig = d2i_ECDSA_SIG(NULL, &p, der_len); + if (!sig) { + printf("Failed to parse DER signature\n"); + goto cleanup; + } + + /* Get r and s components */ + ECDSA_SIG_get0(sig, &r, &s); + if (!r || !s) { + printf("Failed to get r,s from ECDSA signature\n"); + goto cleanup; + } + + /* Convert r and s to fixed-length byte arrays */ + memset(cose_sig, 0, field_size * 2); + + /* Convert r to bytes (big-endian, fixed length) */ + int r_len = BN_num_bytes(r); + if (r_len > field_size) { + printf("r component too large for field size\n"); + goto cleanup; + } + BN_bn2bin(r, cose_sig + (field_size - r_len)); + + /* Convert s to bytes (big-endian, fixed length) */ + int s_len = BN_num_bytes(s); + if (s_len > field_size) { + printf("s component too large for field size\n"); + goto cleanup; + } + BN_bn2bin(s, cose_sig + field_size + (field_size - s_len)); + + *cose_len = field_size * 2; + ret = true; + +cleanup: + if (sig) { + ECDSA_SIG_free(sig); + } + return ret; +} + + +static bool signature_is_der_format(const unsigned char *sig, size_t sig_len) +{ + if (sig_len < 6 || sig[0] != 0x30) { + return false; + } + + size_t declared_len = sig[1]; + if (sig[1] & 0x80) { + int len_bytes = sig[1] & 0x7f; + if (len_bytes > 2 || len_bytes == 0) return false; + + declared_len = 0; + for (int i = 0; i < len_bytes; i++) { + declared_len = (declared_len << 8) | sig[2 + i]; + } + } + + return (declared_len + 2) <= sig_len; +} + + +static int get_curve_nid_from_key(EVP_PKEY *pkey) +{ + if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) { + return -1; + } + + EC_KEY *ec_key = EVP_PKEY_get1_EC_KEY(pkey); + if (!ec_key) { + return -1; + } + + const EC_GROUP *group = EC_KEY_get0_group(ec_key); + int nid = -1; + if (group) { + nid = EC_GROUP_get_curve_name(group); + } + + EC_KEY_free(ec_key); + return nid; +} + + +bool verify_cvm_cose_sign(qbuf_t signed_cose, qbuf_t pub_key) +{ + qbuf_t payload; + enum t_cose_err_t ret; + struct t_cose_key key_pair; + struct t_cose_sign1_verify_ctx verify_ctx; + bool conversion_performed = false; + unsigned char *converted_cose_data = NULL; + size_t converted_cose_len = 0; + qbuf_t final_signed_cose = signed_cose; + + ret = init_signing_key(&key_pair, pub_key); + if (ret != T_COSE_SUCCESS) { + printf("Failed to initialize key: %d\n", ret); + return false; + } + + EVP_PKEY *verification_key = (EVP_PKEY*)key_pair.k.key_ptr; + int key_type = EVP_PKEY_base_id(verification_key); + + if (key_type == EVP_PKEY_EC) { + QCBORDecodeContext decode_context; + QCBORItem item; + QCBORDecode_Init(&decode_context, signed_cose, QCBOR_DECODE_MODE_NORMAL); + + QCBORDecode_VGetNext(&decode_context, &item); + if (item.uDataType == QCBOR_TYPE_ARRAY && item.val.uCount == 4) { + QCBORDecode_VGetNext(&decode_context, &item); + QCBORDecode_VGetNext(&decode_context, &item); + QCBORDecode_VGetNext(&decode_context, &item); + QCBORDecode_VGetNext(&decode_context, &item); + + if (item.uDataType == QCBOR_TYPE_BYTE_STRING) { + const unsigned char *signature_data = item.val.string.ptr; + size_t signature_len = item.val.string.len; + + if (signature_is_der_format(signature_data, signature_len)) { + int curve_nid = get_curve_nid_from_key(verification_key); + if (curve_nid > 0) { + int field_size = (curve_nid == NID_secp521r1) ? 66 : + (curve_nid == NID_secp384r1) ? 48 : 32; + unsigned char *cose_signature = malloc(field_size * 2); + size_t cose_sig_len = field_size * 2; + + if (ecdsa_signature_der_to_cose(signature_data, signature_len, + cose_signature, &cose_sig_len, curve_nid)) { + converted_cose_len = signed_cose.len - signature_len + cose_sig_len; + converted_cose_data = malloc(converted_cose_len); + + if (converted_cose_data) { + size_t prefix_len = (unsigned char*)signature_data - (unsigned char*)signed_cose.ptr; + memcpy(converted_cose_data, signed_cose.ptr, prefix_len); + + converted_cose_data[prefix_len - 1] = cose_sig_len; + memcpy(converted_cose_data + prefix_len, cose_signature, cose_sig_len); + + size_t remaining_len = signed_cose.len - prefix_len - signature_len; + if (remaining_len > 0) { + memcpy(converted_cose_data + prefix_len + cose_sig_len, + (unsigned char*)signature_data + signature_len, remaining_len); + } + + final_signed_cose.ptr = converted_cose_data; + final_signed_cose.len = converted_cose_len; + conversion_performed = true; + } + } + + free(cose_signature); + } + } + } + } + } + + t_cose_sign1_verify_init(&verify_ctx, 0); + t_cose_sign1_set_verification_key(&verify_ctx, key_pair); + + ret = t_cose_sign1_verify(&verify_ctx, + final_signed_cose, + &payload, + NULL); + + if (converted_cose_data) { + free(converted_cose_data); + } + free_signing_key(key_pair); + + if (ret != T_COSE_SUCCESS) { + printf("t_cose_sign1_verify ret: %d\n", ret); + return false; + } + + return true; +} + +/* +/* Complete CCA token signature verification with platform token support +/* Enhanced to support ECC CPAK certificates and RAK keys +*/ +bool verify_cca_token_signatures(cert_info_t *cert_info, + qbuf_t plat_cose, + qbuf_t cvm_cose, + qbuf_t cvm_pub_key, + qbuf_t plat_challenge, + qbuf_t cvm_pub_key_algo) +{ + X509 *x509_root = X509_new(); + X509 *x509_sub = X509_new(); + X509 *x509_aik = X509_new(); + bool ret; + unsigned int ret_bits = 0xFFFFFFFF; + unsigned int index = 0; + + if (!x509_root || !x509_sub || !x509_aik) { + printf("Failed to init X509!\n"); + ret_bits = 0x7FFFFFFF; + goto free; + } + + if (plat_cose.ptr != NULL && plat_cose.len > 0) { + ret = verify_pubkhash_challenge(cvm_pub_key, plat_challenge, cvm_pub_key_algo); + printf("Verifying if cVM token RAK matches platform token challenge: %s \n", + ret ? "Success" : "Failed"); + if (ret == false) { + ret_bits &= ~(1 << index); + } + index += 1; + } + + ret = verify_cvm_cose_sign(cvm_cose, cvm_pub_key); + printf("Verifying if cVM token signature is signed by RAK: %s \n", + ret ? "Success" : "Failed"); + if (ret == false) { + ret_bits &= ~(1 << index); + } + index += 1; + + if (!read_x509_from_pem(cert_info->cert_path_prefix, + cert_info->aik_cert_filename, &x509_aik)) { + printf("Failed to read x509_aik cert from %s/%s\n", + cert_info->cert_path_prefix, cert_info->aik_cert_filename); + ret = false; + ret_bits &= ~(1 << index); + } + index += 1; + + if (plat_cose.ptr != NULL && plat_cose.len > 0) { + ret = verify_plat_cose_sign(plat_cose, x509_aik); + printf("Verifying if platform token signature is signed by IAK: %s \n", + ret ? "Success" : "Failed"); + if (ret == false) { + ret_bits &= ~(1 << index); + } + index += 1; + } + + if (!file_exists(cert_info->cert_path_prefix, + cert_info->root_cert_filename)) { + download_cert_pem(cert_info->cert_path_prefix, + cert_info->root_cert_filename, + cert_info->root_cert_url); + } + + if (!read_x509_from_pem(cert_info->cert_path_prefix, + cert_info->root_cert_filename, &x509_root)) { + printf("Failed to read x509_root cert\n"); + ret = false; + ret_bits &= ~(1 << index); + } + index += 1; + + if (!file_exists(cert_info->cert_path_prefix, + cert_info->sub_cert_filename)) { + download_cert_pem(cert_info->cert_path_prefix, + cert_info->sub_cert_filename, + cert_info->sub_cert_url); + } + + if (!read_x509_from_pem(cert_info->cert_path_prefix, + cert_info->sub_cert_filename, &x509_sub)) { + printf("Failed to read x509_sub cert\n"); + ret = false; + ret_bits &= ~(1 << index); + } + index += 1; + + ret = validate_aik_cert_chain(x509_aik, x509_sub, x509_root); + printf("Verifying IAK certificate chain: %s \n", + ret ? "Success" : "Failed"); + if (ret == false) { + ret_bits &= ~(1 << index); + } + index += 1; + + /* + /* In mixed CPAK/RAK scenarios, we consider the verification successful + /* if the critical components pass: + /* 1. CVM token signature verification (RAK signs CVM token) + /* 2. Platform token signature verification (CPAK signs platform token) + /* 3. Challenge binding (RAK public key hash matches platform challenge) + /* 4. Certificate chain validation + */ + bool critical_verifications_passed = true; + + if (plat_cose.ptr != NULL && plat_cose.len > 0) { + if (!(ret_bits & (1 << 0))) { + printf("Critical: Challenge binding failed\n"); + critical_verifications_passed = false; + } + if (!(ret_bits & (1 << 3))) { + printf("Critical: Platform token signature verification failed\n"); + critical_verifications_passed = false; + } + } + + if (!(ret_bits & (1 << 1))) { + printf("Critical: CVM token signature verification failed\n"); + critical_verifications_passed = false; + } + + /* + /* CRITICAL SECURITY FIX: ALL verification steps must pass + /* Certificate chain validation and certificate loading are MANDATORY + */ + printf("INFO: Total verification steps: %d, ret_bits: 0x%08X\n", index, ret_bits); + + /* Check certificate loading failures */ + if (plat_cose.ptr != NULL && plat_cose.len > 0) { + /* Platform token scenario: check all verification steps */ + if (!(ret_bits & (1 << 2))) { /* AIK cert loading */ + printf("Critical: AIK certificate loading failed\n"); + critical_verifications_passed = false; + } + if (!(ret_bits & (1 << 4))) { /* Root cert loading */ + printf("Critical: Root certificate loading failed\n"); + critical_verifications_passed = false; + } + if (!(ret_bits & (1 << 5))) { /* Sub cert loading */ + printf("Critical: Sub certificate loading failed\n"); + critical_verifications_passed = false; + } + if (!(ret_bits & (1 << 6))) { /* Certificate chain validation */ + printf("Critical: Certificate chain validation failed\n"); + critical_verifications_passed = false; + } + } else { + /* CVM-only token scenario: different bit positions */ + if (!(ret_bits & (1 << 1))) { /* AIK cert loading */ + printf("Critical: AIK certificate loading failed\n"); + critical_verifications_passed = false; + } + if (!(ret_bits & (1 << 2))) { /* Root cert loading */ + printf("Critical: Root certificate loading failed\n"); + critical_verifications_passed = false; + } + if (!(ret_bits & (1 << 3))) { /* Sub cert loading */ + printf("Critical: Sub certificate loading failed\n"); + critical_verifications_passed = false; + } + if (!(ret_bits & (1 << 4))) { /* Certificate chain validation */ + printf("Critical: Certificate chain validation failed\n"); + critical_verifications_passed = false; + } + } + +free: + X509_free(x509_root); + X509_free(x509_sub); + X509_free(x509_aik); + + printf("CCA token signature validate [%s]\n", + critical_verifications_passed ? "Success" : "Failed"); + + return critical_verifications_passed; +} diff --git a/migcvm-agent/src/attest/utils.c b/migcvm-agent/src/attest/utils.c new file mode 100644 index 0000000000000000000000000000000000000000..8722fcdf4624f125fe3de2dc18b6df6fdd58732c --- /dev/null +++ b/migcvm-agent/src/attest/utils.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ +#include +#include +#include +#include +#include +#include +#include "utils.h" + +#define CMDLINE_SIZE 1000 + +int hex_to_bytes(unsigned char *in, size_t in_len, unsigned char *out, size_t *out_len) +{ + int i; + + if (in == NULL || out == NULL || out_len == NULL) { + printf("Param is NULL."); + return 1; + } + if (in_len % 2 != 0 || in_len / 2 > *out_len) { + printf("Invalid input size.\n"); + return 1; + } + for (i = 0; i < in_len / 2; i++) { + if (sscanf(in + i * 2, "%2hhx", out + i) != 1) { + printf("Invalid input.\n"); + return 1; + } + } + *out_len = i; + + return 0; +} + +int download_cert_pem(const char *prefix, const char *filename, const char *url) +{ + int count = 0; + char cmdline_str[CMDLINE_SIZE] = {0}; + + count = snprintf(cmdline_str, sizeof(cmdline_str), "wget -O %s/%s %s", + prefix, filename, url); + if (count >= CMDLINE_SIZE) { + printf("Param too long.\n"); + return 1; + } + + if (!file_exists(prefix, filename)) { + if (system(cmdline_str) != 0) { + printf("Failed to download %s/%s\n", prefix, filename); + return 1; + } + } + + return 0; +} + +int file_exists(const char *prefix, const char *filename) +{ + char fullpath[PATH_MAX] = {0}; + snprintf(fullpath, sizeof(fullpath), "%s/%s", prefix, filename); + return access(fullpath, F_OK) == 0; +} + +/* + * File reading utility function + * Reads binary data from a file and returns it as a dynamically allocated buffer + */ +uint8_t* read_file_data(const char* filename, size_t* out_size) +{ + if (!filename || !out_size) { + return NULL; + } + + /* Open file */ + FILE* fp = fopen(filename, "rb"); + if (!fp) { + printf("Error: Could not open file: %s\n", filename); + return NULL; + } + + /* Get file size */ + fseek(fp, 0, SEEK_END); + size_t file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + /* Allocate memory */ + uint8_t* data = (uint8_t*)malloc(file_size); + if (!data) { + printf("Error: Failed to allocate memory for file data\n"); + fclose(fp); + return NULL; + } + + /* Read data */ + if (fread(data, 1, file_size, fp) != file_size) { + printf("Error: Failed to read file data\n"); + free(data); + fclose(fp); + return NULL; + } + + fclose(fp); + *out_size = file_size; + return data; +} + +/* + * Text file reading utility function + * Reads text data from a file and returns it as a null-terminated string + */ +char* read_text_file(const char* filename, size_t* out_size) +{ + if (!filename || !out_size) { + return NULL; + } + + /* Open file */ + FILE* fp = fopen(filename, "r"); + if (!fp) { + printf("Error: Could not open file: %s\n", filename); + return NULL; + } + + /* Get file size */ + fseek(fp, 0, SEEK_END); + size_t file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + /* Allocate memory (add 1 for null terminator) */ + char* data = (char*)malloc(file_size + 1); + if (!data) { + printf("Error: Failed to allocate memory for file data\n"); + fclose(fp); + return NULL; + } + + /* Read data */ + if (fread(data, 1, file_size, fp) != file_size) { + printf("Error: Failed to read file data\n"); + free(data); + fclose(fp); + return NULL; + } + + /* Add null terminator */ + data[file_size] = '\0'; + + fclose(fp); + *out_size = file_size; + return data; +} + +/* + * File saving utility function + * Saves binary data to a file + */ +bool save_file_data(const char *file_name, const unsigned char *data, size_t size) +{ + FILE *file = fopen(file_name, "wb"); + if (file == NULL) { + printf("Failed to open file %s for writing.\n", file_name); + return false; + } + + size_t bytes_written = fwrite(data, 1, size, file); + if (bytes_written != size) { + printf("Failed to write data to file %s.\n", file_name); + fclose(file); + return false; + } + + fclose(file); + return true; +} diff --git a/migcvm-agent/src/attest/verify.c b/migcvm-agent/src/attest/verify.c new file mode 100644 index 0000000000000000000000000000000000000000..b4227d5ef44f653d1edbde724f3d15e8be07b1d9 --- /dev/null +++ b/migcvm-agent/src/attest/verify.c @@ -0,0 +1,386 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "binary_blob.h" +#include "event_log.h" +#include "firmware_state.h" +#include "verify.h" + +/* Length of REM value read from rem.txt file (each value is 32 bytes, represented as 64 hex characters) */ +#define REM_HEX_LENGTH 64 +#define HASH_STR_LENGTH 64 + +/* Forward declarations of all static functions */ +static bool hex_str_to_bytes(const char* hex_str, uint8_t* bytes, size_t length); +static void bytes_to_hex_string(const uint8_t* bytes, size_t len, char* hex_str); +static bool parse_json_file(const char* filename, firmware_reference_t* ref); +static void free_firmware_reference(firmware_reference_t* ref); +static bool compare_and_print_hash(const char* component_name, const char* ref_hash, + const uint8_t* actual_hash, size_t hash_size); +static char* extract_json_string(const char* json, const char* key); + +static bool hex_str_to_bytes(const char* hex_str, uint8_t* bytes, size_t length) +{ + if (!hex_str || !bytes || strlen(hex_str) != length * 2) { + return false; + } + + for (size_t i = 0; i < length; i++) { + char hex[3] = {hex_str[i * 2], hex_str[i * 2 + 1], 0}; + unsigned int value; + if (sscanf(hex, "%02x", &value) != 1) { + return false; + } + bytes[i] = (uint8_t)value; + } + return true; +} + +bool read_token_rem(rem_t rems[REM_COUNT]) +{ + /* Read REM file content */ + size_t file_size; + char* file_content = read_text_file(g_config.rem_file, &file_size); + if (!file_content) { + return false; + } + + char* line = file_content; + int rem_index = 0; + bool success = false; + + /* Process each line */ + while (line && rem_index < REM_COUNT) { + /* Find end of line */ + char* newline = strchr(line, '\n'); + if (newline) { + *newline = '\0'; + } + + /* Find REM marker */ + char* pos = strstr(line, "REM["); + if (pos) { + /* Find the REM value hex string */ + pos = strchr(line, ':'); + if (pos) { + pos++; /* Skip colon */ + + /* Skip spaces */ + while (*pos == ' ') { + pos++; + } + + /* Convert hex string to byte array */ + if (!hex_str_to_bytes(pos, rems[rem_index].data, REM_LENGTH_BYTES)) { + printf("Error: Failed to parse REM[%d] value\n", rem_index); + goto cleanup; + } + + rem_index++; + } + } + + /* Move to next line */ + line = newline ? newline + 1 : NULL; + } + + success = (rem_index == REM_COUNT); + +cleanup: + free(file_content); + return success; +} + +void verify_single_rem(int rem_index, const rem_t* rem1, const rem_t* rem2) +{ + if (!rem1 || !rem2) { + printf("Error: Invalid REM pointers for verification\n"); + return; + } + + if (rem_compare(rem1, rem2)) { + printf("REM[%d] passed the verification.\n", rem_index); + printf("Expected: "); + rem_dump(rem1); + printf("Got: "); + rem_dump(rem2); + } else { + printf("REM[%d] did not pass the verification\n", rem_index); + printf("Expected: "); + rem_dump(rem1); + printf("Got: "); + rem_dump(rem2); + } +} + +bool verify_firmware_state(const char* json_file, const firmware_log_state_t* state) +{ + if (!json_file || !state) { + return false; + } + + firmware_reference_t ref = {0}; + bool result = false; + + /* Parse JSON file */ + if (!parse_json_file(json_file, &ref)) { + printf("Error: Failed to parse JSON file\n"); + return false; + } + + /* Verify hash algorithm */ + if (strcmp(ref.hash_alg, "sha-256") != 0) { + printf("Error: Unsupported hash algorithm: %s\n", ref.hash_alg); + goto cleanup; + } + + printf("\nVerifying firmware components...\n"); + + /* Verify EFI state (grub) */ + if (state->efi && state->efi->image_count > 0) { + bool found_match = false; + for (uint32_t i = 0; i < state->efi->image_count; i++) { + if (compare_and_print_hash("GRUB", ref.grub, + state->efi->images[i].image_hash, + state->efi->images[i].image_hash_size)) { + found_match = true; + break; + } + } + if (!found_match) { + goto cleanup; + } + } + + /* Verify GRUB configuration */ + if (state->grub && state->grub->config_hash) { + if (!compare_and_print_hash("GRUB config", ref.grub_cfg, + state->grub->config_hash, + state->grub->config_hash_size)) { + goto cleanup; + } + } + + /* Verify kernel and initramfs - match any kernel version */ + if (state->linux_kernel) { + bool kernel_match = false; + + /* Try to match against any kernel version in the reference */ + for (int i = 0; i < ref.kernel_count; i++) { + bool version_match = true; + + /* Check kernel hash if available */ + if (state->linux_kernel->kernel_hash) { + if (!ref.kernels[i].kernel) { + printf("FAILED: Kernel hash exists but reference is missing\n"); + version_match = false; + } else if (!compare_and_print_hash("Kernel", ref.kernels[i].kernel, + state->linux_kernel->kernel_hash, + state->linux_kernel->kernel_hash_size)) { + version_match = false; + } + } + + /* Check initramfs hash if available */ + if (version_match && state->linux_kernel->initrd_hash) { + if (!ref.kernels[i].initramfs) { + printf("FAILED: Initramfs hash exists but reference is missing\n"); + version_match = false; + } else if (!compare_and_print_hash("Initramfs", ref.kernels[i].initramfs, + state->linux_kernel->initrd_hash, + state->linux_kernel->initrd_hash_size)) { + version_match = false; + } + } + + /* If this version matches, we're done */ + if (version_match) { + if (ref.kernels[i].version) { + printf("Matched kernel version: %s\n", ref.kernels[i].version); + } + kernel_match = true; + break; + } + } + + /* If no kernel matched, verification fails */ + if (!kernel_match) { + goto cleanup; + } + } + + printf("\nAll firmware components verification passed\n"); + result = true; + +cleanup: + free_firmware_reference(&ref); + return result; +} + + +/* Helper function: Convert byte array to hex string */ +static void bytes_to_hex_string(const uint8_t* bytes, size_t len, char* hex_str) +{ + for (size_t i = 0; i < len; i++) { + sprintf(hex_str + (i * 2), "%02x", bytes[i]); + } +} + +/* Helper function: Extract string value from JSON */ +static char* extract_json_string(const char* json, const char* key) +{ + char* value = NULL; + char search_key[64]; + snprintf(search_key, sizeof(search_key), "\"%s\":", key); + + char* pos = strstr(json, search_key); + if (pos) { + pos = strchr(pos + strlen(search_key), '"'); + if (pos) { + pos++; /* Skip quote */ + char* end = strchr(pos, '"'); + if (end) { + size_t len = end - pos; + value = (char*)malloc(len + 1); + if (value) { + strncpy(value, pos, len); + value[len] = '\0'; + } + } + } + } + return value; +} + +/* Helper function: Parse JSON file */ +static bool parse_json_file(const char* filename, firmware_reference_t* ref) +{ + if (!filename || !ref) { + return false; + } + + size_t file_size; + char* json_content = read_text_file(filename, &file_size); + if (!json_content) { + return false; + } + + /* Initialize kernels array */ + ref->kernels = NULL; + ref->kernel_count = 0; + + ref->grub = extract_json_string(json_content, "grub"); + ref->grub_cfg = extract_json_string(json_content, "grub.cfg"); + ref->hash_alg = extract_json_string(json_content, "hash_alg"); + + /* Parse kernels array */ + char* kernels_start = strstr(json_content, "\"kernels\":"); + if (kernels_start) { + kernels_start = strchr(kernels_start, '['); + if (kernels_start) { + char* kernels_end = strchr(kernels_start, ']'); + if (kernels_end) { + /* Count number of kernel objects */ + char* ptr = kernels_start; + while (ptr < kernels_end) { + if (*ptr == '{') { + ref->kernel_count++; + } + ptr++; + } + + if (ref->kernel_count > 0) { + /* Allocate memory for kernel objects */ + ref->kernels = (kernel_version_t*)calloc(ref->kernel_count, sizeof(kernel_version_t)); + if (!ref->kernels) { + goto error; + } + + /* Extract each kernel object */ + int idx = 0; + ptr = kernels_start; + while (ptr < kernels_end && idx < ref->kernel_count) { + ptr = strchr(ptr, '{'); + if (!ptr || ptr >= kernels_end) break; + + char* obj_end = strchr(ptr, '}'); + if (!obj_end || obj_end >= kernels_end) break; + + /* Extract temporary JSON object */ + size_t obj_len = obj_end - ptr + 1; + char* obj_json = (char*)malloc(obj_len + 1); + if (!obj_json) break; + + strncpy(obj_json, ptr, obj_len); + obj_json[obj_len] = '\0'; + + /* Parse kernel version object */ + ref->kernels[idx].version = extract_json_string(obj_json, "version"); + ref->kernels[idx].kernel = extract_json_string(obj_json, "kernel"); + ref->kernels[idx].initramfs = extract_json_string(obj_json, "initramfs"); + + free(obj_json); + idx++; + ptr = obj_end + 1; + } + } + } + } + } + + free(json_content); + return (ref->grub && ref->grub_cfg && ref->hash_alg && ref->kernel_count > 0); + +error: + free(json_content); + free_firmware_reference(ref); + return false; +} + +/* Helper function: Free JSON parsing results */ +static void free_firmware_reference(firmware_reference_t* ref) +{ + if (!ref) { + return; + } + free(ref->grub); + free(ref->grub_cfg); + free(ref->hash_alg); + + /* Free kernel version data */ + if (ref->kernels) { + for (int i = 0; i < ref->kernel_count; i++) { + free(ref->kernels[i].version); + free(ref->kernels[i].kernel); + free(ref->kernels[i].initramfs); + } + free(ref->kernels); + ref->kernels = NULL; + } + ref->kernel_count = 0; +} + +/* Helper function: Compare hash value and print result */ +static bool compare_and_print_hash(const char* component_name, const char* ref_hash, + const uint8_t* actual_hash, size_t hash_size) +{ + if (!ref_hash || !actual_hash) { + return false; + } + + char actual_hex[HASH_STR_LENGTH + 1] = {0}; + bytes_to_hex_string(actual_hash, hash_size, actual_hex); + + bool match = (strncmp(ref_hash, actual_hex, HASH_STR_LENGTH) == 0); + printf("\n%s verification %s\n", component_name, match ? "passed" : "failed"); + printf("Expected: %s\n", ref_hash); + printf("Got: %s\n", actual_hex); + return match; +} \ No newline at end of file diff --git a/migcvm-agent/src/migcvm-agent.c b/migcvm-agent/src/migcvm-agent.c new file mode 100644 index 0000000000000000000000000000000000000000..806fda2fb2ee618a7f827dbc310138c2cce51e6c --- /dev/null +++ b/migcvm-agent/src/migcvm-agent.c @@ -0,0 +1,1786 @@ +/* Copyright (c) 2021 Intel Corporation + * Copyright (c) 2020-2021 Alibaba Cloud + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/* server */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "utils.h" + +/* client */ +#include +#include +#include +#include "openssl/rand.h" +#include "openssl/x509.h" +#include "openssl/pem.h" +#include "openssl/bio.h" +#include "openssl/evp.h" +#include "openssl/ec.h" + +#include "token_parse.h" +#include "token_validate.h" +#include "platform_verify.h" +#include "common.h" +#include "ima_measure.h" + +#include "token_parse.h" +#include "token_validate.h" +#include "event_log.h" +#include "firmware_state.h" +#include "binary_blob.h" +#include "verify.h" +#include "config.h" + +#include "socket_agent.h" +#include "migcvm_tsi.h" + +#define MIG_TYPE_SERVER 1 +#define MIG_TYPE_CLIENT 2 + +rats_tls_log_level_t global_log_level = RATS_TLS_LOG_LEVEL_DEFAULT; + +static uint8_t g_rim_ref[MAX_MEASUREMENT_SIZE]; +static size_t g_rim_ref_size = MAX_MEASUREMENT_SIZE; +static int start_listening = 1; +pending_guest_rd_t *pending_guest_rds = NULL; + +rats_tls_err_t read_ima_measurements(uint8_t **value, size_t *size) +{ + FILE *file; + uint8_t buffer[IMA_READ_BLCOK_SIZE]; + size_t byte_read; + rats_tls_err_t ret = RATS_TLS_ERR_NO_MEM; + + file = fopen(SERVER_IMA_MEASUREMENTS_PATH, "rb"); + if (file == NULL) { + RTLS_ERR("Error opening file: %s\n", strerror(errno)); + return RATS_TLS_ERR_INVALID; + } + RTLS_INFO("file opened: %s\n", SERVER_IMA_MEASUREMENTS_PATH); + + while ((byte_read = fread(buffer, 1, IMA_READ_BLCOK_SIZE, file)) > 0) { + uint8_t *content = realloc(*value, *size + byte_read); + if (content == NULL) { + free(*value); + RTLS_ERR("memory reallocation failed"); + goto close; + } + + *value = content; + memcpy(*value + *size, buffer, byte_read); + *size += byte_read; + } + ret = RATS_TLS_ERR_NONE; + +close: + fclose(file); + return ret; +} + +int send_ima_log(rats_tls_handle handle) +{ + uint8_t *ima_meas_buf = NULL; + size_t ima_size = 0; + size_t send_size = 0; + size_t len = sizeof(size_t); + int ret = ENCLAVE_ATTESTER_ERR_UNKNOWN; + + ret = read_ima_measurements(&ima_meas_buf, &ima_size); + if (ret == 0 && ima_size != 0) { + RTLS_INFO("Read %zu bytes from binary_runtime_measurements\n", ima_size); + } else { + RTLS_ERR("Failed to read binary_runtime_measurements\n"); + return ret; + } + + ret = rats_tls_transmit(handle, &ima_size, &len); + if (ret != RATS_TLS_ERR_NONE || len != sizeof(size_t)) { + RTLS_ERR("Failed to send IMA log size %#x\n", ret); + ret = RATS_TLS_ERR_LOAD_TLS_WRAPPERS; + goto free; + } + + while (send_size < ima_size) { + len = ima_size - send_size; + ret = rats_tls_transmit(handle, ima_meas_buf + send_size, &len); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to send IMA log data %#x\n", ret); + goto free; + } + send_size += len; + } + +free: + free(ima_meas_buf); + return ret; +} + +int deal_rootfs_key(rats_tls_handle handle) +{ + uint8_t key_file[MAX] = {}; + size_t len = sizeof(key_file); + int ret = ENCLAVE_ATTESTER_ERR_UNKNOWN; + + ret = rats_tls_receive(handle, key_file, &len); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to receive %#x\n", ret); + return ret; + } + + ret = save_file_data(KEY_FILE_PATH, key_file, len) ? 0 : 1; + if (ret != 0) { + RTLS_ERR("Failed to save key file data.\n"); + return ret; + } + + return RATS_TLS_ERR_NONE; +} + +int send_ccel_data(rats_tls_handle handle) +{ + size_t ccel_size = 0; + uint8_t *ccel_data = NULL; + int ret = ENCLAVE_ATTESTER_ERR_UNKNOWN; + + ccel_data = read_file_data(SERVER_CCEL_ACPI_TABLE_PATH, &ccel_size); + if (!ccel_data) { + RTLS_ERR("Failed to read CCEL ACPI table data\n"); + return ret; + } + + size_t len = ccel_size; + ret = rats_tls_transmit(handle, ccel_data, &len); + if (ret != RATS_TLS_ERR_NONE || len != ccel_size) { + RTLS_ERR("Failed to send CCEL ACPI table data %#x\n", ret); + ret = RATS_TLS_ERR_LOAD_TLS_WRAPPERS; + goto free; + } + + RTLS_INFO("Successfully sent CCEL ACPI table (%zu bytes)\n", ccel_size); + +free: + free(ccel_data); + return ret; +} + +int send_event_log(rats_tls_handle handle) +{ + size_t event_log_size = 0; + uint8_t *event_log = NULL; + int ret = ENCLAVE_ATTESTER_ERR_UNKNOWN; + + event_log = read_file_data(SERVER_CCEL_EVENT_LOG_PATH, &event_log_size); + if (!event_log) { + RTLS_ERR("Failed to read event log data\n"); + return ret; + } + + /* First send the event log size */ + size_t len = sizeof(size_t); + ret = rats_tls_transmit(handle, &event_log_size, &len); + if (ret != RATS_TLS_ERR_NONE || len != sizeof(size_t)) { + RTLS_ERR("Failed to send event log size %#x\n", ret); + ret = RATS_TLS_ERR_LOAD_TLS_WRAPPERS; + goto free; + } + + /* Then send the event log data */ + size_t send_size = 0; + while (send_size < event_log_size) { + len = event_log_size - send_size; + ret = rats_tls_transmit(handle, event_log + send_size, &len); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to send event log data at offset %zu %#x\n", send_size, ret); + goto free; + } + send_size += len; + RTLS_INFO("Sent %zu of %zu bytes\n", send_size, event_log_size); + } + + RTLS_INFO("Successfully sent complete event log (%zu bytes)\n", event_log_size); + +free: + free(event_log); + return ret; +} + +int deal_client_req(rats_tls_handle handle) +{ + int ret = ENCLAVE_ATTESTER_ERR_UNKNOWN; + char buf[256] = {0}; + size_t len = sizeof(buf); + + ret = rats_tls_receive(handle, buf, &len); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to receive %#x\n", ret); + return ret; + } + + if (len >= sizeof(buf)) + len = sizeof(buf) - 1; + buf[len] = '\0'; + + RTLS_INFO("Received from Client: %s\n", buf); + + /* Process CCEL table request */ + if (strncmp(buf, "REQUEST_CCEL_TABLE", strlen("REQUEST_CCEL_TABLE")) == 0) { + ret = send_ccel_data(handle); + if (ret) { + RTLS_ERR("Send CCEL ACPI table failed %#x\n", ret); + return ret; + } + RTLS_INFO("Send CCEL ACPI table success\n"); + return deal_client_req(handle); + } + + /* Process event log request */ + if (strncmp(buf, "REQUEST_EVENT_LOG", strlen("REQUEST_EVENT_LOG")) == 0) { + ret = send_event_log(handle); + if (ret) { + RTLS_ERR("Send event log failed %#x\n", ret); + return ret; + } + RTLS_INFO("Send event log success\n"); + return deal_client_req(handle); + } + + /* Process TOKEN */ + if (strncmp(buf, TOKEN, strlen(TOKEN)) == 0) { + strcpy(buf, "Attestation Passed, Swtiching Root....."); + len = sizeof(buf); + ret = rats_tls_transmit(handle, buf, &len); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to transmit %#x\n", ret); + return ret; + } + return RATS_TLS_ERR_NONE; /* Return success directly */ + } + + if (strncmp(buf, REQUEST_IMA_LOG, strlen(REQUEST_IMA_LOG)) == 0) { + ret = send_ima_log(handle); + if (ret) { + RTLS_ERR("Send IMA log failed %#x\n", ret); + } else { + RTLS_INFO("Send IMA log success\n"); + /* expect receive the PASS TOKEN */ + ret = deal_client_req(handle); + } + return ret; + } + + if (strncmp(buf, ENABLE_FDE_TOKEN, strlen(ENABLE_FDE_TOKEN)) == 0) { + ret = deal_rootfs_key(handle); + if (ret) { + RTLS_ERR("Save rootfs key failed %#x\n", ret); + } else { + RTLS_INFO("Save rootfs key success\n"); + /* expect receive the PASS TOKEN */ + if ((ret = deal_client_req(handle))) { + RTLS_ERR("recevice PASS ACK failed %#x\n", ret); + return ret; + } + ret = 0x68; /* Return specific value for LUKS completion */ + } + return ret; + } + + strcpy(buf, "Attestation Failed, Continue....."); + /* Reply back to the client */ + len = sizeof(buf); + ret = rats_tls_transmit(handle, buf, &len); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to transmit %#x\n", ret); + } + ret = ENCLAVE_ATTESTER_ERR_UNKNOWN; + + return ret; +} + +/* client */ +static void display_hash_as_hex_string(const uint8_t *hash, size_t hash_len, const char *label) +{ + if (label) { + RTLS_INFO("%s: ", label); + } + for (size_t i = 0; i < hash_len; ++i) { + printf("%02x", hash[i]); + } + printf("\n"); +} + +static bool is_rem_entry_zero(const qbuf_t *rem_entry) +{ + if (!rem_entry || rem_entry->ptr == NULL || rem_entry->len == 0) { + return true; + } + for (size_t i = 0; i < rem_entry->len; ++i) { + if (((const uint8_t *)rem_entry->ptr)[i] != 0) { + return false; + } + } + return true; +} + +#define CHECK_LENGHT_ASSIGN(src, max_len, dst) \ +do { \ + if (strnlen(src, max_len) == max_len) { \ + printf("input param len too long.\n"); \ + return -1; \ + } \ + dst = src; \ +} while(0) + +/* Forward declarations */ +static char* extract_json_string(const char* json, const char* key); +static int parse_input_mig_agent_args(int argc, char **argv, mig_agent_args *args); +static int request_and_save_firmware_data(rats_tls_handle handle); +static int handle_eventlogs_command(void); + +int user_callback(void *args) +{ + rtls_evidence_t *ev = (rtls_evidence_t *)args; + bool ret = true; + FILE *fp; + size_t byte_write; + int validate = 1; + int verify = 1; + rtls_core_context_t *ctx = (rtls_core_context_t *)ev; + mig_agent_args *client_config = NULL; + rats_tls_handle handle = ctx; + + RTLS_INFO("Entering user_callback function\n"); + + /* Try to get client_config from core context */ + if (ctx && ctx->config.custom_claims && ctx->config.custom_claims->value) { + client_config = (mig_agent_args *)ctx->config.custom_claims->value; + RTLS_DEBUG("Got client_config from ctx->config\n"); + } + + RTLS_INFO("Context check: ctx=%p, client_config=%p\n", ctx, client_config); + + /* Step 1: Parse and verify token regardless of client_config */ + RTLS_DEBUG("Starting token verification\n"); + cca_token_t token = {0}; + cca_token_buf_t cca_token_buf = {0}; + memcpy(&cca_token_buf, ev->cca.evidence, ev->cca.evidence_sz); + + ret = parse_cca_attestation_token(&token, cca_token_buf.buf, cca_token_buf.buf_size); + if (ret != VIRTCCA_SUCCESS) { + RTLS_ERR("failed to parse virtcca token\n"); + return ENCLAVE_VERIFIER_ERR_CBOR; + } + RTLS_DEBUG("Token parsed successfully\n"); + + cert_info_t cert_info; + /* Detect AIK certificate type and configure certificate chain accordingly */ + // cert_type_t aik_cert_type = detect_aik_cert_type(DEFAULT_AIK_CERT_PEM_FILENAME); + // configure_cert_info_by_type(&cert_info, aik_cert_type); + + /* + * Determine if we have platform token or CVM-only token + * If we have platform token, use proper platform verification + * Otherwise, use empty placeholders for backward compatibility + */ + qbuf_t platform_cose = {.ptr = NULL, .len = 0}; + qbuf_t platform_challenge = {.ptr = NULL, .len = 0}; + + /* Check if platform token exists */ + bool has_platform = (token.platform_cose.len > 0); + if (has_platform) { + platform_cose = token.platform_cose; + platform_challenge = token.platform_token.challenge; + RTLS_INFO("Platform token detected, enabling full platform verification\n"); + } else { + RTLS_INFO("CVM-only token detected, using backward compatibility mode\n"); + } + + // ret = verify_cca_token_signatures(&cert_info, + // platform_cose, + // token.cvm_cose, + // token.cvm_token.pub_key, + // platform_challenge, + // token.cvm_token.pub_key_hash_algo_id); + // if (!ret) { + // RTLS_ERR("Token signature verification failed"); + // return false; + // } + RTLS_DEBUG("Token signatures verified successfully"); + + /* Step 2: Verify RIM */ + if (token.cvm_token.rim.len != g_rim_ref_size || + memcmp(g_rim_ref, token.cvm_token.rim.ptr, token.cvm_token.rim.len)) { + RTLS_ERR("RIM verification failed\n"); + printf("Verifying if RIM of cVM token matches reference value: Failed\n"); + return false; + } + RTLS_INFO("RIM verification passed\n"); + + /* Step 3: Platform SW Components Verification */ + /* Note: Platform verification is now handled in verify_evidence.c */ + if (has_platform && client_config && client_config->verify_platform_components) { + RTLS_INFO("Platform SW components verification will be handled by verifier"); + } else if (client_config && client_config->verify_platform_components) { + RTLS_WARN("Platform verification requested but no platform token present in attestation"); + } + + RTLS_DEBUG("All verifications completed successfully\n"); + return true; +} + +/* + * Parse IMA measurement file to determine the actual PCR index being used + * Returns the PCR index found in the file, or -1 on error + */ +static int parse_ima_pcr_index(void) +{ + FILE *fp; + int pcr_index = -1; + + /* + * IMA measurements file is in binary format, not text format + * Each entry has a header structure containing PCR index + */ + struct { + u_int32_t pcr; + u_int8_t digest[20]; /* SHA1_DIGEST_LENGTH */ + u_int32_t name_len; + } header; + + fp = fopen(CLIENT_IMA_MEASUREMENTS_PATH, "rb"); /* Open in binary mode */ + if (!fp) { + RTLS_ERR("Unable to open IMA measurements file: %s\n", CLIENT_IMA_MEASUREMENTS_PATH); + return -1; + } + + /* Skip the first entry (boot_aggregate) and read the second entry */ + /* which contains the actual PCR index used for file measurements */ + for (int entry_count = 0; entry_count < 2; entry_count++) { + if (fread(&header, sizeof(header), 1, fp) != 1) { + RTLS_ERR("Failed to read IMA measurement header for entry %d\n", entry_count); + fclose(fp); + return -1; + } + + if (entry_count == 0) { + /* This is boot_aggregate entry, skip it */ + RTLS_DEBUG("Skipping boot_aggregate entry with PCR %d\n", header.pcr); + + /* Skip template name */ + if (header.name_len > 0 && header.name_len < 1024) { + fseek(fp, header.name_len, SEEK_CUR); + } + + /* Skip template data for boot_aggregate */ + /* For ima-ng template: skip template_data_len + template_data */ + u_int32_t template_data_len; + if (fread(&template_data_len, sizeof(u_int32_t), 1, fp) == 1 && template_data_len < 10240) { + fseek(fp, template_data_len, SEEK_CUR); + } + } else { + /* This is the actual file measurement entry */ + pcr_index = header.pcr; + RTLS_INFO("Found actual measurement PCR index %d from IMA measurements file (skipped boot_aggregate)\n", pcr_index); + break; + } + } + + fclose(fp); + + if (pcr_index == -1) { + RTLS_ERR("Could not determine PCR index from IMA measurements\n"); + return -1; + } + + return pcr_index; +} + +static int verify_ima_log(rats_tls_handle handle, mig_agent_args *args) +{ + size_t len = sizeof(size_t); + size_t ima_log_size = 0; + uint8_t *ima_log_buf = NULL; + size_t recv_size = 0; + FILE *fp = NULL; + int ret = ENCLAVE_VERIFIER_ERR_UNKNOWN; + cca_token_t token = {0}; + cca_token_buf_t *cca_token_buf_ptr = NULL; + int target_pcr_for_ima = -1; /* Will be set to 1 or 4 */ + int rem_index_for_ima = -1; /* Will be set to 0 or 3 */ + const uint8_t *reference_rem_ptr = NULL; + size_t reference_rem_len = 0; + + RTLS_INFO("Starting automatic IMA measurement verification..."); + + ret = rats_tls_receive(handle, &ima_log_size, &len); + if (ret != RATS_TLS_ERR_NONE || len != sizeof(size_t)) { + RTLS_INFO("Failed to receive IMA log size %#x\n", ret); + return RATS_TLS_ERR_LOAD_TLS_WRAPPERS; + } + if (ima_log_size == 0 || ima_log_size > MAX_IMA_LOG_SIZE) { + RTLS_INFO("IMA log size is invalid, %u\n", ima_log_size); + return RATS_TLS_ERR_LOAD_ENCLAVE_ATTESTERS; + } + + ima_log_buf = malloc(ima_log_size); + if (ima_log_buf == NULL) { + RTLS_INFO("Malloc IMA log buffer failed.\n"); + return ENCLAVE_VERIFIER_ERR_NO_MEM; + } + + while (recv_size < ima_log_size) { + len = ima_log_size - recv_size; + ret = rats_tls_receive(handle, ima_log_buf + recv_size, &len); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_INFO("Filed to receive IMA log data.\n"); + goto free; + } + recv_size += len; + } + + /* write IMA log to file for next verify */ + if ((fp = fopen(CLIENT_IMA_MEASUREMENTS_PATH, "w")) == NULL) { + RTLS_INFO("Filed to open file %s.\n", CLIENT_IMA_MEASUREMENTS_PATH); + ret = ENCLAVE_VERIFIER_ERR_UNKNOWN; + goto free; + } + if ((len = fwrite(ima_log_buf, ima_log_size, 1, fp)) != 1) { + RTLS_INFO("Filed to write IMA log to file.\n"); + ret = ENCLAVE_VERIFIER_ERR_UNKNOWN; + } + +close: + fclose(fp); +free: + free(ima_log_buf); + if (ret) + return ret; + + rtls_core_context_t *ctx = (rtls_core_context_t *)handle; + + if (!ctx || !ctx->verifier || !ctx->verifier->verifier_private) { + RTLS_ERR("Verifier context or private data not found, cannot get CVM token for IMA validation.\n"); + return RATS_TLS_ERR_INVALID; + } + cca_token_buf_ptr = (cca_token_buf_t *)ctx->verifier->verifier_private; + + if (parse_cca_attestation_token(&token, cca_token_buf_ptr->buf, cca_token_buf_ptr->buf_size) != VIRTCCA_SUCCESS) { + RTLS_ERR("Failed to parse virtcca token from verifier context for IMA validation.\n"); + return ENCLAVE_VERIFIER_ERR_CBOR; + } + + /* + * Parse IMA measurement file to determine the actual PCR index being used + * Based on the PCR index, determine which REM to use: + * - PCR 4: use REM[3] (UEFI boot mode) + * - PCR 1 or 10: use REM[0] (Direct boot mode) + */ + RTLS_INFO("Attempting to parse PCR index from IMA measurements file...\n"); + int actual_pcr_index = parse_ima_pcr_index(); + if (actual_pcr_index == -1) { + RTLS_ERR("Failed to parse PCR index from IMA measurements\n"); + return RATS_TLS_ERR_INVALID; + } + RTLS_INFO("Successfully parsed PCR index: %d\n", actual_pcr_index); + + if (actual_pcr_index == 4) { + /* UEFI boot mode: PCR 4 -> REM[3] */ + if (REM_COUNT <= 3 || token.cvm_token.rem[3].len != SHA256_SIZE || is_rem_entry_zero(&token.cvm_token.rem[3])) { + RTLS_ERR("REM[3] is invalid or zero for PCR 4 verification\n"); + return RATS_TLS_ERR_INVALID; + } + target_pcr_for_ima = 4; + rem_index_for_ima = 3; + reference_rem_ptr = (const uint8_t *)token.cvm_token.rem[3].ptr; + reference_rem_len = token.cvm_token.rem[3].len; + RTLS_INFO("UEFI boot mode detected: using REM[3] for PCR 4 verification\n"); + } else if (actual_pcr_index == 1 || actual_pcr_index == 10) { + /* Direct boot mode: PCR 1 or 10 -> REM[0] */ + if (REM_COUNT <= 0 || token.cvm_token.rem[0].len != SHA256_SIZE || is_rem_entry_zero(&token.cvm_token.rem[0])) { + RTLS_ERR("REM[0] is invalid or zero for PCR %d verification\n", actual_pcr_index); + return RATS_TLS_ERR_INVALID; + } + target_pcr_for_ima = actual_pcr_index; /* Use the actual PCR index detected */ + rem_index_for_ima = 0; + reference_rem_ptr = (const uint8_t *)token.cvm_token.rem[0].ptr; + reference_rem_len = token.cvm_token.rem[0].len; + RTLS_INFO("Direct boot mode detected: using REM[0] for PCR %d verification\n", actual_pcr_index); + } else { + RTLS_ERR("Unsupported PCR index %d found in IMA measurements\n", actual_pcr_index); + return RATS_TLS_ERR_INVALID; + } + + RTLS_INFO("Using REM[%d] (PCR%d) from CVM token for IMA validation.\n", rem_index_for_ima, target_pcr_for_ima); + display_hash_as_hex_string(reference_rem_ptr, reference_rem_len, "REM_FOR_IMA"); + + ret = ima_measure(reference_rem_ptr, + reference_rem_len, + args->digest_file, 1, 1, target_pcr_for_ima); + if (ret != 0) { + RTLS_ERR("IMA measurement verification failed for PCR%d. ima_measure returned: %d\n", target_pcr_for_ima, ret); + return ENCLAVE_VERIFIER_ERR_UNKNOWN; + } + + RTLS_INFO("IMA measurement verification successful for PCR%d.\n", target_pcr_for_ima); + return RATS_TLS_ERR_NONE; +} + +static int deal_ima(rats_tls_handle handle, mig_agent_args *args) +{ + int ret = ENCLAVE_VERIFIER_ERR_UNKNOWN; + + RTLS_DEBUG("IMA file hash path %s\n", args->digest_file); + if (args->digest_file == NULL || strlen(args->digest_file) == 0) { + RTLS_INFO("No need to request and verify IMA log.\n"); + return RATS_TLS_ERR_BASE; + } + const char *msg = REQUEST_IMA_LOG; + size_t len = strlen(msg); + ret = rats_tls_transmit(handle, (void *)msg, &len); + if (ret != RATS_TLS_ERR_NONE || len != strlen(msg)) { + RTLS_ERR("Failed to request IMA log %#x\n", ret); + return RATS_TLS_ERR_LOAD_TLS_WRAPPERS; + } + + ret = verify_ima_log(handle, args); + return ret; +} + +static int tc_attr_set_echo(bool enbale) +{ + struct termios tty; + if (tcgetattr(STDIN_FILENO, &tty) < 0) { + RTLS_ERR("tcgetattr failed, err: %s\n", strerror(errno)); + return ENCLAVE_VERIFIER_ERR_UNKNOWN; + } + + if (enbale) { + tty.c_lflag |= ECHO; + } else { + tty.c_lflag &= ~ECHO; + } + + if (tcsetattr(STDIN_FILENO, TCSANOW, &tty) < 0) { + RTLS_ERR("tcsetattr failed, err: %s\n", strerror(errno)); + return ENCLAVE_VERIFIER_ERR_UNKNOWN; + } + + return RATS_TLS_ERR_BASE; +} + +static int deal_passwd(char password[MAX_PASSWD_LEN]) +{ + int i = 0; + char ch; + + RTLS_INFO("Enter remote disk image password: "); + if (tc_attr_set_echo(false)) { + return ENCLAVE_VERIFIER_ERR_UNKNOWN; + } + while ((ch = getchar()) != '\n' && ch != EOF) { + if (i > MAX_PASSWD_LEN - 1) { + RTLS_ERR("Input passwd too long,\n"); + return ENCLAVE_VERIFIER_ERR_UNKNOWN; + } + password[i++] = ch; + } + putchar('\n'); + if (tc_attr_set_echo(true)) { + return ENCLAVE_VERIFIER_ERR_UNKNOWN; + } + + for (i = 0; i < MAX_PASSWD_LEN, i++) { + password[i] = '\0'; + } + + return RATS_TLS_ERR_BASE; +} + +static int deal_fde_key(rats_tls_handle handle, bool use_fde, const char* rootfs_key_file) +{ + int ret = ENCLAVE_VERIFIER_ERR_UNKNOWN; + + RTLS_DEBUG("deal FDE key %d\n", use_fde); + if (use_fde == false) { + RTLS_INFO("FDE is not enabled\n"); + return RATS_TLS_ERR_BASE; + } + + char *msg = ENABLE_FDE_TOKEN; + size_t len = strlen(msg); + ret = rats_tls_transmit(handle, (void *)msg, &len); + if (ret != RATS_TLS_ERR_NONE || len != strlen(msg)) { + RTLS_ERR("Failed to send fde token %#x\n", ret); + return RATS_TLS_ERR_LOAD_TLS_WRAPPERS; + } + + size_t key_file_len; + uint8_t* key_file = read_file_data(rootfs_key_file, &key_file_len); + if (!key_file) { + RTLS_ERR("Failed to read rootfs key file\n"); + return ret; + } + ret = rats_tls_transmit(handle, key_file, &key_file_len); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to send rootfs key file %#x\n", ret); + ret = RATS_TLS_ERR_LOAD_TLS_WRAPPERS; + goto free; + } + + RTLS_INFO("Successfully sent rootfs key file (%zu bytes)\n", key_file_len); + +free: + free(key_file); + return ret; +} + +static int mig_agent_init(mig_agent_args *args) +{ + args->agent_type = 0; + + args->srv_ip = malloc(MAX_PAYLOAD_SIZE); + if (args->srv_ip == NULL) { + perror("malloc failed"); + return -1; + } + + args->attester_type = ""; + args->verifier_type = ""; + args->tls_type = ""; + args->crypto_type = ""; + args->port = MIGCVM_PORT; + args->digest_file = ""; + args->log_level = RATS_TLS_LOG_LEVEL_INFO; + args->mutual = false; + args->provide_endorsements = false; + args->use_firmware = false; + args->dump_eventlog = false; + args->ref_json_file = NULL; + args->use_fde = false; + args->rootfs_key_file = NULL; + args->verify_platform_components = false; + args->platform_ref_json_file = NULL; + + return 0; +} + +static int mig_agent_exit(mig_agent_args *args) +{ + free(args->srv_ip); + return 0; +} + +/* New function: Request and save firmware data */ +static int request_and_save_firmware_data(rats_tls_handle handle) +{ + int ret; + + /* Request CCEL table */ + const char *ccel_req = "REQUEST_CCEL_TABLE"; + size_t len = strlen(ccel_req); + ret = rats_tls_transmit(handle, (void *)ccel_req, &len); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to request CCEL table %#x\n", ret); + return ret; + } + + /* Receive CCEL data */ + unsigned char ccel_table[MAX] = {0}; + size_t ccel_table_len = MAX; + ret = rats_tls_receive(handle, ccel_table, &ccel_table_len); + if (ret != RATS_TLS_ERR_NONE || ccel_table_len == 0) { + RTLS_ERR("Failed to receive CCEL table %#x\n", ret); + return ret; + } + RTLS_INFO("Received CCEL table data, size: %zu bytes\n", ccel_table_len); + + /* Save CCEL table */ + FILE *fp = fopen(CLIENT_CCEL_ACPI_TABLE_PATH, "wb"); + if (!fp) { + RTLS_ERR("Failed to open file %s for writing\n", CLIENT_CCEL_ACPI_TABLE_PATH); + return RATS_TLS_ERR_INVALID; + } + if (fwrite(ccel_table, 1, ccel_table_len, fp) != ccel_table_len) { + RTLS_ERR("Failed to write CCEL table to file\n"); + fclose(fp); + return RATS_TLS_ERR_INVALID; + } + fclose(fp); + RTLS_INFO("CCEL table saved to %s\n", CLIENT_CCEL_ACPI_TABLE_PATH); + + /* Request event log */ + RTLS_INFO("Requesting event log...\n"); + const char *log_req = "REQUEST_EVENT_LOG"; + len = strlen(log_req); + ret = rats_tls_transmit(handle, (void *)log_req, &len); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to request event log %#x\n", ret); + return ret; + } + + /* Receive event log size */ + size_t expected_size = 0; + len = sizeof(size_t); + ret = rats_tls_receive(handle, &expected_size, &len); + if (ret != RATS_TLS_ERR_NONE || expected_size == 0 || expected_size > MAX_LOG) { + RTLS_ERR("Failed to receive event log size or invalid size %#x\n", ret); + return ret; + } + RTLS_INFO("Expecting event log size: %zu bytes\n", expected_size); + + /* Allocate receive buffer */ + unsigned char *event_log = (unsigned char *)malloc(expected_size); + if (!event_log) { + RTLS_ERR("Failed to allocate memory for event log\n"); + return RATS_TLS_ERR_NO_MEM; + } + + /* Receive event log data */ + size_t total_received = 0; + while (total_received < expected_size) { + len = expected_size - total_received; + ret = rats_tls_receive(handle, event_log + total_received, &len); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to receive event log data %#x\n", ret); + free(event_log); + return ret; + } + total_received += len; + } + RTLS_INFO("Received complete event log (%zu bytes)\n", expected_size); + + /* Save event log */ + fp = fopen(CLIENT_CCEL_EVENT_LOG_PATH, "wb"); + if (!fp) { + RTLS_ERR("Failed to open file %s for writing\n", CLIENT_CCEL_EVENT_LOG_PATH); + free(event_log); + return RATS_TLS_ERR_INVALID; + } + if (fwrite(event_log, 1, expected_size, fp) != expected_size) { + RTLS_ERR("Failed to write event log to file\n"); + fclose(fp); + free(event_log); + return RATS_TLS_ERR_INVALID; + } + fclose(fp); + free(event_log); + RTLS_INFO("Event log saved to %s\n\n", CLIENT_CCEL_EVENT_LOG_PATH); + + return RATS_TLS_ERR_NONE; +} + +static void print_hex_dump_with_ascii(const uint8_t* data, size_t length) +{ + char ascii_buf[17] = {0}; + + printf("=> Read CCEL ACPI Table\n"); + for (size_t i = 0; i < length; i++) { + if (i % 16 == 0) { + if (i > 0) { + printf(" %s\n", ascii_buf); + } + printf("%08zX ", i); + memset(ascii_buf, 0, sizeof(ascii_buf)); + } + printf("%02X ", data[i]); + ascii_buf[i % 16] = isprint(data[i]) ? data[i] : '.'; + } + + /* Process the last line */ + if (length % 16 != 0) { + for (size_t i = length % 16; i < 16; i++) { + printf(" "); + } + } + printf(" %s\n", ascii_buf); +} + +static void print_acpi_table(const uint8_t* ccel_data, size_t file_size, const acpi_table_info_t* info) +{ + print_hex_dump_with_ascii(ccel_data, file_size); + + printf("Revision: %d\n", info->revision); + printf("Length: %zu\n", file_size); + printf("Checksum: %02X\n", info->checksum); + + printf("OEM ID: b'"); + for (int i = 0; i < 6; i++) { + printf("%c", info->oem_id[i]); + } + printf("'\n"); + + printf("CC Type: %d\n", info->cc_type); + printf("CC Sub-type: %d\n", info->cc_subtype); + + printf("Log Lenght: 0x%08lX\n", (unsigned long)info->log_length); + printf("Log Address: 0x%08lX\n", (unsigned long)info->log_address); + printf("\n"); +} + +static bool parse_acpi_table(const uint8_t* ccel_data, size_t file_size, acpi_table_info_t* info) +{ + if (!ccel_data || !info || file_size < 56) { + return false; + } + + if (memcmp(ccel_data, "CCEL", 4) != 0) { + printf("Error: Invalid CCEL signature\n"); + return false; + } + + info->revision = ccel_data[8]; + info->checksum = ccel_data[9]; + memcpy(info->oem_id, ccel_data + 10, 6); + info->cc_type = ccel_data[36]; + info->cc_subtype = ccel_data[37]; + info->log_length = *(uint64_t*)(ccel_data + 40); + info->log_address = *(uint64_t*)(ccel_data + 48); + + print_acpi_table(ccel_data, file_size, info); + + return true; +} + +static int handle_eventlogs_command(void) +{ + size_t file_size; + uint8_t* ccel_data = read_file_data(CLIENT_CCEL_ACPI_TABLE_PATH, &file_size); + if (!ccel_data) { + return 1; + } + + acpi_table_info_t table_info; + if (!parse_acpi_table(ccel_data, file_size, &table_info)) { + free(ccel_data); + return 1; + } + + event_log_t event_log; + if (!event_log_init(&event_log, (size_t)table_info.log_address, (size_t)table_info.log_length)) { + printf("Error: Failed to initialize event log\n"); + free(ccel_data); + return 1; + } + + event_log_dump(&event_log); + + free(ccel_data); + return 0; +} + +static char* extract_json_string(const char* json, const char* key) +{ + char* value = NULL; + char search_key[64]; + snprintf(search_key, sizeof(search_key), "\"%s\":", key); + + char* pos = strstr(json, search_key); + if (pos) { + pos = strchr(pos + strlen(search_key), '"'); + if (pos) { + pos++; /* Skip quote */ + char* end = strchr(pos, '"'); + if (end) { + size_t len = end - pos; + value = (char*)malloc(len + 1); + if (value) { + strncpy(value, pos, len); + value[len] = '\0'; + } + } + } + } + return value; +} + +static int parse_input_mig_agent_args(int argc, char **argv, mig_agent_args *args) +{ + int opt; + char *rim = NULL; + char *const short_options = "i:p:r:f:h"; + struct option long_options[] = { + { "ip", required_argument, NULL, 'i' }, + { "port", required_argument, NULL, 'p' }, + { "rim", required_argument, NULL, 'r' }, + { "firmware", required_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { 0, 0, 0, 0 } + }; + + do { + opt = getopt_long(argc, argv, short_options, long_options, NULL); + switch (opt) { + case 'i': + args->srv_ip = optarg; + break; + case 'p': + args->port = atoi(optarg); + break; + case 'r': + CHECK_LENGHT_ASSIGN(optarg, MAX_MEASUREMENT_SIZE + 1, rim); + if (hex_to_bytes((unsigned char*)rim, strlen(rim), g_rim_ref, &g_rim_ref_size) != 0) { + printf("change rim to bytes failed\n"); + return -1; + } + break; + case 'f': + args->use_firmware = true; + args->ref_json_file = optarg; + break; + case 'g': + if (args->use_firmware) { + printf("Error: Cannot use -g and -f together\n"); + return -1; + } + args->dump_eventlog = true; + break; + case -1: + break; + default: + puts(" Usage:\n\n" + " rats-tls-client [arguments]\n\n" + " Options:\n\n" + " --attester/-a value set the type of quote attester\n" + " --verifier/-v value set the type of quote verifier\n" + " --tls/-t value set the type of tls wrapper\n" + " --crypto/-c value set the type of crypto wrapper\n" + " --mutual/-m set to enable mutual attestation\n" + " --endorsements/-e set to let attester provide endorsements\n" + " --log-level/-l set the log level\n" + " --ip/-i set the listening ip address\n" + " --port/-p set the listening tcp port\n" + " --rim/-r set the initial measurement of cVM\n" + " --digest/-d set the digest list file for verifying IMA measurement\n" + " --firmware/-f enable firmware verification with JSON reference file\n" + " --eventlog/-g dump VCCA event logs\n" + " --fdekey/-k enable Full Disk Encryption with rootfs key file\n" + " --platform/-P enable platform SW-components verification with JSON reference file\n" + " --help/-h show the usage\n"); + return -1; + } + } while (opt != -1); + global_log_level = RATS_TLS_LOG_LEVEL_INFO; + + return 0; +} + +int rats_tls_server_startup(mig_agent_args *args) +{ + rats_tls_conf_t conf; + rats_tls_err_t ret; + memset(&conf, 0, sizeof(conf)); + conf.log_level = args->log_level; + strcpy(conf.attester_type, args->attester_type); + strcpy(conf.verifier_type, args->verifier_type); + strcpy(conf.tls_type, args->tls_type); + strcpy(conf.crypto_type, args->crypto_type); + int sockfd = -1; + + conf.cert_algo = RATS_TLS_CERT_ALGO_DEFAULT; + conf.flags |= RATS_TLS_CONF_FLAGS_SERVER; + if (args->mutual) + conf.flags |= RATS_TLS_CONF_FLAGS_MUTUAL; + + if (args->provide_endorsements) + conf.flags |= RATS_TLS_CONF_FLAGS_PROVIDE_ENDORSEMENTS; + + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + RTLS_ERR("Failed to call socket()"); + return -1; + } + + int reuse = 1; + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&reuse, sizeof(int)) < 0) { + RTLS_ERR("Failed to call setsockopt()"); + return -1; + } + + /* Set keepalive options */ + int flag = 1; + int tcp_keepalive_time = 30; + int tcp_keepalive_intvl = 10; + int tcp_keepalive_probes = 5; + if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof(flag)) < 0) { + RTLS_ERR("Failed to call setsockopt()"); + return -1; + } + if (setsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE, &tcp_keepalive_time, + sizeof(tcp_keepalive_time)) < 0) { + RTLS_ERR("Failed to call setsockopt()"); + return -1; + } + if (setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, &tcp_keepalive_intvl, + sizeof(tcp_keepalive_intvl)) < 0) { + RTLS_ERR("Failed to call setsockopt()"); + return -1; + } + if (setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, &tcp_keepalive_probes, + sizeof(tcp_keepalive_probes)) < 0) { + RTLS_ERR("Failed to call setsockopt()"); + return -1; + } + + struct sockaddr_in s_addr; + memset(&s_addr, 0, sizeof(s_addr)); + s_addr.sin_family = AF_INET; + s_addr.sin_addr.s_addr = inet_addr(args->srv_ip); + s_addr.sin_port = htons(args->port); + + /* Bind the server socket */ + if (bind(sockfd, (struct sockaddr *)&s_addr, sizeof(s_addr)) == -1) { + RTLS_ERR("Failed to call bind()"); + return -1; + } + + /* Listen for a new connection, allow 5 pending connections */ + if (listen(sockfd, 5) == -1) { + RTLS_ERR("Failed to call listen()"); + return -1; + } + + rats_tls_handle handle; + + ret = rats_tls_init(&conf, &handle); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to initialize rats tls %#x\n", ret); + goto err; + } + + ret = rats_tls_set_verification_callback(&handle, NULL); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to set verification callback %#x\n", ret); + goto err; + } + + while (start_listening) { + RTLS_INFO("Waiting for a connection ...\n"); + + /* Accept client connections */ + struct sockaddr_in c_addr; + socklen_t size = sizeof(c_addr); + + int connd = accept(sockfd, (struct sockaddr *)&c_addr, &size); + if (connd < 0) { + RTLS_ERR("Failed to call accept()"); + continue; + } + + ret = rats_tls_negotiate(handle, connd); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to negotiate %#x\n", ret); + goto close_connd; + } + + RTLS_DEBUG("Client connected successfully\n"); + ret = deal_client_req(handle); + if (ret != RATS_TLS_ERR_NONE && ret != 0x68) { + RTLS_ERR("Client verify failed %#x\n", ret); + } else { + RTLS_INFO("Client verify success, do other jobs.\n"); + close(connd); + break; + } + + close_connd: + close(connd); + } + + shutdown(sockfd, SHUT_RDWR); + close(sockfd); + + if (rats_tls_cleanup(handle) != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to cleanup\n"); + return RATS_TLS_ERR_INVALID; + } + + if (ret == 0x68) { + return 0x68; + } else { + return 0x67; + } + +err: + /* Ignore the error code of cleanup in order to return the prepositional error */ + if (sockfd != -1) { + shutdown(sockfd, SHUT_RDWR); + close(sockfd); + sockfd = -1; + } + rats_tls_cleanup(handle); + return -1; +} + +int rats_tls_client_startup(mig_agent_args *args) +{ + int ret; + rats_tls_conf_t conf; + rats_tls_handle handle = NULL; + + /* Create static configuration storage */ + static mig_agent_args static_args; + static claim_t static_claim; + + /* Copy parameters to static storage */ + memcpy(&static_args, args, sizeof(mig_agent_args)); + + memset(&conf, 0, sizeof(conf)); + conf.log_level = args->log_level; + strcpy(conf.attester_type, args->attester_type); + strcpy(conf.verifier_type, args->verifier_type); + strcpy(conf.tls_type, args->tls_type); + strcpy(conf.crypto_type, args->crypto_type); + conf.cert_algo = RATS_TLS_CERT_ALGO_DEFAULT; + if (args->mutual) + conf.flags |= RATS_TLS_CONF_FLAGS_MUTUAL; + if (args->provide_endorsements) + conf.flags |= RATS_TLS_CONF_FLAGS_PROVIDE_ENDORSEMENTS; + + /* Set static claim, using type casting to avoid warnings */ + static_claim.name = "mig_agent_args"; + static_claim.value = (uint8_t *)&static_args; + static_claim.value_size = sizeof(mig_agent_args); + conf.custom_claims = &static_claim; + conf.custom_claims_length = 1; /* Set length to 1, indicating only one claim */ + + RTLS_INFO("Setting up custom claims with args=%p", &static_args); + + /* Create socket and connect */ + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + RTLS_ERR("failed to call socket()\n"); + return -1; + } + + struct sockaddr_in s_addr; + memset(&s_addr, 0, sizeof(s_addr)); + s_addr.sin_family = AF_INET; + s_addr.sin_port = htons(args->port); + + if (inet_pton(AF_INET, args->srv_ip, &s_addr.sin_addr) != 1) { + RTLS_ERR("invalid server address\n"); + ret = -1; + goto err; + } + + if ((ret = connect(sockfd, (struct sockaddr *)&s_addr, sizeof(s_addr))) == -1) { + RTLS_ERR("failed to call connect()\n"); + goto err; + } + + ret = rats_tls_init(&conf, &handle); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to initialize rats tls %#x\n", ret); + goto err; + } + + /* Ensure configuration is correctly copied to handle */ + rtls_core_context_t *ctx = (rtls_core_context_t *)handle; + if (!ctx->config.custom_claims) { + RTLS_ERR("Failed to set custom claims in handle\n"); + ret = RATS_TLS_ERR_INVALID; + goto err; + } + RTLS_DEBUG("Custom claims set in handle: %p", ctx->config.custom_claims->value); + + ret = rats_tls_set_verification_callback(&handle, user_callback); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to set verification callback %#x\n", ret); + goto err; + } + + ret = rats_tls_negotiate(handle, sockfd); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to negotiate %#x\n", ret); + goto err; + } + + /* Platform SW Components Verification */ + if (static_args.verify_platform_components) { + RTLS_INFO("Starting Platform SW Components verification..."); + + /* Get token from verifier context */ + rtls_core_context_t *ctx = (rtls_core_context_t *)handle; + if (!ctx || !ctx->verifier || !ctx->verifier->verifier_private) { + RTLS_ERR("Failed to get verifier context for platform verification\n"); + ret = RATS_TLS_ERR_INVALID; + goto err; + } + + /* Parse token from verifier_private */ + cca_token_t token = {0}; + cca_token_buf_t *cca_token_buf = (cca_token_buf_t *)ctx->verifier->verifier_private; + + if (parse_cca_attestation_token(&token, cca_token_buf->buf, cca_token_buf->buf_size) != VIRTCCA_SUCCESS) { + RTLS_ERR("Failed to parse virtcca token for platform verification\n"); + ret = RATS_TLS_ERR_INVALID; + goto err; + } + + /* Check if platform token exists */ + if (token.platform_cose.ptr != NULL && token.platform_cose.len > 0) { + if (!static_args.platform_ref_json_file) { + RTLS_ERR("Platform verification enabled but no reference JSON file provided\n"); + ret = RATS_TLS_ERR_INVALID; + goto err; + } + + /* Load platform reference values */ + platform_ref_values_t ref_values = {0}; + if (!load_platform_ref_values(static_args.platform_ref_json_file, &ref_values)) { + RTLS_ERR("Failed to load platform reference values from: %s\n", static_args.platform_ref_json_file); + ret = RATS_TLS_ERR_INVALID; + goto err; + } + + /* Perform platform SW components verification */ + if (!verify_platform_sw_components(&token.platform_token, &ref_values)) { + RTLS_ERR("Platform SW components verification failed\n"); + ret = RATS_TLS_ERR_INVALID; + goto err; + } + + RTLS_INFO("Platform SW components verification PASSED!\n"); + } else { + RTLS_WARN("Platform verification requested but no platform token present in attestation\n"); + } + } + + /* Handle IMA verification */ + if ((ret = deal_ima(handle, &static_args)) != RATS_TLS_ERR_NONE) { + RTLS_ERR("Verify IMA measurement failed %#x\n", ret); + goto err; + } + + /* If firmware verification is enabled, first get and verify firmware state */ + if (static_args.use_firmware || static_args.dump_eventlog) { + RTLS_INFO("Starting firmware verification process\n"); + + /* Request and receive CCEL table and event log */ + ret = request_and_save_firmware_data(handle); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to get firmware data %#x\n", ret); + goto err; + } + + /* If in dump_eventlog mode, directly execute handle_eventlogs_command and return */ + if (static_args.dump_eventlog) { + ret = handle_eventlogs_command(); + goto err; + } + + /* Below is the firmware verification logic */ + /* Initialize event log processor */ + event_log_t event_log; + if (!event_log_init(&event_log, 0, 0)) { + RTLS_ERR("Failed to initialize event log\n"); + ret = RATS_TLS_ERR_INVALID; + goto err; + } + + /* Replay event log to calculate REM values */ + if (!event_log_replay(&event_log)) { + RTLS_ERR("Failed to replay event log\n"); + ret = RATS_TLS_ERR_INVALID; + goto err; + } + + /* Verify REM values from token */ + RTLS_INFO("Verifying REM values from token...\n"); + + /* Get token from verifier context */ + rtls_core_context_t *ctx = (rtls_core_context_t *)handle; + if (!ctx || !ctx->verifier || !ctx->verifier->verifier_private) { + RTLS_ERR("Failed to get verifier context\n"); + ret = RATS_TLS_ERR_INVALID; + goto err; + } + + /* Parse token from verifier_private */ + cca_token_t token = {0}; + cca_token_buf_t *cca_token_buf = (cca_token_buf_t *)ctx->verifier->verifier_private; + + if (parse_cca_attestation_token(&token, cca_token_buf->buf, cca_token_buf->buf_size) != VIRTCCA_SUCCESS) { + RTLS_ERR("Failed to parse virtcca token\n"); + ret = RATS_TLS_ERR_INVALID; + goto err; + } + + bool all_rems_passed = true; + for (int i = 0; i < REM_COUNT; i++) { + if (token.cvm_token.rem[i].len != sizeof(rem_t)) { + RTLS_ERR("Invalid REM[%d] size in token\n", i); + ret = RATS_TLS_ERR_INVALID; + goto err; + } + + verify_single_rem(i, (rem_t*)token.cvm_token.rem[i].ptr, &event_log.rems[i]); + if (!rem_compare((rem_t*)token.cvm_token.rem[i].ptr, &event_log.rems[i])) { + all_rems_passed = false; + } + } + + if (!all_rems_passed) { + RTLS_ERR("REM verification failed\n"); + ret = RATS_TLS_ERR_INVALID; + goto err; + } + + RTLS_INFO("All REM values verified successfully\n"); + printf("\n=====================================\n"); + + /* Create firmware state */ + firmware_log_state_t* state = firmware_log_state_create(&event_log); + if (!state) { + RTLS_ERR("Failed to create firmware state\n"); + ret = RATS_TLS_ERR_INVALID; + goto err; + } + + /* Extract firmware state */ + if (!firmware_log_state_extract(&event_log, state)) { + RTLS_ERR("Failed to extract firmware state\n"); + firmware_log_state_free(state); + ret = RATS_TLS_ERR_INVALID; + goto err; + } + + /* Verify firmware state */ + if (!verify_firmware_state(static_args.ref_json_file, state)) { + RTLS_ERR("Firmware state verification failed\n"); + firmware_log_state_free(state); + ret = RATS_TLS_ERR_INVALID; + goto err; + } + + firmware_log_state_free(state); + RTLS_INFO("Firmware verification completed successfully\n\n"); + } + + if ((ret = deal_fde_key(handle, args->use_fde, args->rootfs_key_file)) != RATS_TLS_ERR_NONE) { + RTLS_ERR("deal fde key failed %#x\n", ret); + goto err; + } + + const char *msg = TOKEN; + size_t len = strlen(msg); + ret = rats_tls_transmit(handle, (void *)msg, &len); + if (ret != RATS_TLS_ERR_NONE || len != strlen(msg)) { + RTLS_ERR("Failed to transmit %#x\n", ret); + goto err; + } + + char buf[1024]; + len = sizeof(buf); + ret = rats_tls_receive(handle, buf, &len); + if (ret != RATS_TLS_ERR_NONE) { + RTLS_ERR("Failed to receive %#x\n", ret); + goto err; + } + + if (len >= sizeof(buf)) + len = sizeof(buf) - 1; + buf[len] = '\0'; + + printf("Sent to Server: %s\n", msg); + printf("The msk is: 0x1\n"); + printf("Received from Server: %s\n", buf); + +err: + if (handle) { + /* Clear custom_claims before cleanup to prevent freeing static memory */ + rtls_core_context_t *ctx = (rtls_core_context_t *)handle; + if (ctx) { + ctx->config.custom_claims = NULL; + ctx->config.custom_claims_length = 0; + } + rats_tls_cleanup(handle); + } + shutdown(sockfd, SHUT_RDWR); + close(sockfd); + return ret; +} + +static int insert_a_guest_rd_in_pending_guest_rds(unsigned long long guest_rd) +{ + for (int i = 0; i < MAX_BIND_VM; i++) { + if (pending_guest_rds->guest_rd[i] == 0) { + pending_guest_rds->guest_rd[i] = guest_rd; + return 0; + } + } + return -1; +} + +static void ras_tls_handler(const struct socket_msg *msg, int conn_fd, mig_agent_args *args) +{ + int ret; + RTLS_INFO("Custom handling: cmd=%s, payload_type=%d\n", + msg->cmd, msg->payload_type); + + if (msg->payload_type != PAYLOAD_TYPE_CHAR) { + RTLS_ERR("Expected char payload, got type %d\n", msg->payload_type); + return; + } + + char ip_buffer[MAX_PAYLOAD_SIZE]; + if (strcmp(msg->cmd, "s") == 0) { + RTLS_INFO("start server\n"); + payload_decode(msg, ip_buffer); + strncpy(args->srv_ip, ip_buffer, MAX_PAYLOAD_SIZE); + ret = rats_tls_server_startup(args); + } else if (strcmp(msg->cmd, "c") == 0) { + RTLS_INFO("start client\n"); + payload_decode(msg, ip_buffer); + strncpy(args->srv_ip, ip_buffer, MAX_PAYLOAD_SIZE); + ret = rats_tls_client_startup(args); + } else { + printf("error msg->cmd %s\n", msg->cmd); + } + + const char *resp = "ACK"; + write(conn_fd, resp, strlen(resp) + 1); +} + +static void host_handler(const struct socket_msg *msg, int conn_fd, + mig_agent_args *args) +{ + RTLS_INFO("Host handling: cmd=%s, payload_type=%d\n", + msg->cmd, msg->payload_type); + + if (strcmp(msg->cmd, "BIND_COMPLETE") == 0) { + if (msg->payload_type != PAYLOAD_TYPE_ULL) { + RTLS_ERR("Expected ULL payload for BIND_COMPLETE, got type %d\n", + msg->payload_type); + return; + } + + unsigned long long guest_rd; + payload_decode(msg, &guest_rd); + + if (insert_a_guest_rd_in_pending_guest_rds(guest_rd)) { + perror("Failed to insert guest RD"); + } + } + + printf("Host handling: cmd=%s, payload_type=%d\n", + msg->cmd, msg->payload_type); +} + +static unsigned long long get_first_rd(pending_guest_rd_t *pending_guest_rds) +{ + for (int i = 0; i < MAX_BIND_VM; i++) { + if (pending_guest_rds->guest_rd[i]) { + return pending_guest_rds->guest_rd[i]; + } + } + return -1; +} + +static int prepare_migration(unsigned long long guest_rd) +{ + tsi_ctx *ctx = NULL; + virtcca_mig_info_t *migvm_info; + migration_info_t *attest_info; + int ret; + int function_to_test = 0; /* 1 for get_migration_info, 2 for set_migration_bind_slot */ + + /* Initialize TSI context */ + ctx = tsi_new_ctx(); + if (ctx == NULL) { + printf("Failed to create TSI context\n"); + return -1; + } + + printf("using guest_rd: 0x%llx\n", guest_rd); + migvm_info = (virtcca_mig_info_t *)malloc(sizeof(virtcca_mig_info_t)); + if (!migvm_info) { + printf("Failed to initialize migvm_info\n"); + tsi_free_ctx(ctx); + return -1; + } + migvm_info->guest_rd = guest_rd; + attest_info = (migration_info_t *)malloc(sizeof(migration_info_t)); + if (!attest_info) { + printf("Failed to initialize attest_info\n"); + tsi_free_ctx(ctx); + return -1; + }; + attest_info->msk[0] = 0x11; + + attest_info->pending_guest_rds = pending_guest_rds; + migvm_info->mig_info = attest_info; + + printf("Testing get_rd_info with guestRd=0x%llx...\n", migvm_info->guest_rd); + ret = set_migration_bind_slot_and_mask(ctx, migvm_info, attest_info); + if (ret == TSI_SUCCESS) { + printf("set_migration_bind_slot_and_mask succeeded\n"); + } else { + printf("set_migration_bind_slot_and_mask failed with error: 0x%08x\n", ret); + return -1; + } + free(attest_info); + free(migvm_info); + tsi_free_ctx(ctx); + return ret; +} + +static unsigned long long get_guest_rd(pending_guest_rd_t *pending_guest_rds, bool *startup) +{ + tsi_ctx *ctx = NULL; + virtcca_mig_info_t *migvm_info; + migration_info_t *attest_info; + int ret; + int function_to_test = 0; /* 1 for get_migration_info, 2 for set_migration_bind_slot */ + + printf("size of virtcca_mig_info_t is %zu\n", sizeof(virtcca_mig_info_t)); + printf("size of migration_info_t is %zu\n", sizeof(migration_info_t)); + if (!*startup) + return get_first_rd(pending_guest_rds); + ctx = tsi_new_ctx(); + if (ctx == NULL) { + printf("Failed to create TSI context\n"); + return -1; + } + attest_info = (migration_info_t *)malloc(sizeof(migration_info_t)); + if (!attest_info) { + printf("Failed to initialize attest_info\n"); + tsi_free_ctx(ctx); + return -1; + }; + migvm_info = (virtcca_mig_info_t *)malloc(sizeof(virtcca_mig_info_t)); + // if (memset_s(&migvm_info, sizeof(migvm_info), 0, sizeof(migvm_info)) != 0) { + if (!migvm_info) { + printf("Failed to initialize migvm_info\n"); + tsi_free_ctx(ctx); + return -1; + } + attest_info->pending_guest_rds = pending_guest_rds; + ret = get_migration_binded_rds(ctx, migvm_info, attest_info); + + if (ret == TSI_SUCCESS) { + printf("get_rd_info succeeded\n"); + } else { + printf("get_rd_info failed with error: 0x%08x\n", ret); + tsi_free_ctx(ctx); + return -1; + } + tsi_free_ctx(ctx); + *startup = false; + free(migvm_info); + free(attest_info); + return get_first_rd(pending_guest_rds); +} + +int main(int argc, char **argv) +{ + pthread_t vsock_thread, tcp_thread; + mig_agent_args input_args = {0}; + struct socket_agent_cfg cfg = { + .args = &input_args, + .cid = MIGCVM_CID, + .port = HOST_AGENT_PORT, + .backlog = 5 /* the length of the listening queue */ + }; + + mig_agent_init(&input_args); + if (parse_input_mig_agent_args(argc, argv, &input_args)) { + RTLS_ERR("Error parsing input args.\n"); + goto out; + } + + int ret = socket_agent_start_with_handler(&cfg, ras_tls_handler); /* host socket */ + if (ret != 0) { + fprintf(stderr, "Failed to start socket agent: %d\n", ret); + goto out; + } + +out: + mig_agent_exit(&input_args); + return 0; +} + +static int auto_test_main(int argc, char **argv) +{ + pthread_t vsock_thread, tcp_thread; + mig_agent_args input_args = {0}; + int ret = 0; + bool rds_should_init = true; + + pending_guest_rds = (pending_guest_rd_t *)malloc(sizeof(pending_guest_rd_t)); + if (!pending_guest_rds) { + perror("Failed to allocate pending_guest_rds"); + return -1; + } + + struct socket_agent_cfg cfg = { + .args = &input_args, + .cid = MIGCVM_CID, + .port = HOST_AGENT_PORT, + .backlog = 5 /* the length of the listening queue */ + }; + + mig_agent_init(&input_args); + if (parse_input_mig_agent_args(argc, argv, &input_args)) { + RTLS_ERR("Error parsing input args.\n"); + goto out; + } + + /* use tsi get the rds which will be migrated */ + unsigned long long guest_rd = get_guest_rd(pending_guest_rds, &rds_should_init); + if (guest_rd != 0) { + ret = socket_agent_start_with_handler(&cfg, ras_tls_handler); /* rats-tls socket */ + if (ret != 0) { + fprintf(stderr, "Failed to start tls socket agent: %d\n", ret); + goto out; + } + } + + ret = prepare_migration(guest_rd); + if (ret != 0) { + fprintf(stderr, "Failed to prepare migration: %d\n", ret); + goto out; + } + + while (start_listening) { + /* todo: state machine to handle multi migration */ + ret = socket_agent_start_with_handler(&cfg, host_handler); /* host socket */ + if (ret != 0) { + fprintf(stderr, "Failed to start host socket agent: %d\n", ret); + goto out; + } + guest_rd = get_guest_rd(pending_guest_rds, &rds_should_init); + ret = socket_agent_start_with_handler(&cfg, ras_tls_handler); /* rats-tls socket */ + if (ret != 0) { + fprintf(stderr, "Failed to start tls socket agent: %d\n", ret); + goto out; + } + ret = prepare_migration(guest_rd); + if (ret != 0) { + fprintf(stderr, "Failed to prepare migration: %d\n", ret); + break; + } + } + +out: + mig_agent_exit(&input_args); + free(pending_guest_rds); + pending_guest_rds = NULL; + return 0; +} \ No newline at end of file diff --git a/migcvm-agent/src/migcvm_tsi/CMakeLists.txt b/migcvm-agent/src/migcvm_tsi/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..1ff4d5e7f467ec325f12b9359b5e8b1d5e021ae3 --- /dev/null +++ b/migcvm-agent/src/migcvm_tsi/CMakeLists.txt @@ -0,0 +1,9 @@ +# src/migcvm_tsi/CMakeLists.txt + +add_library(migcvm-tsi STATIC migcvm_tsi.c) + +target_compile_options(migcvm-tsi PRIVATE -fPIE) + +target_include_directories(migcvm-tsi PUBLIC + ${PROJECT_SOURCE_DIR}/inc/migcvm_tsi +) \ No newline at end of file diff --git a/migcvm-agent/src/migcvm_tsi/migcvm_tsi.c b/migcvm-agent/src/migcvm_tsi/migcvm_tsi.c new file mode 100644 index 0000000000000000000000000000000000000000..54b0de66fbb3548dede12dcc86bd44fbe6c6637e --- /dev/null +++ b/migcvm-agent/src/migcvm_tsi/migcvm_tsi.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "migcvm_tsi.h" + +tsi_ctx *tsi_new_ctx(void) +{ + tsi_ctx *ctx = calloc(1, sizeof(tsi_ctx)); + if (ctx == NULL) { + printf("Failed to allocate TSI context: out of memory\n"); + return NULL; + } + ctx->fd = open("/dev/tsi", O_RDWR | O_CLOEXEC); + if (ctx->fd == -1) { + printf("Failed to open TSI device: (errno=%d)\n", errno); + free(ctx); + return NULL; + } + return ctx; +} + +void tsi_free_ctx(tsi_ctx *ctx) +{ + if (ctx == NULL) { + return; + } + if (ctx->fd != -1) { + close(ctx->fd); + } + close(ctx->fd); + free(ctx); +} + +int get_migration_info_and_mask(tsi_ctx *ctx, virtcca_mig_info_t *migvm_info, migration_info_t *attest_info) +{ + int ret; + if (ctx == NULL || migvm_info == NULL || attest_info == NULL) { + return NULL_INPUT; + } + + migvm_info->ops = OP_MIGRATE_GET_ATTR; + migvm_info->size = sizeof(migration_info_t); + printf("migvm_info->size: %zu\n", migvm_info->size); + if (attest_info == NULL) { + printf("Failed to allocate memory for migration_info: out of memory\n"); + return TSI_ERROR; + } + migvm_info->mig_info = attest_info; + + ret = ioctl(ctx->fd, TMM_GET_MIGRATION_INFO, migvm_info); + if (ret != 0) { + char error_message[256] = {0}; + strerror_r(errno, error_message, sizeof(error_message)); + printf("Failed to get migration info: %s (errno=%d)\n", error_message, errno); + return TSI_ERROR; + } + + printf("the local msk is 0x%lx\n", attest_info->msk[0]); + printf("the local msk is 0x%lx\n", attest_info->msk[1]); + printf("the local msk is 0x%lx\n", attest_info->msk[2]); + printf("the local msk is 0x%lx\n", attest_info->msk[3]); + + if (attest_info->set_key) { + printf("This is a source VM.\n"); + } else { + printf("This is a destination VM.\n"); + } + return TSI_SUCCESS; +} + +int set_migration_bind_slot_and_mask(tsi_ctx *ctx, virtcca_mig_info_t *migvm_info, migration_info_t *attest_info) +{ + int ret; + if (ctx == NULL || migvm_info == NULL || attest_info == NULL) { + return NULL_INPUT; + } + + migvm_info->size = sizeof(migration_info_t); + printf("migvm_info->size: %zu\n", migvm_info->size); + migvm_info->ops = OP_MIGRATE_SET_SLOT; + migvm_info->mig_info = attest_info; + + ret = ioctl(ctx->fd, TMM_GET_MIGRATION_INFO, migvm_info); + if (ret != 0) { + printf("Failed to get migration info. errno: %d\n", errno); + return TSI_ERROR; + } + + return TSI_SUCCESS; +} + +int get_migration_binded_rds(tsi_ctx *ctx, virtcca_mig_info_t *migvm_info, migration_info_t *attest_info) +{ + int ret; + if (ctx == NULL || migvm_info == NULL || attest_info == NULL) { + return NULL_INPUT; + } + + migvm_info->ops = OP_MIGRATE_PEEK_RDS; + migvm_info->size = sizeof(migration_info_t); + printf("migvm_info->size: %zu\n", migvm_info->size); + if (attest_info->pending_guest_rds == NULL) { + printf("Failed to allocate memory for get_migration_binded_rds: out of memory\n"); + return TSI_ERROR; + } + migvm_info->mig_info = attest_info; + + ret = ioctl(ctx->fd, TMM_GET_MIGRATION_INFO, migvm_info); + if (ret != 0) { + printf("Failed to get migration info: (errno=%d)\n", errno); + return TSI_ERROR; + } + + if (attest_info->pending_guest_rds) { + for (int i = 0; i < MAX_BIND_VM; ++i) { + printf("Pending guest rd %d: 0x%llx\n", i, attest_info->pending_guest_rds->guest_rd[i]); + } + } else { + printf("the pending_guest_rds is not set\n"); + } + + return TSI_SUCCESS; +} \ No newline at end of file diff --git a/migcvm-agent/src/socket/host_socket_agent.c b/migcvm-agent/src/socket/host_socket_agent.c new file mode 100644 index 0000000000000000000000000000000000000000..107d1ed7390d607af8ec89e51ddea73f761d4a05 --- /dev/null +++ b/migcvm-agent/src/socket/host_socket_agent.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "socket_agent.h" + +static int start_listening = 1; + +static void default_msg_handler(const struct socket_msg *msg, int conn_fd) +{ + RTLS_INFO("Received: cmd='%s', payload_type=%d, payload_len=%u\n", + msg->cmd, msg->payload_type, msg->payload_len); + const char *resp = "ACK"; + if (write(conn_fd, resp, strlen(resp) + 1) == -1) { + perror("Failed to send ACK (ignored)"); + } +} + +static void calling_handler(const int listen_fd, socket_msg_handler handler, + mig_agent_args *args) +{ + int conn_fd = accept(listen_fd, NULL, NULL); + if (conn_fd < 0) { + perror("accept failed"); + return; + } + + RTLS_INFO("New connection accepted\n"); + struct socket_msg msg; + ssize_t bytes_read = read(conn_fd, &msg, sizeof(msg)); + if (bytes_read == sizeof(msg)) { + if (handler) { + RTLS_INFO("Handling message: cmd=%s, payload_type=%d\n", + msg.cmd, msg.payload_type); + handler(&msg, conn_fd, args); + } + } else if (bytes_read < 0) { + char error_message[256] = {0}; + strerror_r(errno, error_message, sizeof(error_message)); + RTLS_ERR("read failed: %s (errno=%d)\n", error_message, errno); + } else if (bytes_read == 0) { + RTLS_INFO("Connection closed by client\n"); + } else { + RTLS_INFO("Partial message received: %zd/%zu bytes\n", bytes_read, sizeof(msg)); + } + close(conn_fd); +} + +int socket_agent_start_with_handler(const struct socket_agent_cfg *cfg, + socket_msg_handler handler) +{ + int listen_fd; + char error_message[256] = {0}; + struct sockaddr_vm sa = { + .svm_family = AF_VSOCK, + .svm_cid = cfg->cid, + .svm_port = cfg->port + }; + + /* create socket */ + listen_fd = socket(AF_VSOCK, SOCK_STREAM, 0); + if (listen_fd < 0) { + perror("socket creation failed"); + return -1; + } + + /* allow reuse of local addresses */ + int opt = 1; + if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { + perror("setsockopt failed"); + close(listen_fd); + return -1; + } + + RTLS_INFO("Trying to bind: CID=%u, Port=%d\n", sa.svm_cid, sa.svm_port); + if (bind(listen_fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { + strerror_r(errno, error_message, sizeof(error_message)); + RTLS_ERR("bind failed: %s (errno=%d)\n", error_message, errno); + close(listen_fd); + return -1; + } + + RTLS_INFO("Successfully bound to port %d\n", sa.svm_port); + if (listen(listen_fd, cfg->backlog) < 0) { + strerror_r(errno, error_message, sizeof(error_message)); + RTLS_INFO("listen failed: %s (errno=%d)\n", error_message, errno); + close(listen_fd); + return -1; + } + + RTLS_INFO("Listening for VSOCK connections on port %d...\n", sa.svm_port); + calling_handler(listen_fd, handler, cfg->args); + close(listen_fd); + return 0; +} + +static void payload_encode_char(struct socket_msg *msg, const char *in) +{ + msg->payload_type = PAYLOAD_TYPE_CHAR; + strncpy(msg->payload.char_payload, in, MAX_PAYLOAD_SIZE); + msg->payload_len = strlen(in) + 1; +} + +static void payload_encode_ull(struct socket_msg *msg, unsigned long long in) +{ + msg->payload_type = PAYLOAD_TYPE_ULL; + msg->payload.ull_payload = in; + msg->payload_len = sizeof(unsigned long long); +} + +void payload_decode(const struct socket_msg *msg, void *out) +{ + if (msg->payload_type == PAYLOAD_TYPE_CHAR) { + strncpy((char *)out, msg->payload.char_payload, msg->payload_len); + } else if (msg->payload_type == PAYLOAD_TYPE_ULL) { + *(unsigned long long *)out = msg->payload.ull_payload; + } +} \ No newline at end of file