1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "extensions/browser/content_hash_reader.h"
6
7 #include "base/files/file_util.h"
8 #include "base/memory/ptr_util.h"
9 #include "base/metrics/histogram_macros.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/timer/elapsed_timer.h"
13 #include "base/values.h"
14 #include "crypto/sha2.h"
15 #include "extensions/browser/computed_hashes.h"
16 #include "extensions/browser/content_hash_tree.h"
17 #include "extensions/browser/content_verifier/content_hash.h"
18 #include "extensions/browser/verified_contents.h"
19
20 namespace extensions {
21
ContentHashReader(InitStatus status)22 ContentHashReader::ContentHashReader(InitStatus status) : status_(status) {}
23
24 ContentHashReader::~ContentHashReader() = default;
25
26 // static
Create(const base::FilePath & relative_path,const scoped_refptr<const ContentHash> & content_hash)27 std::unique_ptr<const ContentHashReader> ContentHashReader::Create(
28 const base::FilePath& relative_path,
29 const scoped_refptr<const ContentHash>& content_hash) {
30 base::ElapsedTimer timer;
31
32 ComputedHashes::Status hashes_status = content_hash->computed_hashes_status();
33 if (hashes_status == ComputedHashes::Status::UNKNOWN ||
34 hashes_status == ComputedHashes::Status::READ_FAILED) {
35 // Failure: no hashes at all.
36 return base::WrapUnique(new ContentHashReader(InitStatus::HASHES_MISSING));
37 }
38 if (hashes_status == ComputedHashes::Status::PARSE_FAILED) {
39 // Failure: hashes are unreadable.
40 return base::WrapUnique(new ContentHashReader(InitStatus::HASHES_DAMAGED));
41 }
42 DCHECK_EQ(ComputedHashes::Status::SUCCESS, hashes_status);
43
44 const ComputedHashes& computed_hashes = content_hash->computed_hashes();
45 base::Optional<std::string> root;
46
47 int block_size;
48 std::vector<std::string> block_hashes;
49
50 if (computed_hashes.GetHashes(relative_path, &block_size, &block_hashes) &&
51 block_size % crypto::kSHA256Length == 0) {
52 root =
53 ComputeTreeHashRoot(block_hashes, block_size / crypto::kSHA256Length);
54 }
55
56 ContentHash::TreeHashVerificationResult verification =
57 content_hash->VerifyTreeHashRoot(relative_path,
58 base::OptionalOrNullptr(root));
59
60 // Extensions sometimes request resources that do not have an entry in
61 // computed_hashes.json or verified_content.json. This can happen, for
62 // example, when an extension sends an XHR to a resource. This should not be
63 // considered as a failure.
64 if (verification != ContentHash::TreeHashVerificationResult::SUCCESS) {
65 base::FilePath full_path =
66 content_hash->extension_root().Append(relative_path);
67 // Making a request to a non-existent file or to a directory should not
68 // result in content verification failure.
69 // TODO(proberge): This logic could be simplified if |content_verify_job|
70 // kept track of whether the file being verified was successfully read.
71 // A content verification failure should be triggered if there is a mismatch
72 // between the file read state and the existence of verification hashes.
73 if (verification == ContentHash::TreeHashVerificationResult::NO_ENTRY &&
74 (!base::PathExists(full_path) || base::DirectoryExists(full_path))) {
75 // Expected failure: no hashes for non-existing resource.
76 return base::WrapUnique(new ContentHashReader(
77 InitStatus::NO_HASHES_FOR_NON_EXISTING_RESOURCE));
78 }
79
80 // Failure: no hashes when resource need them.
81 return base::WrapUnique(
82 new ContentHashReader(InitStatus::NO_HASHES_FOR_RESOURCE));
83 }
84
85 auto hash_reader =
86 base::WrapUnique(new ContentHashReader(InitStatus::SUCCESS));
87 hash_reader->block_size_ = block_size;
88 hash_reader->hashes_ = std::move(block_hashes);
89
90 UMA_HISTOGRAM_TIMES("ExtensionContentHashReader.InitLatency",
91 timer.Elapsed());
92 return hash_reader; // Success.
93 }
94
block_count() const95 int ContentHashReader::block_count() const {
96 return hashes_.size();
97 }
98
block_size() const99 int ContentHashReader::block_size() const {
100 return block_size_;
101 }
102
GetHashForBlock(int block_index,const std::string ** result) const103 bool ContentHashReader::GetHashForBlock(int block_index,
104 const std::string** result) const {
105 if (status_ != InitStatus::SUCCESS)
106 return false;
107 DCHECK(block_index >= 0);
108
109 if (static_cast<unsigned>(block_index) >= hashes_.size())
110 return false;
111 *result = &hashes_[block_index];
112
113 return true;
114 }
115
116 } // namespace extensions
117