PageRenderTime 43ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/ash/services/device_sync/cryptauth_group_private_key_sharer_impl.cc

https://github.com/chromium/chromium
C++ | 380 lines | 293 code | 59 blank | 28 comment | 14 complexity | 79e06c0048a5e34b9004cf4b94a23c36 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause
  1. // Copyright 2019 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. #include "ash/services/device_sync/cryptauth_group_private_key_sharer_impl.h"
  5. #include <utility>
  6. #include "ash/components/multidevice/logging/logging.h"
  7. #include "ash/services/device_sync/async_execution_time_metrics_logger.h"
  8. #include "ash/services/device_sync/cryptauth_client.h"
  9. #include "ash/services/device_sync/cryptauth_ecies_encryptor_impl.h"
  10. #include "ash/services/device_sync/cryptauth_key.h"
  11. #include "ash/services/device_sync/cryptauth_task_metrics_logger.h"
  12. #include "base/bind.h"
  13. #include "base/memory/ptr_util.h"
  14. #include "base/metrics/histogram_functions.h"
  15. #include "crypto/sha2.h"
  16. namespace ash {
  17. namespace device_sync {
  18. namespace {
  19. // Timeout values for asynchronous operations.
  20. // TODO(https://crbug.com/933656): Use async execution time metrics to tune
  21. // these timeout values. For now, set these timeouts to the max execution time
  22. // recorded by the metrics.
  23. constexpr base::TimeDelta kWaitingForGroupPrivateKeyEncryptionTimeout =
  24. kMaxAsyncExecutionTime;
  25. constexpr base::TimeDelta kWaitingForShareGroupPrivateKeyResponseTimeout =
  26. kMaxAsyncExecutionTime;
  27. CryptAuthDeviceSyncResult::ResultCode
  28. ShareGroupPrivateKeyNetworkRequestErrorToResultCode(NetworkRequestError error) {
  29. switch (error) {
  30. case NetworkRequestError::kOffline:
  31. return CryptAuthDeviceSyncResult::ResultCode::
  32. kErrorShareGroupPrivateKeyApiCallOffline;
  33. case NetworkRequestError::kEndpointNotFound:
  34. return CryptAuthDeviceSyncResult::ResultCode::
  35. kErrorShareGroupPrivateKeyApiCallEndpointNotFound;
  36. case NetworkRequestError::kAuthenticationError:
  37. return CryptAuthDeviceSyncResult::ResultCode::
  38. kErrorShareGroupPrivateKeyApiCallAuthenticationError;
  39. case NetworkRequestError::kBadRequest:
  40. return CryptAuthDeviceSyncResult::ResultCode::
  41. kErrorShareGroupPrivateKeyApiCallBadRequest;
  42. case NetworkRequestError::kResponseMalformed:
  43. return CryptAuthDeviceSyncResult::ResultCode::
  44. kErrorShareGroupPrivateKeyApiCallResponseMalformed;
  45. case NetworkRequestError::kInternalServerError:
  46. return CryptAuthDeviceSyncResult::ResultCode::
  47. kErrorShareGroupPrivateKeyApiCallInternalServerError;
  48. case NetworkRequestError::kUnknown:
  49. return CryptAuthDeviceSyncResult::ResultCode::
  50. kErrorShareGroupPrivateKeyApiCallUnknownError;
  51. }
  52. }
  53. // The first 8 bytes of the SHA-256 hash of |str|, converted into a 64-bit
  54. // signed integer in little-endian order. This format is chosen to be consistent
  55. // with the CryptAuth backend implementation.
  56. int64_t CalculateInt64Sha256Hash(const std::string& str) {
  57. uint8_t hash_bytes[sizeof(int64_t)];
  58. crypto::SHA256HashString(str, hash_bytes, sizeof(hash_bytes));
  59. int64_t hash_int64 = 0;
  60. for (size_t i = 0; i < 8u; ++i)
  61. hash_int64 |= static_cast<int64_t>(hash_bytes[i]) << (i * 8);
  62. return hash_int64;
  63. }
  64. void RecordGroupPrivateKeyEncryptionMetrics(
  65. const base::TimeDelta& execution_time,
  66. CryptAuthAsyncTaskResult result) {
  67. LogAsyncExecutionTimeMetric(
  68. "CryptAuth.DeviceSyncV2.GroupPrivateKeySharer.ExecutionTime."
  69. "GroupPrivateKeyEncryption",
  70. execution_time);
  71. LogCryptAuthAsyncTaskSuccessMetric(
  72. "CryptAuth.DeviceSyncV2.GroupPrivateKeySharer.AsyncTaskResult."
  73. "GroupPrivateKeyEncryption",
  74. result);
  75. }
  76. void RecordShareGroupPrivateKeyMetrics(const base::TimeDelta& execution_time,
  77. CryptAuthApiCallResult result) {
  78. LogAsyncExecutionTimeMetric(
  79. "CryptAuth.DeviceSyncV2.GroupPrivateKeySharer.ExecutionTime."
  80. "ShareGroupPrivateKey",
  81. execution_time);
  82. LogCryptAuthApiCallSuccessMetric(
  83. "CryptAuth.DeviceSyncV2.GroupPrivateKeySharer.ApiCallResult."
  84. "ShareGroupPrivateKey",
  85. result);
  86. }
  87. } // namespace
  88. // static
  89. CryptAuthGroupPrivateKeySharerImpl::Factory*
  90. CryptAuthGroupPrivateKeySharerImpl::Factory::test_factory_ = nullptr;
  91. // static
  92. std::unique_ptr<CryptAuthGroupPrivateKeySharer>
  93. CryptAuthGroupPrivateKeySharerImpl::Factory::Create(
  94. CryptAuthClientFactory* client_factory,
  95. std::unique_ptr<base::OneShotTimer> timer) {
  96. if (test_factory_)
  97. return test_factory_->CreateInstance(client_factory, std::move(timer));
  98. return base::WrapUnique(
  99. new CryptAuthGroupPrivateKeySharerImpl(client_factory, std::move(timer)));
  100. }
  101. // static
  102. void CryptAuthGroupPrivateKeySharerImpl::Factory::SetFactoryForTesting(
  103. Factory* test_factory) {
  104. test_factory_ = test_factory;
  105. }
  106. CryptAuthGroupPrivateKeySharerImpl::Factory::~Factory() = default;
  107. CryptAuthGroupPrivateKeySharerImpl::CryptAuthGroupPrivateKeySharerImpl(
  108. CryptAuthClientFactory* client_factory,
  109. std::unique_ptr<base::OneShotTimer> timer)
  110. : client_factory_(client_factory), timer_(std::move(timer)) {
  111. DCHECK(client_factory);
  112. }
  113. CryptAuthGroupPrivateKeySharerImpl::~CryptAuthGroupPrivateKeySharerImpl() =
  114. default;
  115. // static
  116. absl::optional<base::TimeDelta>
  117. CryptAuthGroupPrivateKeySharerImpl::GetTimeoutForState(State state) {
  118. switch (state) {
  119. case State::kWaitingForGroupPrivateKeyEncryption:
  120. return kWaitingForGroupPrivateKeyEncryptionTimeout;
  121. case State::kWaitingForShareGroupPrivateKeyResponse:
  122. return kWaitingForShareGroupPrivateKeyResponseTimeout;
  123. default:
  124. // Signifies that there should not be a timeout.
  125. return absl::nullopt;
  126. }
  127. }
  128. // static
  129. absl::optional<CryptAuthDeviceSyncResult::ResultCode>
  130. CryptAuthGroupPrivateKeySharerImpl::ResultCodeErrorFromTimeoutDuringState(
  131. State state) {
  132. switch (state) {
  133. case State::kWaitingForGroupPrivateKeyEncryption:
  134. return CryptAuthDeviceSyncResult::ResultCode::
  135. kErrorTimeoutWaitingForGroupPrivateKeyEncryption;
  136. case State::kWaitingForShareGroupPrivateKeyResponse:
  137. return CryptAuthDeviceSyncResult::ResultCode::
  138. kErrorTimeoutWaitingForShareGroupPrivateKeyResponse;
  139. default:
  140. return absl::nullopt;
  141. }
  142. }
  143. void CryptAuthGroupPrivateKeySharerImpl::SetState(State state) {
  144. timer_->Stop();
  145. PA_LOG(INFO) << "Transitioning from " << state_ << " to " << state;
  146. state_ = state;
  147. last_state_change_timestamp_ = base::TimeTicks::Now();
  148. absl::optional<base::TimeDelta> timeout_for_state = GetTimeoutForState(state);
  149. if (!timeout_for_state)
  150. return;
  151. timer_->Start(FROM_HERE, *timeout_for_state,
  152. base::BindOnce(&CryptAuthGroupPrivateKeySharerImpl::OnTimeout,
  153. base::Unretained(this)));
  154. }
  155. void CryptAuthGroupPrivateKeySharerImpl::OnTimeout() {
  156. // If there's a timeout specified, there should be a corresponding error code.
  157. absl::optional<CryptAuthDeviceSyncResult::ResultCode> error_code =
  158. ResultCodeErrorFromTimeoutDuringState(state_);
  159. DCHECK(error_code);
  160. base::TimeDelta execution_time =
  161. base::TimeTicks::Now() - last_state_change_timestamp_;
  162. switch (state_) {
  163. case State::kWaitingForGroupPrivateKeyEncryption:
  164. RecordGroupPrivateKeyEncryptionMetrics(
  165. execution_time, CryptAuthAsyncTaskResult::kTimeout);
  166. break;
  167. case State::kWaitingForShareGroupPrivateKeyResponse:
  168. RecordShareGroupPrivateKeyMetrics(execution_time,
  169. CryptAuthApiCallResult::kTimeout);
  170. break;
  171. default:
  172. NOTREACHED();
  173. }
  174. FinishAttempt(*error_code);
  175. }
  176. void CryptAuthGroupPrivateKeySharerImpl::OnAttemptStarted(
  177. const cryptauthv2::RequestContext& request_context,
  178. const CryptAuthKey& group_key,
  179. const IdToEncryptingKeyMap& id_to_encrypting_key_map) {
  180. DCHECK_EQ(State::kNotStarted, state_);
  181. DCHECK(!group_key.private_key().empty());
  182. CryptAuthEciesEncryptor::IdToInputMap group_private_keys_to_encrypt;
  183. for (const auto& id_encrypting_key_pair : id_to_encrypting_key_map) {
  184. const std::string& id = id_encrypting_key_pair.first;
  185. const std::string& encrypting_key = id_encrypting_key_pair.second;
  186. // If the encrypting key is empty, the group private key cannot be
  187. // encrypted. Skip this ID and attempt to encrypt the group private key for
  188. // as many IDs as possible.
  189. bool is_encrypting_key_empty = encrypting_key.empty();
  190. base::UmaHistogramBoolean(
  191. "CryptAuth.DeviceSyncV2.GroupPrivateKeySharer.IsEncryptingKeyEmpty",
  192. is_encrypting_key_empty);
  193. if (is_encrypting_key_empty) {
  194. PA_LOG(ERROR) << "Cannot encrypt group private key for device with ID "
  195. << id << ". Encrypting key is empty.";
  196. did_non_fatal_error_occur_ = true;
  197. continue;
  198. }
  199. group_private_keys_to_encrypt[id] = CryptAuthEciesEncryptor::PayloadAndKey(
  200. group_key.private_key(), encrypting_key);
  201. }
  202. // All encrypting keys are empty; encryption not possible.
  203. if (group_private_keys_to_encrypt.empty()) {
  204. FinishAttempt(
  205. CryptAuthDeviceSyncResult::ResultCode::kErrorEncryptingGroupPrivateKey);
  206. return;
  207. }
  208. SetState(State::kWaitingForGroupPrivateKeyEncryption);
  209. encryptor_ = CryptAuthEciesEncryptorImpl::Factory::Create();
  210. encryptor_->BatchEncrypt(
  211. group_private_keys_to_encrypt,
  212. base::BindOnce(
  213. &CryptAuthGroupPrivateKeySharerImpl::OnGroupPrivateKeysEncrypted,
  214. base::Unretained(this), request_context, group_key));
  215. }
  216. void CryptAuthGroupPrivateKeySharerImpl::OnGroupPrivateKeysEncrypted(
  217. const cryptauthv2::RequestContext& request_context,
  218. const CryptAuthKey& group_key,
  219. const CryptAuthEciesEncryptor::IdToOutputMap&
  220. id_to_encrypted_group_private_key_map) {
  221. DCHECK_EQ(State::kWaitingForGroupPrivateKeyEncryption, state_);
  222. // Record a success because the operation did not timeout. A separate metric
  223. // tracks individual encryption failures.
  224. RecordGroupPrivateKeyEncryptionMetrics(
  225. base::TimeTicks::Now() - last_state_change_timestamp_,
  226. CryptAuthAsyncTaskResult::kSuccess);
  227. cryptauthv2::ShareGroupPrivateKeyRequest request;
  228. request.mutable_context()->CopyFrom(request_context);
  229. for (const auto& id_encrypted_key_pair :
  230. id_to_encrypted_group_private_key_map) {
  231. // If the group private key could not be encrypted for this ID--due to an
  232. // invalid encrypting key, for instance--skip it. Continue to share as many
  233. // encrypted group private keys as possible.
  234. bool was_encryption_successful = id_encrypted_key_pair.second.has_value();
  235. base::UmaHistogramBoolean(
  236. "CryptAuth.DeviceSyncV2.GroupPrivateKeySharer.EncryptionSuccess",
  237. was_encryption_successful);
  238. if (!was_encryption_successful) {
  239. PA_LOG(ERROR) << "Group private key could not be encrypted for device "
  240. << "with ID " << id_encrypted_key_pair.first;
  241. did_non_fatal_error_occur_ = true;
  242. continue;
  243. }
  244. cryptauthv2::EncryptedGroupPrivateKey* encrypted_key =
  245. request.add_encrypted_group_private_keys();
  246. encrypted_key->set_recipient_device_id(id_encrypted_key_pair.first);
  247. encrypted_key->set_sender_device_id(request_context.device_id());
  248. encrypted_key->set_encrypted_private_key(*id_encrypted_key_pair.second);
  249. // CryptAuth requires a SHA-256 hash of the group public key as an int64.
  250. encrypted_key->set_group_public_key_hash(
  251. CalculateInt64Sha256Hash(group_key.public_key()));
  252. }
  253. // All encryption attempts failed; nothing to share.
  254. if (request.encrypted_group_private_keys().empty()) {
  255. FinishAttempt(
  256. CryptAuthDeviceSyncResult::ResultCode::kErrorEncryptingGroupPrivateKey);
  257. return;
  258. }
  259. SetState(State::kWaitingForShareGroupPrivateKeyResponse);
  260. cryptauth_client_ = client_factory_->CreateInstance();
  261. cryptauth_client_->ShareGroupPrivateKey(
  262. request,
  263. base::BindOnce(
  264. &CryptAuthGroupPrivateKeySharerImpl::OnShareGroupPrivateKeySuccess,
  265. base::Unretained(this)),
  266. base::BindOnce(
  267. &CryptAuthGroupPrivateKeySharerImpl::OnShareGroupPrivateKeyFailure,
  268. base::Unretained(this)));
  269. }
  270. void CryptAuthGroupPrivateKeySharerImpl::OnShareGroupPrivateKeySuccess(
  271. const cryptauthv2::ShareGroupPrivateKeyResponse& response) {
  272. DCHECK_EQ(State::kWaitingForShareGroupPrivateKeyResponse, state_);
  273. RecordShareGroupPrivateKeyMetrics(
  274. base::TimeTicks::Now() - last_state_change_timestamp_,
  275. CryptAuthApiCallResult::kSuccess);
  276. CryptAuthDeviceSyncResult::ResultCode result_code =
  277. did_non_fatal_error_occur_
  278. ? CryptAuthDeviceSyncResult::ResultCode::kFinishedWithNonFatalErrors
  279. : CryptAuthDeviceSyncResult::ResultCode::kSuccess;
  280. FinishAttempt(result_code);
  281. }
  282. void CryptAuthGroupPrivateKeySharerImpl::OnShareGroupPrivateKeyFailure(
  283. NetworkRequestError error) {
  284. DCHECK_EQ(State::kWaitingForShareGroupPrivateKeyResponse, state_);
  285. RecordShareGroupPrivateKeyMetrics(
  286. base::TimeTicks::Now() - last_state_change_timestamp_,
  287. CryptAuthApiCallResultFromNetworkRequestError(error));
  288. FinishAttempt(ShareGroupPrivateKeyNetworkRequestErrorToResultCode(error));
  289. }
  290. void CryptAuthGroupPrivateKeySharerImpl::FinishAttempt(
  291. CryptAuthDeviceSyncResult::ResultCode result_code) {
  292. encryptor_.reset();
  293. cryptauth_client_.reset();
  294. SetState(State::kFinished);
  295. OnAttemptFinished(result_code);
  296. }
  297. std::ostream& operator<<(
  298. std::ostream& stream,
  299. const CryptAuthGroupPrivateKeySharerImpl::State& state) {
  300. switch (state) {
  301. case CryptAuthGroupPrivateKeySharerImpl::State::kNotStarted:
  302. stream << "[GroupPrivateKeySharer state: Not started]";
  303. break;
  304. case CryptAuthGroupPrivateKeySharerImpl::State::
  305. kWaitingForGroupPrivateKeyEncryption:
  306. stream << "[GroupPrivateKeySharer state: Waiting for group private key "
  307. << "encryption]";
  308. break;
  309. case CryptAuthGroupPrivateKeySharerImpl::State::
  310. kWaitingForShareGroupPrivateKeyResponse:
  311. stream << "[GroupPrivateKeySharer state: Waiting for "
  312. << "ShareGroupPrivateKey response]";
  313. break;
  314. case CryptAuthGroupPrivateKeySharerImpl::State::kFinished:
  315. stream << "[GroupPrivateKeySharer state: Finished]";
  316. break;
  317. }
  318. return stream;
  319. }
  320. } // namespace device_sync
  321. } // namespace ash