Commit 34e2627e authored by Scott Violet's avatar Scott Violet Committed by Commit Bot

sessions: adds support for encryption

Encryption is done at the command level. Specifically, when encrypted
each command is written as a length (not encrypted) followed by the
encrypted command-id and command-contents pair.

BUG=1033924
TEST=component_unittests CommandStorageBackend*

Change-Id: I4899b6daefba85ef3ae865262b375ad345341c63
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2017981Reviewed-by: default avatarAdam Langley <agl@chromium.org>
Commit-Queue: Scott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/master@{#735963}
parent 57e438bc
...@@ -138,6 +138,7 @@ source_set("shared") { ...@@ -138,6 +138,7 @@ source_set("shared") {
"//components/prefs", "//components/prefs",
"//components/tab_groups", "//components/tab_groups",
"//components/variations", "//components/variations",
"//crypto",
"//skia", "//skia",
"//ui/base", "//ui/base",
"//ui/gfx", "//ui/gfx",
...@@ -182,6 +183,7 @@ source_set("unit_tests") { ...@@ -182,6 +183,7 @@ source_set("unit_tests") {
} }
testonly = true testonly = true
sources = [ sources = [
"core/command_storage_backend_unittest.cc",
"core/serialized_navigation_entry_unittest.cc", "core/serialized_navigation_entry_unittest.cc",
"core/session_id_generator_unittest.cc", "core/session_id_generator_unittest.cc",
"core/snapshotting_session_backend_unittest.cc", "core/snapshotting_session_backend_unittest.cc",
......
include_rules = [ include_rules = [
"+components/history/core/common", "+components/history/core/common",
"+components/variations", "+components/variations",
"+crypto",
"+ui/base", "+ui/base",
"+ui/gfx", "+ui/gfx",
] ]
...@@ -21,6 +21,10 @@ namespace base { ...@@ -21,6 +21,10 @@ namespace base {
class File; class File;
} }
namespace crypto {
class Aead;
}
namespace sessions { namespace sessions {
// CommandStorageBackend is the backend used by CommandStorageManager. It writes // CommandStorageBackend is the backend used by CommandStorageManager. It writes
...@@ -39,6 +43,9 @@ class SESSIONS_EXPORT CommandStorageBackend ...@@ -39,6 +43,9 @@ class SESSIONS_EXPORT CommandStorageBackend
// for testing. // for testing.
static const int kFileReadBufferSize; static const int kFileReadBufferSize;
// Number of bytes encryption adds.
static const size_type kEncryptionOverheadInBytes;
// Creates a CommandStorageBackend. This method is invoked on the MAIN thread, // Creates a CommandStorageBackend. This method is invoked on the MAIN thread,
// and does no IO. The real work is done from Init, which is invoked on // and does no IO. The real work is done from Init, which is invoked on
// a background task runer. // a background task runer.
...@@ -54,15 +61,18 @@ class SESSIONS_EXPORT CommandStorageBackend ...@@ -54,15 +61,18 @@ class SESSIONS_EXPORT CommandStorageBackend
} }
// Appends the specified commands to the current file. If |truncate| is true // Appends the specified commands to the current file. If |truncate| is true
// the file is truncated. // the file is truncated. If |truncate| is true and |crypto_key| is non-empty,
// then all commands are encrypted using the supplied key.
void AppendCommands( void AppendCommands(
std::vector<std::unique_ptr<sessions::SessionCommand>> commands, std::vector<std::unique_ptr<sessions::SessionCommand>> commands,
bool truncate); bool truncate,
const std::vector<uint8_t>& crypto_key = std::vector<uint8_t>());
// Invoked from the service to read the commands that make up the last // Reads the commands that make up the current session. If |crypto_key|
// session, invokes ReadLastSessionCommandsImpl to do the work. // is non-empty, it is used to decrypt the file.
void ReadCurrentSessionCommands( void ReadCurrentSessionCommands(
const base::CancelableTaskTracker::IsCanceledCallback& is_canceled, const base::CancelableTaskTracker::IsCanceledCallback& is_canceled,
const std::vector<uint8_t>& crypto_key,
GetCommandsCallback callback); GetCommandsCallback callback);
bool inited() const { return inited_; } bool inited() const { return inited_; }
...@@ -79,12 +89,12 @@ class SESSIONS_EXPORT CommandStorageBackend ...@@ -79,12 +89,12 @@ class SESSIONS_EXPORT CommandStorageBackend
const base::FilePath& path() const { return path_; } const base::FilePath& path() const { return path_; }
// Reads the commands from the current file. // Reads the commands from the specified file. If |crypto_key| is non-empty,
// // it is used to decrypt the file. On success, the read commands are added to
// On success, the read commands are added to commands. It is up to the // |commands|.
// caller to delete the commands.
bool ReadCommandsFromFile( bool ReadCommandsFromFile(
const base::FilePath& path, const base::FilePath& path,
const std::vector<uint8_t>& crypto_key,
std::vector<std::unique_ptr<sessions::SessionCommand>>* commands); std::vector<std::unique_ptr<sessions::SessionCommand>>* commands);
// Closes the file. The next time AppendCommands() is called the file will // Closes the file. The next time AppendCommands() is called the file will
...@@ -112,6 +122,19 @@ class SESSIONS_EXPORT CommandStorageBackend ...@@ -112,6 +122,19 @@ class SESSIONS_EXPORT CommandStorageBackend
base::File* file, base::File* file,
const std::vector<std::unique_ptr<sessions::SessionCommand>>& commands); const std::vector<std::unique_ptr<sessions::SessionCommand>>& commands);
// Writes |command| to |file|. Returns true on success.
bool AppendCommandToFile(base::File* file,
const sessions::SessionCommand& command);
// Encrypts |command| and writes it to |file|. Returns true on success.
// The contents of the command and id are encrypted together. This is
// preceded by the length of the command.
bool AppendEncryptedCommandToFile(base::File* file,
const sessions::SessionCommand& command);
// Returns true if commands are encrypted.
bool IsEncrypted() const { return !crypto_key_.empty(); }
// Path commands are saved to. // Path commands are saved to.
const base::FilePath path_; const base::FilePath path_;
...@@ -122,6 +145,12 @@ class SESSIONS_EXPORT CommandStorageBackend ...@@ -122,6 +145,12 @@ class SESSIONS_EXPORT CommandStorageBackend
// runner. // runner.
bool inited_ = false; bool inited_ = false;
std::vector<uint8_t> crypto_key_;
std::unique_ptr<crypto::Aead> aead_;
// Incremented every time a command is written.
int commands_written_ = 0;
DISALLOW_COPY_AND_ASSIGN(CommandStorageBackend); DISALLOW_COPY_AND_ASSIGN(CommandStorageBackend);
}; };
......
This diff is collapsed.
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "components/sessions/core/command_storage_backend.h" #include "components/sessions/core/command_storage_backend.h"
#include "components/sessions/core/command_storage_manager_delegate.h" #include "components/sessions/core/command_storage_manager_delegate.h"
#include "crypto/random.h"
namespace sessions { namespace sessions {
namespace { namespace {
...@@ -50,14 +51,24 @@ constexpr base::TimeDelta kSaveDelay = base::TimeDelta::FromMilliseconds(2500); ...@@ -50,14 +51,24 @@ constexpr base::TimeDelta kSaveDelay = base::TimeDelta::FromMilliseconds(2500);
CommandStorageManager::CommandStorageManager( CommandStorageManager::CommandStorageManager(
const base::FilePath& path, const base::FilePath& path,
CommandStorageManagerDelegate* delegate) CommandStorageManagerDelegate* delegate,
bool enable_crypto)
: CommandStorageManager(base::MakeRefCounted<CommandStorageBackend>( : CommandStorageManager(base::MakeRefCounted<CommandStorageBackend>(
CreateDefaultBackendTaskRunner(), CreateDefaultBackendTaskRunner(),
path), path),
delegate) {} delegate) {
use_crypto_ = enable_crypto;
}
CommandStorageManager::~CommandStorageManager() = default; CommandStorageManager::~CommandStorageManager() = default;
// static
std::vector<uint8_t> CommandStorageManager::CreateCryptoKey() {
std::vector<uint8_t> key(32);
crypto::RandBytes(&(key.front()), key.size());
return key;
}
void CommandStorageManager::ScheduleCommand( void CommandStorageManager::ScheduleCommand(
std::unique_ptr<SessionCommand> command) { std::unique_ptr<SessionCommand> command) {
DCHECK(command); DCHECK(command);
...@@ -118,12 +129,15 @@ void CommandStorageManager::Save() { ...@@ -118,12 +129,15 @@ void CommandStorageManager::Save() {
if (pending_commands_.empty()) if (pending_commands_.empty())
return; return;
// We create a new vector which will receive all elements from the std::vector<uint8_t> crypto_key;
// current commands. This will also clear the current list. if (use_crypto_ && pending_reset_) {
crypto_key = CreateCryptoKey();
delegate_->OnGeneratedNewCryptoKey(crypto_key);
}
backend_task_runner()->PostNonNestableTask( backend_task_runner()->PostNonNestableTask(
FROM_HERE, FROM_HERE,
base::BindOnce(&CommandStorageBackend::AppendCommands, backend_, base::BindOnce(&CommandStorageBackend::AppendCommands, backend_,
std::move(pending_commands_), pending_reset_)); std::move(pending_commands_), pending_reset_, crypto_key));
if (pending_reset_) { if (pending_reset_) {
commands_since_reset_ = 0; commands_since_reset_ = 0;
...@@ -138,6 +152,7 @@ bool CommandStorageManager::HasPendingSave() const { ...@@ -138,6 +152,7 @@ bool CommandStorageManager::HasPendingSave() const {
base::CancelableTaskTracker::TaskId base::CancelableTaskTracker::TaskId
CommandStorageManager::ScheduleGetCurrentSessionCommands( CommandStorageManager::ScheduleGetCurrentSessionCommands(
GetCommandsCallback callback, GetCommandsCallback callback,
const std::vector<uint8_t>& decryption_key,
base::CancelableTaskTracker* tracker) { base::CancelableTaskTracker* tracker) {
base::CancelableTaskTracker::IsCanceledCallback is_canceled; base::CancelableTaskTracker::IsCanceledCallback is_canceled;
GetCommandsCallback backend_callback; GetCommandsCallback backend_callback;
...@@ -147,7 +162,8 @@ CommandStorageManager::ScheduleGetCurrentSessionCommands( ...@@ -147,7 +162,8 @@ CommandStorageManager::ScheduleGetCurrentSessionCommands(
backend_task_runner()->PostNonNestableTask( backend_task_runner()->PostNonNestableTask(
FROM_HERE, FROM_HERE,
base::BindOnce(&CommandStorageBackend::ReadCurrentSessionCommands, base::BindOnce(&CommandStorageBackend::ReadCurrentSessionCommands,
backend_.get(), is_canceled, std::move(backend_callback))); backend_.get(), is_canceled, decryption_key,
std::move(backend_callback)));
return id; return id;
} }
......
...@@ -5,7 +5,10 @@ ...@@ -5,7 +5,10 @@
#ifndef COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_MANAGER_H_ #ifndef COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_MANAGER_H_
#define COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_MANAGER_H_ #define COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_MANAGER_H_
#include <stddef.h>
#include <memory> #include <memory>
#include <vector>
#include "base/callback.h" #include "base/callback.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
...@@ -37,12 +40,16 @@ class SESSIONS_EXPORT CommandStorageManager { ...@@ -37,12 +40,16 @@ class SESSIONS_EXPORT CommandStorageManager {
// Creates a new CommandStorageManager. After creation you need to invoke // Creates a new CommandStorageManager. After creation you need to invoke
// Init. |delegate| will remain owned by the creator and it is guaranteed // Init. |delegate| will remain owned by the creator and it is guaranteed
// that its lifetime surpasses this class. // that its lifetime surpasses this class. |path| is the path to save files
// |type| gives the type of session service, |path| the path to save files to. // to. If |enable_crypto| is true, the contents of the file are encrypted.
CommandStorageManager(const base::FilePath& path, CommandStorageManager(const base::FilePath& path,
CommandStorageManagerDelegate* delegate); CommandStorageManagerDelegate* delegate,
bool enable_crypto = false);
virtual ~CommandStorageManager(); virtual ~CommandStorageManager();
// Helper to generate a new key.
static std::vector<uint8_t> CreateCryptoKey();
// Returns the set of commands which were scheduled to be written. Once // Returns the set of commands which were scheduled to be written. Once
// committed to the backend, the commands are removed from here. // committed to the backend, the commands are removed from here.
const std::vector<std::unique_ptr<SessionCommand>>& pending_commands() { const std::vector<std::unique_ptr<SessionCommand>>& pending_commands() {
...@@ -88,9 +95,11 @@ class SESSIONS_EXPORT CommandStorageManager { ...@@ -88,9 +95,11 @@ class SESSIONS_EXPORT CommandStorageManager {
// occurred. // occurred.
bool HasPendingSave() const; bool HasPendingSave() const;
// Requests the commands for the current session. // Requests the commands for the current session. If |decryption_key| is
// non-empty it is used to decrypt the contents of the file.
base::CancelableTaskTracker::TaskId ScheduleGetCurrentSessionCommands( base::CancelableTaskTracker::TaskId ScheduleGetCurrentSessionCommands(
GetCommandsCallback callback, GetCommandsCallback callback,
const std::vector<uint8_t>& decryption_key,
base::CancelableTaskTracker* tracker); base::CancelableTaskTracker* tracker);
protected: protected:
...@@ -122,6 +131,9 @@ class SESSIONS_EXPORT CommandStorageManager { ...@@ -122,6 +131,9 @@ class SESSIONS_EXPORT CommandStorageManager {
// The backend object which reads and saves commands. // The backend object which reads and saves commands.
scoped_refptr<CommandStorageBackend> backend_; scoped_refptr<CommandStorageBackend> backend_;
// If true, all commands are encrypted.
bool use_crypto_ = false;
// Commands we need to send over to the backend. // Commands we need to send over to the backend.
std::vector<std::unique_ptr<SessionCommand>> pending_commands_; std::vector<std::unique_ptr<SessionCommand>> pending_commands_;
......
...@@ -5,6 +5,10 @@ ...@@ -5,6 +5,10 @@
#ifndef COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_MANAGER_DELEGATE_H_ #ifndef COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_MANAGER_DELEGATE_H_
#define COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_MANAGER_DELEGATE_H_ #define COMPONENTS_SESSIONS_CORE_COMMAND_STORAGE_MANAGER_DELEGATE_H_
#include <stddef.h>
#include <vector>
namespace sessions { namespace sessions {
// The CommandStorageManagerDelegate decouples the CommandStorageManager from // The CommandStorageManagerDelegate decouples the CommandStorageManager from
...@@ -20,6 +24,10 @@ class CommandStorageManagerDelegate { ...@@ -20,6 +24,10 @@ class CommandStorageManagerDelegate {
// Called when commands are about to be written to disc. // Called when commands are about to be written to disc.
virtual void OnWillSaveCommands() {} virtual void OnWillSaveCommands() {}
// Called when a new crypto key has been generated. This is only called if
// CommandStorageManager was configured to enable encryption.
virtual void OnGeneratedNewCryptoKey(const std::vector<uint8_t>& key) {}
protected: protected:
virtual ~CommandStorageManagerDelegate() {} virtual ~CommandStorageManagerDelegate() {}
}; };
......
...@@ -62,7 +62,7 @@ void SnapshottingCommandStorageBackend::ReadLastSessionCommands( ...@@ -62,7 +62,7 @@ void SnapshottingCommandStorageBackend::ReadLastSessionCommands(
InitIfNecessary(); InitIfNecessary();
std::vector<std::unique_ptr<sessions::SessionCommand>> commands; std::vector<std::unique_ptr<sessions::SessionCommand>> commands;
ReadCommandsFromFile(last_file_path_, &commands); ReadCommandsFromFile(last_file_path_, std::vector<uint8_t>(), &commands);
std::move(callback).Run(std::move(commands)); std::move(callback).Run(std::move(commands));
} }
......
...@@ -69,7 +69,7 @@ SessionService::SessionService(const base::FilePath& path, BrowserImpl* browser) ...@@ -69,7 +69,7 @@ SessionService::SessionService(const base::FilePath& path, BrowserImpl* browser)
command_storage_manager_->ScheduleGetCurrentSessionCommands( command_storage_manager_->ScheduleGetCurrentSessionCommands(
base::BindOnce(&SessionService::OnGotCurrentSessionCommands, base::BindOnce(&SessionService::OnGotCurrentSessionCommands,
base::Unretained(this)), base::Unretained(this)),
&cancelable_task_tracker_); std::vector<uint8_t>(), &cancelable_task_tracker_);
} }
SessionService::~SessionService() { SessionService::~SessionService() {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment