Sylvain Munaut: Abusing Calypso
[HTML5TV.git] / docs / RTMPE.txt
1 RTMPE
2 -----
3
4 This document is a clean-room specification of the RTMP "Encryption"
5 scheme called RTMPE.  It contains industry-standard crypto primitives,
6 ARC4, HMACSHA256 and Diffie-Hellman.  The specification was created
7 by reviewing the source code of rtmpdump v1.6.
8
9 Academic and other discussion is invited.  Distribution of this document
10 is unlimited and encouraged.  Implementations even more so.
11
12 More info: http://lkcl.net/rtmp
13
14 Revisions
15 ---------
16
17 23 may 2009: first draft
18 24 may 2009: added analysis section concluding algorithm is like SSL
19 25 may 2009: renamed "key" to "const".  explicitly mention lengths in notes.
20 27 may 2009: corrections in use of Get*GenuineConst fns (thanks to KG)
21
22 Conventions
23 -----------
24
25 data[x:y] means "bytes x through y, inclusive" - like in python
26 x+y on bytes means "append the two byte streams, consecutively"
27 data[x] means "the byte offset by x" - like in python.
28 /* ... */ means comments
29 bigendian32(x) means create 4 bytes in big-endian order, from a 32-bit integer.
30
31
32 Constants
33 ---------
34
35 RTMP_SIG_SIZE = 1536
36 SHA256DL = 32 /* SHA 256-byte Digest Length */
37
38 RandomCrud = {
39     0xf0, 0xee, 0xc2, 0x4a,
40     0x80, 0x68, 0xbe, 0xe8, 0x2e, 0x00, 0xd0, 0xd1,
41     0x02, 0x9e, 0x7e, 0x57, 0x6e, 0xec, 0x5d, 0x2d,
42     0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36,
43     0xcf, 0xeb, 0x31, 0xae
44 }
45
46 SWFVerifySig = { 0x1, 0x1 }
47
48 /* data in quotes does not include quotes as part of data */
49 GenuineFMSConst = "Genuine Adobe Flash Media Server 001" /* 36 bytes long */
50 GenuineFPConst  = "Genuine Adobe Flash Player 001" /* 30 bytes long */
51
52 GenuineFMSConstCrud = GenuineFMSConst + RandomCrud
53 GenuineFPConstCrud = GenuineFPConst + RandomCrud
54
55
56 GetServerDHOffset
57 -----------------
58
59 The purpose of this function is to calculate the offset of the Server's
60 Diffie-Hellmann key.
61
62 Its input is 4 consecutive bytes.
63
64     offset = byte[0] + byte[1] + byte[2] + byte[3]
65     offset = modulo(offset,632)
66     offset = offset + 8
67
68 For sanity, the offset should be no bigger than (767-128)
69
70 GetServerGenuineConstDigestOffset
71 ---------------------------------
72
73 The purpose of this function is to calculate the offset of the Server's
74 Digest.
75
76 Input data is 4 consecutive bytes.
77
78     offset = byte[0] + byte[1] + byte[2] + byte[3]
79     offset = modulo(offset,728)
80     offset = offset + 776
81
82 For sanity, the offset should be no bigger than (1535-32)
83
84 GetClientDHOffset
85 -----------------
86
87 The purpose of this function is to calculate the offset of the client's
88 Diffie-Hellmann key.
89
90 Input data is 4 consecutive bytes.
91
92     offset = byte[0] + byte[1] + byte[2] + byte[3]
93     offset = modulo(offset,632)
94     offset = offset + 772
95
96 For sanity, the offset should be no bigger than (RTMP_SIG_SIZE-128-4)
97
98 GetClientGenuineConstDigestOffset
99 ---------------------------------
100
101 The purpose of this function is to calculate the offset of the client's
102 Digest.
103
104 Input data is 4 consecutive bytes.
105
106     offset = byte[0] + byte[1] + byte[2] + byte[3]
107     offset = modulo(offset,728)
108     offset = offset + 12
109
110 For sanity, the offset should be no bigger than (771-32)
111
112
113 Packet Format
114 -------------
115
116 The packets consist of a one byte command followed by a 1536 byte message
117
118     Bytes    : Description
119     -------    -----------
120     0          Command
121     1:1536     message of RTMP_SIG_SIZE bytes
122
123
124 Client First Exchange
125 ---------------------
126
127 This is the first packet to be generated.
128 clientsig and clientsig2 are RTMP_SIG_SIZE bytes.
129 serversig and serversig2 are RTMP_SIG_SIZE bytes.
130
131 Note: Encryption is only supported on versions at least 9.0.115.0
132
133 Note: The 0x08 command-byte is not yet known.  It is understood
134 to involve further obfuscation of the Client and Server Digests,
135 and is understood to be implemented in Flash 10.
136
137 Command byte:
138     0x06 if encrypted
139     0x08 if further encrypted (undocumented)
140     0x03 if unencrypted
141
142 Message:
143     0:3        32-bit system time, network byte ordered (htonl)
144     4:7        Client Version.  e.g. 0x09 0x0 0x7c 0x2 is 9.0.124.2
145     8:11       Obfuscated pointer to "Genuine FP" key 
146     12:1531    Random Data, 128-bit Diffie-Hellmann key and "Genuine FP" key.
147     1532:1535  Obfuscated pointer to 128-bit Diffie-Hellmann key 
148
149 Calculate location of Diffie Hellmann Public Key and create it:
150
151     dhpkl = GetClientDHoffset(clientsig[1532:1535])
152     DHPrivateKeyC, DHPublicKeyC = DHKeyGenerate(128) /* 128-bit */
153     clientsig[dhpkl:dhpkl+127] = DHPublicKeyC
154
155 Calculate location of Client Digest and create it:
156
157     /* Note: the SHA digest message is calculated from the bytes of
158        the message, excluding the 32-bytes where the digest itself goes.
159        Note also that GenuineFPConst is 30 bytes long.
160     */
161
162     cdl = GetClientGenuineConstDigestOffset(clientsig[8:11])
163     msg = clientsig[0:cdl-1] + clientsig[cdl+SHA256DL:RTMP_SIG_SIZE-1]
164     clientsig[cdl:cdl+SHA256DL-1] = HMACsha256(msg, GenuineFPConst)
165
166 First Exchange:
167
168     Send all 1537 bytes (command + clientsig) to the server;
169     Read 1537 bytes (command + serversig) from the server.
170
171 Note that the exact circumstances under which "Message Format 1"
172 or "Message Format 2" are utilised is unknown.  It is therefore
173 necessary for clients to utilise the SHA verification to determine
174 which of the two message formats is being received (!)
175
176 Command byte:
177     0x06 if encrypted - same as client request
178     0x03 if unencrypted - same as client request
179
180 Message Format 1:
181     0:3        32-bit system time, network byte ordered (htonl)
182     4:7        Server Version.  e.g. 0x09 0x0 0x7c 0x2 is 9.0.124.2
183     8:11       Obfuscated pointer to "Genuine FMS" key 
184     12:1531    Random Data, 128-bit Diffie-Hellmann key and "Genuine FMS" key.
185     1532:1535  Obfuscated pointer to 128-bit Diffie-Hellmann key 
186
187 Calculate location of Server Digest and compare it:
188
189     /* Note that GenuineFMSConst is 36 bytes long. */
190     sdl = GetClientGenuineConstDigestOffset(serversig[8:11])
191     msg = serversig[0:sdl-1] + serversig[sdl+SHA256DL:RTMP_SIG_SIZE-1]
192     Compare(serversig[sdl:sdl+SHA256DL-1], HMACsha256(msg, GenuineFMSConst))
193
194 Calculate location of Server Diffie Hellmann Public Key and get it:
195
196     dhpkl = GetClientDHoffset(serversig[1532:1535])
197     DHPublicKeyS = serversig[dhpkl:dhpkl+127]
198
199 Message Format 2:
200     0:3        32-bit system time, network byte ordered (htonl)
201     4:7        Server Version.  e.g. 0x09 0x0 0x7c 0x2 is 9.0.124.2
202     8:767      Random Data and 128-bit Diffie-Hellmann key 
203     768:771    Obfuscated pointer to 128-bit Diffie-Hellmann key 
204     772:775    Obfuscated pointer to "Genuine FMS" key 
205     776:1535   Random Data and "Genuine FMS" key.
206
207 Calculate location of Server Digest and compare it:
208
209     /* Note that GenuineFMSConst is 36 bytes long. */
210     sdl = GetServerGenuineConstDigestOffset(serversig[772:775])
211     msg = serversig[0:sdl-1] + serversig[sdl+SHA256DL:RTMP_SIG_SIZE-1]
212     Compare(serversig[sdl:sdl+SHA256DL-1], HMACsha256(msg, GenuineFMSConst))
213
214 Calculate location of Server Diffie Hellmann Public Key and get it:
215
216     dhpkl = GetServerDHoffset(serversig[768:771])
217     DHPublicKeyS = serversig[dhpkl:dhpkl+127]
218
219 Compute Diffie-Hellmann Shared Secret:
220
221 The key is only needed if encryption was negotiated.
222
223     DHSharedSecret = DH(DHPrivateKeyC, DHPublicKeyS)
224
225 Compute SWFVerification token:
226
227 If a SWFHash is used, a SWFVerification response will need to
228 be calculated, and returned on-demand to a "ping" request.
229 SWFsize is the size of the SWF file.
230
231 Note: It is assumed that the reader is familiar enough with RTMP to
232 know what a "ping" is.  Where the ordinary ping type is 0x0006,
233 and the pong response is of type 0x0007, an SWF verification ping
234 is of type 0x001a and the SWF verification pong is of type 0x001b.
235 Packet sizes of type 0x001b are 44 bytes: 2 bytes for the type itself
236 and 42 bytes for the SWF verification response.
237
238     swfvk = serversig[RTMP_SIG_SIZE-SHA256DL:RTMP_SIG_SIZE-1]
239     SWFDigest = SWFVerifySig + bigendian32(SWFsize) + bigendian32(SWFsize) + 
240                 HMACsha256(SWFHash, swfvk)
241
242 Initialise ARC4 Send / Receive Keys:
243
244 The ARC4 keys KeyIn and KeyOut are used to decrypt and encrypt
245 incoming and outgoing data, respectively.
246
247     KeyIn  = ARC4Key(HMACsha256(DHPublicKeyS, DHSharedSecret)[0:15])
248     KeyOut = ARC4Key(HMACsha256(DHPublicKeyC, DHSharedSecret)[0:15])
249
250     Explanation in words:
251
252     To calculate the ARC4 key for the data received by the client
253     (KeyIn), take the Server's initial 128-bit Diffie-Hellmann Secret
254     (from which the DH Shared Secret was calculated) and calculate
255     the HMACsha256 digest of that server's secret, using the DH
256     Shared Secret as the HMACsha256 key.
257
258     To calculate the ARC4 key for the data sent by the client
259     (KeyOut), take the Client's initial 128-bit Diffie-Hellmann Secret
260     (from which the DH Shared Secret was calculated) and calculate
261     the HMACsha256 digest of the client's secret, using the DH
262     Shared Secret as the HMACsha256 key.
263
264 Read Second Exchange:
265
266 Note: the second response appears to be read directly after the first
267 response, rather than the normal client-server arrangement of interleaving
268 client writes with server sends.
269
270     Read 1536 bytes (serversig2) from the server.
271
272 Validate Second Response:
273
274 If Flash Player version 9 Hand-shaking is not being utilised,
275 then the server will have simply sent a copy of the client's
276 own previous packet back to it.  Otherwise, the client verifies
277 the response (the first four bytes of which are likely to be zero
278 if there was a validation error), as follows:
279
280     digest = HMACsha256(DHPublicKeyC, GenuineFMSConstCrud)
281     signature = HMACsha256(serversig2[0:RTMP_SIG_SIZE-SHA256DL-1], digest)
282     Compare(signature, serversig2[RTMP_SIG_SIZE-SHA256DL:RTMP_SIG_SIZE-1])
283
284 Generate Second Response:
285
286     clientsig2[0:RTMP_SIG_SIZE] = Random Data
287     digest = HMACsha256(DHPublicKeyS, GenuineFPConstCrud)
288     signature = HMACsha256(clientsig2[0:RTMP_SIG_SIZE-SHA256DL-1], digest)
289
290 Send Second Response:
291
292     Write 1536 bytes (clientsig2) to server.
293
294 Update ARC4 Keys:
295
296 If encryption is enabled, then ONLY after the handshaking is completed
297 is the ARC4 keys applied to future communication.
298
299
300 Analysis
301 --------
302
303 The creation of the ARC4 encryption keys are created ultimately from
304 nothing more than a Diffie-Hellmann key exchange, excluding constants
305 and publicly-transferred information that is passed through hashing
306 algorithms, and is thus vulnerable to a man-in-the-middle attack.
307 There is no input into the algorithm from a secret key, password
308 or passphrase.  The same effect as this algorithm could therefore be
309 achieved with a well-known industry standard algorithm such as SSL
310 (if you removed SSL's protection against man-in-the-middle attacks).
311
312 The "verification" process involves nothing more than publicly-obtainable
313 information (the 32-byte SWFHash and the SWF size) and publicly-exchanged
314 data (the last 32 bytes of the first server response).
315
316 According to rtmpdump's README:
317
318     Download the swf player you want to use for SWFVerification, unzip it using
319         $ flasm -x file.swf
320     It will show the decompressed filesize, use it for --swfsize
321     Now generate the hash
322         $ openssl sha -sha256 -hmac "Genuine Adobe Flash Player 001" file.swf
323     and use the --swfhash "01234..." option to pass it.  e.g.
324         $ ./rtmpdump --swfhash "123456..." --swfsize 987...
325
326 In other words, the "verification" algorithm basically links the
327 SWF file with the content that is being accessed through it.  The SWF file
328 unfortunately has to be made publicly available via web sites, and so
329 can be easily obtained.
330
331 Thus, the only "security" is given by linking the last 32 bytes of
332 the first server response in to the "verification" algorithm.
333 Unfortunately, this information was also generated with no passwords
334 or secret keys, and is transmitted in-the-clear.
335
336 Overall, then, the Adobe RTMPE algorithm tries to provide end-to-end
337 secrecy in exactly the same way that SSL provides end-to-end secrecy,
338 but the algorithm is subject to man-in-the-middle attacks, provides no
339 security, relies on publicly obtainable information and the algorithm
340 itself to obfuscate the content, and uses no authentication of any kind.
341