blob: 31e1b5419a544e169ed053b610596305a5960c33 [file] [log] [blame]
// 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/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"
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 = strlen(quic::ChannelIDVerifier::kContextStr) + 1;
const size_t len2 = strlen(quic::ChannelIDVerifier::kClientToServerStr) + 1;
std::vector<uint8_t> data(len1 + len2 + signed_data.size());
memcpy(&data[0], quic::ChannelIDVerifier::kContextStr, len1);
memcpy(&data[len1], quic::ChannelIDVerifier::kClientToServerStr, len2);
memcpy(&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;
}
memcpy(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