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 <common/utils/file_util.h>
10 #include <common/utils/byte_size_literals.h>
12 #include <manifest_parser/utils/logging.h>
23 #include <system_error>
26 namespace ci = common_installer;
27 namespace fs = std::filesystem;
31 const std::size_t kEncryptionChunkMaxSize = 8_kB; // bytes
32 const std::set<std::string> encryptSet { ".html", ".htm", ".css", ".js"};
34 FILE* OpenFile(const fs::path& path, const std::string& mode) {
35 FILE* result = nullptr;
37 result = fopen(path.c_str(), mode.c_str());
38 if (result == nullptr)
39 LOG(ERROR) << "open [" << path << "] fail, errno : " << errno;
44 std::size_t ReadBytes(unsigned char* buffer, std::size_t count, FILE* stream) {
45 std::size_t result = std::fread(buffer,
49 if (result != count) {
50 if (0 != std::ferror(stream)) {
51 LOG(ERROR) << "Error while reading data";
58 void WriteBytes(unsigned char* buffer, std::size_t count, FILE* stream) {
59 // original file is treated as destination!
63 std::size_t bytes_written = 0;
65 std::size_t bytes_appended = std::fwrite(
66 buffer + bytes_written,
67 sizeof(unsigned char),
68 count - bytes_written,
70 if ((bytes_appended == 0)) {
71 LOG(ERROR) << "Error while writing data";
74 bytes_written += bytes_appended;
75 } while ((bytes_written < count));
82 namespace encryption {
84 ci::Step::Status StepEncryptResources::precheck() {
85 backend_data_ = static_cast<WgtBackendData*>(context_->backend_data.get());
87 LOG(ERROR) << "no backend data";
88 return ci::Step::Status::ERROR;
94 LOG(ERROR) << "unpacked_dir_path attribute is empty";
95 return Step::Status::INVALID_VALUE;
97 if (!fs::exists(input_)) {
98 LOG(ERROR) << "unpacked_dir_path (" << input_ << ") path does not exist";
99 return Step::Status::INVALID_VALUE;
102 return ci::Step::Status::OK;
105 ci::Step::Status StepEncryptResources::process() {
106 if (!backend_data_->settings.get().encryption_enabled()) {
107 LOG(DEBUG) << "no encryption";
108 return ci::Step::Status::OK;
110 LOG(DEBUG) << "Encrypting";
112 if (!Encrypt(input_)) {
113 LOG(ERROR) << "Error during encryption";
114 return ci::Step::Status::ERROR;
117 return ci::Step::Status::OK;
120 bool StepEncryptResources::Encrypt(const fs::path &src) {
121 // traversing through src dir (recurrence if subdir found)
122 // for every file found, check if it should be encrypted (ToBeEncrypted)
123 // if yes, encrypt it (and replace original one)
125 for (fs::directory_iterator file(src);
126 file != fs::directory_iterator();
128 std::error_code error;
129 fs::path current(file->path());
131 bool is_dir = fs::is_directory(current, error);
133 LOG(ERROR) << "Failed to check directory status: " << error.message();
137 if (!Encrypt(current))
142 bool is_sym = fs::is_symlink(symlink_status(current, error));
144 LOG(ERROR) << "Failed to check symlink status: " << error.message();
150 // it is regular file (not dir, not symlink)
151 if (ToBeEncrypted(current)) {
152 LOG(INFO) << "File for encryption: " << current;
153 if (!EncryptFile(current))
160 bool StepEncryptResources::EncryptFile(const fs::path &src) {
162 memset(&info, 0, sizeof(info));
165 FILE *input = OpenFile(src, "rb");
167 LOG(ERROR) << "Cannot open file for encryption: " << src;
172 if (fstat(fd, &info) != 0) {
173 LOG(ERROR) << "Could not access file " << src;
178 const std::size_t fileSize = info.st_size;
180 LOG(ERROR) << src << " size is 0, so encryption will be skipped";
185 fs::path encFile(src.string() + ".enc");
186 FILE *output = OpenFile(encFile, "wb");
188 LOG(ERROR) << "Cannot create encrypted file: " << encFile;
193 std::size_t chunkSize = (fileSize > kEncryptionChunkMaxSize ?
194 kEncryptionChunkMaxSize : fileSize);
196 std::unique_ptr<unsigned char[]> inChunk(new unsigned char[chunkSize]);
197 std::size_t bytesRead = 0;
200 bytesRead = ReadBytes(inChunk.get(), chunkSize, input);
201 if (0 != bytesRead) {
202 unsigned char* encrypted_data = nullptr;
203 size_t encrypted_size = 0;
204 // TODO(p.sikorski) check if it is Preloaded
206 if (context_->request_mode.get() == ci::RequestMode::GLOBAL) {
207 ret = wae_encrypt_global_web_application(
208 context_->pkgid.get().c_str(),
209 context_->is_readonly_package.get() ? true : false,
215 ret = wae_encrypt_web_application(
217 context_->pkgid.get().c_str(),
224 if (WAE_ERROR_NONE != ret) {
225 LOG(ERROR) << "Error during encrypting:";
227 case WAE_ERROR_INVALID_PARAMETER:
228 LOG(ERROR) << "WAE_ERROR_INVALID_PARAMETER";
230 case WAE_ERROR_PERMISSION_DENIED:
231 LOG(ERROR) << "WAE_ERROR_PERMISSION_DENIED";
233 case WAE_ERROR_NO_KEY:
234 LOG(ERROR) << "WAE_ERROR_NO_KEY";
236 case WAE_ERROR_KEY_MANAGER:
237 LOG(ERROR) << "WAE_ERROR_KEY_MANAGER";
239 case WAE_ERROR_CRYPTO:
240 LOG(ERROR) << "WAE_ERROR_CRYPTO";
242 case WAE_ERROR_UNKNOWN:
243 LOG(ERROR) << "WAE_ERROR_UNKNOWN";
246 LOG(ERROR) << "UNKNOWN";
254 if (encrypted_size <= 0) {
255 LOG(ERROR) << "Encryption Failed using TrustZone";
261 std::stringstream toString;
262 toString << encrypted_size;
264 WriteBytes((unsigned char*)toString.str().c_str(), sizeof(int), output);
265 WriteBytes((unsigned char*)encrypted_data, encrypted_size, output);
266 free(encrypted_data);
268 inChunk.reset(new unsigned char[chunkSize]);
269 } while (!std::feof(input));
273 LOG(DEBUG) << "File encrypted successfully";
274 if (0 != unlink(src.c_str())) {
280 LOG(DEBUG) << "Rename encrypted file";
281 if (0 != std::rename(encFile.c_str(), src.c_str()))
287 void StepEncryptResources::SetEncryptionRoot() {
288 input_ = context_->unpacked_dir_path.get();
291 bool StepEncryptResources::ToBeEncrypted(const fs::path &file) {
292 size_t found_key = file.string().rfind(".");
293 if (std::string::npos != found_key) {
294 std::string mimeType = file.string().substr(found_key);
295 std::transform(mimeType.begin(), mimeType.end(), mimeType.begin(),
297 return encryptSet.count(mimeType) > 0;
302 } // namespace encryption