| 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 |