1 // Copyright (c) 2011 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 "chrome/browser/password_manager/encryptor.h" 6 7 #include <CommonCrypto/CommonCryptor.h> // for kCCBlockSizeAES128 8 9 #include "base/logging.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/utf_string_conversions.h" 12 #include "crypto/encryptor.h" 13 #include "crypto/symmetric_key.h" 14 #include "chrome/browser/password_manager/encryptor_password_mac.h" 15 #include "chrome/browser/keychain_mac.h" 16 17 namespace { 18 19 // Salt for Symmetric key derivation. 20 const char kSalt[] = "saltysalt"; 21 22 // Key size required for 128 bit AES. 23 const size_t kDerivedKeySizeInBits = 128; 24 25 // Constant for Symmetic key derivation. 26 const size_t kEncryptionIterations = 1003; 27 28 // TODO(dhollowa): Refactor to allow dependency injection of Keychain. 29 static bool use_mock_keychain = false; 30 31 // Prefix for cypher text returned by current encryption version. We prefix 32 // the cypher text with this string so that future data migration can detect 33 // this and migrate to different encryption without data loss. 34 const char kEncryptionVersionPrefix[] = "v10"; 35 36 // Generates a newly allocated SymmetricKey object based on the password found 37 // in the Keychain. The generated key is for AES encryption. Ownership of the 38 // key is passed to the caller. Returns NULL key in the case password access 39 // is denied or key generation error occurs. 40 crypto::SymmetricKey* GetEncryptionKey() { 41 42 std::string password; 43 if (use_mock_keychain) { 44 password = "mock_password"; 45 } else { 46 MacKeychain keychain; 47 EncryptorPassword encryptor_password(keychain); 48 password = encryptor_password.GetEncryptorPassword(); 49 } 50 51 if (password.empty()) 52 return NULL; 53 54 std::string salt(kSalt); 55 56 // Create an encryption key from our password and salt. 57 scoped_ptr<crypto::SymmetricKey> encryption_key( 58 crypto::SymmetricKey::DeriveKeyFromPassword(crypto::SymmetricKey::AES, 59 password, 60 salt, 61 kEncryptionIterations, 62 kDerivedKeySizeInBits)); 63 DCHECK(encryption_key.get()); 64 65 return encryption_key.release(); 66 } 67 68 } // namespace 69 70 bool Encryptor::EncryptString16(const string16& plaintext, 71 std::string* ciphertext) { 72 return EncryptString(UTF16ToUTF8(plaintext), ciphertext); 73 } 74 75 bool Encryptor::DecryptString16(const std::string& ciphertext, 76 string16* plaintext) { 77 std::string utf8; 78 if (!DecryptString(ciphertext, &utf8)) 79 return false; 80 81 *plaintext = UTF8ToUTF16(utf8); 82 return true; 83 } 84 85 bool Encryptor::EncryptString(const std::string& plaintext, 86 std::string* ciphertext) { 87 if (plaintext.empty()) { 88 *ciphertext = std::string(); 89 return true; 90 } 91 92 scoped_ptr<crypto::SymmetricKey> encryption_key(GetEncryptionKey()); 93 if (!encryption_key.get()) 94 return false; 95 96 std::string iv(kCCBlockSizeAES128, ' '); 97 crypto::Encryptor encryptor; 98 if (!encryptor.Init(encryption_key.get(), crypto::Encryptor::CBC, iv)) 99 return false; 100 101 if (!encryptor.Encrypt(plaintext, ciphertext)) 102 return false; 103 104 // Prefix the cypher text with version information. 105 ciphertext->insert(0, kEncryptionVersionPrefix); 106 return true; 107 } 108 109 bool Encryptor::DecryptString(const std::string& ciphertext, 110 std::string* plaintext) { 111 if (ciphertext.empty()) { 112 *plaintext = std::string(); 113 return true; 114 } 115 116 // Check that the incoming cyphertext was indeed encrypted with the expected 117 // version. If the prefix is not found then we'll assume we're dealing with 118 // old data saved as clear text and we'll return it directly. 119 // Credit card numbers are current legacy data, so false match with prefix 120 // won't happen. 121 if (ciphertext.find(kEncryptionVersionPrefix) != 0) { 122 *plaintext = ciphertext; 123 return true; 124 } 125 126 // Strip off the versioning prefix before decrypting. 127 std::string raw_ciphertext = 128 ciphertext.substr(strlen(kEncryptionVersionPrefix)); 129 130 scoped_ptr<crypto::SymmetricKey> encryption_key(GetEncryptionKey()); 131 if (!encryption_key.get()) 132 return false; 133 134 std::string iv(kCCBlockSizeAES128, ' '); 135 crypto::Encryptor encryptor; 136 if (!encryptor.Init(encryption_key.get(), crypto::Encryptor::CBC, iv)) 137 return false; 138 139 if (!encryptor.Decrypt(raw_ciphertext, plaintext)) 140 return false; 141 142 return true; 143 } 144 145 void Encryptor::UseMockKeychain(bool use_mock) { 146 use_mock_keychain = use_mock; 147 } 148 149