DtlsTransport.cpp 36.2 KB
Newer Older
xiongziliang committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/**
ISC License

Copyright © 2015, Iñaki Baz Castillo <ibc@aliax.net>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#define MS_CLASS "RTC::DtlsTransport"
20 21
// #define MS_LOG_DEV_LEVEL 3

ziyue committed
22
#include "DtlsTransport.hpp"
ziyue committed
23
#include "logger.h"
24 25 26 27 28
#include <openssl/asn1.h>
#include <openssl/bn.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
ziyue committed
29 30
#include <cstdio>  // std::sprintf(), std::fopen()
#include <cstring> // std::memcpy(), std::strcmp()
31
#include "Util/util.h"
ziyue committed
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

#define LOG_OPENSSL_ERROR(desc)                                                                    \
	do                                                                                               \
	{                                                                                                \
		if (ERR_peek_error() == 0)                                                                     \
			MS_ERROR("OpenSSL error [desc:'%s']", desc);                                                 \
		else                                                                                           \
		{                                                                                              \
			int64_t err;                                                                                 \
			while ((err = ERR_get_error()) != 0)                                                         \
			{                                                                                            \
				MS_ERROR("OpenSSL error [desc:'%s', error:'%s']", desc, ERR_error_string(err, nullptr));   \
			}                                                                                            \
			ERR_clear_error();                                                                           \
		}                                                                                              \
	} while (false)
48 49 50

/* Static methods for OpenSSL callbacks. */

ziyue committed
51 52 53
inline static int onSslCertificateVerify(int /*preverifyOk*/, X509_STORE_CTX* /*ctx*/)
{
	MS_TRACE();
54

ziyue committed
55 56
	// Always valid since DTLS certificates are self-signed.
	return 1;
57 58
}

ziyue committed
59 60 61 62 63 64 65 66
inline static unsigned int onSslDtlsTimer(SSL* /*ssl*/, unsigned int timerUs)
{
	if (timerUs == 0)
		return 100000;
	else if (timerUs >= 4000000)
		return 4000000;
	else
		return 2 * timerUs;
67 68
}

