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 17namespace { 18 19// Salt for Symmetric key derivation. 20const char kSalt[] = "saltysalt"; 21 22// Key size required for 128 bit AES. 23const size_t kDerivedKeySizeInBits = 128; 24 25// Constant for Symmetic key derivation. 26const size_t kEncryptionIterations = 1003; 27 28// TODO(dhollowa): Refactor to allow dependency injection of Keychain. 29static 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. 34const 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. 40crypto::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 70bool Encryptor::EncryptString16(const string16& plaintext, 71 std::string* ciphertext) { 72 return EncryptString(UTF16ToUTF8(plaintext), ciphertext); 73} 74 75bool 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 85bool 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 109bool 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 145void Encryptor::UseMockKeychain(bool use_mock) { 146 use_mock_keychain = use_mock; 147} 148 149