| Index: chrome/browser/extensions/extension_creator.cc
|
| diff --git a/chrome/browser/extensions/extension_creator.cc b/chrome/browser/extensions/extension_creator.cc
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..d5400e0632c8bff107609d4fd3eef659d8ef37c8
|
| --- /dev/null
|
| +++ b/chrome/browser/extensions/extension_creator.cc
|
| @@ -0,0 +1,275 @@
|
| +// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome/browser/extensions/extension_creator.h"
|
| +
|
| +#include <vector>
|
| +#include <string>
|
| +
|
| +#include "base/crypto/rsa_private_key.h"
|
| +#include "base/crypto/signature_creator.h"
|
| +#include "base/file_util.h"
|
| +#include "base/scoped_handle.h"
|
| +#include "base/string_util.h"
|
| +#include "chrome/browser/extensions/extensions_service.h"
|
| +#include "chrome/common/extensions/extension.h"
|
| +#include "chrome/common/extensions/extension_error_reporter.h"
|
| +#include "chrome/common/chrome_switches.h"
|
| +#include "chrome/common/extensions/extension.h"
|
| +#include "chrome/common/json_value_serializer.h"
|
| +#include "chrome/common/zip.h"
|
| +#include "net/base/base64.h"
|
| +
|
| +namespace {
|
| + const int kRSAKeySize = 1024;
|
| +};
|
| +
|
| +DictionaryValue* ExtensionCreator::InitializeInput(
|
| + const FilePath& extension_dir,
|
| + const FilePath& private_key_path,
|
| + const FilePath& private_key_output_path) {
|
| + // Validate input |extension_dir|.
|
| + if (extension_dir.value().empty() ||
|
| + !file_util::DirectoryExists(extension_dir)) {
|
| + error_message_ = "Input directory must exist.";
|
| + return false;
|
| + }
|
| +
|
| + // Validate input |private_key| (if provided).
|
| + if (!private_key_path.value().empty() &&
|
| + !file_util::PathExists(private_key_path)) {
|
| + error_message_ = "Input value for private key must be a valid path.";
|
| + return false;
|
| + }
|
| +
|
| + // If an |output_private_key| path is given, make sure it doesn't over-write
|
| + // an existing private key.
|
| + if (private_key_path.value().empty() &&
|
| + !private_key_output_path.value().empty() &&
|
| + file_util::PathExists(private_key_output_path)) {
|
| + error_message_ = "Private key exists next to input directory. Try using "
|
| + "--pack-extension-key";
|
| + return false;
|
| + }
|
| +
|
| + // Read the manifest.
|
| + FilePath manifest_path = extension_dir.AppendASCII(
|
| + Extension::kManifestFilename);
|
| + if (!file_util::PathExists(manifest_path)) {
|
| + error_message_ = "Extension must contain '";
|
| + error_message_.append(Extension::kManifestFilename);
|
| + error_message_.append("'.");
|
| + return false;
|
| + }
|
| +
|
| + JSONFileValueSerializer serializer(manifest_path);
|
| + std::string serialization_error;
|
| + Value* input_manifest = (serializer.Deserialize(&serialization_error));
|
| + if (!input_manifest) {
|
| + error_message_ = "Invalid manifest.json: ";
|
| + error_message_.append(serialization_error);
|
| + return false;
|
| + }
|
| +
|
| + if (!input_manifest->IsType(Value::TYPE_DICTIONARY)) {
|
| + error_message_ = "Invalid manifest.json";
|
| + return false;
|
| + }
|
| +
|
| + return static_cast<DictionaryValue*>(input_manifest);
|
| +}
|
| +
|
| +base::RSAPrivateKey* ExtensionCreator::ReadInputKey(const FilePath&
|
| + private_key_path) {
|
| + if (!file_util::PathExists(private_key_path)) {
|
| + error_message_ = "Input value for private key must exist.";
|
| + return false;
|
| + }
|
| +
|
| + std::string private_key_contents;
|
| + if (!file_util::ReadFileToString(private_key_path,
|
| + &private_key_contents)) {
|
| + error_message_ = "Failed to read private key.";
|
| + return false;
|
| + }
|
| +
|
| + std::string private_key_bytes;
|
| + if (!Extension::ParsePEMKeyBytes(private_key_contents,
|
| + &private_key_bytes)) {
|
| + error_message_ = "Invalid private key.";
|
| + return false;
|
| + }
|
| +
|
| + return base::RSAPrivateKey::CreateFromPrivateKeyInfo(
|
| + std::vector<uint8>(private_key_bytes.begin(), private_key_bytes.end()));
|
| +}
|
| +
|
| +base::RSAPrivateKey* ExtensionCreator::GenerateKey(const FilePath&
|
| + output_private_key_path) {
|
| + scoped_ptr<base::RSAPrivateKey> key_pair(
|
| + base::RSAPrivateKey::Create(kRSAKeySize));
|
| + if (!key_pair.get()) {
|
| + error_message_ = "Yikes! Failed to generate random RSA private key.";
|
| + return NULL;
|
| + }
|
| +
|
| + std::vector<uint8> private_key_vector;
|
| + if (!key_pair->ExportPrivateKey(&private_key_vector)) {
|
| + error_message_ = "Failed to export private key.";
|
| + return NULL;
|
| + }
|
| + std::string private_key_bytes(private_key_vector.begin(),
|
| + private_key_vector.end());
|
| +
|
| + std::string private_key;
|
| + if (!Extension::ProducePEM(private_key_bytes, &private_key)) {
|
| + error_message_ = "Failed to output private key.";
|
| + return NULL;
|
| + }
|
| + std::string pem_output;
|
| + if (!Extension::FormatPEMForFileOutput(private_key, &pem_output,
|
| + false)) {
|
| + error_message_ = "Failed to output private key.";
|
| + return NULL;
|
| + }
|
| +
|
| + if (!output_private_key_path.empty()) {
|
| + if (-1 == file_util::WriteFile(output_private_key_path,
|
| + pem_output.c_str(), pem_output.size())) {
|
| + error_message_ = "Failed to write private key.";
|
| + return NULL;
|
| + }
|
| + }
|
| +
|
| + return key_pair.release();
|
| +}
|
| +
|
| +bool ExtensionCreator::CreateAndSignZip(const FilePath& extension_dir,
|
| + base::RSAPrivateKey *key_pair,
|
| + FilePath* zip_path,
|
| + std::string* signature) {
|
| + file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("chrome_"), zip_path);
|
| + *zip_path = zip_path->Append(FILE_PATH_LITERAL("extension.zip"));
|
| +
|
| + if (!Zip(extension_dir, *zip_path)) {
|
| + error_message_ = "Failed to create temporary zip file during packaging.";
|
| + return false;
|
| + }
|
| +
|
| + scoped_ptr<base::SignatureCreator> signature_creator(
|
| + base::SignatureCreator::Create(key_pair));
|
| + ScopedStdioHandle zip_handle(file_util::OpenFile(*zip_path, "rb"));
|
| + uint8 buffer[1 << 16];
|
| + int bytes_read = -1;
|
| + while ((bytes_read = fread(buffer, 1, sizeof(buffer),
|
| + zip_handle.get())) > 0) {
|
| + if (!signature_creator->Update(buffer, bytes_read)) {
|
| + error_message_ = "Error while signing extension.";
|
| + return false;
|
| + }
|
| + }
|
| + zip_handle.Close();
|
| +
|
| + std::vector<uint8> signature_vector;
|
| + signature_creator->Final(&signature_vector);
|
| + std::string signature_bytes(signature_vector.begin(), signature_vector.end());
|
| + bool result = net::Base64Encode(signature_bytes, signature);
|
| + DCHECK(result);
|
| + return true;
|
| +}
|
| +
|
| +bool ExtensionCreator::PrepareManifestForExport(base::RSAPrivateKey *key_pair,
|
| + const std::string& signature,
|
| + DictionaryValue* manifest) {
|
| + std::vector<uint8> public_key_vector;
|
| + if (!key_pair->ExportPublicKey(&public_key_vector)) {
|
| + error_message_ = "Failed to export public key.";
|
| + return false;
|
| + }
|
| +
|
| + std::string public_key_bytes(public_key_vector.begin(),
|
| + public_key_vector.end());
|
| + std::string public_key;
|
| + if (!net::Base64Encode(public_key_bytes, &public_key)) {
|
| + error_message_ = "Error while signing extension.";
|
| + return false;
|
| + }
|
| +
|
| + manifest->SetString(Extension::kSignatureKey, signature);
|
| + manifest->SetString(Extension::kPublicKeyKey, public_key);
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool ExtensionCreator::WriteCRX(const FilePath& crx_path,
|
| + DictionaryValue* manifest,
|
| + const FilePath& zip_path) {
|
| + std::string manifest_string;
|
| + JSONStringValueSerializer serializer(&manifest_string);
|
| + if (!serializer.Serialize(*manifest)) {
|
| + error_message_ = "Failed to write crx.";
|
| + return false;
|
| + }
|
| +
|
| + if (file_util::PathExists(crx_path))
|
| + file_util::Delete(crx_path, false);
|
| + ScopedStdioHandle crx_handle(file_util::OpenFile(crx_path, "wb"));
|
| +
|
| + ExtensionsService::ExtensionHeader header;
|
| + memcpy(&header.magic, ExtensionsService::kExtensionFileMagic,
|
| + sizeof(ExtensionsService::kExtensionFileMagic));
|
| + header.version = 1; // kExpectedVersion
|
| + header.header_size = sizeof(ExtensionsService::ExtensionHeader);
|
| + header.manifest_size = manifest_string.size();
|
| +
|
| + fwrite(&header, sizeof(ExtensionsService::ExtensionHeader), 1,
|
| + crx_handle.get());
|
| + fwrite(manifest_string.c_str(), sizeof(char), manifest_string.size(),
|
| + crx_handle.get());
|
| +
|
| + uint8 buffer[1 << 16];
|
| + int bytes_read = -1;
|
| + ScopedStdioHandle zip_handle(file_util::OpenFile(zip_path, "rb"));
|
| + while ((bytes_read = fread(buffer, 1, sizeof(buffer),
|
| + zip_handle.get())) > 0) {
|
| + fwrite(buffer, sizeof(char), bytes_read, crx_handle.get());
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool ExtensionCreator::Run(const FilePath& extension_dir,
|
| + const FilePath& crx_path,
|
| + const FilePath& private_key_path,
|
| + const FilePath& output_private_key_path) {
|
| + // Check input diretory and read manifest.
|
| + scoped_ptr<DictionaryValue> manifest(InitializeInput(extension_dir,
|
| + private_key_path, output_private_key_path));
|
| + if (!manifest.get())
|
| + return false;
|
| +
|
| + // Initialize Key Pair
|
| + scoped_ptr<base::RSAPrivateKey> key_pair;
|
| + if (!private_key_path.value().empty())
|
| + key_pair.reset(ReadInputKey(private_key_path));
|
| + else
|
| + key_pair.reset(GenerateKey(output_private_key_path));
|
| + if (!key_pair.get())
|
| + return false;
|
| +
|
| + // Zip up the extension.
|
| + FilePath zip_path;
|
| + std::string signature;
|
| + if (!CreateAndSignZip(extension_dir, key_pair.get(), &zip_path, &signature))
|
| + return false;
|
| +
|
| + if (!PrepareManifestForExport(key_pair.get(), signature, manifest.get()))
|
| + return false;
|
| +
|
| + // Write the final crx out to disk.
|
| + if (!WriteCRX(crx_path, manifest.get(), zip_path))
|
| + return false;
|
| +
|
| + return true;
|
| +}
|
|
|