Browse code

RTMPE protocol support

This adds two protocols, but one of them is an internal implementation
detail just used as an abstraction layer/generalization in the code. The
RTMPE protocol implementation uses ffrtmpcrypt:// as an alternative to the
tcp:// protocol. This allows moving most of the lower level logic out
from the higher level generic rtmp code.

Signed-off-by: Martin Storsjö <martin@martin.st>

Samuel Pitoiset authored on 2012/07/19 21:13:58
Showing 12 changed files
... ...
@@ -39,6 +39,7 @@ version <next>:
39 39
 - RTMPTS protocol support
40 40
 - JPEG 2000 encoding support through OpenJPEG
41 41
 - G.723.1 demuxer and decoder
42
+- RTMPE protocol support
42 43
 
43 44
 
44 45
 version 0.8:
... ...
@@ -950,6 +950,7 @@ CONFIG_LIST="
950 950
     fastdiv
951 951
     fft
952 952
     frei0r
953
+    gcrypt
953 954
     gnutls
954 955
     gpl
955 956
     gray
... ...
@@ -982,6 +983,7 @@ CONFIG_LIST="
982 982
     mdct
983 983
     memalign_hack
984 984
     mpegaudiodsp
985
+    nettle
985 986
     network
986 987
     nonfree
987 988
     openssl
... ...
@@ -1543,6 +1545,9 @@ vfwcap_indev_extralibs="-lavicap32"
1543 1543
 x11_grab_device_indev_deps="x11grab XShmCreateImage"
1544 1544
 
1545 1545
 # protocols
1546
+ffrtmpcrypt_protocol_deps="!librtmp_protocol"
1547
+ffrtmpcrypt_protocol_deps_any="gcrypt nettle openssl"
1548
+ffrtmpcrypt_protocol_select="tcp_protocol"
1546 1549
 ffrtmphttp_protocol_deps="!librtmp_protocol"
1547 1550
 ffrtmphttp_protocol_select="http_protocol"
1548 1551
 gopher_protocol_deps="network"
... ...
@@ -1560,6 +1565,7 @@ mmsh_protocol_select="http_protocol"
1560 1560
 mmst_protocol_deps="network"
1561 1561
 rtmp_protocol_deps="!librtmp_protocol"
1562 1562
 rtmp_protocol_select="tcp_protocol"
1563
+rtmpe_protocol_select="ffrtmpcrypt_protocol"
1563 1564
 rtmps_protocol_deps="!librtmp_protocol"
1564 1565
 rtmps_protocol_select="tls_protocol"
1565 1566
 rtmpt_protocol_select="ffrtmphttp_protocol"
... ...
@@ -3014,6 +3020,11 @@ enabled openssl    && { check_lib openssl/ssl.h SSL_library_init -lssl -lcrypto
3014 3014
                         check_lib openssl/ssl.h SSL_library_init -lssl -lcrypto -lws2_32 -lgdi32 ||
3015 3015
                         die "ERROR: openssl not found"; }
3016 3016
 
3017
+if enabled gnutls; then
3018
+    { check_lib nettle/bignum.h nettle_mpz_get_str_256 -lnettle -lhogweed -lgmp && enable nettle; } ||
3019
+    { check_lib gcrypt.h gcry_mpi_new -lgcrypt && enable gcrypt; }
3020
+fi
3021
+
3017 3022
 # libdc1394 check
3018 3023
 if enabled libdc1394; then
3019 3024
     { check_lib dc1394/dc1394.h dc1394_new -ldc1394 -lraw1394 &&
... ...
@@ -844,7 +844,7 @@ performance on systems without hardware floating point support).
844 844
 @item MMST         @tab X
845 845
 @item pipe         @tab X
846 846
 @item RTMP         @tab X
847
-@item RTMPE        @tab E
847
+@item RTMPE        @tab X
848 848
 @item RTMPS        @tab X
849 849
 @item RTMPT        @tab X
850 850
 @item RTMPTE       @tab E
... ...
@@ -247,6 +247,15 @@ For example to read with @command{avplay} a multimedia resource named
247 247
 avplay rtmp://myserver/vod/sample
248 248
 @end example
249 249
 
250
+@section rtmpe
251
+
252
+Encrypted Real-Time Messaging Protocol.
253
+
254
+The Encrypted Real-Time Messaging Protocol (RTMPE) is used for
255
+streaming multimedia content within standard cryptographic primitives,
256
+consisting of Diffie-Hellman key exchange and HMACSHA256, generating
257
+a pair of RC4 keys.
258
+
250 259
 @section rtmps
251 260
 
252 261
 Real-Time Messaging Protocol over a secure SSL connection.
... ...
@@ -341,6 +341,7 @@ OBJS-$(CONFIG_LIBRTMP)                   += librtmp.o
341 341
 OBJS-$(CONFIG_APPLEHTTP_PROTOCOL)        += hlsproto.o
342 342
 OBJS-$(CONFIG_CONCAT_PROTOCOL)           += concat.o
343 343
 OBJS-$(CONFIG_CRYPTO_PROTOCOL)           += crypto.o
344
+OBJS-$(CONFIG_FFRTMPCRYPT_PROTOCOL)      += rtmpcrypt.o rtmpdh.o
344 345
 OBJS-$(CONFIG_FFRTMPHTTP_PROTOCOL)       += rtmphttp.o
345 346
 OBJS-$(CONFIG_FILE_PROTOCOL)             += file.o
346 347
 OBJS-$(CONFIG_GOPHER_PROTOCOL)           += gopher.o
... ...
@@ -353,6 +354,7 @@ OBJS-$(CONFIG_MMST_PROTOCOL)             += mmst.o mms.o asf.o
353 353
 OBJS-$(CONFIG_MD5_PROTOCOL)              += md5proto.o
354 354
 OBJS-$(CONFIG_PIPE_PROTOCOL)             += file.o
355 355
 OBJS-$(CONFIG_RTMP_PROTOCOL)             += rtmpproto.o rtmppkt.o
356
+OBJS-$(CONFIG_RTMPE_PROTOCOL)            += rtmpproto.o rtmppkt.o
356 357
 OBJS-$(CONFIG_RTMPS_PROTOCOL)            += rtmpproto.o rtmppkt.o
357 358
 OBJS-$(CONFIG_RTMPT_PROTOCOL)            += rtmpproto.o rtmppkt.o
358 359
 OBJS-$(CONFIG_RTMPTS_PROTOCOL)           += rtmpproto.o rtmppkt.o
... ...
@@ -247,6 +247,7 @@ void av_register_all(void)
247 247
 #endif
248 248
     REGISTER_PROTOCOL (CONCAT, concat);
249 249
     REGISTER_PROTOCOL (CRYPTO, crypto);
250
+    REGISTER_PROTOCOL (FFRTMPCRYPT, ffrtmpcrypt);
250 251
     REGISTER_PROTOCOL (FFRTMPHTTP, ffrtmphttp);
251 252
     REGISTER_PROTOCOL (FILE, file);
252 253
     REGISTER_PROTOCOL (GOPHER, gopher);
