1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "components/os_crypt/os_crypt.h" 6 7#include <CommonCrypto/CommonCryptor.h> // for kCCBlockSizeAES128 8 9#include "base/command_line.h" 10#include "base/debug/leak_annotations.h" 11#include "base/logging.h" 12#include "base/memory/scoped_ptr.h" 13#include "base/strings/utf_string_conversions.h" 14#include "base/synchronization/lock.h" 15#include "components/os_crypt/keychain_password_mac.h" 16#include "components/os_crypt/os_crypt_switches.h" 17#include "crypto/apple_keychain.h" 18#include "crypto/encryptor.h" 19#include "crypto/symmetric_key.h" 20 21using crypto::AppleKeychain; 22 23namespace { 24 25// Salt for Symmetric key derivation. 26const char kSalt[] = "saltysalt"; 27 28// Key size required for 128 bit AES. 29const size_t kDerivedKeySizeInBits = 128; 30 31// Constant for Symmetic key derivation. 32const size_t kEncryptionIterations = 1003; 33 34// TODO(dhollowa): Refactor to allow dependency injection of Keychain. 35static bool use_mock_keychain = false; 36 37// Prefix for cypher text returned by current encryption version. We prefix 38// the cypher text with this string so that future data migration can detect 39// this and migrate to different encryption without data loss. 40const char kEncryptionVersionPrefix[] = "v10"; 41 42// Generates a newly allocated SymmetricKey object based on the password found 43// in the Keychain. The generated key is for AES encryption. Returns NULL key 44// in the case password access is denied or key generation error occurs. 45crypto::SymmetricKey* GetEncryptionKey() { 46 static crypto::SymmetricKey* cached_encryption_key = NULL; 47 static bool key_is_cached = false; 48 static base::Lock lock; 49 base::AutoLock auto_lock(lock); 50 51 if (key_is_cached) 52 return cached_encryption_key; 53 54 static bool mock_keychain_command_line_flag = 55 CommandLine::ForCurrentProcess()->HasSwitch( 56 os_crypt::switches::kUseMockKeychain); 57 58 std::string password; 59 if (use_mock_keychain || mock_keychain_command_line_flag) { 60 password = "mock_password"; 61 } else { 62 AppleKeychain keychain; 63 KeychainPassword encryptor_password(keychain); 64 password = encryptor_password.GetPassword(); 65 } 66 67 // Subsequent code must guarantee that the correct key is cached before 68 // returning. 69 key_is_cached = true; 70 71 if (password.empty()) 72 return cached_encryption_key; 73 74 std::string salt(kSalt); 75 76 // Create an encryption key from our password and salt. The key is 77 // intentionally leaked. 78 cached_encryption_key = 79 crypto::SymmetricKey::DeriveKeyFromPassword(crypto::SymmetricKey::AES, 80 password, 81 salt, 82 kEncryptionIterations, 83 kDerivedKeySizeInBits); 84 ANNOTATE_LEAKING_OBJECT_PTR(cached_encryption_key); 85 DCHECK(cached_encryption_key); 86 return cached_encryption_key; 87} 88 89} // namespace 90 91bool OSCrypt::EncryptString16(const base::string16& plaintext, 92 std::string* ciphertext) { 93 return EncryptString(base::UTF16ToUTF8(plaintext), ciphertext); 94} 95 96bool OSCrypt::DecryptString16(const std::string& ciphertext, 97 base::string16* plaintext) { 98 std::string utf8; 99 if (!DecryptString(ciphertext, &utf8)) 100 return false; 101 102 *plaintext = base::UTF8ToUTF16(utf8); 103 return true; 104} 105 106bool OSCrypt::EncryptString(const std::string& plaintext, 107 std::string* ciphertext) { 108 if (plaintext.empty()) { 109 *ciphertext = std::string(); 110 return true; 111 } 112 113 crypto::SymmetricKey* encryption_key = GetEncryptionKey(); 114 if (!encryption_key) 115 return false; 116 117 std::string iv(kCCBlockSizeAES128, ' '); 118 crypto::Encryptor encryptor; 119 if (!encryptor.Init(encryption_key, crypto::Encryptor::CBC, iv)) 120 return false; 121 122 if (!encryptor.Encrypt(plaintext, ciphertext)) 123 return false; 124 125 // Prefix the cypher text with version information. 126 ciphertext->insert(0, kEncryptionVersionPrefix); 127 return true; 128} 129 130bool OSCrypt::DecryptString(const std::string& ciphertext, 131 std::string* plaintext) { 132 if (ciphertext.empty()) { 133 *plaintext = std::string(); 134 return true; 135 } 136 137 // Check that the incoming cyphertext was indeed encrypted with the expected 138 // version. If the prefix is not found then we'll assume we're dealing with 139 // old data saved as clear text and we'll return it directly. 140 // Credit card numbers are current legacy data, so false match with prefix 141 // won't happen. 142 if (ciphertext.find(kEncryptionVersionPrefix) != 0) { 143 *plaintext = ciphertext; 144 return true; 145 } 146 147 // Strip off the versioning prefix before decrypting. 148 std::string raw_ciphertext = 149 ciphertext.substr(strlen(kEncryptionVersionPrefix)); 150 151 crypto::SymmetricKey* encryption_key = GetEncryptionKey(); 152 if (!encryption_key) 153 return false; 154 155 std::string iv(kCCBlockSizeAES128, ' '); 156 crypto::Encryptor encryptor; 157 if (!encryptor.Init(encryption_key, crypto::Encryptor::CBC, iv)) 158 return false; 159 160 if (!encryptor.Decrypt(raw_ciphertext, plaintext)) 161 return false; 162 163 return true; 164} 165 166void OSCrypt::UseMockKeychain(bool use_mock) { 167 use_mock_keychain = use_mock; 168} 169 170