[FFmpeg-soc] [soc]: r4462 - in rtmp: TODO rtmpdec.c

kostya subversion at mplayerhq.hu
Tue Jun 16 18:59:46 CEST 2009


Author: kostya
Date: Tue Jun 16 18:59:46 2009
New Revision: 4462

Log:
Proper RTMP handshake with SHA-256 digesting

Modified:
   rtmp/TODO
   rtmp/rtmpdec.c

Modified: rtmp/TODO
==============================================================================
--- rtmp/TODO	Tue Jun 16 18:52:51 2009	(r4461)
+++ rtmp/TODO	Tue Jun 16 18:59:46 2009	(r4462)
@@ -1,4 +1,3 @@
-Proper handshake
 Proper "connect" call (additional parameters and filename parsing)
 Factorize a lot of FLV demuxer code
 Set correct timestamps

Modified: rtmp/rtmpdec.c
==============================================================================
--- rtmp/rtmpdec.c	Tue Jun 16 18:52:51 2009	(r4461)
+++ rtmp/rtmpdec.c	Tue Jun 16 18:59:46 2009	(r4462)
@@ -24,6 +24,8 @@
 
 #include "libavcodec/bytestream.h"
 #include "libavutil/avstring.h"
+#include "libavutil/lfg.h"
+#include "libavutil/sha.h"
 #include "avformat.h"
 
 #include <unistd.h>
@@ -42,6 +44,28 @@ typedef struct RTMPState {
     char playpath[256];
 } RTMPState;
 
