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