blob: 1eb90d4b1d375321c093ed67ec3ac6cb9bf977d7 [file] [log] [blame]
// Copyright 2017 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 "components/crx_file/crx_creator.h"
#include <string>
#include <vector>
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/stl_util.h"
#include "components/crx_file/crx3.pb.h"
#include "components/crx_file/crx_file.h"
#include "crypto/rsa_private_key.h"
#include "crypto/sha2.h"
#include "crypto/signature_creator.h"
namespace crx_file {
namespace {
std::string GetCrxId(const std::string& key) {
uint8_t hash[16] = {}; // CRX IDs are 16 bytes long.
crypto::SHA256HashString(key, hash, sizeof(hash));
static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size.");
return std::string(reinterpret_cast<char*>(hash), sizeof(hash));
}
// Read to the end of the file, updating the signer.
CreatorResult ReadAndSignArchive(base::File* file,
crypto::SignatureCreator* signer,
std::vector<uint8_t>* signature) {
uint8_t buffer[1 << 12] = {};
int read = 0;
static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size.");
while ((read = file->ReadAtCurrentPos(reinterpret_cast<char*>(buffer),
base::size(buffer))) > 0) {
if (!signer->Update(buffer, read))
return CreatorResult::ERROR_SIGNING_FAILURE;
}
if (read < 0)
return CreatorResult::ERROR_SIGNING_FAILURE;
return signer->Final(signature) ? CreatorResult::OK
: CreatorResult::ERROR_SIGNING_FAILURE;
}
bool WriteBuffer(base::File* file, const char buffer[], int len) {
return file->WriteAtCurrentPos(buffer, len) == len;
}
bool WriteArchive(base::File* out, base::File* in) {
char buffer[1 << 12] = {};
int read = 0;
in->Seek(base::File::Whence::FROM_BEGIN, 0);
while ((read = in->ReadAtCurrentPos(buffer, base::size(buffer))) > 0) {
if (out->WriteAtCurrentPos(buffer, read) != read)
return false;
}
return read == 0;
}
} // namespace
CreatorResult Create(const base::FilePath& output_path,
const base::FilePath& zip_path,
crypto::RSAPrivateKey* signing_key) {
// Get the public key.
std::vector<uint8_t> public_key;
signing_key->ExportPublicKey(&public_key);
const std::string public_key_str(public_key.begin(), public_key.end());
// Assemble SignedData section.
SignedData signed_header_data;
signed_header_data.set_crx_id(GetCrxId(public_key_str));
const std::string signed_header_data_str =
signed_header_data.SerializeAsString();
const int signed_header_size = signed_header_data_str.size();
const uint8_t signed_header_size_octets[] = {
signed_header_size, signed_header_size >> 8, signed_header_size >> 16,
signed_header_size >> 24};
// Create a signer, init with purpose, SignedData length, run SignedData
// through, run ZIP through.
auto signer = crypto::SignatureCreator::Create(
signing_key, crypto::SignatureCreator::HashAlgorithm::SHA256);
signer->Update(kSignatureContext, base::size(kSignatureContext));
signer->Update(signed_header_size_octets,
base::size(signed_header_size_octets));
signer->Update(
reinterpret_cast<const uint8_t*>(signed_header_data_str.data()),
signed_header_data_str.size());
base::File file(zip_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file.IsValid())
return CreatorResult::ERROR_FILE_NOT_READABLE;
std::vector<uint8_t> signature;
const CreatorResult signing_result =
ReadAndSignArchive(&file, signer.get(), &signature);
if (signing_result != CreatorResult::OK)
return signing_result;
// Create CRXFileHeader.
CrxFileHeader header;
AsymmetricKeyProof* proof = header.add_sha256_with_rsa();
proof->set_public_key(public_key_str);
proof->set_signature(std::string(signature.begin(), signature.end()));
header.set_signed_header_data(signed_header_data_str);
const std::string header_str = header.SerializeAsString();
const int header_size = header_str.size();
const uint8_t header_size_octets[] = {header_size, header_size >> 8,
header_size >> 16, header_size >> 24};
// Write CRX.
const uint8_t format_version_octets[] = {3, 0, 0, 0};
base::File crx(output_path,
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
if (!crx.IsValid())
return CreatorResult::ERROR_FILE_NOT_WRITABLE;
static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size.");
if (!WriteBuffer(&crx, kCrxFileHeaderMagic, kCrxFileHeaderMagicSize) ||
!WriteBuffer(&crx, reinterpret_cast<const char*>(format_version_octets),
base::size(format_version_octets)) ||
!WriteBuffer(&crx, reinterpret_cast<const char*>(header_size_octets),
base::size(header_size_octets)) ||
!WriteBuffer(&crx, header_str.c_str(), header_str.length()) ||
!WriteArchive(&crx, &file)) {
return CreatorResult::ERROR_FILE_WRITE_FAILURE;
}
return CreatorResult::OK;
}
} // namespace crx_file