File encryption method reading whole files into a dynamic stack buffer 38/81738/15 submit/tizen/20160831.042125
authorJongHeon Choi <j-h.choi@samsung.com>
Thu, 28 Jul 2016 06:19:06 +0000 (15:19 +0900)
committerJongHeon Choi <j-h.choi@samsung.com>
Wed, 31 Aug 2016 01:25:36 +0000 (18:25 -0700)
Related changes:
[crosswalk-tizen] https://github.com/crosswalk-project/crosswalk-tizen/pull/135
                  https://review.tizen.org/gerrit/#/c/83459/

Change-Id: I87d41c578e161622035e65e324b3d9590e2c266e

src/wgt/step/encryption/step_encrypt_resources.cc

index ad5485f..4d5fb91 100644 (file)
 #include <boost/system/error_code.hpp>
 
 #include <common/utils/file_util.h>
+#include <common/utils/byte_size_literals.h>
 
 #include <manifest_parser/utils/logging.h>
+#include <sys/stat.h>
 
 #include <algorithm>
 #include <cstdio>
 
 namespace {
 
+const std::size_t kEncryptionChunkMaxSize = 8_kB; // bytes
 const std::set<std::string> encryptSet { ".html", ".htm", ".css", ".js"};
 
+FILE* OpenFile(const std::string& path, const std::string& mode) {
+  FILE* result = nullptr;
+
+  do {
+    result = fopen(path.c_str(), mode.c_str());
+  } while ((nullptr == result));
+
+  return result;
+}
+
+std::size_t ReadBytes(unsigned char* buffer, std::size_t count, FILE* stream) {
+  std::size_t result = std::fread(buffer,
+                                  1,
+                                  count,
+                                  stream);
+  if (result != count) {
+    if (0 != std::ferror(stream)) {
+      LOG(ERROR) << "Error while reading data";
+    }
+  }
+
+  return result;
+}
+
+void WriteBytes(unsigned char* buffer, std::size_t count, FILE* stream) {
+  // original file is treated as destination!
+  std::size_t bytes_written = 0;
+  std::size_t bytes_to_write = 0;
+  do {
+    bytes_to_write = count - bytes_written;
+    bytes_written = std::fwrite(buffer + bytes_written,
+                               sizeof(unsigned char),
+                               count - bytes_written,
+                               stream);
+    if ((bytes_written != bytes_to_write)) {
+      LOG(ERROR) << "Error while writing data";
+      free(buffer);
+      fclose(stream);
+    }
+  } while ((bytes_written != bytes_to_write));
+}
+
 }  // namespace
 
 
