Skip to content

Commit

Permalink
[OSCrypt] Allow to create multiple OSCrypt instances
Browse files Browse the repository at this point in the history
Removed leaking global variables. Replaced pointer to function
with OnceCallback. Next step update callers to use signleton
interface and remove free function interface, update unittests.

Bug: 1308315
Change-Id: I84096aacffb74aa15beb0ac978bb6e1fba1793a9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3535979
Reviewed-by: Christos Froussios <cfroussios@chromium.org>
Commit-Queue: Paweł Barań <pbaran@opera.com>
Cr-Commit-Position: refs/heads/main@{#984295}
  • Loading branch information
Pawel Baran authored and Chromium LUCI CQ committed Mar 23, 2022
1 parent eb06dc9 commit 3162aeb
Show file tree
Hide file tree
Showing 6 changed files with 324 additions and 308 deletions.
150 changes: 108 additions & 42 deletions components/os_crypt/os_crypt.h
Expand Up @@ -8,6 +8,7 @@
#include <memory>
#include <string>

#include "base/callback.h"
#include "base/component_export.h"
#include "base/memory/ref_counted.h"
#include "base/task/single_thread_task_runner.h"
Expand All @@ -23,6 +24,12 @@ class PrefRegistrySimple;
class PrefService;
#endif

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_APPLE)
namespace crypto {
class SymmetricKey;
}
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_APPLE)

