diff --git a/OAT.xml b/OAT.xml index de43b3a52772cf67f1af61d8603165a9dd0837a4..20a119eccd893c3a0806a1a0e7aecd445897cab3 100644 --- a/OAT.xml +++ b/OAT.xml @@ -49,8 +49,14 @@ + + + - + + + + diff --git a/interfaces/innerkits/common/include/errcode.h b/interfaces/innerkits/common/include/errcode.h index 1145993eb5a8222ab66783924d388fd1201f6eaf..ad3c25026ed4bebe45bf20ef7ed2932e74c6335b 100644 --- a/interfaces/innerkits/common/include/errcode.h +++ b/interfaces/innerkits/common/include/errcode.h @@ -96,6 +96,7 @@ enum SignBlockErrCode { CS_ERR_SIGN_EXTENSION_OFFSET_ALIGN = -0x622, CS_ERR_TARGET_FILE_PATH = -0x623, CS_ERR_INVALID_OWNER_ID = -0x624, + CS_CODE_SIGN_NOT_EXISTS = -0x625, }; } } diff --git a/services/key_enable/utils/BUILD.gn b/services/key_enable/utils/BUILD.gn index c8c22abf6bb82c4c0c4a207505f1034fa90464b6..8bcde1a5980e940a76ab6d2bb6b6713bb6304551 100644 --- a/services/key_enable/utils/BUILD.gn +++ b/services/key_enable/utils/BUILD.gn @@ -16,7 +16,7 @@ import("../../../code_signature.gni") ohos_static_library("libkey_enable_utils") { sources = [ - "src/cert_utils.cpp", + "src/cert_path.cpp", "src/key_utils.cpp", "src/local_code_sign_utils.cpp", ] diff --git a/services/key_enable/utils/include/cert_utils.h b/services/key_enable/utils/include/cert_path.h similarity index 100% rename from services/key_enable/utils/include/cert_utils.h rename to services/key_enable/utils/include/cert_path.h diff --git a/services/key_enable/utils/src/cert_utils.cpp b/services/key_enable/utils/src/cert_path.cpp similarity index 98% rename from services/key_enable/utils/src/cert_utils.cpp rename to services/key_enable/utils/src/cert_path.cpp index 3feacbd6f0bfdf44e4b472079ce88d057a5ce2aa..35e6ffd09f48b5419a3cbce76a490543e940df7a 100644 --- a/services/key_enable/utils/src/cert_utils.cpp +++ b/services/key_enable/utils/src/cert_path.cpp @@ -21,7 +21,7 @@ #include #include "log.h" #include "errcode.h" -#include "cert_utils.h" +#include "cert_path.h" using namespace OHOS::Security::CodeSign; diff --git a/test/fuzztest/local_code_sign_stub/signlocalcodestub_fuzzer/signlocalcodestub_fuzzer.cpp b/test/fuzztest/local_code_sign_stub/signlocalcodestub_fuzzer/signlocalcodestub_fuzzer.cpp index 8ed4e3734cd88814a7748a3530c85ad7e3604534..ccd998a2bd5b4e1b3677bd29fbec498c31affdab 100644 --- a/test/fuzztest/local_code_sign_stub/signlocalcodestub_fuzzer/signlocalcodestub_fuzzer.cpp +++ b/test/fuzztest/local_code_sign_stub/signlocalcodestub_fuzzer/signlocalcodestub_fuzzer.cpp @@ -41,7 +41,6 @@ namespace OHOS { SetSelfTokenID(tokenId); } - bool SignLocalCodeStubFuzzTest(const uint8_t *data, size_t size) { if ((data == nullptr) || (size == 0)) { diff --git a/test/unittest/BUILD.gn b/test/unittest/BUILD.gn index d4d216af80aea12b66ade7d2003a867666c48af7..c1e76fbbe1b93089901f8d9b70b2cdc49f470a12 100644 --- a/test/unittest/BUILD.gn +++ b/test/unittest/BUILD.gn @@ -14,9 +14,27 @@ import("//build/test.gni") import("../../code_signature.gni") -ohos_unittest("code_sign_ioctl_unittest") { +ohos_source_set("key_enable_src_set") { + sources = [ + "${code_signature_root_dir}/services/key_enable/utils/src/cert_path.cpp", + "utils/src/enable_key_utils.cpp", + ] + include_dirs = [ + "utils/include", + "${code_signature_root_dir}/services/key_enable/utils/include", + ] + configs = [ + "${code_signature_root_dir}:common_utils_config", + "${code_signature_root_dir}:common_public_config", + ] + external_deps = [ "hilog:libhilog" ] + part_name = "code_signature" + subsystem_name = "security" +} + +ohos_unittest("add_cert_path_unittest") { module_out_path = "security/code_signature" - sources = [ "code_sign_ioctl_test.cpp" ] + sources = [ "add_cert_path_test.cpp" ] deps = [ "${googletest_dir}:gtest" ] } @@ -27,11 +45,13 @@ ohos_unittest("code_sign_utils_unittest") { sources = [ "code_sign_utils_test.cpp" ] deps = [ + ":key_enable_src_set", "${code_signature_root_dir}/interfaces/innerkits/code_sign_utils:libcode_sign_utils", "${googletest_dir}:gtest", ] include_dirs = [ + "utils/include", "${code_signature_root_dir}/interfaces/innerkits/code_sign_utils/include", "${code_signature_root_dir}/utils/include", ] @@ -165,12 +185,33 @@ ohos_rust_unittest("rust_key_enable_unittest") { part_name = "code_signature" } +ohos_unittest("enable_verity_ioctl_unittest") { + module_out_path = "security/code_signature" + resource_config_file = "resources/ohos_test.xml" + sources = [ "enable_verity_test.cpp" ] + + include_dirs = [ "utils/include" ] + configs = [ + "${code_signature_root_dir}:common_utils_config", + "${code_signature_root_dir}:common_public_config", + ] + deps = [ + ":key_enable_src_set", + "${googletest_dir}:gtest", + ] + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + ] +} + group("unittest_group") { testonly = true if (!defined(ohos_lite)) { deps = [ - ":code_sign_ioctl_unittest", + ":add_cert_path_unittest", ":code_sign_utils_unittest", + ":enable_verity_ioctl_unittest", ":local_code_sign_unittest", ":multi_thread_local_sign_unittest", ":rust_key_enable_unittest", diff --git a/test/unittest/code_sign_ioctl_test.cpp b/test/unittest/add_cert_path_test.cpp similarity index 83% rename from test/unittest/code_sign_ioctl_test.cpp rename to test/unittest/add_cert_path_test.cpp index 848c05ec56e8211581a98b39eedad115a75c48ed..17c0ed4a3f98add000867be9b932a9af40f4b2de 100644 --- a/test/unittest/code_sign_ioctl_test.cpp +++ b/test/unittest/add_cert_path_test.cpp @@ -46,6 +46,16 @@ static const string DEV_NAME = "/dev/code_sign"; static const string TEST_SUBJECT = "OpenHarmony Application Release"; static const string TEST_ISSUER = "OpenHarmony Application CA"; +class AddCertPathTest : public testing::Test { +public: + AddCertPathTest() {}; + virtual ~AddCertPathTest() {}; + static void SetUpTestCase() {}; + static void TearDownTestCase() {}; + void SetUp() {}; + void TearDown() {}; +}; + static bool CallIoctl(const char *signing, const char *issuer, uint32_t max_cert_chain) { int fd = open(DEV_NAME.c_str(), O_WRONLY); @@ -63,47 +73,37 @@ static bool CallIoctl(const char *signing, const char *issuer, uint32_t max_cert return ret; } -class CodeSignIoctlTest : public testing::Test { -public: - CodeSignIoctlTest() {}; - virtual ~CodeSignIoctlTest() {}; - static void SetUpTestCase() {}; - static void TearDownTestCase() {}; - void SetUp() {}; - void TearDown() {}; -}; - /** - * @tc.name: CodeSignIoctlTest_0001 + * @tc.name: AddCertPathTest_0001 * @tc.desc: successfully called interface * @tc.type: Func * @tc.require: */ -HWTEST_F(CodeSignIoctlTest, CodeSignIoctlTest_0001, TestSize.Level0) +HWTEST_F(AddCertPathTest, AddCertPathTest_0001, TestSize.Level0) { int ret = CallIoctl(TEST_SUBJECT.c_str(), TEST_ISSUER.c_str(), MAX_CERT_CHAIN); EXPECT_EQ(ret, 0); } /** - * @tc.name: CodeSignIoctlTest_0002 + * @tc.name: AddCertPathTest_0002 * @tc.desc: calling interface with greater than path len * @tc.type: Func * @tc.require: */ -HWTEST_F(CodeSignIoctlTest, CodeSignIoctlTest_0002, TestSize.Level0) +HWTEST_F(AddCertPathTest, AddCertPathTest_0002, TestSize.Level0) { int ret = CallIoctl(TEST_SUBJECT.c_str(), TEST_ISSUER.c_str(), GREATER_THAN_MAX_CERT_CHAIN); EXPECT_NE(ret, 0); } /** - * @tc.name: CodeSignIoctlTest_0003 + * @tc.name: AddCertPathTest_0003 * @tc.desc: calling interface with invalid path len * @tc.type: Func * @tc.require: */ -HWTEST_F(CodeSignIoctlTest, CodeSignIoctlTest_0003, TestSize.Level0) +HWTEST_F(AddCertPathTest, AddCertPathTest_0003, TestSize.Level0) { int ret = CallIoctl(TEST_SUBJECT.c_str(), TEST_ISSUER.c_str(), LESS_THAN_MIN_CERT_CHAIN); EXPECT_NE(ret, 0); diff --git a/test/unittest/code_sign_utils_test.cpp b/test/unittest/code_sign_utils_test.cpp index 22b191df605d1977055df0f7030d7c013b04c309..67ad295afa93bcb61ea8c58ac1893b5325f1626d 100644 --- a/test/unittest/code_sign_utils_test.cpp +++ b/test/unittest/code_sign_utils_test.cpp @@ -23,6 +23,7 @@ #include "code_sign_utils.h" #include "code_sign_block.h" +#include "enable_key_utils.h" namespace OHOS { namespace Security { @@ -30,22 +31,9 @@ namespace CodeSign { using namespace testing::ext; using namespace std; -struct cert_chain_info { - uint32_t signing_length; - uint32_t issuer_length; - uint64_t signing; - uint64_t issuer; - uint32_t max_cert_chain; - uint8_t reserved[36]; -}; - -#define WRITE_CERT_CHAIN _IOW('k', 1, cert_chain_info) - -static const uint32_t MAX_CERT_CHAIN = 3; static const std::string TMP_BASE_PATH = "/data/service/el1/public/bms/bundle_manager_service/tmp"; static const std::string TEST_APP_DTAT_DIR = "/data/app/el1/bundle/public/com.example.codesignaturetest"; static const std::string APP_BASE_PATH = "/data/app/el1/bundle/public/tmp"; -static const string DEV_NAME = "/dev/code_sign"; static const string SUBJECT = "Huawei: HarmonyOS Application Code Signature"; static const string ISSUER = "Huawei CBG Software Signing Service CA Test"; static const string OH_SUBJECT = "OpenHarmony Application Release"; @@ -127,29 +115,16 @@ class CodeSignUtilsTest : public testing::Test { public: CodeSignUtilsTest() {}; virtual ~CodeSignUtilsTest() {}; - static void SetUpTestCase() {}; + static void SetUpTestCase() + { + EXPECT_EQ(EnableTestKey(SUBJECT.c_str(), ISSUER.c_str()), 0); + EXPECT_EQ(EnableTestKey(OH_SUBJECT.c_str(), OH_ISSUER.c_str()), 0); + }; static void TearDownTestCase() {}; void SetUp() {}; void TearDown() {}; }; -static bool CallIoctl(const char *signing, const char *issuer, uint32_t max_cert_chain) -{ - int fd = open(DEV_NAME.c_str(), O_WRONLY); - EXPECT_GE(fd, 0); - - cert_chain_info arg = { 0 }; - arg.signing = reinterpret_cast(signing); - arg.issuer = reinterpret_cast(issuer); - arg.signing_length = strlen(signing) + 1; - arg.issuer_length = strlen(issuer) + 1; - arg.max_cert_chain = max_cert_chain; - int ret = ioctl(fd, WRITE_CERT_CHAIN, &arg); - - close(fd); - return ret; -} - static bool ReadSignatureFromFile(const std::string &path, ByteBuffer &data) { FILE *file = fopen(path.c_str(), "rb"); @@ -341,14 +316,11 @@ HWTEST_F(CodeSignUtilsTest, CodeSignUtilsTest_0010, TestSize.Level0) */ HWTEST_F(CodeSignUtilsTest, CodeSignUtilsTest_0011, TestSize.Level0) { - int ret = CallIoctl(SUBJECT.c_str(), ISSUER.c_str(), MAX_CERT_CHAIN); - EXPECT_EQ(ret, 0); - ByteBuffer buffer; bool flag = ReadSignatureFromFile(g_filesigEnablePath, buffer); EXPECT_EQ(flag, true); - ret = CodeSignUtils::EnforceCodeSignForFile(g_fileEnableSuc, buffer); + int32_t ret = CodeSignUtils::EnforceCodeSignForFile(g_fileEnableSuc, buffer); EXPECT_EQ(ret, CS_SUCCESS); } @@ -360,10 +332,7 @@ HWTEST_F(CodeSignUtilsTest, CodeSignUtilsTest_0011, TestSize.Level0) */ HWTEST_F(CodeSignUtilsTest, CodeSignUtilsTest_0012, TestSize.Level0) { - int ret = CallIoctl(SUBJECT.c_str(), ISSUER.c_str(), MAX_CERT_CHAIN); - EXPECT_EQ(ret, 0); - - ret = CodeSignUtils::EnforceCodeSignForApp(g_hapWithoutLibRetSuc, g_sigWithoutLibRetSucPath); + int32_t ret = CodeSignUtils::EnforceCodeSignForApp(g_hapWithoutLibRetSuc, g_sigWithoutLibRetSucPath); EXPECT_EQ(ret, CS_SUCCESS); ret = CodeSignUtils::EnforceCodeSignForApp(g_hapWithMultiLibRetSuc, g_sigWithMultiLibRetSucPath); @@ -500,9 +469,7 @@ HWTEST_F(CodeSignUtilsTest, CodeSignUtilsTest_0017, TestSize.Level0) { std::string hapRealPath = APP_BASE_PATH + "/demo_with_multi_lib/entry-default-signed-debug.hap"; EntryMap entryMap; - int ret = CallIoctl(OH_SUBJECT.c_str(), OH_ISSUER.c_str(), MAX_CERT_CHAIN); - EXPECT_EQ(ret, 0); - ret = CodeSignUtils::EnforceCodeSignForAppWithOwnerId("DEBUG_LIB_ID", hapRealPath, entryMap, FILE_SELF); + int32_t ret = CodeSignUtils::EnforceCodeSignForAppWithOwnerId("DEBUG_LIB_ID", hapRealPath, entryMap, FILE_SELF); EXPECT_EQ(ret, CS_SUCCESS); } @@ -516,9 +483,8 @@ HWTEST_F(CodeSignUtilsTest, CodeSignUtilsTest_0018, TestSize.Level0) { std::string hapRealPath = APP_BASE_PATH + "/demo_with_multi_lib/entry-default-signed-release.hap"; EntryMap entryMap; - int ret = CallIoctl(OH_SUBJECT.c_str(), OH_ISSUER.c_str(), MAX_CERT_CHAIN); - EXPECT_EQ(ret, 0); - ret = CodeSignUtils::EnforceCodeSignForAppWithOwnerId("test-app-identifier", hapRealPath, entryMap, FILE_SELF); + int32_t ret = CodeSignUtils::EnforceCodeSignForAppWithOwnerId("test-app-identifier", + hapRealPath, entryMap, FILE_SELF); EXPECT_EQ(ret, CS_SUCCESS); } @@ -532,9 +498,7 @@ HWTEST_F(CodeSignUtilsTest, CodeSignUtilsTest_0019, TestSize.Level0) { std::string hapRealPath = APP_BASE_PATH + "/demo_with_multi_lib/entry-default-signed-debug.hap"; EntryMap entryMap; - int ret = CallIoctl(OH_SUBJECT.c_str(), OH_ISSUER.c_str(), MAX_CERT_CHAIN); - EXPECT_EQ(ret, 0); - ret = CodeSignUtils::EnforceCodeSignForAppWithOwnerId("INVALID_ID", hapRealPath, entryMap, FILE_SELF); + int32_t ret = CodeSignUtils::EnforceCodeSignForAppWithOwnerId("INVALID_ID", hapRealPath, entryMap, FILE_SELF); EXPECT_EQ(ret, CS_ERR_INVALID_OWNER_ID); } @@ -548,9 +512,7 @@ HWTEST_F(CodeSignUtilsTest, CodeSignUtilsTest_0020, TestSize.Level0) { std::string hapRealPath = APP_BASE_PATH + "/demo_with_multi_lib/entry-default-signed-release.hap"; EntryMap entryMap; - int ret = CallIoctl(OH_SUBJECT.c_str(), OH_ISSUER.c_str(), MAX_CERT_CHAIN); - EXPECT_EQ(ret, 0); - ret = CodeSignUtils::EnforceCodeSignForAppWithOwnerId("INVALID_ID", hapRealPath, entryMap, FILE_SELF); + int32_t ret = CodeSignUtils::EnforceCodeSignForAppWithOwnerId("INVALID_ID", hapRealPath, entryMap, FILE_SELF); EXPECT_EQ(ret, CS_ERR_INVALID_OWNER_ID); } } // namespace CodeSign diff --git a/test/unittest/enable_verity_test.cpp b/test/unittest/enable_verity_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1054705ad0b243bf21153adaf039dcb657fe4d06 --- /dev/null +++ b/test/unittest/enable_verity_test.cpp @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "byte_buffer.h" +#include "directory_ex.h" +#include "enable_key_utils.h" +#include "log.h" + +using namespace testing::ext; + +namespace OHOS { +namespace Security { +namespace CodeSign { +constexpr uint32_t HASH_PAGE_SIZE = 4096; +constexpr uint32_t BUFFER_LENGTH = 4096; + +const std::string TEST_FILES_DIR = "/data/test/tmp/"; +const std::string TEST_FILES_LIST[] = { + "file_4K/file_4K", + "file_4K_greater/file_4K_greater", + "file_4K_less/file_4K_less", + "file_4M/file_4M", + "file_4M_greater/file_4M_greater", + "file_4M_less/file_4M_less" +}; +const int TEST_FILE_NUM = sizeof(TEST_FILES_LIST) / sizeof(TEST_FILES_LIST[0]); +const std::string TEST_DEFAULT_FILE = TEST_FILES_DIR + "file_4M_greater/file_4M_greater"; +const std::string FAKE_STRING = "AAAAA"; + +const std::string TEST_SUBJECT = "OpenHarmony Application Release"; +const std::string TEST_ISSUER = "OpenHarmony Application CA"; + +class EnableVerityTest : public testing::Test { +public: + EnableVerityTest() {}; + virtual ~EnableVerityTest() {}; + static void SetUpTestCase() + { + EXPECT_EQ(EnableTestKey(TEST_SUBJECT.c_str(), TEST_ISSUER.c_str()), 0); + }; + static void TearDownTestCase() {}; + void SetUp() {}; + void TearDown() {}; +}; + +static size_t GetFileSize(const std::string &path) +{ + FILE *file = fopen(path.c_str(), "rb"); + if (file == nullptr) { + return false; + } + if (fseek(file, 0L, SEEK_END) != 0) { + (void)fclose(file); + return false; + } + size_t fileSize = ftell(file); + (void)fclose(file); + return fileSize; +} + +static bool ReadDataFromFile(const std::string &path, ByteBuffer &data) +{ + FILE *file = fopen(path.c_str(), "rb"); + if (file == nullptr) { + return false; + } + if (fseek(file, 0L, SEEK_END) != 0) { + (void)fclose(file); + return false; + } + + size_t fileSize = ftell(file); + if (fileSize == 0) { + (void)fclose(file); + return true; + } + rewind(file); + if (!data.Resize(fileSize)) { + (void)fclose(file); + return false; + } + size_t ret = fread(data.GetBuffer(), 1, fileSize, file); + (void)fclose(file); + if (ret < fileSize) { + LOG_ERROR(LABEL, "Read size (%{public}zu) < file size", ret); + return false; + } + return true; +} + +static void CopyData(const std::string &srcPath, FILE *fout) +{ + ByteBuffer data; + EXPECT_EQ(ReadDataFromFile(srcPath, data), true); + if (data.GetSize() > 0) { + int32_t ret = fwrite(data.GetBuffer(), 1, data.GetSize(), fout); + EXPECT_EQ(ret, data.GetSize()); + } +} + +static bool CopyFile(const std::string &srcPath, const std::string &dstPath) +{ + FILE *fout = fopen(dstPath.c_str(), "wb"); + if (fout == nullptr) { + return false; + } + CopyData(srcPath, fout); + (void)fclose(fout); + return true; +} + +static void CleanFile(const std::string &filePath) +{ + EXPECT_EQ(OHOS::RemoveFile(filePath), true); +} + +static bool ExpandFile(const std::string &srcPath, const std::string &expandDataFile, + size_t gapSize, const std::string &dstPath) +{ + FILE *fout = fopen(dstPath.c_str(), "wb"); + if (fout == nullptr) { + return false; + } + CopyData(srcPath, fout); + uint8_t buffer[BUFFER_LENGTH]; + (void)memset_s(buffer, BUFFER_LENGTH, 0, BUFFER_LENGTH); + size_t writeSize = BUFFER_LENGTH; + size_t totalSize = 0; + size_t ret; + while (totalSize < gapSize) { + if (gapSize - totalSize < BUFFER_LENGTH) { + writeSize = gapSize - totalSize; + } + ret = fwrite(buffer, 1, writeSize, fout); + if (ret != writeSize) { + (void)fclose(fout); + return false; + } + LOG_ERROR(LABEL, "write padding = %{public}zu", writeSize); + totalSize += writeSize; + } + CopyData(expandDataFile, fout); + (void)fclose(fout); + return true; +} + +static void DropAllCaches() +{ + std::fstream fout; + fout.open("/proc/sys/vm/drop_caches", std::ios::out); + fout << "3"; + fout.close(); +} + +static void CheckEnableSuccess(const std::string &filePath) +{ + DropAllCaches(); + FILE *fout = fopen(filePath.c_str(), "wb"); + EXPECT_EQ(fout, nullptr); + + ByteBuffer tmp; + EXPECT_EQ(ReadDataFromFile(filePath, tmp), true); +} + +static inline size_t GetRoundUp(size_t fileSize) +{ + return ceil((double)fileSize / HASH_PAGE_SIZE) * HASH_PAGE_SIZE; +} + +static bool TamperFileTail(const std::string &filePath) +{ + FILE *file = fopen(filePath.c_str(), "ab"); + if (file == nullptr) { + return false; + } + if (fseek(file, 0L, SEEK_END) != 0) { + (void)fclose(file); + return false; + } + + size_t fileSize = ftell(file); + if (fseek(file, fileSize - FAKE_STRING.size(), SEEK_SET)) { + (void)fclose(file); + return false; + } + int32_t ret = fwrite(FAKE_STRING.c_str(), 1, FAKE_STRING.size(), file); + EXPECT_EQ(ret, FAKE_STRING.size()); + (void)fclose(file); + return true; +} + +static bool TamperFileHead(const std::string &filePath) +{ + FILE *file = fopen(filePath.c_str(), "ab"); + if (file == nullptr) { + return false; + } + if (fseek(file, 0L, SEEK_SET) != 0) { + (void)fclose(file); + return false; + } + + int32_t ret = fwrite(FAKE_STRING.c_str(), 1, FAKE_STRING.size(), file); + EXPECT_EQ(ret, FAKE_STRING.size()); + (void)fclose(file); + return true; +} + +int32_t EnableVerityOnOneFile(const std::string filePath, + struct code_sign_enable_arg *arg) +{ + int fd = open(filePath.c_str(), O_RDONLY); + int ret = 0; + + int error = ioctl(fd, FS_IOC_ENABLE_CODE_SIGN, arg); + if (error < 0) { + LOG_ERROR(LABEL, "Enable fs-verity failed, errno = <%{public}d, %{public}s>", + errno, strerror(errno)); + ret = errno; + } + close(fd); + return ret; +} + +static std::string MakeExpandTreeFile(const std::string &filePath, + struct code_sign_enable_arg *arg) +{ + size_t treeOffset = GetRoundUp(arg->data_size); + std::string expandFilePath = filePath + "_expand_tree"; + EXPECT_EQ(ExpandFile(filePath, filePath + ".tree", + treeOffset - arg->data_size, expandFilePath), true); + return expandFilePath; +} + +static void FillCommonArgs(const std::string &filePath, bool isInsideTree, + struct code_sign_enable_arg *arg, ByteBuffer &signature) +{ + bool ret; + + if (isInsideTree) { + ret = ReadDataFromFile(filePath + "_inside_tree.sig", signature); + } else { + ret = ReadDataFromFile(filePath + "_no_tree.sig", signature); + } + EXPECT_EQ(ret, true); + + size_t fileSize = GetFileSize(filePath); + arg->version = 1; + arg->cs_version = 1; // version of code signing features + arg->hash_algorithm = 1; + arg->block_size = HASH_PAGE_SIZE; + arg->salt_ptr = 0; + arg->salt_size = 0; + arg->data_size = fileSize; + arg->sig_size = signature.GetSize(); + arg->sig_ptr = reinterpret_cast(signature.GetBuffer()); +} + +static void FillOptional(const std::string &filePath, struct code_sign_enable_arg *arg) +{ + ByteBuffer rootHash; + EXPECT_EQ(ReadDataFromFile(filePath + ".hash", rootHash), true); + arg->flags = 1; + arg->tree_offset = GetRoundUp(arg->data_size); + arg->root_hash_ptr = reinterpret_cast(rootHash.GetBuffer()); +} + +static void EnableExpandedTamperFile(const std::string &filePath, + bool (*tamperFileFunc)(const std::string &filePath)) +{ + struct code_sign_enable_arg arg = {}; + ByteBuffer signature; + FillCommonArgs(filePath, true, &arg, signature); + FillOptional(filePath, &arg); + + // tamper file + std::string tmpFilePath = filePath + "_tmp"; + EXPECT_EQ(CopyFile(filePath, tmpFilePath), true); + EXPECT_EQ(tamperFileFunc(tmpFilePath), true); + + // expand tampered file + std::string treeFile = filePath + ".tree"; + std::string expandFilePath = filePath + "_expand_tree"; + size_t treeOffset = GetRoundUp(arg.data_size); + EXPECT_EQ(ExpandFile(tmpFilePath, treeFile, + treeOffset - arg.data_size, expandFilePath), true); + + // Enable successully but cannot read + EXPECT_EQ(EnableVerityOnOneFile(expandFilePath, &arg), 0); + ByteBuffer tmp; + EXPECT_EQ(ReadDataFromFile(expandFilePath, tmp), false); + + CleanFile(expandFilePath); +} + +/** + * @tc.name: CodeSignUtilsTest_0001 + * @tc.desc: enable all data in file successfully + * @tc.type: Func + * @tc.require:I8DH28 + */ +HWTEST_F(EnableVerityTest, EnableVerityTest_0001, TestSize.Level0) +{ + for (int i = 0; i < TEST_FILE_NUM; i++) { + std::string filePath = TEST_FILES_DIR + TEST_FILES_LIST[i]; + LOG_INFO(LABEL, "Test on file path = %{public}s", filePath.c_str()); + struct code_sign_enable_arg arg = {}; + ByteBuffer signature; + FillCommonArgs(filePath, false, &arg, signature); + std::string copiedFile = filePath + "_copy"; + EXPECT_EQ(CopyFile(filePath, copiedFile), true); + EXPECT_EQ(EnableVerityOnOneFile(copiedFile, &arg), 0); + CheckEnableSuccess(copiedFile); + CleanFile(copiedFile); + } +} + +/** + * @tc.name: CodeSignUtilsTest_0002 + * @tc.desc: enable orignial file with wrong file size + * @tc.type: Func + * @tc.require:I8DH28 + */ +HWTEST_F(EnableVerityTest, EnableVerityTest_0002, TestSize.Level0) +{ + std::string filePath = TEST_DEFAULT_FILE; + + struct code_sign_enable_arg arg = {}; + ByteBuffer signature; + FillCommonArgs(filePath, false, &arg, signature); + + std::string copiedFile = filePath + "_copy"; + EXPECT_EQ(CopyFile(filePath, copiedFile), true); + + uint32_t fileSize = arg.data_size; + // size is set to less than file size + // descriptor is unmatched, signature verification failed. + arg.data_size = fileSize - 1; + EXPECT_EQ(EnableVerityOnOneFile(copiedFile, &arg), EKEYREJECTED); + + // size is set to greater than file size + // unable to pass parameter check + arg.data_size = fileSize + 1; + EXPECT_EQ(EnableVerityOnOneFile(copiedFile, &arg), EINVAL); + + CleanFile(copiedFile); +} + +/** + * @tc.name: CodeSignUtilsTest_0003 + * @tc.desc: enable expanded file successfully + * @tc.type: Func + * @tc.require:I8DH28 + */ +HWTEST_F(EnableVerityTest, EnableVerityTest_0003, TestSize.Level0) +{ + for (int i = 0; i < TEST_FILE_NUM; i++) { + std::string filePath = TEST_FILES_DIR + TEST_FILES_LIST[i]; + struct code_sign_enable_arg arg = {}; + ByteBuffer signature; + FillCommonArgs(filePath, false, &arg, signature); + + std::string expandFilePath = MakeExpandTreeFile(filePath, &arg); + EXPECT_EQ(EnableVerityOnOneFile(expandFilePath, &arg), 0); + CheckEnableSuccess(expandFilePath); + CleanFile(expandFilePath); + } +} + +/** + * @tc.name: CodeSignUtilsTest_0004 + * @tc.desc: enable expanded file with inside tree successfully + * @tc.type: Func + * @tc.require:I8DH28 + */ +HWTEST_F(EnableVerityTest, EnableVerityTest_0004, TestSize.Level0) +{ + for (int i = 0; i < TEST_FILE_NUM; i++) { + std::string filePath = TEST_FILES_DIR + TEST_FILES_LIST[i]; + LOG_INFO(LABEL, "Test on file path = %{public}s", filePath.c_str()); + + struct code_sign_enable_arg arg = {}; + ByteBuffer signature; + FillCommonArgs(filePath, true, &arg, signature); + FillOptional(filePath, &arg); + std::string expandFilePath = MakeExpandTreeFile(filePath, &arg); + EXPECT_EQ(EnableVerityOnOneFile(expandFilePath, &arg), 0); + CheckEnableSuccess(expandFilePath); + CleanFile(expandFilePath); + } +} + +/** + * @tc.name: CodeSignUtilsTest_0005 + * @tc.desc: enable expanded file with wrong tree offset + * @tc.type: Func + * @tc.require:I8DH28 + */ +HWTEST_F(EnableVerityTest, EnableVerityTest_0005, TestSize.Level0) +{ + std::string filePath = TEST_DEFAULT_FILE; + struct code_sign_enable_arg arg = {}; + ByteBuffer signature; + FillCommonArgs(filePath, true, &arg, signature); + FillOptional(filePath, &arg); + std::string expandFilePath = MakeExpandTreeFile(filePath, &arg); + + uint32_t treeOffset = arg.tree_offset; + // byte alignment check failed + arg.tree_offset = treeOffset + 1; + EXPECT_EQ(EnableVerityOnOneFile(expandFilePath, &arg), EINVAL); + + // unmatched fsverity descriptor + arg.tree_offset = treeOffset - HASH_PAGE_SIZE; + EXPECT_EQ(EnableVerityOnOneFile(expandFilePath, &arg), EKEYREJECTED); + + CleanFile(expandFilePath); +} + +/** + * @tc.name: CodeSignUtilsTest_0006 + * @tc.desc: enable expanded file with wrong root hash + * @tc.type: Func + * @tc.require:I8DH28 + */ +HWTEST_F(EnableVerityTest, EnableVerityTest_0006, TestSize.Level0) +{ + std::string filePath = TEST_DEFAULT_FILE; + struct code_sign_enable_arg arg = {}; + ByteBuffer signature; + FillCommonArgs(filePath, true, &arg, signature); + FillOptional(filePath, &arg); + std::string expandFilePath = MakeExpandTreeFile(filePath, &arg); + + (void)memcpy_s(reinterpret_cast(arg.root_hash_ptr), + FAKE_STRING.size(), FAKE_STRING.c_str(), FAKE_STRING.size()); + + EXPECT_EQ(EnableVerityOnOneFile(expandFilePath, &arg), EKEYREJECTED); + + CleanFile(expandFilePath); +} + +/** + * @tc.name: CodeSignUtilsTest_0007 + * @tc.desc: enable expanded file with wrong file + * @tc.type: Func + * @tc.require:I8DH28 + */ +HWTEST_F(EnableVerityTest, EnableVerityTest_0007, TestSize.Level0) +{ + std::string filePath = TEST_DEFAULT_FILE; + EnableExpandedTamperFile(filePath, TamperFileHead); + + EnableExpandedTamperFile(filePath, TamperFileTail); +} +} // namespace CodeSign +} // namespace Security +} // namespace OHOS diff --git a/test/unittest/resources/demo_verity/file_4K/file_4K b/test/unittest/resources/demo_verity/file_4K/file_4K new file mode 100644 index 0000000000000000000000000000000000000000..ef6ce649d71849f425f4152130fd2b656c29065a Binary files /dev/null and b/test/unittest/resources/demo_verity/file_4K/file_4K differ diff --git a/test/unittest/resources/demo_verity/file_4K/file_4K.hash b/test/unittest/resources/demo_verity/file_4K/file_4K.hash new file mode 100644 index 0000000000000000000000000000000000000000..f05da993ebf2dfa7a1e5741f4cf4a37a5bb3fe98 --- /dev/null +++ b/test/unittest/resources/demo_verity/file_4K/file_4K.hash @@ -0,0 +1,2 @@ +ׇoK:ki#DxZ +(= \ No newline at end of file diff --git a/test/unittest/resources/demo_verity/file_4K/file_4K.tree b/test/unittest/resources/demo_verity/file_4K/file_4K.tree new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/test/unittest/resources/demo_verity/file_4K/file_4K_expand_tree b/test/unittest/resources/demo_verity/file_4K/file_4K_expand_tree new file mode 100644 index 0000000000000000000000000000000000000000..1020fe35b73cde34ff3c1d9363798a92322049d1 Binary files /dev/null and b/test/unittest/resources/demo_verity/file_4K/file_4K_expand_tree differ diff --git a/test/unittest/resources/demo_verity/file_4K/file_4K_inside_tree.sig b/test/unittest/resources/demo_verity/file_4K/file_4K_inside_tree.sig new file mode 100644 index 0000000000000000000000000000000000000000..c721521235d72da8aecdfd1e5b43128bbd34d5cf Binary files /dev/null and b/test/unittest/resources/demo_verity/file_4K/file_4K_inside_tree.sig differ diff --git a/test/unittest/resources/demo_verity/file_4K/file_4K_no_tree.sig b/test/unittest/resources/demo_verity/file_4K/file_4K_no_tree.sig new file mode 100644 index 0000000000000000000000000000000000000000..83677cb396b77a410544b7e5b638d8f9d133ba4c Binary files /dev/null and b/test/unittest/resources/demo_verity/file_4K/file_4K_no_tree.sig differ diff --git a/test/unittest/resources/demo_verity/file_4K_greater/file_4K_greater b/test/unittest/resources/demo_verity/file_4K_greater/file_4K_greater new file mode 100644 index 0000000000000000000000000000000000000000..688a743d914decba64ff66a70acd835b9cbc00a6 Binary files /dev/null and b/test/unittest/resources/demo_verity/file_4K_greater/file_4K_greater differ diff --git a/test/unittest/resources/demo_verity/file_4K_greater/file_4K_greater.hash b/test/unittest/resources/demo_verity/file_4K_greater/file_4K_greater.hash new file mode 100644 index 0000000000000000000000000000000000000000..2f6837f02acbf76ef27f4d52ba914ac1c174830d --- /dev/null +++ b/test/unittest/resources/demo_verity/file_4K_greater/file_4K_greater.hash @@ -0,0 +1 @@ +@~&~\ƗL|.|nA \ No newline at end of file diff --git a/test/unittest/resources/demo_verity/file_4K_greater/file_4K_greater.tree b/test/unittest/resources/demo_verity/file_4K_greater/file_4K_greater.tree new file mode 100644 index 0000000000000000000000000000000000000000..d2246d37a34245caa4a8f02e633c76abd0911479 Binary files /dev/null and b/test/unittest/resources/demo_verity/file_4K_greater/file_4K_greater.tree differ diff --git a/test/unittest/resources/demo_verity/file_4K_greater/file_4K_greater_expand_tree b/test/unittest/resources/demo_verity/file_4K_greater/file_4K_greater_expand_tree new file mode 100644 index 0000000000000000000000000000000000000000..109d20e11f56937ea2332ab4ff3c5b84fafe9c80 Binary files /dev/null and b/test/unittest/resources/demo_verity/file_4K_greater/file_4K_greater_expand_tree differ diff --git a/test/unittest/resources/demo_verity/file_4K_greater/file_4K_greater_inside_tree.sig b/test/unittest/resources/demo_verity/file_4K_greater/file_4K_greater_inside_tree.sig new file mode 100644 index 0000000000000000000000000000000000000000..3b492e2353921af79194b3c3d77edcac61d7c165 Binary files /dev/null and b/test/unittest/resources/demo_verity/file_4K_greater/file_4K_greater_inside_tree.sig differ diff --git a/test/unittest/resources/demo_verity/file_4K_greater/file_4K_greater_no_tree.sig b/test/unittest/resources/demo_verity/file_4K_greater/file_4K_greater_no_tree.sig new file mode 100644 index 0000000000000000000000000000000000000000..b16d5a45d95c8e0cf1243e1c409ad7bcbb496beb Binary files /dev/null and b/test/unittest/resources/demo_verity/file_4K_greater/file_4K_greater_no_tree.sig differ diff --git a/test/unittest/resources/demo_verity/file_4K_less/file_4K_less b/test/unittest/resources/demo_verity/file_4K_less/file_4K_less new file mode 100644 index 0000000000000000000000000000000000000000..bd2259de75f2c2612995f3fab5856c9d09985c35 Binary files /dev/null and b/test/unittest/resources/demo_verity/file_4K_less/file_4K_less differ diff --git a/test/unittest/resources/demo_verity/file_4K_less/file_4K_less.hash b/test/unittest/resources/demo_verity/file_4K_less/file_4K_less.hash new file mode 100644 index 0000000000000000000000000000000000000000..aeb395b86a29179022a3a634da8cc4213015f3b2 --- /dev/null +++ b/test/unittest/resources/demo_verity/file_4K_less/file_4K_less.hash @@ -0,0 +1 @@ +WǿꨬX