ziyue committed
69 70 71
namespace RTC
{
	/* Static. */
72

ziyue committed
73
	// clang-format off
74 75 76 77 78 79 80 81 82 83 84 85
	static constexpr int DtlsMtu{ 1350 };
	// AES-HMAC: http://tools.ietf.org/html/rfc3711
	static constexpr size_t SrtpMasterKeyLength{ 16 };
	static constexpr size_t SrtpMasterSaltLength{ 14 };
	static constexpr size_t SrtpMasterLength{ SrtpMasterKeyLength + SrtpMasterSaltLength };
	// AES-GCM: http://tools.ietf.org/html/rfc7714
	static constexpr size_t SrtpAesGcm256MasterKeyLength{ 32 };
	static constexpr size_t SrtpAesGcm256MasterSaltLength{ 12 };
	static constexpr size_t SrtpAesGcm256MasterLength{ SrtpAesGcm256MasterKeyLength + SrtpAesGcm256MasterSaltLength };
	static constexpr size_t SrtpAesGcm128MasterKeyLength{ 16 };
	static constexpr size_t SrtpAesGcm128MasterSaltLength{ 12 };
	static constexpr size_t SrtpAesGcm128MasterLength{ SrtpAesGcm128MasterKeyLength + SrtpAesGcm128MasterSaltLength };
ziyue committed
86
	// clang-format on
87

ziyue committed
88 89
	/* Class variables. */
	// clang-format off
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
	std::map<std::string, DtlsTransport::FingerprintAlgorithm> DtlsTransport::string2FingerprintAlgorithm =
	{
		{ "sha-1",   DtlsTransport::FingerprintAlgorithm::SHA1   },
		{ "sha-224", DtlsTransport::FingerprintAlgorithm::SHA224 },
		{ "sha-256", DtlsTransport::FingerprintAlgorithm::SHA256 },
		{ "sha-384", DtlsTransport::FingerprintAlgorithm::SHA384 },
		{ "sha-512", DtlsTransport::FingerprintAlgorithm::SHA512 }
	};
	std::map<DtlsTransport::FingerprintAlgorithm, std::string> DtlsTransport::fingerprintAlgorithm2String =
	{
		{ DtlsTransport::FingerprintAlgorithm::SHA1,   "sha-1"   },
		{ DtlsTransport::FingerprintAlgorithm::SHA224, "sha-224" },
		{ DtlsTransport::FingerprintAlgorithm::SHA256, "sha-256" },
		{ DtlsTransport::FingerprintAlgorithm::SHA384, "sha-384" },
		{ DtlsTransport::FingerprintAlgorithm::SHA512, "sha-512" }
	};
	std::map<std::string, DtlsTransport::Role> DtlsTransport::string2Role =
	{
		{ "auto",   DtlsTransport::Role::AUTO   },
		{ "client", DtlsTransport::Role::CLIENT },
		{ "server", DtlsTransport::Role::SERVER }
	};
	std::vector<DtlsTransport::SrtpCryptoSuiteMapEntry> DtlsTransport::srtpCryptoSuites =
	{
ziyue committed
114 115 116 117
		{ RTC::SrtpSession::CryptoSuite::AEAD_AES_256_GCM, "SRTP_AEAD_AES_256_GCM" },
		{ RTC::SrtpSession::CryptoSuite::AEAD_AES_128_GCM, "SRTP_AEAD_AES_128_GCM" },
		{ RTC::SrtpSession::CryptoSuite::AES_CM_128_HMAC_SHA1_80, "SRTP_AES128_CM_SHA1_80" },
		{ RTC::SrtpSession::CryptoSuite::AES_CM_128_HMAC_SHA1_32, "SRTP_AES128_CM_SHA1_32" }
118
	};
ziyue committed
119
	// clang-format on
120

121 122
	INSTANCE_IMP(DtlsTransport::DtlsEnvironment);

ziyue committed
123
	/* Class methods. */
124

125
    DtlsTransport::DtlsEnvironment::DtlsEnvironment()
ziyue committed
126 127
	{
		MS_TRACE();
128

ziyue committed
129 130 131 132 133 134 135 136 137 138 139
		// Generate a X509 certificate and private key (unless PEM files are provided).
		if (true /*
		  Settings::configuration.dtlsCertificateFile.empty() ||
		  Settings::configuration.dtlsPrivateKeyFile.empty()*/)
		{
			GenerateCertificateAndPrivateKey();
		}
		else
		{
			ReadCertificateAndPrivateKeyFromFiles();
		}
140

ziyue committed
141 142
		// Create a global SSL_CTX.
		CreateSslCtx();
143

ziyue committed
144 145 146
		// Generate certificate fingerprints.
		GenerateFingerprints();
	}
147

148
    DtlsTransport::DtlsEnvironment::~DtlsEnvironment()
ziyue committed
149 150
	{
		MS_TRACE();
151

152 153 154 155 156 157
		if (privateKey)
			EVP_PKEY_free(privateKey);
		if (certificate)
			X509_free(certificate);
		if (sslCtx)
			SSL_CTX_free(sslCtx);
ziyue committed
158
	}
159

160
	void DtlsTransport::DtlsEnvironment::GenerateCertificateAndPrivateKey()
ziyue committed
161 162
	{
		MS_TRACE();
163

ziyue committed
164 165 166 167
		int ret{ 0 };
		EC_KEY* ecKey{ nullptr };
		X509_NAME* certName{ nullptr };
		std::string subject =
168
		  std::string("mediasoup") + to_string(rand() % 999999 + 100000);
169

ziyue committed
170 171
		// Create key with curve.
		ecKey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
172

ziyue committed
173 174 175
		if (!ecKey)
		{
			LOG_OPENSSL_ERROR("EC_KEY_new_by_curve_name() failed");
176

ziyue committed
177 178
			goto error;
		}
179

ziyue committed
180
		EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
181

ziyue committed
182 183
		// NOTE: This can take some time.
		ret = EC_KEY_generate_key(ecKey);
184

ziyue committed
185 186 187
		if (ret == 0)
		{
			LOG_OPENSSL_ERROR("EC_KEY_generate_key() failed");
188

ziyue committed
189 190
			goto error;
		}
191

ziyue committed
192
		// Create a private key object.
193
		privateKey = EVP_PKEY_new();
194

195
		if (!privateKey)
ziyue committed
196 197
		{
			LOG_OPENSSL_ERROR("EVP_PKEY_new() failed");
198

ziyue committed
199 200
			goto error;
		}
201

ziyue committed
202
		// NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
203
		ret = EVP_PKEY_assign_EC_KEY(privateKey, ecKey);
204

ziyue committed
205 206 207
		if (ret == 0)
		{
			LOG_OPENSSL_ERROR("EVP_PKEY_assign_EC_KEY() failed");
208

ziyue committed
209 210
			goto error;
		}
211

ziyue committed
212 213
		// The EC key now belongs to the private key, so don't clean it up separately.
		ecKey = nullptr;
214

ziyue committed
215
		// Create the X509 certificate.
216
		certificate = X509_new();
217

218
		if (!certificate)
ziyue committed
219 220
		{
			LOG_OPENSSL_ERROR("X509_new() failed");
221

ziyue committed
222 223
			goto error;
		}
224

ziyue committed
225
		// Set version 3 (note that 0 means version 1).
226
		X509_set_version(certificate, 2);
227

ziyue committed
228 229
		// Set serial number (avoid default 0).
		ASN1_INTEGER_set(
230
		  X509_get_serialNumber(certificate),
ziyue committed
231
		  static_cast<uint64_t>(rand() % 999999 + 100000));
232

ziyue committed
233
		// Set valid period.
234 235
		X509_gmtime_adj(X509_get_notBefore(certificate), -315360000); // -10 years.
		X509_gmtime_adj(X509_get_notAfter(certificate), 315360000);   // 10 years.
236

ziyue committed
237
		// Set the public key for the certificate using the key.
238
		ret = X509_set_pubkey(certificate, privateKey);
239

ziyue committed
240 241 242
		if (ret == 0)
		{
			LOG_OPENSSL_ERROR("X509_set_pubkey() failed");
243

ziyue committed
244 245
			goto error;
		}
246

ziyue committed
247
		// Set certificate fields.
248
		certName = X509_get_subject_name(certificate);
249

ziyue committed
250 251 252
		if (!certName)
		{
			LOG_OPENSSL_ERROR("X509_get_subject_name() failed");
253

ziyue committed
254 255
			goto error;
		}
256

ziyue committed
257 258 259 260
		X509_NAME_add_entry_by_txt(
		  certName, "O", MBSTRING_ASC, reinterpret_cast<const uint8_t*>(subject.c_str()), -1, -1, 0);
		X509_NAME_add_entry_by_txt(
		  certName, "CN", MBSTRING_ASC, reinterpret_cast<const uint8_t*>(subject.c_str()), -1, -1, 0);
261

ziyue committed
262
		// It is self-signed so set the issuer name to be the same as the subject.
263
		ret = X509_set_issuer_name(certificate, certName);
264

ziyue committed
265 266 267
		if (ret == 0)
		{
			LOG_OPENSSL_ERROR("X509_set_issuer_name() failed");
268

ziyue committed
269 270
			goto error;
		}
271

ziyue committed
272
		// Sign the certificate with its own private key.
273
		ret = X509_sign(certificate, privateKey, EVP_sha1());
274

ziyue committed
275 276 277
		if (ret == 0)
		{
			LOG_OPENSSL_ERROR("X509_sign() failed");
278

ziyue committed
279 280
			goto error;
		}
281

ziyue committed
282
		return;
283

ziyue committed
284
	error:
285

ziyue committed
286 287
		if (ecKey)
			EC_KEY_free(ecKey);
288

289 290
		if (privateKey)
			EVP_PKEY_free(privateKey); // NOTE: This also frees the EC key.
291

292 293
		if (certificate)
			X509_free(certificate);
ziyue committed
294 295 296

		MS_THROW_ERROR("DTLS certificate and private key generation failed");
	}
297

298
	void DtlsTransport::DtlsEnvironment::ReadCertificateAndPrivateKeyFromFiles()
ziyue committed
299
	{
300
#if 0
ziyue committed
301
		MS_TRACE();
302

ziyue committed
303
		FILE* file{ nullptr };
304

ziyue committed
305
		file = fopen(Settings::configuration.dtlsCertificateFile.c_str(), "r");
306

ziyue committed
307 308 309
		if (!file)
		{
			MS_ERROR("error reading DTLS certificate file: %s", std::strerror(errno));
310

ziyue committed
311 312
			goto error;
		}
313

314
		certificate = PEM_read_X509(file, nullptr, nullptr, nullptr);
315

316
		if (!certificate)
ziyue committed
317 318
		{
			LOG_OPENSSL_ERROR("PEM_read_X509() failed");
319

ziyue committed
320 321
			goto error;
		}
322

ziyue committed
323
		fclose(file);
324

ziyue committed
325
		file = fopen(Settings::configuration.dtlsPrivateKeyFile.c_str(), "r");
326

ziyue committed
327 328 329
		if (!file)
		{
			MS_ERROR("error reading DTLS private key file: %s", std::strerror(errno));
330

ziyue committed
331 332
			goto error;
		}
333

334
		privateKey = PEM_read_PrivateKey(file, nullptr, nullptr, nullptr);
335

336
		if (!privateKey)
ziyue committed
337 338
		{
			LOG_OPENSSL_ERROR("PEM_read_PrivateKey() failed");
339

ziyue committed
340 341
			goto error;
		}
342

ziyue committed
343
		fclose(file);
344

ziyue committed
345
		return;
346

ziyue committed
347
	error:
348

ziyue committed
349
		MS_THROW_ERROR("error reading DTLS certificate and private key PEM files");
350
#endif
ziyue committed
351
	}
352

353
	void DtlsTransport::DtlsEnvironment::CreateSslCtx()
ziyue committed
354 355
	{
		MS_TRACE();
356

ziyue committed
357 358
		std::string dtlsSrtpCryptoSuites;
		int ret;
359

ziyue committed
360
		/* Set the global DTLS context. */
361

ziyue committed
362
		// Both DTLS 1.0 and 1.2 (requires OpenSSL >= 1.1.0).
363
		sslCtx = SSL_CTX_new(DTLS_method());
364

365
		if (!sslCtx)
ziyue committed
366 367
		{
			LOG_OPENSSL_ERROR("SSL_CTX_new() failed");
368

ziyue committed
369 370
			goto error;
		}
371

372
		ret = SSL_CTX_use_certificate(sslCtx, certificate);
373

ziyue committed
374 375 376
		if (ret == 0)
		{
			LOG_OPENSSL_ERROR("SSL_CTX_use_certificate() failed");
377

ziyue committed
378 379
			goto error;
		}
380

381
		ret = SSL_CTX_use_PrivateKey(sslCtx, privateKey);
382

ziyue committed
383 384 385
		if (ret == 0)
		{
			LOG_OPENSSL_ERROR("SSL_CTX_use_PrivateKey() failed");
386

ziyue committed
387 388
			goto error;
		}
389

390
		ret = SSL_CTX_check_private_key(sslCtx);
391

ziyue committed
392 393 394
		if (ret == 0)
		{
			LOG_OPENSSL_ERROR("SSL_CTX_check_private_key() failed");
395

ziyue committed
396 397
			goto error;
		}
398

ziyue committed
399 400
		// Set options.
		SSL_CTX_set_options(
401
		  sslCtx,
ziyue committed
402 403
		  SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_TICKET | SSL_OP_SINGLE_ECDH_USE |
		    SSL_OP_NO_QUERY_MTU);
404

ziyue committed
405
		// Don't use sessions cache.
406
		SSL_CTX_set_session_cache_mode(sslCtx, SSL_SESS_CACHE_OFF);
407

ziyue committed
408 409 410
		// 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.
411
		SSL_CTX_set_read_ahead(sslCtx, 1);
412

413
		SSL_CTX_set_verify_depth(sslCtx, 4);
414

ziyue committed
415 416
		// Require certificate from peer.
		SSL_CTX_set_verify(
417
		  sslCtx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, onSslCertificateVerify);
418

ziyue committed
419
		// Set SSL info callback.
420
		SSL_CTX_set_info_callback(sslCtx, [](const SSL* ssl, int where, int ret){
ziyue committed
421 422 423 424
            static_cast<RTC::DtlsTransport*>(SSL_get_ex_data(ssl, 0))->OnSslInfo(where, ret);
        });
		// Set ciphers.
		ret = SSL_CTX_set_cipher_list(
425
		  sslCtx, "DEFAULT:!NULL:!aNULL:!SHA256:!SHA384:!aECDH:!AESGCM+AES256:!aPSK");
426

ziyue committed
427 428 429
		if (ret == 0)
		{
			LOG_OPENSSL_ERROR("SSL_CTX_set_cipher_list() failed");
430

ziyue committed
431 432
			goto error;
		}
433

ziyue committed
434 435 436 437
		// 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
438

ziyue committed
439
		// For OpenSSL >= 1.0.2.
440
		SSL_CTX_set_ecdh_auto(sslCtx, 1);
441

ziyue committed
442 443 444 445 446 447 448
		// Set the "use_srtp" DTLS extension.
		for (auto it = DtlsTransport::srtpCryptoSuites.begin();
		     it != DtlsTransport::srtpCryptoSuites.end();
		     ++it)
		{
			if (it != DtlsTransport::srtpCryptoSuites.begin())
				dtlsSrtpCryptoSuites += ":";
449

ziyue committed
450 451 452
			SrtpCryptoSuiteMapEntry* cryptoSuiteEntry = std::addressof(*it);
			dtlsSrtpCryptoSuites += cryptoSuiteEntry->name;
		}
453

ziyue committed
454
		MS_DEBUG_2TAGS(dtls, srtp, "setting SRTP cryptoSuites for DTLS: %s", dtlsSrtpCryptoSuites.c_str());
455

ziyue committed
456
		// NOTE: This function returns 0 on success.
457
		ret = SSL_CTX_set_tlsext_use_srtp(sslCtx, dtlsSrtpCryptoSuites.c_str());
458

ziyue committed
459 460 461 462 463
		if (ret != 0)
		{
			MS_ERROR(
			  "SSL_CTX_set_tlsext_use_srtp() failed when entering '%s'", dtlsSrtpCryptoSuites.c_str());
			LOG_OPENSSL_ERROR("SSL_CTX_set_tlsext_use_srtp() failed");
464

ziyue committed
465 466
			goto error;
		}
467

ziyue committed
468
		return;
469

ziyue committed
470
	error:
471

472
		if (sslCtx)
ziyue committed
473
		{
474 475
			SSL_CTX_free(sslCtx);
			sslCtx = nullptr;
ziyue committed
476
		}
477

ziyue committed
478 479
		MS_THROW_ERROR("SSL context creation failed");
	}
480

481
	void DtlsTransport::DtlsEnvironment::GenerateFingerprints()
ziyue committed
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
	{
		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");
			}

521
			ret = X509_digest(certificate, hashFunction, binaryFingerprint, &size);
ziyue committed
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543

			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;

544
			localFingerprints.push_back(fingerprint);
ziyue committed
545 546 547 548 549 550 551 552
		}
	}

	/* Instance methods. */

	DtlsTransport::DtlsTransport(EventPoller::Ptr poller,Listener* listener) : poller(std::move(poller)), listener(listener)
	{
		MS_TRACE();
553
        env = DtlsEnvironment::Instance().shared_from_this();
554

ziyue committed
555
		/* Set SSL. */
556

557
		this->ssl = SSL_new(env->sslCtx);
558

ziyue committed
559 560 561
		if (!this->ssl)
		{
			LOG_OPENSSL_ERROR("SSL_new() failed");
562

ziyue committed
563 564
			goto error;
		}
565

ziyue committed
566 567
		// Set this as custom data.
		SSL_set_ex_data(this->ssl, 0, static_cast<void*>(this));
568

ziyue committed
569
		this->sslBioFromNetwork = BIO_new(BIO_s_mem());
570

ziyue committed
571 572 573
		if (!this->sslBioFromNetwork)
		{
			LOG_OPENSSL_ERROR("BIO_new() failed");
574

ziyue committed
575
			SSL_free(this->ssl);
576

ziyue committed
577 578
			goto error;
		}
579

ziyue committed
580
		this->sslBioToNetwork = BIO_new(BIO_s_mem());
581

ziyue committed
582 583 584
		if (!this->sslBioToNetwork)
		{
			LOG_OPENSSL_ERROR("BIO_new() failed");
585

ziyue committed
586 587
			BIO_free(this->sslBioFromNetwork);
			SSL_free(this->ssl);
588

ziyue committed
589 590
			goto error;
		}
591

ziyue committed
592
		SSL_set_bio(this->ssl, this->sslBioFromNetwork, this->sslBioToNetwork);
593

ziyue committed
594 595 596
		// Set the MTU so that we don't send packets that are too large with no fragmentation.
		SSL_set_mtu(this->ssl, DtlsMtu);
		DTLS_set_link_mtu(this->ssl, DtlsMtu);
597

ziyue committed
598 599
		// Set callback handler for setting DTLS timer interval.
		DTLS_set_timer_cb(this->ssl, onSslDtlsTimer);
600

ziyue committed
601
		return;
602

ziyue committed
603
	error:
604

ziyue committed
605 606 607 608
		// NOTE: At this point SSL_set_bio() was not called so we must free BIOs as
		// well.
		if (this->sslBioFromNetwork)
			BIO_free(this->sslBioFromNetwork);
609

ziyue committed
610 611
		if (this->sslBioToNetwork)
			BIO_free(this->sslBioToNetwork);
612

ziyue committed
613 614
		if (this->ssl)
			SSL_free(this->ssl);
615

ziyue committed
616 617 618 619
		// NOTE: If this is not catched by the caller the program will abort, but
		// this should never happen.
		MS_THROW_ERROR("DtlsTransport instance creation failed");
	}
