This CL adds these classes but does not change any functonality or interface yet. This is in preparation for future CLs. To be used for this: https://codereview.webrtc.org/2000163002/ RTCCertificateGenerator is meant to replace DtlsIdentityStoreInterface and implementations. In order to continue to support mocking and to help with the transition, RTCCertificateGenerator gets an interface that it implements (just like the store has both interface and impl). PeerConnectionFactoryInterface::CreatePeerConnection will take an RTCCertificateGeneratorInterface instead of DtlsIdentityStoreInterface. As to not break Chromium, both versions of CreatePeerConnection need to exist for a transition period. This will be done by wrapping a store into a generator wrapper - RTCCertificateGeneratorStoreWrapper. BUG=webrtc:5707, webrtc:5708 R=hta@webrtc.org, tommi@chromium.org, tommi@webrtc.org Review URL: https://codereview.webrtc.org/2001103002 . Cr-Commit-Position: refs/heads/master@{#12879}
292 lines
10 KiB
C++
292 lines
10 KiB
C++
/*
|
|
* Copyright 2015 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include "webrtc/api/dtlsidentitystore.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "webrtc/api/webrtcsessiondescriptionfactory.h"
|
|
#include "webrtc/base/logging.h"
|
|
|
|
using webrtc::DtlsIdentityRequestObserver;
|
|
|
|
namespace webrtc {
|
|
|
|
// Passed to SSLIdentity::Generate, "WebRTC". Used for the certificates'
|
|
// subject and issuer name.
|
|
const char kIdentityName[] = "WebRTC";
|
|
|
|
namespace {
|
|
|
|
enum {
|
|
MSG_DESTROY,
|
|
MSG_GENERATE_IDENTITY,
|
|
MSG_GENERATE_IDENTITY_RESULT
|
|
};
|
|
|
|
// A |DtlsIdentityRequestObserver| that informs an
|
|
// |RTCCertificateGeneratorCallback| of the result of an identity request. On
|
|
// success, a certificate is created using the identity before passing it to
|
|
// the callback.
|
|
class RTCCertificateStoreCallbackObserver
|
|
: public webrtc::DtlsIdentityRequestObserver {
|
|
public:
|
|
RTCCertificateStoreCallbackObserver(
|
|
const rtc::scoped_refptr<rtc::RTCCertificateGeneratorCallback>& callback)
|
|
: callback_(callback) {}
|
|
|
|
private:
|
|
void OnFailure(int error) override {
|
|
LOG(LS_WARNING) << "DtlsIdentityRequestObserver failure code: " << error;
|
|
Callback(nullptr);
|
|
}
|
|
void OnSuccess(const std::string& der_cert,
|
|
const std::string& der_private_key) override {
|
|
std::string pem_cert = rtc::SSLIdentity::DerToPem(
|
|
rtc::kPemTypeCertificate,
|
|
reinterpret_cast<const unsigned char*>(der_cert.data()),
|
|
der_cert.length());
|
|
std::string pem_key = rtc::SSLIdentity::DerToPem(
|
|
rtc::kPemTypeRsaPrivateKey,
|
|
reinterpret_cast<const unsigned char*>(der_private_key.data()),
|
|
der_private_key.length());
|
|
std::unique_ptr<rtc::SSLIdentity> identity(
|
|
rtc::SSLIdentity::FromPEMStrings(pem_key, pem_cert));
|
|
OnSuccess(std::move(identity));
|
|
}
|
|
void OnSuccess(std::unique_ptr<rtc::SSLIdentity> identity) override {
|
|
Callback(rtc::RTCCertificate::Create(std::move(identity)));
|
|
}
|
|
|
|
void Callback(rtc::scoped_refptr<rtc::RTCCertificate> certificate) {
|
|
if (certificate)
|
|
callback_->OnSuccess(certificate);
|
|
else
|
|
callback_->OnFailure();
|
|
}
|
|
|
|
rtc::scoped_refptr<rtc::RTCCertificateGeneratorCallback> callback_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
// This class runs on the worker thread to generate the identity. It's necessary
|
|
// to separate this class from DtlsIdentityStore so that it can live on the
|
|
// worker thread after DtlsIdentityStore is destroyed.
|
|
class DtlsIdentityStoreImpl::WorkerTask : public sigslot::has_slots<>,
|
|
public rtc::MessageHandler {
|
|
public:
|
|
WorkerTask(DtlsIdentityStoreImpl* store, rtc::KeyType key_type)
|
|
: signaling_thread_(rtc::Thread::Current()),
|
|
store_(store),
|
|
key_type_(key_type) {
|
|
store_->SignalDestroyed.connect(this, &WorkerTask::OnStoreDestroyed);
|
|
}
|
|
|
|
virtual ~WorkerTask() { RTC_DCHECK(signaling_thread_->IsCurrent()); }
|
|
|
|
private:
|
|
void GenerateIdentity_w() {
|
|
LOG(LS_INFO) << "Generating identity, using keytype " << key_type_;
|
|
std::unique_ptr<rtc::SSLIdentity> identity(
|
|
rtc::SSLIdentity::Generate(kIdentityName, key_type_));
|
|
|
|
// Posting to |this| avoids touching |store_| on threads other than
|
|
// |signaling_thread_| and thus avoids having to use locks.
|
|
IdentityResultMessageData* msg = new IdentityResultMessageData(
|
|
new IdentityResult(key_type_, std::move(identity)));
|
|
signaling_thread_->Post(this, MSG_GENERATE_IDENTITY_RESULT, msg);
|
|
}
|
|
|
|
void OnMessage(rtc::Message* msg) override {
|
|
switch (msg->message_id) {
|
|
case MSG_GENERATE_IDENTITY:
|
|
// This message always runs on the worker thread.
|
|
GenerateIdentity_w();
|
|
|
|
// Must delete |this|, owned by msg->pdata, on the signaling thread to
|
|
// avoid races on disconnecting the signal.
|
|
signaling_thread_->Post(this, MSG_DESTROY, msg->pdata);
|
|
break;
|
|
case MSG_GENERATE_IDENTITY_RESULT:
|
|
RTC_DCHECK(signaling_thread_->IsCurrent());
|
|
{
|
|
std::unique_ptr<IdentityResultMessageData> pdata(
|
|
static_cast<IdentityResultMessageData*>(msg->pdata));
|
|
if (store_) {
|
|
store_->OnIdentityGenerated(pdata->data()->key_type_,
|
|
std::move(pdata->data()->identity_));
|
|
}
|
|
}
|
|
break;
|
|
case MSG_DESTROY:
|
|
RTC_DCHECK(signaling_thread_->IsCurrent());
|
|
delete msg->pdata;
|
|
// |this| has now been deleted. Don't touch member variables.
|
|
break;
|
|
default:
|
|
RTC_CHECK(false) << "Unexpected message type";
|
|
}
|
|
}
|
|
|
|
void OnStoreDestroyed() {
|
|
RTC_DCHECK(signaling_thread_->IsCurrent());
|
|
store_ = nullptr;
|
|
}
|
|
|
|
rtc::Thread* const signaling_thread_;
|
|
DtlsIdentityStoreImpl* store_; // Only touched on |signaling_thread_|.
|
|
const rtc::KeyType key_type_;
|
|
};
|
|
|
|
DtlsIdentityStoreImpl::DtlsIdentityStoreImpl(rtc::Thread* signaling_thread,
|
|
rtc::Thread* worker_thread)
|
|
: signaling_thread_(signaling_thread),
|
|
worker_thread_(worker_thread),
|
|
request_info_() {
|
|
RTC_DCHECK(signaling_thread_->IsCurrent());
|
|
}
|
|
|
|
DtlsIdentityStoreImpl::~DtlsIdentityStoreImpl() {
|
|
RTC_DCHECK(signaling_thread_->IsCurrent());
|
|
SignalDestroyed();
|
|
}
|
|
|
|
void DtlsIdentityStoreImpl::RequestIdentity(
|
|
const rtc::KeyParams& key_params,
|
|
const rtc::Optional<uint64_t>& expires_ms,
|
|
const rtc::scoped_refptr<DtlsIdentityRequestObserver>& observer) {
|
|
RTC_DCHECK(signaling_thread_->IsCurrent());
|
|
RTC_DCHECK(observer);
|
|
|
|
// Dropping parameterization and |expires_ms|.
|
|
// TODO(hbos,torbjorng): Use parameterizaton/expiration. webrtc:5092.
|
|
GenerateIdentity(key_params.type(), observer);
|
|
}
|
|
|
|
void DtlsIdentityStoreImpl::OnMessage(rtc::Message* msg) {
|
|
RTC_DCHECK(signaling_thread_->IsCurrent());
|
|
switch (msg->message_id) {
|
|
case MSG_GENERATE_IDENTITY_RESULT: {
|
|
std::unique_ptr<IdentityResultMessageData> pdata(
|
|
static_cast<IdentityResultMessageData*>(msg->pdata));
|
|
OnIdentityGenerated(pdata->data()->key_type_,
|
|
std::move(pdata->data()->identity_));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DtlsIdentityStoreImpl::HasFreeIdentityForTesting(
|
|
rtc::KeyType key_type) const {
|
|
RTC_DCHECK(signaling_thread_->IsCurrent());
|
|
return request_info_[key_type].free_identity_.get() != nullptr;
|
|
}
|
|
|
|
void DtlsIdentityStoreImpl::GenerateIdentity(
|
|
rtc::KeyType key_type,
|
|
const rtc::scoped_refptr<DtlsIdentityRequestObserver>& observer) {
|
|
RTC_DCHECK(signaling_thread_->IsCurrent());
|
|
|
|
// Enqueue observer to be informed when generation of |key_type| is completed.
|
|
if (observer.get()) {
|
|
request_info_[key_type].request_observers_.push(observer);
|
|
|
|
// Already have a free identity generated?
|
|
if (request_info_[key_type].free_identity_.get()) {
|
|
// Return identity async - post even though we are on |signaling_thread_|.
|
|
LOG(LS_VERBOSE) << "Using a free DTLS identity.";
|
|
++request_info_[key_type].gen_in_progress_counts_;
|
|
IdentityResultMessageData* msg =
|
|
new IdentityResultMessageData(new IdentityResult(
|
|
key_type, std::move(request_info_[key_type].free_identity_)));
|
|
signaling_thread_->Post(this, MSG_GENERATE_IDENTITY_RESULT, msg);
|
|
return;
|
|
}
|
|
|
|
// Free identity in the process of being generated?
|
|
if (request_info_[key_type].gen_in_progress_counts_ ==
|
|
request_info_[key_type].request_observers_.size()) {
|
|
// No need to do anything, the free identity will be returned to the
|
|
// observer in a MSG_GENERATE_IDENTITY_RESULT.
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Enqueue/Post a worker task to do the generation.
|
|
++request_info_[key_type].gen_in_progress_counts_;
|
|
WorkerTask* task = new WorkerTask(this, key_type); // Post 1 task/request.
|
|
// The WorkerTask is owned by the message data to make sure it will not be
|
|
// leaked even if the task does not get run.
|
|
WorkerTaskMessageData* msg = new WorkerTaskMessageData(task);
|
|
worker_thread_->Post(task, MSG_GENERATE_IDENTITY, msg);
|
|
}
|
|
|
|
void DtlsIdentityStoreImpl::OnIdentityGenerated(
|
|
rtc::KeyType key_type,
|
|
std::unique_ptr<rtc::SSLIdentity> identity) {
|
|
RTC_DCHECK(signaling_thread_->IsCurrent());
|
|
|
|
RTC_DCHECK(request_info_[key_type].gen_in_progress_counts_);
|
|
--request_info_[key_type].gen_in_progress_counts_;
|
|
|
|
rtc::scoped_refptr<webrtc::DtlsIdentityRequestObserver> observer;
|
|
if (!request_info_[key_type].request_observers_.empty()) {
|
|
observer = request_info_[key_type].request_observers_.front();
|
|
request_info_[key_type].request_observers_.pop();
|
|
}
|
|
|
|
if (observer.get() == nullptr) {
|
|
// No observer - store result in |free_identities_|.
|
|
RTC_DCHECK(!request_info_[key_type].free_identity_.get());
|
|
request_info_[key_type].free_identity_.swap(identity);
|
|
if (request_info_[key_type].free_identity_.get())
|
|
LOG(LS_VERBOSE) << "A free DTLS identity was saved.";
|
|
else
|
|
LOG(LS_WARNING) << "Failed to generate DTLS identity (preemptively).";
|
|
} else {
|
|
// Return the result to the observer.
|
|
if (identity.get()) {
|
|
LOG(LS_VERBOSE) << "A DTLS identity is returned to an observer.";
|
|
observer->OnSuccess(std::move(identity));
|
|
} else {
|
|
LOG(LS_WARNING) << "Failed to generate DTLS identity.";
|
|
observer->OnFailure(0);
|
|
}
|
|
|
|
// Preemptively generate another identity of the same type?
|
|
if (worker_thread_ != signaling_thread_ && // Only do in background thread.
|
|
key_type == rtc::KT_RSA && // Only necessary for RSA.
|
|
!request_info_[key_type].free_identity_.get() &&
|
|
request_info_[key_type].request_observers_.size() ==
|
|
request_info_[key_type].gen_in_progress_counts_) {
|
|
GenerateIdentity(key_type, nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
RTCCertificateGeneratorStoreWrapper::RTCCertificateGeneratorStoreWrapper(
|
|
std::unique_ptr<DtlsIdentityStoreInterface> store)
|
|
: store_(std::move(store)) {
|
|
RTC_DCHECK(store_);
|
|
}
|
|
|
|
void RTCCertificateGeneratorStoreWrapper::GenerateCertificateAsync(
|
|
const rtc::KeyParams& key_params,
|
|
const rtc::Optional<uint64_t>& expires_ms,
|
|
const rtc::scoped_refptr<rtc::RTCCertificateGeneratorCallback>& callback) {
|
|
store_->RequestIdentity(
|
|
key_params,
|
|
expires_ms,
|
|
new rtc::RefCountedObject<RTCCertificateStoreCallbackObserver>(callback));
|
|
}
|
|
|
|
} // namespace webrtc
|