Chromium Code Reviews| 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 "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 | |
| OLD | NEW |