620

ziyue committed
621 622 623
	DtlsTransport::~DtlsTransport()
	{
		MS_TRACE();
624

ziyue committed
625 626 627 628 629 630
		if (IsRunning())
		{
			// Send close alert to the peer.
			SSL_shutdown(this->ssl);
			SendPendingOutgoingDtlsData();
		}
631

ziyue committed
632 633 634
		if (this->ssl)
		{
			SSL_free(this->ssl);
635

ziyue committed
636 637 638 639
			this->ssl               = nullptr;
			this->sslBioFromNetwork = nullptr;
			this->sslBioToNetwork   = nullptr;
		}
640

ziyue committed
641 642 643
		// Close the DTLS timer.
		this->timer = nullptr;
	}
644

ziyue committed
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
	void DtlsTransport::Dump() const
	{
		MS_TRACE();

		std::string state{ "new" };
		std::string role{ "none " };

		switch (this->state)
		{
			case DtlsState::CONNECTING:
				state = "connecting";
				break;
			case DtlsState::CONNECTED:
				state = "connected";
				break;
			case DtlsState::FAILED:
				state = "failed";
				break;
			case DtlsState::CLOSED:
				state = "closed";
				break;
			default:;
		}

		switch (this->localRole)
		{
			case Role::AUTO:
				role = "auto";
				break;
			case Role::SERVER:
				role = "server";
				break;
			case Role::CLIENT:
				role = "client";
				break;
			default:;
		}

		MS_DUMP("<DtlsTransport>");
		MS_DUMP("  state           : %s", state.c_str());
		MS_DUMP("  role            : %s", role.c_str());
		MS_DUMP("  handshake done: : %s", this->handshakeDone ? "yes" : "no");
		MS_DUMP("</DtlsTransport>");
	}

	void DtlsTransport::Run(Role localRole)
	{
		MS_TRACE();
693

ziyue committed
694 695 696
		MS_ASSERT(
		  localRole == Role::CLIENT || localRole == Role::SERVER,
		  "local DTLS role must be 'client' or 'server'");
697

ziyue committed
698
		Role previousLocalRole = this->localRole;
699

ziyue committed
700 701 702
		if (localRole == previousLocalRole)
		{
			MS_ERROR("same local DTLS role provided, doing nothing");
703

ziyue committed
704 705
			return;
		}
706

ziyue committed
707 708 709 710
		// 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");
711

ziyue committed
712 713
			Reset();
		}
