blob: 4935e15d81d4cb3d1dd5a97760d8793e296896c6 [file] [log] [blame]
tengs28cb8d62015-04-13 19:46:411// Copyright 2015 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
khorimoto999e934c2016-11-18 20:10:425#include "components/cryptauth/cryptauth_enroller_impl.h"
tengs28cb8d62015-04-13 19:46:416
dcheng51ace48a2015-12-26 22:45:177#include <utility>
8
tengs28cb8d62015-04-13 19:46:419#include "base/bind.h"
James Hawkins31399f92018-03-28 17:16:3510#include "base/metrics/histogram_macros.h"
James Hawkins813085e2018-03-30 18:56:4111#include "chromeos/components/proximity_auth/logging/logging.h"
khorimoto999e934c2016-11-18 20:10:4212#include "components/cryptauth/cryptauth_client_impl.h"
13#include "components/cryptauth/cryptauth_enrollment_utils.h"
14#include "components/cryptauth/secure_message_delegate.h"
tengs7555b0b2015-06-29 18:47:1515#include "crypto/sha2.h"
tengs28cb8d62015-04-13 19:46:4116
khorimoto999e934c2016-11-18 20:10:4217namespace cryptauth {
tengs28cb8d62015-04-13 19:46:4118
19namespace {
20
21// A successful SetupEnrollment or FinishEnrollment response should contain this
22// status string.
tengs7555b0b2015-06-29 18:47:1523const char kResponseStatusOk[] = "ok";
tengs28cb8d62015-04-13 19:46:4124
25// The name of the "gcmV1" protocol that the enrolling device supports.
26const char kSupportedEnrollmentTypeGcmV1[] = "gcmV1";
27
28// The version field of the GcmMetadata message.
29const int kGCMMetadataVersion = 1;
30
31// Returns true if |device_info| contains the required fields for enrollment.
khorimotof7a6aff2017-01-19 22:50:4732bool ValidateDeviceInfo(const GcmDeviceInfo& device_info) {
tengs28cb8d62015-04-13 19:46:4133 if (!device_info.has_long_device_id()) {
tengs7555b0b2015-06-29 18:47:1534 PA_LOG(ERROR) << "Expected long_device_id field in GcmDeviceInfo.";
tengs28cb8d62015-04-13 19:46:4135 return false;
36 }
37
38 if (!device_info.has_device_type()) {
tengs7555b0b2015-06-29 18:47:1539 PA_LOG(ERROR) << "Expected device_type field in GcmDeviceInfo.";
tengs28cb8d62015-04-13 19:46:4140 return false;
41 }
42
43 return true;
44}
45
46// Creates the public metadata to put in the SecureMessage that is sent to the
47// server with the FinishEnrollment request.
48std::string CreateEnrollmentPublicMetadata() {
khorimotof7a6aff2017-01-19 22:50:4749 GcmMetadata metadata;
tengs28cb8d62015-04-13 19:46:4150 metadata.set_version(kGCMMetadataVersion);
khorimotof7a6aff2017-01-19 22:50:4751 metadata.set_type(MessageType::ENROLLMENT);
tengs28cb8d62015-04-13 19:46:4152 return metadata.SerializeAsString();
53}
54
55} // namespace
56
57CryptAuthEnrollerImpl::CryptAuthEnrollerImpl(
dcheng2f012692016-04-21 00:19:3458 std::unique_ptr<CryptAuthClientFactory> client_factory,
59 std::unique_ptr<SecureMessageDelegate> secure_message_delegate)
dcheng51ace48a2015-12-26 22:45:1760 : client_factory_(std::move(client_factory)),
61 secure_message_delegate_(std::move(secure_message_delegate)),
62 weak_ptr_factory_(this) {}
tengs28cb8d62015-04-13 19:46:4163
64CryptAuthEnrollerImpl::~CryptAuthEnrollerImpl() {
65}
66
67void CryptAuthEnrollerImpl::Enroll(
tengs7555b0b2015-06-29 18:47:1568 const std::string& user_public_key,
69 const std::string& user_private_key,
khorimotof7a6aff2017-01-19 22:50:4770 const GcmDeviceInfo& device_info,
71 InvocationReason invocation_reason,
tengs28cb8d62015-04-13 19:46:4172 const EnrollmentFinishedCallback& callback) {
73 if (!callback_.is_null()) {
tengs7555b0b2015-06-29 18:47:1574 PA_LOG(ERROR) << "Enroll() already called. Do not reuse.";
tengs28cb8d62015-04-13 19:46:4175 callback.Run(false);
76 return;
77 }
78
tengs7555b0b2015-06-29 18:47:1579 user_public_key_ = user_public_key;
80 user_private_key_ = user_private_key;
tengs28cb8d62015-04-13 19:46:4181 device_info_ = device_info;
82 invocation_reason_ = invocation_reason;
83 callback_ = callback;
84
85 if (!ValidateDeviceInfo(device_info)) {
86 callback.Run(false);
87 return;
88 }
89
90 secure_message_delegate_->GenerateKeyPair(
91 base::Bind(&CryptAuthEnrollerImpl::OnKeyPairGenerated,
92 weak_ptr_factory_.GetWeakPtr()));
93}
94
95void CryptAuthEnrollerImpl::OnKeyPairGenerated(const std::string& public_key,
96 const std::string& private_key) {
tengs7555b0b2015-06-29 18:47:1597 PA_LOG(INFO) << "Ephemeral key pair generated, calling SetupEnrollment API.";
tengs28cb8d62015-04-13 19:46:4198 session_public_key_ = public_key;
99 session_private_key_ = private_key;
100
101 cryptauth_client_ = client_factory_->CreateInstance();
khorimotof7a6aff2017-01-19 22:50:47102 SetupEnrollmentRequest request;
tengs28cb8d62015-04-13 19:46:41103 request.add_types(kSupportedEnrollmentTypeGcmV1);
104 request.set_invocation_reason(invocation_reason_);
105 cryptauth_client_->SetupEnrollment(
106 request, base::Bind(&CryptAuthEnrollerImpl::OnSetupEnrollmentSuccess,
107 weak_ptr_factory_.GetWeakPtr()),
108 base::Bind(&CryptAuthEnrollerImpl::OnSetupEnrollmentFailure,
109 weak_ptr_factory_.GetWeakPtr()));
110}
111
112void CryptAuthEnrollerImpl::OnSetupEnrollmentSuccess(
khorimotof7a6aff2017-01-19 22:50:47113 const SetupEnrollmentResponse& response) {
tengs28cb8d62015-04-13 19:46:41114 if (response.status() != kResponseStatusOk) {
tengs7555b0b2015-06-29 18:47:15115 PA_LOG(WARNING) << "Unexpected status for SetupEnrollment: "
116 << response.status();
tengs28cb8d62015-04-13 19:46:41117 callback_.Run(false);
118 return;
119 }
120
121 if (response.infos_size() == 0) {
tengs7555b0b2015-06-29 18:47:15122 PA_LOG(ERROR) << "No response info returned by server for SetupEnrollment";
tengs28cb8d62015-04-13 19:46:41123 callback_.Run(false);
124 return;
125 }
126
tengs7555b0b2015-06-29 18:47:15127 PA_LOG(INFO) << "SetupEnrollment request succeeded: deriving symmetric key.";
tengs28cb8d62015-04-13 19:46:41128 setup_info_ = response.infos(0);
129 device_info_.set_enrollment_session_id(setup_info_.enrollment_session_id());
tengs7555b0b2015-06-29 18:47:15130
tengs28cb8d62015-04-13 19:46:41131 secure_message_delegate_->DeriveKey(
132 session_private_key_, setup_info_.server_ephemeral_key(),
133 base::Bind(&CryptAuthEnrollerImpl::OnKeyDerived,
134 weak_ptr_factory_.GetWeakPtr()));
135}
136
137void CryptAuthEnrollerImpl::OnSetupEnrollmentFailure(const std::string& error) {
tengs7555b0b2015-06-29 18:47:15138 PA_LOG(WARNING) << "SetupEnrollment API failed with error: " << error;
tengs28cb8d62015-04-13 19:46:41139 callback_.Run(false);
140}
141
142void CryptAuthEnrollerImpl::OnKeyDerived(const std::string& symmetric_key) {
tengs7555b0b2015-06-29 18:47:15143 PA_LOG(INFO) << "Derived symmetric key, "
144 << "encrypting enrollment data for upload.";
145
146 // Make sure we're enrolling the same public key used below to sign the
147 // secure message.
148 device_info_.set_user_public_key(user_public_key_);
149 device_info_.set_key_handle(user_public_key_);
150
151 // Hash the symmetric key and add it to the |device_info_| to be uploaded.
152 device_info_.set_device_master_key_hash(
153 crypto::SHA256HashString(symmetric_key));
154
155 // The server verifies that the access token set here and in the header
156 // of the FinishEnrollment() request are the same.
157 device_info_.set_oauth_token(cryptauth_client_->GetAccessTokenUsed());
158 PA_LOG(INFO) << "Using access token: " << device_info_.oauth_token();
159
tengs28cb8d62015-04-13 19:46:41160 symmetric_key_ = symmetric_key;
161 SecureMessageDelegate::CreateOptions options;
162 options.encryption_scheme = securemessage::NONE;
163 options.signature_scheme = securemessage::ECDSA_P256_SHA256;
tengs7555b0b2015-06-29 18:47:15164 options.verification_key_id = user_public_key_;
tengs28cb8d62015-04-13 19:46:41165
166 // The inner message contains the signed device information that will be
167 // sent to CryptAuth.
168 secure_message_delegate_->CreateSecureMessage(
tengs7555b0b2015-06-29 18:47:15169 device_info_.SerializeAsString(), user_private_key_, options,
tengs28cb8d62015-04-13 19:46:41170 base::Bind(&CryptAuthEnrollerImpl::OnInnerSecureMessageCreated,
171 weak_ptr_factory_.GetWeakPtr()));
172}
173
174void CryptAuthEnrollerImpl::OnInnerSecureMessageCreated(
175 const std::string& inner_message) {
tengs7555b0b2015-06-29 18:47:15176 if (inner_message.empty()) {
177 PA_LOG(ERROR) << "Error creating inner message";
178 callback_.Run(false);
179 return;
180 }
181
tengs28cb8d62015-04-13 19:46:41182 SecureMessageDelegate::CreateOptions options;
183 options.encryption_scheme = securemessage::AES_256_CBC;
184 options.signature_scheme = securemessage::HMAC_SHA256;
185 options.public_metadata = CreateEnrollmentPublicMetadata();
186
187 // The outer message encrypts and signs the inner message with the derived
188 // symmetric session key.
189 secure_message_delegate_->CreateSecureMessage(
190 inner_message, symmetric_key_, options,
191 base::Bind(&CryptAuthEnrollerImpl::OnOuterSecureMessageCreated,
192 weak_ptr_factory_.GetWeakPtr()));
193}
194
195void CryptAuthEnrollerImpl::OnOuterSecureMessageCreated(
196 const std::string& outer_message) {
tengs7555b0b2015-06-29 18:47:15197 PA_LOG(INFO) << "SecureMessage created, calling FinishEnrollment API.";
198
khorimotof7a6aff2017-01-19 22:50:47199 FinishEnrollmentRequest request;
tengs28cb8d62015-04-13 19:46:41200 request.set_enrollment_session_id(setup_info_.enrollment_session_id());
201 request.set_enrollment_message(outer_message);
202 request.set_device_ephemeral_key(session_public_key_);
203 request.set_invocation_reason(invocation_reason_);
204
205 cryptauth_client_ = client_factory_->CreateInstance();
206 cryptauth_client_->FinishEnrollment(
207 request, base::Bind(&CryptAuthEnrollerImpl::OnFinishEnrollmentSuccess,
208 weak_ptr_factory_.GetWeakPtr()),
209 base::Bind(&CryptAuthEnrollerImpl::OnFinishEnrollmentFailure,
210 weak_ptr_factory_.GetWeakPtr()));
211}
212
213void CryptAuthEnrollerImpl::OnFinishEnrollmentSuccess(
khorimotof7a6aff2017-01-19 22:50:47214 const FinishEnrollmentResponse& response) {
James Hawkins31399f92018-03-28 17:16:35215 const bool success = response.status() == kResponseStatusOk;
216
217 if (!success) {
tengs7555b0b2015-06-29 18:47:15218 PA_LOG(WARNING) << "Unexpected status for FinishEnrollment: "
219 << response.status();
tengs28cb8d62015-04-13 19:46:41220 }
James Hawkins31399f92018-03-28 17:16:35221
222 UMA_HISTOGRAM_BOOLEAN("CryptAuth.Enrollment.Result", success);
223 callback_.Run(success);
tengs28cb8d62015-04-13 19:46:41224}
225
226void CryptAuthEnrollerImpl::OnFinishEnrollmentFailure(
227 const std::string& error) {
tengs7555b0b2015-06-29 18:47:15228 PA_LOG(WARNING) << "FinishEnrollment API failed with error: " << error;
tengs28cb8d62015-04-13 19:46:41229 callback_.Run(false);
230}
231
khorimoto999e934c2016-11-18 20:10:42232} // namespace cryptauth