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 "chromeos/cryptohome/cryptohome_library.h" 6 7 #include <map> 8 9 #include "base/bind.h" 10 #include "base/chromeos/chromeos_version.h" 11 #include "base/memory/weak_ptr.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/string_util.h" 14 #include "chromeos/dbus/cryptohome_client.h" 15 #include "chromeos/dbus/dbus_thread_manager.h" 16 #include "crypto/encryptor.h" 17 #include "crypto/nss_util.h" 18 #include "crypto/sha2.h" 19 #include "crypto/symmetric_key.h" 20 21 namespace chromeos { 22 23 namespace { 24 25 const char kStubSystemSalt[] = "stub_system_salt"; 26 const size_t kNonceSize = 16; 27 28 // Does nothing. Used as a Cryptohome::VoidMethodCallback. 29 void DoNothing(DBusMethodCallStatus call_status) {} 30 31 } // namespace 32 33 // This class handles the interaction with the ChromeOS cryptohome library APIs. 34 class CryptohomeLibraryImpl : public CryptohomeLibrary { 35 public: 36 CryptohomeLibraryImpl() : weak_ptr_factory_(this) { 37 } 38 39 virtual ~CryptohomeLibraryImpl() { 40 } 41 42 virtual bool TpmIsEnabled() OVERRIDE { 43 bool result = false; 44 DBusThreadManager::Get()->GetCryptohomeClient()->CallTpmIsEnabledAndBlock( 45 &result); 46 return result; 47 } 48 49 virtual bool TpmIsOwned() OVERRIDE { 50 bool result = false; 51 DBusThreadManager::Get()->GetCryptohomeClient()->CallTpmIsOwnedAndBlock( 52 &result); 53 return result; 54 } 55 56 virtual bool TpmIsBeingOwned() OVERRIDE { 57 bool result = false; 58 DBusThreadManager::Get()->GetCryptohomeClient()-> 59 CallTpmIsBeingOwnedAndBlock(&result); 60 return result; 61 } 62 63 virtual void TpmCanAttemptOwnership() OVERRIDE { 64 DBusThreadManager::Get()->GetCryptohomeClient()->TpmCanAttemptOwnership( 65 base::Bind(&DoNothing)); 66 } 67 68 virtual void TpmClearStoredPassword() OVERRIDE { 69 DBusThreadManager::Get()->GetCryptohomeClient()-> 70 CallTpmClearStoredPasswordAndBlock(); 71 } 72 73 virtual bool InstallAttributesGet( 74 const std::string& name, std::string* value) OVERRIDE { 75 std::vector<uint8> buf; 76 bool success = false; 77 DBusThreadManager::Get()->GetCryptohomeClient()-> 78 InstallAttributesGet(name, &buf, &success); 79 if (success) { 80 // Cryptohome returns 'buf' with a terminating '\0' character. 81 DCHECK(!buf.empty()); 82 DCHECK_EQ(buf.back(), 0); 83 value->assign(reinterpret_cast<char*>(buf.data()), buf.size() - 1); 84 } 85 return success; 86 } 87 88 virtual bool InstallAttributesSet( 89 const std::string& name, const std::string& value) OVERRIDE { 90 std::vector<uint8> buf(value.c_str(), value.c_str() + value.size() + 1); 91 bool success = false; 92 DBusThreadManager::Get()->GetCryptohomeClient()-> 93 InstallAttributesSet(name, buf, &success); 94 return success; 95 } 96 97 virtual bool InstallAttributesFinalize() OVERRIDE { 98 bool success = false; 99 DBusThreadManager::Get()->GetCryptohomeClient()-> 100 InstallAttributesFinalize(&success); 101 return success; 102 } 103 104 virtual bool InstallAttributesIsInvalid() OVERRIDE { 105 bool result = false; 106 DBusThreadManager::Get()->GetCryptohomeClient()-> 107 InstallAttributesIsInvalid(&result); 108 return result; 109 } 110 111 virtual bool InstallAttributesIsFirstInstall() OVERRIDE { 112 bool result = false; 113 DBusThreadManager::Get()->GetCryptohomeClient()-> 114 InstallAttributesIsFirstInstall(&result); 115 return result; 116 } 117 118 virtual std::string GetSystemSalt() OVERRIDE { 119 LoadSystemSalt(); // no-op if it's already loaded. 120 return StringToLowerASCII(base::HexEncode( 121 reinterpret_cast<const void*>(system_salt_.data()), 122 system_salt_.size())); 123 } 124 125 virtual std::string EncryptWithSystemSalt(const std::string& token) OVERRIDE { 126 // Don't care about token encryption while debugging. 127 if (!base::chromeos::IsRunningOnChromeOS()) 128 return token; 129 130 if (!LoadSystemSaltKey()) { 131 LOG(WARNING) << "System salt key is not available for encrypt."; 132 return std::string(); 133 } 134 return EncryptTokenWithKey(system_salt_key_.get(), 135 GetSystemSalt(), 136 token); 137 } 138 139 virtual std::string DecryptWithSystemSalt( 140 const std::string& encrypted_token_hex) OVERRIDE { 141 // Don't care about token encryption while debugging. 142 if (!base::chromeos::IsRunningOnChromeOS()) 143 return encrypted_token_hex; 144 145 if (!LoadSystemSaltKey()) { 146 LOG(WARNING) << "System salt key is not available for decrypt."; 147 return std::string(); 148 } 149 return DecryptTokenWithKey(system_salt_key_.get(), 150 GetSystemSalt(), 151 encrypted_token_hex); 152 } 153 154 private: 155 void LoadSystemSalt() { 156 if (!system_salt_.empty()) 157 return; 158 DBusThreadManager::Get()->GetCryptohomeClient()-> 159 GetSystemSalt(&system_salt_); 160 CHECK(!system_salt_.empty()); 161 CHECK_EQ(system_salt_.size() % 2, 0U); 162 } 163 164 // TODO: should this use the system salt for both the password and the salt 165 // value, or should this use a separate salt value? 166 bool LoadSystemSaltKey() { 167 if (!system_salt_key_.get()) 168 system_salt_key_.reset(PassphraseToKey(GetSystemSalt(), GetSystemSalt())); 169 return system_salt_key_.get(); 170 } 171 172 crypto::SymmetricKey* PassphraseToKey(const std::string& passphrase, 173 const std::string& salt) { 174 return crypto::SymmetricKey::DeriveKeyFromPassword( 175 crypto::SymmetricKey::AES, passphrase, salt, 1000, 256); 176 } 177 178 179 // Encrypts (AES) the token given |key| and |salt|. 180 std::string EncryptTokenWithKey(crypto::SymmetricKey* key, 181 const std::string& salt, 182 const std::string& token) { 183 crypto::Encryptor encryptor; 184 if (!encryptor.Init(key, crypto::Encryptor::CTR, std::string())) { 185 LOG(WARNING) << "Failed to initialize Encryptor."; 186 return std::string(); 187 } 188 std::string nonce = salt.substr(0, kNonceSize); 189 std::string encoded_token; 190 CHECK(encryptor.SetCounter(nonce)); 191 if (!encryptor.Encrypt(token, &encoded_token)) { 192 LOG(WARNING) << "Failed to encrypt token."; 193 return std::string(); 194 } 195 196 return StringToLowerASCII(base::HexEncode( 197 reinterpret_cast<const void*>(encoded_token.data()), 198 encoded_token.size())); 199 } 200 201 // Decrypts (AES) hex encoded encrypted token given |key| and |salt|. 202 std::string DecryptTokenWithKey(crypto::SymmetricKey* key, 203 const std::string& salt, 204 const std::string& encrypted_token_hex) { 205 std::vector<uint8> encrypted_token_bytes; 206 if (!base::HexStringToBytes(encrypted_token_hex, &encrypted_token_bytes)) { 207 LOG(WARNING) << "Corrupt encrypted token found."; 208 return std::string(); 209 } 210 211 std::string encrypted_token( 212 reinterpret_cast<char*>(encrypted_token_bytes.data()), 213 encrypted_token_bytes.size()); 214 crypto::Encryptor encryptor; 215 if (!encryptor.Init(key, crypto::Encryptor::CTR, std::string())) { 216 LOG(WARNING) << "Failed to initialize Encryptor."; 217 return std::string(); 218 } 219 220 std::string nonce = salt.substr(0, kNonceSize); 221 std::string token; 222 CHECK(encryptor.SetCounter(nonce)); 223 if (!encryptor.Decrypt(encrypted_token, &token)) { 224 LOG(WARNING) << "Failed to decrypt token."; 225 return std::string(); 226 } 227 return token; 228 } 229 230 base::WeakPtrFactory<CryptohomeLibraryImpl> weak_ptr_factory_; 231 std::vector<uint8> system_salt_; 232 // A key based on the system salt. Useful for encrypting device-level 233 // data for which we have no additional credentials. 234 scoped_ptr<crypto::SymmetricKey> system_salt_key_; 235 236 DISALLOW_COPY_AND_ASSIGN(CryptohomeLibraryImpl); 237 }; 238 239 class CryptohomeLibraryStubImpl : public CryptohomeLibrary { 240 public: 241 CryptohomeLibraryStubImpl() 242 : locked_(false) {} 243 virtual ~CryptohomeLibraryStubImpl() {} 244 245 virtual bool TpmIsEnabled() OVERRIDE { 246 return true; 247 } 248 249 virtual bool TpmIsOwned() OVERRIDE { 250 return true; 251 } 252 253 virtual bool TpmIsBeingOwned() OVERRIDE { 254 return true; 255 } 256 257 virtual void TpmCanAttemptOwnership() OVERRIDE {} 258 259 virtual void TpmClearStoredPassword() OVERRIDE {} 260 261 virtual bool InstallAttributesGet( 262 const std::string& name, std::string* value) OVERRIDE { 263 if (install_attrs_.find(name) != install_attrs_.end()) { 264 *value = install_attrs_[name]; 265 return true; 266 } 267 return false; 268 } 269 270 virtual bool InstallAttributesSet( 271 const std::string& name, const std::string& value) OVERRIDE { 272 install_attrs_[name] = value; 273 return true; 274 } 275 276 virtual bool InstallAttributesFinalize() OVERRIDE { 277 locked_ = true; 278 return true; 279 } 280 281 virtual bool InstallAttributesIsInvalid() OVERRIDE { 282 return false; 283 } 284 285 virtual bool InstallAttributesIsFirstInstall() OVERRIDE { 286 return !locked_; 287 } 288 289 virtual std::string GetSystemSalt() OVERRIDE { 290 return kStubSystemSalt; 291 } 292 293 virtual std::string EncryptWithSystemSalt(const std::string& token) OVERRIDE { 294 return token; 295 } 296 297 virtual std::string DecryptWithSystemSalt( 298 const std::string& encrypted_token_hex) OVERRIDE { 299 return encrypted_token_hex; 300 } 301 302 private: 303 std::map<std::string, std::string> install_attrs_; 304 bool locked_; 305 DISALLOW_COPY_AND_ASSIGN(CryptohomeLibraryStubImpl); 306 }; 307 308 CryptohomeLibrary::CryptohomeLibrary() {} 309 CryptohomeLibrary::~CryptohomeLibrary() {} 310 311 static CryptohomeLibrary* g_cryptohome_library = NULL; 312 static CryptohomeLibrary* g_test_cryptohome_library = NULL; 313 314 // static 315 void CryptohomeLibrary::Initialize() { 316 CHECK(!g_cryptohome_library); 317 if (base::chromeos::IsRunningOnChromeOS()) 318 g_cryptohome_library = new CryptohomeLibraryImpl(); 319 else 320 g_cryptohome_library = new CryptohomeLibraryStubImpl(); 321 } 322 323 // static 324 bool CryptohomeLibrary::IsInitialized() { 325 return g_cryptohome_library; 326 } 327 328 // static 329 void CryptohomeLibrary::Shutdown() { 330 CHECK(g_cryptohome_library); 331 delete g_cryptohome_library; 332 g_cryptohome_library = NULL; 333 } 334 335 // static 336 CryptohomeLibrary* CryptohomeLibrary::Get() { 337 CHECK(g_cryptohome_library || g_test_cryptohome_library) 338 << "CryptohomeLibrary::Get() called before Initialize()"; 339 if (g_test_cryptohome_library) 340 return g_test_cryptohome_library; 341 return g_cryptohome_library; 342 } 343 344 // static 345 void CryptohomeLibrary::SetForTest(CryptohomeLibrary* impl) { 346 CHECK(!g_test_cryptohome_library || !impl); 347 g_test_cryptohome_library = impl; 348 } 349 350 // static 351 CryptohomeLibrary* CryptohomeLibrary::GetTestImpl() { 352 return new CryptohomeLibraryStubImpl(); 353 } 354 355 } // namespace chromeos 356