@@ -105,102 +150,124 @@ bool StepEncryptResources::Encrypt(const bf::path &src) {
 }
 
 bool StepEncryptResources::EncryptFile(const bf::path &src) {
-  FILE *input = fopen(src.string().c_str(), "rb");
-  if (!input) {
-    LOG(ERROR) << "Cannot open file for encryption: " << src;
+  bf::path encFile(src.string() + ".enc");
+  struct stat info;
+  memset(&info, 0, sizeof(info));
+  if (stat(src.string().c_str(), &info) != 0) {
+    LOG(ERROR) << "Could not access file " << src.string();
     return false;
   }
-
-  // read size
-  fseek(input , 0 , SEEK_END);
-  size_t length = ftell(input);
-
-  // don't encrypt empty files because libwebappenc doesn't support it
-  if (length == 0) {
-    fclose(input);
+  const std::size_t fileSize = info.st_size;
+  if (0 == fileSize) {
+    LOG(ERROR) << src.string().c_str() << " size is 0, so encryption is skiped";
     return true;
   }
 
-  rewind(input);
-
-  char *input_buffer = new char[length];
-  if (length != fread(input_buffer, sizeof(char), length, input)) {
-    LOG(ERROR) << "Read error, file: " << src;
-    fclose(input);
-    delete []input_buffer;
+  FILE *input = OpenFile(src.string().c_str(), "rb");
+  if (input == nullptr) {
+    LOG(ERROR) << "Cannot open file for encryption: " << src.string();
     return false;
   }
-  fclose(input);
 
-  unsigned char* encrypted_data = nullptr;
-  size_t enc_data_len = 0;
-  int ret;
-  if (context_->request_mode.get() == common_installer::RequestMode::GLOBAL)
-    ret = wae_encrypt_global_web_application(
-            context_->pkgid.get().c_str(),
-            context_->is_preload_request.get() ?
-            true : false,
-            reinterpret_cast<const unsigned char*>(input_buffer),
-            length,
-            &encrypted_data,
-            &enc_data_len);
-  else
-    ret = wae_encrypt_web_application(
-            context_->uid.get(),
-            context_->pkgid.get().c_str(),
-            reinterpret_cast<const unsigned char*>(input_buffer),
-            length,
-            &encrypted_data,
-            &enc_data_len);
-  delete []input_buffer;
-  if (WAE_ERROR_NONE != ret) {
-    switch (ret) {
-    case WAE_ERROR_INVALID_PARAMETER:
-      LOG(ERROR) << "Error during encrypting: WAE_ERROR_INVALID_PARAMETER";
-      break;
-    case WAE_ERROR_PERMISSION_DENIED:
-      LOG(ERROR) << "Error during encrypting: WAE_ERROR_PERMISSION_DENIED";
-      break;
-    case WAE_ERROR_NO_KEY:
-      LOG(ERROR) << "Error during encrypting: WAE_ERROR_NO_KEY";
-      break;
-    case WAE_ERROR_KEY_MANAGER:
-      LOG(ERROR) << "Error during encrypting: WAE_ERROR_KEY_MANAGER";
-      break;
-    case WAE_ERROR_CRYPTO:
-      LOG(ERROR) << "Error during encrypting: WAE_ERROR_CRYPTO";
-      break;
-    case WAE_ERROR_UNKNOWN:
-      LOG(ERROR) << "Error during encrypting: WAE_ERROR_UNKNOWN";
-      break;
-    default:
-      LOG(ERROR) << "Error during encrypting: UNKNOWN";
-      break;
-    }
+  FILE *output = OpenFile(encFile.string().c_str(), "wb");
+  if (output == nullptr) {
+    LOG(ERROR) << "Cannot create encrypted file: " << encFile.string();
     return false;
   }
 
-  // original file is treated as destination!
-  FILE *output = fopen(src.string().c_str(), "wb");
-  if (!output) {
-    LOG(ERROR) << "Cannot create encrypted file: " << src;
-    free(encrypted_data);
+  std::size_t chunkSize = (fileSize > kEncryptionChunkMaxSize
+                                 ? kEncryptionChunkMaxSize : fileSize);
+
+  std::unique_ptr<unsigned char[]> inChunk(new unsigned char[chunkSize]);
+  std::size_t bytesRead = 0;
+
+  do {
+    bytesRead = ReadBytes(inChunk.get(), chunkSize, input);
+    if (0 != bytesRead) {
+      unsigned char* encrypted_data = nullptr;
+      size_t encrypted_size = 0;
+      // TODO(p.sikorski) check if it is Preloaded
+      int ret;
+      if (context_->request_mode.get() == common_installer::RequestMode::GLOBAL) {
+        ret = wae_encrypt_global_web_application(
+                context_->pkgid.get().c_str(),
+                context_->is_preload_request.get() ?
+                true : false,
+                inChunk.get(),
+                (size_t)bytesRead,
+                &encrypted_data,
+                &encrypted_size);
+      } else {
+          ret = wae_encrypt_web_application(
+                  context_->uid.get(),
+                  context_->pkgid.get().c_str(),
+                  inChunk.get(),
+                  (size_t)bytesRead,
+                  &encrypted_data,
+                  &encrypted_size);
+      }
+
+      if (WAE_ERROR_NONE != ret) {
+        LOG(ERROR) << "Error during encrypting:";
+        switch (ret) {
+          case WAE_ERROR_INVALID_PARAMETER:
+            LOG(ERROR) << "WAE_ERROR_INVALID_PARAMETER";
+            break;
+          case WAE_ERROR_PERMISSION_DENIED:
+            LOG(ERROR) << "WAE_ERROR_PERMISSION_DENIED";
+            break;
+          case WAE_ERROR_NO_KEY:
+            LOG(ERROR) << "WAE_ERROR_NO_KEY";
+            break;
+          case WAE_ERROR_KEY_MANAGER:
+            LOG(ERROR) << "WAE_ERROR_KEY_MANAGER";
+            break;
+          case WAE_ERROR_CRYPTO:
+            LOG(ERROR) << "WAE_ERROR_CRYPTO";
+            break;
+          case WAE_ERROR_UNKNOWN:
+            LOG(ERROR) << "WAE_ERROR_UNKNOWN";
+            break;
+          default:
+            LOG(ERROR) << "UNKNOWN";
+            break;
+        }
+        fclose(output);
+        fclose(input);
+        return false;
+      }
+
+      if (encrypted_size <= 0) {
+        LOG(ERROR) << "Encryption Failed using TrustZone";
+        fclose(output);
+        fclose(input);
+        return false;
+      }
+
+      std::stringstream toString;
+      toString << encrypted_size;
+
+      WriteBytes((unsigned char*)toString.str().c_str(), sizeof(int), output);
+      WriteBytes((unsigned char*)encrypted_data, encrypted_size, output);
+      free(encrypted_data);
+    }
+    inChunk.reset(new unsigned char[chunkSize]);
+
+  } while (!std::feof(input));
+
+  fclose(output);
+  fclose(input);
+
+  LOG(DEBUG) << "File encrypted successfully";
+  if (0 != unlink(src.string().c_str())) {
     return false;
   }
 
-  if (enc_data_len != fwrite(reinterpret_cast<const char*>(encrypted_data),
-                             sizeof(char),
-                             enc_data_len,
-                             output)) {
-    LOG(ERROR) << "Write error, file: " << src;
-    free(encrypted_data);
-    fclose(output);
+  LOG(DEBUG) << "Rename encrypted file";
+  if (0 != std::rename(encFile.c_str(), src.string().c_str())) {
     return false;
   }
 
-
-  fclose(output);
-  free(encrypted_data);
   return true;
 }