1 // Copyright (c) 2011 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 "chrome/browser/extensions/extension_creator.h"
6
7 #include <vector>
8 #include <string>
9
10 #include "base/file_util.h"
11 #include "base/memory/scoped_handle.h"
12 #include "base/memory/scoped_temp_dir.h"
13 #include "base/string_util.h"
14 #include "crypto/rsa_private_key.h"
15 #include "crypto/signature_creator.h"
16 #include "chrome/browser/extensions/sandboxed_extension_unpacker.h"
17 #include "chrome/common/extensions/extension.h"
18 #include "chrome/common/extensions/extension_file_util.h"
19 #include "chrome/common/zip.h"
20 #include "grit/generated_resources.h"
21 #include "ui/base/l10n/l10n_util.h"
22
23 namespace {
24 const int kRSAKeySize = 1024;
25 };
26
InitializeInput(const FilePath & extension_dir,const FilePath & private_key_path,const FilePath & private_key_output_path)27 bool ExtensionCreator::InitializeInput(
28 const FilePath& extension_dir,
29 const FilePath& private_key_path,
30 const FilePath& private_key_output_path) {
31 // Validate input |extension_dir|.
32 if (extension_dir.value().empty() ||
33 !file_util::DirectoryExists(extension_dir)) {
34 error_message_ =
35 l10n_util::GetStringUTF8(IDS_EXTENSION_DIRECTORY_NO_EXISTS);
36 return false;
37 }
38
39 FilePath absolute_extension_dir = extension_dir;
40 if (!file_util::AbsolutePath(&absolute_extension_dir)) {
41 error_message_ =
42 l10n_util::GetStringUTF8(IDS_EXTENSION_CANT_GET_ABSOLUTE_PATH);
43 return false;
44 }
45
46 // Validate input |private_key| (if provided).
47 if (!private_key_path.value().empty() &&
48 !file_util::PathExists(private_key_path)) {
49 error_message_ =
50 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_INVALID_PATH);
51 return false;
52 }
53
54 // If an |output_private_key| path is given, make sure it doesn't over-write
55 // an existing private key.
56 if (private_key_path.value().empty() &&
57 !private_key_output_path.value().empty() &&
58 file_util::PathExists(private_key_output_path)) {
59 error_message_ =
60 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_EXISTS);
61 return false;
62 }
63
64 // Load the extension once. We don't really need it, but this does a lot of
65 // useful validation of the structure.
66 scoped_refptr<Extension> extension(
67 extension_file_util::LoadExtension(absolute_extension_dir,
68 Extension::INTERNAL,
69 Extension::STRICT_ERROR_CHECKS,
70 &error_message_));
71 if (!extension.get())
72 return false; // LoadExtension already set error_message_.
73
74 return true;
75 }
76
ReadInputKey(const FilePath & private_key_path)77 crypto::RSAPrivateKey* ExtensionCreator::ReadInputKey(const FilePath&
78 private_key_path) {
79 if (!file_util::PathExists(private_key_path)) {
80 error_message_ =
81 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_NO_EXISTS);
82 return NULL;
83 }
84
85 std::string private_key_contents;
86 if (!file_util::ReadFileToString(private_key_path,
87 &private_key_contents)) {
88 error_message_ =
89 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_READ);
90 return NULL;
91 }
92
93 std::string private_key_bytes;
94 if (!Extension::ParsePEMKeyBytes(private_key_contents,
95 &private_key_bytes)) {
96 error_message_ =
97 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_INVALID);
98 return NULL;
99 }
100
101 return crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(
102 std::vector<uint8>(private_key_bytes.begin(), private_key_bytes.end()));
103 }
104
GenerateKey(const FilePath & output_private_key_path)105 crypto::RSAPrivateKey* ExtensionCreator::GenerateKey(const FilePath&
106 output_private_key_path) {
107 scoped_ptr<crypto::RSAPrivateKey> key_pair(
108 crypto::RSAPrivateKey::Create(kRSAKeySize));
109 if (!key_pair.get()) {
110 error_message_ =
111 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_GENERATE);
112 return NULL;
113 }
114
115 std::vector<uint8> private_key_vector;
116 if (!key_pair->ExportPrivateKey(&private_key_vector)) {
117 error_message_ =
118 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_EXPORT);
119 return NULL;
120 }
121 std::string private_key_bytes(
122 reinterpret_cast<char*>(&private_key_vector.front()),
123 private_key_vector.size());
124
125 std::string private_key;
126 if (!Extension::ProducePEM(private_key_bytes, &private_key)) {
127 error_message_ =
128 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_OUTPUT);
129 return NULL;
130 }
131 std::string pem_output;
132 if (!Extension::FormatPEMForFileOutput(private_key, &pem_output,
133 false)) {
134 error_message_ =
135 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_OUTPUT);
136 return NULL;
137 }
138
139 if (!output_private_key_path.empty()) {
140 if (-1 == file_util::WriteFile(output_private_key_path,
141 pem_output.c_str(), pem_output.size())) {
142 error_message_ =
143 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_OUTPUT);
144 return NULL;
145 }
146 }
147
148 return key_pair.release();
149 }
150
CreateZip(const FilePath & extension_dir,const FilePath & temp_path,FilePath * zip_path)151 bool ExtensionCreator::CreateZip(const FilePath& extension_dir,
152 const FilePath& temp_path,
153 FilePath* zip_path) {
154 *zip_path = temp_path.Append(FILE_PATH_LITERAL("extension.zip"));
155
156 if (!Zip(extension_dir, *zip_path, false)) { // no hidden files
157 error_message_ =
158 l10n_util::GetStringUTF8(IDS_EXTENSION_FAILED_DURING_PACKAGING);
159 return false;
160 }
161
162 return true;
163 }
164
SignZip(const FilePath & zip_path,crypto::RSAPrivateKey * private_key,std::vector<uint8> * signature)165 bool ExtensionCreator::SignZip(const FilePath& zip_path,
166 crypto::RSAPrivateKey* private_key,
167 std::vector<uint8>* signature) {
168 scoped_ptr<crypto::SignatureCreator> signature_creator(
169 crypto::SignatureCreator::Create(private_key));
170 ScopedStdioHandle zip_handle(file_util::OpenFile(zip_path, "rb"));
171 size_t buffer_size = 1 << 16;
172 scoped_array<uint8> buffer(new uint8[buffer_size]);
173 int bytes_read = -1;
174 while ((bytes_read = fread(buffer.get(), 1, buffer_size,
175 zip_handle.get())) > 0) {
176 if (!signature_creator->Update(buffer.get(), bytes_read)) {
177 error_message_ =
178 l10n_util::GetStringUTF8(IDS_EXTENSION_ERROR_WHILE_SIGNING);
179 return false;
180 }
181 }
182 zip_handle.Close();
183
184 signature_creator->Final(signature);
185 return true;
186 }
187
WriteCRX(const FilePath & zip_path,crypto::RSAPrivateKey * private_key,const std::vector<uint8> & signature,const FilePath & crx_path)188 bool ExtensionCreator::WriteCRX(const FilePath& zip_path,
189 crypto::RSAPrivateKey* private_key,
190 const std::vector<uint8>& signature,
191 const FilePath& crx_path) {
192 if (file_util::PathExists(crx_path))
193 file_util::Delete(crx_path, false);
194 ScopedStdioHandle crx_handle(file_util::OpenFile(crx_path, "wb"));
195
196 std::vector<uint8> public_key;
197 if (!private_key->ExportPublicKey(&public_key)) {
198 error_message_ =
199 l10n_util::GetStringUTF8(IDS_EXTENSION_PUBLIC_KEY_FAILED_TO_EXPORT);
200 return false;
201 }
202
203 SandboxedExtensionUnpacker::ExtensionHeader header;
204 memcpy(&header.magic, SandboxedExtensionUnpacker::kExtensionHeaderMagic,
205 SandboxedExtensionUnpacker::kExtensionHeaderMagicSize);
206 header.version = SandboxedExtensionUnpacker::kCurrentVersion;
207 header.key_size = public_key.size();
208 header.signature_size = signature.size();
209
210 if (fwrite(&header, sizeof(SandboxedExtensionUnpacker::ExtensionHeader), 1,
211 crx_handle.get()) != 1) {
212 PLOG(ERROR) << "fwrite failed to write header";
213 }
214 if (fwrite(&public_key.front(), sizeof(uint8), public_key.size(),
215 crx_handle.get()) != public_key.size()) {
216 PLOG(ERROR) << "fwrite failed to write public_key.front";
217 }
218 if (fwrite(&signature.front(), sizeof(uint8), signature.size(),
219 crx_handle.get()) != signature.size()) {
220 PLOG(ERROR) << "fwrite failed to write signature.front";
221 }
222
223 size_t buffer_size = 1 << 16;
224 scoped_array<uint8> buffer(new uint8[buffer_size]);
225 size_t bytes_read = 0;
226 ScopedStdioHandle zip_handle(file_util::OpenFile(zip_path, "rb"));
227 while ((bytes_read = fread(buffer.get(), 1, buffer_size,
228 zip_handle.get())) > 0) {
229 if (fwrite(buffer.get(), sizeof(char), bytes_read, crx_handle.get()) !=
230 bytes_read) {
231 PLOG(ERROR) << "fwrite failed to write buffer";
232 }
233 }
234
235 return true;
236 }
237
Run(const FilePath & extension_dir,const FilePath & crx_path,const FilePath & private_key_path,const FilePath & output_private_key_path)238 bool ExtensionCreator::Run(const FilePath& extension_dir,
239 const FilePath& crx_path,
240 const FilePath& private_key_path,
241 const FilePath& output_private_key_path) {
242 // Check input diretory and read manifest.
243 if (!InitializeInput(extension_dir, private_key_path,
244 output_private_key_path)) {
245 return false;
246 }
247
248 // Initialize Key Pair
249 scoped_ptr<crypto::RSAPrivateKey> key_pair;
250 if (!private_key_path.value().empty())
251 key_pair.reset(ReadInputKey(private_key_path));
252 else
253 key_pair.reset(GenerateKey(output_private_key_path));
254 if (!key_pair.get())
255 return false;
256
257 ScopedTempDir temp_dir;
258 if (!temp_dir.CreateUniqueTempDir())
259 return false;
260
261 // Zip up the extension.
262 FilePath zip_path;
263 std::vector<uint8> signature;
264 bool result = false;
265 if (CreateZip(extension_dir, temp_dir.path(), &zip_path) &&
266 SignZip(zip_path, key_pair.get(), &signature) &&
267 WriteCRX(zip_path, key_pair.get(), signature, crx_path)) {
268 result = true;
269 }
270
271 file_util::Delete(zip_path, false);
272 return result;
273 }
274