namespace os_crypt {
struct Config;
}
Expand Down Expand Up @@ -78,7 +85,8 @@ COMPONENT_EXPORT(OS_CRYPT) void ResetStateForTesting();
#if (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMECAST))
COMPONENT_EXPORT(OS_CRYPT)
void UseMockKeyStorageForTesting(
std::unique_ptr<KeyStorageLinux> (*get_key_storage_mock)());
base::OnceCallback<std::unique_ptr<KeyStorageLinux>()>
storage_provider_factory);
COMPONENT_EXPORT(OS_CRYPT) void ClearCacheForTesting();
COMPONENT_EXPORT(OS_CRYPT)
void SetEncryptionPasswordForTesting(const std::string& password);
Expand All @@ -89,18 +97,23 @@ void SetEncryptionPasswordForTesting(const std::string& password);
// strings. Note that on Mac, access to the system Keychain is required and
// these calls can block the current thread to collect user input. The same is
// true for Linux, if a password management tool is available.
class OSCryptImpl {
class COMPONENT_EXPORT(OS_CRYPT) OSCryptImpl {
public:
OSCryptImpl() = delete;
OSCryptImpl();
~OSCryptImpl();
OSCryptImpl(const OSCryptImpl&) = delete;
OSCryptImpl(OSCryptImpl&&) = delete;
OSCryptImpl& operator=(const OSCryptImpl&) = delete;
OSCryptImpl& operator=(OSCryptImpl&&) = delete;

// Returns singleton instance of OSCryptImpl.
static OSCryptImpl* GetInstance();

#if BUILDFLAG(IS_LINUX)
// Set the configuration of OSCryptImpl.
// This method, or SetRawEncryptionKey(), must be called before using
// EncryptString() and DecryptString().
static void SetConfig(
std::unique_ptr<os_crypt::Config> config);
void SetConfig(std::unique_ptr<os_crypt::Config> config);
#endif // BUILDFLAG(IS_LINUX)

// On Linux returns true iff the real secret key (not hardcoded one) is
Expand All @@ -109,64 +122,54 @@ class OSCryptImpl {
// locked mock Keychain). On Windows returns true if non mock encryption
// key is available. On other platforms, returns false as OSCryptImpl will use
// a hardcoded key.
static bool IsEncryptionAvailable();
bool IsEncryptionAvailable();

// Encrypt a string16. The output (second argument) is really an array of
// bytes, but we're passing it back as a std::string.
static bool EncryptString16(
const std::u16string& plaintext,
std::string* ciphertext);
bool EncryptString16(const std::u16string& plaintext,
std::string* ciphertext);

// Decrypt an array of bytes obtained with EncryptString16 back into a
// string16. Note that the input (first argument) is a std::string, so you
// need to first get your (binary) data into a string.
static bool DecryptString16(
const std::string& ciphertext,
std::u16string* plaintext);
bool DecryptString16(const std::string& ciphertext,
std::u16string* plaintext);

// Encrypt a string.
static bool EncryptString(
const std::string& plaintext,
std::string* ciphertext);
bool EncryptString(const std::string& plaintext, std::string* ciphertext);

// Decrypt an array of bytes obtained with EnctryptString back into a string.
// Note that the input (first argument) is a std::string, so you need to first
// get your (binary) data into a string.
static bool DecryptString(
const std::string& ciphertext,
std::string* plaintext);
bool DecryptString(const std::string& ciphertext, std::string* plaintext);

#if BUILDFLAG(IS_WIN)
// Registers preferences used by OSCryptImpl.
static void RegisterLocalPrefs(
PrefRegistrySimple* registry);
static void RegisterLocalPrefs(PrefRegistrySimple* registry);

// Initialises OSCryptImpl.
// This method should be called on the main UI thread before any calls to
// encryption or decryption. Returns |true| if os_crypt successfully
// initialized.
static bool Init(PrefService* local_state);
bool Init(PrefService* local_state);

// Initialises OSCryptImpl using an encryption key present in the
// |local_state|. It is similar to the Init() method above, however, it will
// not create a new encryption key if it is not present in the |local_state|.

static OSCrypt::InitResult
InitWithExistingKey(PrefService* local_state);
OSCrypt::InitResult InitWithExistingKey(PrefService* local_state);
#endif

#if BUILDFLAG(IS_APPLE)
// For unit testing purposes we instruct the Encryptor to use a mock Keychain
// on the Mac. The default is to use the real Keychain. Use OSCryptMocker,
// instead of calling this method directly.
static void UseMockKeychainForTesting(
bool use_mock);
void UseMockKeychainForTesting(bool use_mock);

// When Keychain is locked, it's not possible to get the encryption key. This
// is used only for testing purposes. Enabling locked Keychain also enables
// mock Keychain. Use OSCryptMocker, instead of calling this method directly.
static void UseLockedMockKeychainForTesting(
bool use_locked);
void UseLockedMockKeychainForTesting(bool use_locked);
#endif

// Get the raw encryption key to be used for all AES encryption. The result
Expand All @@ -176,46 +179,109 @@ class OSCryptImpl {
// - key generation error
// - if a hardcoded password is used instead of a random per-user key
// This method is thread-safe.
static std::string GetRawEncryptionKey();
std::string GetRawEncryptionKey();

// Set the raw encryption key to be used for all AES encryption.
// On platforms that may use a hardcoded key, |key| can be empty and
// OSCryptImpl will default to the hardcoded key. This method is thread-safe.
static void SetRawEncryptionKey(
const std::string& key);
void SetRawEncryptionKey(const std::string& key);

#if BUILDFLAG(IS_WIN)
// For unit testing purposes we instruct the Encryptor to use a mock Key. The
// default is to use the real Key bound to profile. Use OSCryptMocker, instead
// of calling this method directly.
static void UseMockKeyForTesting(bool use_mock);
void UseMockKeyForTesting(bool use_mock);

// For unit testing purposes, encrypt data using the older DPAPI method rather
// than using a session key.
static void SetLegacyEncryptionForTesting(
bool legacy);
void SetLegacyEncryptionForTesting(bool legacy);

// For unit testing purposes, reset the state of OSCryptImpl so a new key can
// be loaded via Init() or SetRawEncryptionkey().
static void ResetStateForTesting();
void ResetStateForTesting();
#endif

#if (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMECAST))
// For unit testing purposes, inject methods to be used.
// |get_key_storage_mock| provides the desired |KeyStorage| implementation.
// If the provider returns |nullptr|, a hardcoded password will be used.
// If |get_key_storage_mock| is nullptr, restores the real implementation.
static void UseMockKeyStorageForTesting(
std::unique_ptr<KeyStorageLinux> (*get_key_storage_mock)());
// |storage_provider_factory| provides the desired |KeyStorage|
// implementation. If the provider returns |nullptr|, a hardcoded password
// will be used. If |storage_provider_factory| is null callback, restores the
// real implementation.
void UseMockKeyStorageForTesting(
base::OnceCallback<std::unique_ptr<KeyStorageLinux>()>
storage_provider_factory);

// Clears any caching and most lazy initialisations performed by the
// production code. Should be used after any test which required a password.
static void ClearCacheForTesting();
void ClearCacheForTesting();

// Sets the password with which the encryption key is derived, e.g. "peanuts".
static void SetEncryptionPasswordForTesting(
const std::string& password);
void SetEncryptionPasswordForTesting(const std::string& password);
#endif // (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMECAST))
private:
#if BUILDFLAG(IS_APPLE)
// Generates a newly allocated SymmetricKey object based on the password found
// in the Keychain. The generated key is for AES encryption. Returns NULL
// key in the case password access is denied or key generation error occurs.
crypto::SymmetricKey* GetEncryptionKey();
#endif // BUILDFLAG(IS_APPLE)

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_APPLE)
// This lock is used to make the GetEncryptionKey and
// GetRawEncryptionKey methods thread-safe.
static base::Lock& GetLock();
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_APPLE)

#if BUILDFLAG(IS_LINUX)
// Create the KeyStorage. Will be null if no service is found. A Config must
// be set before every call to this method.
std::unique_ptr<KeyStorageLinux> CreateKeyStorage();

// Returns a cached string of "peanuts". Is thread-safe.
crypto::SymmetricKey* GetPasswordV10();

// Caches and returns the password from the KeyStorage or null if there is no
// service. Is thread-safe.
crypto::SymmetricKey* GetPasswordV11();

// For password_v10, nullptr means uninitialised.
std::unique_ptr<crypto::SymmetricKey> password_v10_cache_;

// For password_v11, nullptr means no backend.
std::unique_ptr<crypto::SymmetricKey> password_v11_cache_;

bool is_password_v11_cached_ = false;

// |config_| is used to initialise |password_v11_cache_| and then cleared.
std::unique_ptr<os_crypt::Config> config_;

base::OnceCallback<std::unique_ptr<KeyStorageLinux>()>
storage_provider_factory_;
#endif // BUILDFLAG(IS_LINUX)

#if BUILDFLAG(IS_WIN)
// Use mock key instead of a real encryption key. Used for testing.
bool use_mock_key_ = false;

// Store data using the legacy (DPAPI) method rather than session key.
bool use_legacy_ = false;

// Encryption Key. Set either by calling Init() or SetRawEncryptionKey().
std::string encryption_key_;

// Mock Encryption Key. Only set and used if use_mock_key_ is true.
std::string mock_encryption_key_;
#endif // BUILDFLAG(IS_WIN)

#if BUILDFLAG(IS_APPLE)
// The cached AES encryption key.
std::unique_ptr<crypto::SymmetricKey> cached_encryption_key_;
// TODO(dhollowa): Refactor to allow dependency injection of Keychain.
bool use_mock_keychain_ = false;
// This flag is used to make the GetEncryptionKey method return NULL if used
// along with mock Keychain.
bool use_locked_mock_keychain_ = false;
#endif
};

#endif // COMPONENTS_OS_CRYPT_OS_CRYPT_H_

0 comments on commit 3162aeb

Please sign in to comment.