Update mojo sdk to rev c02a28868825edfa57ab77947b8cb15e741c5598
[chromium-blink-merge.git] / components / proximity_auth / cryptauth / cryptauth_enroller_impl.cc
blobc595be1420c3def2f52b2df77563a9ae933d1165
1 // 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.
5 #include "components/proximity_auth/cryptauth/cryptauth_enroller_impl.h"
7 #include "base/bind.h"
8 #include "components/proximity_auth/cryptauth/base64url.h"
9 #include "components/proximity_auth/cryptauth/cryptauth_client_impl.h"
10 #include "components/proximity_auth/cryptauth/cryptauth_enrollment_utils.h"
11 #include "components/proximity_auth/cryptauth/fake_secure_message_delegate.h"
12 #include "components/proximity_auth/cryptauth/secure_message_delegate.h"
13 #include "components/proximity_auth/logging/logging.h"
14 #include "crypto/sha2.h"
16 namespace proximity_auth {
18 namespace {
20 // A successful SetupEnrollment or FinishEnrollment response should contain this
21 // status string.
22 const char kResponseStatusOk[] = "ok";
24 // The name of the "gcmV1" protocol that the enrolling device supports.
25 const char kSupportedEnrollmentTypeGcmV1[] = "gcmV1";
27 // The version field of the GcmMetadata message.
28 const int kGCMMetadataVersion = 1;
30 // Returns true if |device_info| contains the required fields for enrollment.
31 bool ValidateDeviceInfo(const cryptauth::GcmDeviceInfo& device_info) {
32 if (!device_info.has_long_device_id()) {
33 PA_LOG(ERROR) << "Expected long_device_id field in GcmDeviceInfo.";
34 return false;
37 if (!device_info.has_device_type()) {
38 PA_LOG(ERROR) << "Expected device_type field in GcmDeviceInfo.";
39 return false;
42 return true;
45 // Creates the public metadata to put in the SecureMessage that is sent to the
46 // server with the FinishEnrollment request.
47 std::string CreateEnrollmentPublicMetadata() {
48 cryptauth::GcmMetadata metadata;
49 metadata.set_version(kGCMMetadataVersion);
50 metadata.set_type(cryptauth::MessageType::ENROLLMENT);
51 return metadata.SerializeAsString();
54 } // namespace
56 CryptAuthEnrollerImpl::CryptAuthEnrollerImpl(
57 scoped_ptr<CryptAuthClientFactory> client_factory,
58 scoped_ptr<SecureMessageDelegate> secure_message_delegate)
59 : client_factory_(client_factory.Pass()),
60 secure_message_delegate_(secure_message_delegate.Pass()),
61 weak_ptr_factory_(this) {
64 CryptAuthEnrollerImpl::~CryptAuthEnrollerImpl() {
67 void CryptAuthEnrollerImpl::Enroll(
68 const std::string& user_public_key,
69 const std::string& user_private_key,
70 const cryptauth::GcmDeviceInfo& device_info,
71 cryptauth::InvocationReason invocation_reason,
72 const EnrollmentFinishedCallback& callback) {
73 if (!callback_.is_null()) {
74 PA_LOG(ERROR) << "Enroll() already called. Do not reuse.";
75 callback.Run(false);
76 return;
79 user_public_key_ = user_public_key;
80 user_private_key_ = user_private_key;
81 device_info_ = device_info;
82 invocation_reason_ = invocation_reason;
83 callback_ = callback;
85 if (!ValidateDeviceInfo(device_info)) {
86 callback.Run(false);
87 return;
90 secure_message_delegate_->GenerateKeyPair(
91 base::Bind(&CryptAuthEnrollerImpl::OnKeyPairGenerated,
92 weak_ptr_factory_.GetWeakPtr()));
95 void CryptAuthEnrollerImpl::OnKeyPairGenerated(const std::string& public_key,
96 const std::string& private_key) {
97 PA_LOG(INFO) << "Ephemeral key pair generated, calling SetupEnrollment API.";
98 session_public_key_ = public_key;
99 session_private_key_ = private_key;
101 cryptauth_client_ = client_factory_->CreateInstance();
102 cryptauth::SetupEnrollmentRequest request;
103 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()));
112 void CryptAuthEnrollerImpl::OnSetupEnrollmentSuccess(
113 const cryptauth::SetupEnrollmentResponse& response) {
114 if (response.status() != kResponseStatusOk) {
115 PA_LOG(WARNING) << "Unexpected status for SetupEnrollment: "
116 << response.status();
117 callback_.Run(false);
118 return;
121 if (response.infos_size() == 0) {
122 PA_LOG(ERROR) << "No response info returned by server for SetupEnrollment";
123 callback_.Run(false);
124 return;
127 PA_LOG(INFO) << "SetupEnrollment request succeeded: deriving symmetric key.";
128 setup_info_ = response.infos(0);
129 device_info_.set_enrollment_session_id(setup_info_.enrollment_session_id());
131 secure_message_delegate_->DeriveKey(
132 session_private_key_, setup_info_.server_ephemeral_key(),
133 base::Bind(&CryptAuthEnrollerImpl::OnKeyDerived,
134 weak_ptr_factory_.GetWeakPtr()));
137 void CryptAuthEnrollerImpl::OnSetupEnrollmentFailure(const std::string& error) {
138 PA_LOG(WARNING) << "SetupEnrollment API failed with error: " << error;
139 callback_.Run(false);
142 void CryptAuthEnrollerImpl::OnKeyDerived(const std::string& symmetric_key) {
143 PA_LOG(INFO) << "Derived symmetric key, "
144 << "encrypting enrollment data for upload.";
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_);
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));
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();
160 symmetric_key_ = symmetric_key;
161 SecureMessageDelegate::CreateOptions options;
162 options.encryption_scheme = securemessage::NONE;
163 options.signature_scheme = securemessage::ECDSA_P256_SHA256;
164 options.verification_key_id = user_public_key_;
166 // The inner message contains the signed device information that will be
167 // sent to CryptAuth.
168 secure_message_delegate_->CreateSecureMessage(
169 device_info_.SerializeAsString(), user_private_key_, options,
170 base::Bind(&CryptAuthEnrollerImpl::OnInnerSecureMessageCreated,
171 weak_ptr_factory_.GetWeakPtr()));
174 void CryptAuthEnrollerImpl::OnInnerSecureMessageCreated(
175 const std::string& inner_message) {
176 if (inner_message.empty()) {
177 PA_LOG(ERROR) << "Error creating inner message";
178 callback_.Run(false);
179 return;
182 SecureMessageDelegate::CreateOptions options;
183 options.encryption_scheme = securemessage::AES_256_CBC;
184 options.signature_scheme = securemessage::HMAC_SHA256;
185 options.public_metadata = CreateEnrollmentPublicMetadata();
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()));
195 void CryptAuthEnrollerImpl::OnOuterSecureMessageCreated(
196 const std::string& outer_message) {
197 PA_LOG(INFO) << "SecureMessage created, calling FinishEnrollment API.";
199 cryptauth::FinishEnrollmentRequest request;
200 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_);
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()));
213 void CryptAuthEnrollerImpl::OnFinishEnrollmentSuccess(
214 const cryptauth::FinishEnrollmentResponse& response) {
215 if (response.status() != kResponseStatusOk) {
216 PA_LOG(WARNING) << "Unexpected status for FinishEnrollment: "
217 << response.status();
218 callback_.Run(false);
219 } else {
220 callback_.Run(true);
224 void CryptAuthEnrollerImpl::OnFinishEnrollmentFailure(
225 const std::string& error) {
226 PA_LOG(WARNING) << "FinishEnrollment API failed with error: " << error;
227 callback_.Run(false);
230 } // namespace proximity_auth