[rtmpdump] r106 - in trunk: handshake.h rtmp.c rtmp.h

hyc subversion at mplayerhq.hu
Tue Dec 22 01:50:59 CET 2009


Author: hyc
Date: Tue Dec 22 01:50:58 2009
New Revision: 106

Log:
Added server-side handshaking. Will probably split these files later...

Modified:
   trunk/handshake.h
   trunk/rtmp.c
   trunk/rtmp.h

Modified: trunk/handshake.h
==============================================================================
--- trunk/handshake.h	Mon Dec 21 21:50:25 2009	(r105)
+++ trunk/handshake.h	Tue Dec 22 01:50:58 2009	(r106)
@@ -564,3 +564,313 @@ HandShake(RTMP * r, bool FP9HandShake)
   Log(LOGDEBUG, "%s: Handshaking finished....", __FUNCTION__);
   return true;
 }
+
+static bool
+SHandShake(RTMP * r)
+{
+  int i;
+  int dhposClient = 0;
+  int dhposServer = 0;
+  int digestPosServer = 0;
+  RC4_KEY *keyIn = 0;
+  RC4_KEY *keyOut = 0;
+  bool FP9HandShake = false;
+  bool encrypted;
+
+  char clientsig[RTMP_SIG_SIZE];
+  char serverbuf[RTMP_SIG_SIZE + 1], *serversig = serverbuf+1;
+  char type;
+  uint32_t uptime;
+
+  if (ReadN(r, &type, 1) != 1)	/* 0x03 or 0x06 */
+    return false;
+
+  Log(LOGDEBUG, "%s: Type Requested : %02X", __FUNCTION__, type);
+
+  if (type == 3)
+    {
+      encrypted = false;
+      r->Link.protocol = RTMP_PROTOCOL_RTMP;
+    }
+  else if (type == 6 || type == 8)
+    {
+      encrypted = true;
+      FP9HandShake = true;
+      r->Link.protocol = RTMP_PROTOCOL_RTMPE;
+    }
+  else
+    {
+      Log(LOGERROR, "%s: Unknown version %02x",
+	  __FUNCTION__, type);
+      return false;
+    }
+
+  serverbuf[0] = type;
+
+  r->Link.rc4keyIn = r->Link.rc4keyOut = 0;
+
+  uptime = htonl(RTMP_GetTime());
+  memcpy(serversig, &uptime, 4);
+
+  if (FP9HandShake)
+    {
+      /* Server version */
+      serversig[4] = 3;
+      serversig[5] = 5;
+      serversig[6] = 1;
+      serversig[7] = 1;
+    }
+  else
+    {
+      memset(&serversig[4], 0, 4);
+    }
+
+  /* generate random data */
+#ifdef _DEBUG
+  for (i = 8; i < RTMP_SIG_SIZE; i++)
+    serversig[i] = 0;
+#else
+  for (i = 8; i < RTMP_SIG_SIZE; i++)
+    serversig[i] = (char) (rand() % 256);
+#endif
+
+  /* set handshake digest */
+  if (FP9HandShake)
+    {
+      if (encrypted)
+	{
+	  /* generate Diffie-Hellmann parameters */
+	  r->Link.dh = DHInit(1024);
+	  if (!r->Link.dh)
+	    {
+	      Log(LOGERROR, "%s: Couldn't initialize Diffie-Hellmann!",
+		  __FUNCTION__);
+	      return false;
+	    }
+
+	  dhposServer = GetDHOffset2(serversig, RTMP_SIG_SIZE);
+	  Log(LOGDEBUG, "%s: DH pubkey position: %d", __FUNCTION__, dhposServer);
+
+	  if (!DHGenerateKey(r->Link.dh))
+	    {
+	      Log(LOGERROR, "%s: Couldn't generate Diffie-Hellmann public key!",
+		  __FUNCTION__);
+	      return false;
+	    }
+
+	  if (!DHGetPublicKey
+	      (r->Link.dh, (uint8_t *) &serversig[dhposServer], 128))
+	    {
+	      Log(LOGERROR, "%s: Couldn't write public key!", __FUNCTION__);
+	      return false;
+	    }
+	}
+
+      digestPosServer = GetDigestOffset2(serversig, RTMP_SIG_SIZE);	/* reuse this value in verification */
+      Log(LOGDEBUG, "%s: Client digest offset: %d", __FUNCTION__,
+	  digestPosServer);
+
+      CalculateDigest(digestPosServer, serversig, GenuineFMSKey, 36,
+		      &serversig[digestPosServer]);
+
+      Log(LOGDEBUG, "%s: Initial server digest: ", __FUNCTION__);
+      LogHex(LOGDEBUG, (char *) serversig + digestPosServer,
+	     SHA256_DIGEST_LENGTH);
+    }
+
+#ifdef _DEBUG
+  Log(LOGDEBUG, "Serversig: ");
+  LogHex(LOGDEBUG, serversig, RTMP_SIG_SIZE);
+#endif
+
+  if (!WriteN(r, serverbuf, RTMP_SIG_SIZE + 1))
+    return false;
+
+  if (ReadN(r, clientsig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
+    return false;
+
+  /* decode client response */
+  memcpy(&uptime, clientsig, 4);
+  uptime = ntohl(uptime);
+
+  Log(LOGDEBUG, "%s: Client Uptime : %d", __FUNCTION__, uptime);
+  Log(LOGDEBUG, "%s: Player Version: %d.%d.%d.%d", __FUNCTION__, clientsig[4],
+      clientsig[5], clientsig[6], clientsig[7]);
+
+#ifdef _DEBUG
+  Log(LOGDEBUG, "Client signature:");
+  LogHex(LOGDEBUG, clientsig, RTMP_SIG_SIZE);
+#endif
+
+  if (FP9HandShake)
+    {
+      /* we have to use this signature now to find the correct algorithms for getting the digest and DH positions */
+      int digestPosClient = GetDigestOffset1(clientsig, RTMP_SIG_SIZE);
+
+      if (!VerifyDigest(digestPosClient, clientsig, GenuineFPKey, 30))
+	{
+	  Log(LOGWARNING, "Trying different position for client digest!\n");
+	  digestPosClient = GetDigestOffset2(clientsig, RTMP_SIG_SIZE);
+
+	  if (!VerifyDigest(digestPosServer, clientsig, GenuineFPKey, 30))
+	    {
+	      Log(LOGERROR, "Couldn't verify the client digest\n");	/* continuing anyway will probably fail */
+	      return false;
+	    }
+	  dhposClient = GetDHOffset2(clientsig, RTMP_SIG_SIZE);
+	}
+      else
+        {
+	  dhposClient = GetDHOffset1(clientsig, RTMP_SIG_SIZE);
+        }
+
+      Log(LOGDEBUG, "%s: Client DH public key offset: %d", __FUNCTION__,
+	  dhposClient);
+
+      /* generate SWFVerification token (SHA256 HMAC hash of decompressed SWF, key are the last 32 bytes of the server handshake) */
+      if (r->Link.SWFHash.av_len)
+	{
+	  const char swfVerify[] = { 0x01, 0x01 };
+
+	  memcpy(r->Link.SWFVerificationResponse, swfVerify, 2);
+	  AMF_EncodeInt32(&r->Link.SWFVerificationResponse[2], r->Link.SWFSize);
+	  AMF_EncodeInt32(&r->Link.SWFVerificationResponse[6], r->Link.SWFSize);
+	  HMACsha256(r->Link.SWFHash.av_val, SHA256_DIGEST_LENGTH,
+		     &serversig[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH],
+		     SHA256_DIGEST_LENGTH, &r->Link.SWFVerificationResponse[10]);
+	}
+
+      /* do Diffie-Hellmann Key exchange for encrypted RTMP */
+      if (encrypted)
+	{
+	  /* compute secret key */
+	  uint8_t secretKey[128] = { 0 };
+
+	  int len =
+	    DHComputeSharedSecretKey(r->Link.dh,
+				     (uint8_t *) &clientsig[dhposClient], 128,
+				     secretKey);
+	  if (len < 0)
+	    {
+	      Log(LOGDEBUG, "%s: Wrong secret key position!", __FUNCTION__);
+	      return false;
+	    }
+
+	  Log(LOGDEBUG, "%s: Secret key: ", __FUNCTION__);
+	  LogHex(LOGDEBUG, (char *) secretKey, 128);
+
+	  InitRC4Encryption(secretKey,
+			    (uint8_t *) &clientsig[dhposClient],
+			    (uint8_t *) &serversig[dhposServer],
+			    &keyIn, &keyOut);
+	}
+
+
+      /* calculate response now */
+      char digestResp[SHA256_DIGEST_LENGTH];
+      char *signatureResp = clientsig+RTMP_SIG_SIZE-SHA256_DIGEST_LENGTH;
+
+      HMACsha256(&clientsig[digestPosClient], SHA256_DIGEST_LENGTH,
+		 GenuineFMSKey, sizeof(GenuineFMSKey), digestResp);
+      HMACsha256(clientsig, RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH, digestResp,
+		 SHA256_DIGEST_LENGTH, signatureResp);
+
+      /* some info output */
+      Log(LOGDEBUG,
+	  "%s: Calculated digest key from secure key and server digest: ",
+	  __FUNCTION__);
+      LogHex(LOGDEBUG, digestResp, SHA256_DIGEST_LENGTH);
+
+      Log(LOGDEBUG, "%s: Server signature calculated:", __FUNCTION__);
+      LogHex(LOGDEBUG, signatureResp, SHA256_DIGEST_LENGTH);
+    }
+  else
+    {
+      uptime = htonl(RTMP_GetTime());
+      memcpy(clientsig+4, &uptime, 4);
+    }
+
+#ifdef _DEBUG
+  Log(LOGDEBUG, "%s: Sending handshake response: ",
+    __FUNCTION__);
+  LogHex(LOGDEBUG, clientsig, RTMP_SIG_SIZE);
+#endif
+  if (!WriteN(r, clientsig, RTMP_SIG_SIZE))
+    return false;
+
+  /* 2nd part of handshake */
+  if (ReadN(r, clientsig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
+    return false;
+
+#ifdef _DEBUG
+  Log(LOGDEBUG, "%s: 2nd handshake: ", __FUNCTION__);
+  LogHex(LOGDEBUG, clientsig, RTMP_SIG_SIZE);
+#endif
+
+  if (FP9HandShake)
+    {
+      char signature[SHA256_DIGEST_LENGTH];
+      char digest[SHA256_DIGEST_LENGTH];
+
+      Log(LOGDEBUG, "%s: Client sent signature:", __FUNCTION__);
+      LogHex(LOGDEBUG, &clientsig[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH],
+	     SHA256_DIGEST_LENGTH);
+
+      /* verify client response */
+      HMACsha256(&clientsig[digestPosServer], SHA256_DIGEST_LENGTH,
+		 GenuineFPKey, sizeof(GenuineFPKey), digest);
+      HMACsha256(clientsig, RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH, digest,
+		 SHA256_DIGEST_LENGTH, signature);
+
+      /* show some information */
+      Log(LOGDEBUG, "%s: Digest key: ", __FUNCTION__);
+      LogHex(LOGDEBUG, digest, SHA256_DIGEST_LENGTH);
+
+      Log(LOGDEBUG, "%s: Signature calculated:", __FUNCTION__);
+      LogHex(LOGDEBUG, signature, SHA256_DIGEST_LENGTH);
+      if (memcmp
+	  (signature, &clientsig[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH],
+	   SHA256_DIGEST_LENGTH) != 0)
+	{
+	  Log(LOGWARNING, "%s: Client not genuine Adobe!", __FUNCTION__);
+	  return false;
+	}
+      else
+	{
+	  Log(LOGDEBUG, "%s: Genuine Adobe Flash Player", __FUNCTION__);
+	}
+
+      if (encrypted)
+	{
+	  /* set keys for encryption from now on */
+	  r->Link.rc4keyIn = keyIn;
+	  r->Link.rc4keyOut = keyOut;
+
+	  char buff[RTMP_SIG_SIZE];
+
+	  /* update the keystreams */
+	  if (r->Link.rc4keyIn)
+	    {
+	      RC4(r->Link.rc4keyIn, RTMP_SIG_SIZE, (uint8_t *) buff,
+		  (uint8_t *) buff);
+	    }
+
+	  if (r->Link.rc4keyOut)
+	    {
+	      RC4(r->Link.rc4keyOut, RTMP_SIG_SIZE, (uint8_t *) buff,
+		  (uint8_t *) buff);
+	    }
+	}
+    }
+  else
+    {
+      if (memcmp(serversig, clientsig, RTMP_SIG_SIZE) != 0)
+	{
+	  Log(LOGWARNING, "%s: client signature does not match!",
+	      __FUNCTION__);
+	}
+    }
+
+  Log(LOGDEBUG, "%s: Handshaking finished....", __FUNCTION__);
+  return true;
+}

Modified: trunk/rtmp.c
==============================================================================
--- trunk/rtmp.c	Mon Dec 21 21:50:25 2009	(r105)
+++ trunk/rtmp.c	Tue Dec 22 01:50:58 2009	(r106)
@@ -1050,7 +1050,7 @@ RTMP_SendPause(RTMP * r, bool DoPause, d
   RTMPPacket packet;
   char pbuf[256];
 
-  packet.m_nChannel = 0x08;	// video channel 
+  packet.m_nChannel = 0x08;	// video channel
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
   packet.m_packetType = 0x14;	// invoke
   packet.m_nInfoField1 = 0;
@@ -1078,7 +1078,7 @@ SendSeek(RTMP * r, double dTime)
   RTMPPacket packet;
   char pbuf[256];
 
-  packet.m_nChannel = 0x08;	// video channel 
+  packet.m_nChannel = 0x08;	// video channel
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
   packet.m_packetType = 0x14;	// invoke
   packet.m_nInfoField1 = 0;
@@ -1163,7 +1163,7 @@ SendCheckBW(RTMP * r)
 
   packet.m_nBodySize = enc - packet.m_body;
 
-  // triggers _onbwcheck and eventually results in _onbwdone 
+  // triggers _onbwcheck and eventually results in _onbwdone
   return SendRTMP(r, &packet, false);
 }
 
@@ -1844,7 +1844,7 @@ ReadPacket(RTMP * r, RTMPPacket * packet
     {
       packet->m_nInfoField1 = AMF_DecodeInt24(header);
 
-      //Log(LOGDEBUG, "%s, reading RTMP packet chunk on channel %x, headersz %i, timestamp %i, abs timestamp %i", __FUNCTION__, packet.m_nChannel, nSize, packet.m_nInfoField1, packet.m_hasAbsTimestamp); 
+      //Log(LOGDEBUG, "%s, reading RTMP packet chunk on channel %x, headersz %i, timestamp %i, abs timestamp %i", __FUNCTION__, packet.m_nChannel, nSize, packet.m_nInfoField1, packet.m_hasAbsTimestamp);
 
       if (nSize >= 6)
 	{
@@ -1896,9 +1896,9 @@ ReadPacket(RTMP * r, RTMPPacket * packet
     {
       packet->m_nTimeStamp = packet->m_nInfoField1;
 
-      // make packet's timestamp absolute 
+      // make packet's timestamp absolute
       if (!packet->m_hasAbsTimestamp)
-	packet->m_nTimeStamp += r->m_channelTimestamp[packet->m_nChannel];	// timestamps seem to be always relative!! 
+	packet->m_nTimeStamp += r->m_channelTimestamp[packet->m_nChannel];	// timestamps seem to be always relative!!
 
       r->m_channelTimestamp[packet->m_nChannel] = packet->m_nTimeStamp;
 
@@ -1963,25 +1963,25 @@ static bool
 HandShake(RTMP * r, bool FP9HandShake)
 {
   int i;
-  char clientsig[RTMP_SIG_SIZE + 1];
+  char clientbuf[RTMP_SIG_SIZE + 1], *clientsig = clientbuf+1;
   char serversig[RTMP_SIG_SIZE];
 
-  clientsig[0] = 0x03;		// not encrypted
+  clientbuf[0] = 0x03;		// not encrypted
 
   uint32_t uptime = htonl(RTMP_GetTime());
-  memcpy(clientsig + 1, &uptime, 4);
+  memcpy(clientsig, &uptime, 4);
 
-  memset(&clientsig[5], 0, 4);
+  memset(&clientsig[4], 0, 4);
 
 #ifdef _DEBUG
-  for (i = 9; i < RTMP_SIG_SIZE; i++)
+  for (i = 8; i < RTMP_SIG_SIZE; i++)
     clientsig[i] = 0xff;
 #else
-  for (i = 9; i < RTMP_SIG_SIZE; i++)
+  for (i = 8; i < RTMP_SIG_SIZE; i++)
     clientsig[i] = (char) (rand() % 256);
 #endif
 
-  if (!WriteN(r, clientsig, RTMP_SIG_SIZE + 1))
+  if (!WriteN(r, clientbuf, RTMP_SIG_SIZE + 1))
     return false;
 
   char type;
@@ -1990,9 +1990,9 @@ HandShake(RTMP * r, bool FP9HandShake)
 
   Log(LOGDEBUG, "%s: Type Answer   : %02X", __FUNCTION__, type);
 
-  if (type != clientsig[0])
+  if (type != clientbuf[0])
     Log(LOGWARNING, "%s: Type mismatch: client sent %d, server answered %d",
-	__FUNCTION__, clientsig[0], type);
+	__FUNCTION__, clientbuf[0], type);
 
   if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
     return false;
@@ -2011,11 +2011,72 @@ HandShake(RTMP * r, bool FP9HandShake)
   if (!WriteN(r, serversig, RTMP_SIG_SIZE))
     return false;
 
-  char resp[RTMP_SIG_SIZE];
-  if (ReadN(r, resp, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
+  if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
     return false;
 
-  bool bMatch = (memcmp(resp, clientsig + 1, RTMP_SIG_SIZE) == 0);
+  bool bMatch = (memcmp(serversig, clientsig, RTMP_SIG_SIZE) == 0);
+  if (!bMatch)
+    {
+      Log(LOGWARNING, "%s, client signature does not match!", __FUNCTION__);
+    }
+  return true;
+}
+
+static bool
+SHandShake(RTMP * r)
+{
+  int i;
+  char serverbuf[RTMP_SIG_SIZE + 1], *serversig = serverbuf+1;
+  char clientsig[RTMP_SIG_SIZE];
+  uint32_t uptime;
+
+  if (ReadN(r, serverbuf, 1) != 1)	// 0x03 or 0x06
+    return false;
+
+  Log(LOGDEBUG, "%s: Type Request  : %02X", __FUNCTION__, serverbuf[0]);
+
+  if (serverbuf[0] != 3)
+    {
+      Log(LOGERROR, "%s: Type unknown: client sent %02X",
+	  __FUNCTION__, serverbuf[0]);
+      return false;
+    }
+
+  uptime = htonl(RTMP_GetTime());
+  memcpy(serversig, &uptime, 4);
+
+  memset(&serversig[4], 0, 4);
+#ifdef _DEBUG
+  for (i = 8; i < RTMP_SIG_SIZE; i++)
+    serversig[i] = 0xff;
+#else
+  for (i = 8; i < RTMP_SIG_SIZE; i++)
+    serversig[i] = (char) (rand() % 256);
+#endif
+
+  if (!WriteN(r, serverbuf, RTMP_SIG_SIZE + 1))
+    return false;
+
+  if (ReadN(r, clientsig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
+    return false;
+
+  // decode client response
+
+  memcpy(&uptime, clientsig, 4);
+  uptime = ntohl(uptime);
+
+  Log(LOGDEBUG, "%s: Client Uptime : %d", __FUNCTION__, uptime);
+  Log(LOGDEBUG, "%s: Player Version: %d.%d.%d.%d", __FUNCTION__, clientsig[4],
+      clientsig[5], clientsig[6], clientsig[7]);
+
+  // 2nd part of handshake
+  if (!WriteN(r, clientsig, RTMP_SIG_SIZE))
+    return false;
+
+  if (ReadN(r, clientsig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
+    return false;
+
+  bool bMatch = (memcmp(serversig, clientsig, RTMP_SIG_SIZE) == 0);
   if (!bMatch)
     {
       Log(LOGWARNING, "%s, client signature does not match!", __FUNCTION__);
@@ -2115,6 +2176,12 @@ SendRTMP(RTMP * r, RTMPPacket * packet, 
   return true;
 }
 
+bool
+RTMP_Serve(RTMP *r)
+{
+  return SHandShake(r);
+}
+
 void
 RTMP_Close(RTMP * r)
 {

Modified: trunk/rtmp.h
==============================================================================
--- trunk/rtmp.h	Mon Dec 21 21:50:25 2009	(r105)
+++ trunk/rtmp.h	Tue Dec 22 01:50:58 2009	(r106)
@@ -75,7 +75,7 @@ typedef struct RTMPPacket
   BYTE m_packetType;
   int m_nChannel;
   int32_t m_nInfoField1;	// 3 first bytes
-  int32_t m_nInfoField2;	// last 4 bytes in a long header, absolute timestamp for long headers, relative timestamp for short headers 
+  int32_t m_nInfoField2;	// last 4 bytes in a long header, absolute timestamp for long headers, relative timestamp for short headers
   bool m_hasAbsTimestamp;	// timestamp absolute or relative?
   uint32_t m_nTimeStamp;	// absolute timestamp
   uint32_t m_nBodySize;
@@ -182,6 +182,7 @@ void RTMP_SetupStream(RTMP *r, int proto
 		      uint32_t dLength, bool bLiveStream, long int timeout);
 
 bool RTMP_Connect(RTMP *r);
+bool RTMP_Serve(RTMP *r);
 
 bool RTMP_IsConnected(RTMP *r);
 bool RTMP_IsTimedout(RTMP *r);


More information about the rtmpdump mailing list