blob: 3fcaf56c714011045eaeb078afc6878b862b666e [file]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/common/safe_browsing/binary_feature_extractor.h"
#include "build/build_config.h"
#include <memory>
#include <utility>
#include "base/containers/heap_array.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/memory_mapped_file.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_view_util.h"
#include "components/safe_browsing/core/common/proto/csd.pb.h"
#include "crypto/hash.h"
namespace safe_browsing {
BinaryFeatureExtractor::BinaryFeatureExtractor() = default;
BinaryFeatureExtractor::~BinaryFeatureExtractor() = default;
bool BinaryFeatureExtractor::ExtractImageFeatures(
const base::FilePath& file_path,
ExtractHeadersOption options,
ClientDownloadRequest_ImageHeaders* image_headers,
google::protobuf::RepeatedPtrField<std::string>* signed_data) {
base::FilePath temp_dir;
if (!base::GetTempDir(&temp_dir)) {
return false;
}
base::FilePath temp_path;
base::File temp_file = base::CreateAndOpenTemporaryFileInDir(
temp_dir, &temp_path,
base::File::FLAG_WIN_TEMPORARY | base::File::FLAG_DELETE_ON_CLOSE);
if (!temp_file.IsValid()) {
return false;
}
#if !BUILDFLAG(IS_WIN)
// CreateAndOpenTemporaryFileInDir delegates deletion to the caller on POSIX.
// We unlink the file immediately after creation to ensure it is deleted even
// if the process crashes, while keeping the file descriptor open for use.
base::DeleteFile(temp_path);
#endif
{
// FLAG_WIN_SHARE_DELETE allows the download's rename to its final name to
// proceed on Windows while this read handle is still open, since the
// rename can otherwise take several retries to succeed on large files
// (https://crbug.com/545877431).
base::File source_file(file_path, base::File::FLAG_OPEN |
base::File::FLAG_READ |
base::File::FLAG_WIN_SHARE_DELETE);
if (!source_file.IsValid()) {
return false;
}
if (!base::CopyFileContents(source_file, temp_file)) {
base::DeleteFile(temp_path);
return false;
}
}
base::MemoryMappedFile mapped_file;
if (!mapped_file.Initialize(std::move(temp_file))) {
return false;
}
return ExtractImageFeaturesFromData(mapped_file.bytes(), options,
image_headers, signed_data);
}
bool BinaryFeatureExtractor::ExtractImageFeaturesFromFile(
base::File file,
ExtractHeadersOption options,
ClientDownloadRequest_ImageHeaders* image_headers,
google::protobuf::RepeatedPtrField<std::string>* signed_data) {
base::MemoryMappedFile mapped_file;
if (!mapped_file.Initialize(std::move(file)))
return false;
return ExtractImageFeaturesFromData(mapped_file.bytes(), options,
image_headers, signed_data);
}
void BinaryFeatureExtractor::ExtractDigest(
const base::FilePath& file_path,
ClientDownloadRequest_Digests* digests) {
// See the comment in ExtractImageFeatures() about FLAG_WIN_SHARE_DELETE.
base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_WIN_SHARE_DELETE);
if (file.IsValid()) {
auto buf = base::HeapArray<uint8_t>::Uninit(1 << 12);
crypto::hash::Hasher hasher(crypto::hash::HashKind::kSha256);
std::optional<size_t> result;
while (true) {
result = file.ReadAtCurrentPos(buf);
if (!result.has_value() || result.value() == 0) {
break;
}
hasher.Update(buf.first(*result));
}
// The loop was broken out of because of EOF, not an error.
if (result.has_value() && result.value() == 0) {
std::array<uint8_t, crypto::hash::kSha256Size> hash;
hasher.Finish(hash);
digests->set_sha256(base::as_string_view(hash));
}
}
}
} // namespace safe_browsing