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

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

Issue 2874503002: Refactor CRX verification in preparation to support CRX₃ files. (Closed)
Patch Set: Base CL 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;
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 size_t length,
32 base::File* file,
33 crypto::SecureHash* hash) {
34 // We assume a char is 8 bits long.
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.
41 uint32_t ReadAndHashLittleEndianUInt32(base::File* file,
42 crypto::SecureHash* hash) {
43 uint8_t buffer[4];
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);
57 }
58
59 void CrxVerifier::RequireFileHash(const std::vector<uint8_t>& expected_hash) {
60 expected_hash_ = expected_hash;
61 }
62
63 void CrxVerifier::GetPublicKey(std::string* public_key) {
64 public_key_ = public_key;
65 }
66
67 void CrxVerifier::GetCrxId(std::string* crx_id) {
68 crx_id_ = crx_id;
69 }
70
71 void Crx3Verifier::RequirePublisherProof() {
72 require_publisher_proof_ = true;
73 }
74
75 Crx3Verifier::Crx3Verifier() {
76 allow_crx2_ = false;
77 }
78
79 Crx3Verifier::~Crx3Verifier() {}
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];
92 int read = file.ReadAtCurrentPos(buffer, kCrx2FileHeaderMagicSize);
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());
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];
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;
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 uint32_t key_size = ReadAndHashLittleEndianUInt32(file, hash);
135 if (key_size > kMaxPublicKeySize)
136 return Result::ERROR_HEADER_INVALID;
137 uint32_t sig_size = ReadAndHashLittleEndianUInt32(file, hash);
138 if (sig_size > kMaxSignatureSize)
139 return Result::ERROR_HEADER_INVALID;
140 std::vector<uint8_t> key(key_size);
141 if (ReadAndHashBuffer(key.data(), key_size, file, hash) !=
142 static_cast<int>(key_size))
143 return Result::ERROR_HEADER_INVALID;
144 for (const auto& expected_hash : key_hashes_) {
145 // In practice we expect zero or one key_hashes_ for CRX2 files.
146 std::vector<uint8_t> hash(crypto::kSHA256Length);
147 std::unique_ptr<crypto::SecureHash> sha256(
148 crypto::SecureHash::Create(crypto::SecureHash::SHA256));
149 sha256->Update(key.data(), key.size());
150 sha256->Finish(hash.data(), hash.size());
151 if (hash != expected_hash)
152 return Result::ERROR_REQUIRED_PROOF_MISSING;
153 }
154
155 std::vector<uint8_t> sig(sig_size);
156 if (ReadAndHashBuffer(sig.data(), sig_size, file, hash) !=
157 static_cast<int>(sig_size))
158 return Result::ERROR_HEADER_INVALID;
159 crypto::SignatureVerifier verifier;
160 if (!verifier.VerifyInit(crypto::SignatureVerifier::RSA_PKCS1_SHA1,
161 sig.data(), sig.size(), key.data(), key.size())) {
162 return Result::ERROR_SIGNATURE_INITIALIZATION_FAILED;
163 }
164
165 // Read the rest of the file.
166 uint8_t buffer[1 << 12] = {};
167 size_t len;
168 while ((len = ReadAndHashBuffer(buffer, arraysize(buffer), file, hash)) > 0)
169 verifier.VerifyUpdate(buffer, len);
170 if (!verifier.VerifyFinal())
171 return Result::ERROR_SIGNATURE_VERIFICATION_FAILED;
172
173 std::string public_key_bytes =
174 std::string(reinterpret_cast<char*>(&key.front()), key.size());
175 if (public_key_)
176 base::Base64Encode(public_key_bytes, public_key_);
177 if (crx_id_)
178 *crx_id_ = id_util::GenerateId(public_key_bytes);
179 return Result::OK_FULL;
180 }
181
182 } // namespace crx_file
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698