| // Copyright 2016 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 "components/os_crypt/os_crypt.h" |
| |
| #include <stddef.h> |
| |
| #include <algorithm> |
| #include <iterator> |
| #include <memory> |
| |
| #include "base/cxx17_backports.h" |
| #include "base/logging.h" |
| #include "base/memory/singleton.h" |
| #include "base/no_destructor.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/synchronization/lock.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "components/os_crypt/key_storage_config_linux.h" |
| #include "components/os_crypt/key_storage_linux.h" |
| #include "crypto/encryptor.h" |
| #include "crypto/symmetric_key.h" |
| |
| namespace { |
| |
| // Salt for Symmetric key derivation. |
| constexpr char kSalt[] = "saltysalt"; |
| |
| // Key size required for 128 bit AES. |
| constexpr size_t kDerivedKeySizeInBits = 128; |
| |
| // Constant for Symmetric key derivation. |
| constexpr size_t kEncryptionIterations = 1; |
| |
| // Size of initialization vector for AES 128-bit. |
| constexpr size_t kIVBlockSizeAES128 = 16; |
| |
| // Prefixes for cypher text returned by obfuscation version. We prefix the |
| // ciphertext with this string so that future data migration can detect |
| // this and migrate to full encryption without data loss. kObfuscationPrefixV10 |
| // means that the hardcoded password will be used. kObfuscationPrefixV11 means |
| // that a password is/will be stored using an OS-level library (e.g Libsecret). |
| // V11 will not be used if such a library is not available. |
| constexpr char kObfuscationPrefixV10[] = "v10"; |
| constexpr char kObfuscationPrefixV11[] = "v11"; |
| |
| // Generates a newly allocated SymmetricKey object based on a password. |
| // Ownership of the key is passed to the caller. Returns null key if a key |
| // generation error occurs. |
| std::unique_ptr<crypto::SymmetricKey> GenerateEncryptionKey( |
| const std::string& password) { |
| const std::string salt(kSalt); |
| |
| // Create an encryption key from our password and salt. |
| std::unique_ptr<crypto::SymmetricKey> encryption_key( |
| crypto::SymmetricKey::DeriveKeyFromPasswordUsingPbkdf2( |
| crypto::SymmetricKey::AES, password, salt, kEncryptionIterations, |
| kDerivedKeySizeInBits)); |
| DCHECK(encryption_key); |
| |
| return encryption_key; |
| } |
| |
| } // namespace |
| |
| namespace OSCrypt { |
| void SetConfig(std::unique_ptr<os_crypt::Config> config) { |
| OSCryptImpl::GetInstance()->SetConfig(std::move(config)); |
| } |
| bool EncryptString16(const std::u16string& plaintext, std::string* ciphertext) { |
| return OSCryptImpl::GetInstance()->EncryptString16(plaintext, ciphertext); |
| } |
| bool DecryptString16(const std::string& ciphertext, std::u16string* plaintext) { |
| return OSCryptImpl::GetInstance()->DecryptString16(ciphertext, plaintext); |
| } |
| bool EncryptString(const std::string& plaintext, std::string* ciphertext) { |
| return OSCryptImpl::GetInstance()->EncryptString(plaintext, ciphertext); |
| } |
| bool DecryptString(const std::string& ciphertext, std::string* plaintext) { |
| return OSCryptImpl::GetInstance()->DecryptString(ciphertext, plaintext); |
| } |
| std::string GetRawEncryptionKey() { |
| return OSCryptImpl::GetInstance()->GetRawEncryptionKey(); |
| } |
| void SetRawEncryptionKey(const std::string& key) { |
| OSCryptImpl::GetInstance()->SetRawEncryptionKey(key); |
| } |
| bool IsEncryptionAvailable() { |
| return OSCryptImpl::GetInstance()->IsEncryptionAvailable(); |
| } |
| void UseMockKeyStorageForTesting( |
| base::OnceCallback<std::unique_ptr<KeyStorageLinux>()> |
| storage_provider_factory) { |
| OSCryptImpl::GetInstance()->UseMockKeyStorageForTesting( |
| std::move(storage_provider_factory)); |
| } |
| void ClearCacheForTesting() { |
| OSCryptImpl::GetInstance()->ClearCacheForTesting(); |
| } |
| void SetEncryptionPasswordForTesting(const std::string& password) { |
| OSCryptImpl::GetInstance()->SetEncryptionPasswordForTesting(password); |
| } |
| } // namespace OSCrypt |
| |
| OSCryptImpl* OSCryptImpl::GetInstance() { |
| return base::Singleton<OSCryptImpl, |
| base::LeakySingletonTraits<OSCryptImpl>>::get(); |
| } |
| |
| OSCryptImpl::OSCryptImpl() |
| : storage_provider_factory_(base::BindOnce(&OSCryptImpl::CreateKeyStorage, |
| base::Unretained(this))) {} |
| |
| OSCryptImpl::~OSCryptImpl() = default; |
| |
| bool OSCryptImpl::EncryptString16(const std::u16string& plaintext, |
| std::string* ciphertext) { |
| return EncryptString(base::UTF16ToUTF8(plaintext), ciphertext); |
| } |
| |
| bool OSCryptImpl::DecryptString16(const std::string& ciphertext, |
| std::u16string* plaintext) { |
| std::string utf8; |
| if (!DecryptString(ciphertext, &utf8)) |
| return false; |
| |
| *plaintext = base::UTF8ToUTF16(utf8); |
| return true; |
| } |
| |
| bool OSCryptImpl::EncryptString(const std::string& plaintext, |
| std::string* ciphertext) { |
| if (plaintext.empty()) { |
| ciphertext->clear(); |
| return true; |
| } |
| |
| // If we are able to create a V11 key (i.e. a KeyStorage was available), then |
| // we'll use it. If not, we'll use V10. |
| crypto::SymmetricKey* encryption_key = GetPasswordV11(); |
| std::string obfuscation_prefix = kObfuscationPrefixV11; |
| if (!encryption_key) { |
| encryption_key = GetPasswordV10(); |
| obfuscation_prefix = kObfuscationPrefixV10; |
| } |
| |
| if (!encryption_key) |
| return false; |
| |
| const std::string iv(kIVBlockSizeAES128, ' '); |
| crypto::Encryptor encryptor; |
| if (!encryptor.Init(encryption_key, crypto::Encryptor::CBC, iv)) |
| return false; |
| |
| if (!encryptor.Encrypt(plaintext, ciphertext)) |
| return false; |
| |
| // Prefix the cipher text with version information. |
| ciphertext->insert(0, obfuscation_prefix); |
| return true; |
| } |
| |
| bool OSCryptImpl::DecryptString(const std::string& ciphertext, |
| std::string* plaintext) { |
| if (ciphertext.empty()) { |
| plaintext->clear(); |
| return true; |
| } |
| |
| // Check that the incoming ciphertext was encrypted and with what version. |
| // Credit card numbers are current legacy unencrypted data, so false match |
| // with prefix won't happen. |
| crypto::SymmetricKey* encryption_key = nullptr; |
| std::string obfuscation_prefix; |
| if (base::StartsWith(ciphertext, kObfuscationPrefixV10, |
| base::CompareCase::SENSITIVE)) { |
| encryption_key = GetPasswordV10(); |
| obfuscation_prefix = kObfuscationPrefixV10; |
| } else if (base::StartsWith(ciphertext, kObfuscationPrefixV11, |
| base::CompareCase::SENSITIVE)) { |
| encryption_key = GetPasswordV11(); |
| obfuscation_prefix = kObfuscationPrefixV11; |
| } else { |
| // If the prefix is not found then we'll assume we're dealing with |
| // old data saved as clear text and we'll return it directly. |
| *plaintext = ciphertext; |
| return true; |
| } |
| |
| if (!encryption_key) { |
| VLOG(1) << "Decryption failed: could not get the key"; |
| return false; |
| } |
| |
| const std::string iv(kIVBlockSizeAES128, ' '); |
| crypto::Encryptor encryptor; |
| if (!encryptor.Init(encryption_key, crypto::Encryptor::CBC, iv)) |
| return false; |
| |
| // Strip off the versioning prefix before decrypting. |
| const std::string raw_ciphertext = |
| ciphertext.substr(obfuscation_prefix.length()); |
| |
| if (!encryptor.Decrypt(raw_ciphertext, plaintext)) { |
| VLOG(1) << "Decryption failed"; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void OSCryptImpl::SetConfig(std::unique_ptr<os_crypt::Config> config) { |
| // Setting initialisation parameters makes no sense after initializing. |
| DCHECK(!is_password_v11_cached_); |
| config_ = std::move(config); |
| } |
| |
| bool OSCryptImpl::IsEncryptionAvailable() { |
| return GetPasswordV11(); |
| } |
| |
| void OSCryptImpl::SetRawEncryptionKey(const std::string& raw_key) { |
| base::AutoLock auto_lock(OSCryptImpl::GetLock()); |
| // Check if the v11 password is already cached. If it is, then data encrypted |
| // with the old password might not be decryptable. |
| DCHECK(!is_password_v11_cached_); |
| // The config won't be used if this function is being called. Callers should |
| // choose between setting a config and setting a raw encryption key. |
| DCHECK(!config_); |
| if (!raw_key.empty()) { |
| password_v11_cache_ = |
| crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, raw_key); |
| } |
| // Always set |is_password_v11_cached_|, even if given an empty string. |
| // Note that |raw_key| can be an empty string if real V11 encryption is not |
| // available, and setting |is_password_v11_cached_| causes GetPasswordV11() to |
| // correctly return nullptr in that case. |
| is_password_v11_cached_ = true; |
| } |
| |
| std::string OSCryptImpl::GetRawEncryptionKey() { |
| if (crypto::SymmetricKey* key = GetPasswordV11()) |
| return key->key(); |
| return std::string(); |
| } |
| |
| void OSCryptImpl::ClearCacheForTesting() { |
| password_v10_cache_.reset(); |
| password_v11_cache_.reset(); |
| is_password_v11_cached_ = false; |
| config_.reset(); |
| } |
| |
| void OSCryptImpl::UseMockKeyStorageForTesting( |
| base::OnceCallback<std::unique_ptr<KeyStorageLinux>()> |
| storage_provider_factory) { |
| if (storage_provider_factory) |
| storage_provider_factory_ = std::move(storage_provider_factory); |
| else |
| storage_provider_factory_ = |
| base::BindOnce(&OSCryptImpl::CreateKeyStorage, base::Unretained(this)); |
| } |
| |
| // Create the KeyStorage. Will be null if no service is found. A Config must be |
| // set before every call to this function. |
| std::unique_ptr<KeyStorageLinux> OSCryptImpl::CreateKeyStorage() { |
| CHECK(config_); |
| std::unique_ptr<KeyStorageLinux> key_storage = |
| KeyStorageLinux::CreateService(*config_); |
| config_.reset(); |
| return key_storage; |
| } |
| |
| void OSCryptImpl::SetEncryptionPasswordForTesting(const std::string& password) { |
| ClearCacheForTesting(); // IN-TEST |
| password_v11_cache_ = GenerateEncryptionKey(password); |
| is_password_v11_cached_ = true; |
| } |
| |
| // Returns a cached string of "peanuts". Is thread-safe. |
| crypto::SymmetricKey* OSCryptImpl::GetPasswordV10() { |
| base::AutoLock auto_lock(OSCryptImpl::GetLock()); |
| if (!password_v10_cache_.get()) { |
| password_v10_cache_ = GenerateEncryptionKey("peanuts"); |
| } |
| return password_v10_cache_.get(); |
| } |
| |
| // Caches and returns the password from the KeyStorage or null if there is no |
| // service. Is thread-safe. |
| crypto::SymmetricKey* OSCryptImpl::GetPasswordV11() { |
| base::AutoLock auto_lock(OSCryptImpl::GetLock()); |
| if (!is_password_v11_cached_) { |
| std::unique_ptr<KeyStorageLinux> key_storage = |
| std::move(storage_provider_factory_).Run(); |
| if (key_storage) { |
| absl::optional<std::string> key = key_storage->GetKey(); |
| if (key.has_value()) { |
| password_v11_cache_ = GenerateEncryptionKey(*key); |
| } |
| } |
| is_password_v11_cached_ = true; |
| } |
| return password_v11_cache_.get(); |
| } |
| |
| // static |
| base::Lock& OSCryptImpl::GetLock() { |
| static base::NoDestructor<base::Lock> os_crypt_lock; |
| return *os_crypt_lock; |
| } |