... ...
@@ -259,6 +260,7 @@ void av_register_all(void)
259 259
     REGISTER_PROTOCOL (MD5,  md5);
260 260
     REGISTER_PROTOCOL (PIPE, pipe);
261 261
     REGISTER_PROTOCOL (RTMP, rtmp);
262
+    REGISTER_PROTOCOL (RTMPE, rtmpe);
262 263
     REGISTER_PROTOCOL (RTMPS, rtmps);
263 264
     REGISTER_PROTOCOL (RTMPT, rtmpt);
264 265
     REGISTER_PROTOCOL (RTMPTS, rtmpts);
265 266
new file mode 100644
... ...
@@ -0,0 +1,309 @@
0
+/*
1
+ * RTMPE network protocol
2
+ * Copyright (c) 2012 Samuel Pitoiset
3
+ *
4
+ * This file is part of Libav.
5
+ *
6
+ * Libav is free software; you can redistribute it and/or
7
+ * modify it under the terms of the GNU Lesser General Public
8
+ * License as published by the Free Software Foundation; either
9
+ * version 2.1 of the License, or (at your option) any later version.
10
+ *
11
+ * Libav is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
+ * Lesser General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Lesser General Public
17
+ * License along with Libav; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+ */
20
+
21
+/**
22
+ * @file
23
+ * RTMPE protocol
24
+ */
25
+
26
+#include "libavutil/blowfish.h"
27
+#include "libavutil/intreadwrite.h"
28
+#include "libavutil/rc4.h"
29
+#include "libavutil/xtea.h"
30
+
31
+#include "internal.h"
32
+#include "rtmp.h"
33
+#include "rtmpdh.h"
34
+#include "rtmpcrypt.h"
35
+#include "url.h"
36
+
37
+/* protocol handler context */
38
+typedef struct RTMPEContext {
39
+    URLContext   *stream;            ///< TCP stream
40
+    FF_DH        *dh;                ///< Diffie-Hellman context
41
+    struct AVRC4 key_in;             ///< RC4 key used for decrypt data
42
+    struct AVRC4 key_out;            ///< RC4 key used for encrypt data
43
+    int          handshaked;         ///< flag indicating when the handshake is performed
44
+} RTMPEContext;
45
+
46
+static const uint8_t rtmpe8_keys[16][16] = {
47
+    { 0xbf, 0xf0, 0x34, 0xb2, 0x11, 0xd9, 0x08, 0x1f,
48
+      0xcc, 0xdf, 0xb7, 0x95, 0x74, 0x8d, 0xe7, 0x32 },
49
+    { 0x08, 0x6a, 0x5e, 0xb6, 0x17, 0x43, 0x09, 0x0e,
50
+      0x6e, 0xf0, 0x5a, 0xb8, 0xfe, 0x5a, 0x39, 0xe2 },
51
+    { 0x7b, 0x10, 0x95, 0x6f, 0x76, 0xce, 0x05, 0x21,
52
+      0x23, 0x88, 0xa7, 0x3a, 0x44, 0x01, 0x49, 0xa1 },
53
+    { 0xa9, 0x43, 0xf3, 0x17, 0xeb, 0xf1, 0x1b, 0xb2,
54
+      0xa6, 0x91, 0xa5, 0xee, 0x17, 0xf3, 0x63, 0x39 },
55
+    { 0x7a, 0x30, 0xe0, 0x0a, 0xb5, 0x29, 0xe2, 0x2c,
56
+      0xa0, 0x87, 0xae, 0xa5, 0xc0, 0xcb, 0x79, 0xac },
57
+    { 0xbd, 0xce, 0x0c, 0x23, 0x2f, 0xeb, 0xde, 0xff,
58
+      0x1c, 0xfa, 0xae, 0x16, 0x11, 0x23, 0x23, 0x9d },
59
+    { 0x55, 0xdd, 0x3f, 0x7b, 0x77, 0xe7, 0xe6, 0x2e,
60
+      0x9b, 0xb8, 0xc4, 0x99, 0xc9, 0x48, 0x1e, 0xe4 },
61
+    { 0x40, 0x7b, 0xb6, 0xb4, 0x71, 0xe8, 0x91, 0x36,
62
+      0xa7, 0xae, 0xbf, 0x55, 0xca, 0x33, 0xb8, 0x39 },
63
+    { 0xfc, 0xf6, 0xbd, 0xc3, 0xb6, 0x3c, 0x36, 0x97,
64
+      0x7c, 0xe4, 0xf8, 0x25, 0x04, 0xd9, 0x59, 0xb2 },
65
+    { 0x28, 0xe0, 0x91, 0xfd, 0x41, 0x95, 0x4c, 0x4c,
66
+      0x7f, 0xb7, 0xdb, 0x00, 0xe3, 0xa0, 0x66, 0xf8 },
67
+    { 0x57, 0x84, 0x5b, 0x76, 0x4f, 0x25, 0x1b, 0x03,
68
+      0x46, 0xd4, 0x5b, 0xcd, 0xa2, 0xc3, 0x0d, 0x29 },
69
+    { 0x0a, 0xcc, 0xee, 0xf8, 0xda, 0x55, 0xb5, 0x46,
70
+      0x03, 0x47, 0x34, 0x52, 0x58, 0x63, 0x71, 0x3b },
71
+    { 0xb8, 0x20, 0x75, 0xdc, 0xa7, 0x5f, 0x1f, 0xee,
72
+      0xd8, 0x42, 0x68, 0xe8, 0xa7, 0x2a, 0x44, 0xcc },
73
+    { 0x07, 0xcf, 0x6e, 0x9e, 0xa1, 0x6d, 0x7b, 0x25,
74
+      0x9f, 0xa7, 0xae, 0x6c, 0xd9, 0x2f, 0x56, 0x29 },
75
+    { 0xfe, 0xb1, 0xea, 0xe4, 0x8c, 0x8c, 0x3c, 0xe1,
76
+      0x4e, 0x00, 0x64, 0xa7, 0x6a, 0x38, 0x7c, 0x2a },
77
+    { 0x89, 0x3a, 0x94, 0x27, 0xcc, 0x30, 0x13, 0xa2,
78
+      0xf1, 0x06, 0x38, 0x5b, 0xa8, 0x29, 0xf9, 0x27 }
79
+};
80
+
81
+static const uint8_t rtmpe9_keys[16][24] = {
82
+    { 0x79, 0x34, 0x77, 0x4c, 0x67, 0xd1, 0x38, 0x3a, 0xdf, 0xb3, 0x56, 0xbe,
83
+      0x8b, 0x7b, 0xd0, 0x24, 0x38, 0xe0, 0x73, 0x58, 0x41, 0x5d, 0x69, 0x67, },
84
+    { 0x46, 0xf6, 0xb4, 0xcc, 0x01, 0x93, 0xe3, 0xa1, 0x9e, 0x7d, 0x3c, 0x65,
85
+      0x55, 0x86, 0xfd, 0x09, 0x8f, 0xf7, 0xb3, 0xc4, 0x6f, 0x41, 0xca, 0x5c, },
86
+    { 0x1a, 0xe7, 0xe2, 0xf3, 0xf9, 0x14, 0x79, 0x94, 0xc0, 0xd3, 0x97, 0x43,
87
+      0x08, 0x7b, 0xb3, 0x84, 0x43, 0x2f, 0x9d, 0x84, 0x3f, 0x21, 0x01, 0x9b, },
88
+    { 0xd3, 0xe3, 0x54, 0xb0, 0xf7, 0x1d, 0xf6, 0x2b, 0x5a, 0x43, 0x4d, 0x04,
89
+      0x83, 0x64, 0x3e, 0x0d, 0x59, 0x2f, 0x61, 0xcb, 0xb1, 0x6a, 0x59, 0x0d, },
90
+    { 0xc8, 0xc1, 0xe9, 0xb8, 0x16, 0x56, 0x99, 0x21, 0x7b, 0x5b, 0x36, 0xb7,
91
+      0xb5, 0x9b, 0xdf, 0x06, 0x49, 0x2c, 0x97, 0xf5, 0x95, 0x48, 0x85, 0x7e, },
92
+    { 0xeb, 0xe5, 0xe6, 0x2e, 0xa4, 0xba, 0xd4, 0x2c, 0xf2, 0x16, 0xe0, 0x8f,
93
+      0x66, 0x23, 0xa9, 0x43, 0x41, 0xce, 0x38, 0x14, 0x84, 0x95, 0x00, 0x53, },
94
+    { 0x66, 0xdb, 0x90, 0xf0, 0x3b, 0x4f, 0xf5, 0x6f, 0xe4, 0x9c, 0x20, 0x89,
95
+      0x35, 0x5e, 0xd2, 0xb2, 0xc3, 0x9e, 0x9f, 0x7f, 0x63, 0xb2, 0x28, 0x81, },
96
+    { 0xbb, 0x20, 0xac, 0xed, 0x2a, 0x04, 0x6a, 0x19, 0x94, 0x98, 0x9b, 0xc8,
97
+      0xff, 0xcd, 0x93, 0xef, 0xc6, 0x0d, 0x56, 0xa7, 0xeb, 0x13, 0xd9, 0x30, },
98
+    { 0xbc, 0xf2, 0x43, 0x82, 0x09, 0x40, 0x8a, 0x87, 0x25, 0x43, 0x6d, 0xe6,
99
+      0xbb, 0xa4, 0xb9, 0x44, 0x58, 0x3f, 0x21, 0x7c, 0x99, 0xbb, 0x3f, 0x24, },
100
+    { 0xec, 0x1a, 0xaa, 0xcd, 0xce, 0xbd, 0x53, 0x11, 0xd2, 0xfb, 0x83, 0xb6,
101
+      0xc3, 0xba, 0xab, 0x4f, 0x62, 0x79, 0xe8, 0x65, 0xa9, 0x92, 0x28, 0x76, },
102
+    { 0xc6, 0x0c, 0x30, 0x03, 0x91, 0x18, 0x2d, 0x7b, 0x79, 0xda, 0xe1, 0xd5,
103
+      0x64, 0x77, 0x9a, 0x12, 0xc5, 0xb1, 0xd7, 0x91, 0x4f, 0x96, 0x4c, 0xa3, },
104
+    { 0xd7, 0x7c, 0x2a, 0xbf, 0xa6, 0xe7, 0x85, 0x7c, 0x45, 0xad, 0xff, 0x12,
105
+      0x94, 0xd8, 0xde, 0xa4, 0x5c, 0x3d, 0x79, 0xa4, 0x44, 0x02, 0x5d, 0x22, },
106
+    { 0x16, 0x19, 0x0d, 0x81, 0x6a, 0x4c, 0xc7, 0xf8, 0xb8, 0xf9, 0x4e, 0xcd,
107
+      0x2c, 0x9e, 0x90, 0x84, 0xb2, 0x08, 0x25, 0x60, 0xe1, 0x1e, 0xae, 0x18, },
108
+    { 0xe9, 0x7c, 0x58, 0x26, 0x1b, 0x51, 0x9e, 0x49, 0x82, 0x60, 0x61, 0xfc,
109
+      0xa0, 0xa0, 0x1b, 0xcd, 0xf5, 0x05, 0xd6, 0xa6, 0x6d, 0x07, 0x88, 0xa3, },
110
+    { 0x2b, 0x97, 0x11, 0x8b, 0xd9, 0x4e, 0xd9, 0xdf, 0x20, 0xe3, 0x9c, 0x10,
111
+      0xe6, 0xa1, 0x35, 0x21, 0x11, 0xf9, 0x13, 0x0d, 0x0b, 0x24, 0x65, 0xb2, },
112
+    { 0x53, 0x6a, 0x4c, 0x54, 0xac, 0x8b, 0x9b, 0xb8, 0x97, 0x29, 0xfc, 0x60,
113
+      0x2c, 0x5b, 0x3a, 0x85, 0x68, 0xb5, 0xaa, 0x6a, 0x44, 0xcd, 0x3f, 0xa7, },
114
+};
115
+
116
+int ff_rtmpe_gen_pub_key(URLContext *h, uint8_t *buf)
117
+{
118
+    RTMPEContext *rt = h->priv_data;
119
+    int offset, ret;
120
+
121
+    if (!(rt->dh = ff_dh_init(1024)))
122
+        return AVERROR(ENOMEM);
123
+
124
+    offset = ff_rtmp_calc_digest_pos(buf, 768, 632, 8);
125
+    if (offset < 0)
126
+        return offset;
127
+
128
+    /* generate a Diffie-Hellmann public key */
129
+    if ((ret = ff_dh_generate_public_key(rt->dh)) < 0)
130
+        return ret;
131
+
132
+    /* write the public key into the handshake buffer */
133
+    if ((ret = ff_dh_write_public_key(rt->dh, buf + offset, 128)) < 0)
134
+        return ret;
135
+
136
+    return 0;
137
+}
138
+
139
+int ff_rtmpe_compute_secret_key(URLContext *h, const uint8_t *serverdata,
140
+                                const uint8_t *clientdata, int type)
141
+{
142
+    RTMPEContext *rt = h->priv_data;
143
+    uint8_t secret_key[128], digest[32];
144
+    int server_pos, client_pos;
145
+    int ret;
146
+
147
+    if (type) {
148
+        if ((server_pos = ff_rtmp_calc_digest_pos(serverdata, 1532, 632, 772)) < 0)
149
+            return server_pos;
150
+    } else {
151
+        if ((server_pos = ff_rtmp_calc_digest_pos(serverdata, 768, 632, 8)) < 0)
152
+            return server_pos;
153
+    }
154
+
155
+    if ((client_pos = ff_rtmp_calc_digest_pos(clientdata, 768, 632, 8)) < 0)
156
+        return client_pos;
157
+
158
+    /* compute the shared secret secret in order to compute RC4 keys */
159
+    if ((ret = ff_dh_compute_shared_secret_key(rt->dh, serverdata + server_pos,
160
+                                               128, secret_key)) < 0)
161
+        return ret;
162
+
163
+    /* set output key */
164
+    if ((ret = ff_rtmp_calc_digest(serverdata + server_pos, 128, 0, secret_key,
165
+                                   128, digest)) < 0)
166
+        return ret;
167
+    av_rc4_init(&rt->key_out, digest, 16 * 8, 1);
168
+
169
+    /* set input key */
170
+    if ((ret = ff_rtmp_calc_digest(clientdata + client_pos, 128, 0, secret_key,
171
+                                   128, digest)) < 0)
172
+        return ret;
173
+    av_rc4_init(&rt->key_in, digest, 16 * 8, 1);
174
+
175
+    return 0;
176
+}
177
+
178
+static void rtmpe8_sig(const uint8_t *in, uint8_t *out, int key_id)
179
+{
180
+    struct AVXTEA ctx;
181
+
182
+    av_xtea_init(&ctx, rtmpe8_keys[key_id]);
183
+    av_xtea_crypt(&ctx, out, in, 1, NULL, 0);
184
+}
185
+
186
+static void rtmpe9_sig(const uint8_t *in, uint8_t *out, int key_id)
187
+{
188
+    struct AVBlowfish ctx;
189
+    uint32_t xl, xr;
190
+
191
+    xl = AV_RL32(in);
192
+    xr = AV_RL32(in + 4);
193
+
194
+    av_blowfish_init(&ctx, rtmpe9_keys[key_id], 24);
195
+    av_blowfish_crypt_ecb(&ctx, &xl, &xr, 0);
196
+
197
+    AV_WL32(out, xl);
198
+    AV_WL32(out + 4, xr);
199
+}
200
+
201
+void ff_rtmpe_encrypt_sig(URLContext *h, uint8_t *sig, const uint8_t *digest,
202
+                          int type)
203
+{
204
+    int i;
205
+
206
+    for (i = 0; i < 32; i += 8) {
207
+        if (type == 8) {
208
+            /* RTMPE type 8 uses XTEA on the signature */
209
+            rtmpe8_sig(sig + i, sig + i, digest[i] % 15);
210
+        } else if (type == 9) {
211
+            /* RTMPE type 9 uses Blowfish on the signature */
212
+            rtmpe9_sig(sig + i, sig + i, digest[i] % 15);
213
+        }
214
+    }
215
+}
216
+
217
+int ff_rtmpe_update_keystream(URLContext *h)
218
+{
219
+    RTMPEContext *rt = h->priv_data;
220
+    char buf[RTMP_HANDSHAKE_PACKET_SIZE];
221
+
222
+    /* skip past 1536 bytes of the RC4 bytestream */
223
+    av_rc4_crypt(&rt->key_in, buf, NULL, sizeof(buf), NULL, 1);
224
+    av_rc4_crypt(&rt->key_out, buf, NULL, sizeof(buf), NULL, 1);
225
+
226
+    /* the next requests will be encrypted using RC4 keys */
227
+    rt->handshaked = 1;
228
+
229
+    return 0;
230
+}
231
+
232
+static int rtmpe_close(URLContext *h)
233
+{
234
+    RTMPEContext *rt = h->priv_data;
235
+
236
+    ff_dh_free(rt->dh);
237
+    ffurl_close(rt->stream);
238
+
239
+    return 0;
240
+}
241
+
242
+static int rtmpe_open(URLContext *h, const char *uri, int flags)
243
+{
244
+    RTMPEContext *rt = h->priv_data;
245
+    char host[256], url[1024];
246
+    int ret, port;
247
+
248
+    av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, NULL, 0, uri);
249
+
250
+    if (port < 0)
251
+        port = 1935;
252
+
253
+    /* open the tcp connection */
254
+    ff_url_join(url, sizeof(url), "tcp", NULL, host, port, NULL);
255
+    if ((ret = ffurl_open(&rt->stream, url, AVIO_FLAG_READ_WRITE,
256
+                          &h->interrupt_callback, NULL)) < 0) {
257
+        rtmpe_close(h);
258
+        return ret;
259
+    }
260
+
261
+    return 0;
262
+}
263
+
264
+static int rtmpe_read(URLContext *h, uint8_t *buf, int size)
265
+{
266
+    RTMPEContext *rt = h->priv_data;
267
+    int ret;
268
+
269
+    rt->stream->flags |= h->flags & AVIO_FLAG_NONBLOCK;
270
+    ret = ffurl_read(rt->stream, buf, size);
271
+    rt->stream->flags &= ~AVIO_FLAG_NONBLOCK;
272
+
273
+    if (ret < 0 && ret != AVERROR_EOF)
274
+        return ret;
275
+
276
+    if (rt->handshaked && ret > 0) {
277
+        /* decrypt data received by the server */
278
+        av_rc4_crypt(&rt->key_in, buf, buf, ret, NULL, 1);
279
+    }
280
+
281
+    return ret;
282
+}
283
+
284
+static int rtmpe_write(URLContext *h, const uint8_t *buf, int size)
285
+{
286
+    RTMPEContext *rt = h->priv_data;
287
+    int ret;
288
+
289
+    if (rt->handshaked) {
290
+        /* encrypt data to send to the server */
291
+        av_rc4_crypt(&rt->key_out, buf, buf, size, NULL, 1);
292
+    }
293
+
294
+    if ((ret = ffurl_write(rt->stream, buf, size)) < 0)
295
+        return ret;
296
+
297
+    return size;
298
+}
299
+
300
+URLProtocol ff_ffrtmpcrypt_protocol = {
301
+    .name            = "ffrtmpcrypt",
302
+    .url_open        = rtmpe_open,
303
+    .url_read        = rtmpe_read,
304
+    .url_write       = rtmpe_write,
305
+    .url_close       = rtmpe_close,
306
+    .priv_data_size  = sizeof(RTMPEContext),
307
+    .flags           = URL_PROTOCOL_FLAG_NETWORK,
308
+};
0 309
new file mode 100644
... ...
@@ -0,0 +1,69 @@
0
+/*
1
+ * RTMPE encryption utilities
2
+ * Copyright (c) 2012 Samuel Pitoiset
3
+ *
4
+ * This file is part of Libav.
5
+ *
6
+ * Libav is free software; you can redistribute it and/or
7
+ * modify it under the terms of the GNU Lesser General Public
8
+ * License as published by the Free Software Foundation; either
9
+ * version 2.1 of the License, or (at your option) any later version.
10
+ *
11
+ * Libav is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
+ * Lesser General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Lesser General Public
17
+ * License along with Libav; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+ */
20
+
21
+#ifndef AVFORMAT_RTMPCRYPT_H
22
+#define AVFORMAT_RTMPCRYPT_H
23
+
24
+#include <stdint.h>
25
+
26
+#include "url.h"
27
+
28
+/**
29
+ * Initialize the Diffie-Hellmann context and generate the public key.
30
+ *
31
+ * @param h     an URLContext
32
+ * @param buf   handshake data (1536 bytes)
33
+ * @return zero on success, negative value otherwise
34
+ */
35
+int ff_rtmpe_gen_pub_key(URLContext *h, uint8_t *buf);
36
+
37
+/**
38
+ * Compute the shared secret key and initialize the RC4 encryption.
39
+ *
40
+ * @param h             an URLContext
41
+ * @param serverdata    server data (1536 bytes)
42
+ * @param clientdata    client data (1536 bytes)
43
+ * @param type          the position of the server digest
44
+ * @return zero on success, negative value otherwise
45
+ */
46
+int ff_rtmpe_compute_secret_key(URLContext *h, const uint8_t *serverdata,
47
+                                const uint8_t *clientdata, int type);
48
+
49
+/**
50
+ * Encrypt the signature.
51
+ *
52
+ * @param h             an URLContext
53
+ * @param signature     the signature to encrypt
54
+ * @param digest        the digest used for finding the encryption key
55
+ * @param type          type of encryption (8 for XTEA, 9 for Blowfish)
56
+ */
57
+void ff_rtmpe_encrypt_sig(URLContext *h, uint8_t *signature,
58
+                          const uint8_t *digest, int type);
59
+
60
+/**
61
+ * Update the keystream and set RC4 keys for encryption.
62
+ *
63
+ * @param h an URLContext
64
+ * @return zero on success, negative value otherwise
65
+ */
66
+int ff_rtmpe_update_keystream(URLContext *h);
67
+
68
+#endif /* AVFORMAT_RTMPCRYPT_H */
0 69
new file mode 100644
... ...
@@ -0,0 +1,329 @@
0
+/*
1
+ * RTMP Diffie-Hellmann utilities
2
+ * Copyright (c) 2012 Samuel Pitoiset
3
+ *
4
+ * This file is part of Libav.
5
+ *
6
+ * Libav is free software; you can redistribute it and/or
7
+ * modify it under the terms of the GNU Lesser General Public
8
+ * License as published by the Free Software Foundation; either
9
+ * version 2.1 of the License, or (at your option) any later version.
10
+ *
11
+ * Libav is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
+ * Lesser General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Lesser General Public
17
+ * License along with Libav; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+ */
20
+
21
+/**
22
+ * @file
23
+ * RTMP Diffie-Hellmann utilities
24
+ */
25
+
26
+#include "config.h"
27
+#include "rtmpdh.h"
28
+
29
+#define P1024                                          \
30
+    "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
31
+    "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
32
+    "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
33
+    "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
34
+    "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \
35
+    "FFFFFFFFFFFFFFFF"
36
+
37
+#define Q1024                                          \
38
+    "7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68" \
39
+    "948127044533E63A0105DF531D89CD9128A5043CC71A026E" \
40
+    "F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122" \
41
+    "F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6" \
42
+    "F71C35FDAD44CFD2D74F9208BE258FF324943328F67329C0" \
43
+    "FFFFFFFFFFFFFFFF"
44
+
45
+#if CONFIG_NETTLE || CONFIG_GCRYPT
46
+#if CONFIG_NETTLE
47
+#define bn_new(bn)                      \
48
+    do {                                \
49
+        bn = av_malloc(sizeof(*bn));    \
50
+        if (bn)                         \
51
+            mpz_init2(bn, 1);           \
52
+    } while (0)
53
+#define bn_free(bn)     \
54
+    do {                \
55
+        mpz_clear(bn);  \
56
+        av_free(bn);    \
57
+    } while (0)
58
+#define bn_set_word(bn, w)          mpz_set_ui(bn, w)
59
+#define bn_cmp(a, b)                mpz_cmp(a, b)
60
+#define bn_copy(to, from)           mpz_set(to, from)
61
+#define bn_sub_word(bn, w)          mpz_sub_ui(bn, bn, w)
62
+#define bn_cmp_1(bn)                mpz_cmp_ui(bn, 1)
63
+#define bn_num_bytes(bn)            (mpz_sizeinbase(bn, 2) + 7) / 8
64
+#define bn_bn2bin(bn, buf, len)     nettle_mpz_get_str_256(len, buf, bn)
65
+#define bn_bin2bn(bn, buf, len)                     \
66
+    do {                                            \
67
+        bn_new(bn);                                 \
68
+        if (bn)                                     \
69
+            nettle_mpz_set_str_256_u(bn, len, buf); \
70
+    } while (0)
71
+#define bn_hex2bn(bn, buf, ret)                     \
72
+    do {                                            \
73
+        bn_new(bn);                                 \
74
+        if (bn)                                     \
75
+            ret = (mpz_set_str(bn, buf, 16) == 0);  \
76
+    } while (0)
77
+#define bn_modexp(bn, y, q, p)      mpz_powm(bn, y, q, p)
78
+#define bn_random(bn, num_bytes)    mpz_random(bn, num_bytes);
79
+#elif CONFIG_GCRYPT
80
+#define bn_new(bn)                  bn = gcry_mpi_new(1)
81
+#define bn_free(bn)                 gcry_mpi_release(bn)
82
+#define bn_set_word(bn, w)          gcry_mpi_set_ui(bn, w)
83
+#define bn_cmp(a, b)                gcry_mpi_cmp(a, b)
84
+#define bn_copy(to, from)           gcry_mpi_set(to, from)
85
+#define bn_sub_word(bn, w)          gcry_mpi_sub_ui(bn, bn, w)
86
+#define bn_cmp_1(bn)                gcry_mpi_cmp_ui(bn, 1)
87
+#define bn_num_bytes(bn)            (gcry_mpi_get_nbits(bn) + 7) / 8
88
+#define bn_bn2bin(bn, buf, len)     gcry_mpi_print(GCRYMPI_FMT_USG, buf, len, NULL, bn)
89
+#define bn_bin2bn(bn, buf, len)     gcry_mpi_scan(&bn, GCRYMPI_FMT_USG, buf, len, NULL)
90
+#define bn_hex2bn(bn, buf, ret)     ret = (gcry_mpi_scan(&bn, GCRYMPI_FMT_HEX, buf, 0, 0) == 0)
91
+#define bn_modexp(bn, y, q, p)      gcry_mpi_powm(bn, y, q, p)
92
+#define bn_random(bn, num_bytes)    gcry_mpi_randomize(bn, num_bytes, GCRY_WEAK_RANDOM)
93
+#endif
94
+
95
+#define MAX_BYTES 18000
96
+
97
+#define dh_new()                    av_malloc(sizeof(FF_DH))
98
+
99
+static FFBigNum dh_generate_key(FF_DH *dh)
100
+{
101
+    int num_bytes;
102
+
103
+    num_bytes = bn_num_bytes(dh->p) - 1;
104
+    if (num_bytes <= 0 || num_bytes > MAX_BYTES)
105
+        return NULL;
106
+
107
+    bn_new(dh->priv_key);
108
+    if (!dh->priv_key)
109
+        return NULL;
110
+    bn_random(dh->priv_key, num_bytes);
111
+
112
+    bn_new(dh->pub_key);
113
+    if (!dh->pub_key) {
114
+        bn_free(dh->priv_key);
115
+        return NULL;
116
+    }
117
+
118
+    bn_modexp(dh->pub_key, dh->g, dh->priv_key, dh->p);
119
+
120
+    return dh->pub_key;
121
+}
122
+
123
+static int dh_compute_key(FF_DH *dh, FFBigNum pub_key_bn,
124
+                          uint32_t pub_key_len, uint8_t *secret_key)
125
+{
126
+    FFBigNum k;
127
+    int num_bytes;
128
+
129
+    num_bytes = bn_num_bytes(dh->p);
130
+    if (num_bytes <= 0 || num_bytes > MAX_BYTES)
131
+        return -1;
132
+
133
+    bn_new(k);
134
+    if (!k)
135
+        return -1;
136
+
137
+    bn_modexp(k, pub_key_bn, dh->priv_key, dh->p);
138
+    bn_bn2bin(k, secret_key, pub_key_len);
139
+    bn_free(k);
140
+
141
+    /* return the length of the shared secret key like DH_compute_key */
142
+    return pub_key_len;
143
+}
144
+
145
+void ff_dh_free(FF_DH *dh)
146
+{
147
+    bn_free(dh->p);
148
+    bn_free(dh->g);
149
+    bn_free(dh->pub_key);
150
+    bn_free(dh->priv_key);
151
+    av_free(dh);
152
+}
153
+#elif CONFIG_OPENSSL
154
+#define bn_new(bn)                  bn = BN_new()
155
+#define bn_free(bn)                 BN_free(bn)
156
+#define bn_set_word(bn, w)          BN_set_word(bn, w)
157
+#define bn_cmp(a, b)                BN_cmp(a, b)
158
+#define bn_copy(to, from)           BN_copy(to, from)
159
+#define bn_sub_word(bn, w)          BN_sub_word(bn, w)
160
+#define bn_cmp_1(bn)                BN_cmp(bn, BN_value_one())
161
+#define bn_num_bytes(bn)            BN_num_bytes(bn)
162
+#define bn_bn2bin(bn, buf, len)     BN_bn2bin(bn, buf)
163
+#define bn_bin2bn(bn, buf, len)     bn = BN_bin2bn(buf, len, 0)
164
+#define bn_hex2bn(bn, buf, ret)     ret = BN_hex2bn(&bn, buf)
165
+#define bn_modexp(bn, y, q, p)               \
166
+    do {                                     \
167
+        BN_CTX *ctx = BN_CTX_new();          \
168
+        if (!ctx)                            \
169
+            return AVERROR(ENOMEM);          \
170
+        if (!BN_mod_exp(bn, y, q, p, ctx)) { \
171
+            BN_CTX_free(ctx);                \
172
+            return AVERROR(EINVAL);          \
173
+        }                                    \
174
+        BN_CTX_free(ctx);                    \
175
+    } while (0)
176
+
177
+#define dh_new()                                DH_new()
178
+#define dh_generate_key(dh)                     DH_generate_key(dh)
179
+#define dh_compute_key(dh, pub, len, secret)    DH_compute_key(secret, pub, dh)
180
+
181
+void ff_dh_free(FF_DH *dh)
182
+{
183
+    DH_free(dh);
184
+}
185
+#endif
186
+
187
+static int dh_is_valid_public_key(FFBigNum y, FFBigNum p, FFBigNum q)
188
+{
189
+    FFBigNum bn = NULL;
190
+    int ret = AVERROR(EINVAL);
191
+
192
+    bn_new(bn);
193
+    if (!bn)
194
+        return AVERROR(ENOMEM);
195
+
196
+    /* y must lie in [2, p - 1] */
197
+    bn_set_word(bn, 1);
198
+    if (!bn_cmp(y, bn))
199
+        goto fail;
200
+
201
+    /* bn = p - 2 */
202
+    bn_copy(bn, p);
203
+    bn_sub_word(bn, 1);
204
+    if (!bn_cmp(y, bn))
205
+        goto fail;
206
+
207
+    /* Verify with Sophie-Germain prime
208
+     *
209
+     * This is a nice test to make sure the public key position is calculated
210
+     * correctly. This test will fail in about 50% of the cases if applied to
211
+     * random data.
212
+     */
213
+    /* y must fulfill y^q mod p = 1 */
214
+    bn_modexp(bn, y, q, p);
215
+
216
+    if (bn_cmp_1(bn))
217
+        goto fail;
218
+
219
+    ret = 0;
220
+fail:
221
+    bn_free(bn);
222
+
223
+    return ret;
224
+}
225
+
226
+av_cold FF_DH *ff_dh_init(int key_len)
227
+{
228
+    FF_DH *dh;
229
+    int ret;
230
+
231
+    if (!(dh = dh_new()))
232
+        return NULL;
233
+
234
+    bn_new(dh->g);
235
+    if (!dh->g)
236
+        goto fail;
237
+
238
+    bn_hex2bn(dh->p, P1024, ret);
239
+    if (!ret)
240
+        goto fail;
241
+
242
+    bn_set_word(dh->g, 2);
243
+    dh->length = key_len;
244
+
245
+    return dh;
246
+
247
+fail:
248
+    ff_dh_free(dh);
249
+
250
+    return NULL;
251
+}
252
+
253
+int ff_dh_generate_public_key(FF_DH *dh)
254
+{
255
+    int ret = 0;
256
+
257
+    while (!ret) {
258
+        FFBigNum q1 = NULL;
259
+
260
+        if (!dh_generate_key(dh))
261
+            return AVERROR(EINVAL);
262
+
263
+        bn_hex2bn(q1, Q1024, ret);
264
+        if (!ret)
265
+            return AVERROR(ENOMEM);
266
+
267
+        ret = dh_is_valid_public_key(dh->pub_key, dh->p, q1);
268
+        bn_free(q1);
269
+
270
+        if (!ret) {
271
+            /* the public key is valid */
272
+            break;
273
+        }
274
+    }
275
+
276
+    return ret;
277
+}
278
+
279
+int ff_dh_write_public_key(FF_DH *dh, uint8_t *pub_key, int pub_key_len)
280
+{
281
+    int len;
282
+
283
+    /* compute the length of the public key */
284
+    len = bn_num_bytes(dh->pub_key);
285
+    if (len <= 0 || len > pub_key_len)
286
+        return AVERROR(EINVAL);
287
+
288
+    /* convert the public key value into big-endian form */
289
+    memset(pub_key, 0, pub_key_len);
290
+    bn_bn2bin(dh->pub_key, pub_key + pub_key_len - len, len);
291
+
292
+    return 0;
293
+}
294
+
295
+int ff_dh_compute_shared_secret_key(FF_DH *dh, const uint8_t *pub_key,
296
+                                    int pub_key_len, uint8_t *secret_key)
297
+{
298
+    FFBigNum q1 = NULL, pub_key_bn = NULL;
299
+    int ret;
300
+
301
+    /* convert the big-endian form of the public key into a bignum */
302
+    bn_bin2bn(pub_key_bn, pub_key, pub_key_len);
303
+    if (!pub_key_bn)
304
+        return AVERROR(ENOMEM);
305
+
306
+    /* convert the string containing a hexadecimal number into a bignum */
307
+    bn_hex2bn(q1, Q1024, ret);
308
+    if (!ret) {
309
+        ret = AVERROR(ENOMEM);
310
+        goto fail;
311
+    }
312
+
313
+    /* when the public key is valid we have to compute the shared secret key */
314
+    if ((ret = dh_is_valid_public_key(pub_key_bn, dh->p, q1)) < 0) {
315
+        goto fail;
316
+    } else if ((ret = dh_compute_key(dh, pub_key_bn, pub_key_len,
317
+                                     secret_key)) < 0) {
318
+        ret = AVERROR(EINVAL);
319
+        goto fail;
320
+    }
321
+
322
+fail:
323
+    bn_free(pub_key_bn);
324
+    bn_free(q1);
325
+
326
+    return ret;
327
+}
328
+
0 329
new file mode 100644
... ...
@@ -0,0 +1,102 @@
0
+/*
1
+ * RTMP Diffie-Hellmann utilities
2
+ * Copyright (c) 2012 Samuel Pitoiset
3
+ *
4
+ * This file is part of Libav.
5
+ *
6
+ * Libav is free software; you can redistribute it and/or
7
+ * modify it under the terms of the GNU Lesser General Public
8
+ * License as published by the Free Software Foundation; either
9
+ * version 2.1 of the License, or (at your option) any later version.
10
+ *
11
+ * Libav is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
+ * Lesser General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Lesser General Public
17
+ * License along with Libav; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+ */
20
+
21
+#ifndef AVFORMAT_RTMPDH_H
22
+#define AVFORMAT_RTMPDH_H
23
+
24
+#include "avformat.h"
25
+#include "config.h"
26
+
27
+#if CONFIG_NETTLE || CONFIG_GCRYPT
28
+#if CONFIG_NETTLE
29
+#include <gmp.h>
30
+#include <nettle/bignum.h>
31
+
32
+typedef mpz_ptr FFBigNum;
33
+#elif CONFIG_GCRYPT
34
+#include <gcrypt.h>
35
+
36
+typedef gcry_mpi_t FFBigNum;
37
+#endif
38
+
39
+typedef struct FF_DH {
40
+  FFBigNum p;
41
+  FFBigNum g;
42
+  FFBigNum pub_key;
43
+  FFBigNum priv_key;
44
+  long length;
45
+} FF_DH;
46
+
47
+#elif CONFIG_OPENSSL
48
+#include <openssl/bn.h>
49
+#include <openssl/dh.h>
50
+
51
+typedef BIGNUM *FFBigNum;
52
+typedef DH FF_DH;
53
+#endif
54
+
55
+/**
56
+ * Initialize a Diffie-Hellmann context.
57
+ *
58
+ * @param key_len length of the key
59
+ * @return a new Diffie-Hellmann context on success, NULL otherwise
60
+ */
61
+FF_DH *ff_dh_init(int key_len);
62
+
63
+/**
64
+ * Free a Diffie-Hellmann context.
65
+ *
66
+ * @param dh a Diffie-Hellmann context to free
67
+ */
68
+void ff_dh_free(FF_DH *dh);
69
+
70
+/**
71
+ * Generate a public key.
72
+ *
73
+ * @param dh a Diffie-Hellmann context
74
+ * @return zero on success, negative value otherwise
75
+ */
76
+int ff_dh_generate_public_key(FF_DH *dh);
77
+
78
+/**
79
+ * Write the public key into the given buffer.
80
+ *
81
+ * @param dh            a Diffie-Hellmann context, containing the public key to write
82
+ * @param pub_key       the buffer where the public key is written
83
+ * @param pub_key_len   the length of the buffer
84
+ * @return zero on success, negative value otherwise
85
+ */
86
+int ff_dh_write_public_key(FF_DH *dh, uint8_t *pub_key, int pub_key_len);
87
+
88
+/**
89
+ * Compute the shared secret key from the private FF_DH value and the
90
+ * other party's public value.
91
+ *
92
+ * @param dh            a Diffie-Hellmann context, containing the private key
93
+ * @param pub_key       the buffer containing the public key
94
+ * @param pub_key_len   the length of the buffer
95
+ * @param secret_key    the buffer where the secret key is written
96
+ * @return length of the shared secret key on success, negative value otherwise
97
+ */
98
+int ff_dh_compute_shared_secret_key(FF_DH *dh, const uint8_t *pub_key,
99
+                                    int pub_key_len, uint8_t *secret_key);
100
+
101
+#endif /* AVFORMAT_RTMPDH_H */
... ...
@@ -37,6 +37,7 @@
37 37
 
