blob: 15311fdc802f7fc3169a60d48d0793ebff4181a9 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/sync/trusted_vault/trusted_vault_connection_impl.h"
#include <utility>
#include "base/containers/span.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/sync/protocol/vault.pb.h"
#include "components/sync/trusted_vault/download_keys_response_handler.h"
#include "components/sync/trusted_vault/securebox.h"
#include "components/sync/trusted_vault/trusted_vault_access_token_fetcher.h"
#include "components/sync/trusted_vault/trusted_vault_request.h"
#include "crypto/hmac.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace syncer {
namespace {
const size_t kMemberProofLength = 32;
const uint8_t kWrappedKeyHeader[] = {'V', '1', ' ', 's', 'h', 'a', 'r',
'e', 'd', '_', 'k', 'e', 'y'};
const char kJoinSecurityDomainsURLPath[] = "/domain:join";
const char kListSecurityDomainsURLPathAndQuery[] = "/domain:list?view=1";
const char kSecurityDomainName[] = "chromesync";
// Helper function for filling protobuf bytes field: protobuf represent them as
// std::string, while in code std::vector<uint8_t> or base::span<uint8_t> is
// more common.
// TODO(crbug.com/1113598): this function and its counterpart (proto string to
// bytes vector) is useful in many places under trusted_vault directory,
// consider moving it to some utility file.
void AssignBytesToProtoString(base::span<const uint8_t> bytes,
std::string* bytes_proto_field) {
*bytes_proto_field = std::string(bytes.begin(), bytes.end());
}
void ProcessRegisterDeviceResponse(
TrustedVaultConnection::RegisterAuthenticationFactorCallback callback,
TrustedVaultRequest::HttpStatus http_status,
const std::string& response_body) {
TrustedVaultRequestStatus registration_status;
switch (http_status) {
case TrustedVaultRequest::HttpStatus::kSuccess:
registration_status = TrustedVaultRequestStatus::kSuccess;
break;
case TrustedVaultRequest::HttpStatus::kOtherError:
registration_status = TrustedVaultRequestStatus::kOtherError;
break;
case TrustedVaultRequest::HttpStatus::kBadRequest:
// Bad request response indicates that client data is outdated (e.g.
// locally available trusted vault key is not the recent one).
registration_status = TrustedVaultRequestStatus::kLocalDataObsolete;
}
std::move(callback).Run(registration_status);
}
std::vector<uint8_t> ComputeWrappedKey(
const SecureBoxPublicKey& public_key,
const std::vector<uint8_t>& trusted_vault_key) {
return public_key.Encrypt(
/*shared_secret=*/base::span<const uint8_t>(), kWrappedKeyHeader,
/*payload=*/trusted_vault_key);
}
std::vector<uint8_t> ComputeMemberProof(
const SecureBoxPublicKey& public_key,
const std::vector<uint8_t>& trusted_vault_key) {
crypto::HMAC hmac(crypto::HMAC::SHA256);
CHECK(hmac.Init(trusted_vault_key));
std::vector<uint8_t> digest_bytes(kMemberProofLength);
CHECK(hmac.Sign(public_key.ExportToBytes(), digest_bytes));
return digest_bytes;
}
sync_pb::SharedKey CreateMemberSharedKey(
int trusted_vault_key_version,
const std::vector<uint8_t>& trusted_vault_key,
const SecureBoxPublicKey& public_key) {
sync_pb::SharedKey shared_key;
shared_key.set_epoch(trusted_vault_key_version);
AssignBytesToProtoString(ComputeWrappedKey(public_key, trusted_vault_key),
shared_key.mutable_wrapped_key());
AssignBytesToProtoString(ComputeMemberProof(public_key, trusted_vault_key),
shared_key.mutable_member_proof());
return shared_key;
}
sync_pb::JoinSecurityDomainsRequest CreateJoinSecurityDomainsRequest(
int last_trusted_vault_key_version,
const std::vector<uint8_t>& last_trusted_vault_key,
const SecureBoxPublicKey& public_key) {
sync_pb::SecurityDomain::Member member;
const std::vector<uint8_t> public_key_bytes = public_key.ExportToBytes();
AssignBytesToProtoString(public_key.ExportToBytes(),
member.mutable_public_key());
*member.add_keys() = CreateMemberSharedKey(
last_trusted_vault_key_version, last_trusted_vault_key, public_key);
sync_pb::SecurityDomain security_domain;
security_domain.set_name(kSecurityDomainName);
*security_domain.add_members() = member;
sync_pb::JoinSecurityDomainsRequest result;
*result.add_security_domains() = security_domain;
return result;
}
void ProcessDownloadKeysResponse(
std::unique_ptr<DownloadKeysResponseHandler> response_handler,
TrustedVaultConnection::DownloadKeysCallback callback,
TrustedVaultRequest::HttpStatus http_status,
const std::string& response_body) {
DownloadKeysResponseHandler::ProcessedResponse processed_response =
response_handler->ProcessResponse(http_status, response_body);
std::move(callback).Run(processed_response.status, processed_response.keys,
processed_response.last_key_version);
}
} // namespace
TrustedVaultConnectionImpl::TrustedVaultConnectionImpl(
const GURL& trusted_vault_service_url,
std::unique_ptr<network::PendingSharedURLLoaderFactory>
pending_url_loader_factory,
std::unique_ptr<TrustedVaultAccessTokenFetcher> access_token_fetcher)
: pending_url_loader_factory_(std::move(pending_url_loader_factory)),
access_token_fetcher_(std::move(access_token_fetcher)),
trusted_vault_service_url_(trusted_vault_service_url) {
DCHECK(trusted_vault_service_url_.is_valid());
}
TrustedVaultConnectionImpl::~TrustedVaultConnectionImpl() = default;
std::unique_ptr<TrustedVaultConnection::Request>
TrustedVaultConnectionImpl::RegisterAuthenticationFactor(
const CoreAccountInfo& account_info,
const std::vector<uint8_t>& last_trusted_vault_key,
int last_trusted_vault_key_version,
const SecureBoxPublicKey& public_key,
RegisterAuthenticationFactorCallback callback) {
auto request = std::make_unique<TrustedVaultRequest>(
TrustedVaultRequest::HttpMethod::kPost,
GURL(trusted_vault_service_url_.spec() + kJoinSecurityDomainsURLPath),
/*serialized_request_proto=*/
CreateJoinSecurityDomainsRequest(last_trusted_vault_key_version,
last_trusted_vault_key, public_key)
.SerializeAsString());
request->FetchAccessTokenAndSendRequest(
account_info.account_id, GetOrCreateURLLoaderFactory(),
access_token_fetcher_.get(),
base::BindOnce(ProcessRegisterDeviceResponse, std::move(callback)));
return request;
}
std::unique_ptr<TrustedVaultConnection::Request>
TrustedVaultConnectionImpl::DownloadKeys(
const CoreAccountInfo& account_info,
const std::vector<uint8_t>& last_trusted_vault_key,
int last_trusted_vault_key_version,
std::unique_ptr<SecureBoxKeyPair> device_key_pair,
DownloadKeysCallback callback) {
auto request = std::make_unique<TrustedVaultRequest>(
TrustedVaultRequest::HttpMethod::kGet,
GURL(trusted_vault_service_url_.spec() +
kListSecurityDomainsURLPathAndQuery),
/*serialized_request_proto=*/base::nullopt);
request->FetchAccessTokenAndSendRequest(
account_info.account_id, GetOrCreateURLLoaderFactory(),
access_token_fetcher_.get(),
base::BindOnce(ProcessDownloadKeysResponse,
/*response_processor=*/
std::make_unique<DownloadKeysResponseHandler>(
last_trusted_vault_key, last_trusted_vault_key_version,
std::move(device_key_pair)),
std::move(callback)));
return request;
}
scoped_refptr<network::SharedURLLoaderFactory>
TrustedVaultConnectionImpl::GetOrCreateURLLoaderFactory() {
if (!url_loader_factory_) {
url_loader_factory_ = network::SharedURLLoaderFactory::Create(
std::move(pending_url_loader_factory_));
}
return url_loader_factory_;
}
} // namespace syncer