Chromium Code Reviews| 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 <memory> | 8 #include <memory> |
| 9 #include <set> | |
| 10 #include <utility> | |
| 9 | 11 |
| 10 #include "base/base64.h" | 12 #include "base/base64.h" |
| 13 #include "base/bind.h" | |
| 14 #include "base/callback.h" | |
| 11 #include "base/files/file.h" | 15 #include "base/files/file.h" |
| 12 #include "base/files/file_path.h" | 16 #include "base/files/file_path.h" |
| 13 #include "base/memory/ptr_util.h" | 17 #include "base/memory/ptr_util.h" |
| 18 #include "base/strings/string_number_conversions.h" | |
| 14 #include "components/crx_file/crx2_file.h" | 19 #include "components/crx_file/crx2_file.h" |
| 20 #include "components/crx_file/crx3.pb.h" | |
| 15 #include "components/crx_file/id_util.h" | 21 #include "components/crx_file/id_util.h" |
| 16 #include "crypto/secure_hash.h" | 22 #include "crypto/secure_hash.h" |
| 17 #include "crypto/secure_util.h" | 23 #include "crypto/secure_util.h" |
| 18 #include "crypto/sha2.h" | 24 #include "crypto/sha2.h" |
| 19 #include "crypto/signature_verifier.h" | 25 #include "crypto/signature_verifier.h" |
| 20 | 26 |
| 21 namespace crx_file { | 27 namespace crx_file { |
| 22 | 28 |
| 23 namespace { | 29 namespace { |
| 24 | 30 |
| 25 // The maximum size the crx2 parser will tolerate for a public key. | 31 // The maximum size the Crx2 parser will tolerate for a public key. |
| 26 constexpr uint32_t kMaxPublicKeySize = 1 << 16; | 32 constexpr uint32_t kMaxPublicKeySize = 1 << 16; |
| 27 | 33 |
| 28 // The maximum size the crx2 parser will tolerate for a signature. | 34 // The maximum size the Crx2 parser will tolerate for a signature. |
| 29 constexpr uint32_t kMaxSignatureSize = 1 << 16; | 35 constexpr uint32_t kMaxSignatureSize = 1 << 16; |
| 30 | 36 |
| 37 // The maximum size the Crx3 parser will tolerate for a header. | |
| 38 constexpr uint32_t kMaxHeaderSize = 1 << 18; | |
| 39 | |
| 40 // The salt for Crx3 signing, encoded in UTF8. | |
| 41 constexpr unsigned char kSalt[] = u8"CRX3 SignedData\0"; | |
| 42 | |
| 43 // The SHA256 hash of the "ecdsa_2017_public" Crx3 key. | |
| 44 constexpr uint8_t kPublisherKeyHash[] = { | |
| 45 0x61, 0xf7, 0xf2, 0xa6, 0xbf, 0xcf, 0x74, 0xcd, 0x0b, 0xc1, 0xfe, | |
| 46 0x24, 0x97, 0xcc, 0x9b, 0x04, 0x25, 0x4c, 0x65, 0x8f, 0x79, 0xf2, | |
| 47 0x14, 0x53, 0x92, 0x86, 0x7e, 0xa8, 0x36, 0x63, 0x67, 0xcf}; | |
| 48 | |
| 31 int ReadAndHashBuffer(uint8_t* buffer, | 49 int ReadAndHashBuffer(uint8_t* buffer, |
| 32 int length, | 50 int length, |
| 33 base::File* file, | 51 base::File* file, |
| 34 crypto::SecureHash* hash) { | 52 crypto::SecureHash* hash) { |
| 35 static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size."); | 53 static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size."); |
| 36 int read = file->ReadAtCurrentPos(reinterpret_cast<char*>(buffer), length); | 54 int read = file->ReadAtCurrentPos(reinterpret_cast<char*>(buffer), length); |
| 37 hash->Update(buffer, read); | 55 hash->Update(buffer, read); |
| 38 return read; | 56 return read; |
| 39 } | 57 } |
| 40 | 58 |
| 41 // Returns UINT32_MAX in the case of an unexpected EOF or read error, else | 59 // Returns UINT32_MAX in the case of an unexpected EOF or read error, else |
| 42 // returns the read uint32. | 60 // returns the read uint32. |
| 43 uint32_t ReadAndHashLittleEndianUInt32(base::File* file, | 61 uint32_t ReadAndHashLittleEndianUInt32(base::File* file, |
| 44 crypto::SecureHash* hash) { | 62 crypto::SecureHash* hash) { |
| 45 uint8_t buffer[4] = {}; | 63 uint8_t buffer[4] = {}; |
| 46 if (ReadAndHashBuffer(buffer, 4, file, hash) != 4) | 64 if (ReadAndHashBuffer(buffer, 4, file, hash) != 4) |
| 47 return UINT32_MAX; | 65 return UINT32_MAX; |
| 48 return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0]; | 66 return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0]; |
| 49 } | 67 } |
| 50 | 68 |
| 69 bool ReadHashAndVerifyArchive( | |
| 70 base::File* file, | |
| 71 crypto::SecureHash* hash, | |
| 72 const std::vector<std::unique_ptr<crypto::SignatureVerifier>>& verifiers) { | |
| 73 uint8_t buffer[1 << 12] = {}; | |
| 74 size_t len; | |
|
Sorin Jianu
2017/05/17 23:54:53
not initialized
waffles
2017/05/18 00:43:53
Done, thanks; messed it up in the merge.
| |
| 75 while ((len = ReadAndHashBuffer(buffer, arraysize(buffer), file, hash)) > 0) { | |
| 76 for (auto& verifier : verifiers) | |
| 77 verifier->VerifyUpdate(buffer, len); | |
| 78 } | |
| 79 for (auto& verifier : verifiers) { | |
| 80 if (!verifier->VerifyFinal()) | |
| 81 return false; | |
| 82 } | |
| 83 return true; | |
| 84 } | |
| 85 | |
| 51 VerifierResult VerifyCrx3( | 86 VerifierResult VerifyCrx3( |
| 52 base::File* file, | 87 base::File* file, |
| 53 crypto::SecureHash* hash, | 88 crypto::SecureHash* hash, |
| 54 const std::vector<std::vector<uint8_t>>& required_key_hashes, | 89 const std::vector<std::vector<uint8_t>>& required_key_hashes, |
| 55 std::string* public_key, | 90 std::string* public_key, |
| 56 std::string* crx_id) { | 91 std::string* crx_id, |
| 57 // Crx3 files are not yet supported - treat this as a malformed header. | 92 bool require_publisher_key) { |
| 58 return VerifierResult::ERROR_HEADER_INVALID; | 93 // Parse header data. |
| 94 const uint32_t header_size = ReadAndHashLittleEndianUInt32(file, hash); | |
| 95 if (header_size > kMaxHeaderSize) | |
| 96 return VerifierResult::ERROR_HEADER_INVALID; | |
| 97 std::vector<uint8_t> header_bytes(header_size); | |
| 98 if (ReadAndHashBuffer(header_bytes.data(), header_size, file, hash) != | |
| 99 static_cast<int>(header_size)) | |
| 100 return VerifierResult::ERROR_HEADER_INVALID; | |
| 101 CrxFileHeader header; | |
| 102 if (!header.ParseFromArray(header_bytes.data(), header_size)) | |
| 103 return VerifierResult::ERROR_HEADER_INVALID; | |
| 104 | |
| 105 // Parse signed header data. | |
| 106 const std::string& signed_header_data_str = header.signed_header_data(); | |
| 107 SignedData signed_header_data; | |
| 108 if (!signed_header_data.ParseFromString(signed_header_data_str)) | |
| 109 return VerifierResult::ERROR_HEADER_INVALID; | |
| 110 const std::string& crx_id_encoded = signed_header_data.crx_id(); | |
| 111 const std::string declared_crx_id = | |
| 112 base::HexEncode(crx_id_encoded.data(), crx_id_encoded.size()); | |
| 113 | |
| 114 // Compute the signing prefix for the signed header data size. | |
| 115 const int signed_header_size = signed_header_data_str.size(); | |
| 116 const uint8_t header_size_octets[] = { | |
| 117 signed_header_size, signed_header_size >> 8, signed_header_size >> 16, | |
| 118 signed_header_size >> 24}; | |
| 119 | |
| 120 // Set up the required keys. | |
| 121 std::set<std::vector<uint8_t>> required_key_set(required_key_hashes.begin(), | |
| 122 required_key_hashes.end()); | |
| 123 if (require_publisher_key) { | |
| 124 const std::vector<uint8_t> publisher_key( | |
| 125 kPublisherKeyHash, kPublisherKeyHash + arraysize(kPublisherKeyHash)); | |
|
Sorin Jianu
2017/05/17 23:54:53
I've started using begin and end for initializers
waffles
2017/05/18 00:43:53
Done, thanks!
| |
| 126 required_key_set.insert(publisher_key); | |
| 127 } | |
| 128 std::string public_key_bytes; | |
| 129 std::vector<std::unique_ptr<crypto::SignatureVerifier>> verifiers = {}; | |
|
Sorin Jianu
2017/05/17 23:54:53
do we need {} ?
waffles
2017/05/18 00:43:53
Done.
| |
| 130 const std::vector< | |
| 131 std::pair<std::function<const google::protobuf::RepeatedPtrField< | |
| 132 AsymmetricKeyProof>&()>, | |
| 133 crypto::SignatureVerifier::SignatureAlgorithm>> | |
| 134 proof_types = { | |
| 135 std::make_pair([&] { return header.sha256_with_rsa(); }, | |
| 136 crypto::SignatureVerifier::RSA_PKCS1_SHA256), | |
| 137 std::make_pair([&] { return header.sha256_with_ecdsa(); }, | |
| 138 crypto::SignatureVerifier::ECDSA_SHA256)}; | |
| 139 // Initialize all verifiers and update them with the salts / prefixes. | |
| 140 for (auto proof_type : proof_types) { | |
|
Sorin Jianu
2017/05/17 23:54:53
maybe const auto& or auto& ?
waffles
2017/05/18 00:43:53
Done.
| |
| 141 for (auto proof : proof_type.first()) { | |
|
Sorin Jianu
2017/05/17 23:54:53
maybe const auto & if possible?
waffles
2017/05/18 00:43:53
Done.
| |
| 142 const std::string& key = proof.public_key(); | |
| 143 const std::string& sig = proof.signature(); | |
| 144 if (id_util::GenerateId(key) == declared_crx_id) | |
| 145 public_key_bytes = key; | |
| 146 std::vector<uint8_t> key_hash(crypto::kSHA256Length); | |
| 147 crypto::SHA256HashString(key, key_hash.data(), key_hash.size()); | |
| 148 required_key_set.erase(key_hash); | |
| 149 std::unique_ptr<crypto::SignatureVerifier> v = | |
| 150 base::MakeUnique<crypto::SignatureVerifier>(); | |
| 151 static_assert(sizeof(unsigned char) == sizeof(uint8_t), | |
| 152 "Unsupported char size."); | |
| 153 if (!v->VerifyInit( | |
| 154 proof_type.second, reinterpret_cast<const uint8_t*>(sig.data()), | |
| 155 sig.size(), reinterpret_cast<const uint8_t*>(key.data()), | |
| 156 key.size())) | |
| 157 return VerifierResult::ERROR_SIGNATURE_INITIALIZATION_FAILED; | |
| 158 v->VerifyUpdate(kSalt, arraysize(kSalt)); | |
|
Sorin Jianu
2017/05/17 23:54:53
do we want arraysize here or sizeof? It won't make
waffles
2017/05/18 00:43:53
I think arraysize, since that is semantically cons
| |
| 159 v->VerifyUpdate(header_size_octets, arraysize(header_size_octets)); | |
| 160 v->VerifyUpdate( | |
| 161 reinterpret_cast<const uint8_t*>(signed_header_data_str.data()), | |
| 162 signed_header_data_str.size()); | |
| 163 verifiers.push_back(std::move(v)); | |
| 164 } | |
| 165 } | |
| 166 if (!public_key_bytes.empty() || !required_key_set.empty()) | |
| 167 return VerifierResult::ERROR_REQUIRED_PROOF_MISSING; | |
| 168 | |
| 169 // Verify the entire archive. | |
| 170 if (!ReadHashAndVerifyArchive(file, hash, verifiers)) | |
| 171 return VerifierResult::ERROR_SIGNATURE_VERIFICATION_FAILED; | |
| 172 | |
| 173 if (public_key) | |
| 174 base::Base64Encode(public_key_bytes, public_key); | |
| 175 if (crx_id) | |
| 176 *crx_id = declared_crx_id; | |
| 177 return VerifierResult::OK_FULL; | |
| 59 } | 178 } |
| 60 | 179 |
| 61 VerifierResult VerifyCrx2( | 180 VerifierResult VerifyCrx2( |
| 62 base::File* file, | 181 base::File* file, |
| 63 crypto::SecureHash* hash, | 182 crypto::SecureHash* hash, |
| 64 const std::vector<std::vector<uint8_t>>& required_key_hashes, | 183 const std::vector<std::vector<uint8_t>>& required_key_hashes, |
| 65 std::string* public_key, | 184 std::string* public_key, |
| 66 std::string* crx_id) { | 185 std::string* crx_id) { |
| 67 const uint32_t key_size = ReadAndHashLittleEndianUInt32(file, hash); | 186 const uint32_t key_size = ReadAndHashLittleEndianUInt32(file, hash); |
| 68 if (key_size > kMaxPublicKeySize) | 187 if (key_size > kMaxPublicKeySize) |
| 69 return VerifierResult::ERROR_HEADER_INVALID; | 188 return VerifierResult::ERROR_HEADER_INVALID; |
| 70 const uint32_t sig_size = ReadAndHashLittleEndianUInt32(file, hash); | 189 const uint32_t sig_size = ReadAndHashLittleEndianUInt32(file, hash); |
| 71 if (sig_size > kMaxSignatureSize) | 190 if (sig_size > kMaxSignatureSize) |
| 72 return VerifierResult::ERROR_HEADER_INVALID; | 191 return VerifierResult::ERROR_HEADER_INVALID; |
| 73 std::vector<uint8_t> key(key_size); | 192 std::vector<uint8_t> key(key_size); |
| 74 if (ReadAndHashBuffer(key.data(), key_size, file, hash) != | 193 if (ReadAndHashBuffer(key.data(), key_size, file, hash) != |
| 75 static_cast<int>(key_size)) | 194 static_cast<int>(key_size)) |
| 76 return VerifierResult::ERROR_HEADER_INVALID; | 195 return VerifierResult::ERROR_HEADER_INVALID; |
| 77 for (const auto& expected_hash : required_key_hashes) { | 196 for (const auto& expected_hash : required_key_hashes) { |
| 78 // In practice we expect zero or one key_hashes_ for CRX2 files. | 197 // In practice we expect zero or one key_hashes_ for Crx2 files. |
| 79 std::vector<uint8_t> hash(crypto::kSHA256Length); | 198 std::vector<uint8_t> hash(crypto::kSHA256Length); |
| 80 std::unique_ptr<crypto::SecureHash> sha256 = | 199 std::unique_ptr<crypto::SecureHash> sha256 = |
| 81 crypto::SecureHash::Create(crypto::SecureHash::SHA256); | 200 crypto::SecureHash::Create(crypto::SecureHash::SHA256); |
| 82 sha256->Update(key.data(), key.size()); | 201 sha256->Update(key.data(), key.size()); |
| 83 sha256->Finish(hash.data(), hash.size()); | 202 sha256->Finish(hash.data(), hash.size()); |
| 84 if (hash != expected_hash) | 203 if (hash != expected_hash) |
| 85 return VerifierResult::ERROR_REQUIRED_PROOF_MISSING; | 204 return VerifierResult::ERROR_REQUIRED_PROOF_MISSING; |
| 86 } | 205 } |
| 87 | 206 |
| 88 std::vector<uint8_t> sig(sig_size); | 207 std::vector<uint8_t> sig(sig_size); |
| 89 if (ReadAndHashBuffer(sig.data(), sig_size, file, hash) != | 208 if (ReadAndHashBuffer(sig.data(), sig_size, file, hash) != |
| 90 static_cast<int>(sig_size)) | 209 static_cast<int>(sig_size)) |
| 91 return VerifierResult::ERROR_HEADER_INVALID; | 210 return VerifierResult::ERROR_HEADER_INVALID; |
| 92 crypto::SignatureVerifier verifier; | 211 std::vector<std::unique_ptr<crypto::SignatureVerifier>> verifiers; |
| 93 if (!verifier.VerifyInit(crypto::SignatureVerifier::RSA_PKCS1_SHA1, | 212 verifiers.push_back(base::MakeUnique<crypto::SignatureVerifier>()); |
| 94 sig.data(), sig.size(), key.data(), key.size())) { | 213 if (!verifiers[0]->VerifyInit(crypto::SignatureVerifier::RSA_PKCS1_SHA1, |
| 214 sig.data(), sig.size(), key.data(), | |
| 215 key.size())) { | |
| 95 return VerifierResult::ERROR_SIGNATURE_INITIALIZATION_FAILED; | 216 return VerifierResult::ERROR_SIGNATURE_INITIALIZATION_FAILED; |
| 96 } | 217 } |
| 97 | 218 |
| 98 // Read the rest of the file. | 219 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; | 220 return VerifierResult::ERROR_SIGNATURE_VERIFICATION_FAILED; |
| 105 | 221 |
| 106 const std::string public_key_bytes(key.begin(), key.end()); | 222 const std::string public_key_bytes(key.begin(), key.end()); |
| 107 if (public_key) | 223 if (public_key) |
| 108 base::Base64Encode(public_key_bytes, public_key); | 224 base::Base64Encode(public_key_bytes, public_key); |
| 109 if (crx_id) | 225 if (crx_id) |
| 110 *crx_id = id_util::GenerateId(public_key_bytes); | 226 *crx_id = id_util::GenerateId(public_key_bytes); |
| 111 return VerifierResult::OK_FULL; | 227 return VerifierResult::OK_FULL; |
| 112 } | 228 } |
| 113 | 229 |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 141 | 257 |
| 142 // Version number. | 258 // Version number. |
| 143 const uint32_t version = | 259 const uint32_t version = |
| 144 ReadAndHashLittleEndianUInt32(&file, file_hash.get()); | 260 ReadAndHashLittleEndianUInt32(&file, file_hash.get()); |
| 145 VerifierResult result; | 261 VerifierResult result; |
| 146 if (format == VerifierFormat::CRX2_OR_CRX3 && | 262 if (format == VerifierFormat::CRX2_OR_CRX3 && |
| 147 (version == 2 || (diff && version == 0))) | 263 (version == 2 || (diff && version == 0))) |
| 148 result = VerifyCrx2(&file, file_hash.get(), required_key_hashes, public_key, | 264 result = VerifyCrx2(&file, file_hash.get(), required_key_hashes, public_key, |
| 149 crx_id); | 265 crx_id); |
| 150 else if (version == 3) | 266 else if (version == 3) |
| 151 result = VerifyCrx3(&file, file_hash.get(), required_key_hashes, public_key, | 267 result = |
| 152 crx_id); | 268 VerifyCrx3(&file, file_hash.get(), required_key_hashes, public_key, |
| 269 crx_id, format == VerifierFormat::CRX3_WITH_PUBLISHER_PROOF); | |
| 153 else | 270 else |
| 154 result = VerifierResult::ERROR_HEADER_INVALID; | 271 result = VerifierResult::ERROR_HEADER_INVALID; |
| 155 if (result != VerifierResult::OK_FULL) | 272 if (result != VerifierResult::OK_FULL) |
| 156 return result; | 273 return result; |
| 157 | 274 |
| 158 // Finalize file hash. | 275 // Finalize file hash. |
| 159 uint8_t final_hash[crypto::kSHA256Length] = {}; | 276 uint8_t final_hash[crypto::kSHA256Length] = {}; |
| 160 file_hash->Finish(final_hash, sizeof(final_hash)); | 277 file_hash->Finish(final_hash, sizeof(final_hash)); |
| 161 if (!required_file_hash.empty()) { | 278 if (!required_file_hash.empty()) { |
| 162 if (required_file_hash.size() != crypto::kSHA256Length) | 279 if (required_file_hash.size() != crypto::kSHA256Length) |
| 163 return VerifierResult::ERROR_EXPECTED_HASH_INVALID; | 280 return VerifierResult::ERROR_EXPECTED_HASH_INVALID; |
| 164 if (!crypto::SecureMemEqual(final_hash, required_file_hash.data(), | 281 if (!crypto::SecureMemEqual(final_hash, required_file_hash.data(), |
| 165 crypto::kSHA256Length)) | 282 crypto::kSHA256Length)) |
| 166 return VerifierResult::ERROR_FILE_HASH_FAILED; | 283 return VerifierResult::ERROR_FILE_HASH_FAILED; |
| 167 } | 284 } |
| 168 return diff ? VerifierResult::OK_DELTA : VerifierResult::OK_FULL; | 285 return diff ? VerifierResult::OK_DELTA : VerifierResult::OK_FULL; |
| 169 } | 286 } |
| 170 | 287 |
| 171 } // namespace crx_file | 288 } // namespace crx_file |
| OLD | NEW |