| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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_file.h" | |
| 6 | |
| 7 #include "base/base64.h" | |
| 8 #include "base/files/file_path.h" | |
| 9 #include "base/files/file_util.h" | |
| 10 #include "base/files/scoped_file.h" | |
| 11 #include "base/macros.h" | |
| 12 #include "base/memory/ptr_util.h" | |
| 13 #include "base/numerics/safe_math.h" | |
| 14 #include "base/strings/string_number_conversions.h" | |
| 15 #include "base/strings/string_util.h" | |
| 16 #include "components/crx_file/id_util.h" | |
| 17 #include "crypto/secure_hash.h" | |
| 18 #include "crypto/sha2.h" | |
| 19 #include "crypto/signature_verifier.h" | |
| 20 | |
| 21 namespace crx_file { | |
| 22 | |
| 23 namespace { | |
| 24 | |
| 25 // The current version of the crx format. | |
| 26 static const uint32_t kCurrentVersion = 2; | |
| 27 | |
| 28 // The current version of the crx diff format. | |
| 29 static const uint32_t kCurrentDiffVersion = 0; | |
| 30 | |
| 31 // The maximum size the crx parser will tolerate for a public key. | |
| 32 static const uint32_t kMaxPublicKeySize = 1 << 16; | |
| 33 | |
| 34 // The maximum size the crx parser will tolerate for a signature. | |
| 35 static const uint32_t kMaxSignatureSize = 1 << 16; | |
| 36 | |
| 37 // Helper function to read bytes into a buffer while also updating a hash with | |
| 38 // those bytes. Returns the number of bytes read. | |
| 39 size_t ReadAndHash(void* ptr, | |
| 40 size_t size, | |
| 41 size_t nmemb, | |
| 42 FILE* stream, | |
| 43 crypto::SecureHash* hash) { | |
| 44 size_t item_count = fread(ptr, size, nmemb, stream); | |
| 45 base::CheckedNumeric<size_t> byte_count(item_count); | |
| 46 byte_count *= size; | |
| 47 if (!byte_count.IsValid()) | |
| 48 return 0; | |
| 49 if (item_count > 0 && hash) { | |
| 50 hash->Update(ptr, byte_count.ValueOrDie()); | |
| 51 } | |
| 52 return byte_count.ValueOrDie(); | |
| 53 } | |
| 54 | |
| 55 // Helper function to finish computing a hash and return an error if the | |
| 56 // result of the hash didn't meet an expected base64-encoded value. | |
| 57 CrxFile::ValidateError FinalizeHash(const std::string& extension_id, | |
| 58 crypto::SecureHash* hash, | |
| 59 const std::string& expected_hash) { | |
| 60 CHECK(hash != nullptr); | |
| 61 uint8_t output[crypto::kSHA256Length] = {}; | |
| 62 hash->Finish(output, sizeof(output)); | |
| 63 std::string hash_base64 = | |
| 64 base::ToLowerASCII(base::HexEncode(output, sizeof(output))); | |
| 65 if (hash_base64 != expected_hash) { | |
| 66 LOG(ERROR) << "Hash check failed for extension: " << extension_id | |
| 67 << ", expected " << expected_hash << ", got " << hash_base64; | |
| 68 return CrxFile::ValidateError::CRX_HASH_VERIFICATION_FAILED; | |
| 69 } else { | |
| 70 return CrxFile::ValidateError::NONE; | |
| 71 } | |
| 72 } | |
| 73 | |
| 74 } // namespace | |
| 75 | |
| 76 // The magic string embedded in the header. | |
| 77 const char kCrxFileHeaderMagic[] = "Cr24"; | |
| 78 const char kCrxDiffFileHeaderMagic[] = "CrOD"; | |
| 79 | |
| 80 std::unique_ptr<CrxFile> CrxFile::Parse(const CrxFile::Header& header, | |
| 81 CrxFile::Error* error) { | |
| 82 if (HeaderIsValid(header, error)) | |
| 83 return base::WrapUnique(new CrxFile(header)); | |
| 84 return nullptr; | |
| 85 } | |
| 86 | |
| 87 std::unique_ptr<CrxFile> CrxFile::Create(const uint32_t key_size, | |
| 88 const uint32_t signature_size, | |
| 89 CrxFile::Error* error) { | |
| 90 CrxFile::Header header; | |
| 91 memcpy(&header.magic, kCrxFileHeaderMagic, kCrxFileHeaderMagicSize); | |
| 92 header.version = kCurrentVersion; | |
| 93 header.key_size = key_size; | |
| 94 header.signature_size = signature_size; | |
| 95 if (HeaderIsValid(header, error)) | |
| 96 return base::WrapUnique(new CrxFile(header)); | |
| 97 return nullptr; | |
| 98 } | |
| 99 | |
| 100 bool CrxFile::HeaderIsDelta(const CrxFile::Header& header) { | |
| 101 return !strncmp(kCrxDiffFileHeaderMagic, header.magic, sizeof(header.magic)); | |
| 102 } | |
| 103 | |
| 104 // static | |
| 105 CrxFile::ValidateError CrxFile::ValidateSignature( | |
| 106 const base::FilePath& crx_path, | |
| 107 const std::string& expected_hash, | |
| 108 std::string* public_key, | |
| 109 std::string* extension_id, | |
| 110 CrxFile::Header* header_out) { | |
| 111 base::ScopedFILE file(base::OpenFile(crx_path, "rb")); | |
| 112 std::unique_ptr<crypto::SecureHash> hash; | |
| 113 if (!expected_hash.empty()) | |
| 114 hash = crypto::SecureHash::Create(crypto::SecureHash::SHA256); | |
| 115 | |
| 116 if (!file.get()) | |
| 117 return ValidateError::CRX_FILE_NOT_READABLE; | |
| 118 | |
| 119 CrxFile::Header header; | |
| 120 size_t len = ReadAndHash(&header, sizeof(header), 1, file.get(), hash.get()); | |
| 121 if (len != sizeof(header)) | |
| 122 return ValidateError::CRX_HEADER_INVALID; | |
| 123 if (header_out) | |
| 124 *header_out = header; | |
| 125 | |
| 126 CrxFile::Error error; | |
| 127 std::unique_ptr<CrxFile> crx(CrxFile::Parse(header, &error)); | |
| 128 if (!crx) { | |
| 129 switch (error) { | |
| 130 case CrxFile::kWrongMagic: | |
| 131 return ValidateError::CRX_MAGIC_NUMBER_INVALID; | |
| 132 case CrxFile::kInvalidVersion: | |
| 133 return ValidateError::CRX_VERSION_NUMBER_INVALID; | |
| 134 | |
| 135 case CrxFile::kInvalidKeyTooLarge: | |
| 136 case CrxFile::kInvalidSignatureTooLarge: | |
| 137 return ValidateError::CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE; | |
| 138 | |
| 139 case CrxFile::kInvalidKeyTooSmall: | |
| 140 return ValidateError::CRX_ZERO_KEY_LENGTH; | |
| 141 case CrxFile::kInvalidSignatureTooSmall: | |
| 142 return ValidateError::CRX_ZERO_SIGNATURE_LENGTH; | |
| 143 | |
| 144 default: | |
| 145 return ValidateError::CRX_HEADER_INVALID; | |
| 146 } | |
| 147 } | |
| 148 | |
| 149 std::vector<uint8_t> key(header.key_size); | |
| 150 len = ReadAndHash(&key.front(), sizeof(uint8_t), header.key_size, file.get(), | |
| 151 hash.get()); | |
| 152 if (len != header.key_size) | |
| 153 return ValidateError::CRX_PUBLIC_KEY_INVALID; | |
| 154 | |
| 155 std::vector<uint8_t> signature(header.signature_size); | |
| 156 len = ReadAndHash(&signature.front(), sizeof(uint8_t), header.signature_size, | |
| 157 file.get(), hash.get()); | |
| 158 if (len < header.signature_size) | |
| 159 return ValidateError::CRX_SIGNATURE_INVALID; | |
| 160 | |
| 161 crypto::SignatureVerifier verifier; | |
| 162 if (!verifier.VerifyInit(crypto::SignatureVerifier::RSA_PKCS1_SHA1, | |
| 163 signature.data(), signature.size(), key.data(), | |
| 164 key.size())) { | |
| 165 // Signature verification initialization failed. This is most likely | |
| 166 // caused by a public key in the wrong format (should encode algorithm). | |
| 167 return ValidateError::CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED; | |
| 168 } | |
| 169 | |
| 170 uint8_t buf[1 << 12] = {}; | |
| 171 while ((len = ReadAndHash(buf, sizeof(buf[0]), arraysize(buf), file.get(), | |
| 172 hash.get())) > 0) | |
| 173 verifier.VerifyUpdate(buf, len); | |
| 174 | |
| 175 if (!verifier.VerifyFinal()) | |
| 176 return ValidateError::CRX_SIGNATURE_VERIFICATION_FAILED; | |
| 177 | |
| 178 std::string public_key_bytes = | |
| 179 std::string(reinterpret_cast<char*>(&key.front()), key.size()); | |
| 180 if (public_key) | |
| 181 base::Base64Encode(public_key_bytes, public_key); | |
| 182 | |
| 183 std::string id = id_util::GenerateId(public_key_bytes); | |
| 184 if (extension_id) | |
| 185 *extension_id = id; | |
| 186 | |
| 187 if (!expected_hash.empty()) | |
| 188 return FinalizeHash(id, hash.get(), expected_hash); | |
| 189 | |
| 190 return ValidateError::NONE; | |
| 191 } | |
| 192 | |
| 193 CrxFile::CrxFile(const Header& header) : header_(header) {} | |
| 194 | |
| 195 bool CrxFile::HeaderIsValid(const CrxFile::Header& header, | |
| 196 CrxFile::Error* error) { | |
| 197 bool valid = false; | |
| 198 bool diffCrx = false; | |
| 199 if (!strncmp(kCrxDiffFileHeaderMagic, header.magic, sizeof(header.magic))) | |
| 200 diffCrx = true; | |
| 201 if (strncmp(kCrxFileHeaderMagic, header.magic, sizeof(header.magic)) && | |
| 202 !diffCrx) | |
| 203 *error = kWrongMagic; | |
| 204 else if (header.version != kCurrentVersion | |
| 205 && !(diffCrx && header.version == kCurrentDiffVersion)) | |
| 206 *error = kInvalidVersion; | |
| 207 else if (header.key_size > kMaxPublicKeySize) | |
| 208 *error = kInvalidKeyTooLarge; | |
| 209 else if (header.key_size == 0) | |
| 210 *error = kInvalidKeyTooSmall; | |
| 211 else if (header.signature_size > kMaxSignatureSize) | |
| 212 *error = kInvalidSignatureTooLarge; | |
| 213 else if (header.signature_size == 0) | |
| 214 *error = kInvalidSignatureTooSmall; | |
| 215 else | |
| 216 valid = true; | |
| 217 return valid; | |
| 218 } | |
| 219 | |
| 220 } // namespace crx_file | |
| OLD | NEW |