UnixStreamSocket::UnixStreamSocket(int fd) :
		::UnixStreamSocket::UnixStreamSocket(fd, NS_MAX_SIZE)
	{
		MS_TRACE_STD();

		// Create the JSON reader.
		{
			Json::CharReaderBuilder builder;
			Json::Value settings = Json::nullValue;
			Json::Value invalid_settings;

			builder.strictMode(&settings);

			MS_ASSERT(builder.validate(&invalid_settings), "invalid Json::CharReaderBuilder");

			this->jsonReader = builder.newCharReader();
		}

		// Create the JSON writer.
		{
			Json::StreamWriterBuilder builder;
			Json::Value invalid_settings;

			builder["commentStyle"] = "None";
			builder["indentation"] = "";
			builder["enableYAMLCompatibility"] = false;
			builder["dropNullPlaceholders"] = false;

			MS_ASSERT(builder.validate(&invalid_settings), "invalid Json::StreamWriterBuilder");

			this->jsonWriter = builder.newStreamWriter();
		}
	}
Beispiel #2
0
	inline
	RTC::SrtpSession::Profile DtlsTransport::GetNegotiatedSrtpProfile()
	{
		MS_TRACE();

		RTC::SrtpSession::Profile negotiated_srtp_profile = RTC::SrtpSession::Profile::NONE;

		// Ensure that the SRTP profile has been negotiated.
		SRTP_PROTECTION_PROFILE* ssl_srtp_profile = SSL_get_selected_srtp_profile(this->ssl);
		if (!ssl_srtp_profile)
		{
			return negotiated_srtp_profile;
		}

		// Get the negotiated SRTP profile.
		for (auto it = DtlsTransport::srtpProfiles.begin(); it != DtlsTransport::srtpProfiles.end(); ++it)
		{
			SrtpProfileMapEntry* profile_entry = &(*it);

			if (std::strcmp(ssl_srtp_profile->name, profile_entry->name) == 0)
			{
				MS_DEBUG("chosen SRTP profile: %s", profile_entry->name);

				negotiated_srtp_profile = profile_entry->profile;
			}
		}

		MS_ASSERT(negotiated_srtp_profile != RTC::SrtpSession::Profile::NONE, "chosen SRTP profile is not an available one");

		return negotiated_srtp_profile;
	}
Beispiel #3
0
/*
	split encryptedPackage as [uint64_t:encData]
*/
inline uint64_t GetEncodedData(std::string& encData, const std::string& encryptedPackage)
{
	if (encryptedPackage.size() < 8) {
		throw cybozu::Exception("ms:GetEncodedData:tool small") << encryptedPackage.size();
	}
	const char *p = &encryptedPackage[0];
	const uint64_t size = cybozu::Get64bitAsLE(p);
	dprintf("package size:header %d encryptedPackage %d\n", (int)size, (int)encryptedPackage.size());
	MS_ASSERT(encryptedPackage.size() - 8 >= size);
	encData = encryptedPackage.substr(8);
	return size;
}
Beispiel #4
0
	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("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->onDtlsConnecting(this);

		switch (this->localRole)
		{
			case Role::CLIENT:
				MS_DEBUG("running [role:client]");

				SSL_set_connect_state(this->ssl);
				SSL_do_handshake(this->ssl);
				SendPendingOutgoingDtlsData();
				SetTimeout();
				break;
			case Role::SERVER:
				MS_DEBUG("running [role:server]");

				SSL_set_accept_state(this->ssl);
				SSL_do_handshake(this->ssl);
				break;
			default:
				MS_ABORT("invalid local DTLS role");
				break;
		}
	}
Beispiel #5
0
	inline
	void DtlsTransport::ProcessHandshake()
	{
		MS_TRACE();

		MS_ASSERT(this->handshakeDone, "handshake not done yet");

		// If the remote fingerprint is not yet set then do nothing (this method
		// will be called when the fingerprint is set).
		if (this->remoteFingerprint.algorithm == FingerprintAlgorithm::NONE)
		{
			MS_DEBUG("remote fingerprint not yet set, waiting for it");

			return;
		}

		// Validate the remote fingerprint.
		if (!CheckRemoteFingerprint())
		{
			Reset();

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

			return;
		}

		// Get the negotiated SRTP profile.
		RTC::SrtpSession::Profile srtp_profile;
		srtp_profile = GetNegotiatedSrtpProfile();

		if (srtp_profile != RTC::SrtpSession::Profile::NONE)
		{
			// Extract the SRTP keys (will notify the listener with them).
			ExtractSrtpKeys(srtp_profile);
		}
		else
		{
			// NOTE: We assume that "use_srtp" DTLS extension is required even if
			// there is no audio/video.
			MS_WARN("SRTP profile not negotiated");

			Reset();

			// Set state and notify the listener.
			this->state = DtlsState::FAILED;
			this->listener->onDtlsFailed(this);
		}
	}
