OLD | NEW |
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 // Read to the end of the file, updating the hash and all verifiers. |
| 75 bool ReadHashAndVerifyArchive(base::File* file, |
| 76 crypto::SecureHash* hash, |
| 77 const VerifierCollection& verifiers) { |
| 78 uint8_t buffer[1 << 12] = {}; |
| 79 size_t len = 0; |
| 80 while ((len = ReadAndHashBuffer(buffer, arraysize(buffer), file, hash)) > 0) { |
| 81 for (auto& verifier : verifiers) |
| 82 verifier->VerifyUpdate(buffer, len); |
| 83 } |
| 84 for (auto& verifier : verifiers) { |
| 85 if (!verifier->VerifyFinal()) |
| 86 return false; |
| 87 } |
| 88 return true; |
| 89 } |
| 90 |
| 91 // The remaining contents of a Crx3 file are [header-size][header][archive]. |
| 92 // [header] is an encoded protocol buffer and contains both a signed and |
| 93 // unsigned section. The unsigned section contains a set of key/signature pairs, |
| 94 // and the signed section is the encoding of another protocol buffer. All |
| 95 // signatures cover [prefix][signed-header-size][signed-header][archive]. |
51 VerifierResult VerifyCrx3( | 96 VerifierResult VerifyCrx3( |
52 base::File* file, | 97 base::File* file, |
53 crypto::SecureHash* hash, | 98 crypto::SecureHash* hash, |
54 const std::vector<std::vector<uint8_t>>& required_key_hashes, | 99 const std::vector<std::vector<uint8_t>>& required_key_hashes, |
55 std::string* public_key, | 100 std::string* public_key, |
56 std::string* crx_id) { | 101 std::string* crx_id, |
57 // Crx3 files are not yet supported - treat this as a malformed header. | 102 bool require_publisher_key) { |
58 return VerifierResult::ERROR_HEADER_INVALID; | 103 // Parse [header-size] and [header]. |
| 104 const uint32_t header_size = ReadAndHashLittleEndianUInt32(file, hash); |
| 105 if (header_size > kMaxHeaderSize) |
| 106 return VerifierResult::ERROR_HEADER_INVALID; |
| 107 std::vector<uint8_t> header_bytes(header_size); |
| 108 // Assuming kMaxHeaderSize can fit in an int, the following cast is safe. |
| 109 if (ReadAndHashBuffer(header_bytes.data(), header_size, file, hash) != |
| 110 static_cast<int>(header_size)) |
| 111 return VerifierResult::ERROR_HEADER_INVALID; |
| 112 CrxFileHeader header; |
| 113 if (!header.ParseFromArray(header_bytes.data(), header_size)) |
| 114 return VerifierResult::ERROR_HEADER_INVALID; |
| 115 |
| 116 // Parse [signed-header]. |
| 117 const std::string& signed_header_data_str = header.signed_header_data(); |
| 118 SignedData signed_header_data; |
| 119 if (!signed_header_data.ParseFromString(signed_header_data_str)) |
| 120 return VerifierResult::ERROR_HEADER_INVALID; |
| 121 const std::string& crx_id_encoded = signed_header_data.crx_id(); |
| 122 const std::string declared_crx_id = id_util::GenerateIdFromHex( |
| 123 base::HexEncode(crx_id_encoded.data(), crx_id_encoded.size())); |
| 124 |
| 125 // Create a little-endian representation of [signed-header-size]. |
| 126 const int signed_header_size = signed_header_data_str.size(); |
| 127 const uint8_t header_size_octets[] = { |
| 128 signed_header_size, signed_header_size >> 8, signed_header_size >> 16, |
| 129 signed_header_size >> 24}; |
| 130 |
| 131 // Create a set of all required key hashes. |
| 132 std::set<std::vector<uint8_t>> required_key_set(required_key_hashes.begin(), |
| 133 required_key_hashes.end()); |
| 134 if (require_publisher_key) { |
| 135 required_key_set.emplace(std::begin(kPublisherKeyHash), |
| 136 std::end(kPublisherKeyHash)); |
| 137 } |
| 138 |
| 139 using ProofFetcher = const RepeatedProof& (CrxFileHeader::*)() const; |
| 140 ProofFetcher rsa = &CrxFileHeader::sha256_with_rsa; |
| 141 ProofFetcher ecdsa = &CrxFileHeader::sha256_with_ecdsa; |
| 142 |
| 143 std::string public_key_bytes; |
| 144 VerifierCollection verifiers; |
| 145 verifiers.reserve(header.sha256_with_rsa_size() + |
| 146 header.sha256_with_ecdsa_size()); |
| 147 const std::vector< |
| 148 std::pair<ProofFetcher, crypto::SignatureVerifier::SignatureAlgorithm>> |
| 149 proof_types = { |
| 150 std::make_pair(rsa, crypto::SignatureVerifier::RSA_PKCS1_SHA256), |
| 151 std::make_pair(ecdsa, crypto::SignatureVerifier::ECDSA_SHA256)}; |
| 152 |
| 153 // Initialize all verifiers and update them with |
| 154 // [prefix][signed-header-size][signed-header]. |
| 155 // Clear any elements of required_key_set that are encountered, and watch for |
| 156 // the developer key. |
| 157 for (const auto& proof_type : proof_types) { |
| 158 for (const auto& proof : (header.*proof_type.first)()) { |
| 159 const std::string& key = proof.public_key(); |
| 160 const std::string& sig = proof.signature(); |
| 161 if (id_util::GenerateId(key) == declared_crx_id) |
| 162 public_key_bytes = key; |
| 163 std::vector<uint8_t> key_hash(crypto::kSHA256Length); |
| 164 crypto::SHA256HashString(key, key_hash.data(), key_hash.size()); |
| 165 required_key_set.erase(key_hash); |
| 166 auto v = 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)); |
| 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 |
OLD | NEW |