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 |