38 38
 #include "flv.h"
39 39
 #include "rtmp.h"
40
+#include "rtmpcrypt.h"
40 41
 #include "rtmppkt.h"
41 42
 #include "url.h"
42 43
 
... ...
@@ -92,6 +93,7 @@ typedef struct RTMPContext {
92 92
     int           server_bw;                  ///< server bandwidth
93 93
     int           client_buffer_time;         ///< client buffer time in ms
94 94
     int           flush_interval;             ///< number of packets flushed in the same request (RTMPT only)
95
+    int           encrypted;                  ///< use an encrypted connection (RTMPE only)
95 96
 } RTMPContext;
96 97
 
97 98
 #define PLAYER_KEY_OPEN_PART_LEN 30   ///< length of partial key used for first client digest signing
... ...
@@ -649,13 +651,17 @@ int ff_rtmp_calc_digest_pos(const uint8_t *buf, int off, int mod_val,
649 649
  * will be stored) into that packet.
650 650
  *
651 651
  * @param buf handshake data (1536 bytes)
652
+ * @param encrypted use an encrypted connection (RTMPE)
652 653
  * @return offset to the digest inside input data
653 654
  */
654
-static int rtmp_handshake_imprint_with_digest(uint8_t *buf)
655
+static int rtmp_handshake_imprint_with_digest(uint8_t *buf, int encrypted)
655 656
 {
656 657
     int ret, digest_pos;
657 658
 
658
-    digest_pos = ff_rtmp_calc_digest_pos(buf, 8, 728, 12);
659
+    if (encrypted)
660
+        digest_pos = ff_rtmp_calc_digest_pos(buf, 772, 728, 776);
661
+    else
662
+        digest_pos = ff_rtmp_calc_digest_pos(buf, 8, 728, 12);
659 663
 
660 664
     ret = ff_rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
661 665
                               rtmp_player_key, PLAYER_KEY_OPEN_PART_LEN,
... ...
@@ -712,8 +718,9 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt)
712 712
     uint8_t serverdata[RTMP_HANDSHAKE_PACKET_SIZE+1];
