From 8c6e462b80fea372013c803e1e443ac75156c5ef Mon Sep 17 00:00:00 2001 From: Jamie Cui Date: Tue, 16 Dec 2025 14:17:24 +0800 Subject: [PATCH 1/2] feat(virtrust): add SM3 file hashing and update domain measure logic - Integrate SM3 cryptographic hashing for VM disk images Modify - domain measurement API to include disk path and digest Add file - I/O utilities for secure file handling Remove unused migration - helper components Enhance error handling and validation in domain - start flow Add comprehensive tests for SM3 file hashing - functionality --- virtrust/src/virtrust/CMakeLists.txt | 2 +- virtrust/src/virtrust/api/domain.cpp | 83 ++++--- virtrust/src/virtrust/base/str_utils.h | 1 + virtrust/src/virtrust/crypto/sm3.cpp | 18 ++ virtrust/src/virtrust/crypto/sm3.h | 5 +- virtrust/src/virtrust/crypto/sm3_test.cpp | 230 +++++++++++++++++- virtrust/src/virtrust/utils/CMakeLists.txt | 6 +- .../src/virtrust/utils/migrate_helper.cpp | 7 - virtrust/src/virtrust/utils/migrate_helper.h | 46 ---- .../virtrust/utils/migrate_helper_test.cpp | 191 --------------- 10 files changed, 308 insertions(+), 281 deletions(-) delete mode 100644 virtrust/src/virtrust/utils/migrate_helper.cpp delete mode 100644 virtrust/src/virtrust/utils/migrate_helper.h delete mode 100644 virtrust/src/virtrust/utils/migrate_helper_test.cpp diff --git a/virtrust/src/virtrust/CMakeLists.txt b/virtrust/src/virtrust/CMakeLists.txt index b57c331..ca78eae 100644 --- a/virtrust/src/virtrust/CMakeLists.txt +++ b/virtrust/src/virtrust/CMakeLists.txt @@ -77,7 +77,7 @@ target_include_directories( PRIVATE ${CMAKE_DEPS_INCLUDEDIR} ${CMAKE_CURRENT_BINARY_DIR}/src $) -target_link_libraries(virtrust-shared PRIVATE Deps::spdlog boundscheck) +target_link_libraries(virtrust-shared PRIVATE Deps::spdlog Deps::secure_c) target_link_libraries( virtrust-shared diff --git a/virtrust/src/virtrust/api/domain.cpp b/virtrust/src/virtrust/api/domain.cpp index 2a08f22..8dfb828 100644 --- a/virtrust/src/virtrust/api/domain.cpp +++ b/virtrust/src/virtrust/api/domain.cpp @@ -116,13 +116,22 @@ VirtrustRc GetVirshMeasureSummary(virtrust::ForeignMounter &mounter, virtrust::V return VirtrustRc::OK; } -bool CalcVirshMeasure(std::string_view guestName, VirshMeasureSummary &measureSummary) +// REVIEW: DomainStart needs the hash digest of vm's disk image, so we pass the diskDigest and diskPath to upper level +bool CalcVirshMeasure(std::string_view guestName, VirshMeasureSummary &measureSummary, std::string &diskPath, + std::vector &diskDigest) { - const std::string guestXmlPah = fmt::format(VIRTRUST_XML_REGEX_PATH, guestName); + const std::string guestXmlPath = fmt::format(VIRTRUST_XML_REGEX_PATH, guestName); virtrust::VirtXmlParser xmlParser; virtrust::VerifyConfig verifyConfig; - if (!xmlParser.Parse(verifyConfig, guestXmlPah)) { - VIRTRUST_LOG_ERROR("|main|END|returnF|file: {}|parse xml file failed.", guestXmlPah); + if (!xmlParser.Parse(verifyConfig, guestXmlPath)) { + VIRTRUST_LOG_ERROR("|main|END|returnF|file: {}|parse xml file failed.", guestXmlPath); + return false; + } + + // SM3 on disk image + diskPath = verifyConfig.GetDiskPath(); + if (DoSm3File(diskPath, diskDigest) != Sm3Rc::OK) { + VIRTRUST_LOG_ERROR("|main|END|returnF||digest failed."); return false; } @@ -160,6 +169,7 @@ bool CalcVirshMeasure(std::string_view guestName, VirshMeasureSummary &measureSu } VIRTRUST_LOG_INFO("|main|END|returnS||grubContentLength:{}", measureSummary.grub.content_.size()); mounter.Unmount(); + diskPath = verifyConfig.GetDiskPath(); return true; } @@ -219,11 +229,12 @@ void FreeMeasureInfo(struct MeasureInfo *bios, struct MeasureInfo *shim, struct } } -bool CheckGuestBeforeStart(std::string_view domainName, std::string &uuid) +bool CheckGuestBeforeStart(std::string_view domainName, std::string &uuid, std::string &diskPath, + std::vector &diskDigest) { // 收集需要度量文件的摘要值 VirshMeasureSummary measureSummary(uuid); - if (!CalcVirshMeasure(domainName, measureSummary)) { + if (!CalcVirshMeasure(domainName, measureSummary, diskPath, diskDigest)) { return false; } // 转换为tsb-agent需要的结构体 @@ -267,7 +278,9 @@ bool UpdateMeasure(std::string_view domainName, std::string &uuid) { VIRTRUST_LOG_INFO("|UpdateMeasure|START|||domainName:{}", domainName); VirshMeasureSummary measureSummary(uuid); - if (!CalcVirshMeasure(domainName, measureSummary)) { + std::string tmpDiskPath; // HACK: we don't use this tmpDiskPath + std::vector tmpDiskDigest; // HACK: we don't use this tmpDiskDigest + if (!CalcVirshMeasure(domainName, measureSummary, tmpDiskPath, tmpDiskDigest)) { return false; } struct MeasureInfo *bios = nullptr; @@ -322,7 +335,7 @@ VirtrustRc CheckCreateDomainName(const std::string &arg, std::string &domainName } // 处理--name=***或-n=***或-n***** bool isLongContainsName = arg.length() > 7 && arg.substr(0, 7) == "--name="; // 7是--name=的长度 - bool isShortContainsName = arg.length() > 3 && arg.substr(0, 2) == "-n"; // 这里大于3是处理-n并且紧跟字符的情况 + bool isShortContainsName = arg.length() > 3 && arg.substr(0, 2) == "-n"; // 这里大于3是处理-n并且紧跟字符的情况 if (isLongContainsName || isShortContainsName) { if (isLongContainsName || (isLongContainsName && arg.find('=') != std::string::npos)) { domainName = arg.substr(arg.find('=') + 1); @@ -431,7 +444,6 @@ VirtrustRc CreateDomainAndVRoot(const std::unique_ptr &conn, const std: description.state = 0; if (strncpy_s(description.name, sizeof(description.name), domainName.data(), sizeof(description.name) - 1) != EOK) { VIRTRUST_LOG_ERROR("|DomainCreate|END|returnF||strncpy_s domainName failed."); - return VirtrustRc::ERROR; } if (strncpy_s(description.uuid, sizeof(description.uuid), uuid, sizeof(description.uuid) - 1) != EOK) { @@ -445,15 +457,15 @@ VirtrustRc CreateDomainAndVRoot(const std::unique_ptr &conn, const std: (void)UndefineDomainWithRetry(domain, domainName, VIR_DOMAIN_UNDEFINE_NVRAM, libvirt); return VirtrustRc::ERROR; } - if (!allowStoreMeasurements) { - VIRTRUST_LOG_DEBUG("|DomainCreate|END|returnS||create domainName : {} success", domainName); - return VirtrustRc::OK; - } - std::string uuidStr(uuid); - if (!UpdateMeasure(domainName, uuidStr)) { - VIRTRUST_LOG_ERROR("|DomainCreate|END|returnF||UpdateMeasure failed"); - (void)UndefineDomainWithRetry(domain, domainName, VIR_DOMAIN_UNDEFINE_NVRAM, libvirt); - return VirtrustRc::ERROR; + + // if allow store measurements, we additionally calls the update measure of tsb agent api + if (allowStoreMeasurements) { + std::string uuidStr(uuid); + if (!UpdateMeasure(domainName, uuidStr)) { + VIRTRUST_LOG_ERROR("|DomainCreate|END|returnF||UpdateMeasure failed"); + (void)UndefineDomainWithRetry(domain, domainName, VIR_DOMAIN_UNDEFINE_NVRAM, libvirt); + return VirtrustRc::ERROR; + } } VIRTRUST_LOG_DEBUG("|DomainCreate|END|returnS||create domainName: {} success", domainName); return VirtrustRc::OK; @@ -947,18 +959,23 @@ VirtrustRc DomainStart(const std::unique_ptr &conn, const std::string & { auto start = std::chrono::high_resolution_clock::now(); VIRTRUST_LOG_DEBUG("|DomainStart||START||start domainName: {}, isonlyTsb:{}", domainName, isOnlyTsb); - if (conn == nullptr) { - VIRTRUST_LOG_ERROR("|DomainStart|END|returnF|| ConnCtx is nullptr."); - return VirtrustRc::ERROR; - } FileLock fileLock(LOCK_FILE); if (!fileLock.IsLocked()) { return VirtrustRc::ERROR; } + + // conn must exits if (conn == nullptr) { - VIRTRUST_LOG_ERROR("|DomainStart|END|returnF||conn is nullptr"); + VIRTRUST_LOG_ERROR("|DomainStart|END|returnF|| ConnCtx is nullptr."); return VirtrustRc::ERROR; } + + // check flags + if (flags != DOMAIN_START_NONE) { + VIRTRUST_LOG_ERROR("flags only support: {}", static_cast(DOMAIN_START_NONE)); + return VirtrustRc::ERROR; + } + // 如果带--only-tsb仅更新tsb资源 if (isOnlyTsb) { std::string uuidStr = domainName; @@ -974,21 +991,25 @@ VirtrustRc DomainStart(const std::unique_ptr &conn, const std::string & VIRTRUST_LOG_DEBUG("|DomainStart||END|returnS|start domainName: {} success", domainName); return VirtrustRc::OK; } - if (flags != DOMAIN_START_NONE) { - VIRTRUST_LOG_ERROR("flags only support: {}", static_cast(DOMAIN_START_NONE)); - return VirtrustRc::ERROR; - } + + // get domain auto domain = std::make_unique(conn, domainName); if (domain->Get() == nullptr) { VIRTRUST_LOG_ERROR("failed to find domain: {}", domainName); return VirtrustRc::ERROR; } - std::string uuid = GetUUIDStr(domain->Get()); + + // start vroot auto asyncStartVRoot = std::async(&StartVRoot, uuid.data()); VIRTRUST_LOG_INFO("Perform checking on: {} before start", domainName); - auto checkOk = CheckGuestBeforeStart(domainName, uuid); + + std::string diskPath; // qcow2 image path + std::vector diskDigest; // qcow2 image path + auto checkOk = CheckGuestBeforeStart(domainName, uuid, diskPath, diskDigest); auto startVRootRc = asyncStartVRoot.get(); + + // if failed if (!checkOk && startVRootRc == 0) { VIRTRUST_LOG_ERROR("Check domain failed,domainName: {}", domainName); if (StopVRoot(uuid.data()) != 0) { @@ -1001,13 +1022,15 @@ VirtrustRc DomainStart(const std::unique_ptr &conn, const std::string & return VirtrustRc::ERROR; } - if (Libvirt::GetInstance().virDomainCreateWithFlags(domain->Get(), flags) < 0) { + if (Libvirt::GetInstance().virDomainCreateWithFlags(domain->Get(), flags) < 0 || + DoSm3File(diskPath, diskDigest) != Sm3Rc::OK) { VIRTRUST_LOG_ERROR("failed to start domain: {}", domainName); if (StopVRoot(uuid.data()) != 0) { VIRTRUST_LOG_ERROR("stop vRoot failed domain: {}", domainName); } return VirtrustRc::ERROR; } + auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start); VIRTRUST_LOG_DEBUG("|DomainStart||END|returnS|start domainName: {} success", domainName); diff --git a/virtrust/src/virtrust/base/str_utils.h b/virtrust/src/virtrust/base/str_utils.h index 39cea02..cadd5ab 100644 --- a/virtrust/src/virtrust/base/str_utils.h +++ b/virtrust/src/virtrust/base/str_utils.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include diff --git a/virtrust/src/virtrust/crypto/sm3.cpp b/virtrust/src/virtrust/crypto/sm3.cpp index 9d90cb5..9f65d08 100644 --- a/virtrust/src/virtrust/crypto/sm3.cpp +++ b/virtrust/src/virtrust/crypto/sm3.cpp @@ -10,6 +10,7 @@ #include "virtrust/base/logger.h" #include "virtrust/dllib/openssl.h" +#include "virtrust/utils/file_io.h" namespace virtrust { @@ -150,4 +151,21 @@ Sm3Rc DoSm3(std::string_view data, std::vector &out) return Sm3Rc::OK; } +Sm3Rc DoSm3File(const std::string &filePath, std::vector &out) +{ + std::string fileContent; + try { + FileInputStream fis(filePath); + fileContent = fis.ReadAll(); + } catch (std::exception &e) { + VIRTRUST_LOG_ERROR("ERROR while reading file: {}", filePath); + return Sm3Rc::ERROR; + } + + if (virtrust::DoSm3(fileContent, out) != Sm3Rc::OK) { + return Sm3Rc::ERROR; + } + return Sm3Rc::OK; +} + } // namespace virtrust diff --git a/virtrust/src/virtrust/crypto/sm3.h b/virtrust/src/virtrust/crypto/sm3.h index 74beeb8..541d98b 100644 --- a/virtrust/src/virtrust/crypto/sm3.h +++ b/virtrust/src/virtrust/crypto/sm3.h @@ -41,4 +41,7 @@ private: std::array DoSm3(std::string_view data); Sm3Rc DoSm3(std::string_view data, std::vector &out); -} // namespace virtrust \ No newline at end of file +// file sm3 +Sm3Rc DoSm3File(const std::string &filePath, std::vector &out); + +} // namespace virtrust diff --git a/virtrust/src/virtrust/crypto/sm3_test.cpp b/virtrust/src/virtrust/crypto/sm3_test.cpp index df1cfb1..57a2b18 100644 --- a/virtrust/src/virtrust/crypto/sm3_test.cpp +++ b/virtrust/src/virtrust/crypto/sm3_test.cpp @@ -2,6 +2,7 @@ * Copyright (C) Huawei Technologies Co., Ltd. 2025-2025.All rights reserved. */ +#include #include #include #include @@ -11,6 +12,7 @@ #include "virtrust/crypto/sm3.h" #include "virtrust/dllib/openssl.h" +#include "virtrust/utils/file_io.h" namespace virtrust::test { namespace { @@ -36,6 +38,232 @@ inline std::string BytesToHexString(const std::vector &bytes) } // namespace +// Helper function to create temporary test files +std::string CreateTempTestFile(const std::string &content, const std::string &suffix = "") +{ + // Get the project root path + auto filePath = std::filesystem::path(__FILE__).parent_path(); + std::string tempPath = (filePath / ".." / ".." / ".." / "test" / "data" / ("sm3_test_temp" + suffix + ".txt")) + .lexically_normal() + .string(); + + // Create temporary file with test content + FileOutputStream fos(tempPath); + fos.Write(content); + return tempPath; +} + +// Helper function to clean up temporary test files +void CleanupTempTestFile(const std::string &filePath) +{ + std::filesystem::remove(filePath); +} + +// Test DoSm3File function with various scenarios +TEST(Sm3Test, DoSm3FileBasic) +{ + const std::string testContent = "Hello, World!"; + std::string tempFile = CreateTempTestFile(testContent); + + std::vector hashResult(Sm3::DigestSize()); + auto result = DoSm3File(tempFile, hashResult); + + EXPECT_EQ(result, Sm3Rc::OK); + EXPECT_EQ(hashResult.size(), Sm3::DigestSize()); + + // Verify hash is not all zeros + bool allZero = true; + for (uint8_t byte : hashResult) { + if (byte != 0) { + allZero = false; + break; + } + } + EXPECT_FALSE(allZero); + + // Compare with direct string hash + auto directHash = DoSm3(testContent); + EXPECT_EQ(hashResult.size(), directHash.size()); + EXPECT_EQ(memcmp(hashResult.data(), directHash.data(), hashResult.size()), 0); + + CleanupTempTestFile(tempFile); +} + +// Test DoSm3File with empty file +TEST(Sm3Test, DoSm3FileEmpty) +{ + const std::string testContent = ""; + std::string tempFile = CreateTempTestFile(testContent, "_empty"); + + std::vector hashResult(Sm3::DigestSize()); + auto result = DoSm3File(tempFile, hashResult); + + EXPECT_EQ(result, Sm3Rc::OK); + EXPECT_EQ(hashResult.size(), Sm3::DigestSize()); + + // Compare with direct empty string hash + auto directHash = DoSm3(testContent); + EXPECT_EQ(hashResult.size(), directHash.size()); + EXPECT_EQ(memcmp(hashResult.data(), directHash.data(), hashResult.size()), 0); + + CleanupTempTestFile(tempFile); +} + +// Test DoSm3File with large file content +TEST(Sm3Test, DoSm3FileLarge) +{ + // Create a large test content (but within the 1GB limit) + std::string largeContent(10000, 'A'); // 10KB of 'A's + std::string tempFile = CreateTempTestFile(largeContent, "_large"); + + std::vector hashResult(Sm3::DigestSize()); + auto result = DoSm3File(tempFile, hashResult); + + EXPECT_EQ(result, Sm3Rc::OK); + EXPECT_EQ(hashResult.size(), Sm3::DigestSize()); + + // Compare with direct string hash + auto directHash = DoSm3(largeContent); + EXPECT_EQ(hashResult.size(), directHash.size()); + EXPECT_EQ(memcmp(hashResult.data(), directHash.data(), hashResult.size()), 0); + + CleanupTempTestFile(tempFile); +} + +// Test DoSm3File with binary content +TEST(Sm3Test, DoSm3FileBinary) +{ + // Create binary content with null bytes and other special characters + std::string binaryContent = "Binary\x00\xFF\xFE\x01Content"; + std::string tempFile = CreateTempTestFile(binaryContent, "_binary"); + + std::vector hashResult(Sm3::DigestSize()); + auto result = DoSm3File(tempFile, hashResult); + + EXPECT_EQ(result, Sm3Rc::OK); + EXPECT_EQ(hashResult.size(), Sm3::DigestSize()); + + // Compare with direct string hash + auto directHash = DoSm3(binaryContent); + EXPECT_EQ(hashResult.size(), directHash.size()); + EXPECT_EQ(memcmp(hashResult.data(), directHash.data(), hashResult.size()), 0); + + CleanupTempTestFile(tempFile); +} + +// Test DoSm3File with multibyte characters (UTF-8) +TEST(Sm3Test, DoSm3FileUtf8) +{ + const std::string utf8Content = "测试中文内容🚀 UTF-8 ñáéíóú"; + std::string tempFile = CreateTempTestFile(utf8Content, "_utf8"); + + std::vector hashResult(Sm3::DigestSize()); + auto result = DoSm3File(tempFile, hashResult); + + EXPECT_EQ(result, Sm3Rc::OK); + EXPECT_EQ(hashResult.size(), Sm3::DigestSize()); + + // Compare with direct string hash + auto directHash = DoSm3(utf8Content); + EXPECT_EQ(hashResult.size(), directHash.size()); + EXPECT_EQ(memcmp(hashResult.data(), directHash.data(), hashResult.size()), 0); + + CleanupTempTestFile(tempFile); +} + +// Test DoSm3File with nonexistent file +TEST(Sm3Test, DoSm3FileNonexistent) +{ + std::string nonexistentFile = "/nonexistent/path/file.txt"; + + std::vector hashResult(Sm3::DigestSize()); + auto result = DoSm3File(nonexistentFile, hashResult); + + EXPECT_EQ(result, Sm3Rc::ERROR); +} + +// Test DoSm3File with insufficient output buffer +TEST(Sm3Test, DoSm3FileInsufficientBuffer) +{ + const std::string testContent = "Test content"; + std::string tempFile = CreateTempTestFile(testContent, "_insufficient"); + + // Create output vector with insufficient size + std::vector smallBuffer(Sm3::DigestSize() - 1); + auto result = DoSm3File(tempFile, smallBuffer); + + // The function should still return OK, but the underlying memcpy_s should fail + // This tests the robustness of the implementation + EXPECT_EQ(result, Sm3Rc::ERROR); + + CleanupTempTestFile(tempFile); +} + +// Test DoSm3File consistency across multiple calls +TEST(Sm3Test, DoSm3FileConsistency) +{ + const std::string testContent = "Consistency test content"; + std::string tempFile = CreateTempTestFile(testContent, "_consistency"); + + std::vector hashResult1(Sm3::DigestSize()); + std::vector hashResult2(Sm3::DigestSize()); + + auto result1 = DoSm3File(tempFile, hashResult1); + auto result2 = DoSm3File(tempFile, hashResult2); + + EXPECT_EQ(result1, Sm3Rc::OK); + EXPECT_EQ(result2, Sm3Rc::OK); + EXPECT_EQ(hashResult1.size(), hashResult2.size()); + EXPECT_EQ(memcmp(hashResult1.data(), hashResult2.data(), hashResult1.size()), 0); + + CleanupTempTestFile(tempFile); +} + +// Test DoSm3File with different file sizes +TEST(Sm3Test, DoSm3FileVariousSizes) +{ + std::vector testSizes = {1, 10, 100, 1000, 5000}; + + for (size_t size : testSizes) { + std::string content(size, 'X'); + std::string suffix = "_size_" + std::to_string(size); + std::string tempFile = CreateTempTestFile(content, suffix); + + std::vector hashResult(Sm3::DigestSize()); + auto result = DoSm3File(tempFile, hashResult); + + EXPECT_EQ(result, Sm3Rc::OK); + EXPECT_EQ(hashResult.size(), Sm3::DigestSize()); + + // Compare with direct string hash + auto directHash = DoSm3(content); + EXPECT_EQ(hashResult.size(), directHash.size()); + EXPECT_EQ(memcmp(hashResult.data(), directHash.data(), hashResult.size()), 0); + + CleanupTempTestFile(tempFile); + } +} + +// Test DoSm3File with lines and special characters +TEST(Sm3Test, DoSm3FileLinesAndSpecialChars) +{ + const std::string lineContent = "Line 1\nLine 2\r\nLine 3\tTabbed\tContent\"Quotes\"'Apostrophes'"; + std::string tempFile = CreateTempTestFile(lineContent, "_lines"); + + std::vector hashResult(Sm3::DigestSize()); + auto result = DoSm3File(tempFile, hashResult); + + EXPECT_EQ(result, Sm3Rc::OK); + EXPECT_EQ(hashResult.size(), Sm3::DigestSize()); + + // Compare with direct string hash + auto directHash = DoSm3(lineContent); + EXPECT_EQ(hashResult.size(), directHash.size()); + EXPECT_EQ(memcmp(hashResult.data(), directHash.data(), hashResult.size()), 0); + + CleanupTempTestFile(tempFile); +} + TEST(Sm3Test, Works) { std::string msg = "123"; @@ -249,4 +477,4 @@ TEST(Sm3Test, HashConcatenation) EXPECT_NE(memcmp(hash1Result.data(), hash3Result.data(), hash1Result.size()), 0); EXPECT_NE(memcmp(hash2Result.data(), hash3Result.data(), hash2Result.size()), 0); } -} // namespace virtrust::test \ No newline at end of file +} // namespace virtrust::test diff --git a/virtrust/src/virtrust/utils/CMakeLists.txt b/virtrust/src/virtrust/utils/CMakeLists.txt index 8742aec..02eab44 100644 --- a/virtrust/src/virtrust/utils/CMakeLists.txt +++ b/virtrust/src/virtrust/utils/CMakeLists.txt @@ -1,16 +1,14 @@ # Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. set(VIRTRUST_SOURCE_FILES - ${VIRTRUST_SOURCE_FILES} - ${CMAKE_CURRENT_LIST_DIR}/file_io.cpp + ${VIRTRUST_SOURCE_FILES} ${CMAKE_CURRENT_LIST_DIR}/file_io.cpp ${CMAKE_CURRENT_LIST_DIR}/virt_xml_parser.cpp - ${CMAKE_CURRENT_LIST_DIR}/migrate_helper.cpp ${CMAKE_CURRENT_LIST_DIR}/foreign_mounter.cpp) add_virtrust_test_if(file_io_test ${BUILD_TEST}) add_virtrust_sh_test_if(virt_xml_parser_test ${BUILD_TEST}) add_virtrust_sh_test_if(foreign_mounter_test ${BUILD_TEST}) -add_virtrust_test_if(migrate_helper_test ${BUILD_TEST}) +# add_virtrust_test_if(migrate_helper_test ${BUILD_TEST}) # add_virtrust_test_if(async_timer_test ${BUILD_TEST}) set(VIRTRUST_SOURCE_FILES diff --git a/virtrust/src/virtrust/utils/migrate_helper.cpp b/virtrust/src/virtrust/utils/migrate_helper.cpp deleted file mode 100644 index 61d241b..0000000 --- a/virtrust/src/virtrust/utils/migrate_helper.cpp +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. - */ - -#include "virtrust/utils/migrate_helper.h" - -namespace virtrust {} // namespace virtrust \ No newline at end of file diff --git a/virtrust/src/virtrust/utils/migrate_helper.h b/virtrust/src/virtrust/utils/migrate_helper.h deleted file mode 100644 index 52faf35..0000000 --- a/virtrust/src/virtrust/utils/migrate_helper.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. - */ - -#pragma once - -#include -#include - -#include "virtrust/api/context.h" - -namespace virtrust { - -class MigrateHelper { -public: - explicit MigrateHelper() = default; - ~MigrateHelper() = default; - - explicit MigrateHelper(std::string destUri) : destUri_(std::move(destUri)) - {} - - void SetDstUri(const std::string &destUri) - { - destUri_ = destUri; - } - - std::string GetDstUri() - { - return destUri_; - } - - // step 1: get report that the hardware is okay - void GetReport(); - - // step 2: get the private shared key pair - void GetKey(); - - // step 3: key exchange to get a shared key - void ExchangeKey(); - -private: - ConnCtx conn_; - std::string destUri_; -}; - -} // namespace virtrust \ No newline at end of file diff --git a/virtrust/src/virtrust/utils/migrate_helper_test.cpp b/virtrust/src/virtrust/utils/migrate_helper_test.cpp deleted file mode 100644 index ad43432..0000000 --- a/virtrust/src/virtrust/utils/migrate_helper_test.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) Huawei Technologies Co., Ltd. 2025-2025.All rights reserved. - */ - -#include -#include - -#include "gtest/gtest.h" - -#include "virtrust/base/logger.h" -#include "virtrust/utils/file_io.h" -#include "virtrust/utils/foreign_mounter.h" -#include "virtrust/utils/migrate_helper.h" -#include "virtrust/utils/virt_xml_parser.h" - -namespace virtrust { - -namespace { -std::string GetTestFilePath() -{ - // Get the project root path (which is the directory) - auto filePath = std::filesystem::path(__FILE__); - return (filePath / ".." / ".." / ".." / ".." / "test" / "data" / "test.txt").lexically_normal().string(); -} - -const std::string TEST_XML_CONTENT = R"( - - test-domain - 1048576 - 2 - - hvm - -)"; - -} // namespace - -TEST(UtilsTest, FileIoWorks) -{ - std::string path(GetTestFilePath()); - FileInputStream fis = FileInputStream(path); - std::string connent = fis.ReadAll(); - EXPECT_FALSE(connent.empty()); -} - -TEST(UtilsTest, FileIoReadALL) -{ - std::string path(GetTestFilePath()); - FileInputStream fis = FileInputStream(path); - std::string connent = fis.ReadAll(); - EXPECT_FALSE(connent.empty()); -} - -TEST(UtilsTest, FileIoGetLength) -{ - std::string path(GetTestFilePath()); - FileInputStream fis = FileInputStream(path); - size_t length = fis.GetLength(); - EXPECT_GT(length, static_cast(0)); -} - -TEST(UtilsTest, FileIoGetName) -{ - std::string path(GetTestFilePath()); - FileInputStream fis = FileInputStream(path); - const std::string &name = fis.GetName(); - EXPECT_EQ(name, path); -} - -TEST(UtilsTest, FileIoEof) -{ - std::string path(GetTestFilePath()); - FileInputStream fis = FileInputStream(path); - - // Initially not at EOF - EXPECT_FALSE(fis.Eof()); - - // Read all content - std::string content = fis.ReadAll(); - EXPECT_FALSE(content.empty()); -} - -TEST(UtilsTest, FileIoSeekgAndTellg) -{ - std::string path(GetTestFilePath()); - FileInputStream fis = FileInputStream(path); - - // Get initial position - size_t initialPos = fis.Tellg(); - EXPECT_EQ(initialPos, static_cast(0)); - - // Read part of the file - std::string content; - fis.GetLine(content, '\n'); - - // Get current position - size_t currentPos = fis.Tellg(); - EXPECT_GT(currentPos, initialPos); - - // Seek back to beginning - fis.Seekg(0); - size_t newPos = fis.Tellg(); - EXPECT_EQ(newPos, static_cast(0)); -} - -TEST(UtilsTest, FileIoTransferTo) -{ - std::string path(GetTestFilePath()); - FileInputStream fis = FileInputStream(path); - - std::ostringstream oss; - fis.TransferTo(oss); - - EXPECT_FALSE(oss.str().empty()); -} - -TEST(UtilsTest, FileIoGetLine) -{ - std::string path(GetTestFilePath()); - FileInputStream fis = FileInputStream(path); - - std::string line; - fis.GetLine(line, '\n'); - - EXPECT_FALSE(line.empty()); -} - -TEST(UtilsTest, FileIoGetSpawn) -{ - std::string path(GetTestFilePath()); - FileInputStream fis = FileInputStream(path); - - // Save current position - size_t originalPos = fis.Tellg(); - - // Spawn a new stream from current position - auto spawnedStream = fis.Spawn(); - - // Both streams should be at same position - ASSERT_EQ(spawnedStream->Tellg(), originalPos); - - // Original stream should still work - std::string line; - fis.GetLine(line, '\n'); - EXPECT_FALSE(line.empty()); -} - -TEST(UtilsTest, FileIoFileStreamOperators) -{ - std::string path(GetTestFilePath()); - FileInputStream fis = FileInputStream(path); - - // Test boolean conversion operators - EXPECT_TRUE(static_cast(fis)); - EXPECT_FALSE(!fis); - - // Test with invalid file (should not throw in constructor but fail on usage) - try { - FileInputStream invalidFis("nonexistent_file.txt"); - // Just test that construction didn't crash, but subsequent operations should fail - } catch (...) { - // Excepted - } -} - -TEST(UtilsTest, ForeignMounter) -{ - // This is a placeholder test - the actual implementation might depend on - // system-specific mount points which are not available in test environment - // We mainly test taht the class can be instantiated and basic operations work - - // Test defaulf construstor - wrap in try-catch to handle missing libraries - try { - ForeignMounter mounter; - EXPECT_TRUE(true); // Basic check taht it compiles and can be constructed - } catch (...) { - // Expected when libguestfs library is not available - EXPECT_TRUE(true); // Test passes if we can handle the missing library gracefully - } -} - -TEST(UtilsTest, MigrateHelper) -{ - // This is a placeholder test - the actual implementation might depend on - // complex migration operations that are hard to test without proper environment - - // Test defaulf construstor - MigrateHelper helper; - EXPECT_TRUE(true); // Basic check taht it compiles and can be constructed -} -} // namespace virtrust \ No newline at end of file -- Gitee From 3a1dd5c0174eac8c8084a2100ea6e18195d9c6eb Mon Sep 17 00:00:00 2001 From: Jamie Cui Date: Wed, 17 Dec 2025 09:25:58 +0800 Subject: [PATCH 2/2] fix(domain): verify disk digest integrity on domain start Check SM3 digest of disk file before starting domain to ensure integrity. Previously, the digest was computed but not verified against the expected value. This change compares the computed digest with the stored digest and fails domain startup if they don't match. --- virtrust/src/virtrust/api/domain.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/virtrust/src/virtrust/api/domain.cpp b/virtrust/src/virtrust/api/domain.cpp index 8dfb828..786caa7 100644 --- a/virtrust/src/virtrust/api/domain.cpp +++ b/virtrust/src/virtrust/api/domain.cpp @@ -1022,8 +1022,9 @@ VirtrustRc DomainStart(const std::unique_ptr &conn, const std::string & return VirtrustRc::ERROR; } + std::vector checkDiskDigest; if (Libvirt::GetInstance().virDomainCreateWithFlags(domain->Get(), flags) < 0 || - DoSm3File(diskPath, diskDigest) != Sm3Rc::OK) { + DoSm3File(diskPath, checkDiskDigest) != Sm3Rc::OK || checkDiskDigest != diskDigest) { VIRTRUST_LOG_ERROR("failed to start domain: {}", domainName); if (StopVRoot(uuid.data()) != 0) { VIRTRUST_LOG_ERROR("stop vRoot failed domain: {}", domainName); -- Gitee