1 // Copyright 2014 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.
5 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_create_keys_operation.h"
10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/strings/string_util.h"
13 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.h"
14 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_types.h"
15 #include "chromeos/cryptohome/homedir_methods.h"
16 #include "chromeos/cryptohome/system_salt_getter.h"
17 #include "chromeos/dbus/dbus_thread_manager.h"
18 #include "chromeos/dbus/easy_unlock_client.h"
19 #include "chromeos/login/auth/key.h"
20 #include "components/proximity_auth/cryptauth/base64url.h"
21 #include "crypto/encryptor.h"
22 #include "crypto/random.h"
23 #include "crypto/symmetric_key.h"
24 #include "google_apis/gaia/gaia_auth_util.h"
25 #include "third_party/cros_system_api/dbus/service_constants.h"
31 const int kUserKeyByteSize
= 16;
32 const int kSessionKeyByteSize
= 16;
34 const int kEasyUnlockKeyRevision
= 1;
35 const int kEasyUnlockKeyPrivileges
=
36 cryptohome::PRIV_MOUNT
| cryptohome::PRIV_ADD
| cryptohome::PRIV_REMOVE
;
40 /////////////////////////////////////////////////////////////////////////////
41 // EasyUnlockCreateKeysOperation::ChallengeCreator
43 class EasyUnlockCreateKeysOperation::ChallengeCreator
{
45 typedef base::Callback
<void (bool success
)> ChallengeCreatedCallback
;
46 ChallengeCreator(const std::string
& user_key
,
47 const std::string
& session_key
,
48 const std::string
& tpm_pub_key
,
49 EasyUnlockDeviceKeyData
* device
,
50 const ChallengeCreatedCallback
& callback
);
55 const std::string
& user_key() const { return user_key_
; }
58 void OnEcKeyPairGenerated(const std::string
& ec_public_key
,
59 const std::string
& ec_private_key
);
60 void OnEskGenerated(const std::string
& esk
);
61 void OnTPMPublicKeyWrapped(const std::string
& wrapped_key
);
63 void WrapTPMPublicKey();
64 void GeneratePayload();
65 void OnPayloadMessageGenerated(const std::string
& payload_message
);
66 void OnPayloadGenerated(const std::string
& payload
);
68 void OnChallengeGenerated(const std::string
& challenge
);
70 const std::string user_key_
;
71 const std::string session_key_
;
72 const std::string tpm_pub_key_
;
73 std::string wrapped_tpm_pub_key_
;
74 EasyUnlockDeviceKeyData
* device_
;
75 ChallengeCreatedCallback callback_
;
77 std::string ec_public_key_
;
80 // Owned by DBusThreadManager
81 chromeos::EasyUnlockClient
* easy_unlock_client_
;
83 base::WeakPtrFactory
<ChallengeCreator
> weak_ptr_factory_
;
85 DISALLOW_COPY_AND_ASSIGN(ChallengeCreator
);
88 EasyUnlockCreateKeysOperation::ChallengeCreator::ChallengeCreator(
89 const std::string
& user_key
,
90 const std::string
& session_key
,
91 const std::string
& tpm_pub_key
,
92 EasyUnlockDeviceKeyData
* device
,
93 const ChallengeCreatedCallback
& callback
)
94 : user_key_(user_key
),
95 session_key_(session_key
),
96 tpm_pub_key_(tpm_pub_key
),
100 chromeos::DBusThreadManager::Get()->GetEasyUnlockClient()),
101 weak_ptr_factory_(this) {
104 EasyUnlockCreateKeysOperation::ChallengeCreator::~ChallengeCreator() {
107 void EasyUnlockCreateKeysOperation::ChallengeCreator::Start() {
108 easy_unlock_client_
->GenerateEcP256KeyPair(
109 base::Bind(&ChallengeCreator::OnEcKeyPairGenerated
,
110 weak_ptr_factory_
.GetWeakPtr()));
113 void EasyUnlockCreateKeysOperation::ChallengeCreator::OnEcKeyPairGenerated(
114 const std::string
& ec_private_key
,
115 const std::string
& ec_public_key
) {
116 if (ec_private_key
.empty() || ec_public_key
.empty()) {
117 LOG(ERROR
) << "Easy unlock failed to generate ec key pair.";
118 callback_
.Run(false);
122 std::string device_pub_key
;
123 if (!proximity_auth::Base64UrlDecode(device_
->public_key
, &device_pub_key
)) {
124 LOG(ERROR
) << "Easy unlock failed to decode device public key.";
125 callback_
.Run(false);
129 ec_public_key_
= ec_public_key
;
130 easy_unlock_client_
->PerformECDHKeyAgreement(
133 base::Bind(&ChallengeCreator::OnEskGenerated
,
134 weak_ptr_factory_
.GetWeakPtr()));
137 void EasyUnlockCreateKeysOperation::ChallengeCreator::OnEskGenerated(
138 const std::string
& esk
) {
140 LOG(ERROR
) << "Easy unlock failed to generate challenge esk.";
141 callback_
.Run(false);
149 void EasyUnlockCreateKeysOperation::ChallengeCreator::WrapTPMPublicKey() {
150 easy_unlock_client_
->WrapPublicKey(
151 easy_unlock::kKeyAlgorithmRSA
,
153 base::Bind(&ChallengeCreator::OnTPMPublicKeyWrapped
,
154 weak_ptr_factory_
.GetWeakPtr()));
157 void EasyUnlockCreateKeysOperation::ChallengeCreator::OnTPMPublicKeyWrapped(
158 const std::string
& wrapped_key
) {
159 if (wrapped_key
.empty()) {
160 LOG(ERROR
) << "Unable to wrap RSA key";
161 callback_
.Run(false);
164 wrapped_tpm_pub_key_
= wrapped_key
;
168 void EasyUnlockCreateKeysOperation::ChallengeCreator::GeneratePayload() {
169 // Work around to get HeaderAndBody bytes to use as challenge payload.
170 EasyUnlockClient::CreateSecureMessageOptions options
;
172 options
.verification_key_id
= wrapped_tpm_pub_key_
;
173 options
.encryption_type
= easy_unlock::kEncryptionTypeAES256CBC
;
174 options
.signature_type
= easy_unlock::kSignatureTypeHMACSHA256
;
176 easy_unlock_client_
->CreateSecureMessage(
179 base::Bind(&ChallengeCreator::OnPayloadMessageGenerated
,
180 weak_ptr_factory_
.GetWeakPtr()));
184 EasyUnlockCreateKeysOperation::ChallengeCreator::OnPayloadMessageGenerated(
185 const std::string
& payload_message
) {
186 EasyUnlockClient::UnwrapSecureMessageOptions options
;
188 options
.encryption_type
= easy_unlock::kEncryptionTypeAES256CBC
;
189 options
.signature_type
= easy_unlock::kSignatureTypeHMACSHA256
;
191 easy_unlock_client_
->UnwrapSecureMessage(
194 base::Bind(&ChallengeCreator::OnPayloadGenerated
,
195 weak_ptr_factory_
.GetWeakPtr()));
198 void EasyUnlockCreateKeysOperation::ChallengeCreator::OnPayloadGenerated(
199 const std::string
& payload
) {
200 if (payload
.empty()) {
201 LOG(ERROR
) << "Easy unlock failed to generate challenge payload.";
202 callback_
.Run(false);
206 EasyUnlockClient::CreateSecureMessageOptions options
;
208 options
.decryption_key_id
= ec_public_key_
;
209 options
.encryption_type
= easy_unlock::kEncryptionTypeAES256CBC
;
210 options
.signature_type
= easy_unlock::kSignatureTypeHMACSHA256
;
212 easy_unlock_client_
->CreateSecureMessage(
215 base::Bind(&ChallengeCreator::OnChallengeGenerated
,
216 weak_ptr_factory_
.GetWeakPtr()));
219 void EasyUnlockCreateKeysOperation::ChallengeCreator::OnChallengeGenerated(
220 const std::string
& challenge
) {
221 if (challenge
.empty()) {
222 LOG(ERROR
) << "Easy unlock failed to generate challenge.";
223 callback_
.Run(false);
227 device_
->challenge
= challenge
;
231 /////////////////////////////////////////////////////////////////////////////
232 // EasyUnlockCreateKeysOperation
234 EasyUnlockCreateKeysOperation::EasyUnlockCreateKeysOperation(
235 const UserContext
& user_context
,
236 const std::string
& tpm_public_key
,
237 const EasyUnlockDeviceKeyDataList
& devices
,
238 const CreateKeysCallback
& callback
)
239 : user_context_(user_context
),
240 tpm_public_key_(tpm_public_key
),
243 key_creation_index_(0),
244 weak_ptr_factory_(this) {
245 // Must have the secret and callback.
246 DCHECK(!user_context_
.GetKey()->GetSecret().empty());
247 DCHECK(!callback_
.is_null());
250 EasyUnlockCreateKeysOperation::~EasyUnlockCreateKeysOperation() {
253 void EasyUnlockCreateKeysOperation::Start() {
254 key_creation_index_
= 0;
255 CreateKeyForDeviceAtIndex(key_creation_index_
);
258 void EasyUnlockCreateKeysOperation::CreateKeyForDeviceAtIndex(size_t index
) {
259 DCHECK_GE(index
, 0u);
260 if (index
== devices_
.size()) {
265 std::string user_key
;
266 crypto::RandBytes(WriteInto(&user_key
, kUserKeyByteSize
+ 1),
269 scoped_ptr
<crypto::SymmetricKey
> session_key(
270 crypto::SymmetricKey::GenerateRandomKey(crypto::SymmetricKey::AES
,
271 kSessionKeyByteSize
* 8));
273 std::string
iv(kSessionKeyByteSize
, ' ');
274 crypto::Encryptor encryptor
;
275 if (!encryptor
.Init(session_key
.get(), crypto::Encryptor::CBC
, iv
)) {
276 LOG(ERROR
) << "Easy unlock failed to init encryptor for key creation.";
277 callback_
.Run(false);
281 EasyUnlockDeviceKeyData
* device
= &devices_
[index
];
282 if (!encryptor
.Encrypt(user_key
, &device
->wrapped_secret
)) {
283 LOG(ERROR
) << "Easy unlock failed to encrypt user key for key creation.";
284 callback_
.Run(false);
288 std::string raw_session_key
;
289 session_key
->GetRawKey(&raw_session_key
);
291 challenge_creator_
.reset(new ChallengeCreator(
296 base::Bind(&EasyUnlockCreateKeysOperation::OnChallengeCreated
,
297 weak_ptr_factory_
.GetWeakPtr(),
299 challenge_creator_
->Start();
302 void EasyUnlockCreateKeysOperation::OnChallengeCreated(size_t index
,
304 DCHECK_EQ(key_creation_index_
, index
);
307 LOG(ERROR
) << "Easy unlock failed to create challenge for key creation.";
308 callback_
.Run(false);
312 SystemSaltGetter::Get()->GetSystemSalt(
313 base::Bind(&EasyUnlockCreateKeysOperation::OnGetSystemSalt
,
314 weak_ptr_factory_
.GetWeakPtr(),
318 void EasyUnlockCreateKeysOperation::OnGetSystemSalt(
320 const std::string
& system_salt
) {
321 DCHECK_EQ(key_creation_index_
, index
);
322 if (system_salt
.empty()) {
323 LOG(ERROR
) << "Easy unlock failed to get system salt for key creation.";
324 callback_
.Run(false);
328 Key
user_key(challenge_creator_
->user_key());
329 user_key
.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF
, system_salt
);
331 EasyUnlockDeviceKeyData
* device
= &devices_
[index
];
332 cryptohome::KeyDefinition
key_def(
333 user_key
.GetSecret(),
334 EasyUnlockKeyManager::GetKeyLabel(index
),
335 kEasyUnlockKeyPrivileges
);
336 key_def
.revision
= kEasyUnlockKeyRevision
;
337 key_def
.provider_data
.push_back(cryptohome::KeyDefinition::ProviderData(
338 kEasyUnlockKeyMetaNameBluetoothAddress
, device
->bluetooth_address
));
339 key_def
.provider_data
.push_back(cryptohome::KeyDefinition::ProviderData(
340 kEasyUnlockKeyMetaNamePsk
, device
->psk
));
341 key_def
.provider_data
.push_back(cryptohome::KeyDefinition::ProviderData(
342 kEasyUnlockKeyMetaNamePubKey
, device
->public_key
));
343 key_def
.provider_data
.push_back(cryptohome::KeyDefinition::ProviderData(
344 kEasyUnlockKeyMetaNameChallenge
, device
->challenge
));
345 key_def
.provider_data
.push_back(cryptohome::KeyDefinition::ProviderData(
346 kEasyUnlockKeyMetaNameWrappedSecret
, device
->wrapped_secret
));
348 // Add cryptohome key.
349 std::string canonicalized
=
350 gaia::CanonicalizeEmail(user_context_
.GetUserID());
351 cryptohome::Identification
id(canonicalized
);
353 scoped_ptr
<Key
> auth_key(new Key(*user_context_
.GetKey()));
354 if (auth_key
->GetKeyType() == Key::KEY_TYPE_PASSWORD_PLAIN
)
355 auth_key
->Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF
, system_salt
);
357 cryptohome::Authorization
auth(auth_key
->GetSecret(), auth_key
->GetLabel());
358 cryptohome::HomedirMethods::GetInstance()->AddKeyEx(
363 base::Bind(&EasyUnlockCreateKeysOperation::OnKeyCreated
,
364 weak_ptr_factory_
.GetWeakPtr(),
369 void EasyUnlockCreateKeysOperation::OnKeyCreated(
373 cryptohome::MountError return_code
) {
374 DCHECK_EQ(key_creation_index_
, index
);
377 LOG(ERROR
) << "Easy unlock failed to create key, code=" << return_code
;
378 callback_
.Run(false);
382 // If the key associated with the current context changed (i.e. in the case
383 // the current signin flow was Easy signin), update the user context.
384 if (user_context_
.GetAuthFlow() == UserContext::AUTH_FLOW_EASY_UNLOCK
&&
385 user_context_
.GetKey()->GetLabel() ==
386 EasyUnlockKeyManager::GetKeyLabel(key_creation_index_
)) {
387 user_context_
.SetKey(user_key
);
390 ++key_creation_index_
;
391 CreateKeyForDeviceAtIndex(key_creation_index_
);
394 } // namespace chromeos