Home | History | Annotate | Download | only in cryptohome
      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