1 // Copyright (c) 2012 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.
5 #include "chrome/browser/extensions/extension_creator.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_file.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/strings/string_util.h"
16 #include "chrome/browser/extensions/extension_creator_filter.h"
17 #include "chrome/grit/generated_resources.h"
18 #include "components/crx_file/crx_file.h"
19 #include "components/crx_file/id_util.h"
20 #include "crypto/rsa_private_key.h"
21 #include "crypto/signature_creator.h"
22 #include "extensions/common/extension.h"
23 #include "extensions/common/file_util.h"
24 #include "third_party/zlib/google/zip.h"
25 #include "ui/base/l10n/l10n_util.h"
28 const int kRSAKeySize
= 2048;
31 namespace extensions
{
33 ExtensionCreator::ExtensionCreator() : error_type_(kOtherError
) {
36 bool ExtensionCreator::InitializeInput(
37 const base::FilePath
& extension_dir
,
38 const base::FilePath
& crx_path
,
39 const base::FilePath
& private_key_path
,
40 const base::FilePath
& private_key_output_path
,
42 // Validate input |extension_dir|.
43 if (extension_dir
.value().empty() ||
44 !base::DirectoryExists(extension_dir
)) {
46 l10n_util::GetStringUTF8(IDS_EXTENSION_DIRECTORY_NO_EXISTS
);
50 base::FilePath absolute_extension_dir
=
51 base::MakeAbsoluteFilePath(extension_dir
);
52 if (absolute_extension_dir
.empty()) {
54 l10n_util::GetStringUTF8(IDS_EXTENSION_CANT_GET_ABSOLUTE_PATH
);
58 // Validate input |private_key| (if provided).
59 if (!private_key_path
.value().empty() &&
60 !base::PathExists(private_key_path
)) {
62 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_INVALID_PATH
);
66 // If an |output_private_key| path is given, make sure it doesn't over-write
67 // an existing private key.
68 if (private_key_path
.value().empty() &&
69 !private_key_output_path
.value().empty() &&
70 base::PathExists(private_key_output_path
)) {
72 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_EXISTS
);
76 // Check whether crx file already exists. Should be last check, as this is
78 if (!(run_flags
& kOverwriteCRX
) && base::PathExists(crx_path
)) {
79 error_message_
= l10n_util::GetStringUTF8(IDS_EXTENSION_CRX_EXISTS
);
80 error_type_
= kCRXExists
;
88 bool ExtensionCreator::ValidateManifest(const base::FilePath
& extension_dir
,
89 crypto::RSAPrivateKey
* key_pair
,
91 std::vector
<uint8
> public_key_bytes
;
92 if (!key_pair
->ExportPublicKey(&public_key_bytes
)) {
94 l10n_util::GetStringUTF8(IDS_EXTENSION_PUBLIC_KEY_FAILED_TO_EXPORT
);
98 std::string public_key
;
99 public_key
.insert(public_key
.begin(),
100 public_key_bytes
.begin(), public_key_bytes
.end());
102 std::string extension_id
= crx_file::id_util::GenerateId(public_key
);
104 // Load the extension once. We don't really need it, but this does a lot of
105 // useful validation of the structure.
107 Extension::FOLLOW_SYMLINKS_ANYWHERE
| Extension::ERROR_ON_PRIVATE_KEY
;
108 if (run_flags
& kRequireModernManifestVersion
)
109 create_flags
|= Extension::REQUIRE_MODERN_MANIFEST_VERSION
;
111 scoped_refptr
<Extension
> extension(
112 file_util::LoadExtension(extension_dir
,
117 return !!extension
.get();
120 crypto::RSAPrivateKey
* ExtensionCreator::ReadInputKey(const base::FilePath
&
122 if (!base::PathExists(private_key_path
)) {
124 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_NO_EXISTS
);
128 std::string private_key_contents
;
129 if (!base::ReadFileToString(private_key_path
, &private_key_contents
)) {
131 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_READ
);
135 std::string private_key_bytes
;
136 if (!Extension::ParsePEMKeyBytes(private_key_contents
,
137 &private_key_bytes
)) {
139 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_INVALID
);
143 return crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(
144 std::vector
<uint8
>(private_key_bytes
.begin(), private_key_bytes
.end()));
147 crypto::RSAPrivateKey
* ExtensionCreator::GenerateKey(const base::FilePath
&
148 output_private_key_path
) {
149 scoped_ptr
<crypto::RSAPrivateKey
> key_pair(
150 crypto::RSAPrivateKey::Create(kRSAKeySize
));
153 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_GENERATE
);
157 std::vector
<uint8
> private_key_vector
;
158 if (!key_pair
->ExportPrivateKey(&private_key_vector
)) {
160 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_EXPORT
);
163 std::string
private_key_bytes(
164 reinterpret_cast<char*>(&private_key_vector
.front()),
165 private_key_vector
.size());
167 std::string private_key
;
168 if (!Extension::ProducePEM(private_key_bytes
, &private_key
)) {
170 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_OUTPUT
);
173 std::string pem_output
;
174 if (!Extension::FormatPEMForFileOutput(private_key
, &pem_output
,
177 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_OUTPUT
);
181 if (!output_private_key_path
.empty()) {
182 if (-1 == base::WriteFile(output_private_key_path
,
183 pem_output
.c_str(), pem_output
.size())) {
185 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_OUTPUT
);
190 return key_pair
.release();
193 bool ExtensionCreator::CreateZip(const base::FilePath
& extension_dir
,
194 const base::FilePath
& temp_path
,
195 base::FilePath
* zip_path
) {
196 *zip_path
= temp_path
.Append(FILE_PATH_LITERAL("extension.zip"));
198 scoped_refptr
<ExtensionCreatorFilter
> filter
= new ExtensionCreatorFilter();
199 const base::Callback
<bool(const base::FilePath
&)>& filter_cb
=
200 base::Bind(&ExtensionCreatorFilter::ShouldPackageFile
, filter
.get());
201 if (!zip::ZipWithFilterCallback(extension_dir
, *zip_path
, filter_cb
)) {
203 l10n_util::GetStringUTF8(IDS_EXTENSION_FAILED_DURING_PACKAGING
);
210 bool ExtensionCreator::SignZip(const base::FilePath
& zip_path
,
211 crypto::RSAPrivateKey
* private_key
,
212 std::vector
<uint8
>* signature
) {
213 scoped_ptr
<crypto::SignatureCreator
> signature_creator(
214 crypto::SignatureCreator::Create(private_key
,
215 crypto::SignatureCreator::SHA1
));
216 base::ScopedFILE
zip_handle(base::OpenFile(zip_path
, "rb"));
217 size_t buffer_size
= 1 << 16;
218 scoped_ptr
<uint8
[]> buffer(new uint8
[buffer_size
]);
220 while ((bytes_read
= fread(buffer
.get(), 1, buffer_size
,
221 zip_handle
.get())) > 0) {
222 if (!signature_creator
->Update(buffer
.get(), bytes_read
)) {
224 l10n_util::GetStringUTF8(IDS_EXTENSION_ERROR_WHILE_SIGNING
);
230 if (!signature_creator
->Final(signature
)) {
232 l10n_util::GetStringUTF8(IDS_EXTENSION_ERROR_WHILE_SIGNING
);
238 bool ExtensionCreator::WriteCRX(const base::FilePath
& zip_path
,
239 crypto::RSAPrivateKey
* private_key
,
240 const std::vector
<uint8
>& signature
,
241 const base::FilePath
& crx_path
) {
242 if (base::PathExists(crx_path
))
243 base::DeleteFile(crx_path
, false);
244 base::ScopedFILE
crx_handle(base::OpenFile(crx_path
, "wb"));
245 if (!crx_handle
.get()) {
246 error_message_
= l10n_util::GetStringUTF8(IDS_EXTENSION_SHARING_VIOLATION
);
250 std::vector
<uint8
> public_key
;
251 CHECK(private_key
->ExportPublicKey(&public_key
));
253 crx_file::CrxFile::Error error
;
254 scoped_ptr
<crx_file::CrxFile
> crx(
255 crx_file::CrxFile::Create(public_key
.size(), signature
.size(), &error
));
257 LOG(ERROR
) << "cannot create CrxFileHeader: " << error
;
259 const crx_file::CrxFile::Header header
= crx
->header();
261 if (fwrite(&header
, sizeof(header
), 1, crx_handle
.get()) != 1) {
262 PLOG(ERROR
) << "fwrite failed to write header";
264 if (fwrite(&public_key
.front(), sizeof(uint8
), public_key
.size(),
265 crx_handle
.get()) != public_key
.size()) {
266 PLOG(ERROR
) << "fwrite failed to write public_key.front";
268 if (fwrite(&signature
.front(), sizeof(uint8
), signature
.size(),
269 crx_handle
.get()) != signature
.size()) {
270 PLOG(ERROR
) << "fwrite failed to write signature.front";
273 size_t buffer_size
= 1 << 16;
274 scoped_ptr
<uint8
[]> buffer(new uint8
[buffer_size
]);
275 size_t bytes_read
= 0;
276 base::ScopedFILE
zip_handle(base::OpenFile(zip_path
, "rb"));
277 while ((bytes_read
= fread(buffer
.get(), 1, buffer_size
,
278 zip_handle
.get())) > 0) {
279 if (fwrite(buffer
.get(), sizeof(char), bytes_read
, crx_handle
.get()) !=
281 PLOG(ERROR
) << "fwrite failed to write buffer";
288 bool ExtensionCreator::Run(const base::FilePath
& extension_dir
,
289 const base::FilePath
& crx_path
,
290 const base::FilePath
& private_key_path
,
291 const base::FilePath
& output_private_key_path
,
293 // Check input diretory and read manifest.
294 if (!InitializeInput(extension_dir
, crx_path
, private_key_path
,
295 output_private_key_path
, run_flags
)) {
299 // Initialize Key Pair
300 scoped_ptr
<crypto::RSAPrivateKey
> key_pair
;
301 if (!private_key_path
.value().empty())
302 key_pair
.reset(ReadInputKey(private_key_path
));
304 key_pair
.reset(GenerateKey(output_private_key_path
));
308 // Perform some extra validation by loading the extension.
309 // TODO(aa): Can this go before creating the key pair? This would mean not
310 // passing ID into LoadExtension which seems OK.
311 if (!ValidateManifest(extension_dir
, key_pair
.get(), run_flags
))
314 base::ScopedTempDir temp_dir
;
315 if (!temp_dir
.CreateUniqueTempDir())
318 // Zip up the extension.
319 base::FilePath zip_path
;
320 std::vector
<uint8
> signature
;
322 if (CreateZip(extension_dir
, temp_dir
.path(), &zip_path
) &&
323 SignZip(zip_path
, key_pair
.get(), &signature
) &&
324 WriteCRX(zip_path
, key_pair
.get(), signature
, crx_path
)) {
328 base::DeleteFile(zip_path
, false);
332 } // namespace extensions