/*

 * Copyright (c) 2023-2025 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 <cstdlib>

#include <fcntl.h>

#include <gtest/gtest.h>

#include <string>

#include <sys/mman.h>

#include <sys/stat.h>

#include <unistd.h>



#include "access_token_setter.h"

#include "byte_buffer.h"

#include "code_sign_attr_utils.h"

#include "code_sign_test_common.h"

#include "code_sign_utils.h"

#include "local_code_sign_client.h"

#include "local_code_sign_kit.h"

#include "local_code_sign_load_callback.h"

#include "local_key_helper.h"

#include "log.h"

#include "signer_info.h"



using namespace OHOS::Security::CodeSign;

using namespace testing::ext;

using namespace std;



namespace OHOS {

namespace Security {

namespace CodeSign {

static const std::string AN_BASE_PATH = "/data/local/ark-cache/tmp/";

static const std::string DEMO_AN_PATH = AN_BASE_PATH + "demo.an";

static const std::string DEMO_AN_PATH2 = AN_BASE_PATH + "demo2.an";

static const std::string EMPTY_FILE_PATH = AN_BASE_PATH + "empty.an";



class LocalCodeSignTest : public testing::Test {

public:

    LocalCodeSignTest() {};

    virtual ~LocalCodeSignTest() {};

    static void SetUpTestCase() {};

    static void TearDownTestCase() {};

    void SetUp() {};

    void TearDown() {};

};



/**

 * @tc.name: LocalCodeSignTest_0001

 * @tc.desc: init local certificate successfully

 * @tc.type: Func

 * @tc.require:

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0001, TestSize.Level0)

{

    ByteBuffer cert;

    uint64_t selfTokenId = GetSelfTokenID();

    EXPECT_TRUE(MockTokenId("key_enable"));

    int ret = LocalCodeSignKit::InitLocalCertificate(cert);

    EXPECT_EQ(0, SetSelfTokenID(selfTokenId));

    EXPECT_EQ(ret, CS_SUCCESS);

}



/**

 * @tc.name: LocalCodeSignTest_0002

 * @tc.desc: init local certificate failed with invalid caller

 * @tc.type: Func

 * @tc.require:

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0002, TestSize.Level0)

{

    ByteBuffer cert;

    int ret = LocalCodeSignKit::InitLocalCertificate(cert);

    EXPECT_EQ(ret, CS_ERR_NO_PERMISSION);

}



/**

 * @tc.name: LocalCodeSignTest_0003

 * @tc.desc: sign local code successfully, owner ID is empty

 * @tc.type: Func

 * @tc.require:

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0003, TestSize.Level0)

{

    ByteBuffer sig;

    uint64_t selfTokenId = GetSelfTokenID();

    EXPECT_TRUE(MockTokenId("compiler_service"));

    int ret = LocalCodeSignKit::SignLocalCode(DEMO_AN_PATH, sig);

    EXPECT_EQ(0, SetSelfTokenID(selfTokenId));

    EXPECT_EQ(ret, CS_SUCCESS);

    std::string retOwnerID;

    ret = CodeSignUtils::ParseOwnerIdFromSignature(sig, retOwnerID);

    EXPECT_EQ(ret, CS_ERR_NO_OWNER_ID);

    EXPECT_EQ(retOwnerID, "");

    ret = CodeSignUtils::EnforceCodeSignForFile(DEMO_AN_PATH, sig);

    EXPECT_EQ(ret, GetEnforceFileResult());

}



/**

 * @tc.name: LocalCodeSignTest_0004

 * @tc.desc: sign local code failed with invalid caller

 * @tc.type: Func

 * @tc.require:

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0004, TestSize.Level0)

{

    ByteBuffer sig;

    int ret = LocalCodeSignKit::SignLocalCode(DEMO_AN_PATH, sig);

    EXPECT_EQ(ret, CS_ERR_NO_PERMISSION);

}



/**

 * @tc.name: LocalCodeSignTest_0005

 * @tc.desc: sign local code failed with wrong path

 * @tc.type: Func

 * @tc.require:

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0005, TestSize.Level0)

{

    ByteBuffer sig;

    uint64_t selfTokenId = GetSelfTokenID();

    EXPECT_TRUE(MockTokenId("compiler_service"));

    int ret = LocalCodeSignKit::SignLocalCode(DEMO_AN_PATH + "invalid", sig);

    EXPECT_EQ(0, SetSelfTokenID(selfTokenId));

    EXPECT_EQ(ret, CS_ERR_FILE_PATH);

}



/**

 * @tc.name: LocalCodeSignTest_0006

 * @tc.desc: local codesignsvr died

 * @tc.type: Func

 * @tc.require:

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0006, TestSize.Level0)

{

    LocalCodeSignClient *client = GetLocalCodeSignClient();

    EXPECT_NE(client, nullptr);

    sptr<ISystemAbilityManager> systemAbilityManager =

        SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();

    EXPECT_NE(systemAbilityManager, nullptr);

    sptr<IRemoteObject> remoteObject =

        systemAbilityManager->GetSystemAbility(LOCAL_CODE_SIGN_SA_ID);

    client->OnRemoteLocalCodeSignSvrDied(remoteObject);

}



/**

 * @tc.name: LocalCodeSignTest_0007

 * @tc.desc: sign local code with owner ID successfully, parse owner ID from signature success

 * @tc.type: Func

 * @tc.require: issueI88PPA

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0007, TestSize.Level0)

{

    ByteBuffer sig;

    uint64_t selfTokenId = GetSelfTokenID();

    EXPECT_TRUE(MockTokenId("compiler_service"));

    std::string ownerID = "AppName123";

    int ret = LocalCodeSignKit::SignLocalCode(ownerID, DEMO_AN_PATH2, sig);

    EXPECT_EQ(0, SetSelfTokenID(selfTokenId));

    EXPECT_EQ(ret, CS_SUCCESS);

    

    std::string retOwnerID;

    ret = CodeSignUtils::ParseOwnerIdFromSignature(sig, retOwnerID);

    EXPECT_EQ(ownerID, retOwnerID);

    ret = CodeSignUtils::EnforceCodeSignForFile(DEMO_AN_PATH2, sig);

    EXPECT_EQ(ret, GetEnforceFileResult());

}



/**

 * @tc.name: LocalCodeSignTest_0008

 * @tc.desc: sign local code with empty owner ID successfully

 * @tc.type: Func

 * @tc.require: issueI88PPA

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0008, TestSize.Level0)

{

    ByteBuffer sig;

    uint64_t selfTokenId = GetSelfTokenID();

    EXPECT_TRUE(MockTokenId("compiler_service"));

    std::string ownerID = "";

    int ret = LocalCodeSignKit::SignLocalCode(ownerID, DEMO_AN_PATH2, sig);

    EXPECT_EQ(0, SetSelfTokenID(selfTokenId));

    EXPECT_EQ(ret, CS_SUCCESS);

    std::string retOwnerID;

    ret = CodeSignUtils::ParseOwnerIdFromSignature(sig, retOwnerID);

    EXPECT_EQ(ret, CS_ERR_NO_OWNER_ID);

    EXPECT_EQ(retOwnerID, "");

}



/**

 * @tc.name: LocalCodeSignTest_0009

 * @tc.desc: sign local code with owner ID failed, reason = invalid path

 * @tc.type: Func

 * @tc.require: issueI88PPA

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0009, TestSize.Level0)

{

    ByteBuffer sig;

    uint64_t selfTokenId = GetSelfTokenID();

    EXPECT_TRUE(MockTokenId("compiler_service"));

    std::string ownerID = "AppName123";

    int ret = LocalCodeSignKit::SignLocalCode(ownerID, DEMO_AN_PATH2 + "invalid", sig);

    EXPECT_EQ(0, SetSelfTokenID(selfTokenId));

    EXPECT_EQ(ret, CS_ERR_FILE_PATH);

}



/**

 * @tc.name: LocalCodeSignTest_0010

 * @tc.desc: sign local code failed with invalid caller

 * @tc.type: Func

 * @tc.require: issueI88PPA

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0010, TestSize.Level0)

{

    ByteBuffer sig;

    std::string ownerID = "AppName123";

    int ret = LocalCodeSignKit::SignLocalCode(ownerID, DEMO_AN_PATH2, sig);

    EXPECT_EQ(ret, CS_ERR_NO_PERMISSION);

}



/**

 * @tc.name: LocalCodeSignTest_0011

 * @tc.desc: sign local code failed with ownerID exceed 128 bytes

 * @tc.type: Func

 * @tc.require: issueI8FCGF

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0011, TestSize.Level0)

{

    ByteBuffer sig;

    uint64_t selfTokenId = GetSelfTokenID();

    EXPECT_TRUE(MockTokenId("compiler_service"));

    std::string ownerID(33, 'a');

    int ret = LocalCodeSignKit::SignLocalCode(ownerID, DEMO_AN_PATH2, sig);

    EXPECT_EQ(0, SetSelfTokenID(selfTokenId));

    EXPECT_EQ(ret, CS_ERR_INVALID_OWNER_ID);

}



/**

 * @tc.name: LocalCodeSignTest_0012

 * @tc.desc: sign local code failed with ownerID exceed 128 bytes

 * @tc.type: Func

 * @tc.require: issueI8FCGF

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0012, TestSize.Level0)

{

    ByteBuffer sig;

    uint64_t selfTokenId = GetSelfTokenID();

    EXPECT_TRUE(MockTokenId("compiler_service"));

    std::string ownerID = "AppName123";



    int ret = LocalCodeSignKit::SignLocalCode(ownerID, DEMO_AN_PATH2, sig);



    EXPECT_TRUE(MockTokenId("local_code_sign"));

    sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();

    EXPECT_NE(samgr, nullptr);



    ret = samgr->UnloadSystemAbility(LOCAL_CODE_SIGN_SA_ID);

    EXPECT_EQ(ret, ERR_OK);

    EXPECT_TRUE(MockTokenId("compiler_service"));

    LocalCodeSignKit::SignLocalCode(ownerID, DEMO_AN_PATH2, sig);

    EXPECT_EQ(0, SetSelfTokenID(selfTokenId));

}



/**

 * @tc.name: LocalCodeSignTest_0013

 * @tc.desc: load sa success and return remote object is not null

 * @tc.type: Func

 * @tc.require:

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0013, TestSize.Level0)

{

    LocalCodeSignLoadCallback cb;

    cb.OnLoadSystemAbilityFail(LOCAL_CODE_SIGN_SA_ID);

    cb.OnLoadSystemAbilitySuccess(LOCAL_CODE_SIGN_SA_ID - 1, nullptr);

    cb.OnLoadSystemAbilitySuccess(LOCAL_CODE_SIGN_SA_ID, nullptr);



    sptr<ISystemAbilityManager> systemAbilityManager =

        SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();

    EXPECT_NE(systemAbilityManager, nullptr);

    sptr<IRemoteObject> remoteObject =

        systemAbilityManager->GetSystemAbility(LOCAL_CODE_SIGN_SA_ID);

    cb.OnLoadSystemAbilitySuccess(LOCAL_CODE_SIGN_SA_ID, remoteObject);

}



/**

 * @tc.name: LocalCodeSignTest_0014

 * @tc.desc: sign local code failed with ownerID equals SYSTEM_LIB_ID

 * @tc.type: Func

 * @tc.require:

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0014, TestSize.Level0)

{

    ByteBuffer sig;

    uint64_t selfTokenId = GetSelfTokenID();

    EXPECT_TRUE(MockTokenId("compiler_service"));

    std::string ownerID = OWNERID_SYSTEM_TAG;

    int ret = LocalCodeSignKit::SignLocalCode(ownerID, DEMO_AN_PATH2, sig);

    EXPECT_EQ(0, SetSelfTokenID(selfTokenId));

    EXPECT_EQ(ret, CS_ERR_FORBIDDEN_OWNER_ID);

}



/**

 * @tc.name: LocalCodeSignTest_0015

 * @tc.desc: sign local code failed with ownerID equals COMPAT_LIB_ID

 * @tc.type: Func

 * @tc.require:

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0015, TestSize.Level0)

{

    ByteBuffer sig;

    uint64_t selfTokenId = GetSelfTokenID();

    EXPECT_TRUE(MockTokenId("compiler_service"));

    std::string ownerID = OWNERID_COMPAT_TAG;

    int ret = LocalCodeSignKit::SignLocalCode(ownerID, DEMO_AN_PATH2, sig);

    EXPECT_EQ(0, SetSelfTokenID(selfTokenId));

    EXPECT_EQ(ret, CS_ERR_FORBIDDEN_OWNER_ID);

}



/**

 * @tc.name: LocalCodeSignTest_0016

 * @tc.desc: sign local code by fd successfully with empty owner ID

 * @tc.type: Func

 * @tc.require:

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0016, TestSize.Level0)

{

    ByteBuffer sig;

    uint64_t selfTokenId = GetSelfTokenID();

    EXPECT_TRUE(MockTokenId("compiler_service"));

    int fd = open(DEMO_AN_PATH.c_str(), O_RDONLY);

    EXPECT_GE(fd, 0);

    std::string ownerID;

    int ret = LocalCodeSignKit::SignLocalCodeByFd(ownerID, fd, sig);

    close(fd);

    EXPECT_EQ(0, SetSelfTokenID(selfTokenId));

    EXPECT_EQ(ret, CS_SUCCESS);

}



/**

 * @tc.name: LocalCodeSignTest_0017

 * @tc.desc: sign local code by fd failed with invalid caller

 * @tc.type: Func

 * @tc.require:

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0017, TestSize.Level0)

{

    ByteBuffer sig;

    int fd = open(DEMO_AN_PATH.c_str(), O_RDONLY);

    EXPECT_GE(fd, 0);

    std::string ownerID;

    int ret = LocalCodeSignKit::SignLocalCodeByFd(ownerID, fd, sig);

    close(fd);

    EXPECT_EQ(ret, CS_ERR_NO_PERMISSION);

}



/**

 * @tc.name: LocalCodeSignTest_0018

 * @tc.desc: sign local code by fd failed with invalid fd (negative)

 * @tc.type: Func

 * @tc.require:

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0018, TestSize.Level0)

{

    ByteBuffer sig;

    uint64_t selfTokenId = GetSelfTokenID();

    EXPECT_TRUE(MockTokenId("compiler_service"));

    std::string ownerID;

    int ret = LocalCodeSignKit::SignLocalCodeByFd(ownerID, -1, sig);

    EXPECT_EQ(0, SetSelfTokenID(selfTokenId));

    EXPECT_EQ(ret, CS_ERR_INVALID_FD);

}



/**

 * @tc.name: LocalCodeSignTest_0019

 * @tc.desc: sign local code by fd successfully with owner ID, parse owner ID from signature success

 * @tc.type: Func

 * @tc.require:

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0019, TestSize.Level0)

{

    ByteBuffer sig;

    uint64_t selfTokenId = GetSelfTokenID();

    EXPECT_TRUE(MockTokenId("compiler_service"));

    std::string ownerID = "AppName123";

    int fd = open(DEMO_AN_PATH2.c_str(), O_RDONLY);

    EXPECT_GE(fd, 0);

    int ret = LocalCodeSignKit::SignLocalCodeByFd(ownerID, fd, sig);

    close(fd);

    EXPECT_EQ(0, SetSelfTokenID(selfTokenId));

    EXPECT_EQ(ret, CS_SUCCESS);



    std::string retOwnerID;

    ret = CodeSignUtils::ParseOwnerIdFromSignature(sig, retOwnerID);

    EXPECT_EQ(ownerID, retOwnerID);

    ret = CodeSignUtils::EnforceCodeSignForFile(DEMO_AN_PATH2, sig);

    EXPECT_EQ(ret, GetEnforceFileResult());

}



/**

 * @tc.name: LocalCodeSignTest_0021

 * @tc.desc: sign local code by fd failed with ownerID exceeding 32 bytes

 * @tc.type: Func

 * @tc.require:

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0021, TestSize.Level0)

{

    ByteBuffer sig;

    uint64_t selfTokenId = GetSelfTokenID();

    EXPECT_TRUE(MockTokenId("compiler_service"));

    std::string ownerID(33, 'a');

    int fd = open(DEMO_AN_PATH2.c_str(), O_RDONLY);

    EXPECT_GE(fd, 0);

    int ret = LocalCodeSignKit::SignLocalCodeByFd(ownerID, fd, sig);

    close(fd);

    EXPECT_EQ(0, SetSelfTokenID(selfTokenId));

    EXPECT_EQ(ret, CS_ERR_INVALID_OWNER_ID);

}



/**

 * @tc.name: LocalCodeSignTest_0022

 * @tc.desc: sign local code by fd failed with ownerID equals SYSTEM_LIB_ID

 * @tc.type: Func

 * @tc.require:

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0022, TestSize.Level0)

{

    ByteBuffer sig;

    uint64_t selfTokenId = GetSelfTokenID();

    EXPECT_TRUE(MockTokenId("compiler_service"));

    std::string ownerID = OWNERID_SYSTEM_TAG;

    int fd = open(DEMO_AN_PATH2.c_str(), O_RDONLY);

    EXPECT_GE(fd, 0);

    int ret = LocalCodeSignKit::SignLocalCodeByFd(ownerID, fd, sig);

    close(fd);

    EXPECT_EQ(0, SetSelfTokenID(selfTokenId));

    EXPECT_EQ(ret, CS_ERR_FORBIDDEN_OWNER_ID);

}



/**

 * @tc.name: LocalCodeSignTest_0023

 * @tc.desc: sign local code by fd failed with ownerID equals COMPAT_LIB_ID

 * @tc.type: Func

 * @tc.require:

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0023, TestSize.Level0)

{

    ByteBuffer sig;

    uint64_t selfTokenId = GetSelfTokenID();

    EXPECT_TRUE(MockTokenId("compiler_service"));

    std::string ownerID = OWNERID_COMPAT_TAG;

    int fd = open(DEMO_AN_PATH2.c_str(), O_RDONLY);

    EXPECT_GE(fd, 0);

    int ret = LocalCodeSignKit::SignLocalCodeByFd(ownerID, fd, sig);

    close(fd);

    EXPECT_EQ(0, SetSelfTokenID(selfTokenId));

    EXPECT_EQ(ret, CS_ERR_FORBIDDEN_OWNER_ID);

}



/**

 * @tc.name: LocalCodeSignTest_0024

 * @tc.desc: sign local code by fd with empty file (libfsverity computes digest for size=0, signing succeeds)

 * @tc.type: Func

 * @tc.require:

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0024, TestSize.Level0)

{

    // This test requires local signing key to be provisioned.

    // Skip on environments where the key is not available (e.g., RK boards without HUKS provisioning).

    if (GetEnforceFileResult() != CS_SUCCESS) {

        GTEST_SKIP() << "Local signing key not provisioned, skipping empty file signing test";

    }



    ByteBuffer sig;

    uint64_t selfTokenId = GetSelfTokenID();

    EXPECT_TRUE(MockTokenId("compiler_service"));

    int fd = open(EMPTY_FILE_PATH.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0644);

    EXPECT_GE(fd, 0);

    std::string ownerID = "AppName123";

    int ret = LocalCodeSignKit::SignLocalCodeByFd(ownerID, fd, sig);

    close(fd);

    unlink(EMPTY_FILE_PATH.c_str());

    EXPECT_EQ(0, SetSelfTokenID(selfTokenId));

    // libfsverity can compute digest for empty file (size=0), signing succeeds

    EXPECT_EQ(ret, CS_SUCCESS);

}



/**

 * @tc.name: LocalCodeSignTest_0025

 * @tc.desc: sign local code by fd with memfd (non-regular file)

 * @tc.type: Func

 * @tc.require:

 */

HWTEST_F(LocalCodeSignTest, LocalCodeSignTest_0025, TestSize.Level0)

{

    if (GetEnforceFileResult() != CS_SUCCESS) {

        GTEST_SKIP() << "Local signing key not provisioned, skipping memfd signing test";

    }



    int fd = memfd_create("test_memfd_sign", MFD_ALLOW_SEALING);

    ASSERT_GE(fd, 0);

    const char data[] = "memfd test data for code signing";

    ssize_t written = write(fd, data, sizeof(data));

    ASSERT_EQ(written, static_cast<ssize_t>(sizeof(data)));

    lseek(fd, 0, SEEK_SET);



    ByteBuffer sig;

    uint64_t selfTokenId = GetSelfTokenID();

    EXPECT_TRUE(MockTokenId("compiler_service"));

    std::string ownerID = "MemfdTest";

    int ret = LocalCodeSignKit::SignLocalCodeByFd(ownerID, fd, sig);

    close(fd);

    EXPECT_EQ(0, SetSelfTokenID(selfTokenId));

    EXPECT_EQ(ret, CS_SUCCESS);

}

} // namespace CodeSign

} // namespace Security

} // namespace OHOS