714

ziyue committed
715 716
		// Update local role.
		this->localRole = localRole;
717

ziyue committed
718 719 720
		// Set state and notify the listener.
		this->state = DtlsState::CONNECTING;
		this->listener->OnDtlsTransportConnecting(this);
721

ziyue committed
722 723 724 725 726
		switch (this->localRole)
		{
			case Role::CLIENT:
			{
				MS_DEBUG_TAG(dtls, "running [role:client]");
727

ziyue committed
728 729 730 731
				SSL_set_connect_state(this->ssl);
				SSL_do_handshake(this->ssl);
				SendPendingOutgoingDtlsData();
				SetTimeout();
732

ziyue committed
733 734
				break;
			}
735

ziyue committed
736 737 738
			case Role::SERVER:
			{
				MS_DEBUG_TAG(dtls, "running [role:server]");
739

ziyue committed
740 741
				SSL_set_accept_state(this->ssl);
				SSL_do_handshake(this->ssl);
742

ziyue committed
743 744
				break;
			}
745

ziyue committed
746 747 748 749 750 751
			default:
			{
				MS_ABORT("invalid local DTLS role");
			}
		}
	}
752

ziyue committed
753 754 755
	bool DtlsTransport::SetRemoteFingerprint(Fingerprint fingerprint)
	{
		MS_TRACE();
756

ziyue committed
757 758
		MS_ASSERT(
		  fingerprint.algorithm != FingerprintAlgorithm::NONE, "no fingerprint algorithm provided");
759

ziyue committed
760
		this->remoteFingerprint = fingerprint;
761

ziyue committed
762 763 764 765 766
		// The remote fingerpring may have been set after DTLS handshake was done,
		// so we may need to process it now.
		if (this->handshakeDone && this->state != DtlsState::CONNECTED)
		{
			MS_DEBUG_TAG(dtls, "handshake already done, processing it right now");
767

ziyue committed
768 769
			return ProcessHandshake();
		}
770

ziyue committed
771 772
		return true;
	}
