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 21 using crypto::AppleKeychain; 22 23 namespace { 24 25 // Salt for Symmetric key derivation. 26 const char kSalt[] = "saltysalt"; 27 28 // Key size required for 128 bit AES. 29 const size_t kDerivedKeySizeInBits = 128; 30 31 // Constant for Symmetic key derivation. 32 const size_t kEncryptionIterations = 1003; 33 34 // TODO(dhollowa): Refactor to allow dependency injection of Keychain. 35 static 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. 40 const 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. 45 crypto::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 91 bool OSCrypt::EncryptString16(const base::string16& plaintext, 92 std::string* ciphertext) { 93 return EncryptString(base::UTF16ToUTF8(plaintext), ciphertext); 94 } 95 96 bool 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 106 bool 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 130 bool 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 166 void OSCrypt::UseMockKeychain(bool use_mock) { 167 use_mock_keychain = use_mock; 168 } 169 170