• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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