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

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

Issue 2874503002: Refactor CRX verification in preparation to support CRX₃ files. (Closed)
Patch Set: Do not reorder histogram 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 "base/memory/ptr_util.h"
14 #include "components/crx_file/crx2_file.h"
15 #include "components/crx_file/id_util.h"
16 #include "crypto/secure_hash.h"
17 #include "crypto/secure_util.h"
18 #include "crypto/sha2.h"
19 #include "crypto/signature_verifier.h"
20
21 namespace crx_file {
22
23 namespace {
24
25 // The maximum size the crx2 parser will tolerate for a public key.
26 constexpr uint32_t kMaxPublicKeySize = 1 << 16;
27
28 // The maximum size the crx2 parser will tolerate for a signature.
29 constexpr uint32_t kMaxSignatureSize = 1 << 16;
30
31 int ReadAndHashBuffer(uint8_t* buffer,
32 int length,
33 base::File* file,
34 crypto::SecureHash* hash) {
35 static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size.");
36 int read = file->ReadAtCurrentPos(reinterpret_cast<char*>(buffer), length);
37 hash->Update(buffer, read);
38 return read;
39 }
40
41 // Returns UINT32_MAX in the case of an unexpected EOF or read error, else
42 // returns the read uint32.
43 uint32_t ReadAndHashLittleEndianUInt32(base::File* file,
44 crypto::SecureHash* hash) {
45 uint8_t buffer[4] = {};
46 if (ReadAndHashBuffer(buffer, 4, file, hash) != 4)
47 return UINT32_MAX;
48 return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0];
49 }
50
51 VerifierResult VerifyCrx3(
52 base::File* file,
53 crypto::SecureHash* hash,
54 const std::vector<std::vector<uint8_t>>& required_key_hashes,
55 std::string* public_key,
56 std::string* crx_id) {
57 // Crx3 files are not yet supported - treat this as a malformed header.
58 return VerifierResult::ERROR_HEADER_INVALID;
59 }
60
61 VerifierResult VerifyCrx2(
62 base::File* file,
63 crypto::SecureHash* hash,
64 const std::vector<std::vector<uint8_t>>& required_key_hashes,
65 std::string* public_key,
66 std::string* crx_id) {
67 const uint32_t key_size = ReadAndHashLittleEndianUInt32(file, hash);
68 if (key_size > kMaxPublicKeySize)
69 return VerifierResult::ERROR_HEADER_INVALID;
70 const uint32_t sig_size = ReadAndHashLittleEndianUInt32(file, hash);
71 if (sig_size > kMaxSignatureSize)
72 return VerifierResult::ERROR_HEADER_INVALID;
73 std::vector<uint8_t> key(key_size);
74 if (ReadAndHashBuffer(key.data(), key_size, file, hash) !=
75 static_cast<int>(key_size))
76 return VerifierResult::ERROR_HEADER_INVALID;
77 for (const auto& expected_hash : required_key_hashes) {
78 // In practice we expect zero or one key_hashes_ for CRX2 files.
79 std::vector<uint8_t> hash(crypto::kSHA256Length);
80 std::unique_ptr<crypto::SecureHash> sha256 =
81 crypto::SecureHash::Create(crypto::SecureHash::SHA256);
82 sha256->Update(key.data(), key.size());
83 sha256->Finish(hash.data(), hash.size());
84 if (hash != expected_hash)
85 return VerifierResult::ERROR_REQUIRED_PROOF_MISSING;
86 }
87
88 std::vector<uint8_t> sig(sig_size);
89 if (ReadAndHashBuffer(sig.data(), sig_size, file, hash) !=
90 static_cast<int>(sig_size))
91 return VerifierResult::ERROR_HEADER_INVALID;
92 crypto::SignatureVerifier verifier;
93 if (!verifier.VerifyInit(crypto::SignatureVerifier::RSA_PKCS1_SHA1,
94 sig.data(), sig.size(), key.data(), key.size())) {
95 return VerifierResult::ERROR_SIGNATURE_INITIALIZATION_FAILED;
96 }
97
98 // Read the rest of the file.
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;
105
106 const std::string public_key_bytes(key.begin(), key.end());
107 if (public_key)
108 base::Base64Encode(public_key_bytes, public_key);
109 if (crx_id)
110 *crx_id = id_util::GenerateId(public_key_bytes);
111 return VerifierResult::OK_FULL;
112 }
113
114 } // namespace
115
116 VerifierResult Verify(
117 const base::FilePath& crx_path,
118 const VerifierFormat& format,
119 const std::vector<std::vector<uint8_t>>& required_key_hashes,
120 const std::vector<uint8_t>& required_file_hash,
121 std::string* public_key,
122 std::string* crx_id) {
123 base::File file(crx_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
124 if (!file.IsValid())
125 return VerifierResult::ERROR_FILE_NOT_READABLE;
126
127 std::unique_ptr<crypto::SecureHash> file_hash =
128 crypto::SecureHash::Create(crypto::SecureHash::SHA256);
129
130 // Magic number.
131 bool diff = false;
132 char buffer[kCrx2FileHeaderMagicSize] = {};
133 if (file.ReadAtCurrentPos(buffer, kCrx2FileHeaderMagicSize) !=
134 kCrx2FileHeaderMagicSize)
135 return VerifierResult::ERROR_HEADER_INVALID;
136 if (!strncmp(buffer, kCrxDiffFileHeaderMagic, kCrx2FileHeaderMagicSize))
137 diff = true;
138 else if (strncmp(buffer, kCrx2FileHeaderMagic, kCrx2FileHeaderMagicSize))
139 return VerifierResult::ERROR_HEADER_INVALID;
140 file_hash->Update(buffer, sizeof(buffer));
141
142 // Version number.
143 const uint32_t version =
144 ReadAndHashLittleEndianUInt32(&file, file_hash.get());
145 VerifierResult result;
146 if (format == VerifierFormat::CRX2_OR_CRX3 &&
147 (version == 2 || (diff && version == 0)))
148 result = VerifyCrx2(&file, file_hash.get(), required_key_hashes, public_key,
149 crx_id);
150 else if (version == 3)
151 result = VerifyCrx3(&file, file_hash.get(), required_key_hashes, public_key,
152 crx_id);
153 else
154 return VerifierResult::ERROR_HEADER_INVALID;
Sorin Jianu 2017/05/16 20:54:50 we could say result = VerifierResult::ERROR_HEADER
waffles 2017/05/16 22:34:52 Done.
155 if (result != VerifierResult::OK_FULL)
156 return result;
157
158 // Finalize file hash.
159 uint8_t final_hash[crypto::kSHA256Length] = {};
160 file_hash->Finish(final_hash, sizeof(final_hash));
161 if (!required_file_hash.empty()) {
162 if (required_file_hash.size() != crypto::kSHA256Length)
163 return VerifierResult::ERROR_EXPECTED_HASH_INVALID;
164 if (!crypto::SecureMemEqual(final_hash, required_file_hash.data(),
165 crypto::kSHA256Length))
166 return VerifierResult::ERROR_FILE_HASH_FAILED;
167 }
168 return diff ? VerifierResult::OK_DELTA : VerifierResult::OK_FULL;
169 }
170
171 } // namespace crx_file
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698