+#define PLAYER_KEY_OPEN_PART_LEN 30
+static const uint8_t rtmp_player_key[] =
+{
+    0x47, 0x65, 0x6E, 0x75, 0x69, 0x6E, 0x65, 0x20, 0x41, 0x64, 0x6F, 0x62, 0x65,
+    0x20, 0x46, 0x6C, 0x61, 0x73, 0x68, 0x20, 0x50, 0x6C, 0x61, 0x79, 0x65, 0x72,
+    0x20, 0x30, 0x30, 0x31, 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E,
+    0x00, 0xD0, 0xD1, 0x02, 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80,
+    0x6F, 0xAB, 0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
+};
+
+#define SERVER_KEY_OPEN_PART_LEN 36
+static const uint8_t rtmp_server_key[] =
+{
+    0x47, 0x65, 0x6E, 0x75, 0x69, 0x6E, 0x65, 0x20, 0x41, 0x64, 0x6F, 0x62, 0x65,
+    0x20, 0x46, 0x6C, 0x61, 0x73, 0x68, 0x20, 0x4D, 0x65, 0x64, 0x69, 0x61, 0x20,
+    0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 001
+
+    0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,
+    0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,
+    0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
+};
+
 static void gen_connect(AVFormatContext *s, RTMPState *rt, const char *proto,
                         const char *host, int port, const char *app)
 {
@@ -142,32 +166,151 @@ static void gen_play(AVFormatContext *s,
     rtmp_packet_destroy(&pkt);
 }
 
+//TODO: Move HMAC code somewhere. Eventually.
+#define HMAC_IPAD_VAL 0x36
+#define HMAC_OPAD_VAL 0x5C
+
+static void rtmp_calc_digest(const uint8_t *src, int len, int gap,
+                             const uint8_t *key, int keylen, uint8_t *dst)
+{
+    struct AVSHA *sha;
+    uint8_t hmac_buf[64+32];
+    int i;
+
+    sha = av_mallocz(av_sha_size);
+
+    memset(hmac_buf, 0, 64);
+    if (keylen < 64)
+        memcpy(hmac_buf, key, keylen);
+    else {
+        av_sha_init(sha, 256);
+        av_sha_update(sha,key, keylen);
+        av_sha_final(sha, hmac_buf);
+    }
+    for (i = 0; i < 64; i++)
+        hmac_buf[i] ^= HMAC_IPAD_VAL;
+
+    av_sha_init(sha, 256);
+    av_sha_update(sha, hmac_buf, 64);
+    if (gap <= 0)
+        av_sha_update(sha, src, len);
+    else { //skip 32 bytes used for storing digest
+        av_sha_update(sha, src, gap);
+        av_sha_update(sha, src + gap + 32, len - gap - 32);
+    }
+    av_sha_final(sha, hmac_buf + 64);
+
+    for (i = 0; i < 64; i++)
+        hmac_buf[i] ^= HMAC_IPAD_VAL ^ HMAC_OPAD_VAL; //reuse XORed key for opad
+    av_sha_init(sha, 256);
+    av_sha_update(sha, hmac_buf, 64+32);
+    av_sha_final(sha, dst);
+
+    av_free(sha);
+}
+
+static int rtmp_handshake_imprint_with_digest(uint8_t *buf)
+{
+    int i, digest_pos = 0;
+
+    for (i = 8; i < 12; i++)
+        digest_pos += buf[i];
+    digest_pos = (digest_pos % 728) + 12;
+
+    rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
+                     rtmp_player_key, PLAYER_KEY_OPEN_PART_LEN,
+                     buf + digest_pos);
+    return digest_pos;
+}
+
+static int rtmp_validate_digest(uint8_t *buf, int off)
+{
+    int i, digest_pos = 0;
+    uint8_t digest[32];
+
+    for (i = 0; i < 4; i++)
+        digest_pos += buf[i + off];
+    digest_pos = (digest_pos % 728) + off + 4;
+
+    rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
+                     rtmp_server_key, SERVER_KEY_OPEN_PART_LEN,
+                     digest);
+    if (!memcmp(digest, buf + digest_pos, 32))
+        return digest_pos;
+    return 0;
+}
+
 static int rtmp_handshake(AVFormatContext *s, RTMPState *rt)
 {
-    uint8_t buf [1+RTMP_HANDSHAKE_PACKET_SIZE];
-    uint8_t buf2[1+RTMP_HANDSHAKE_PACKET_SIZE*2];
+    AVLFG rnd;
+    uint8_t tosend    [RTMP_HANDSHAKE_PACKET_SIZE+1];
+    uint8_t clientdata[RTMP_HANDSHAKE_PACKET_SIZE];
+    uint8_t serverdata[RTMP_HANDSHAKE_PACKET_SIZE+1];
     int i;
+    int server_pos, client_pos;
+    uint8_t digest[32];
 
     av_log(s, AV_LOG_DEBUG, "Handshaking...\n");
 
+    av_lfg_init(&rnd, 0xDEADC0DE);
     // generate handshake packet - 1536 bytes of pseudorandom data
-    buf[0] = 3; //unencrypted data
-    memset(buf+1, 0, RTMP_HANDSHAKE_PACKET_SIZE);
+    tosend[0] = 3; //unencrypted data
+    memset(tosend+1, 0, 4);
     //write client "version"
-    buf[5] = 9;
-    buf[5] = 0;
-    buf[7] = 124;
-    buf[8] = 2;
+    tosend[5] = RTMP_CLIENT_VER1;
+    tosend[6] = RTMP_CLIENT_VER2;
+    tosend[7] = RTMP_CLIENT_VER3;
+    tosend[8] = RTMP_CLIENT_VER4;
+    for (i = 9; i <= RTMP_HANDSHAKE_PACKET_SIZE; i++)
+        tosend[i] = av_lfg_get(&rnd) >> 24;
+    client_pos = rtmp_handshake_imprint_with_digest(tosend + 1);
 
-    url_write(rt->rtmp_hd, buf, RTMP_HANDSHAKE_PACKET_SIZE + 1);
-    i = url_read_complete(rt->rtmp_hd, buf2, RTMP_HANDSHAKE_PACKET_SIZE*2 + 1);
-    if (i != 1 + RTMP_HANDSHAKE_PACKET_SIZE*2 || memcmp(buf + 1, buf2 + 1 + RTMP_HANDSHAKE_PACKET_SIZE, RTMP_HANDSHAKE_PACKET_SIZE)) {
-        av_log(s, AV_LOG_ERROR, "RTMP handshake failed\n");
+    url_write(rt->rtmp_hd, tosend, RTMP_HANDSHAKE_PACKET_SIZE + 1);
+    i = url_read_complete(rt->rtmp_hd, serverdata, RTMP_HANDSHAKE_PACKET_SIZE + 1);
+    if (i != RTMP_HANDSHAKE_PACKET_SIZE + 1) {
+        av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
+        return -1;
+    }
+    i = url_read_complete(rt->rtmp_hd, clientdata, RTMP_HANDSHAKE_PACKET_SIZE);
+    if (i != RTMP_HANDSHAKE_PACKET_SIZE) {
+        av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
+        return -1;
+    }
+
+    av_log(s, AV_LOG_DEBUG, "Server version %d.%d.%d.%d\n",
+           serverdata[5], serverdata[6], serverdata[7], serverdata[8]);
+
+    server_pos = rtmp_validate_digest(serverdata + 1, 772);
+    if (!server_pos) {
+        server_pos = rtmp_validate_digest(serverdata + 1, 8);
+        if (!server_pos) {
+            av_log(s, AV_LOG_ERROR, "Server response validating failed\n");
+            return -1;
+        }
+    }
+
+    rtmp_calc_digest(tosend + 1 + client_pos, 32, 0,
+                     rtmp_server_key, sizeof(rtmp_server_key),
+                     digest);
+    rtmp_calc_digest(clientdata, RTMP_HANDSHAKE_PACKET_SIZE-32, 0,
+                     digest, 32,
+                     digest);
+    if (memcmp(digest, clientdata + RTMP_HANDSHAKE_PACKET_SIZE - 32, 32)) {
+        av_log(s, AV_LOG_ERROR, "Signature mismatch\n");
         return -1;
     }
-    // write reply back to server
-    url_write(rt->rtmp_hd, buf2 + 1, RTMP_HANDSHAKE_PACKET_SIZE);
 
+    for (i = 0; i < RTMP_HANDSHAKE_PACKET_SIZE; i++)
+        tosend[i] = av_lfg_get(&rnd) >> 24;
+    rtmp_calc_digest(serverdata + 1 + server_pos, 32, 0,
+                     rtmp_player_key, sizeof(rtmp_player_key),
+                     digest);
+    rtmp_calc_digest(tosend,  RTMP_HANDSHAKE_PACKET_SIZE - 32, 0,
+                     digest, 32,
+                     tosend + RTMP_HANDSHAKE_PACKET_SIZE - 32);
+
+    // write reply back to server
+    url_write(rt->rtmp_hd, tosend, RTMP_HANDSHAKE_PACKET_SIZE);
     return 0;
 }
 


More information about the FFmpeg-soc mailing list