OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 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/crypto/rsa_private_key.h" |
| 11 #include "base/crypto/signature_creator.h" |
| 12 #include "base/file_util.h" |
| 13 #include "base/scoped_handle.h" |
| 14 #include "base/string_util.h" |
| 15 #include "chrome/browser/extensions/extensions_service.h" |
| 16 #include "chrome/common/extensions/extension.h" |
| 17 #include "chrome/common/extensions/extension_error_reporter.h" |
| 18 #include "chrome/common/chrome_switches.h" |
| 19 #include "chrome/common/extensions/extension.h" |
| 20 #include "chrome/common/json_value_serializer.h" |
| 21 #include "chrome/common/zip.h" |
| 22 #include "net/base/base64.h" |
| 23 |
| 24 namespace { |
| 25 const int kRSAKeySize = 1024; |
| 26 }; |
| 27 |
| 28 DictionaryValue* ExtensionCreator::InitializeInput( |
| 29 const FilePath& extension_dir, |
| 30 const FilePath& private_key_path, |
| 31 const FilePath& private_key_output_path) { |
| 32 // Validate input |extension_dir|. |
| 33 if (extension_dir.value().empty() || |
| 34 !file_util::DirectoryExists(extension_dir)) { |
| 35 error_message_ = "Input directory must exist."; |
| 36 return false; |
| 37 } |
| 38 |
| 39 // Validate input |private_key| (if provided). |
| 40 if (!private_key_path.value().empty() && |
| 41 !file_util::PathExists(private_key_path)) { |
| 42 error_message_ = "Input value for private key must be a valid path."; |
| 43 return false; |
| 44 } |
| 45 |
| 46 // If an |output_private_key| path is given, make sure it doesn't over-write |
| 47 // an existing private key. |
| 48 if (private_key_path.value().empty() && |
| 49 !private_key_output_path.value().empty() && |
| 50 file_util::PathExists(private_key_output_path)) { |
| 51 error_message_ = "Private key exists next to input directory. Try using " |
| 52 "--pack-extension-key"; |
| 53 return false; |
| 54 } |
| 55 |
| 56 // Read the manifest. |
| 57 FilePath manifest_path = extension_dir.AppendASCII( |
| 58 Extension::kManifestFilename); |
| 59 if (!file_util::PathExists(manifest_path)) { |
| 60 error_message_ = "Extension must contain '"; |
| 61 error_message_.append(Extension::kManifestFilename); |
| 62 error_message_.append("'."); |
| 63 return false; |
| 64 } |
| 65 |
| 66 JSONFileValueSerializer serializer(manifest_path); |
| 67 std::string serialization_error; |
| 68 Value* input_manifest = (serializer.Deserialize(&serialization_error)); |
| 69 if (!input_manifest) { |
| 70 error_message_ = "Invalid manifest.json: "; |
| 71 error_message_.append(serialization_error); |
| 72 return false; |
| 73 } |
| 74 |
| 75 if (!input_manifest->IsType(Value::TYPE_DICTIONARY)) { |
| 76 error_message_ = "Invalid manifest.json"; |
| 77 return false; |
| 78 } |
| 79 |
| 80 return static_cast<DictionaryValue*>(input_manifest); |
| 81 } |
| 82 |
| 83 base::RSAPrivateKey* ExtensionCreator::ReadInputKey(const FilePath& |
| 84 private_key_path) { |
| 85 if (!file_util::PathExists(private_key_path)) { |
| 86 error_message_ = "Input value for private key must exist."; |
| 87 return false; |
| 88 } |
| 89 |
| 90 std::string private_key_contents; |
| 91 if (!file_util::ReadFileToString(private_key_path, |
| 92 &private_key_contents)) { |
| 93 error_message_ = "Failed to read private key."; |
| 94 return false; |
| 95 } |
| 96 |
| 97 std::string private_key_bytes; |
| 98 if (!Extension::ParsePEMKeyBytes(private_key_contents, |
| 99 &private_key_bytes)) { |
| 100 error_message_ = "Invalid private key."; |
| 101 return false; |
| 102 } |
| 103 |
| 104 return base::RSAPrivateKey::CreateFromPrivateKeyInfo( |
| 105 std::vector<uint8>(private_key_bytes.begin(), private_key_bytes.end())); |
| 106 } |
| 107 |
| 108 base::RSAPrivateKey* ExtensionCreator::GenerateKey(const FilePath& |
| 109 output_private_key_path) { |
| 110 scoped_ptr<base::RSAPrivateKey> key_pair( |
| 111 base::RSAPrivateKey::Create(kRSAKeySize)); |
| 112 if (!key_pair.get()) { |
| 113 error_message_ = "Yikes! Failed to generate random RSA private key."; |
| 114 return NULL; |
| 115 } |
| 116 |
| 117 std::vector<uint8> private_key_vector; |
| 118 if (!key_pair->ExportPrivateKey(&private_key_vector)) { |
| 119 error_message_ = "Failed to export private key."; |
| 120 return NULL; |
| 121 } |
| 122 std::string private_key_bytes(private_key_vector.begin(), |
| 123 private_key_vector.end()); |
| 124 |
| 125 std::string private_key; |
| 126 if (!Extension::ProducePEM(private_key_bytes, &private_key)) { |
| 127 error_message_ = "Failed to output private key."; |
| 128 return NULL; |
| 129 } |
| 130 std::string pem_output; |
| 131 if (!Extension::FormatPEMForFileOutput(private_key, &pem_output, |
| 132 false)) { |
| 133 error_message_ = "Failed to output private key."; |
| 134 return NULL; |
| 135 } |
| 136 |
| 137 if (!output_private_key_path.empty()) { |
| 138 if (-1 == file_util::WriteFile(output_private_key_path, |
| 139 pem_output.c_str(), pem_output.size())) { |
| 140 error_message_ = "Failed to write private key."; |
| 141 return NULL; |
| 142 } |
| 143 } |
| 144 |
| 145 return key_pair.release(); |
| 146 } |
| 147 |
| 148 bool ExtensionCreator::CreateAndSignZip(const FilePath& extension_dir, |
| 149 base::RSAPrivateKey *key_pair, |
| 150 FilePath* zip_path, |
| 151 std::string* signature) { |
| 152 file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("chrome_"), zip_path); |
| 153 *zip_path = zip_path->Append(FILE_PATH_LITERAL("extension.zip")); |
| 154 |
| 155 if (!Zip(extension_dir, *zip_path)) { |
| 156 error_message_ = "Failed to create temporary zip file during packaging."; |
| 157 return false; |
| 158 } |
| 159 |
| 160 scoped_ptr<base::SignatureCreator> signature_creator( |
| 161 base::SignatureCreator::Create(key_pair)); |
| 162 ScopedStdioHandle zip_handle(file_util::OpenFile(*zip_path, "rb")); |
| 163 uint8 buffer[1 << 16]; |
| 164 int bytes_read = -1; |
| 165 while ((bytes_read = fread(buffer, 1, sizeof(buffer), |
| 166 zip_handle.get())) > 0) { |
| 167 if (!signature_creator->Update(buffer, bytes_read)) { |
| 168 error_message_ = "Error while signing extension."; |
| 169 return false; |
| 170 } |
| 171 } |
| 172 zip_handle.Close(); |
| 173 |
| 174 std::vector<uint8> signature_vector; |
| 175 signature_creator->Final(&signature_vector); |
| 176 std::string signature_bytes(signature_vector.begin(), signature_vector.end()); |
| 177 bool result = net::Base64Encode(signature_bytes, signature); |
| 178 DCHECK(result); |
| 179 return true; |
| 180 } |
| 181 |
| 182 bool ExtensionCreator::PrepareManifestForExport(base::RSAPrivateKey *key_pair, |
| 183 const std::string& signature, |
| 184 DictionaryValue* manifest) { |
| 185 std::vector<uint8> public_key_vector; |
| 186 if (!key_pair->ExportPublicKey(&public_key_vector)) { |
| 187 error_message_ = "Failed to export public key."; |
| 188 return false; |
| 189 } |
| 190 |
| 191 std::string public_key_bytes(public_key_vector.begin(), |
| 192 public_key_vector.end()); |
| 193 std::string public_key; |
| 194 if (!net::Base64Encode(public_key_bytes, &public_key)) { |
| 195 error_message_ = "Error while signing extension."; |
| 196 return false; |
| 197 } |
| 198 |
| 199 manifest->SetString(Extension::kSignatureKey, signature); |
| 200 manifest->SetString(Extension::kPublicKeyKey, public_key); |
| 201 |
| 202 return true; |
| 203 } |
| 204 |
| 205 bool ExtensionCreator::WriteCRX(const FilePath& crx_path, |
| 206 DictionaryValue* manifest, |
| 207 const FilePath& zip_path) { |
| 208 std::string manifest_string; |
| 209 JSONStringValueSerializer serializer(&manifest_string); |
| 210 if (!serializer.Serialize(*manifest)) { |
| 211 error_message_ = "Failed to write crx."; |
| 212 return false; |
| 213 } |
| 214 |
| 215 if (file_util::PathExists(crx_path)) |
| 216 file_util::Delete(crx_path, false); |
| 217 ScopedStdioHandle crx_handle(file_util::OpenFile(crx_path, "wb")); |
| 218 |
| 219 ExtensionsService::ExtensionHeader header; |
| 220 memcpy(&header.magic, ExtensionsService::kExtensionFileMagic, |
| 221 sizeof(ExtensionsService::kExtensionFileMagic)); |
| 222 header.version = 1; // kExpectedVersion |
| 223 header.header_size = sizeof(ExtensionsService::ExtensionHeader); |
| 224 header.manifest_size = manifest_string.size(); |
| 225 |
| 226 fwrite(&header, sizeof(ExtensionsService::ExtensionHeader), 1, |
| 227 crx_handle.get()); |
| 228 fwrite(manifest_string.c_str(), sizeof(char), manifest_string.size(), |
| 229 crx_handle.get()); |
| 230 |
| 231 uint8 buffer[1 << 16]; |
| 232 int bytes_read = -1; |
| 233 ScopedStdioHandle zip_handle(file_util::OpenFile(zip_path, "rb")); |
| 234 while ((bytes_read = fread(buffer, 1, sizeof(buffer), |
| 235 zip_handle.get())) > 0) { |
| 236 fwrite(buffer, sizeof(char), bytes_read, crx_handle.get()); |
| 237 } |
| 238 |
| 239 return true; |
| 240 } |
| 241 |
| 242 bool ExtensionCreator::Run(const FilePath& extension_dir, |
| 243 const FilePath& crx_path, |
| 244 const FilePath& private_key_path, |
| 245 const FilePath& output_private_key_path) { |
| 246 // Check input diretory and read manifest. |
| 247 scoped_ptr<DictionaryValue> manifest(InitializeInput(extension_dir, |
| 248 private_key_path, output_private_key_path)); |
| 249 if (!manifest.get()) |
| 250 return false; |
| 251 |
| 252 // Initialize Key Pair |
| 253 scoped_ptr<base::RSAPrivateKey> key_pair; |
| 254 if (!private_key_path.value().empty()) |
| 255 key_pair.reset(ReadInputKey(private_key_path)); |
| 256 else |
| 257 key_pair.reset(GenerateKey(output_private_key_path)); |
| 258 if (!key_pair.get()) |
| 259 return false; |
| 260 |
| 261 // Zip up the extension. |
| 262 FilePath zip_path; |
| 263 std::string signature; |
| 264 if (!CreateAndSignZip(extension_dir, key_pair.get(), &zip_path, &signature)) |
| 265 return false; |
| 266 |
| 267 if (!PrepareManifestForExport(key_pair.get(), signature, manifest.get())) |
| 268 return false; |
| 269 |
| 270 // Write the final crx out to disk. |
| 271 if (!WriteCRX(crx_path, manifest.get(), zip_path)) |
| 272 return false; |
| 273 |
| 274 return true; |
| 275 } |
OLD | NEW |