bool OutboundRTMPProtocol::PerformHandshakeStage2(IOBuffer &inputBuffer,
		bool encrypted) {
	if (encrypted || _pProtocolHandler->ValidateHandshake()) {
		if (!VerifyServer(inputBuffer)) {
			FATAL("Unable to verify server");
			return false;
		}
	}

	uint8_t *pInputBuffer = GETIBPOINTER(inputBuffer) + 1;

	uint32_t serverDHOffset = GetDHOffset(pInputBuffer, _usedScheme);
	if (_pDHWrapper == NULL) {
		FATAL("dh wrapper not initialized");
		return false;
	}

	DEBUG_HANDSHAKE("CLIENT: 1. serverDHOffset: %" PRIu32 "; _usedScheme: %" PRIu8 "; serverPublicKey: %s",
			serverDHOffset,
			_usedScheme,
			STR(hex(pInputBuffer + serverDHOffset, 128)));
	if (!_pDHWrapper->CreateSharedKey(pInputBuffer + serverDHOffset, 128)) {
		FATAL("Unable to create shared key");
		return false;
	}

	uint8_t secretKey[128];
	if (!_pDHWrapper->CopySharedKey(secretKey, sizeof (secretKey))) {
		FATAL("Unable to compute shared");
		return false;
	}
	DEBUG_HANDSHAKE("CLIENT: 2. secretKey: %s", STR(hex(secretKey, 128)));

	if (encrypted) {
		_pKeyIn = new RC4_KEY;
		_pKeyOut = new RC4_KEY;

		InitRC4Encryption(
				secretKey,
				(uint8_t*) & pInputBuffer[serverDHOffset],
				_pClientPublicKey,
				_pKeyIn,
				_pKeyOut);

		uint8_t data[1536];
		RC4(_pKeyIn, 1536, data, data);
		RC4(_pKeyOut, 1536, data, data);
	}

	delete _pDHWrapper;
	_pDHWrapper = NULL;

	uint32_t serverDigestOffset = GetDigestOffset(pInputBuffer, _usedScheme);
	DEBUG_HANDSHAKE("CLIENT: 3. serverDigestOffset: %" PRIu32 "; _usedScheme: %" PRIu8, serverDigestOffset, _usedScheme);

	if (_pOutputBuffer == NULL) {
		_pOutputBuffer = new uint8_t[1536];
	} else {
		delete[] _pOutputBuffer;
		_pOutputBuffer = new uint8_t[1536];
	}

	for (uint32_t i = 0; i < 1536; i++) {
		_pOutputBuffer[i] = rand() % 256;
	}

	uint8_t * pChallangeKey = new uint8_t[512];
	HMACsha256(pInputBuffer + serverDigestOffset, 32, genuineFPKey, 62, pChallangeKey);

	uint8_t * pDigest = new uint8_t[512];
	HMACsha256(_pOutputBuffer, 1536 - 32, pChallangeKey, 32, pDigest);

	memcpy(_pOutputBuffer + 1536 - 32, pDigest, 32);
	DEBUG_HANDSHAKE("CLIENT: 4. clientChallange: %s", STR(hex(pDigest, 32)));

	delete[] pChallangeKey;
	delete[] pDigest;

	_outputBuffer.ReadFromBuffer(_pOutputBuffer, 1536);

	delete[] _pOutputBuffer;
	_pOutputBuffer = NULL;

	_rtmpState = RTMP_STATE_DONE;

	return true;
}
bool InboundRTMPProtocol::PerformComplexHandshake(IOBuffer &buffer, bool encrypted) {
	//get the buffers
	uint8_t *pInputBuffer = GETIBPOINTER(buffer);
	if (_pOutputBuffer == NULL) {
		_pOutputBuffer = new uint8_t[3072];
	} else {
		delete[] _pOutputBuffer;
		_pOutputBuffer = new uint8_t[3072];
	}

	//timestamp
	EHTONLP(_pOutputBuffer, (uint32_t) time(NULL));

	//version
	EHTONLP(_pOutputBuffer + 4, (uint32_t) 0x00000000);

	//generate random data
	for (uint32_t i = 8; i < 3072; i++) {
		_pOutputBuffer[i] = rand() % 256;
	}
	for (uint32_t i = 0; i < 10; i++) {
		uint32_t index = rand() % (3072 - HTTP_HEADERS_SERVER_US_LEN);
		memcpy(_pOutputBuffer + index, HTTP_HEADERS_SERVER_US, HTTP_HEADERS_SERVER_US_LEN);
	}

	//**** FIRST 1536 bytes from server response ****//
	//compute DH key position
	uint32_t serverDHOffset = GetDHOffset(_pOutputBuffer, _handshakeScheme);
	uint32_t clientDHOffset = GetDHOffset(pInputBuffer, _handshakeScheme);

	//generate DH key
	DHWrapper dhWrapper(1024);

	if (!dhWrapper.Initialize()) {
		FATAL("Unable to initialize DH wrapper");
		return false;
	}

	if (!dhWrapper.CreateSharedKey(pInputBuffer + clientDHOffset, 128)) {
		FATAL("Unable to create shared key");
		return false;
	}

	if (!dhWrapper.CopyPublicKey(_pOutputBuffer + serverDHOffset, 128)) {
		FATAL("Couldn't write public key!");
		return false;
	}

	if (encrypted) {
		uint8_t secretKey[128];
		if (!dhWrapper.CopySharedKey(secretKey, sizeof (secretKey))) {
			FATAL("Unable to copy shared key");
			return false;
		}

		_pKeyIn = new RC4_KEY;
		_pKeyOut = new RC4_KEY;
		InitRC4Encryption(
				secretKey,
				(uint8_t*) & pInputBuffer[clientDHOffset],
				(uint8_t*) & _pOutputBuffer[serverDHOffset],
				_pKeyIn,
				_pKeyOut);

		//bring the keys to correct cursor
		uint8_t data[1536];
		RC4(_pKeyIn, 1536, data, data);
		RC4(_pKeyOut, 1536, data, data);
	}

	//generate the digest
	uint32_t serverDigestOffset = GetDigestOffset(_pOutputBuffer, _handshakeScheme);

	uint8_t *pTempBuffer = new uint8_t[1536 - 32];
	memcpy(pTempBuffer, _pOutputBuffer, serverDigestOffset);
	memcpy(pTempBuffer + serverDigestOffset, _pOutputBuffer + serverDigestOffset + 32,
			1536 - serverDigestOffset - 32);

	uint8_t *pTempHash = new uint8_t[512];
	HMACsha256(pTempBuffer, 1536 - 32, genuineFMSKey, 36, pTempHash);

	//put the digest in place
	memcpy(_pOutputBuffer + serverDigestOffset, pTempHash, 32);

	//cleanup
	delete[] pTempBuffer;
	delete[] pTempHash;


	//**** SECOND 1536 bytes from server response ****//
	//Compute the chalange index from the initial client request
	uint32_t keyChallengeIndex = GetDigestOffset(pInputBuffer, _handshakeScheme);

	//compute the key
	pTempHash = new uint8_t[512];
	HMACsha256(pInputBuffer + keyChallengeIndex, //pData
			32, //dataLength
			BaseRTMPProtocol::genuineFMSKey, //key
			68, //keyLength
			pTempHash //pResult
			);

	//generate the hash
	uint8_t *pLastHash = new uint8_t[512];
	HMACsha256(_pOutputBuffer + 1536, //pData
			1536 - 32, //dataLength
			pTempHash, //key
			32, //keyLength
			pLastHash //pResult
			);

	//put the hash where it belongs
	memcpy(_pOutputBuffer + 1536 * 2 - 32, pLastHash, 32);


	//cleanup
	delete[] pTempHash;
	delete[] pLastHash;
	//***** DONE BUILDING THE RESPONSE ***//


	//wire the response
	if (encrypted)
		_outputBuffer.ReadFromByte(6);
	else
		_outputBuffer.ReadFromByte(3);
	_outputBuffer.ReadFromBuffer(_pOutputBuffer, 3072);

	//final cleanup
	delete[] _pOutputBuffer;
	_pOutputBuffer = NULL;
	if (!buffer.IgnoreAll()) {
		FATAL("Unable to ignore input buffer");
		return false;
	}

	//signal outbound data
	if (!EnqueueForOutbound()) {
		FATAL("Unable to signal outbound data");
		return false;
	}

	//move to the next stage in the handshake
	_rtmpState = RTMP_STATE_SERVER_RESPONSE_SENT;

	return true;
}