773

ziyue committed
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834
	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, static_cast<const void*>(data), static_cast<int>(len));

		if (written != static_cast<int>(len))
		{
			MS_WARN_TAG(
			  dtls,
			  "OpenSSL BIO_write() wrote less (%zu bytes) than given data (%zu bytes)",
			  static_cast<size_t>(written),
			  len);
		}

		// Must call SSL_read() to process received DTLS data.
		read = SSL_read(this->ssl, static_cast<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<size_t>(read));
		}
	}

	void DtlsTransport::SendApplicationData(const uint8_t* data, size_t len)
	{
		MS_TRACE();
835

ziyue committed
836 837 838 839
		// We cannot send data to the peer if its remote fingerprint is not validated.
		if (this->state != DtlsState::CONNECTED)
		{
			MS_WARN_TAG(dtls, "cannot send application data while DTLS is not fully connected");
840

ziyue committed
841 842
			return;
		}
843

ziyue committed
844 845 846
		if (len == 0)
		{
			MS_WARN_TAG(dtls, "ignoring 0 length data");
847

ziyue committed
848 849
			return;
		}
850

ziyue committed
851
		int written;
852

ziyue committed
853
		written = SSL_write(this->ssl, static_cast<const void*>(data), static_cast<int>(len));
854

ziyue committed
855 856 857
		if (written < 0)
		{
			LOG_OPENSSL_ERROR("SSL_write() failed");
858

ziyue committed
859 860 861 862 863 864 865 866
			if (!CheckStatus(written))
				return;
		}
		else if (written != static_cast<int>(len))
		{
			MS_WARN_TAG(
			  dtls, "OpenSSL SSL_write() wrote less (%d bytes) than given data (%zu bytes)", written, len);
		}
867

ziyue committed
868 869 870
		// Send data.
		SendPendingOutgoingDtlsData();
	}
871

ziyue committed
872 873 874
	void DtlsTransport::Reset()
	{
		MS_TRACE();
875

ziyue committed
876
		int ret;
877

ziyue committed
878 879
		if (!IsRunning())
			return;
880

ziyue committed
881
		MS_WARN_TAG(dtls, "resetting DTLS transport");
882

ziyue committed
883 884
		// Stop the DTLS timer.
		this->timer = nullptr;
885

ziyue committed
886 887 888 889
		// We need to reset the SSL instance so we need to "shutdown" it, but we
		// don't want to send a Close Alert to the peer, so just don't call
		// SendPendingOutgoingDTLSData().
		SSL_shutdown(this->ssl);
890

ziyue committed
891 892 893 894
		this->localRole        = Role::NONE;
		this->state            = DtlsState::NEW;
		this->handshakeDone    = false;
		this->handshakeDoneNow = false;
895

ziyue committed
896 897 898 899 900
		// Reset SSL status.
		// NOTE: For this to properly work, SSL_shutdown() must be called before.
		// NOTE: This may fail if not enough DTLS handshake data has been received,
		// but we don't care so just clear the error queue.
		ret = SSL_clear(this->ssl);
901

ziyue committed
902 903 904
		if (ret == 0)
			ERR_clear_error();
	}
