blob: 96c78131c25dec0b0e64192e9862bea29cee9fd7 [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/resources_integrity.h"
#include <array>
#include "build/build_config.h"
#if defined(OS_WIN)
#include <windows.h>
#endif
#include "base/bind.h"
#include "base/files/file.h"
#include "base/memory/page_size.h"
#include "base/metrics/histogram_functions.h"
#include "base/path_service.h"
#include "base/ranges/algorithm.h"
#include "base/task/thread_pool.h"
#include "chrome/common/chrome_paths.h"
#include "crypto/secure_hash.h"
#if defined(OS_WIN)
#include "chrome/app/chrome_exe_main_win.h"
#else
#include "chrome/app/packed_resources_integrity.h" // nogncheck
#endif // defined(OS_WIN)
namespace {
bool CheckResourceIntegrityInternal(
const base::FilePath& path,
const base::span<const uint8_t, crypto::kSHA256Length> expected_signature) {
// Open the file for reading; allowing other consumers to also open it for
// reading and deleting. Do not allow others to write to it.
base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_EXCLUSIVE_WRITE |
base::File::FLAG_SHARE_DELETE);
if (!file.IsValid())
return false;
auto hash = crypto::SecureHash::Create(crypto::SecureHash::SHA256);
std::vector<char> buffer(base::GetPageSize());
int bytes_read = 0;
do {
bytes_read = file.ReadAtCurrentPos(buffer.data(), buffer.size());
if (bytes_read == -1)
return false;
hash->Update(buffer.data(), bytes_read);
} while (bytes_read > 0);
std::array<uint8_t, crypto::kSHA256Length> digest;
hash->Finish(digest.data(), digest.size());
return base::ranges::equal(digest, expected_signature);
}
void ReportPakIntegrity(const std::string& histogram_name, bool hash_matches) {
base::UmaHistogramBoolean(histogram_name, hash_matches);
}
} // namespace
void CheckResourceIntegrity(
const base::FilePath& path,
const base::span<const uint8_t, crypto::kSHA256Length> expected_signature,
scoped_refptr<base::SequencedTaskRunner> task_runner,
base::OnceCallback<void(bool)> callback) {
task_runner->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&CheckResourceIntegrityInternal, path, expected_signature),
std::move(callback));
}
void CheckPakFileIntegrity() {
base::FilePath resources_pack_path;
base::PathService::Get(chrome::FILE_RESOURCES_PACK, &resources_pack_path);
// On Windows, the hashes cannot be embedded in the chrome.dll target that
// this file is a part of, because it creates a cyclic build dependency
// with the Grit resource allow-list generation. Instead, the hashes are
// embedded in chrome.exe, which provides an exported function to
// access them.
#if defined(OS_WIN)
auto get_pak_file_hashes = reinterpret_cast<decltype(&GetPakFileHashes)>(
::GetProcAddress(::GetModuleHandle(nullptr), "GetPakFileHashes"));
if (!get_pak_file_hashes) {
// This is only exported by chrome.exe and unit_tests.exe, so in
// other tests, like browser_tests.exe, this export will not be available.
return;
}
const uint8_t *resources_hash_raw = nullptr, *chrome_100_hash_raw = nullptr,
*chrome_200_hash_raw = nullptr;
get_pak_file_hashes(&resources_hash_raw, &chrome_100_hash_raw,
&chrome_200_hash_raw);
base::span<const uint8_t, crypto::kSHA256Length> resources_hash(
resources_hash_raw, crypto::kSHA256Length);
base::span<const uint8_t, crypto::kSHA256Length> chrome_100_hash(
chrome_100_hash_raw, crypto::kSHA256Length);
base::span<const uint8_t, crypto::kSHA256Length> chrome_200_hash(
chrome_200_hash_raw, crypto::kSHA256Length);
#else
base::span<const uint8_t, crypto::kSHA256Length> resources_hash =
kSha256_resources_pak;
base::span<const uint8_t, crypto::kSHA256Length> chrome_100_hash =
kSha256_chrome_100_percent_pak;
base::span<const uint8_t, crypto::kSHA256Length> chrome_200_hash =
kSha256_chrome_200_percent_pak;
#endif // defined(OS_WIN)
scoped_refptr<base::SequencedTaskRunner> task_runner =
base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
CheckResourceIntegrity(resources_pack_path, resources_hash, task_runner,
base::BindOnce(&ReportPakIntegrity,
"SafeBrowsing.PakIntegrity.Resources"));
CheckResourceIntegrity(
resources_pack_path.DirName().AppendASCII("chrome_100_percent.pak"),
chrome_100_hash, task_runner,
base::BindOnce(&ReportPakIntegrity,
"SafeBrowsing.PakIntegrity.Chrome100"));
CheckResourceIntegrity(
resources_pack_path.DirName().AppendASCII("chrome_200_percent.pak"),
chrome_200_hash, task_runner,
base::BindOnce(&ReportPakIntegrity,
"SafeBrowsing.PakIntegrity.Chrome200"));
}