// Copyright 2013 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/autofill/browser/wallet/encryption_escrow_client.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/stringprintf.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/utf_string_conversions.h" #include "components/autofill/browser/wallet/encryption_escrow_client_observer.h" #include "components/autofill/browser/wallet/instrument.h" #include "components/autofill/browser/wallet/wallet_service_url.h" #include "googleurl/src/gurl.h" #include "net/base/escape.h" #include "net/http/http_status_code.h" #include "net/url_request/url_fetcher.h" #include "net/url_request/url_request_context_getter.h" namespace { const char kEncryptOtpBodyFormat[] = "cvv=%s:%s"; const char kEscrowInstrumentInformationFormat[] = "gid=%s&cardNumber=%s&cvv=%s"; const char kEscrowCardVerficationNumberFormat[] = "gid=%s&cvv=%s"; const char kApplicationMimeType[] = "application/x-www-form-urlencoded"; // The maximum number of bits in the one time pad that the server is willing to // accept. const size_t kMaxBits = 56; // The minimum number of bits in the one time pad that the server is willing to // accept. const size_t kMinBits = 40; } // anonymous namespace namespace autofill { namespace wallet { EncryptionEscrowClient::EncryptionEscrowClient( net::URLRequestContextGetter* context_getter, EncryptionEscrowClientObserver* observer) : context_getter_(context_getter), observer_(observer), request_type_(NO_PENDING_REQUEST) { DCHECK(context_getter_.get()); DCHECK(observer_); } EncryptionEscrowClient::~EncryptionEscrowClient() {} void EncryptionEscrowClient::EncryptOneTimePad( const std::vector& one_time_pad) { DCHECK_EQ(NO_PENDING_REQUEST, request_type_); size_t num_bits = one_time_pad.size() * 8; DCHECK_LE(num_bits, kMaxBits); DCHECK_GE(num_bits, kMinBits); request_type_ = ENCRYPT_ONE_TIME_PAD; std::string post_body = base::StringPrintf( kEncryptOtpBodyFormat, base::HexEncode(&num_bits, 1).c_str(), base::HexEncode(&(one_time_pad[0]), one_time_pad.size()).c_str()); MakeRequest(GetEncryptionUrl(), post_body); } void EncryptionEscrowClient::EscrowInstrumentInformation( const Instrument& new_instrument, const std::string& obfuscated_gaia_id) { DCHECK_EQ(NO_PENDING_REQUEST, request_type_); request_type_ = ESCROW_INSTRUMENT_INFORMATION; const std::string& primary_account_number = net::EscapeUrlEncodedData( UTF16ToUTF8(new_instrument.primary_account_number()), true); const std::string& card_verification_number = net::EscapeUrlEncodedData( UTF16ToUTF8(new_instrument.card_verification_number()), true); std::string post_body = base::StringPrintf( kEscrowInstrumentInformationFormat, obfuscated_gaia_id.c_str(), primary_account_number.c_str(), card_verification_number.c_str()); MakeRequest(GetEscrowUrl(), post_body); } void EncryptionEscrowClient::EscrowCardVerificationNumber( const std::string& card_verification_number, const std::string& obfuscated_gaia_id) { DCHECK_EQ(NO_PENDING_REQUEST, request_type_); request_type_ = ESCROW_CARD_VERIFICATION_NUMBER; std::string post_body = base::StringPrintf( kEscrowCardVerficationNumberFormat, obfuscated_gaia_id.c_str(), card_verification_number.c_str()); MakeRequest(GetEscrowUrl(), post_body); } void EncryptionEscrowClient::CancelRequest() { request_.reset(); request_type_ = NO_PENDING_REQUEST; } bool EncryptionEscrowClient::HasRequestInProgress() const { return request_; } void EncryptionEscrowClient::MakeRequest(const GURL& url, const std::string& post_body) { DCHECK(!request_.get()); request_.reset(net::URLFetcher::Create( 1, url, net::URLFetcher::POST, this)); request_->SetRequestContext(context_getter_.get()); DVLOG(1) << "url=" << url << ", post_body=" << post_body; request_->SetUploadData(kApplicationMimeType, post_body); request_->Start(); observer_->OnDidMakeRequest(); } // TODO(ahutter): Add manual retry logic if it's necessary. void EncryptionEscrowClient::OnURLFetchComplete( const net::URLFetcher* source) { DCHECK(observer_); scoped_ptr old_request = request_.Pass(); DCHECK_EQ(source, old_request.get()); DVLOG(1) << "Got response from " << source->GetOriginalURL(); RequestType type = request_type_; request_type_ = NO_PENDING_REQUEST; std::string data; source->GetResponseAsString(&data); DVLOG(1) << "Response body: " << data; if (source->GetResponseCode() != net::HTTP_OK) { observer_->OnNetworkError(source->GetResponseCode()); return; } if (data.empty()) { HandleMalformedResponse(old_request.get()); return; } switch (type) { case ENCRYPT_ONE_TIME_PAD: { std::vector splits; // The response from the server should be formatted as // "|". base::SplitString(data, '|', &splits); if (splits.size() == 2) observer_->OnDidEncryptOneTimePad(splits[1], splits[0]); else HandleMalformedResponse(old_request.get()); break; } case ESCROW_INSTRUMENT_INFORMATION: observer_->OnDidEscrowInstrumentInformation(data); break; case ESCROW_CARD_VERIFICATION_NUMBER: observer_->OnDidEscrowCardVerificationNumber(data); break; case NO_PENDING_REQUEST: NOTREACHED(); } } void EncryptionEscrowClient::HandleMalformedResponse(net::URLFetcher* request) { // Called to inform exponential backoff logic of the error. request->ReceivedContentWasMalformed(); observer_->OnMalformedResponse(); } } // namespace wallet } // namespace autofill