905

ziyue committed
906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003
	inline bool DtlsTransport::CheckStatus(int returnCode)
	{
		MS_TRACE();

		int err;
		bool wasHandshakeDone = this->handshakeDone;

		err = SSL_get_error(this->ssl, returnCode);

		switch (err)
		{
			case SSL_ERROR_NONE:
				break;

			case SSL_ERROR_SSL:
				LOG_OPENSSL_ERROR("SSL status: SSL_ERROR_SSL");
				break;

			case SSL_ERROR_WANT_READ:
				break;

			case SSL_ERROR_WANT_WRITE:
				MS_WARN_TAG(dtls, "SSL status: SSL_ERROR_WANT_WRITE");
				break;

			case SSL_ERROR_WANT_X509_LOOKUP:
				MS_DEBUG_TAG(dtls, "SSL status: SSL_ERROR_WANT_X509_LOOKUP");
				break;

			case SSL_ERROR_SYSCALL:
				LOG_OPENSSL_ERROR("SSL status: SSL_ERROR_SYSCALL");
				break;

			case SSL_ERROR_ZERO_RETURN:
				break;

			case SSL_ERROR_WANT_CONNECT:
				MS_WARN_TAG(dtls, "SSL status: SSL_ERROR_WANT_CONNECT");
				break;

			case SSL_ERROR_WANT_ACCEPT:
				MS_WARN_TAG(dtls, "SSL status: SSL_ERROR_WANT_ACCEPT");
				break;

			default:
				MS_WARN_TAG(dtls, "SSL status: unknown error");
		}

		// Check if the handshake (or re-handshake) has been done right now.
		if (this->handshakeDoneNow)
		{
			this->handshakeDoneNow = false;
			this->handshakeDone    = true;

			// Stop the timer.
			this->timer = nullptr;

			// Process the handshake just once (ignore if DTLS renegotiation).
			if (!wasHandshakeDone && this->remoteFingerprint.algorithm != FingerprintAlgorithm::NONE)
				return ProcessHandshake();

			return true;
		}
		// Check if the peer sent close alert or a fatal error happened.
		else if (((SSL_get_shutdown(this->ssl) & SSL_RECEIVED_SHUTDOWN) != 0) || err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL)
		{
			if (this->state == DtlsState::CONNECTED)
			{
				MS_DEBUG_TAG(dtls, "disconnected");

				Reset();

				// Set state and notify the listener.
				this->state = DtlsState::CLOSED;
				this->listener->OnDtlsTransportClosed(this);
			}
			else
			{
				MS_WARN_TAG(dtls, "connection failed");

				Reset();

				// Set state and notify the listener.
				this->state = DtlsState::FAILED;
				this->listener->OnDtlsTransportFailed(this);
			}

			return false;
		}
		else
		{
			return true;
		}
	}

	inline void DtlsTransport::SendPendingOutgoingDtlsData()
	{
		MS_TRACE();
1004

ziyue committed
1005 1006
		if (BIO_eof(this->sslBioToNetwork))
			return;
1007

ziyue committed
1008 1009
		int64_t read;
		char* data{ nullptr };
1010

ziyue committed
1011
		read = BIO_get_mem_data(this->sslBioToNetwork, &data); // NOLINT
1012

ziyue committed
1013 1014
		if (read <= 0)
			return;
1015

ziyue committed
1016
		MS_DEBUG_DEV("%" PRIu64 " bytes of DTLS data ready to sent to the peer", read);
1017

ziyue committed
1018 1019 1020
		// Notify the listener.
		this->listener->OnDtlsTransportSendData(
		  this, reinterpret_cast<uint8_t*>(data), static_cast<size_t>(read));
1021

ziyue committed
1022 1023 1024 1025
		// Clear the BIO buffer.
		// NOTE: the (void) avoids the -Wunused-value warning.
		(void)BIO_reset(this->sslBioToNetwork);
	}
1026

