1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/crx_file/crx_creator.h"
7 #include "base/files/file.h"
8 #include "base/files/file_path.h"
9 #include "base/logging.h"
10 #include "components/crx_file/crx3.pb.h"
11 #include "components/crx_file/crx_file.h"
12 #include "crypto/rsa_private_key.h"
13 #include "crypto/sha2.h"
14 #include "crypto/signature_creator.h"
20 std::string GetCrxId(const std::string& key) {
21 uint8_t hash[16] = {}; // CRX IDs are 16 bytes long.
22 crypto::SHA256HashString(key, hash, sizeof(hash));
23 static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size.");
24 return std::string(reinterpret_cast<char*>(hash), sizeof(hash));
27 // Read to the end of the file, updating the signer.
28 CreatorResult ReadAndSignArchive(base::File* file,
29 crypto::SignatureCreator* signer,
30 std::vector<uint8_t>* signature) {
31 uint8_t buffer[1 << 12] = {};
33 static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size.");
34 while ((read = file->ReadAtCurrentPos(reinterpret_cast<char*>(buffer),
35 std::size(buffer))) > 0) {
36 if (!signer->Update(buffer, read))
37 return CreatorResult::ERROR_SIGNING_FAILURE;
40 return CreatorResult::ERROR_SIGNING_FAILURE;
41 return signer->Final(signature) ? CreatorResult::OK
42 : CreatorResult::ERROR_SIGNING_FAILURE;
45 bool WriteBuffer(base::File* file, const char buffer[], int len) {
46 return file->WriteAtCurrentPos(buffer, len) == len;
49 bool WriteArchive(base::File* out, base::File* in) {
50 char buffer[1 << 12] = {};
52 in->Seek(base::File::Whence::FROM_BEGIN, 0);
53 while ((read = in->ReadAtCurrentPos(buffer, std::size(buffer))) > 0) {
54 if (out->WriteAtCurrentPos(buffer, read) != read)
60 CreatorResult SignArchiveAndCreateHeader(const base::FilePath& output_path,
62 crypto::RSAPrivateKey* signing_key,
63 CrxFileHeader* header) {
64 // Get the public key.
65 std::vector<uint8_t> public_key;
66 signing_key->ExportPublicKey(&public_key);
67 const std::string public_key_str(public_key.begin(), public_key.end());
69 // Assemble SignedData section.
70 SignedData signed_header_data;
71 signed_header_data.set_crx_id(GetCrxId(public_key_str));
72 const std::string signed_header_data_str =
73 signed_header_data.SerializeAsString();
74 const int signed_header_size = signed_header_data_str.size();
75 const uint8_t signed_header_size_octets[] = {
76 static_cast<uint8_t>(signed_header_size),
77 static_cast<uint8_t>(signed_header_size >> 8),
78 static_cast<uint8_t>(signed_header_size >> 16),
79 static_cast<uint8_t>(signed_header_size >> 24)};
81 // Create a signer, init with purpose, SignedData length, run SignedData
82 // through, run ZIP through.
83 auto signer = crypto::SignatureCreator::Create(
84 signing_key, crypto::SignatureCreator::HashAlgorithm::SHA256);
85 signer->Update(reinterpret_cast<const uint8_t*>(kSignatureContext),
86 std::size(kSignatureContext));
87 signer->Update(signed_header_size_octets,
88 std::size(signed_header_size_octets));
90 reinterpret_cast<const uint8_t*>(signed_header_data_str.data()),
91 signed_header_data_str.size());
94 return CreatorResult::ERROR_FILE_NOT_READABLE;
95 std::vector<uint8_t> signature;
96 const CreatorResult signing_result =
97 ReadAndSignArchive(file, signer.get(), &signature);
98 if (signing_result != CreatorResult::OK)
99 return signing_result;
100 AsymmetricKeyProof* proof = header->add_sha256_with_rsa();
101 proof->set_public_key(public_key_str);
102 proof->set_signature(std::string(signature.begin(), signature.end()));
103 header->set_signed_header_data(signed_header_data_str);
104 return CreatorResult::OK;
107 CreatorResult WriteCRX(const CrxFileHeader& header,
108 const base::FilePath& output_path,
110 const std::string header_str = header.SerializeAsString();
111 const int header_size = header_str.size();
112 const uint8_t header_size_octets[] = {
113 static_cast<uint8_t>(header_size), static_cast<uint8_t>(header_size >> 8),
114 static_cast<uint8_t>(header_size >> 16),
115 static_cast<uint8_t>(header_size >> 24)};
117 const uint8_t format_version_octets[] = {3, 0, 0, 0};
118 base::File crx(output_path,
119 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
121 return CreatorResult::ERROR_FILE_NOT_WRITABLE;
122 static_assert(sizeof(char) == sizeof(uint8_t), "Unsupported char size.");
123 if (!WriteBuffer(&crx, kCrxFileHeaderMagic, kCrxFileHeaderMagicSize) ||
124 !WriteBuffer(&crx, reinterpret_cast<const char*>(format_version_octets),
125 std::size(format_version_octets)) ||
126 !WriteBuffer(&crx, reinterpret_cast<const char*>(header_size_octets),
127 std::size(header_size_octets)) ||
128 !WriteBuffer(&crx, header_str.c_str(), header_str.length()) ||
129 !WriteArchive(&crx, file)) {
130 return CreatorResult::ERROR_FILE_WRITE_FAILURE;
132 return CreatorResult::OK;
137 CreatorResult CreateCrxWithVerifiedContentsInHeader(
138 const base::FilePath& output_path,
139 const base::FilePath& zip_path,
140 crypto::RSAPrivateKey* signing_key,
141 const std::string& verified_contents) {
142 CrxFileHeader header;
143 base::File file(zip_path, base::File::FLAG_OPEN | base::File::FLAG_READ |
144 base::File::FLAG_WIN_SHARE_DELETE);
145 PLOG_IF(ERROR, !file.IsValid())
146 << "Failed to open " << zip_path << ": " << file.error_details();
147 const CreatorResult signing_result =
148 SignArchiveAndCreateHeader(output_path, &file, signing_key, &header);
149 if (signing_result != CreatorResult::OK)
150 return signing_result;
152 // Inject the verified contents into the header.
153 header.set_verified_contents(verified_contents);
154 const CreatorResult result = WriteCRX(header, output_path, &file);
158 CreatorResult Create(const base::FilePath& output_path,
159 const base::FilePath& zip_path,
160 crypto::RSAPrivateKey* signing_key) {
161 CrxFileHeader header;
162 base::File file(zip_path, base::File::FLAG_OPEN | base::File::FLAG_READ |
163 base::File::FLAG_WIN_SHARE_DELETE);
164 PLOG_IF(ERROR, !file.IsValid())
165 << "Failed to open " << zip_path << ": " << file.error_details();
166 const CreatorResult signing_result =
167 SignArchiveAndCreateHeader(output_path, &file, signing_key, &header);
168 if (signing_result != CreatorResult::OK)
169 return signing_result;
171 const CreatorResult result = WriteCRX(header, output_path, &file);
175 } // namespace crx_file