remove encryption api dependency from wrt-commons
[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
33 #include <memory>
34
35 #include <dpl/log/log.h>
36 #include <dpl/errno_string.h>
37 #include <dpl/foreach.h>
38 #include <dpl/scoped_fclose.h>
39 #include <dpl/wrt-dao-ro/global_config.h>
40 #include <dpl/string.h>
41 #include <FBaseByteBuffer.h>
42 #include <security/FSecCrypto_TrustZoneService.h>
43
44 #include <widget_install/job_widget_install.h>
45 #include <widget_install/widget_install_context.h>
46 #include <widget_install/widget_install_errors.h>
47
48 using namespace WrtDB;
49
50 namespace {
51 const std::size_t ENCRYPTION_CHUNK_MAX_SIZE = 1008; // bytes
52
53 std::set<std::string>& getSupportedForEncryption()
54 {
55     static std::set<std::string> encryptSet;
56     if (encryptSet.empty()) {
57         encryptSet.insert(".html");
58         encryptSet.insert(".css");
59         encryptSet.insert(".js");
60     }
61     return encryptSet;
62 }
63
64 bool isSupportedForEncryption(const std::string &file)
65 {
66     size_t foundKey = file.rfind(".");
67     if (std::string::npos != foundKey) {
68         std::string mimeType = file.substr(foundKey);
69         return getSupportedForEncryption().count(mimeType) > 0;
70     }
71     return false;
72 }
73
74 /**
75  * Opens a file.
76  *
77  * @param path Path to a file.
78  * @param mode Mode.
79  * @return Stream handle.
80  * @throw ExtractFileFailed If error (other than EINTR) occurs.
81  */
82 FILE* openFile(const std::string& path, const std::string& mode)
83 {
84     FILE* result = NULL;
85
86     do
87     {
88         result = fopen(path.c_str(), mode.c_str());
89     } while ((NULL == result) && (EINTR == errno));
90
91     if (NULL == result)
92     {
93         ThrowMsg(Jobs::WidgetInstall::Exceptions::InternalError,
94                  "Could not open file " << path);
95     }
96
97     return result;
98 }
99
100 /**
101  * Reads bytes from a stream.
102  *
103  * @param buffer Buffer to read the bytes into.
104  * @param count Number of bytes to read.
105  * @param stream Stream to read from.
106  * @return Number of bytes read
107  * @throw ExtractFileFailed If error (other than EINTR) occurs.
108  */
109 std::size_t readBytes(unsigned char* buffer, std::size_t count, FILE* stream)
110 {
111     std::size_t result = std::fread(buffer,
112                                     sizeof(unsigned char),
113                                     count,
114                                     stream);
115
116     if (result != count)
117     {
118         int error = errno;
119         if (0 != std::ferror(stream))
120         {
121             if (EINTR != error)
122             {
123                 ThrowMsg(Jobs::WidgetInstall::Exceptions::InternalError,
124                          "Error while reading data" <<
125                          " [" << DPL::GetErrnoString(error) << "]");
126             }
127         }
128     }
129
130     return result;
131 }
132
133 /**
134  * Writes bytes to a stream.
135  *
136  * @param buffer Data to write.
137  * @param count Number of bytes.
138  * @param stream Stream to write to.
139  * @throw ExtractFileFailed If error (other than EINTR) occurs.
140  */
141 void writeBytes(unsigned char* buffer, std::size_t count, FILE* stream)
142 {
143     std::size_t bytesWritten = 0;
144     std::size_t bytesToWrite = 0;
145     do
146     {
147         bytesToWrite = count - bytesWritten;
148         bytesWritten = std::fwrite(buffer + bytesWritten,
149                                    sizeof(unsigned char),
150                                    count - bytesWritten,
151                                    stream);
152         if ((bytesWritten != bytesToWrite) && (EINTR != errno))
153         {
154             int error = errno;
155             ThrowMsg(Jobs::WidgetInstall::Exceptions::InternalError,
156                      "Error while writing data" <<
157                      " [" << DPL::GetErrnoString(error) << "]");
158         }
159     } while ((bytesWritten != bytesToWrite) && (EINTR == errno));
160 }
161
162 /*
163  * get encrypted string from trustzone
164 */
165 Tizen::Base::ByteBuffer* EncryptChunkByTrustZone(
166         Tizen::Base::ByteBuffer* appInfo,
167         const unsigned char *plainBuffer,
168         int pBufSize)
169 {
170     using namespace Tizen::Base;
171
172     Tizen::Security::Crypto::_TrustZoneService* pInstance;
173     pInstance = Tizen::Security::Crypto::_TrustZoneService::GetInstance();
174
175     ByteBuffer pBuf;
176     pBuf.Construct(pBufSize);
177     const byte *pByte = reinterpret_cast<const byte*>(plainBuffer);
178     pBuf.SetArray(pByte, 0, pBufSize);
179     pBuf.Flip();
180
181     ByteBuffer* getBuffer = pInstance->_TrustZoneService::EncryptN(*appInfo, pBuf);
182     return getBuffer;
183 }
184 }
185
186 namespace Jobs {
187 namespace WidgetInstall {
188 TaskEncryptResource::TaskEncryptResource(InstallerContext& context) :
189     DPL::TaskDecl<TaskEncryptResource>(this),
190     m_context(context)
191 {
192     AddStep(&TaskEncryptResource::StepEncryptResource);
193 }
194
195 void TaskEncryptResource::StepEncryptResource()
196 {
197     LogDebug("Step Encrypt resource");
198
199     EncryptDirectory(m_context.locations->getTemporaryRootDir());
200     m_context.job->UpdateProgress(
201             InstallerContext::INSTALL_ECRYPTION_FILES,
202             "Ecrypt resource files");
203 }
204
205 void TaskEncryptResource::EncryptDirectory(std::string path)
206 {
207     FTS *fts;
208     FTSENT *ftsent;
209     char * const paths[] = { const_cast<char * const>(path.c_str()), NULL };
210
211     if ((fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL) {
212         //ERROR
213         int error = errno;
214         LogWarning(__PRETTY_FUNCTION__ << ": fts_open failed with error: "
215                                        << strerror(error));
216         ThrowMsg(Exceptions::InternalError, "Error reading directory: "
217                  << path);
218     }
219
220     while ((ftsent = fts_read(fts)) != NULL) {
221         switch (ftsent->fts_info) {
222         case FTS_DP:
223         case FTS_DC:
224         case FTS_D:
225         case FTS_DEFAULT:
226         case FTS_SLNONE:
227             //directories, non-regular files, dangling symbolic links
228             break;
229         case FTS_F:
230         case FTS_NSOK:
231         case FTS_SL:
232             //regular files and other objects that can be counted
233             if (isSupportedForEncryption(ftsent->fts_path)) {
234                 EncryptFile(ftsent->fts_path);
235             }
236             break;
237         case FTS_NS:
238         case FTS_DOT:
239         case FTS_DNR:
240         case FTS_ERR:
241         default:
242             LogWarning(__PRETTY_FUNCTION__
243                        << ": traversal failed on file: "
244                        << ftsent->fts_path
245                        << " with error: "
246                        << strerror(ftsent->fts_errno));
247             ThrowMsg(Exceptions::InternalError, "Error reading file");
248             break;
249         }
250     }
251
252     if (fts_close(fts) == -1) {
253         int error = errno;
254         LogWarning(__PRETTY_FUNCTION__ << ": fts_close failed with error: "
255                                        << strerror(error));
256     }
257 }
258
259 void TaskEncryptResource::EncryptFile(const std::string &fileName)
260 {
261     try
262     {
263         LogDebug("Encrypt file: " << fileName);
264         std::string encFile = fileName + ".enc";
265
266         struct stat info;
267         memset(&info, 0, sizeof(info));
268         if (stat(fileName.c_str(), &info) != 0)
269         {
270             int error = errno;
271             ThrowMsg(Exceptions::InternalError,
272                      "Could not access file " << fileName <<
273                      "[" << DPL::GetErrnoString(error) << "]");
274         }
275         const std::size_t fileSize = info.st_size;
276
277         DPL::ScopedFClose inFile(openFile(fileName, "r"));
278         DPL::ScopedFClose outFile(openFile(encFile, "w"));
279
280         const std::size_t chunkSize = (fileSize > ENCRYPTION_CHUNK_MAX_SIZE
281                 ? ENCRYPTION_CHUNK_MAX_SIZE : fileSize);
282
283         std::unique_ptr<unsigned char[]> inChunk(new unsigned char[chunkSize]);
284         std::unique_ptr<unsigned char[]> outChunk;
285
286         std::size_t bytesRead = 0;
287         using namespace Tizen::Base;
288
289         std::string pkgid =
290             DPL::ToUTF8String(m_context.widgetConfig.tzAppid).c_str();
291         const byte *b_pkgid = reinterpret_cast<const byte*>(
292                     pkgid.c_str());
293         ByteBuffer appInfo;
294         appInfo.Construct(pkgid.length());
295         appInfo.SetArray(b_pkgid, 0, pkgid.length());
296         appInfo.Flip();
297
298         do
299         {
300             bytesRead = readBytes(inChunk.get(), chunkSize, inFile.Get());
301             if (0 != bytesRead) {
302
303                 ByteBuffer *getBuffer = EncryptChunkByTrustZone(
304                         &appInfo,
305                         inChunk.get(), bytesRead);
306                 int decBufSize = getBuffer->GetRemaining();
307
308                 outChunk.reset(new unsigned char[decBufSize]);
309                 memcpy(outChunk.get(), getBuffer->GetPointer(), getBuffer->GetRemaining());
310                 getBuffer->Reset();
311
312                 writeBytes(outChunk.get(), decBufSize, outFile.Get());
313             }
314
315         } while (0 == std::feof(inFile.Get()));
316
317         LogDebug("File encrypted successfully");
318
319         outFile.Reset();
320         inFile.Reset();
321
322         LogDebug("Remove plain-text file: " << fileName);
323         if (0 != unlink(fileName.c_str()))
324         {
325             Throw(Exceptions::InternalError);
326         }
327
328         LogDebug("Rename encrypted file");
329         if (0 != std::rename(encFile.c_str(), fileName.c_str()))
330         {
331             Throw(Exceptions::InternalError);
332         }
333
334         std::string realPath = fileName;
335         realPath.replace(0,
336                          m_context.locations->getTemporaryRootDir().length(),
337                          m_context.locations->getSourceDir());
338
339         WrtDB::EncryptedFileInfo fileInfo;
340         fileInfo.fileName = DPL::FromUTF8String(realPath);
341         fileInfo.fileSize = fileSize;
342
343         m_context.widgetConfig.encryptedFiles.insert(fileInfo);
344     }
345     Catch (Exceptions::InternalError)
346     {
347         ReThrowMsg(Exceptions::ExtractFileFailed, fileName);
348     }
349 }
350
351 } //namespace WidgetInstall
352 } //namespace Jobs