ziyue committed
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084
	inline bool DtlsTransport::SetTimeout()
	{
		MS_TRACE();

		MS_ASSERT(
		  this->state == DtlsState::CONNECTING || this->state == DtlsState::CONNECTED,
		  "invalid DTLS state");

		int64_t ret;
        struct timeval dtlsTimeout{ 0, 0 };
		uint64_t timeoutMs;

		// NOTE: If ret == 0 then ignore the value in dtlsTimeout.
		// NOTE: No DTLSv_1_2_get_timeout() or DTLS_get_timeout() in OpenSSL 1.1.0-dev.
		ret = DTLSv1_get_timeout(this->ssl, static_cast<void*>(&dtlsTimeout)); // NOLINT

		if (ret == 0)
			return true;

		timeoutMs = (dtlsTimeout.tv_sec * static_cast<uint64_t>(1000)) + (dtlsTimeout.tv_usec / 1000);

		if (timeoutMs == 0)
		{
			return true;
		}
		else if (timeoutMs < 30000)
		{
			MS_DEBUG_DEV("DTLS timer set in %" PRIu64 "ms", timeoutMs);

			weak_ptr<DtlsTransport> weak_self = shared_from_this();
			this->timer = std::make_shared<Timer>(timeoutMs / 1000.0f, [weak_self](){
			    auto strong_self = weak_self.lock();
			    if(strong_self){
                    strong_self->OnTimer();
			    }
                return true;
			}, this->poller);

			return true;
		}
		// NOTE: Don't start the timer again if the timeout is greater than 30 seconds.
		else
		{
			MS_WARN_TAG(dtls, "DTLS timeout too high (%" PRIu64 "ms), resetting DLTS", timeoutMs);

			Reset();

			// Set state and notify the listener.
			this->state = DtlsState::FAILED;
			this->listener->OnDtlsTransportFailed(this);

			return false;
		}
	}

	inline bool DtlsTransport::ProcessHandshake()
	{
		MS_TRACE();
1085

ziyue committed
1086 1087 1088
		MS_ASSERT(this->handshakeDone, "handshake not done yet");
		MS_ASSERT(
		  this->remoteFingerprint.algorithm != FingerprintAlgorithm::NONE, "remote fingerprint not set");
1089

ziyue committed
1090 1091 1092 1093
		// Validate the remote fingerprint.
		if (!CheckRemoteFingerprint())
		{
			Reset();
1094

ziyue committed
1095 1096 1097
			// Set state and notify the listener.
			this->state = DtlsState::FAILED;
			this->listener->OnDtlsTransportFailed(this);
1098

ziyue committed
1099 1100
			return false;
		}
1101

ziyue committed
1102 1103
		// Get the negotiated SRTP crypto suite.
		RTC::SrtpSession::CryptoSuite srtpCryptoSuite = GetNegotiatedSrtpCryptoSuite();
1104

ziyue committed
1105 1106 1107 1108
		if (srtpCryptoSuite != RTC::SrtpSession::CryptoSuite::NONE)
		{
			// Extract the SRTP keys (will notify the listener with them).
			ExtractSrtpKeys(srtpCryptoSuite);
1109

ziyue committed
1110 1111
			return true;
		}
1112

ziyue committed
1113 1114 1115
		// NOTE: We assume that "use_srtp" DTLS extension is required even if
		// there is no audio/video.
		MS_WARN_2TAGS(dtls, srtp, "SRTP crypto suite not negotiated");
1116

ziyue committed
1117
		Reset();
1118

ziyue committed
1119 1120 1121
		// Set state and notify the listener.
		this->state = DtlsState::FAILED;
		this->listener->OnDtlsTransportFailed(this);
1122

ziyue committed
1123 1124
		return false;
	}
1125

ziyue committed
1126 1127 1128
	inline bool DtlsTransport::CheckRemoteFingerprint()
	{
		MS_TRACE();
1129

ziyue committed
1130 1131
		MS_ASSERT(
		  this->remoteFingerprint.algorithm != FingerprintAlgorithm::NONE, "remote fingerprint not set");
1132

ziyue committed
1133 1134 1135 1136 1137 1138
		X509* certificate;
		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;
1139

ziyue committed
1140
		certificate = SSL_get_peer_certificate(this->ssl);
1141

ziyue committed
1142 1143 1144
		if (!certificate)
		{
			MS_WARN_TAG(dtls, "no certificate was provided by the peer");
1145

ziyue committed
1146 1147
			return false;
		}
1148

ziyue committed
1149 1150 1151 1152 1153
		switch (this->remoteFingerprint.algorithm)
		{
			case FingerprintAlgorithm::SHA1:
				hashFunction = EVP_sha1();
				break;
1154

ziyue committed
1155 1156 1157
			case FingerprintAlgorithm::SHA224:
				hashFunction = EVP_sha224();
				break;
1158

ziyue committed
1159 1160 1161
			case FingerprintAlgorithm::SHA256:
				hashFunction = EVP_sha256();
				break;
1162

ziyue committed
1163 1164 1165
			case FingerprintAlgorithm::SHA384:
				hashFunction = EVP_sha384();
				break;
1166

ziyue committed
1167 1168 1169
			case FingerprintAlgorithm::SHA512:
				hashFunction = EVP_sha512();
				break;
1170

ziyue committed
1171 1172 1173
			default:
				MS_ABORT("unknown algorithm");
		}
1174

ziyue committed
1175 1176
		// Compare the remote fingerprint with the value given via signaling.
		ret = X509_digest(certificate, hashFunction, binaryFingerprint, &size);
1177

ziyue committed
1178 1179 1180
		if (ret == 0)
		{
			MS_ERROR("X509_digest() failed");
1181

ziyue committed
1182
			X509_free(certificate);
1183

ziyue committed
1184 1185
			return false;
		}
1186

ziyue committed
1187 1188 1189 1190 1191 1192
		// 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';
1193

ziyue committed
1194 1195 1196 1197 1198 1199 1200 1201 1202 1203
		if (this->remoteFingerprint.value != hexFingerprint)
		{
			MS_WARN_TAG(
			  dtls,
			  "fingerprint in the remote certificate (%s) does not match the announced one (%s)",
			  hexFingerprint,
			  this->remoteFingerprint.value.c_str());
			X509_free(certificate);
			return false;
		}
1204

ziyue committed
1205
		MS_DEBUG_TAG(dtls, "valid remote fingerprint");
1206

ziyue committed
1207
		// Get the remote certificate in PEM format.
1208

ziyue committed
1209
		BIO* bio = BIO_new(BIO_s_mem());
1210

ziyue committed
1211 1212 1213 1214
		// Ensure the underlying BUF_MEM structure is also freed.
		// NOTE: Avoid stupid "warning: value computed is not used [-Wunused-value]" since
		// BIO_set_close() always returns 1.
		(void)BIO_set_close(bio, BIO_CLOSE);
1215

ziyue committed
1216
		ret = PEM_write_bio_X509(bio, certificate);
1217

ziyue committed
1218 1219 1220
		if (ret != 1)
		{
			LOG_OPENSSL_ERROR("PEM_write_bio_X509() failed");
1221

ziyue committed
1222 1223
			X509_free(certificate);
			BIO_free(bio);
1224

ziyue committed
1225 1226
			return false;
		}
1227

ziyue committed
1228
		BUF_MEM* mem;
1229

ziyue committed
1230
		BIO_get_mem_ptr(bio, &mem); // NOLINT[cppcoreguidelines-pro-type-cstyle-cast]
1231

ziyue committed
1232 1233 1234
		if (!mem || !mem->data || mem->length == 0u)
		{
			LOG_OPENSSL_ERROR("BIO_get_mem_ptr() failed");
1235

ziyue committed
1236 1237
			X509_free(certificate);
			BIO_free(bio);
1238

ziyue committed
1239 1240
			return false;
		}
1241

ziyue committed
1242
		this->remoteCert = std::string(mem->data, mem->length);
1243

ziyue committed
1244 1245
		X509_free(certificate);
		BIO_free(bio);
1246

ziyue committed
1247 1248
		return true;
	}
