Home | History | Annotate | Download | only in extensions
      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 
     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 
     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 
    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 
    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 
    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 
    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 
    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