Remove boost dependency
[platform/core/appfw/wgt-backend.git] / src / wgt / step / encryption / step_encrypt_resources.cc
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.
4
5 #include "wgt/step/encryption/step_encrypt_resources.h"
6
7 #include <web_app_enc.h>
8
9 #include <common/utils/file_util.h>
10 #include <common/utils/byte_size_literals.h>
11
12 #include <manifest_parser/utils/logging.h>
13
14 #include <sys/stat.h>
15 #include <unistd.h>
16
17 #include <algorithm>
18 #include <cstdio>
19 #include <cstdlib>
20 #include <filesystem>
21 #include <set>
22 #include <string>
23 #include <system_error>
24 #include <memory>
25
26 namespace ci = common_installer;
27 namespace fs = std::filesystem;
28
29 namespace {
30
31 const std::size_t kEncryptionChunkMaxSize = 8_kB;  // bytes
32 const std::set<std::string> encryptSet { ".html", ".htm", ".css", ".js"};
33
34 FILE* OpenFile(const fs::path& path, const std::string& mode) {
35   FILE* result = nullptr;
36
37   result = fopen(path.c_str(), mode.c_str());
38   if (result == nullptr)
39     LOG(ERROR) << "open [" << path << "] fail, errno : " << errno;
40
41   return result;
42 }
43
44 std::size_t ReadBytes(unsigned char* buffer, std::size_t count, FILE* stream) {
45   std::size_t result = std::fread(buffer,
46                                   1,
47                                   count,
48                                   stream);
49   if (result != count) {
50     if (0 != std::ferror(stream)) {
51       LOG(ERROR) << "Error while reading data";
52     }
53   }
54
55   return result;
56 }
57
58 void WriteBytes(unsigned char* buffer, std::size_t count, FILE* stream) {
59   // original file is treated as destination!
60   if (count <= 0) {
61     return;
62   }
63   std::size_t bytes_written = 0;
64   do {
65      std::size_t bytes_appended = std::fwrite(
66          buffer + bytes_written,
67          sizeof(unsigned char),
68          count - bytes_written,
69          stream);
70     if ((bytes_appended == 0)) {
71       LOG(ERROR) << "Error while writing data";
72       return;
73     }
74     bytes_written += bytes_appended;
75   } while ((bytes_written < count));
76 }
77
78 }  // namespace
79
80
81 namespace wgt {
82 namespace encryption {
83
84 ci::Step::Status StepEncryptResources::precheck() {
85   backend_data_ = static_cast<WgtBackendData*>(context_->backend_data.get());
86   if (!backend_data_) {
87     LOG(ERROR) << "no backend data";
88     return ci::Step::Status::ERROR;
89   }
90
91   SetEncryptionRoot();
92
93   if (input_.empty()) {
94     LOG(ERROR) << "unpacked_dir_path attribute is empty";
95     return Step::Status::INVALID_VALUE;
96   }
97   if (!fs::exists(input_)) {
98     LOG(ERROR) << "unpacked_dir_path (" << input_ << ") path does not exist";
99     return Step::Status::INVALID_VALUE;
100   }
101
102   return ci::Step::Status::OK;
103 }
104
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;
109   }
110   LOG(DEBUG) << "Encrypting";
111
112   if (!Encrypt(input_)) {
113     LOG(ERROR) << "Error during encryption";
114     return ci::Step::Status::ERROR;
115   }
116
117   return ci::Step::Status::OK;
118 }
119
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)
124   // if not, leave it
125   for (fs::directory_iterator file(src);
126        file != fs::directory_iterator();
127        ++file) {
128     std::error_code error;
129     fs::path current(file->path());
130
131     bool is_dir = fs::is_directory(current, error);
132     if (error) {
133       LOG(ERROR) << "Failed to check directory status: " << error.message();
134       return false;
135     }
136     if (is_dir) {
137       if (!Encrypt(current))
138         return false;
139       continue;
140     }
141
142     bool is_sym = fs::is_symlink(symlink_status(current, error));
143     if (error) {
144       LOG(ERROR) << "Failed to check symlink status: " << error.message();
145       return false;
146     }
147     if (is_sym)
148       continue;
149
150     // it is regular file (not dir, not symlink)
151     if (ToBeEncrypted(current)) {
152       LOG(INFO) << "File for encryption: " << current;
153       if (!EncryptFile(current))
154         return false;
155     }
156   }
157   return true;
158 }
159
160 bool StepEncryptResources::EncryptFile(const fs::path &src) {
161   struct stat info;
162   memset(&info, 0, sizeof(info));
163   int fd;
164
165   FILE *input = OpenFile(src, "rb");
166   if (!input) {
167     LOG(ERROR) << "Cannot open file for encryption: " << src;
168     return false;
169   }
170
171   fd = fileno(input);
172   if (fstat(fd, &info) != 0) {
173     LOG(ERROR) << "Could not access file " << src;
174     fclose(input);
175     return false;
176   }
177
178   const std::size_t fileSize = info.st_size;
179   if (0 == fileSize) {
180     LOG(ERROR) << src << " size is 0, so encryption will be skipped";
181     fclose(input);
182     return true;
183   }
184
185   fs::path encFile(src.string() + ".enc");
186   FILE *output = OpenFile(encFile, "wb");
187   if (!output) {
188     LOG(ERROR) << "Cannot create encrypted file: " << encFile;
189     fclose(input);
190     return false;
191   }
192
193   std::size_t chunkSize = (fileSize > kEncryptionChunkMaxSize ?
194                           kEncryptionChunkMaxSize : fileSize);
195
196   std::unique_ptr<unsigned char[]> inChunk(new unsigned char[chunkSize]);
197   std::size_t bytesRead = 0;
198
199   do {
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
205       int ret;
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,
210                 inChunk.get(),
211                 (size_t)bytesRead,
212                 &encrypted_data,
213                 &encrypted_size);
214       } else {
215           ret = wae_encrypt_web_application(
216                   context_->uid.get(),
217                   context_->pkgid.get().c_str(),
218                   inChunk.get(),
219                   (size_t)bytesRead,
220                   &encrypted_data,
221                   &encrypted_size);
222       }
223
224       if (WAE_ERROR_NONE != ret) {
225         LOG(ERROR) << "Error during encrypting:";
226         switch (ret) {
227           case WAE_ERROR_INVALID_PARAMETER:
228             LOG(ERROR) << "WAE_ERROR_INVALID_PARAMETER";
229             break;
230           case WAE_ERROR_PERMISSION_DENIED:
231             LOG(ERROR) << "WAE_ERROR_PERMISSION_DENIED";
232             break;
233           case WAE_ERROR_NO_KEY:
234             LOG(ERROR) << "WAE_ERROR_NO_KEY";
235             break;
236           case WAE_ERROR_KEY_MANAGER:
237             LOG(ERROR) << "WAE_ERROR_KEY_MANAGER";
238             break;
239           case WAE_ERROR_CRYPTO:
240             LOG(ERROR) << "WAE_ERROR_CRYPTO";
241             break;
242           case WAE_ERROR_UNKNOWN:
243             LOG(ERROR) << "WAE_ERROR_UNKNOWN";
244             break;
245           default:
246             LOG(ERROR) << "UNKNOWN";
247             break;
248         }
249         fclose(output);
250         fclose(input);
251         return false;
252       }
253
254       if (encrypted_size <= 0) {
255         LOG(ERROR) << "Encryption Failed using TrustZone";
256         fclose(output);
257         fclose(input);
258         return false;
259       }
260
261       std::stringstream toString;
262       toString << encrypted_size;
263
264       WriteBytes((unsigned char*)toString.str().c_str(), sizeof(int), output);
265       WriteBytes((unsigned char*)encrypted_data, encrypted_size, output);
266       free(encrypted_data);
267     }
268     inChunk.reset(new unsigned char[chunkSize]);
269   } while (!std::feof(input));
270
271   fclose(output);
272
273   LOG(DEBUG) << "File encrypted successfully";
274   if (0 != unlink(src.c_str())) {
275     fclose(input);
276     return false;
277   }
278   fclose(input);
279
280   LOG(DEBUG) << "Rename encrypted file";
281   if (0 != std::rename(encFile.c_str(), src.c_str()))
282     return false;
283
284   return true;
285 }
286
287 void StepEncryptResources::SetEncryptionRoot() {
288   input_ = context_->unpacked_dir_path.get();
289 }
290
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(),
296                    ::tolower);
297     return encryptSet.count(mimeType) > 0;
298   }
299   return false;
300 }
301
302 }  // namespace encryption
303 }  // namespace wgt