713 713
     int i;
714 714
     int server_pos, client_pos;
715
-    uint8_t digest[32];
716
-    int ret;
715
+    uint8_t digest[32], signature[32];
716
+    int encrypted = rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL;
717
+    int ret, type = 0;
717 718
 
718 719
     av_log(s, AV_LOG_DEBUG, "Handshaking...\n");
719 720
 
... ...
@@ -721,7 +728,24 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt)
721 721
     // generate handshake packet - 1536 bytes of pseudorandom data
722 722
     for (i = 9; i <= RTMP_HANDSHAKE_PACKET_SIZE; i++)
723 723
         tosend[i] = av_lfg_get(&rnd) >> 24;
724
-    client_pos = rtmp_handshake_imprint_with_digest(tosend + 1);
724
+
725
+    if (encrypted) {
726
+        /* When the client wants to use RTMPE, we have to change the command
727
+         * byte to 0x06 which means to use encrypted data and we have to set
728
+         * the flash version to at least 9.0.115.0. */
729
+        tosend[0] = 6;
730
+        tosend[5] = 128;
731
+        tosend[6] = 0;
732
+        tosend[7] = 3;
733
+        tosend[8] = 2;
734
+
735
+        /* Initialize the Diffie-Hellmann context and generate the public key
736
+         * to send to the server. */
737
+        if ((ret = ff_rtmpe_gen_pub_key(rt->stream, tosend + 1)) < 0)
738
+            return ret;
739
+    }
740
+
741
+    client_pos = rtmp_handshake_imprint_with_digest(tosend + 1, encrypted);
725 742
     if (client_pos < 0)
