1 // Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
2 // Use of this source code is governed by a apache 2.0 license that can be
3 // found in the LICENSE file.
5 #include "wgt/step/encryption/step_encrypt_resources.h"
7 #include <web_app_enc.h>
9 #include <boost/filesystem/operations.hpp>
10 #include <boost/system/error_code.hpp>
12 #include <common/utils/file_util.h>
13 #include <common/utils/byte_size_literals.h>
15 #include <manifest_parser/utils/logging.h>
26 const std::size_t kEncryptionChunkMaxSize = 8_kB; // bytes
27 const std::set<std::string> encryptSet { ".html", ".htm", ".css", ".js"};
29 FILE* OpenFile(const std::string& path, const std::string& mode) {
30 FILE* result = nullptr;
33 result = fopen(path.c_str(), mode.c_str());
34 } while ((nullptr == result));
39 std::size_t ReadBytes(unsigned char* buffer, std::size_t count, FILE* stream) {
40 std::size_t result = std::fread(buffer,
44 if (result != count) {
45 if (0 != std::ferror(stream)) {
46 LOG(ERROR) << "Error while reading data";
53 void WriteBytes(unsigned char* buffer, std::size_t count, FILE* stream) {
54 // original file is treated as destination!
58 std::size_t bytes_written = 0;
60 std::size_t bytes_appended = std::fwrite(
61 buffer + bytes_written,
62 sizeof(unsigned char),
63 count - bytes_written,
65 if ((bytes_appended == 0)) {
66 LOG(ERROR) << "Error while writing data";
69 bytes_written += bytes_appended;
70 } while ((bytes_written < count));
77 namespace encryption {
79 namespace bf = boost::filesystem;
80 namespace bs = boost::system;
82 common_installer::Step::Status StepEncryptResources::precheck() {
83 backend_data_ = static_cast<WgtBackendData*>(context_->backend_data.get());
85 LOG(ERROR) << "no backend data";
86 return common_installer::Step::Status::ERROR;
92 LOG(ERROR) << "unpacked_dir_path attribute is empty";
93 return Step::Status::INVALID_VALUE;
95 if (!bf::exists(input_)) {
96 LOG(ERROR) << "unpacked_dir_path (" << input_ << ") path does not exist";
97 return Step::Status::INVALID_VALUE;
100 return common_installer::Step::Status::OK;
103 common_installer::Step::Status StepEncryptResources::process() {
104 if (!backend_data_->settings.get().encryption_enabled()) {
105 LOG(DEBUG) << "no encryption";
106 return common_installer::Step::Status::OK;
108 LOG(DEBUG) << "Encrypting";
110 if (!Encrypt(input_)) {
111 LOG(ERROR) << "Error during encryption";
112 return common_installer::Step::Status::ERROR;
115 return common_installer::Step::Status::OK;
118 bool StepEncryptResources::Encrypt(const bf::path &src) {
119 // traversing through src dir (recurrence if subdir found)
120 // for every file found, check if it should be encrypted (ToBeEncrypted)
121 // if yes, encrypt it (and replace original one)
123 for (bf::directory_iterator file(src);
124 file != bf::directory_iterator();
126 bs::error_code error_code;
127 bf::path current(file->path());
129 bool is_dir = bf::is_directory(current, error_code);
133 if (!Encrypt(current))
138 bool is_sym = bf::is_symlink(symlink_status(current, error_code));
144 // it is regular file (not dir, not symlink)
145 if (ToBeEncrypted(current)) {
146 LOG(INFO) << "File for encryption: " << current;
147 if (!EncryptFile(current))
154 bool StepEncryptResources::EncryptFile(const bf::path &src) {
155 bf::path encFile(src.string() + ".enc");
157 memset(&info, 0, sizeof(info));
158 if (stat(src.string().c_str(), &info) != 0) {
159 LOG(ERROR) << "Could not access file " << src.string();
162 const std::size_t fileSize = info.st_size;
164 LOG(ERROR) << src.string().c_str() << " size is 0, so encryption is skiped";
168 FILE *input = OpenFile(src.string().c_str(), "rb");
169 if (input == nullptr) {
170 LOG(ERROR) << "Cannot open file for encryption: " << src.string();
174 FILE *output = OpenFile(encFile.string().c_str(), "wb");
175 if (output == nullptr) {
176 LOG(ERROR) << "Cannot create encrypted file: " << encFile.string();
181 std::size_t chunkSize = (fileSize > kEncryptionChunkMaxSize
182 ? kEncryptionChunkMaxSize : fileSize);
184 std::unique_ptr<unsigned char[]> inChunk(new unsigned char[chunkSize]);
185 std::size_t bytesRead = 0;
188 bytesRead = ReadBytes(inChunk.get(), chunkSize, input);
189 if (0 != bytesRead) {
190 unsigned char* encrypted_data = nullptr;
191 size_t encrypted_size = 0;
192 // TODO(p.sikorski) check if it is Preloaded
194 if (context_->request_mode.get()
195 == common_installer::RequestMode::GLOBAL) {
196 ret = wae_encrypt_global_web_application(
197 context_->pkgid.get().c_str(),
198 context_->is_readonly_package.get() ?
205 ret = wae_encrypt_web_application(
207 context_->pkgid.get().c_str(),
214 if (WAE_ERROR_NONE != ret) {
215 LOG(ERROR) << "Error during encrypting:";
217 case WAE_ERROR_INVALID_PARAMETER:
218 LOG(ERROR) << "WAE_ERROR_INVALID_PARAMETER";
220 case WAE_ERROR_PERMISSION_DENIED:
221 LOG(ERROR) << "WAE_ERROR_PERMISSION_DENIED";
223 case WAE_ERROR_NO_KEY:
224 LOG(ERROR) << "WAE_ERROR_NO_KEY";
226 case WAE_ERROR_KEY_MANAGER:
227 LOG(ERROR) << "WAE_ERROR_KEY_MANAGER";
229 case WAE_ERROR_CRYPTO:
230 LOG(ERROR) << "WAE_ERROR_CRYPTO";
232 case WAE_ERROR_UNKNOWN:
233 LOG(ERROR) << "WAE_ERROR_UNKNOWN";
236 LOG(ERROR) << "UNKNOWN";
244 if (encrypted_size <= 0) {
245 LOG(ERROR) << "Encryption Failed using TrustZone";
251 std::stringstream toString;
252 toString << encrypted_size;
254 WriteBytes((unsigned char*)toString.str().c_str(), sizeof(int), output);
255 WriteBytes((unsigned char*)encrypted_data, encrypted_size, output);
256 free(encrypted_data);
258 inChunk.reset(new unsigned char[chunkSize]);
259 } while (!std::feof(input));
264 LOG(DEBUG) << "File encrypted successfully";
265 if (0 != unlink(src.string().c_str())) {
269 LOG(DEBUG) << "Rename encrypted file";
270 if (0 != std::rename(encFile.c_str(), src.string().c_str())) {
277 void StepEncryptResources::SetEncryptionRoot() {
278 input_ = context_->unpacked_dir_path.get();
281 bool StepEncryptResources::ToBeEncrypted(const bf::path &file) {
282 size_t found_key = file.string().rfind(".");
283 if (std::string::npos != found_key) {
284 std::string mimeType = file.string().substr(found_key);
285 std::transform(mimeType.begin(), mimeType.end(), mimeType.begin(),
287 return encryptSet.count(mimeType) > 0;
292 } // namespace encryption