1249

ziyue committed
1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361
	inline void DtlsTransport::ExtractSrtpKeys(RTC::SrtpSession::CryptoSuite srtpCryptoSuite)
	{
		MS_TRACE();

		size_t srtpKeyLength{ 0 };
		size_t srtpSaltLength{ 0 };
		size_t srtpMasterLength{ 0 };

		switch (srtpCryptoSuite)
		{
			case RTC::SrtpSession::CryptoSuite::AES_CM_128_HMAC_SHA1_80:
			case RTC::SrtpSession::CryptoSuite::AES_CM_128_HMAC_SHA1_32:
			{
				srtpKeyLength    = SrtpMasterKeyLength;
				srtpSaltLength   = SrtpMasterSaltLength;
				srtpMasterLength = SrtpMasterLength;

				break;
			}

			case RTC::SrtpSession::CryptoSuite::AEAD_AES_256_GCM:
			{
				srtpKeyLength    = SrtpAesGcm256MasterKeyLength;
				srtpSaltLength   = SrtpAesGcm256MasterSaltLength;
				srtpMasterLength = SrtpAesGcm256MasterLength;

				break;
			}

			case RTC::SrtpSession::CryptoSuite::AEAD_AES_128_GCM:
			{
				srtpKeyLength    = SrtpAesGcm128MasterKeyLength;
				srtpSaltLength   = SrtpAesGcm128MasterSaltLength;
				srtpMasterLength = SrtpAesGcm128MasterLength;

				break;
			}

			default:
			{
				MS_ABORT("unknown SRTP crypto suite");
			}
		}

		auto* srtpMaterial = new uint8_t[srtpMasterLength * 2];
		uint8_t* srtpLocalKey{ nullptr };
		uint8_t* srtpLocalSalt{ nullptr };
		uint8_t* srtpRemoteKey{ nullptr };
		uint8_t* srtpRemoteSalt{ nullptr };
		auto* srtpLocalMasterKey  = new uint8_t[srtpMasterLength];
		auto* srtpRemoteMasterKey = new uint8_t[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,
		  srtpCryptoSuite,
		  srtpLocalMasterKey,
		  srtpMasterLength,
		  srtpRemoteMasterKey,
		  srtpMasterLength,
		  this->remoteCert);

		delete[] srtpMaterial;
		delete[] srtpLocalMasterKey;
		delete[] srtpRemoteMasterKey;
	}

	inline RTC::SrtpSession::CryptoSuite DtlsTransport::GetNegotiatedSrtpCryptoSuite()
	{
		MS_TRACE();
1362

ziyue committed
1363
		RTC::SrtpSession::CryptoSuite negotiatedSrtpCryptoSuite = RTC::SrtpSession::CryptoSuite::NONE;
1364

ziyue committed
1365 1366 1367
		// Ensure that the SRTP crypto suite has been negotiated.
		// NOTE: This is a OpenSSL type.
		SRTP_PROTECTION_PROFILE* sslSrtpCryptoSuite = SSL_get_selected_srtp_profile(this->ssl);
1368

ziyue committed
1369 1370
		if (!sslSrtpCryptoSuite)
			return negotiatedSrtpCryptoSuite;
1371

ziyue committed
1372 1373 1374 1375
		// Get the negotiated SRTP crypto suite.
		for (auto& srtpCryptoSuite : DtlsTransport::srtpCryptoSuites)
		{
			SrtpCryptoSuiteMapEntry* cryptoSuiteEntry = std::addressof(srtpCryptoSuite);
1376

ziyue committed
1377 1378 1379
			if (std::strcmp(sslSrtpCryptoSuite->name, cryptoSuiteEntry->name) == 0)
			{
				MS_DEBUG_2TAGS(dtls, srtp, "chosen SRTP crypto suite: %s", cryptoSuiteEntry->name);
1380

ziyue committed
1381 1382 1383
				negotiatedSrtpCryptoSuite = cryptoSuiteEntry->cryptoSuite;
			}
		}
1384

ziyue committed
1385 1386 1387
		MS_ASSERT(
		  negotiatedSrtpCryptoSuite != RTC::SrtpSession::CryptoSuite::NONE,
		  "chosen SRTP crypto suite is not an available one");
1388

ziyue committed
1389 1390
		return negotiatedSrtpCryptoSuite;
	}
1391

ziyue committed
1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465
	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).
	}

	inline void DtlsTransport::OnTimer()
	{
		MS_TRACE();
1466

ziyue committed
1467 1468 1469 1470
		// Workaround for https://github.com/openssl/openssl/issues/7998.
		if (this->handshakeDone)
		{
			MS_DEBUG_DEV("handshake is done so return");
1471

ziyue committed
1472 1473
			return;
		}
1474

ziyue committed
1475
		DTLSv1_handle_timeout(this->ssl);
1476

ziyue committed
1477 1478
		// If required, send DTLS data.
		SendPendingOutgoingDtlsData();
1479

ziyue committed
1480 1481 1482 1483
		// Set the DTLS timer again.
		SetTimeout();
	}
} // namespace RTC