[Release] wrt-installer_0.1.55
[framework/web/wrt-installer.git] / src / jobs / widget_install / task_encrypt_resource.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 /**
17  * @file    task_ecnrypt_resource.cpp
18  * @author  Soyoung Kim (sy037.kim@samsung.com)
19  * @version 1.0
20  * @brief   Implementation file for installer task encrypt resource
21  */
22 #include "task_encrypt_resource.h"
23
24 #undef __USE_FILE_OFFSET64
25
26 #include <unistd.h>
27 #include <sys/stat.h>
28 #include <fts.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <cstdio>
32 #include <sstream>
33 #include<iostream>
34
35 #include <memory>
36
37 #include <dpl/log/log.h>
38 #include <dpl/errno_string.h>
39 #include <dpl/foreach.h>
40 #include <dpl/scoped_fclose.h>
41 #include <dpl/wrt-dao-ro/global_config.h>
42 #include <dpl/string.h>
43 #include <FBaseByteBuffer.h>
44 #include <security/FSecCrypto_TrustZoneService.h>
45
46 #include <widget_install/job_widget_install.h>
47 #include <widget_install/widget_install_context.h>
48 #include <widget_install/widget_install_errors.h>
49
50 using namespace WrtDB;
51
52 extern "C"
53 {
54     void InitWebAppInfo(const char* appId, const char* rootPath);
55 }
56
57 namespace {
58 const std::size_t ENCRYPTION_CHUNK_MAX_SIZE = 4096; // bytes
59 const std::size_t ENCRYPTION_DEC_CHUNK_SIZE = 4; // bytes
60 static bool initWebApp = false;
61
62 std::set<std::string>& getSupportedForEncryption()
63 {
64     static std::set<std::string> encryptSet;
65     if (encryptSet.empty()) {
66         encryptSet.insert(".html");
67         encryptSet.insert(".css");
68         encryptSet.insert(".js");
69     }
70     return encryptSet;
71 }
72
73 bool isSupportedForEncryption(const std::string &file)
74 {
75     size_t foundKey = file.rfind(".");
76     if (std::string::npos != foundKey) {
77         std::string mimeType = file.substr(foundKey);
78         return getSupportedForEncryption().count(mimeType) > 0;
79     }
80     return false;
81 }
82
83 /**
84  * Opens a file.
85  *
86  * @param path Path to a file.
87  * @param mode Mode.
88  * @return Stream handle.
89  * @throw ExtractFileFailed If error (other than EINTR) occurs.
90  */
91 FILE* openFile(const std::string& path, const std::string& mode)
92 {
93     FILE* result = NULL;
94
95     do
96     {
97         result = fopen(path.c_str(), mode.c_str());
98     } while ((NULL == result) && (EINTR == errno));
99
100     if (NULL == result)
101     {
102         ThrowMsg(Jobs::WidgetInstall::Exceptions::EncryptionFailed,
103                  "Could not open file " << path);
104     }
105
106     return result;
107 }
108
109 /**
110  * Reads bytes from a stream.
111  *
112  * @param buffer Buffer to read the bytes into.
113  * @param count Number of bytes to read.
114  * @param stream Stream to read from.
115  * @return Number of bytes read
116  * @throw ExtractFileFailed If error (other than EINTR) occurs.
117  */
118 std::size_t readBytes(unsigned char* buffer, std::size_t count, FILE* stream)
119 {
120     std::size_t result = std::fread(buffer,
121                                     sizeof(unsigned char),
122                                     count,
123                                     stream);
124
125     if (result != count)
126     {
127         int error = errno;
128         if (0 != std::ferror(stream))
129         {
130             if (EINTR != error)
131             {
132                 ThrowMsg(Jobs::WidgetInstall::Exceptions::ErrorExternalInstallingFailure,
133                          "Error while reading data" <<
134                          " [" << DPL::GetErrnoString(error) << "]");
135             }
136         }
137     }
138
139     return result;
140 }
141
142 /**
143  * Writes bytes to a stream.
144  *
145  * @param buffer Data to write.
146  * @param count Number of bytes.
147  * @param stream Stream to write to.
148  * @throw ExtractFileFailed If error (other than EINTR) occurs.
149  */
150 void writeBytes(unsigned char* buffer, std::size_t count, FILE* stream)
151 {
152     std::size_t bytesWritten = 0;
153     std::size_t bytesToWrite = 0;
154     do
155     {
156         bytesToWrite = count - bytesWritten;
157         bytesWritten = std::fwrite(buffer + bytesWritten,
158                                    sizeof(unsigned char),
159                                    count - bytesWritten,
160                                    stream);
161         if ((bytesWritten != bytesToWrite) && (EINTR != errno))
162         {
163             int error = errno;
164             ThrowMsg(Jobs::WidgetInstall::Exceptions::EncryptionFailed,
165                      "Error while writing data" <<
166                      " [" << DPL::GetErrnoString(error) << "]");
167         }
168     } while ((bytesWritten != bytesToWrite) && (EINTR == errno));
169 }
170
171 /*
172  * get encrypted string from trustzone
173 */
174 Tizen::Base::ByteBuffer* EncryptChunkByTrustZone(
175         Tizen::Base::ByteBuffer* appInfo,
176         const unsigned char *plainBuffer,
177         int pBufSize)
178 {
179     using namespace Tizen::Base;
180     if(!initWebApp)
181     {
182         char* pAppId = null;
183         pAppId = (char*)calloc(appInfo->GetRemaining()+1, 1);
184         memcpy(pAppId, appInfo->GetPointer(), appInfo->GetRemaining());
185         InitWebAppInfo(pAppId, "");
186         free (pAppId);
187         initWebApp = true;
188     }
189
190     Tizen::Security::Crypto::_TrustZoneService* pInstance;
191     pInstance = Tizen::Security::Crypto::_TrustZoneService::GetInstance();
192
193     ByteBuffer pBuf;
194     pBuf.Construct(pBufSize);
195     const byte *pByte = reinterpret_cast<const byte*>(plainBuffer);
196     if (pBuf.SetArray(pByte, 0, pBufSize) != E_SUCCESS) {
197         LogDebug("Couldnot set pBuf");
198         return NULL;
199     }
200     pBuf.Flip();
201
202     return pInstance->_TrustZoneService::EncryptN(*appInfo, pBuf);
203 }
204
205 Tizen::Base::ByteBuffer* TEST_dec(
206         Tizen::Base::ByteBuffer* appInfo,
207         const unsigned char *plainBuffer,
208         int pBufSize)
209 {
210     using namespace Tizen::Base;
211
212     Tizen::Security::Crypto::_TrustZoneService* pInstance;
213     pInstance = Tizen::Security::Crypto::_TrustZoneService::GetInstance();
214
215     ByteBuffer pBuf;
216     pBuf.Construct(pBufSize);
217     const byte *pByte = reinterpret_cast<const byte*>(plainBuffer);
218     if (pBuf.SetArray(pByte, 0, pBufSize) != E_SUCCESS) {
219         LogDebug("Couldnot set pBuf");
220         return NULL;
221     }
222     pBuf.Flip();
223
224     return pInstance->_TrustZoneService::DecryptN(*appInfo, pBuf);
225 }
226 }
227
228 namespace Jobs {
229 namespace WidgetInstall {
230 TaskEncryptResource::TaskEncryptResource(InstallerContext& context) :
231     DPL::TaskDecl<TaskEncryptResource>(this),
232     m_context(context)
233 {
234     AddStep(&TaskEncryptResource::StepEncryptResource);
235 }
236
237 void TaskEncryptResource::StepEncryptResource()
238 {
239     LogDebug("Step Encrypt resource");
240
241     EncryptDirectory(m_context.locations->getTemporaryRootDir());
242     m_context.job->UpdateProgress(
243             InstallerContext::INSTALL_ECRYPTION_FILES,
244             "Ecrypt resource files");
245 }
246
247 void TaskEncryptResource::EncryptDirectory(std::string path)
248 {
249     FTS *fts;
250     FTSENT *ftsent;
251     char * const paths[] = { const_cast<char * const>(path.c_str()), NULL };
252
253     if ((fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL) {
254         //ERROR
255         int error = errno;
256         LogWarning(__PRETTY_FUNCTION__ << ": fts_open failed with error: "
257                                        << strerror(error));
258         ThrowMsg(Exceptions::EncryptionFailed, "Error reading directory: "
259                  << path);
260     }
261
262     while ((ftsent = fts_read(fts)) != NULL) {
263         switch (ftsent->fts_info) {
264         case FTS_DP:
265         case FTS_DC:
266         case FTS_D:
267         case FTS_DEFAULT:
268         case FTS_SLNONE:
269             //directories, non-regular files, dangling symbolic links
270             break;
271         case FTS_F:
272         case FTS_NSOK:
273         case FTS_SL:
274             //regular files and other objects that can be counted
275             if (isSupportedForEncryption(ftsent->fts_path)) {
276                 EncryptFile(ftsent->fts_path);
277             }
278             break;
279         case FTS_NS:
280         case FTS_DOT:
281         case FTS_DNR:
282         case FTS_ERR:
283         default:
284             LogWarning(__PRETTY_FUNCTION__
285                        << ": traversal failed on file: "
286                        << ftsent->fts_path
287                        << " with error: "
288                        << strerror(ftsent->fts_errno));
289             ThrowMsg(Exceptions::EncryptionFailed, "Error reading file");
290             break;
291         }
292     }
293
294     if (fts_close(fts) == -1) {
295         int error = errno;
296         LogWarning(__PRETTY_FUNCTION__ << ": fts_close failed with error: "
297                                        << strerror(error));
298     }
299 }
300
301 void TaskEncryptResource::EncryptFile(const std::string &fileName)
302 {
303     LogDebug("Encrypt file: " << fileName);
304     std::string encFile = fileName + ".enc";
305
306     struct stat info;
307     memset(&info, 0, sizeof(info));
308     if (stat(fileName.c_str(), &info) != 0)
309     {
310         int error = errno;
311         ThrowMsg(Exceptions::EncryptionFailed,
312                 "Could not access file " << fileName <<
313                 "[" << DPL::GetErrnoString(error) << "]");
314     }
315     const std::size_t fileSize = info.st_size;
316     if (0 == fileSize) {
317         LogDebug(fileName << " size is 0, so encryption is skiped");
318         return;
319     }
320
321     DPL::ScopedFClose inFile(openFile(fileName, "r"));
322     DPL::ScopedFClose outFile(openFile(encFile, "w"));
323
324     const std::size_t chunkSize = (fileSize > ENCRYPTION_CHUNK_MAX_SIZE
325             ? ENCRYPTION_CHUNK_MAX_SIZE : fileSize);
326
327     std::unique_ptr<unsigned char[]> inChunk(new unsigned char[chunkSize]);
328     std::unique_ptr<unsigned char[]> outChunk;
329
330     std::size_t bytesRead = 0;
331     using namespace Tizen::Base;
332
333     std::string pkgid =
334         DPL::ToUTF8String(m_context.widgetConfig.tzAppid).c_str();
335     const byte *b_pkgid = reinterpret_cast<const byte*>(
336             pkgid.c_str());
337     ByteBuffer appInfo;
338     appInfo.Construct(pkgid.length());
339
340     if (appInfo.SetArray(b_pkgid, 0, pkgid.length()) != E_SUCCESS) {
341         LogDebug("Couldnot set appInfo");
342         return;
343     }
344
345     appInfo.Flip();
346
347     do
348     {
349         bytesRead = readBytes(inChunk.get(), chunkSize, inFile.Get());
350         if (0 != bytesRead) {
351             ByteBuffer *getBuffer = EncryptChunkByTrustZone(
352                     &appInfo,
353                     inChunk.get(), bytesRead);
354             if (getBuffer == NULL) {
355                 ThrowMsg(Exceptions::EncryptionFailed,
356                         "Encryption Failed using TrustZone");
357             }
358             int decBufSize = getBuffer->GetRemaining();
359
360             outChunk.reset(new unsigned char[decBufSize]);
361             memcpy(outChunk.get(), getBuffer->GetPointer(), getBuffer->GetRemaining());
362             getBuffer->Reset();
363
364             char writeSize[ENCRYPTION_DEC_CHUNK_SIZE];
365             memset(writeSize, 0x00, ENCRYPTION_DEC_CHUNK_SIZE);
366             std::stringstream toString;
367             toString << decBufSize;
368             strncpy(writeSize, toString.str().c_str(), toString.str().length());
369
370             writeBytes((unsigned char*)writeSize,
371                     ENCRYPTION_DEC_CHUNK_SIZE, outFile.Get());
372             writeBytes(outChunk.get(), decBufSize, outFile.Get());
373         }
374
375     } while (0 == std::feof(inFile.Get()));
376
377     outFile.Reset();
378     inFile.Reset();
379
380     LogDebug("File encrypted successfully");
381     LogDebug("Remove plain-text file: " << fileName);
382     if (0 != unlink(fileName.c_str()))
383     {
384         Throw(Exceptions::EncryptionFailed);
385     }
386
387     LogDebug("Rename encrypted file");
388     if (0 != std::rename(encFile.c_str(), fileName.c_str()))
389     {
390         Throw(Exceptions::EncryptionFailed);
391     }
392
393     std::string realPath = fileName;
394     realPath.replace(0,
395             m_context.locations->getTemporaryRootDir().length(),
396             m_context.locations->getSourceDir());
397
398     WrtDB::EncryptedFileInfo fileInfo;
399     fileInfo.fileName = DPL::FromUTF8String(realPath);
400     fileInfo.fileSize = fileSize;
401
402     m_context.widgetConfig.encryptedFiles.insert(fileInfo);
403 }
404
405 } //namespace WidgetInstall
406 } //namespace Jobs