Index: extensions/browser/api/lock_screen_data/data_item.cc |
diff --git a/extensions/browser/api/lock_screen_data/data_item.cc b/extensions/browser/api/lock_screen_data/data_item.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..af14d5a51b0a0a5fd7daa5b8f103b68ff379caf0 |
--- /dev/null |
+++ b/extensions/browser/api/lock_screen_data/data_item.cc |
@@ -0,0 +1,233 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "extensions/browser/api/lock_screen_data/data_item.h" |
+ |
+#include <utility> |
+ |
+#include "base/bind.h" |
+#include "base/files/file.h" |
+#include "base/files/file_path.h" |
+#include "base/files/file_util.h" |
+#include "base/location.h" |
+#include "base/memory/ptr_util.h" |
+#include "base/sequenced_task_runner.h" |
+#include "base/task_scheduler/post_task.h" |
+#include "base/values.h" |
+#include "crypto/encryptor.h" |
+#include "crypto/symmetric_key.h" |
+#include "extensions/browser/api/lock_screen_data/operation_result.h" |
+ |
+namespace extensions { |
+namespace lock_screen_data { |
+ |
+namespace { |
+ |
+// Keys used to describe a data item in its dictionary value representation: |
+const char kPrefKeyId[] = "id"; |
+const char kPrefKeyBackingFile[] = "backing_file"; |
+ |
+// Writes |data| to file specified by |path|. The written data will be encrypted |
+// using symmetric AES key |encryption_key|. |
+// |result| - set to the resulting write operation status. |
+// Expected to be called on worker thread. |
+void WriteImpl(const base::FilePath& path, |
+ const std::vector<char>& data, |
+ const std::string& encryption_key, |
+ OperationResult* result) { |
+ std::string iv(16, ' '); |
+ std::unique_ptr<crypto::SymmetricKey> key = |
+ crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, encryption_key); |
+ if (!key) { |
+ *result = OperationResult::kInvalidKey; |
+ return; |
+ } |
+ |
+ crypto::Encryptor encryptor; |
+ if (!encryptor.Init(key.get(), crypto::Encryptor::CBC, iv)) { |
+ *result = OperationResult::kInvalidKey; |
+ return; |
+ } |
+ |
+ std::string encrypted; |
+ if (!encryptor.Encrypt(std::string(data.data(), data.size()), &encrypted)) { |
+ *result = OperationResult::kInvalidKey; |
+ return; |
+ } |
+ |
+ base::File::Error error; |
+ if (!base::DirectoryExists(path.DirName()) && |
+ !base::CreateDirectoryAndGetError(path.DirName(), &error)) { |
+ *result = OperationResult::kFileCreationFailed; |
+ return; |
+ } |
+ |
+ int write_size = base::WriteFile(path, encrypted.data(), encrypted.size()); |
+ *result = (write_size == static_cast<int>(encrypted.size())) |
+ ? OperationResult::kSuccess |
+ : OperationResult::kFileWriteFailed; |
+} |
+ |
+// Reads encrypted data from file specified by |path|. The read data will be |
+// decrypted using using symmetric AES key |encryption_key| before being |
+// returned. |
+// |result| - set to the resulting write operation status. |
+// |data| - set to the clear-text data read from the file. |
+// Expected to be called on worker thread. |
+void ReadImpl(const base::FilePath& path, |
+ const std::string& decryption_key, |
+ OperationResult* result, |
+ std::vector<char>* data) { |
+ std::string read_data; |
+ if (!base::ReadFileToString(path, &read_data)) { |
+ *result = base::PathExists(path) ? OperationResult::kFileReadFailed |
+ : OperationResult::kNotFound; |
+ return; |
+ } |
+ |
+ std::string iv(16, ' '); |
+ std::unique_ptr<crypto::SymmetricKey> key = |
+ crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, decryption_key); |
+ if (!key) { |
+ *result = OperationResult::kInvalidKey; |
+ return; |
+ } |
+ |
+ crypto::Encryptor encryptor; |
+ if (!encryptor.Init(key.get(), crypto::Encryptor::CBC, iv)) { |
+ *result = OperationResult::kInvalidKey; |
+ return; |
+ } |
+ |
+ std::string decrypted; |
+ if (!encryptor.Decrypt(read_data, &decrypted)) { |
+ *result = OperationResult::kWrongKey; |
+ return; |
+ } |
+ |
+ std::vector<char> result_data(decrypted.data(), |
+ decrypted.data() + decrypted.size()); |
+ data->swap(result_data); |
+ *result = OperationResult::kSuccess; |
+} |
+ |
+// Deletes file under |file_path|. |
+void DeleteImpl(const base::FilePath& file_path, |
+ bool recursive, |
+ OperationResult* result) { |
+ *result = base::DeleteFile(file_path, recursive) |
+ ? OperationResult::kSuccess |
+ : OperationResult::kFileDeleteFailed; |
+} |
+ |
+} // namespace |
+ |
+DataItem::DataItem(const std::string& id) : id_(id), weak_ptr_factory_(this) {} |
+ |
+DataItem::~DataItem() = default; |
+ |
+std::unique_ptr<base::DictionaryValue> DataItem::ToValue() { |
+ std::unique_ptr<base::DictionaryValue> result = |
+ base::MakeUnique<base::DictionaryValue>(); |
+ result->SetString(kPrefKeyId, id_); |
+ result->SetString(kPrefKeyBackingFile, backing_file_.value()); |
+ return result; |
+} |
+ |
+bool DataItem::InitFromValue(const base::Value& value) { |
+ const base::DictionaryValue* dict_value = nullptr; |
+ if (!value.GetAsDictionary(&dict_value)) |
+ return false; |
+ |
+ std::string item_id; |
+ if (!dict_value->GetString(kPrefKeyId, &item_id) || id() != item_id) |
+ return false; |
+ |
+ std::string backing_file; |
+ if (dict_value->GetString(kPrefKeyBackingFile, &backing_file)) |
+ set_backing_file(base::FilePath(backing_file)); |
+ |
+ return true; |
+} |
+ |
+OperationResult DataItem::Write( |
+ const std::vector<char>& data, |
+ const std::string& crypto_key, |
+ const scoped_refptr<base::SequencedTaskRunner>& task_runner, |
+ const WriteCallback& callback) { |
+ if (backing_file_.empty()) |
+ return OperationResult::kNoBackingFile; |
+ |
+ std::unique_ptr<OperationResult> result = |
+ base::MakeUnique<OperationResult>(OperationResult::kFailed); |
+ OperationResult* result_ptr = result.get(); |
+ |
+ task_runner->PostTaskAndReply( |
+ FROM_HERE, |
+ base::Bind(&WriteImpl, backing_file_, data, crypto_key, result_ptr), |
+ base::Bind(&DataItem::OnWriteDone, weak_ptr_factory_.GetWeakPtr(), |
+ callback, base::Passed(&result))); |
+ return OperationResult::kPending; |
+} |
+ |
+OperationResult DataItem::Read( |
+ const std::string& crypto_key, |
+ const scoped_refptr<base::SequencedTaskRunner>& task_runner, |
+ const ReadCallback& callback) { |
+ if (backing_file_.empty()) |
+ return OperationResult::kNoBackingFile; |
+ |
+ std::unique_ptr<OperationResult> result = |
+ base::MakeUnique<OperationResult>(OperationResult::kFailed); |
+ OperationResult* result_ptr = result.get(); |
+ |
+ std::unique_ptr<std::vector<char>> data = |
+ base::MakeUnique<std::vector<char>>(); |
+ std::vector<char>* data_ptr = data.get(); |
+ |
+ task_runner->PostTaskAndReply( |
+ FROM_HERE, |
+ base::Bind(&ReadImpl, backing_file_, crypto_key, result_ptr, data_ptr), |
+ base::Bind(&DataItem::OnReadDone, weak_ptr_factory_.GetWeakPtr(), |
+ callback, base::Passed(&result), base::Passed(&data))); |
+ return OperationResult::kPending; |
+} |
+ |
+OperationResult DataItem::Delete( |
+ const scoped_refptr<base::SequencedTaskRunner>& task_runner, |
+ const DeleteCallback& callback) { |
+ if (backing_file_.empty()) |
+ return OperationResult::kNoBackingFile; |
+ |
+ std::unique_ptr<OperationResult> result = |
+ base::MakeUnique<OperationResult>(OperationResult::kFailed); |
+ OperationResult* result_ptr = result.get(); |
+ |
+ task_runner->PostTaskAndReply( |
+ FROM_HERE, base::Bind(&DeleteImpl, backing_file_, false, result_ptr), |
+ base::Bind(&DataItem::OnDeleteDone, weak_ptr_factory_.GetWeakPtr(), |
+ callback, base::Passed(&result))); |
+ |
+ backing_file_ = base::FilePath(); |
+ return OperationResult::kPending; |
+} |
+ |
+void DataItem::OnWriteDone(const DataItem::WriteCallback& callback, |
+ std::unique_ptr<OperationResult> success) { |
+ callback.Run(*success); |
+} |
+ |
+void DataItem::OnReadDone(const DataItem::ReadCallback& callback, |
+ std::unique_ptr<OperationResult> success, |
+ std::unique_ptr<std::vector<char>> data) { |
+ callback.Run(*success, std::move(data)); |
+} |
+ |
+void DataItem::OnDeleteDone(const DataItem::DeleteCallback& callback, |
+ std::unique_ptr<OperationResult> success) { |
+ callback.Run(*success); |
+} |
+ |
+} // namespace lock_screen_data |
+} // namespace extensions |