OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "components/proximity_auth/cryptauth/cryptauth_enroller_impl.h" | |
6 | |
7 #include <utility> | |
8 | |
9 #include "base/bind.h" | |
10 #include "components/proximity_auth/cryptauth/cryptauth_client_impl.h" | |
11 #include "components/proximity_auth/cryptauth/cryptauth_enrollment_utils.h" | |
12 #include "components/proximity_auth/cryptauth/secure_message_delegate.h" | |
13 #include "components/proximity_auth/logging/logging.h" | |
14 #include "crypto/sha2.h" | |
15 | |
16 namespace proximity_auth { | |
17 | |
18 namespace { | |
19 | |
20 // A successful SetupEnrollment or FinishEnrollment response should contain this | |
21 // status string. | |
22 const char kResponseStatusOk[] = "ok"; | |
23 | |
24 // The name of the "gcmV1" protocol that the enrolling device supports. | |
25 const char kSupportedEnrollmentTypeGcmV1[] = "gcmV1"; | |
26 | |
27 // The version field of the GcmMetadata message. | |
28 const int kGCMMetadataVersion = 1; | |
29 | |
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; | |
35 } | |
36 | |
37 if (!device_info.has_device_type()) { | |
38 PA_LOG(ERROR) << "Expected device_type field in GcmDeviceInfo."; | |
39 return false; | |
40 } | |
41 | |
42 return true; | |
43 } | |
44 | |
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(); | |
52 } | |
53 | |
54 } // namespace | |
55 | |
56 CryptAuthEnrollerImpl::CryptAuthEnrollerImpl( | |
57 std::unique_ptr<CryptAuthClientFactory> client_factory, | |
58 std::unique_ptr<SecureMessageDelegate> secure_message_delegate) | |
59 : client_factory_(std::move(client_factory)), | |
60 secure_message_delegate_(std::move(secure_message_delegate)), | |
61 weak_ptr_factory_(this) {} | |
62 | |
63 CryptAuthEnrollerImpl::~CryptAuthEnrollerImpl() { | |
64 } | |
65 | |
66 void CryptAuthEnrollerImpl::Enroll( | |
67 const std::string& user_public_key, | |
68 const std::string& user_private_key, | |
69 const cryptauth::GcmDeviceInfo& device_info, | |
70 cryptauth::InvocationReason invocation_reason, | |
71 const EnrollmentFinishedCallback& callback) { | |
72 if (!callback_.is_null()) { | |
73 PA_LOG(ERROR) << "Enroll() already called. Do not reuse."; | |
74 callback.Run(false); | |
75 return; | |
76 } | |
77 | |
78 user_public_key_ = user_public_key; | |
79 user_private_key_ = user_private_key; | |
80 device_info_ = device_info; | |
81 invocation_reason_ = invocation_reason; | |
82 callback_ = callback; | |
83 | |
84 if (!ValidateDeviceInfo(device_info)) { | |
85 callback.Run(false); | |
86 return; | |
87 } | |
88 | |
89 secure_message_delegate_->GenerateKeyPair( | |
90 base::Bind(&CryptAuthEnrollerImpl::OnKeyPairGenerated, | |
91 weak_ptr_factory_.GetWeakPtr())); | |
92 } | |
93 | |
94 void CryptAuthEnrollerImpl::OnKeyPairGenerated(const std::string& public_key, | |
95 const std::string& private_key) { | |
96 PA_LOG(INFO) << "Ephemeral key pair generated, calling SetupEnrollment API."; | |
97 session_public_key_ = public_key; | |
98 session_private_key_ = private_key; | |
99 | |
100 cryptauth_client_ = client_factory_->CreateInstance(); | |
101 cryptauth::SetupEnrollmentRequest request; | |
102 request.add_types(kSupportedEnrollmentTypeGcmV1); | |
103 request.set_invocation_reason(invocation_reason_); | |
104 cryptauth_client_->SetupEnrollment( | |
105 request, base::Bind(&CryptAuthEnrollerImpl::OnSetupEnrollmentSuccess, | |
106 weak_ptr_factory_.GetWeakPtr()), | |
107 base::Bind(&CryptAuthEnrollerImpl::OnSetupEnrollmentFailure, | |
108 weak_ptr_factory_.GetWeakPtr())); | |
109 } | |
110 | |
111 void CryptAuthEnrollerImpl::OnSetupEnrollmentSuccess( | |
112 const cryptauth::SetupEnrollmentResponse& response) { | |
113 if (response.status() != kResponseStatusOk) { | |
114 PA_LOG(WARNING) << "Unexpected status for SetupEnrollment: " | |
115 << response.status(); | |
116 callback_.Run(false); | |
117 return; | |
118 } | |
119 | |
120 if (response.infos_size() == 0) { | |
121 PA_LOG(ERROR) << "No response info returned by server for SetupEnrollment"; | |
122 callback_.Run(false); | |
123 return; | |
124 } | |
125 | |
126 PA_LOG(INFO) << "SetupEnrollment request succeeded: deriving symmetric key."; | |
127 setup_info_ = response.infos(0); | |
128 device_info_.set_enrollment_session_id(setup_info_.enrollment_session_id()); | |
129 | |
130 secure_message_delegate_->DeriveKey( | |
131 session_private_key_, setup_info_.server_ephemeral_key(), | |
132 base::Bind(&CryptAuthEnrollerImpl::OnKeyDerived, | |
133 weak_ptr_factory_.GetWeakPtr())); | |
134 } | |
135 | |
136 void CryptAuthEnrollerImpl::OnSetupEnrollmentFailure(const std::string& error) { | |
137 PA_LOG(WARNING) << "SetupEnrollment API failed with error: " << error; | |
138 callback_.Run(false); | |
139 } | |
140 | |
141 void CryptAuthEnrollerImpl::OnKeyDerived(const std::string& symmetric_key) { | |
142 PA_LOG(INFO) << "Derived symmetric key, " | |
143 << "encrypting enrollment data for upload."; | |
144 | |
145 // Make sure we're enrolling the same public key used below to sign the | |
146 // secure message. | |
147 device_info_.set_user_public_key(user_public_key_); | |
148 device_info_.set_key_handle(user_public_key_); | |
149 | |
150 // Hash the symmetric key and add it to the |device_info_| to be uploaded. | |
151 device_info_.set_device_master_key_hash( | |
152 crypto::SHA256HashString(symmetric_key)); | |
153 | |
154 // The server verifies that the access token set here and in the header | |
155 // of the FinishEnrollment() request are the same. | |
156 device_info_.set_oauth_token(cryptauth_client_->GetAccessTokenUsed()); | |
157 PA_LOG(INFO) << "Using access token: " << device_info_.oauth_token(); | |
158 | |
159 symmetric_key_ = symmetric_key; | |
160 SecureMessageDelegate::CreateOptions options; | |
161 options.encryption_scheme = securemessage::NONE; | |
162 options.signature_scheme = securemessage::ECDSA_P256_SHA256; | |
163 options.verification_key_id = user_public_key_; | |
164 | |
165 // The inner message contains the signed device information that will be | |
166 // sent to CryptAuth. | |
167 secure_message_delegate_->CreateSecureMessage( | |
168 device_info_.SerializeAsString(), user_private_key_, options, | |
169 base::Bind(&CryptAuthEnrollerImpl::OnInnerSecureMessageCreated, | |
170 weak_ptr_factory_.GetWeakPtr())); | |
171 } | |
172 | |
173 void CryptAuthEnrollerImpl::OnInnerSecureMessageCreated( | |
174 const std::string& inner_message) { | |
175 if (inner_message.empty()) { | |
176 PA_LOG(ERROR) << "Error creating inner message"; | |
177 callback_.Run(false); | |
178 return; | |
179 } | |
180 | |
181 SecureMessageDelegate::CreateOptions options; | |
182 options.encryption_scheme = securemessage::AES_256_CBC; | |
183 options.signature_scheme = securemessage::HMAC_SHA256; | |
184 options.public_metadata = CreateEnrollmentPublicMetadata(); | |
185 | |
186 // The outer message encrypts and signs the inner message with the derived | |
187 // symmetric session key. | |
188 secure_message_delegate_->CreateSecureMessage( | |
189 inner_message, symmetric_key_, options, | |
190 base::Bind(&CryptAuthEnrollerImpl::OnOuterSecureMessageCreated, | |
191 weak_ptr_factory_.GetWeakPtr())); | |
192 } | |
193 | |
194 void CryptAuthEnrollerImpl::OnOuterSecureMessageCreated( | |
195 const std::string& outer_message) { | |
196 PA_LOG(INFO) << "SecureMessage created, calling FinishEnrollment API."; | |
197 | |
198 cryptauth::FinishEnrollmentRequest request; | |
199 request.set_enrollment_session_id(setup_info_.enrollment_session_id()); | |
200 request.set_enrollment_message(outer_message); | |
201 request.set_device_ephemeral_key(session_public_key_); | |
202 request.set_invocation_reason(invocation_reason_); | |
203 | |
204 cryptauth_client_ = client_factory_->CreateInstance(); | |
205 cryptauth_client_->FinishEnrollment( | |
206 request, base::Bind(&CryptAuthEnrollerImpl::OnFinishEnrollmentSuccess, | |
207 weak_ptr_factory_.GetWeakPtr()), | |
208 base::Bind(&CryptAuthEnrollerImpl::OnFinishEnrollmentFailure, | |
209 weak_ptr_factory_.GetWeakPtr())); | |
210 } | |
211 | |
212 void CryptAuthEnrollerImpl::OnFinishEnrollmentSuccess( | |
213 const cryptauth::FinishEnrollmentResponse& response) { | |
214 if (response.status() != kResponseStatusOk) { | |
215 PA_LOG(WARNING) << "Unexpected status for FinishEnrollment: " | |
216 << response.status(); | |
217 callback_.Run(false); | |
218 } else { | |
219 callback_.Run(true); | |
220 } | |
221 } | |
222 | |
223 void CryptAuthEnrollerImpl::OnFinishEnrollmentFailure( | |
224 const std::string& error) { | |
225 PA_LOG(WARNING) << "FinishEnrollment API failed with error: " << error; | |
226 callback_.Run(false); | |
227 } | |
228 | |
229 } // namespace proximity_auth | |
OLD | NEW |