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 |