1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/crx_file/crx_verifier.h"
14 #include "base/base64.h"
15 #include "base/files/file.h"
16 #include "base/files/file_path.h"
17 #include "base/functional/bind.h"
18 #include "base/functional/callback.h"
19 #include "base/numerics/safe_conversions.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "components/crx_file/crx3.pb.h"
22 #include "components/crx_file/crx_file.h"
23 #include "components/crx_file/id_util.h"
24 #include "crypto/secure_hash.h"
25 #include "crypto/secure_util.h"
26 #include "crypto/sha2.h"
27 #include "crypto/signature_verifier.h"
28 #include "third_party/abseil-cpp/absl/types/optional.h"
34 // The SHA256 hash of the DER SPKI "ecdsa_2017_public" Crx3 key.
35 constexpr uint8_t kPublisherKeyHash[] = {
36 0x61, 0xf7, 0xf2, 0xa6, 0xbf, 0xcf, 0x74, 0xcd, 0x0b, 0xc1, 0xfe,
37 0x24, 0x97, 0xcc, 0x9b, 0x04, 0x25, 0x4c, 0x65, 0x8f, 0x79, 0xf2,
38 0x14, 0x53, 0x92, 0x86, 0x7e, 0xa8, 0x36, 0x63, 0x67, 0xcf};
40 // The SHA256 hash of the DER SPKI "ecdsa_2017_public" Crx3 test key.
41 constexpr uint8_t kPublisherTestKeyHash[] = {
42 0x6c, 0x46, 0x41, 0x3b, 0x00, 0xd0, 0xfa, 0x0e, 0x72, 0xc8, 0xd2,
43 0x5f, 0x64, 0xf3, 0xa6, 0x17, 0x03, 0x0d, 0xde, 0x21, 0x61, 0xbe,
44 0xb7, 0x95, 0x91, 0x95, 0x83, 0x68, 0x12, 0xe9, 0x78, 0x1e};
46 using VerifierCollection =
47 std::vector<std::unique_ptr<crypto::SignatureVerifier>>;
48 using RepeatedProof = google::protobuf::RepeatedPtrField<AsymmetricKeyProof>;
50 int ReadAndHashBuffer(uint8_t* buffer,
53 crypto::SecureHash* hash) {
54 static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size.");
55 int read = file->ReadAtCurrentPos(reinterpret_cast<char*>(buffer), length);
57 hash->Update(buffer, read);
61 // Returns UINT32_MAX in the case of an unexpected EOF or read error, else
62 // returns the read uint32.
63 uint32_t ReadAndHashLittleEndianUInt32(base::File* file,
64 crypto::SecureHash* hash) {
65 uint8_t buffer[4] = {};
66 if (ReadAndHashBuffer(buffer, 4, file, hash) != 4)
68 return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0];
71 // Read to the end of the file, updating the hash and all verifiers.
72 bool ReadHashAndVerifyArchive(base::File* file,
73 crypto::SecureHash* hash,
74 const VerifierCollection& verifiers) {
75 uint8_t buffer[1 << 12] = {};
77 while ((len = ReadAndHashBuffer(buffer, std::size(buffer), file, hash)) > 0) {
78 for (auto& verifier : verifiers)
79 verifier->VerifyUpdate(base::make_span(buffer, len));
81 for (auto& verifier : verifiers) {
82 if (!verifier->VerifyFinal())
88 // The remaining contents of a Crx3 file are [header-size][header][archive].
89 // [header] is an encoded protocol buffer and contains both a signed and
90 // unsigned section. The unsigned section contains a set of key/signature pairs,
91 // and the signed section is the encoding of another protocol buffer. All
92 // signatures cover [prefix][signed-header-size][signed-header][archive].
93 VerifierResult VerifyCrx3(
95 crypto::SecureHash* hash,
96 const std::vector<std::vector<uint8_t>>& required_key_hashes,
97 std::string* public_key,
99 std::vector<uint8_t>* compressed_verified_contents,
100 bool require_publisher_key,
101 bool accept_publisher_test_key) {
102 // Parse [header-size] and [header].
104 base::saturated_cast<int>(ReadAndHashLittleEndianUInt32(file, hash));
105 if (header_size == INT_MAX)
106 return VerifierResult::ERROR_HEADER_INVALID;
107 std::vector<uint8_t> header_bytes(header_size);
108 if (ReadAndHashBuffer(header_bytes.data(), header_size, file, hash) !=
110 return VerifierResult::ERROR_HEADER_INVALID;
112 CrxFileHeader header;
113 if (!header.ParseFromArray(header_bytes.data(), header_size))
114 return VerifierResult::ERROR_HEADER_INVALID;
116 // Parse [verified_contents].
117 if (header.has_verified_contents() && compressed_verified_contents) {
118 const std::string& header_verified_contents(header.verified_contents());
119 compressed_verified_contents->assign(header_verified_contents.begin(),
120 header_verified_contents.end());
123 // Parse [signed-header].
124 const std::string& signed_header_data_str = header.signed_header_data();
125 SignedData signed_header_data;
126 if (!signed_header_data.ParseFromString(signed_header_data_str))
127 return VerifierResult::ERROR_HEADER_INVALID;
128 const std::string& crx_id_encoded = signed_header_data.crx_id();
129 const std::string declared_crx_id = id_util::GenerateIdFromHex(
130 base::HexEncode(crx_id_encoded.data(), crx_id_encoded.size()));
132 // Create a little-endian representation of [signed-header-size].
133 const int signed_header_size = signed_header_data_str.size();
134 const uint8_t header_size_octets[] = {
135 static_cast<uint8_t>(signed_header_size),
136 static_cast<uint8_t>(signed_header_size >> 8),
137 static_cast<uint8_t>(signed_header_size >> 16),
138 static_cast<uint8_t>(signed_header_size >> 24)};
140 // Create a set of all required key hashes.
141 std::set<std::vector<uint8_t>> required_key_set(required_key_hashes.begin(),
142 required_key_hashes.end());
144 using ProofFetcher = const RepeatedProof& (CrxFileHeader::*)() const;
145 ProofFetcher rsa = &CrxFileHeader::sha256_with_rsa;
146 ProofFetcher ecdsa = &CrxFileHeader::sha256_with_ecdsa;
148 std::string public_key_bytes;
149 VerifierCollection verifiers;
150 verifiers.reserve(header.sha256_with_rsa_size() +
151 header.sha256_with_ecdsa_size());
153 std::pair<ProofFetcher, crypto::SignatureVerifier::SignatureAlgorithm>>
155 std::make_pair(rsa, crypto::SignatureVerifier::RSA_PKCS1_SHA256),
156 std::make_pair(ecdsa, crypto::SignatureVerifier::ECDSA_SHA256)};
158 std::vector<uint8_t> publisher_key(std::begin(kPublisherKeyHash),
159 std::end(kPublisherKeyHash));
160 absl::optional<std::vector<uint8_t>> publisher_test_key;
161 if (accept_publisher_test_key) {
162 publisher_test_key.emplace(std::begin(kPublisherTestKeyHash),
163 std::end(kPublisherTestKeyHash));
165 bool found_publisher_key = false;
167 // Initialize all verifiers and update them with
168 // [prefix][signed-header-size][signed-header].
169 // Clear any elements of required_key_set that are encountered, and watch for
170 // the developer key.
171 for (const auto& proof_type : proof_types) {
172 for (const auto& proof : (header.*proof_type.first)()) {
173 const std::string& key = proof.public_key();
174 const std::string& sig = proof.signature();
175 if (id_util::GenerateId(key) == declared_crx_id)
176 public_key_bytes = key;
177 std::vector<uint8_t> key_hash(crypto::kSHA256Length);
178 crypto::SHA256HashString(key, key_hash.data(), key_hash.size());
179 required_key_set.erase(key_hash);
180 DCHECK_EQ(accept_publisher_test_key, publisher_test_key.has_value());
181 found_publisher_key =
182 found_publisher_key || key_hash == publisher_key ||
183 (accept_publisher_test_key && key_hash == *publisher_test_key);
184 auto v = std::make_unique<crypto::SignatureVerifier>();
185 static_assert(sizeof(unsigned char) == sizeof(uint8_t),
186 "Unsupported char size.");
187 if (!v->VerifyInit(proof_type.second,
188 base::as_bytes(base::make_span(sig)),
189 base::as_bytes(base::make_span(key))))
190 return VerifierResult::ERROR_SIGNATURE_INITIALIZATION_FAILED;
191 v->VerifyUpdate(base::as_bytes(base::make_span(kSignatureContext)));
192 v->VerifyUpdate(header_size_octets);
193 v->VerifyUpdate(base::as_bytes(base::make_span(signed_header_data_str)));
194 verifiers.push_back(std::move(v));
197 if (public_key_bytes.empty() || !required_key_set.empty())
198 return VerifierResult::ERROR_REQUIRED_PROOF_MISSING;
200 if (require_publisher_key && !found_publisher_key)
201 return VerifierResult::ERROR_REQUIRED_PROOF_MISSING;
203 // Update and finalize the verifiers with [archive].
204 if (!ReadHashAndVerifyArchive(file, hash, verifiers))
205 return VerifierResult::ERROR_SIGNATURE_VERIFICATION_FAILED;
207 base::Base64Encode(public_key_bytes, public_key);
208 *crx_id = declared_crx_id;
209 return VerifierResult::OK_FULL;
214 VerifierResult Verify(
215 const base::FilePath& crx_path,
216 const VerifierFormat& format,
217 const std::vector<std::vector<uint8_t>>& required_key_hashes,
218 const std::vector<uint8_t>& required_file_hash,
219 std::string* public_key,
221 std::vector<uint8_t>* compressed_verified_contents) {
222 std::string public_key_local;
223 std::string crx_id_local;
224 base::File file(crx_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
226 return VerifierResult::ERROR_FILE_NOT_READABLE;
228 std::unique_ptr<crypto::SecureHash> file_hash =
229 crypto::SecureHash::Create(crypto::SecureHash::SHA256);
233 char buffer[kCrxFileHeaderMagicSize] = {};
234 if (file.ReadAtCurrentPos(buffer, kCrxFileHeaderMagicSize) !=
235 kCrxFileHeaderMagicSize)
236 return VerifierResult::ERROR_HEADER_INVALID;
237 if (!strncmp(buffer, kCrxDiffFileHeaderMagic, kCrxFileHeaderMagicSize))
239 else if (strncmp(buffer, kCrxFileHeaderMagic, kCrxFileHeaderMagicSize))
240 return VerifierResult::ERROR_HEADER_INVALID;
241 file_hash->Update(buffer, sizeof(buffer));
244 const uint32_t version =
245 ReadAndHashLittleEndianUInt32(&file, file_hash.get());
246 VerifierResult result;
248 bool require_publisher_key =
249 format == VerifierFormat::CRX3_WITH_PUBLISHER_PROOF ||
250 format == VerifierFormat::CRX3_WITH_TEST_PUBLISHER_PROOF;
252 &file, file_hash.get(), required_key_hashes, &public_key_local,
253 &crx_id_local, compressed_verified_contents, require_publisher_key,
254 format == VerifierFormat::CRX3_WITH_TEST_PUBLISHER_PROOF);
256 result = VerifierResult::ERROR_HEADER_INVALID;
258 if (result != VerifierResult::OK_FULL)
261 // Finalize file hash.
262 uint8_t final_hash[crypto::kSHA256Length] = {};
263 file_hash->Finish(final_hash, sizeof(final_hash));
264 if (!required_file_hash.empty()) {
265 if (required_file_hash.size() != crypto::kSHA256Length)
266 return VerifierResult::ERROR_EXPECTED_HASH_INVALID;
267 if (!crypto::SecureMemEqual(final_hash, required_file_hash.data(),
268 crypto::kSHA256Length))
269 return VerifierResult::ERROR_FILE_HASH_FAILED;
272 // All is well. Set the out-params and return.
274 *public_key = public_key_local;
276 *crx_id = crx_id_local;
277 return diff ? VerifierResult::OK_DELTA : VerifierResult::OK_FULL;
280 } // namespace crx_file