本文共 16217 字,大约阅读时间需要 54 分钟。
dtls主要用来交换srtp的
#在ICE完成后,开始dtls过程
void WebRtcTransport::MayRunDtlsTransport() { MS_TRACE(); // Do nothing if we have the same local DTLS role as the DTLS transport. // NOTE: local role in DTLS transport can be NONE, but not ours. if (this->dtlsTransport->GetLocalRole() == this->dtlsRole) return; // Check our local DTLS role. switch (this->dtlsRole) { // If still 'auto' then transition to 'server' if ICE is 'connected' or // 'completed'. case RTC::DtlsTransport::Role::AUTO: { if ( this->iceServer->GetState() == RTC::IceServer::IceState::CONNECTED || this->iceServer->GetState() == RTC::IceServer::IceState::COMPLETED) { MS_DEBUG_TAG( dtls, "transition from DTLS local role 'auto' to 'server' and running DTLS transport"); this->dtlsRole = RTC::DtlsTransport::Role::SERVER; this->dtlsTransport->Run(RTC::DtlsTransport::Role::SERVER); } break; } // 'client' is only set if a 'connect' request was previously called with // remote DTLS role 'server'. // // If 'client' then wait for ICE to be 'completed' (got USE-CANDIDATE). // // NOTE: This is the theory, however let's be more flexible as told here: // https://bugs.chromium.org/p/webrtc/issues/detail?id=3661 case RTC::DtlsTransport::Role::CLIENT: { if ( this->iceServer->GetState() == RTC::IceServer::IceState::CONNECTED || this->iceServer->GetState() == RTC::IceServer::IceState::COMPLETED) { MS_DEBUG_TAG(dtls, "running DTLS transport in local role 'client'"); this->dtlsTransport->Run(RTC::DtlsTransport::Role::CLIENT); } break; } // If 'server' then run the DTLS transport if ICE is 'connected' (not yet // USE-CANDIDATE) or 'completed'. case RTC::DtlsTransport::Role::SERVER: { if ( this->iceServer->GetState() == RTC::IceServer::IceState::CONNECTED || this->iceServer->GetState() == RTC::IceServer::IceState::COMPLETED) { MS_DEBUG_TAG(dtls, "running DTLS transport in local role 'server'"); this->dtlsTransport->Run(RTC::DtlsTransport::Role::SERVER); } break; } case RTC::DtlsTransport::Role::NONE: { MS_ABORT("local DTLS role not set"); } } }
#dtls过程
void DtlsTransport::Run(Role localRole) { MS_TRACE(); MS_ASSERT( localRole == Role::CLIENT || localRole == Role::SERVER, "local DTLS role must be 'client' or 'server'"); Role previousLocalRole = this->localRole; if (localRole == previousLocalRole) { MS_ERROR("same local DTLS role provided, doing nothing"); return; } // If the previous local DTLS role was 'client' or 'server' do reset. if (previousLocalRole == Role::CLIENT || previousLocalRole == Role::SERVER) { MS_DEBUG_TAG(dtls, "resetting DTLS due to local role change"); Reset(); } // Update local role. this->localRole = localRole; // Set state and notify the listener. this->state = DtlsState::CONNECTING; this->listener->OnDtlsTransportConnecting(this); switch (this->localRole) { case Role::CLIENT: { MS_DEBUG_TAG(dtls, "running [role:client]"); //设置主动连接 SSL_set_connect_state(this->ssl); //开始握手 SSL_do_handshake(this->ssl); //发送数据包 SendPendingOutgoingDtlsData(); //设置超时时间 SetTimeout(); break; } case Role::SERVER: { MS_DEBUG_TAG(dtls, "running [role:server]"); //等待握手 SSL_set_accept_state(this->ssl); //握手 SSL_do_handshake(this->ssl); break; } default: { MS_ABORT("invalid local DTLS role"); } } }
#dtls包处理
void DtlsTransport::ProcessDtlsData(const uint8_t* data, size_t len) { MS_TRACE(); int written; int read; if (!IsRunning()) { MS_ERROR("cannot process data while not running"); return; } // Write the received DTLS data into the sslBioFromNetwork. written = BIO_write(this->sslBioFromNetwork, (const void*)data, static_cast (len)); if (written != static_cast (len)) { MS_WARN_TAG( dtls, "OpenSSL BIO_write() wrote less (%zu bytes) than given data (%zu bytes)", static_cast(written), len); } // Must call SSL_read() to process received DTLS data. read = SSL_read(this->ssl, (void*)DtlsTransport::sslReadBuffer, SslReadBufferSize); // Send data if it's ready. SendPendingOutgoingDtlsData(); // Check SSL status and return if it is bad/closed. if (!CheckStatus(read)) return; // Set/update the DTLS timeout. if (!SetTimeout()) return; // Application data received. Notify to the listener. if (read > 0) { // It is allowed to receive DTLS data even before validating remote fingerprint. if (!this->handshakeDone) { MS_WARN_TAG(dtls, "ignoring application data received while DTLS handshake not done"); return; } // Notify the listener. this->listener->OnDtlsTransportApplicationDataReceived( this, (uint8_t*)DtlsTransport::sslReadBuffer, static_cast (read)); } }
#什么时候handshake完毕?
inline void DtlsTransport::OnSslInfo(int where, int ret) { MS_TRACE(); int w = where & -SSL_ST_MASK; const char* role; if ((w & SSL_ST_CONNECT) != 0) role = "client"; else if ((w & SSL_ST_ACCEPT) != 0) role = "server"; else role = "undefined"; if ((where & SSL_CB_LOOP) != 0) { MS_DEBUG_TAG(dtls, "[role:%s, action:'%s']", role, SSL_state_string_long(this->ssl)); } else if ((where & SSL_CB_ALERT) != 0) { const char* alertType; switch (*SSL_alert_type_string(ret)) { case 'W': alertType = "warning"; break; case 'F': alertType = "fatal"; break; default: alertType = "undefined"; } if ((where & SSL_CB_READ) != 0) { MS_WARN_TAG(dtls, "received DTLS %s alert: %s", alertType, SSL_alert_desc_string_long(ret)); } else if ((where & SSL_CB_WRITE) != 0) { MS_DEBUG_TAG(dtls, "sending DTLS %s alert: %s", alertType, SSL_alert_desc_string_long(ret)); } else { MS_DEBUG_TAG(dtls, "DTLS %s alert: %s", alertType, SSL_alert_desc_string_long(ret)); } } else if ((where & SSL_CB_EXIT) != 0) { if (ret == 0) MS_DEBUG_TAG(dtls, "[role:%s, failed:'%s']", role, SSL_state_string_long(this->ssl)); else if (ret < 0) MS_DEBUG_TAG(dtls, "role: %s, waiting:'%s']", role, SSL_state_string_long(this->ssl)); } else if ((where & SSL_CB_HANDSHAKE_START) != 0) { MS_DEBUG_TAG(dtls, "DTLS handshake start"); } else if ((where & SSL_CB_HANDSHAKE_DONE) != 0) { MS_DEBUG_TAG(dtls, "DTLS handshake done"); this->handshakeDoneNow = true; } // NOTE: checking SSL_get_shutdown(this->ssl) & SSL_RECEIVED_SHUTDOWN here upon // receipt of a close alert does not work (the flag is set after this callback). }
可以看到是一个回调,在这里设置的
// Set SSL info callback. SSL_CTX_set_info_callback(DtlsTransport::sslCtx, onSslInfo);
#dtls如何初始化?
void DtlsTransport::ClassInit() { MS_TRACE(); // Generate a X509 certificate and private key (unless PEM files are provided). if ( Settings::configuration.dtlsCertificateFile.empty() || Settings::configuration.dtlsPrivateKeyFile.empty()) { GenerateCertificateAndPrivateKey(); } else { ReadCertificateAndPrivateKeyFromFiles(); } // Create a global SSL_CTX. CreateSslCtx(); // Generate certificate fingerprints. GenerateFingerprints(); }
读取证书和私钥
void DtlsTransport::ReadCertificateAndPrivateKeyFromFiles() { MS_TRACE(); FILE* file{ nullptr }; file = fopen(Settings::configuration.dtlsCertificateFile.c_str(), "r"); if (file == nullptr) { MS_ERROR("error reading DTLS certificate file: %s", std::strerror(errno)); goto error; } DtlsTransport::certificate = PEM_read_X509(file, nullptr, nullptr, nullptr); if (DtlsTransport::certificate == nullptr) { LOG_OPENSSL_ERROR("PEM_read_X509() failed"); goto error; } fclose(file); file = fopen(Settings::configuration.dtlsPrivateKeyFile.c_str(), "r"); if (file == nullptr) { MS_ERROR("error reading DTLS private key file: %s", std::strerror(errno)); goto error; } DtlsTransport::privateKey = PEM_read_PrivateKey(file, nullptr, nullptr, nullptr); if (DtlsTransport::privateKey == nullptr) { LOG_OPENSSL_ERROR("PEM_read_PrivateKey() failed"); goto error; } fclose(file); return; error: MS_THROW_ERROR("error reading DTLS certificate and private key PEM files"); }
创建ssl_ctx和指纹
void DtlsTransport::CreateSslCtx() { MS_TRACE(); std::string dtlsSrtpProfiles; EC_KEY* ecdh{ nullptr }; int ret;/* Set the global DTLS context. */// Both DTLS 1.0 and 1.2 (requires OpenSSL >= 1.1.0).#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) DtlsTransport::sslCtx = SSL_CTX_new(DTLS_method());// Just DTLS 1.0 (requires OpenSSL >= 1.0.1).#elif (OPENSSL_VERSION_NUMBER >= 0x10001000L) DtlsTransport::sslCtx = SSL_CTX_new(DTLSv1_method());#else#error "too old OpenSSL version"#endif if (DtlsTransport::sslCtx == nullptr) { LOG_OPENSSL_ERROR("SSL_CTX_new() failed"); goto error; } ret = SSL_CTX_use_certificate(DtlsTransport::sslCtx, DtlsTransport::certificate); if (ret == 0) { LOG_OPENSSL_ERROR("SSL_CTX_use_certificate() failed"); goto error; } ret = SSL_CTX_use_PrivateKey(DtlsTransport::sslCtx, DtlsTransport::privateKey); if (ret == 0) { LOG_OPENSSL_ERROR("SSL_CTX_use_PrivateKey() failed"); goto error; } ret = SSL_CTX_check_private_key(DtlsTransport::sslCtx); if (ret == 0) { LOG_OPENSSL_ERROR("SSL_CTX_check_private_key() failed"); goto error; } // Set options. SSL_CTX_set_options( DtlsTransport::sslCtx, SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_TICKET | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_QUERY_MTU); // Don't use sessions cache. SSL_CTX_set_session_cache_mode(DtlsTransport::sslCtx, SSL_SESS_CACHE_OFF); // Read always as much into the buffer as possible. // NOTE: This is the default for DTLS, but a bug in non latest OpenSSL // versions makes this call required. SSL_CTX_set_read_ahead(DtlsTransport::sslCtx, 1); SSL_CTX_set_verify_depth(DtlsTransport::sslCtx, 4); // Require certificate from peer. SSL_CTX_set_verify( DtlsTransport::sslCtx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, onSslCertificateVerify); // Set SSL info callback. SSL_CTX_set_info_callback(DtlsTransport::sslCtx, onSslInfo); // Set ciphers. ret = SSL_CTX_set_cipher_list( DtlsTransport::sslCtx, "ALL:!ADH:!LOW:!EXP:!MD5:!aNULL:!eNULL:@STRENGTH"); if (ret == 0) { LOG_OPENSSL_ERROR("SSL_CTX_set_cipher_list() failed"); goto error; }// Enable ECDH ciphers.// DOC: http://en.wikibooks.org/wiki/OpenSSL/Diffie-Hellman_parameters// NOTE: https://code.google.com/p/chromium/issues/detail?id=406458// NOTE: https://bugs.ruby-lang.org/issues/12324//// Nothing to be done in OpenSSL >= 1.1.0.#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)// For OpenSSL >= 1.0.2.#elif (OPENSSL_VERSION_NUMBER >= 0x10002000L) SSL_CTX_set_ecdh_auto(DtlsTransport::sslCtx, 1);// Older versions.#else ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); if (!ecdh) { LOG_OPENSSL_ERROR("EC_KEY_new_by_curve_name() failed"); goto error; } if (SSL_CTX_set_tmp_ecdh(DtlsTransport::sslCtx, ecdh) != 1) { LOG_OPENSSL_ERROR("SSL_CTX_set_tmp_ecdh() failed"); goto error; } EC_KEY_free(ecdh); ecdh = nullptr;#endif // Set the "use_srtp" DTLS extension. for (auto it = DtlsTransport::srtpProfiles.begin(); it != DtlsTransport::srtpProfiles.end(); ++it) { if (it != DtlsTransport::srtpProfiles.begin()) dtlsSrtpProfiles += ":"; SrtpProfileMapEntry* profileEntry = std::addressof(*it); dtlsSrtpProfiles += profileEntry->name; } MS_DEBUG_2TAGS(dtls, srtp, "setting SRTP profiles for DTLS: %s", dtlsSrtpProfiles.c_str()); // NOTE: This function returns 0 on success. ret = SSL_CTX_set_tlsext_use_srtp(DtlsTransport::sslCtx, dtlsSrtpProfiles.c_str()); if (ret != 0) { MS_ERROR("SSL_CTX_set_tlsext_use_srtp() failed when entering '%s'", dtlsSrtpProfiles.c_str()); LOG_OPENSSL_ERROR("SSL_CTX_set_tlsext_use_srtp() failed"); goto error; } return; error: if (DtlsTransport::sslCtx != nullptr) { SSL_CTX_free(DtlsTransport::sslCtx); DtlsTransport::sslCtx = nullptr; } if (ecdh != nullptr) EC_KEY_free(ecdh); MS_THROW_ERROR("SSL context creation failed"); } void DtlsTransport::GenerateFingerprints() { MS_TRACE(); for (auto& kv : DtlsTransport::string2FingerprintAlgorithm) { const std::string& algorithmString = kv.first; FingerprintAlgorithm algorithm = kv.second; uint8_t binaryFingerprint[EVP_MAX_MD_SIZE]; unsigned int size{ 0 }; char hexFingerprint[(EVP_MAX_MD_SIZE * 3) + 1]; const EVP_MD* hashFunction; int ret; switch (algorithm) { case FingerprintAlgorithm::SHA1: hashFunction = EVP_sha1(); break; case FingerprintAlgorithm::SHA224: hashFunction = EVP_sha224(); break; case FingerprintAlgorithm::SHA256: hashFunction = EVP_sha256(); break; case FingerprintAlgorithm::SHA384: hashFunction = EVP_sha384(); break; case FingerprintAlgorithm::SHA512: hashFunction = EVP_sha512(); break; default: MS_THROW_ERROR("unknown algorithm"); } ret = X509_digest(DtlsTransport::certificate, hashFunction, binaryFingerprint, &size); if (ret == 0) { MS_ERROR("X509_digest() failed"); MS_THROW_ERROR("Fingerprints generation failed"); } // Convert to hexadecimal format in uppercase with colons. for (unsigned int i{ 0 }; i < size; ++i) { std::sprintf(hexFingerprint + (i * 3), "%.2X:", binaryFingerprint[i]); } hexFingerprint[(size * 3) - 1] = '\0'; MS_DEBUG_TAG(dtls, "%-7s fingerprint: %s", algorithmString.c_str(), hexFingerprint); // Store it in the vector. DtlsTransport::Fingerprint fingerprint; fingerprint.algorithm = DtlsTransport::GetFingerprintAlgorithm(algorithmString); fingerprint.value = hexFingerprint; DtlsTransport::localFingerprints.push_back(fingerprint); } }
#最后dtls handshake完了后交换的数据如下
inline void DtlsTransport::ExtractSrtpKeys(RTC::SrtpSession::Profile srtpProfile) { MS_TRACE(); size_t srtpKeyLength{ 0 }; size_t srtpSaltLength{ 0 }; size_t srtpMasterLength{ 0 }; switch (srtpProfile) { case RTC::SrtpSession::Profile::AES_CM_128_HMAC_SHA1_80: case RTC::SrtpSession::Profile::AES_CM_128_HMAC_SHA1_32: { srtpKeyLength = SrtpMasterKeyLength; srtpSaltLength = SrtpMasterSaltLength; srtpMasterLength = SrtpMasterLength; break; } case RTC::SrtpSession::Profile::AEAD_AES_256_GCM: { srtpKeyLength = SrtpAesGcm256MasterKeyLength; srtpSaltLength = SrtpAesGcm256MasterSaltLength; srtpMasterLength = SrtpAesGcm256MasterLength; break; } case RTC::SrtpSession::Profile::AEAD_AES_128_GCM: { srtpKeyLength = SrtpAesGcm128MasterKeyLength; srtpSaltLength = SrtpAesGcm128MasterSaltLength; srtpMasterLength = SrtpAesGcm128MasterLength; break; } default: { MS_ABORT("unknown SRTP profile"); } } uint8_t srtpMaterial[srtpMasterLength * 2]; uint8_t* srtpLocalKey; uint8_t* srtpLocalSalt; uint8_t* srtpRemoteKey; uint8_t* srtpRemoteSalt; uint8_t srtpLocalMasterKey[srtpMasterLength]; uint8_t srtpRemoteMasterKey[srtpMasterLength]; int ret; ret = SSL_export_keying_material( this->ssl, srtpMaterial, srtpMasterLength * 2, "EXTRACTOR-dtls_srtp", 19, nullptr, 0, 0); MS_ASSERT(ret != 0, "SSL_export_keying_material() failed"); switch (this->localRole) { case Role::SERVER: { srtpRemoteKey = srtpMaterial; srtpLocalKey = srtpRemoteKey + srtpKeyLength; srtpRemoteSalt = srtpLocalKey + srtpKeyLength; srtpLocalSalt = srtpRemoteSalt + srtpSaltLength; break; } case Role::CLIENT: { srtpLocalKey = srtpMaterial; srtpRemoteKey = srtpLocalKey + srtpKeyLength; srtpLocalSalt = srtpRemoteKey + srtpKeyLength; srtpRemoteSalt = srtpLocalSalt + srtpSaltLength; break; } default: { MS_ABORT("no DTLS role set"); } } // Create the SRTP local master key. std::memcpy(srtpLocalMasterKey, srtpLocalKey, srtpKeyLength); std::memcpy(srtpLocalMasterKey + srtpKeyLength, srtpLocalSalt, srtpSaltLength); // Create the SRTP remote master key. std::memcpy(srtpRemoteMasterKey, srtpRemoteKey, srtpKeyLength); std::memcpy(srtpRemoteMasterKey + srtpKeyLength, srtpRemoteSalt, srtpSaltLength); // Set state and notify the listener. this->state = DtlsState::CONNECTED; this->listener->OnDtlsTransportConnected( this, srtpProfile, srtpLocalMasterKey, srtpMasterLength, srtpRemoteMasterKey, srtpMasterLength, this->remoteCert); }
转载地址:http://hiprj.baihongyu.com/