Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. | 2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
| 5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
| 6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| 11 #include "webrtc/api/dtlsidentitystore.h" | 11 #include "webrtc/api/dtlsidentitystore.h" |
| 12 | 12 |
| 13 #include <utility> | |
| 14 | |
| 15 #include "webrtc/api/webrtcsessiondescriptionfactory.h" | |
| 16 #include "webrtc/base/logging.h" | |
| 17 | |
| 18 using webrtc::DtlsIdentityRequestObserver; | |
| 19 | |
| 20 namespace webrtc { | 13 namespace webrtc { |
| 21 | 14 |
| 22 // Passed to SSLIdentity::Generate, "WebRTC". Used for the certificates' | 15 // Passed to SSLIdentity::Generate, "WebRTC". Used for the certificates' |
| 23 // subject and issuer name. | 16 // subject and issuer name. |
| 24 const char kIdentityName[] = "WebRTC"; | 17 const char kIdentityName[] = "WebRTC"; |
|
tommi
2016/06/01 12:36:31
can we move this constant to where it is used and
hbos
2016/06/01 13:41:48
Yes, but can I do that in a separate CL?
Turns ou
| |
| 25 | 18 |
| 26 namespace { | |
| 27 | |
| 28 enum { | |
| 29 MSG_DESTROY, | |
| 30 MSG_GENERATE_IDENTITY, | |
| 31 MSG_GENERATE_IDENTITY_RESULT | |
| 32 }; | |
| 33 | |
| 34 // A |DtlsIdentityRequestObserver| that informs an | |
| 35 // |RTCCertificateGeneratorCallback| of the result of an identity request. On | |
| 36 // success, a certificate is created using the identity before passing it to | |
| 37 // the callback. | |
| 38 class RTCCertificateStoreCallbackObserver | |
| 39 : public webrtc::DtlsIdentityRequestObserver { | |
| 40 public: | |
| 41 RTCCertificateStoreCallbackObserver( | |
| 42 const rtc::scoped_refptr<rtc::RTCCertificateGeneratorCallback>& callback) | |
| 43 : callback_(callback) {} | |
| 44 | |
| 45 private: | |
| 46 void OnFailure(int error) override { | |
| 47 LOG(LS_WARNING) << "DtlsIdentityRequestObserver failure code: " << error; | |
| 48 Callback(nullptr); | |
| 49 } | |
| 50 void OnSuccess(const std::string& der_cert, | |
| 51 const std::string& der_private_key) override { | |
| 52 std::string pem_cert = rtc::SSLIdentity::DerToPem( | |
| 53 rtc::kPemTypeCertificate, | |
| 54 reinterpret_cast<const unsigned char*>(der_cert.data()), | |
| 55 der_cert.length()); | |
| 56 std::string pem_key = rtc::SSLIdentity::DerToPem( | |
| 57 rtc::kPemTypeRsaPrivateKey, | |
| 58 reinterpret_cast<const unsigned char*>(der_private_key.data()), | |
| 59 der_private_key.length()); | |
| 60 std::unique_ptr<rtc::SSLIdentity> identity( | |
| 61 rtc::SSLIdentity::FromPEMStrings(pem_key, pem_cert)); | |
| 62 OnSuccess(std::move(identity)); | |
| 63 } | |
| 64 void OnSuccess(std::unique_ptr<rtc::SSLIdentity> identity) override { | |
| 65 Callback(rtc::RTCCertificate::Create(std::move(identity))); | |
| 66 } | |
| 67 | |
| 68 void Callback(rtc::scoped_refptr<rtc::RTCCertificate> certificate) { | |
| 69 if (certificate) | |
| 70 callback_->OnSuccess(certificate); | |
| 71 else | |
| 72 callback_->OnFailure(); | |
| 73 } | |
| 74 | |
| 75 rtc::scoped_refptr<rtc::RTCCertificateGeneratorCallback> callback_; | |
| 76 }; | |
| 77 | |
| 78 } // namespace | |
| 79 | |
| 80 // This class runs on the worker thread to generate the identity. It's necessary | |
| 81 // to separate this class from DtlsIdentityStore so that it can live on the | |
| 82 // worker thread after DtlsIdentityStore is destroyed. | |
| 83 class DtlsIdentityStoreImpl::WorkerTask : public sigslot::has_slots<>, | |
| 84 public rtc::MessageHandler { | |
| 85 public: | |
| 86 WorkerTask(DtlsIdentityStoreImpl* store, rtc::KeyType key_type) | |
| 87 : signaling_thread_(rtc::Thread::Current()), | |
| 88 store_(store), | |
| 89 key_type_(key_type) { | |
| 90 store_->SignalDestroyed.connect(this, &WorkerTask::OnStoreDestroyed); | |
| 91 } | |
| 92 | |
| 93 virtual ~WorkerTask() { RTC_DCHECK(signaling_thread_->IsCurrent()); } | |
| 94 | |
| 95 private: | |
| 96 void GenerateIdentity_w() { | |
| 97 LOG(LS_INFO) << "Generating identity, using keytype " << key_type_; | |
| 98 std::unique_ptr<rtc::SSLIdentity> identity( | |
| 99 rtc::SSLIdentity::Generate(kIdentityName, key_type_)); | |
| 100 | |
| 101 // Posting to |this| avoids touching |store_| on threads other than | |
| 102 // |signaling_thread_| and thus avoids having to use locks. | |
| 103 IdentityResultMessageData* msg = new IdentityResultMessageData( | |
| 104 new IdentityResult(key_type_, std::move(identity))); | |
| 105 signaling_thread_->Post(this, MSG_GENERATE_IDENTITY_RESULT, msg); | |
| 106 } | |
| 107 | |
| 108 void OnMessage(rtc::Message* msg) override { | |
| 109 switch (msg->message_id) { | |
| 110 case MSG_GENERATE_IDENTITY: | |
| 111 // This message always runs on the worker thread. | |
| 112 GenerateIdentity_w(); | |
| 113 | |
| 114 // Must delete |this|, owned by msg->pdata, on the signaling thread to | |
| 115 // avoid races on disconnecting the signal. | |
| 116 signaling_thread_->Post(this, MSG_DESTROY, msg->pdata); | |
| 117 break; | |
| 118 case MSG_GENERATE_IDENTITY_RESULT: | |
| 119 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
| 120 { | |
| 121 std::unique_ptr<IdentityResultMessageData> pdata( | |
| 122 static_cast<IdentityResultMessageData*>(msg->pdata)); | |
| 123 if (store_) { | |
| 124 store_->OnIdentityGenerated(pdata->data()->key_type_, | |
| 125 std::move(pdata->data()->identity_)); | |
| 126 } | |
| 127 } | |
| 128 break; | |
| 129 case MSG_DESTROY: | |
| 130 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
| 131 delete msg->pdata; | |
| 132 // |this| has now been deleted. Don't touch member variables. | |
| 133 break; | |
| 134 default: | |
| 135 RTC_CHECK(false) << "Unexpected message type"; | |
| 136 } | |
| 137 } | |
| 138 | |
| 139 void OnStoreDestroyed() { | |
| 140 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
| 141 store_ = nullptr; | |
| 142 } | |
| 143 | |
| 144 rtc::Thread* const signaling_thread_; | |
| 145 DtlsIdentityStoreImpl* store_; // Only touched on |signaling_thread_|. | |
| 146 const rtc::KeyType key_type_; | |
| 147 }; | |
| 148 | |
| 149 DtlsIdentityStoreImpl::DtlsIdentityStoreImpl(rtc::Thread* signaling_thread, | |
| 150 rtc::Thread* worker_thread) | |
| 151 : signaling_thread_(signaling_thread), | |
| 152 worker_thread_(worker_thread), | |
| 153 request_info_() { | |
| 154 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
| 155 } | |
| 156 | |
| 157 DtlsIdentityStoreImpl::~DtlsIdentityStoreImpl() { | |
| 158 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
| 159 SignalDestroyed(); | |
| 160 } | |
| 161 | |
| 162 void DtlsIdentityStoreImpl::RequestIdentity( | |
| 163 const rtc::KeyParams& key_params, | |
| 164 const rtc::Optional<uint64_t>& expires_ms, | |
| 165 const rtc::scoped_refptr<DtlsIdentityRequestObserver>& observer) { | |
| 166 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
| 167 RTC_DCHECK(observer); | |
| 168 | |
| 169 // Dropping parameterization and |expires_ms|. | |
| 170 // TODO(hbos,torbjorng): Use parameterizaton/expiration. webrtc:5092. | |
| 171 GenerateIdentity(key_params.type(), observer); | |
| 172 } | |
| 173 | |
| 174 void DtlsIdentityStoreImpl::OnMessage(rtc::Message* msg) { | |
| 175 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
| 176 switch (msg->message_id) { | |
| 177 case MSG_GENERATE_IDENTITY_RESULT: { | |
| 178 std::unique_ptr<IdentityResultMessageData> pdata( | |
| 179 static_cast<IdentityResultMessageData*>(msg->pdata)); | |
| 180 OnIdentityGenerated(pdata->data()->key_type_, | |
| 181 std::move(pdata->data()->identity_)); | |
| 182 break; | |
| 183 } | |
| 184 } | |
| 185 } | |
| 186 | |
| 187 bool DtlsIdentityStoreImpl::HasFreeIdentityForTesting( | |
| 188 rtc::KeyType key_type) const { | |
| 189 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
| 190 return request_info_[key_type].free_identity_.get() != nullptr; | |
| 191 } | |
| 192 | |
| 193 void DtlsIdentityStoreImpl::GenerateIdentity( | |
| 194 rtc::KeyType key_type, | |
| 195 const rtc::scoped_refptr<DtlsIdentityRequestObserver>& observer) { | |
| 196 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
| 197 | |
| 198 // Enqueue observer to be informed when generation of |key_type| is completed. | |
| 199 if (observer.get()) { | |
| 200 request_info_[key_type].request_observers_.push(observer); | |
| 201 | |
| 202 // Already have a free identity generated? | |
| 203 if (request_info_[key_type].free_identity_.get()) { | |
| 204 // Return identity async - post even though we are on |signaling_thread_|. | |
| 205 LOG(LS_VERBOSE) << "Using a free DTLS identity."; | |
| 206 ++request_info_[key_type].gen_in_progress_counts_; | |
| 207 IdentityResultMessageData* msg = | |
| 208 new IdentityResultMessageData(new IdentityResult( | |
| 209 key_type, std::move(request_info_[key_type].free_identity_))); | |
| 210 signaling_thread_->Post(this, MSG_GENERATE_IDENTITY_RESULT, msg); | |
| 211 return; | |
| 212 } | |
| 213 | |
| 214 // Free identity in the process of being generated? | |
| 215 if (request_info_[key_type].gen_in_progress_counts_ == | |
| 216 request_info_[key_type].request_observers_.size()) { | |
| 217 // No need to do anything, the free identity will be returned to the | |
| 218 // observer in a MSG_GENERATE_IDENTITY_RESULT. | |
| 219 return; | |
| 220 } | |
| 221 } | |
| 222 | |
| 223 // Enqueue/Post a worker task to do the generation. | |
| 224 ++request_info_[key_type].gen_in_progress_counts_; | |
| 225 WorkerTask* task = new WorkerTask(this, key_type); // Post 1 task/request. | |
| 226 // The WorkerTask is owned by the message data to make sure it will not be | |
| 227 // leaked even if the task does not get run. | |
| 228 WorkerTaskMessageData* msg = new WorkerTaskMessageData(task); | |
| 229 worker_thread_->Post(task, MSG_GENERATE_IDENTITY, msg); | |
| 230 } | |
| 231 | |
| 232 void DtlsIdentityStoreImpl::OnIdentityGenerated( | |
| 233 rtc::KeyType key_type, | |
| 234 std::unique_ptr<rtc::SSLIdentity> identity) { | |
| 235 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
| 236 | |
| 237 RTC_DCHECK(request_info_[key_type].gen_in_progress_counts_); | |
| 238 --request_info_[key_type].gen_in_progress_counts_; | |
| 239 | |
| 240 rtc::scoped_refptr<webrtc::DtlsIdentityRequestObserver> observer; | |
| 241 if (!request_info_[key_type].request_observers_.empty()) { | |
| 242 observer = request_info_[key_type].request_observers_.front(); | |
| 243 request_info_[key_type].request_observers_.pop(); | |
| 244 } | |
| 245 | |
| 246 if (observer.get() == nullptr) { | |
| 247 // No observer - store result in |free_identities_|. | |
| 248 RTC_DCHECK(!request_info_[key_type].free_identity_.get()); | |
| 249 request_info_[key_type].free_identity_.swap(identity); | |
| 250 if (request_info_[key_type].free_identity_.get()) | |
| 251 LOG(LS_VERBOSE) << "A free DTLS identity was saved."; | |
| 252 else | |
| 253 LOG(LS_WARNING) << "Failed to generate DTLS identity (preemptively)."; | |
| 254 } else { | |
| 255 // Return the result to the observer. | |
| 256 if (identity.get()) { | |
| 257 LOG(LS_VERBOSE) << "A DTLS identity is returned to an observer."; | |
| 258 observer->OnSuccess(std::move(identity)); | |
| 259 } else { | |
| 260 LOG(LS_WARNING) << "Failed to generate DTLS identity."; | |
| 261 observer->OnFailure(0); | |
| 262 } | |
| 263 | |
| 264 // Preemptively generate another identity of the same type? | |
| 265 if (worker_thread_ != signaling_thread_ && // Only do in background thread. | |
| 266 key_type == rtc::KT_RSA && // Only necessary for RSA. | |
| 267 !request_info_[key_type].free_identity_.get() && | |
| 268 request_info_[key_type].request_observers_.size() == | |
| 269 request_info_[key_type].gen_in_progress_counts_) { | |
| 270 GenerateIdentity(key_type, nullptr); | |
| 271 } | |
| 272 } | |
| 273 } | |
| 274 | |
| 275 RTCCertificateGeneratorStoreWrapper::RTCCertificateGeneratorStoreWrapper( | |
| 276 std::unique_ptr<DtlsIdentityStoreInterface> store) | |
| 277 : store_(std::move(store)) { | |
| 278 RTC_DCHECK(store_); | |
| 279 } | |
| 280 | |
| 281 void RTCCertificateGeneratorStoreWrapper::GenerateCertificateAsync( | |
| 282 const rtc::KeyParams& key_params, | |
| 283 const rtc::Optional<uint64_t>& expires_ms, | |
| 284 const rtc::scoped_refptr<rtc::RTCCertificateGeneratorCallback>& callback) { | |
| 285 store_->RequestIdentity( | |
| 286 key_params, | |
| 287 expires_ms, | |
| 288 new rtc::RefCountedObject<RTCCertificateStoreCallbackObserver>(callback)); | |
| 289 } | |
| 290 | |
| 291 } // namespace webrtc | 19 } // namespace webrtc |
| OLD | NEW |