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
21namespace chromeos {
22
23namespace {
24
25const char kStubSystemSalt[] = "stub_system_salt";
26const size_t kNonceSize = 16;
27
28// Does nothing.  Used as a Cryptohome::VoidMethodCallback.
29void DoNothing(DBusMethodCallStatus call_status) {}
30
31}  // namespace
32
33// This class handles the interaction with the ChromeOS cryptohome library APIs.
34class 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
239class 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
308CryptohomeLibrary::CryptohomeLibrary() {}
309CryptohomeLibrary::~CryptohomeLibrary() {}
310
311static CryptohomeLibrary* g_cryptohome_library = NULL;
312static CryptohomeLibrary* g_test_cryptohome_library = NULL;
313
314// static
315void 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
324bool CryptohomeLibrary::IsInitialized() {
325  return g_cryptohome_library;
326}
327
328// static
329void CryptohomeLibrary::Shutdown() {
330  CHECK(g_cryptohome_library);
331  delete g_cryptohome_library;
332  g_cryptohome_library = NULL;
333}
334
335// static
336CryptohomeLibrary* 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
345void CryptohomeLibrary::SetForTest(CryptohomeLibrary* impl) {
346  CHECK(!g_test_cryptohome_library || !impl);
347  g_test_cryptohome_library = impl;
348}
349
350// static
351CryptohomeLibrary* CryptohomeLibrary::GetTestImpl() {
352  return new CryptohomeLibraryStubImpl();
353}
354
355} // namespace chromeos
356