Beispiel #6
0
	inline
	void DtlsTransport::ExtractSrtpKeys(RTC::SrtpSession::Profile srtp_profile)
	{
		MS_TRACE();

		uint8_t srtp_material[MS_SRTP_MASTER_LENGTH * 2];
		uint8_t* srtp_local_key;
		uint8_t* srtp_local_salt;
		uint8_t* srtp_remote_key;
		uint8_t* srtp_remote_salt;
		uint8_t srtp_local_master_key[MS_SRTP_MASTER_LENGTH];
		uint8_t srtp_remote_master_key[MS_SRTP_MASTER_LENGTH];
		int ret;

		ret = SSL_export_keying_material(this->ssl, srtp_material, MS_SRTP_MASTER_LENGTH * 2, "EXTRACTOR-dtls_srtp", 19, nullptr, 0, 0);
		MS_ASSERT(ret != 0, "SSL_export_keying_material() failed");

		switch (this->localRole)
		{
			case Role::SERVER:
				srtp_remote_key = srtp_material;
				srtp_local_key = srtp_remote_key + MS_SRTP_MASTER_KEY_LENGTH;
				srtp_remote_salt = srtp_local_key + MS_SRTP_MASTER_KEY_LENGTH;
				srtp_local_salt = srtp_remote_salt + MS_SRTP_MASTER_SALT_LENGTH;
				break;
			case Role::CLIENT:
				srtp_local_key = srtp_material;
				srtp_remote_key = srtp_local_key + MS_SRTP_MASTER_KEY_LENGTH;
				srtp_local_salt = srtp_remote_key + MS_SRTP_MASTER_KEY_LENGTH;
				srtp_remote_salt = srtp_local_salt + MS_SRTP_MASTER_SALT_LENGTH;
				break;
			default:
				MS_ABORT("no DTLS role set");
				break;
		}

		// Create the SRTP local master key.
		std::memcpy(srtp_local_master_key, srtp_local_key, MS_SRTP_MASTER_KEY_LENGTH);
		std::memcpy(srtp_local_master_key + MS_SRTP_MASTER_KEY_LENGTH, srtp_local_salt, MS_SRTP_MASTER_SALT_LENGTH);
		// Create the SRTP remote master key.
		std::memcpy(srtp_remote_master_key, srtp_remote_key, MS_SRTP_MASTER_KEY_LENGTH);
		std::memcpy(srtp_remote_master_key + MS_SRTP_MASTER_KEY_LENGTH, srtp_remote_salt, MS_SRTP_MASTER_SALT_LENGTH);

		// Set state and notify the listener.
		this->state = DtlsState::CONNECTED;
		this->listener->onDtlsConnected(this, srtp_profile, srtp_local_master_key, MS_SRTP_MASTER_LENGTH, srtp_remote_master_key, MS_SRTP_MASTER_LENGTH);
	}
Beispiel #7
0
	void DtlsTransport::SetRemoteFingerprint(Fingerprint fingerprint)
	{
		MS_TRACE();

		MS_ASSERT(fingerprint.algorithm != FingerprintAlgorithm::NONE, "no fingerprint algorithm provided");

		this->remoteFingerprint = fingerprint;

		// 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("handshake already done, processing it right now");

			ProcessHandshake();
		}
	}
Beispiel #8
0
	/**
	 * Reject the Request.
	 * @param reason  Description string.
	 */
	void Request::Reject(const char* reason)
	{
		MS_TRACE();

		static const Json::StaticString k_id("id");
		static const Json::StaticString k_rejected("rejected");
		static const Json::StaticString k_reason("reason");

		MS_ASSERT(!this->replied, "Request already replied");
		this->replied = true;

		Json::Value json(Json::objectValue);

		json[k_id] = (Json::UInt)this->id;
		json[k_rejected] = true;

		if (reason)
			json[k_reason] = reason;

		this->channel->Send(json);
	}
Beispiel #9
0
	void Request::Accept(Json::Value &data)
	{
		MS_TRACE();

		static Json::Value empty_data(Json::objectValue);
		static const Json::StaticString k_id("id");
		static const Json::StaticString k_accepted("accepted");
		static const Json::StaticString k_data("data");

		MS_ASSERT(!this->replied, "Request already replied");
		this->replied = true;

		Json::Value json(Json::objectValue);

		json[k_id] = (Json::UInt)this->id;
		json[k_accepted] = true;

		if (data.isObject())
			json[k_data] = data;
		else
			json[k_data] = empty_data;

		this->channel->Send(json);
	}
Beispiel #10
0
	inline
	bool DtlsTransport::CheckRemoteFingerprint()
	{
		MS_TRACE();

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

		X509* certificate;
		uint8_t binary_fingerprint[EVP_MAX_MD_SIZE];
		unsigned int size = 0;
		char hex_fingerprint[(EVP_MAX_MD_SIZE * 2) + 1];
		const EVP_MD* hash_function;
		int ret;

		certificate = SSL_get_peer_certificate(this->ssl);
		if (!certificate)
		{
			MS_WARN("no certificate was provided by the peer");

			return false;
		}

		switch (this->remoteFingerprint.algorithm)
		{
			case FingerprintAlgorithm::SHA1:
				hash_function = EVP_sha1();
				break;
			case FingerprintAlgorithm::SHA224:
				hash_function = EVP_sha224();
				break;
			case FingerprintAlgorithm::SHA256:
				hash_function = EVP_sha256();
				break;
			case FingerprintAlgorithm::SHA384:
				hash_function = EVP_sha384();
				break;
			case FingerprintAlgorithm::SHA512:
				hash_function = EVP_sha512();
				break;
			default:
				MS_ABORT("unknown algorithm");
		}

		ret = X509_digest(certificate, hash_function, binary_fingerprint, &size);
		X509_free(certificate);
		if (ret == 0)
		{
			MS_ERROR("X509_digest() failed");

			return false;
		}

		// Convert to hexadecimal format in lowecase without colons.
		for (unsigned int i = 0; i < size; i++)
		{
			std::sprintf(hex_fingerprint + (i * 2), "%.2x", binary_fingerprint[i]);
		}
		hex_fingerprint[size * 2] = '\0';

		if (this->remoteFingerprint.value.compare(hex_fingerprint) != 0)
		{
			MS_WARN("fingerprint in the remote certificate (%s) does not match the announced one (%s)", hex_fingerprint, this->remoteFingerprint.value.c_str());

			return false;
		}

		MS_DEBUG("valid remote fingerprint");

		return true;
	}