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

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

Issue 2888853003: Expand CRX verifier to verify CRX₃ files. (Closed)
Patch Set: Fix const name 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
1 // Copyright 2017 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/crx_file/crx_verifier.h" 5 #include "components/crx_file/crx_verifier.h"
6 6
7 #include <cstring> 7 #include <cstring>
8 #include <iterator>
8 #include <memory> 9 #include <memory>
10 #include <set>
11 #include <utility>
9 12
10 #include "base/base64.h" 13 #include "base/base64.h"
14 #include "base/bind.h"
15 #include "base/callback.h"
11 #include "base/files/file.h" 16 #include "base/files/file.h"
12 #include "base/files/file_path.h" 17 #include "base/files/file_path.h"
13 #include "base/memory/ptr_util.h" 18 #include "base/memory/ptr_util.h"
19 #include "base/strings/string_number_conversions.h"
14 #include "components/crx_file/crx2_file.h" 20 #include "components/crx_file/crx2_file.h"
21 #include "components/crx_file/crx3.pb.h"
15 #include "components/crx_file/id_util.h" 22 #include "components/crx_file/id_util.h"
16 #include "crypto/secure_hash.h" 23 #include "crypto/secure_hash.h"
17 #include "crypto/secure_util.h" 24 #include "crypto/secure_util.h"
18 #include "crypto/sha2.h" 25 #include "crypto/sha2.h"
19 #include "crypto/signature_verifier.h" 26 #include "crypto/signature_verifier.h"
20 27
21 namespace crx_file { 28 namespace crx_file {
22 29
23 namespace { 30 namespace {
24 31
25 // The maximum size the crx2 parser will tolerate for a public key. 32 // The maximum size the Crx2 parser will tolerate for a public key.
26 constexpr uint32_t kMaxPublicKeySize = 1 << 16; 33 constexpr uint32_t kMaxPublicKeySize = 1 << 16;
27 34
28 // The maximum size the crx2 parser will tolerate for a signature. 35 // The maximum size the Crx2 parser will tolerate for a signature.
29 constexpr uint32_t kMaxSignatureSize = 1 << 16; 36 constexpr uint32_t kMaxSignatureSize = 1 << 16;
30 37
38 // The maximum size the Crx3 parser will tolerate for a header.
39 constexpr uint32_t kMaxHeaderSize = 1 << 18;
40
41 // The context for Crx3 signing, encoded in UTF8.
42 constexpr unsigned char kSignatureContext[] = u8"CRX3 SignedData";
43
44 // The SHA256 hash of the "ecdsa_2017_public" Crx3 key.
45 constexpr uint8_t kPublisherKeyHash[] = {
46 0x61, 0xf7, 0xf2, 0xa6, 0xbf, 0xcf, 0x74, 0xcd, 0x0b, 0xc1, 0xfe,
47 0x24, 0x97, 0xcc, 0x9b, 0x04, 0x25, 0x4c, 0x65, 0x8f, 0x79, 0xf2,
48 0x14, 0x53, 0x92, 0x86, 0x7e, 0xa8, 0x36, 0x63, 0x67, 0xcf};
49
50 using VerifierCollection =
51 std::vector<std::unique_ptr<crypto::SignatureVerifier>>;
52 using RepeatedProof = google::protobuf::RepeatedPtrField<AsymmetricKeyProof>;
53
31 int ReadAndHashBuffer(uint8_t* buffer, 54 int ReadAndHashBuffer(uint8_t* buffer,
32 int length, 55 int length,
33 base::File* file, 56 base::File* file,
34 crypto::SecureHash* hash) { 57 crypto::SecureHash* hash) {
35 static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size."); 58 static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size.");
36 int read = file->ReadAtCurrentPos(reinterpret_cast<char*>(buffer), length); 59 int read = file->ReadAtCurrentPos(reinterpret_cast<char*>(buffer), length);
37 hash->Update(buffer, read); 60 hash->Update(buffer, read);
38 return read; 61 return read;
39 } 62 }
40 63
41 // Returns UINT32_MAX in the case of an unexpected EOF or read error, else 64 // Returns UINT32_MAX in the case of an unexpected EOF or read error, else
42 // returns the read uint32. 65 // returns the read uint32.
43 uint32_t ReadAndHashLittleEndianUInt32(base::File* file, 66 uint32_t ReadAndHashLittleEndianUInt32(base::File* file,
44 crypto::SecureHash* hash) { 67 crypto::SecureHash* hash) {
45 uint8_t buffer[4] = {}; 68 uint8_t buffer[4] = {};
46 if (ReadAndHashBuffer(buffer, 4, file, hash) != 4) 69 if (ReadAndHashBuffer(buffer, 4, file, hash) != 4)
47 return UINT32_MAX; 70 return UINT32_MAX;
48 return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0]; 71 return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0];
49 } 72 }
50 73
74 bool ReadHashAndVerifyArchive(base::File* file,
Devlin 2017/05/23 20:32:40 add a function comment
waffles 2017/05/23 23:05:13 Done.
75 crypto::SecureHash* hash,
76 const VerifierCollection& verifiers) {
77 uint8_t buffer[1 << 12] = {};
Devlin 2017/05/23 20:32:40 Comment why 1 << 12?
waffles 2017/05/23 23:05:13 I don't know what to write in the comment. This co
Devlin 2017/05/24 17:10:54 Apparently this was added 8 years ago here: https:
78 size_t len = 0;
79 while ((len = ReadAndHashBuffer(buffer, arraysize(buffer), file, hash)) > 0) {
80 for (auto& verifier : verifiers)
81 verifier->VerifyUpdate(buffer, len);
82 }
83 for (auto& verifier : verifiers) {
84 if (!verifier->VerifyFinal())
85 return false;
86 }
87 return true;
88 }
89
90 // The remaining contents of a Crx3 file are [header-size][header][archive].
91 // [header] is an encoded protocol buffer and contains both a signed and
92 // unsigned section. The unsigned section contains a set of key/signature pairs,
93 // and the signed section is the encoding of another protocol buffer. All
94 // signatures cover [prefix][signed-header-size][signed-header][archive].
51 VerifierResult VerifyCrx3( 95 VerifierResult VerifyCrx3(
52 base::File* file, 96 base::File* file,
53 crypto::SecureHash* hash, 97 crypto::SecureHash* hash,
54 const std::vector<std::vector<uint8_t>>& required_key_hashes, 98 const std::vector<std::vector<uint8_t>>& required_key_hashes,
55 std::string* public_key, 99 std::string* public_key,
56 std::string* crx_id) { 100 std::string* crx_id,
57 // Crx3 files are not yet supported - treat this as a malformed header. 101 bool require_publisher_key) {
58 return VerifierResult::ERROR_HEADER_INVALID; 102 // Parse [header-size] and [header].
103 const uint32_t header_size = ReadAndHashLittleEndianUInt32(file, hash);
104 if (header_size > kMaxHeaderSize)
105 return VerifierResult::ERROR_HEADER_INVALID;
106 std::vector<uint8_t> header_bytes(header_size);
107 if (ReadAndHashBuffer(header_bytes.data(), header_size, file, hash) !=
108 static_cast<int>(header_size))
Devlin 2017/05/23 20:32:40 any risk of overflow?
waffles 2017/05/23 23:05:13 I don't think we can compile Chrome on any machine
Devlin 2017/05/24 17:10:54 Ah, missed the check on line 105. I wonder if thi
waffles 2017/05/24 17:53:07 I added a comment. (I hate introducing names... >_
109 return VerifierResult::ERROR_HEADER_INVALID;
110 CrxFileHeader header;
111 if (!header.ParseFromArray(header_bytes.data(), header_size))
112 return VerifierResult::ERROR_HEADER_INVALID;
113
114 // Parse [signed-header].
115 const std::string& signed_header_data_str = header.signed_header_data();
116 SignedData signed_header_data;
117 if (!signed_header_data.ParseFromString(signed_header_data_str))
118 return VerifierResult::ERROR_HEADER_INVALID;
119 const std::string& crx_id_encoded = signed_header_data.crx_id();
120 const std::string declared_crx_id = id_util::GenerateIdFromHex(
121 base::HexEncode(crx_id_encoded.data(), crx_id_encoded.size()));
122
123 // Create a little-endian representation of [signed-header-size].
124 const int signed_header_size = signed_header_data_str.size();
125 const uint8_t header_size_octets[] = {
126 signed_header_size, signed_header_size >> 8, signed_header_size >> 16,
127 signed_header_size >> 24};
128
129 // Create a set of all required key hashes.
130 std::set<std::vector<uint8_t>> required_key_set(required_key_hashes.begin(),
131 required_key_hashes.end());
132 if (require_publisher_key) {
133 const std::vector<uint8_t> publisher_key(std::begin(kPublisherKeyHash),
134 std::end(kPublisherKeyHash));
135 required_key_set.insert(publisher_key);
Devlin 2017/05/23 20:32:40 use emplace? required_key_set.emplace(std::begin(k
waffles 2017/05/23 23:05:13 Thanks, done.
136 }
137 std::string public_key_bytes;
138 VerifierCollection verifiers;
139 const std::vector<std::pair<std::function<const RepeatedProof&()>,
Devlin 2017/05/23 20:32:40 std::function is banned in Chromium. Can you use
waffles 2017/05/23 23:05:13 Hacked on this for a while - does this look better
Devlin 2017/05/24 17:10:54 This is fine. I think base::Callback is mildly mo
waffles 2017/05/24 17:53:07 Great. Yeah, I started to use base::Callback, but
140 crypto::SignatureVerifier::SignatureAlgorithm>>
141 proof_types = {std::make_pair(
142 [&header]() -> const RepeatedProof& {
143 return header.sha256_with_rsa();
144 },
145 crypto::SignatureVerifier::RSA_PKCS1_SHA256),
146 std::make_pair(
147 [&header]() -> const RepeatedProof& {
148 return header.sha256_with_ecdsa();
149 },
150 crypto::SignatureVerifier::ECDSA_SHA256)};
151
152 // Initialize all verifiers and update them with
153 // [prefix][signed-header-size][signed-header].
154 // Clear any elements of required_key_set that are encountered, and watch for
155 // the developer key.
156 for (const auto& proof_type : proof_types) {
157 for (const auto& proof : proof_type.first()) {
158 const std::string& key = proof.public_key();
159 const std::string& sig = proof.signature();
160 if (id_util::GenerateId(key) == declared_crx_id)
161 public_key_bytes = key;
162 std::vector<uint8_t> key_hash(crypto::kSHA256Length);
163 crypto::SHA256HashString(key, key_hash.data(), key_hash.size());
164 required_key_set.erase(key_hash);
165 std::unique_ptr<crypto::SignatureVerifier> v =
Devlin 2017/05/23 20:32:40 optional: you could use auto here since base::Make
waffles 2017/05/23 23:05:13 Done.
166 base::MakeUnique<crypto::SignatureVerifier>();
167 static_assert(sizeof(unsigned char) == sizeof(uint8_t),
168 "Unsupported char size.");
169 if (!v->VerifyInit(
170 proof_type.second, reinterpret_cast<const uint8_t*>(sig.data()),
171 sig.size(), reinterpret_cast<const uint8_t*>(key.data()),
172 key.size()))
173 return VerifierResult::ERROR_SIGNATURE_INITIALIZATION_FAILED;
174 v->VerifyUpdate(kSignatureContext, arraysize(kSignatureContext));
175 v->VerifyUpdate(header_size_octets, arraysize(header_size_octets));
176 v->VerifyUpdate(
177 reinterpret_cast<const uint8_t*>(signed_header_data_str.data()),
178 signed_header_data_str.size());
179 verifiers.push_back(std::move(v));
Devlin 2017/05/23 20:32:40 can we reserve() verifiers?
waffles 2017/05/23 23:05:13 Done.
180 }
181 }
182 if (public_key_bytes.empty() || !required_key_set.empty())
183 return VerifierResult::ERROR_REQUIRED_PROOF_MISSING;
184
185 // Update and finalize the verifiers with [archive].
186 if (!ReadHashAndVerifyArchive(file, hash, verifiers))
187 return VerifierResult::ERROR_SIGNATURE_VERIFICATION_FAILED;
188
189 base::Base64Encode(public_key_bytes, public_key);
190 *crx_id = declared_crx_id;
191 return VerifierResult::OK_FULL;
59 } 192 }
60 193
61 VerifierResult VerifyCrx2( 194 VerifierResult VerifyCrx2(
62 base::File* file, 195 base::File* file,
63 crypto::SecureHash* hash, 196 crypto::SecureHash* hash,
64 const std::vector<std::vector<uint8_t>>& required_key_hashes, 197 const std::vector<std::vector<uint8_t>>& required_key_hashes,
65 std::string* public_key, 198 std::string* public_key,
66 std::string* crx_id) { 199 std::string* crx_id) {
67 const uint32_t key_size = ReadAndHashLittleEndianUInt32(file, hash); 200 const uint32_t key_size = ReadAndHashLittleEndianUInt32(file, hash);
68 if (key_size > kMaxPublicKeySize) 201 if (key_size > kMaxPublicKeySize)
69 return VerifierResult::ERROR_HEADER_INVALID; 202 return VerifierResult::ERROR_HEADER_INVALID;
70 const uint32_t sig_size = ReadAndHashLittleEndianUInt32(file, hash); 203 const uint32_t sig_size = ReadAndHashLittleEndianUInt32(file, hash);
71 if (sig_size > kMaxSignatureSize) 204 if (sig_size > kMaxSignatureSize)
72 return VerifierResult::ERROR_HEADER_INVALID; 205 return VerifierResult::ERROR_HEADER_INVALID;
73 std::vector<uint8_t> key(key_size); 206 std::vector<uint8_t> key(key_size);
74 if (ReadAndHashBuffer(key.data(), key_size, file, hash) != 207 if (ReadAndHashBuffer(key.data(), key_size, file, hash) !=
75 static_cast<int>(key_size)) 208 static_cast<int>(key_size))
76 return VerifierResult::ERROR_HEADER_INVALID; 209 return VerifierResult::ERROR_HEADER_INVALID;
77 for (const auto& expected_hash : required_key_hashes) { 210 for (const auto& expected_hash : required_key_hashes) {
78 // In practice we expect zero or one key_hashes_ for CRX2 files. 211 // In practice we expect zero or one key_hashes_ for Crx2 files.
79 std::vector<uint8_t> hash(crypto::kSHA256Length); 212 std::vector<uint8_t> hash(crypto::kSHA256Length);
80 std::unique_ptr<crypto::SecureHash> sha256 = 213 std::unique_ptr<crypto::SecureHash> sha256 =
81 crypto::SecureHash::Create(crypto::SecureHash::SHA256); 214 crypto::SecureHash::Create(crypto::SecureHash::SHA256);
82 sha256->Update(key.data(), key.size()); 215 sha256->Update(key.data(), key.size());
83 sha256->Finish(hash.data(), hash.size()); 216 sha256->Finish(hash.data(), hash.size());
84 if (hash != expected_hash) 217 if (hash != expected_hash)
85 return VerifierResult::ERROR_REQUIRED_PROOF_MISSING; 218 return VerifierResult::ERROR_REQUIRED_PROOF_MISSING;
86 } 219 }
87 220
88 std::vector<uint8_t> sig(sig_size); 221 std::vector<uint8_t> sig(sig_size);
89 if (ReadAndHashBuffer(sig.data(), sig_size, file, hash) != 222 if (ReadAndHashBuffer(sig.data(), sig_size, file, hash) !=
90 static_cast<int>(sig_size)) 223 static_cast<int>(sig_size))
91 return VerifierResult::ERROR_HEADER_INVALID; 224 return VerifierResult::ERROR_HEADER_INVALID;
92 crypto::SignatureVerifier verifier; 225 std::vector<std::unique_ptr<crypto::SignatureVerifier>> verifiers;
93 if (!verifier.VerifyInit(crypto::SignatureVerifier::RSA_PKCS1_SHA1, 226 verifiers.push_back(base::MakeUnique<crypto::SignatureVerifier>());
94 sig.data(), sig.size(), key.data(), key.size())) { 227 if (!verifiers[0]->VerifyInit(crypto::SignatureVerifier::RSA_PKCS1_SHA1,
228 sig.data(), sig.size(), key.data(),
229 key.size())) {
95 return VerifierResult::ERROR_SIGNATURE_INITIALIZATION_FAILED; 230 return VerifierResult::ERROR_SIGNATURE_INITIALIZATION_FAILED;
96 } 231 }
97 232
98 // Read the rest of the file. 233 if (!ReadHashAndVerifyArchive(file, hash, verifiers))
99 uint8_t buffer[1 << 12] = {};
100 size_t len = 0;
101 while ((len = ReadAndHashBuffer(buffer, arraysize(buffer), file, hash)) > 0)
102 verifier.VerifyUpdate(buffer, len);
103 if (!verifier.VerifyFinal())
104 return VerifierResult::ERROR_SIGNATURE_VERIFICATION_FAILED; 234 return VerifierResult::ERROR_SIGNATURE_VERIFICATION_FAILED;
105 235
106 const std::string public_key_bytes(key.begin(), key.end()); 236 const std::string public_key_bytes(key.begin(), key.end());
107 if (public_key) 237 base::Base64Encode(public_key_bytes, public_key);
108 base::Base64Encode(public_key_bytes, public_key); 238 *crx_id = id_util::GenerateId(public_key_bytes);
109 if (crx_id)
110 *crx_id = id_util::GenerateId(public_key_bytes);
111 return VerifierResult::OK_FULL; 239 return VerifierResult::OK_FULL;
112 } 240 }
113 241
114 } // namespace 242 } // namespace
115 243
116 VerifierResult Verify( 244 VerifierResult Verify(
117 const base::FilePath& crx_path, 245 const base::FilePath& crx_path,
118 const VerifierFormat& format, 246 const VerifierFormat& format,
119 const std::vector<std::vector<uint8_t>>& required_key_hashes, 247 const std::vector<std::vector<uint8_t>>& required_key_hashes,
120 const std::vector<uint8_t>& required_file_hash, 248 const std::vector<uint8_t>& required_file_hash,
121 std::string* public_key, 249 std::string* public_key,
122 std::string* crx_id) { 250 std::string* crx_id) {
251 std::string public_key_local;
252 std::string crx_id_local;
123 base::File file(crx_path, base::File::FLAG_OPEN | base::File::FLAG_READ); 253 base::File file(crx_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
124 if (!file.IsValid()) 254 if (!file.IsValid())
125 return VerifierResult::ERROR_FILE_NOT_READABLE; 255 return VerifierResult::ERROR_FILE_NOT_READABLE;
126 256
127 std::unique_ptr<crypto::SecureHash> file_hash = 257 std::unique_ptr<crypto::SecureHash> file_hash =
128 crypto::SecureHash::Create(crypto::SecureHash::SHA256); 258 crypto::SecureHash::Create(crypto::SecureHash::SHA256);
129 259
130 // Magic number. 260 // Magic number.
131 bool diff = false; 261 bool diff = false;
132 char buffer[kCrx2FileHeaderMagicSize] = {}; 262 char buffer[kCrx2FileHeaderMagicSize] = {};
133 if (file.ReadAtCurrentPos(buffer, kCrx2FileHeaderMagicSize) != 263 if (file.ReadAtCurrentPos(buffer, kCrx2FileHeaderMagicSize) !=
134 kCrx2FileHeaderMagicSize) 264 kCrx2FileHeaderMagicSize)
135 return VerifierResult::ERROR_HEADER_INVALID; 265 return VerifierResult::ERROR_HEADER_INVALID;
136 if (!strncmp(buffer, kCrxDiffFileHeaderMagic, kCrx2FileHeaderMagicSize)) 266 if (!strncmp(buffer, kCrxDiffFileHeaderMagic, kCrx2FileHeaderMagicSize))
137 diff = true; 267 diff = true;
138 else if (strncmp(buffer, kCrx2FileHeaderMagic, kCrx2FileHeaderMagicSize)) 268 else if (strncmp(buffer, kCrx2FileHeaderMagic, kCrx2FileHeaderMagicSize))
139 return VerifierResult::ERROR_HEADER_INVALID; 269 return VerifierResult::ERROR_HEADER_INVALID;
140 file_hash->Update(buffer, sizeof(buffer)); 270 file_hash->Update(buffer, sizeof(buffer));
141 271
142 // Version number. 272 // Version number.
143 const uint32_t version = 273 const uint32_t version =
144 ReadAndHashLittleEndianUInt32(&file, file_hash.get()); 274 ReadAndHashLittleEndianUInt32(&file, file_hash.get());
145 VerifierResult result; 275 VerifierResult result;
146 if (format == VerifierFormat::CRX2_OR_CRX3 && 276 if (format == VerifierFormat::CRX2_OR_CRX3 &&
147 (version == 2 || (diff && version == 0))) 277 (version == 2 || (diff && version == 0)))
148 result = VerifyCrx2(&file, file_hash.get(), required_key_hashes, public_key, 278 result = VerifyCrx2(&file, file_hash.get(), required_key_hashes,
149 crx_id); 279 &public_key_local, &crx_id_local);
150 else if (version == 3) 280 else if (version == 3)
151 result = VerifyCrx3(&file, file_hash.get(), required_key_hashes, public_key, 281 result = VerifyCrx3(&file, file_hash.get(), required_key_hashes,
152 crx_id); 282 &public_key_local, &crx_id_local,
283 format == VerifierFormat::CRX3_WITH_PUBLISHER_PROOF);
153 else 284 else
154 result = VerifierResult::ERROR_HEADER_INVALID; 285 result = VerifierResult::ERROR_HEADER_INVALID;
155 if (result != VerifierResult::OK_FULL) 286 if (result != VerifierResult::OK_FULL)
156 return result; 287 return result;
157 288
158 // Finalize file hash. 289 // Finalize file hash.
159 uint8_t final_hash[crypto::kSHA256Length] = {}; 290 uint8_t final_hash[crypto::kSHA256Length] = {};
160 file_hash->Finish(final_hash, sizeof(final_hash)); 291 file_hash->Finish(final_hash, sizeof(final_hash));
161 if (!required_file_hash.empty()) { 292 if (!required_file_hash.empty()) {
162 if (required_file_hash.size() != crypto::kSHA256Length) 293 if (required_file_hash.size() != crypto::kSHA256Length)
163 return VerifierResult::ERROR_EXPECTED_HASH_INVALID; 294 return VerifierResult::ERROR_EXPECTED_HASH_INVALID;
164 if (!crypto::SecureMemEqual(final_hash, required_file_hash.data(), 295 if (!crypto::SecureMemEqual(final_hash, required_file_hash.data(),
165 crypto::kSHA256Length)) 296 crypto::kSHA256Length))
166 return VerifierResult::ERROR_FILE_HASH_FAILED; 297 return VerifierResult::ERROR_FILE_HASH_FAILED;
167 } 298 }
299
300 // All is well. Set the out-params and return.
301 if (public_key)
302 *public_key = public_key_local;
303 if (crx_id)
304 *crx_id = crx_id_local;
168 return diff ? VerifierResult::OK_DELTA : VerifierResult::OK_FULL; 305 return diff ? VerifierResult::OK_DELTA : VerifierResult::OK_FULL;
169 } 306 }
170 307
171 } // namespace crx_file 308 } // namespace crx_file
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698