| // Copyright 2014 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 "net/quic/crypto/channel_id_chromium.h" | 
 |  | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "crypto/ec_private_key.h" | 
 | #include "crypto/ec_signature_creator.h" | 
 | #include "net/base/net_errors.h" | 
 | #include "net/cert/asn1_util.h" | 
 | #include "net/ssl/channel_id_service.h" | 
 | #include "starboard/client_porting/poem/string_poem.h" | 
 | #include "starboard/memory.h" | 
 | #include "starboard/string.h" | 
 |  | 
 | namespace net { | 
 |  | 
 | ChannelIDKeyChromium::ChannelIDKeyChromium( | 
 |     std::unique_ptr<crypto::ECPrivateKey> ec_private_key) | 
 |     : ec_private_key_(std::move(ec_private_key)) {} | 
 |  | 
 | ChannelIDKeyChromium::~ChannelIDKeyChromium() {} | 
 |  | 
 | bool ChannelIDKeyChromium::Sign(quic::QuicStringPiece signed_data, | 
 |                                 std::string* out_signature) const { | 
 |   std::unique_ptr<crypto::ECSignatureCreator> sig_creator( | 
 |       crypto::ECSignatureCreator::Create(ec_private_key_.get())); | 
 |   if (!sig_creator) { | 
 |     return false; | 
 |   } | 
 |   const size_t len1 = | 
 |       SbStringGetLength(quic::ChannelIDVerifier::kContextStr) + 1; | 
 |   const size_t len2 = | 
 |       SbStringGetLength(quic::ChannelIDVerifier::kClientToServerStr) + 1; | 
 |   std::vector<uint8_t> data(len1 + len2 + signed_data.size()); | 
 |   SbMemoryCopy(&data[0], quic::ChannelIDVerifier::kContextStr, len1); | 
 |   SbMemoryCopy(&data[len1], quic::ChannelIDVerifier::kClientToServerStr, len2); | 
 |   SbMemoryCopy(&data[len1 + len2], signed_data.data(), signed_data.size()); | 
 |   std::vector<uint8_t> der_signature; | 
 |   if (!sig_creator->Sign(&data[0], data.size(), &der_signature)) { | 
 |     return false; | 
 |   } | 
 |   std::vector<uint8_t> raw_signature; | 
 |   if (!sig_creator->DecodeSignature(der_signature, &raw_signature)) { | 
 |     return false; | 
 |   } | 
 |   SbMemoryCopy(base::WriteInto(out_signature, raw_signature.size() + 1), | 
 |                &raw_signature[0], raw_signature.size()); | 
 |   return true; | 
 | } | 
 |  | 
 | std::string ChannelIDKeyChromium::SerializeKey() const { | 
 |   std::string out_key; | 
 |   if (!ec_private_key_->ExportRawPublicKey(&out_key)) { | 
 |     return std::string(); | 
 |   } | 
 |   return out_key; | 
 | } | 
 |  | 
 | // A Job handles the lookup of a single channel ID.  It is owned by the | 
 | // quic::ChannelIDSource. If the operation can not complete synchronously, it | 
 | // will notify the quic::ChannelIDSource upon completion. | 
 | class ChannelIDSourceChromium::Job { | 
 |  public: | 
 |   Job(ChannelIDSourceChromium* channel_id_source, | 
 |       ChannelIDService* channel_id_service); | 
 |  | 
 |   // Starts the channel ID lookup.  If |quic::QUIC_PENDING| is returned, then | 
 |   // |callback| will be invoked asynchronously when the operation completes. | 
 |   quic::QuicAsyncStatus GetChannelIDKey( | 
 |       const std::string& hostname, | 
 |       std::unique_ptr<quic::ChannelIDKey>* channel_id_key, | 
 |       quic::ChannelIDSourceCallback* callback); | 
 |  | 
 |  private: | 
 |   enum State { | 
 |     STATE_NONE, | 
 |     STATE_GET_CHANNEL_ID_KEY, | 
 |     STATE_GET_CHANNEL_ID_KEY_COMPLETE, | 
 |   }; | 
 |  | 
 |   int DoLoop(int last_io_result); | 
 |   void OnIOComplete(int result); | 
 |   int DoGetChannelIDKey(int result); | 
 |   int DoGetChannelIDKeyComplete(int result); | 
 |  | 
 |   // Channel ID source to notify when this jobs completes. | 
 |   ChannelIDSourceChromium* const channel_id_source_; | 
 |  | 
 |   ChannelIDService* const channel_id_service_; | 
 |  | 
 |   std::unique_ptr<crypto::ECPrivateKey> channel_id_crypto_key_; | 
 |   ChannelIDService::Request channel_id_request_; | 
 |  | 
 |   // |hostname| specifies the hostname for which we need a channel ID. | 
 |   std::string hostname_; | 
 |  | 
 |   std::unique_ptr<quic::ChannelIDSourceCallback> callback_; | 
 |  | 
 |   std::unique_ptr<quic::ChannelIDKey> channel_id_key_; | 
 |  | 
 |   State next_state_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(Job); | 
 | }; | 
 |  | 
 | ChannelIDSourceChromium::Job::Job(ChannelIDSourceChromium* channel_id_source, | 
 |                                   ChannelIDService* channel_id_service) | 
 |     : channel_id_source_(channel_id_source), | 
 |       channel_id_service_(channel_id_service), | 
 |       next_state_(STATE_NONE) {} | 
 |  | 
 | quic::QuicAsyncStatus ChannelIDSourceChromium::Job::GetChannelIDKey( | 
 |     const std::string& hostname, | 
 |     std::unique_ptr<quic::ChannelIDKey>* channel_id_key, | 
 |     quic::ChannelIDSourceCallback* callback) { | 
 |   DCHECK(channel_id_key); | 
 |   DCHECK(callback); | 
 |  | 
 |   if (STATE_NONE != next_state_) { | 
 |     DLOG(DFATAL) << "GetChannelIDKey has begun"; | 
 |     return quic::QUIC_FAILURE; | 
 |   } | 
 |  | 
 |   channel_id_key_.reset(); | 
 |  | 
 |   hostname_ = hostname; | 
 |  | 
 |   next_state_ = STATE_GET_CHANNEL_ID_KEY; | 
 |   switch (DoLoop(OK)) { | 
 |     case OK: | 
 |       *channel_id_key = std::move(channel_id_key_); | 
 |       return quic::QUIC_SUCCESS; | 
 |     case ERR_IO_PENDING: | 
 |       callback_.reset(callback); | 
 |       return quic::QUIC_PENDING; | 
 |     default: | 
 |       channel_id_key->reset(); | 
 |       return quic::QUIC_FAILURE; | 
 |   } | 
 | } | 
 |  | 
 | int ChannelIDSourceChromium::Job::DoLoop(int last_result) { | 
 |   int rv = last_result; | 
 |   do { | 
 |     State state = next_state_; | 
 |     next_state_ = STATE_NONE; | 
 |     switch (state) { | 
 |       case STATE_GET_CHANNEL_ID_KEY: | 
 |         DCHECK(rv == OK); | 
 |         rv = DoGetChannelIDKey(rv); | 
 |         break; | 
 |       case STATE_GET_CHANNEL_ID_KEY_COMPLETE: | 
 |         rv = DoGetChannelIDKeyComplete(rv); | 
 |         break; | 
 |       case STATE_NONE: | 
 |       default: | 
 |         rv = ERR_UNEXPECTED; | 
 |         LOG(DFATAL) << "unexpected state " << state; | 
 |         break; | 
 |     } | 
 |   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); | 
 |   return rv; | 
 | } | 
 |  | 
 | void ChannelIDSourceChromium::Job::OnIOComplete(int result) { | 
 |   int rv = DoLoop(result); | 
 |   if (rv != ERR_IO_PENDING) { | 
 |     std::unique_ptr<quic::ChannelIDSourceCallback> callback( | 
 |         callback_.release()); | 
 |     callback->Run(&channel_id_key_); | 
 |     // Will delete |this|. | 
 |     channel_id_source_->OnJobComplete(this); | 
 |   } | 
 | } | 
 |  | 
 | int ChannelIDSourceChromium::Job::DoGetChannelIDKey(int result) { | 
 |   next_state_ = STATE_GET_CHANNEL_ID_KEY_COMPLETE; | 
 |  | 
 |   return channel_id_service_->GetOrCreateChannelID( | 
 |       hostname_, &channel_id_crypto_key_, | 
 |       base::Bind(&ChannelIDSourceChromium::Job::OnIOComplete, | 
 |                  base::Unretained(this)), | 
 |       &channel_id_request_); | 
 | } | 
 |  | 
 | int ChannelIDSourceChromium::Job::DoGetChannelIDKeyComplete(int result) { | 
 |   DCHECK_EQ(STATE_NONE, next_state_); | 
 |   if (result != OK) { | 
 |     DLOG(WARNING) << "Failed to look up channel ID: " << ErrorToString(result); | 
 |     return result; | 
 |   } | 
 |  | 
 |   DCHECK(channel_id_crypto_key_); | 
 |   channel_id_key_.reset( | 
 |       new ChannelIDKeyChromium(std::move(channel_id_crypto_key_))); | 
 |   return result; | 
 | } | 
 |  | 
 | ChannelIDSourceChromium::ChannelIDSourceChromium( | 
 |     ChannelIDService* channel_id_service) | 
 |     : channel_id_service_(channel_id_service) {} | 
 |  | 
 | ChannelIDSourceChromium::~ChannelIDSourceChromium() {} | 
 |  | 
 | quic::QuicAsyncStatus ChannelIDSourceChromium::GetChannelIDKey( | 
 |     const std::string& hostname, | 
 |     std::unique_ptr<quic::ChannelIDKey>* channel_id_key, | 
 |     quic::ChannelIDSourceCallback* callback) { | 
 |   std::unique_ptr<Job> job = std::make_unique<Job>(this, channel_id_service_); | 
 |   quic::QuicAsyncStatus status = | 
 |       job->GetChannelIDKey(hostname, channel_id_key, callback); | 
 |   if (status == quic::QUIC_PENDING) { | 
 |     Job* job_ptr = job.get(); | 
 |     active_jobs_[job_ptr] = std::move(job); | 
 |   } | 
 |   return status; | 
 | } | 
 |  | 
 | void ChannelIDSourceChromium::OnJobComplete(Job* job) { | 
 |   active_jobs_.erase(job); | 
 | } | 
 |  | 
 | }  // namespace net |