726 743
         return client_pos;
727 744
 
... ...
@@ -743,6 +767,7 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt)
743 743
         return ret;
744 744
     }
745 745
 
746
+    av_log(s, AV_LOG_DEBUG, "Type answer %d\n", serverdata[0]);
746 747
     av_log(s, AV_LOG_DEBUG, "Server version %d.%d.%d.%d\n",
747 748
            serverdata[5], serverdata[6], serverdata[7], serverdata[8]);
748 749
 
... ...
@@ -752,6 +777,7 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt)
752 752
             return server_pos;
753 753
 
754 754
         if (!server_pos) {
755
+            type = 1;
755 756
             server_pos = rtmp_validate_digest(serverdata + 1, 8);
756 757
             if (server_pos < 0)
757 758
                 return server_pos;
... ...
@@ -769,11 +795,22 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt)
769 769
             return ret;
770 770
 
771 771
         ret = ff_rtmp_calc_digest(clientdata, RTMP_HANDSHAKE_PACKET_SIZE - 32,
772
-                                  0, digest, 32, digest);
772
+                                  0, digest, 32, signature);
773 773
         if (ret < 0)
774 774
             return ret;
775 775
 
776
-        if (memcmp(digest, clientdata + RTMP_HANDSHAKE_PACKET_SIZE - 32, 32)) {
776
+        if (encrypted) {
777
+            /* Compute the shared secret key sent by the server and initialize
778
+             * the RC4 encryption. */
779
+            if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
780
+                                                   tosend + 1, type)) < 0)
781
+                return ret;
782
+
783
+            /* Encrypt the signature received by the server. */
784
+            ff_rtmpe_encrypt_sig(rt->stream, signature, digest, serverdata[0]);
785
+        }
786
+
787
+        if (memcmp(signature, clientdata + RTMP_HANDSHAKE_PACKET_SIZE - 32, 32)) {
777 788
             av_log(s, AV_LOG_ERROR, "Signature mismatch\n");
778 789
             return AVERROR(EIO);
779 790
         }
