博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
mediaSoup源码分析-dtls操作
阅读量:3561 次
发布时间:2019-05-20

本文共 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/

你可能感兴趣的文章
代码注入
查看>>
off-by-one
查看>>
ctf-pwn的一些小技巧
查看>>
POJ 1915 Knight Moves
查看>>
Git 撤销修改
查看>>
Git 删除文件
查看>>
Git与远程仓库关联以及关联错误解决方法
查看>>
[HDU] 平方和与立方和
查看>>
[HDU 2096] 小明A+B
查看>>
[HDU 2520] 我是菜鸟,我怕谁(不一样的for循环)
查看>>
[HDU 1215] 七夕节(求因子,不超时)
查看>>
[POJ 1915] Knight Moves
查看>>
Memcache技术精华
查看>>
Redis详解入门篇
查看>>
php开启redis扩展包与redis安装
查看>>
php使用openssl来实现非对称加密
查看>>
pdo如何防止 sql注入
查看>>
myisam和innodb的区别
查看>>
MySQL建表规范与注意事项(个人精华)
查看>>
JDK8接口的新特性
查看>>