| 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 | 
|  |