| OLD | NEW |
| (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 "components/crx_file/crx2_file.h" |
| 14 #include "components/crx_file/id_util.h" |
| 15 #include "crypto/secure_hash.h" |
| 16 #include "crypto/secure_util.h" |
| 17 #include "crypto/sha2.h" |
| 18 #include "crypto/signature_verifier.h" |
| 19 |
| 20 namespace crx_file { |
| 21 |
| 22 namespace { |
| 23 |
| 24 // The maximum size the crx2 parser will tolerate for a public key. |
| 25 static const uint32_t kMaxPublicKeySize = 1 << 16; |
| 26 |
| 27 // The maximum size the crx2 parser will tolerate for a signature. |
| 28 static const uint32_t kMaxSignatureSize = 1 << 16; |
| 29 |
| 30 int ReadAndHashBuffer(uint8_t* buffer, |
| 31 size_t length, |
| 32 base::File* file, |
| 33 crypto::SecureHash* hash) { |
| 34 // We assume a char is 8 bits long. |
| 35 int read = file->ReadAtCurrentPos(reinterpret_cast<char*>(buffer), length); |
| 36 hash->Update(buffer, read); |
| 37 return read; |
| 38 } |
| 39 |
| 40 // Returns UINT32_MAX in the case of an unexpected EOF or read error. |
| 41 uint32_t ReadAndHashLittleEndianUInt32(base::File* file, |
| 42 crypto::SecureHash* hash) { |
| 43 uint8_t buffer[4]; |
| 44 if (ReadAndHashBuffer(buffer, 4, file, hash) != 4) |
| 45 return UINT32_MAX; |
| 46 return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0]; |
| 47 } |
| 48 |
| 49 } // namespace |
| 50 |
| 51 CrxVerifier::CrxVerifier() {} |
| 52 |
| 53 CrxVerifier::~CrxVerifier() {} |
| 54 |
| 55 void CrxVerifier::RequireKeyProof(const std::vector<uint8_t>& key_hash) { |
| 56 key_hashes_.push_back(key_hash); |
| 57 } |
| 58 |
| 59 void CrxVerifier::RequireFileHash(const std::vector<uint8_t>& expected_hash) { |
| 60 expected_hash_ = expected_hash; |
| 61 } |
| 62 |
| 63 void CrxVerifier::GetPublicKey(std::string* public_key) { |
| 64 public_key_ = public_key; |
| 65 } |
| 66 |
| 67 void CrxVerifier::GetCrxId(std::string* crx_id) { |
| 68 crx_id_ = crx_id; |
| 69 } |
| 70 |
| 71 void Crx3Verifier::RequirePublisherProof() { |
| 72 require_publisher_proof_ = true; |
| 73 } |
| 74 |
| 75 Crx3Verifier::Crx3Verifier() { |
| 76 allow_crx2_ = false; |
| 77 } |
| 78 |
| 79 Crx3Verifier::~Crx3Verifier() {} |
| 80 |
| 81 CrxVerifier::Result CrxVerifier::Verify(const base::FilePath& crx_path) const { |
| 82 base::File file(crx_path, base::File::FLAG_OPEN | base::File::FLAG_READ); |
| 83 if (!file.IsValid()) |
| 84 return Result::ERROR_FILE_NOT_READABLE; |
| 85 |
| 86 std::unique_ptr<crypto::SecureHash> file_hash = |
| 87 crypto::SecureHash::Create(crypto::SecureHash::SHA256); |
| 88 |
| 89 // Magic number. |
| 90 bool diff = false; |
| 91 char buffer[kCrx2FileHeaderMagicSize]; |
| 92 int read = file.ReadAtCurrentPos(buffer, kCrx2FileHeaderMagicSize); |
| 93 if (read != kCrx2FileHeaderMagicSize) |
| 94 return Result::ERROR_HEADER_INVALID; |
| 95 if (!strncmp(buffer, kCrxDiffFileHeaderMagic, kCrx2FileHeaderMagicSize)) |
| 96 diff = true; |
| 97 else if (strncmp(buffer, kCrx2FileHeaderMagic, kCrx2FileHeaderMagicSize)) |
| 98 return Result::ERROR_HEADER_INVALID; |
| 99 file_hash->Update(buffer, sizeof(buffer)); |
| 100 |
| 101 // Version number. |
| 102 uint32_t version = ReadAndHashLittleEndianUInt32(&file, file_hash.get()); |
| 103 Result result; |
| 104 if (allow_crx2_ && (version == 2 || (diff && version == 0))) |
| 105 result = VerifyCrx2(&file, file_hash.get()); |
| 106 else if (version == 3) |
| 107 result = VerifyCrx3(&file, file_hash.get()); |
| 108 else |
| 109 return Result::ERROR_HEADER_INVALID; |
| 110 if (result != Result::OK_FULL) |
| 111 return result; |
| 112 |
| 113 // Finalize file hash. |
| 114 uint8_t final_hash[crypto::kSHA256Length]; |
| 115 file_hash->Finish(final_hash, sizeof(final_hash)); |
| 116 if (!expected_hash_.empty()) { |
| 117 if (expected_hash_.size() != crypto::kSHA256Length) |
| 118 return Result::ERROR_EXPECTED_HASH_INVALID; |
| 119 if (!crypto::SecureMemEqual(final_hash, expected_hash_.data(), |
| 120 crypto::kSHA256Length)) |
| 121 return Result::ERROR_FILE_HASH_FAILED; |
| 122 } |
| 123 return diff ? Result::OK_DELTA : Result::OK_FULL; |
| 124 } |
| 125 |
| 126 CrxVerifier::Result CrxVerifier::VerifyCrx3(base::File* file, |
| 127 crypto::SecureHash* hash) const { |
| 128 // Crx3 files are not yet supported - treat this as a malformed header. |
| 129 return Result::ERROR_HEADER_INVALID; |
| 130 } |
| 131 |
| 132 CrxVerifier::Result CrxVerifier::VerifyCrx2(base::File* file, |
| 133 crypto::SecureHash* hash) const { |
| 134 uint32_t key_size = ReadAndHashLittleEndianUInt32(file, hash); |
| 135 if (key_size > kMaxPublicKeySize) |
| 136 return Result::ERROR_HEADER_INVALID; |
| 137 uint32_t sig_size = ReadAndHashLittleEndianUInt32(file, hash); |
| 138 if (sig_size > kMaxSignatureSize) |
| 139 return Result::ERROR_HEADER_INVALID; |
| 140 std::vector<uint8_t> key(key_size); |
| 141 if (ReadAndHashBuffer(key.data(), key_size, file, hash) != |
| 142 static_cast<int>(key_size)) |
| 143 return Result::ERROR_HEADER_INVALID; |
| 144 for (const auto& expected_hash : key_hashes_) { |
| 145 // In practice we expect zero or one key_hashes_ for CRX2 files. |
| 146 std::vector<uint8_t> hash(crypto::kSHA256Length); |
| 147 std::unique_ptr<crypto::SecureHash> sha256( |
| 148 crypto::SecureHash::Create(crypto::SecureHash::SHA256)); |
| 149 sha256->Update(key.data(), key.size()); |
| 150 sha256->Finish(hash.data(), hash.size()); |
| 151 if (hash != expected_hash) |
| 152 return Result::ERROR_REQUIRED_PROOF_MISSING; |
| 153 } |
| 154 |
| 155 std::vector<uint8_t> sig(sig_size); |
| 156 if (ReadAndHashBuffer(sig.data(), sig_size, file, hash) != |
| 157 static_cast<int>(sig_size)) |
| 158 return Result::ERROR_HEADER_INVALID; |
| 159 crypto::SignatureVerifier verifier; |
| 160 if (!verifier.VerifyInit(crypto::SignatureVerifier::RSA_PKCS1_SHA1, |
| 161 sig.data(), sig.size(), key.data(), key.size())) { |
| 162 return Result::ERROR_SIGNATURE_INITIALIZATION_FAILED; |
| 163 } |
| 164 |
| 165 // Read the rest of the file. |
| 166 uint8_t buffer[1 << 12] = {}; |
| 167 size_t len; |
| 168 while ((len = ReadAndHashBuffer(buffer, arraysize(buffer), file, hash)) > 0) |
| 169 verifier.VerifyUpdate(buffer, len); |
| 170 if (!verifier.VerifyFinal()) |
| 171 return Result::ERROR_SIGNATURE_VERIFICATION_FAILED; |
| 172 |
| 173 std::string public_key_bytes = |
| 174 std::string(reinterpret_cast<char*>(&key.front()), key.size()); |
| 175 if (public_key_) |
| 176 base::Base64Encode(public_key_bytes, public_key_); |
| 177 if (crx_id_) |
| 178 *crx_id_ = id_util::GenerateId(public_key_bytes); |
| 179 return Result::OK_FULL; |
| 180 } |
| 181 |
| 182 } // namespace crx_file |
| OLD | NEW |