* Copyright (c) 2023-2024 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 "fsverity_utils_helper.h"
#include <common_defs.h>
#include <fcntl.h>
#include <fsverity_uapi.h>
#include <sys/types.h>
#include <unistd.h>
#include "errcode.h"
#include "file_helper.h"
#include "log.h"
namespace OHOS {
namespace Security {
namespace CodeSign {
static constexpr int MAX_DIGEST_SIZE = 64;
static constexpr int FSVERITY_HASH_PAGE_SIZE = 4096;
static const char *FSVERITY_DIGEST_MAGIC = "FSVerity";
static constexpr uint32_t FSVERITY_DIGEST_MAGIC_LENGTH = 8;
FsverityUtilsHelper &FsverityUtilsHelper::GetInstance()
{
static FsverityUtilsHelper singleFsverityUtilsHelper;
return singleFsverityUtilsHelper;
}
FsverityUtilsHelper::FsverityUtilsHelper()
{
Init();
}
FsverityUtilsHelper::~FsverityUtilsHelper() {}
void FsverityUtilsHelper::Init()
{
libfsverity_set_error_callback(ErrorMsgLogCallback);
}
void FsverityUtilsHelper::ErrorMsgLogCallback(const char *msg)
{
if (msg == nullptr) {
LOG_ERROR("fsverity_utils error callback received null message");
return;
}
LOG_ERROR("fsverity_utils error = %{public}s", msg);
}
bool FsverityUtilsHelper::FormatDigest(libfsverity_digest *digest, uint8_t *buffer)
{
if (digest == nullptr || buffer == nullptr) {
LOG_ERROR("Invalid parameter, digest or buffer is null.");
return false;
}
struct fsverity_formatted_digest *ret = reinterpret_cast<struct fsverity_formatted_digest *>(buffer);
if (memcpy_s(ret->magic, FSVERITY_DIGEST_MAGIC_LENGTH, FSVERITY_DIGEST_MAGIC,
FSVERITY_DIGEST_MAGIC_LENGTH) != EOK) {
return false;
}
ret->digest_algorithm = cpu_to_le16(digest->digest_algorithm);
ret->digest_size = cpu_to_le16(digest->digest_size);
if (memcpy_s(ret->digest, MAX_DIGEST_SIZE, digest->digest, digest->digest_size) != EOK) {
return false;
}
return true;
}
bool FsverityUtilsHelper::ComputeDigest(const char *path, struct libfsverity_digest **digest)
{
struct libfsverity_merkle_tree_params tree_params = {
.version = 1,
.hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
.block_size = FSVERITY_HASH_PAGE_SIZE
};
FileReader reader;
if (!reader.Open(path)) {
return false;
}
if (!reader.GetFileSize(&tree_params.file_size)) {
return false;
}
if (libfsverity_compute_digest(&reader, FileReader::ReadFileCallback, &tree_params, digest)) {
LOG_ERROR("Compute digest failed.");
return false;
}
return true;
}
bool FsverityUtilsHelper::ComputeDigestFromFd(int32_t fd, struct libfsverity_digest **digest)
{
struct libfsverity_merkle_tree_params tree_params = {
.version = 1,
.hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
.block_size = FSVERITY_HASH_PAGE_SIZE
};
FileReader reader;
if (!reader.OpenFromFd(fd)) {
return false;
}
if (!reader.GetFileSize(&tree_params.file_size)) {
return false;
}
if (libfsverity_compute_digest(&reader, FileReader::ReadFileCallback, &tree_params, digest)) {
LOG_ERROR("Compute digest from fd failed.");
return false;
}
return true;
}
bool FsverityUtilsHelper::GenerateFormattedDigest(const char *path, ByteBuffer &digestBuffer)
{
LOG_INFO("GenerateFormattedDigest called.");
if (path == nullptr) {
LOG_ERROR("Invalid parameter, path is null.");
return false;
}
struct libfsverity_digest *digest = nullptr;
if (!ComputeDigest(path, &digest)) {
return false;
}
if (digest == nullptr) {
LOG_ERROR("ComputeDigest succeeded but digest is null.");
return false;
}
uint32_t digestLen = sizeof(struct fsverity_formatted_digest) + digest->digest_size;
if (!digestBuffer.Resize(digestLen)) {
free(digest);
return false;
}
bool ret = FormatDigest(digest, digestBuffer.GetBuffer());
free(digest);
return ret;
}
bool FsverityUtilsHelper::GenerateFormattedDigestFromFd(int32_t fd, ByteBuffer &digestBuffer)
{
LOG_INFO("GenerateFormattedDigestFromFd called, fd=%{public}d", fd);
struct libfsverity_digest *digest = nullptr;
if (!ComputeDigestFromFd(fd, &digest)) {
return false;
}
if (digest == nullptr) {
LOG_ERROR("ComputeDigestFromFd succeeded but digest is null.");
return false;
}
uint32_t digestLen = sizeof(struct fsverity_formatted_digest) + digest->digest_size;
if (!digestBuffer.Resize(digestLen)) {
free(digest);
return false;
}
bool ret = FormatDigest(digest, digestBuffer.GetBuffer());
free(digest);
return ret;
}
}
}
}