Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(148)

Side by Side Diff: components/crx_file/crx_verifier.cc

Issue 2874503002: Refactor CRX verification in preparation to support CRX₃ files. (Closed)
Patch Set: No subclass Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 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 "components/crx_file/crx_verifier.h"
6
7 #include <cstring>
8 #include <memory>
9
10 #include "base/base64.h"
11 #include "base/files/file.h"
12 #include "base/files/file_path.h"
13 #include "components/crx_file/crx2_file.h"
14 #include "components/crx_file/id_util.h"
15 #include "crypto/secure_hash.h"
16 #include "crypto/secure_util.h"
17 #include "crypto/sha2.h"
18 #include "crypto/signature_verifier.h"
19
20 namespace crx_file {
21
22 namespace {
23
24 // The maximum size the crx2 parser will tolerate for a public key.
25 static const uint32_t kMaxPublicKeySize = 1 << 16;
Sorin Jianu 2017/05/15 19:49:52 static not needed here, also maybe use constexpr.
waffles 2017/05/16 00:29:02 Done.
26
27 // The maximum size the crx2 parser will tolerate for a signature.
28 static const uint32_t kMaxSignatureSize = 1 << 16;
29
30 int ReadAndHashBuffer(uint8_t* buffer,
31 int length,
32 base::File* file,
33 crypto::SecureHash* hash) {
34 // We assume a char is 8 bits long.
Sorin Jianu 2017/05/15 19:49:52 we can compile time assert on the size of the char
waffles 2017/05/16 00:29:02 Done.
35 int read = file->ReadAtCurrentPos(reinterpret_cast<char*>(buffer), length);
36 hash->Update(buffer, read);
37 return read;
38 }
39
40 // Returns UINT32_MAX in the case of an unexpected EOF or read error.
Sorin Jianu 2017/05/15 19:49:52 Can the hash on an int32 it be UINT32_MAX?
waffles 2017/05/16 00:29:02 I don't understand what you mean. Can you rephrase
41 uint32_t ReadAndHashLittleEndianUInt32(base::File* file,
42 crypto::SecureHash* hash) {
43 uint8_t buffer[4];
Sorin Jianu 2017/05/15 19:49:52 4 as a magic number used a several places, maybe u
Sorin Jianu 2017/05/15 19:49:52 not initialized
waffles 2017/05/16 00:29:02 Do you feel strongly about this? The return expres
waffles 2017/05/16 00:29:02 Done.
44 if (ReadAndHashBuffer(buffer, 4, file, hash) != 4)
45 return UINT32_MAX;
46 return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0];
47 }
48
49 } // namespace
50
51 CrxVerifier::CrxVerifier() {}
52
53 CrxVerifier::~CrxVerifier() {}
54
55 void CrxVerifier::RequireKeyProof(const std::vector<uint8_t>& key_hash) {
56 key_hashes_.push_back(key_hash);
Sorin Jianu 2017/05/15 19:49:52 We could handle the case where the key_hash lenght
waffles 2017/05/16 00:29:01 Obsolete.
57 }
58
59 void CrxVerifier::RequireFileHash(const std::vector<uint8_t>& expected_hash) {
60 DCHECK(!expected_hash.empty());
61 expected_hash_ = expected_hash;
62 }
63
64 void CrxVerifier::GetPublicKey(std::string* public_key) {
65 public_key_ = public_key;
66 }
67
68 void CrxVerifier::GetCrxId(std::string* crx_id) {
69 crx_id_ = crx_id;
70 }
71
72 void CrxVerifier::RequireCrx3() {
73 allow_crx2_ = false;
74 }
75
76 void CrxVerifier::RequirePublisherProof() {
77 DCHECK(!allow_crx2_);
78 require_publisher_proof_ = true;
79 }
80
81 CrxVerifier::Result CrxVerifier::Verify(const base::FilePath& crx_path) const {
82 base::File file(crx_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
83 if (!file.IsValid())
84 return Result::ERROR_FILE_NOT_READABLE;
85
86 std::unique_ptr<crypto::SecureHash> file_hash =
87 crypto::SecureHash::Create(crypto::SecureHash::SHA256);
88
89 // Magic number.
90 bool diff = false;
91 char buffer[kCrx2FileHeaderMagicSize];
Sorin Jianu 2017/05/15 19:49:52 init?
waffles 2017/05/16 00:29:02 Done.
92 int read = file.ReadAtCurrentPos(buffer, kCrx2FileHeaderMagicSize);
Sorin Jianu 2017/05/15 19:49:52 Maybe rename read to num_bytes or something more s
Sorin Jianu 2017/05/15 19:49:52 const?
waffles 2017/05/16 00:29:02 Acknowledged.
waffles 2017/05/16 00:29:02 Inlined it.
93 if (read != kCrx2FileHeaderMagicSize)
94 return Result::ERROR_HEADER_INVALID;
95 if (!strncmp(buffer, kCrxDiffFileHeaderMagic, kCrx2FileHeaderMagicSize))
96 diff = true;
97 else if (strncmp(buffer, kCrx2FileHeaderMagic, kCrx2FileHeaderMagicSize))
98 return Result::ERROR_HEADER_INVALID;
99 file_hash->Update(buffer, sizeof(buffer));
100
101 // Version number.
102 uint32_t version = ReadAndHashLittleEndianUInt32(&file, file_hash.get());
Sorin Jianu 2017/05/15 19:49:52 const?
waffles 2017/05/16 00:29:02 Done.
103 Result result;
104 if (allow_crx2_ && (version == 2 || (diff && version == 0)))
105 result = VerifyCrx2(&file, file_hash.get());
106 else if (version == 3)
107 result = VerifyCrx3(&file, file_hash.get());
108 else
109 return Result::ERROR_HEADER_INVALID;
110 if (result != Result::OK_FULL)
111 return result;
112
113 // Finalize file hash.
114 uint8_t final_hash[crypto::kSHA256Length];
Sorin Jianu 2017/05/15 19:49:52 init
waffles 2017/05/16 00:29:02 Done.
115 file_hash->Finish(final_hash, sizeof(final_hash));
116 if (!expected_hash_.empty()) {
117 if (expected_hash_.size() != crypto::kSHA256Length)
118 return Result::ERROR_EXPECTED_HASH_INVALID;
119 if (!crypto::SecureMemEqual(final_hash, expected_hash_.data(),
120 crypto::kSHA256Length))
121 return Result::ERROR_FILE_HASH_FAILED;
Sorin Jianu 2017/05/15 19:49:52 It would be nice if the entire if can be rewritten
waffles 2017/05/16 00:29:02 Acknowledged. Unfortunately we have to tolerate th
122 }
123 return diff ? Result::OK_DELTA : Result::OK_FULL;
124 }
125
126 CrxVerifier::Result CrxVerifier::VerifyCrx3(base::File* file,
127 crypto::SecureHash* hash) const {
128 // Crx3 files are not yet supported - treat this as a malformed header.
129 return Result::ERROR_HEADER_INVALID;
130 }
131
132 CrxVerifier::Result CrxVerifier::VerifyCrx2(base::File* file,
133 crypto::SecureHash* hash) const {
134 DCHECK(!require_publisher_proof_);
135 uint32_t key_size = ReadAndHashLittleEndianUInt32(file, hash);
Sorin Jianu 2017/05/15 19:49:52 some const here and below.
waffles 2017/05/16 00:29:02 Done.
136 if (key_size > kMaxPublicKeySize)
137 return Result::ERROR_HEADER_INVALID;
138 uint32_t sig_size = ReadAndHashLittleEndianUInt32(file, hash);
139 if (sig_size > kMaxSignatureSize)
140 return Result::ERROR_HEADER_INVALID;
141 std::vector<uint8_t> key(key_size);
142 if (ReadAndHashBuffer(key.data(), key_size, file, hash) !=
143 static_cast<int>(key_size))
144 return Result::ERROR_HEADER_INVALID;
145 for (const auto& expected_hash : key_hashes_) {
146 // In practice we expect zero or one key_hashes_ for CRX2 files.
147 std::vector<uint8_t> hash(crypto::kSHA256Length);
148 std::unique_ptr<crypto::SecureHash> sha256(
Sorin Jianu 2017/05/15 19:49:52 prefer = syntax
waffles 2017/05/16 00:29:02 Done.
149 crypto::SecureHash::Create(crypto::SecureHash::SHA256));
150 sha256->Update(key.data(), key.size());
151 sha256->Finish(hash.data(), hash.size());
152 if (hash != expected_hash)
153 return Result::ERROR_REQUIRED_PROOF_MISSING;
154 }
155
156 std::vector<uint8_t> sig(sig_size);
157 if (ReadAndHashBuffer(sig.data(), sig_size, file, hash) !=
158 static_cast<int>(sig_size))
159 return Result::ERROR_HEADER_INVALID;
160 crypto::SignatureVerifier verifier;
161 if (!verifier.VerifyInit(crypto::SignatureVerifier::RSA_PKCS1_SHA1,
162 sig.data(), sig.size(), key.data(), key.size())) {
163 return Result::ERROR_SIGNATURE_INITIALIZATION_FAILED;
164 }
165
166 // Read the rest of the file.
167 uint8_t buffer[1 << 12] = {};
168 size_t len;
Sorin Jianu 2017/05/15 19:49:52 init
waffles 2017/05/16 00:29:02 Done.
169 while ((len = ReadAndHashBuffer(buffer, arraysize(buffer), file, hash)) > 0)
170 verifier.VerifyUpdate(buffer, len);
171 if (!verifier.VerifyFinal())
172 return Result::ERROR_SIGNATURE_VERIFICATION_FAILED;
173
174 std::string public_key_bytes =
Sorin Jianu 2017/05/15 19:49:52 can you use a ctor or initializer instead of initi
Sorin Jianu 2017/05/15 19:49:52 const?
waffles 2017/05/16 00:29:02 Done.
waffles 2017/05/16 00:29:02 Done.
175 std::string(reinterpret_cast<char*>(&key.front()), key.size());
176 if (public_key_)
177 base::Base64Encode(public_key_bytes, public_key_);
178 if (crx_id_)
179 *crx_id_ = id_util::GenerateId(public_key_bytes);
180 return Result::OK_FULL;
181 }
182
183 } // namespace crx_file
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698