... ...
@@ -792,14 +829,47 @@ static int rtmp_handshake(URLContext *s, RTMPContext *rt)
792 792
         if (ret < 0)
793 793
             return ret;
794 794
 
795
+        if (encrypted) {
796
+            /* Encrypt the signature to be send to the server. */
797
+            ff_rtmpe_encrypt_sig(rt->stream, tosend +
798
+                                 RTMP_HANDSHAKE_PACKET_SIZE - 32, digest,
799
+                                 serverdata[0]);
800
+        }
801
+
795 802
         // write reply back to the server
796 803
         if ((ret = ffurl_write(rt->stream, tosend,
797 804
                                RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
798 805
             return ret;
806
+
807
+        if (encrypted) {
808
+            /* Set RC4 keys for encryption and update the keystreams. */
809
+            if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
810
+                return ret;
811
+        }
799 812
     } else {
813
+        if (encrypted) {
814
+            /* Compute the shared secret key sent by the server and initialize
815
+             * the RC4 encryption. */
816
+            if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
817
+                            tosend + 1, 1)) < 0)
818
+                return ret;
819
+
820
+            if (serverdata[0] == 9) {
821
+                /* Encrypt the signature received by the server. */
822
+                ff_rtmpe_encrypt_sig(rt->stream, signature, digest,
823
+                                     serverdata[0]);
824
+            }
825
+        }
826
+
800 827
         if ((ret = ffurl_write(rt->stream, serverdata + 1,
801 828
                                RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
802 829
             return ret;
830
+
831
+        if (encrypted) {
832
+            /* Set RC4 keys for encryption and update the keystreams. */
833
+            if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
834
+                return ret;
835
+        }
803 836
     }
804 837
 
805 838
     return 0;
... ...
@@ -1122,6 +1192,10 @@ static int rtmp_open(URLContext *s, const char *uri, int flags)
1122 1122
         if (port < 0)
1123 1123
             port = RTMPS_DEFAULT_PORT;
1124 1124
         ff_url_join(buf, sizeof(buf), "tls", NULL, hostname, port, NULL);
1125
+    } else if (!strcmp(proto, "rtmpe")) {
1126
+        /* open the encrypted connection */
1127
+        ff_url_join(buf, sizeof(buf), "ffrtmpcrypt", NULL, hostname, port, NULL);
1128
+        rt->encrypted = 1;
1125 1129
     } else {
1126 1130
         /* open the tcp connection */
1127 1131
         if (port < 0)
... ...
@@ -1446,6 +1520,24 @@ URLProtocol ff_rtmp_protocol = {
1446 1446
     .priv_data_class= &rtmp_class,
1447 1447
 };
1448 1448
 
1449
+static const AVClass rtmpe_class = {
1450
+    .class_name = "rtmpe",
1451
+    .item_name  = av_default_item_name,
1452
+    .option     = rtmp_options,
1453
+    .version    = LIBAVUTIL_VERSION_INT,
1454
+};
1455
+
1456
+URLProtocol ff_rtmpe_protocol = {
1457
+    .name            = "rtmpe",
1458
+    .url_open        = rtmp_open,
1459
+    .url_read        = rtmp_read,
1460
+    .url_write       = rtmp_write,
1461
+    .url_close       = rtmp_close,
1462
+    .priv_data_size  = sizeof(RTMPContext),
1463
+    .flags           = URL_PROTOCOL_FLAG_NETWORK,
1464
+    .priv_data_class = &rtmpe_class,
1465
+};
1466
+
1449 1467
 static const AVClass rtmps_class = {
1450 1468
     .class_name = "rtmps",
1451 1469
     .item_name  = av_default_item_name,
... ...
@@ -30,7 +30,7 @@
30 30
 #include "libavutil/avutil.h"
31 31
 
32 32
 #define LIBAVFORMAT_VERSION_MAJOR 54
33
-#define LIBAVFORMAT_VERSION_MINOR 10
33
+#define LIBAVFORMAT_VERSION_MINOR 11
34 34
 #define LIBAVFORMAT_VERSION_MICRO  0
35 35
 
36 36
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \