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
23namespace {
24  const int kRSAKeySize = 1024;
25};
26
27bool 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
77crypto::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
105crypto::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
151bool 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
165bool 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
188bool 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
238bool 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