OLD | NEW |
| (Empty) |
1 /* | |
2 * rtp_decoder.c | |
3 * | |
4 * decoder structures and functions for SRTP pcap decoder | |
5 * | |
6 * Example: | |
7 * $ wget --no-check-certificate https://raw.githubusercontent.com/gteissier/srt
p-decrypt/master/marseillaise-srtp.pcap | |
8 * $ ./test/rtp_decoder -a -t 0 -e 128 -b aSBrbm93IGFsbCB5b3VyIGxpdHRsZSBzZWNyZX
Rz \ | |
9 * < ~/marseillaise-srtp.pcap | text2pcap -t "%M:%S." -u 10000,10000 - - > ./
marseillaise-rtp.pcap | |
10 * | |
11 * Bernardo Torres <bernardo@torresautomacao.com.br> | |
12 * | |
13 * Some structure and code from https://github.com/gteissier/srtp-decrypt | |
14 */ | |
15 /* | |
16 * | |
17 * Copyright (c) 2001-2006 Cisco Systems, Inc. | |
18 * All rights reserved. | |
19 * | |
20 * Redistribution and use in source and binary forms, with or without | |
21 * modification, are permitted provided that the following conditions | |
22 * are met: | |
23 * | |
24 * Redistributions of source code must retain the above copyright | |
25 * notice, this list of conditions and the following disclaimer. | |
26 * | |
27 * Redistributions in binary form must reproduce the above | |
28 * copyright notice, this list of conditions and the following | |
29 * disclaimer in the documentation and/or other materials provided | |
30 * with the distribution. | |
31 * | |
32 * Neither the name of the Cisco Systems, Inc. nor the names of its | |
33 * contributors may be used to endorse or promote products derived | |
34 * from this software without specific prior written permission. | |
35 * | |
36 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
37 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
38 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
39 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
40 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | |
41 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
42 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
43 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
44 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
45 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
46 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | |
47 * OF THE POSSIBILITY OF SUCH DAMAGE. | |
48 * | |
49 */ | |
50 #include "getopt_s.h" /* for local getopt() */ | |
51 #include <assert.h> /* for assert() */ | |
52 | |
53 #include <pcap.h> | |
54 #include "rtp_decoder.h" | |
55 | |
56 #define MAX_KEY_LEN 96 | |
57 #define MAX_FILTER 256 | |
58 | |
59 int | |
60 main (int argc, char *argv[]) { | |
61 char errbuf[PCAP_ERRBUF_SIZE]; | |
62 bpf_u_int32 pcap_net = 0; | |
63 pcap_t *pcap_handle; | |
64 #if BEW | |
65 struct sockaddr_in local; | |
66 #endif | |
67 sec_serv_t sec_servs = sec_serv_none; | |
68 int c; | |
69 int key_size = 128; | |
70 int tag_size = 8; | |
71 int gcm_on = 0; | |
72 char *input_key = NULL; | |
73 int b64_input = 0; | |
74 char key[MAX_KEY_LEN]; | |
75 struct bpf_program fp; | |
76 char filter_exp[MAX_FILTER] = ""; | |
77 rtp_decoder_t dec; | |
78 srtp_policy_t policy; | |
79 err_status_t status; | |
80 int len; | |
81 int expected_len; | |
82 int do_list_mods = 0; | |
83 | |
84 fprintf(stderr, "Using %s [0x%x]\n", srtp_get_version_string(), srtp_get_versi
on()); | |
85 | |
86 /* initialize srtp library */ | |
87 status = srtp_init(); | |
88 if (status) { | |
89 fprintf(stderr, "error: srtp initialization failed with error code %d\n", st
atus); | |
90 exit(1); | |
91 } | |
92 | |
93 /* check args */ | |
94 while (1) { | |
95 c = getopt_s(argc, argv, "b:k:gt:ae:ld:f:"); | |
96 if (c == -1) { | |
97 break; | |
98 } | |
99 switch (c) { | |
100 case 'b': | |
101 b64_input = 1; | |
102 /* fall thru */ | |
103 case 'k': | |
104 input_key = optarg_s; | |
105 break; | |
106 case 'e': | |
107 key_size = atoi(optarg_s); | |
108 if (key_size != 128 && key_size != 256) { | |
109 fprintf(stderr, "error: encryption key size must be 128 or 256 (%d)\n",
key_size); | |
110 exit(1); | |
111 } | |
112 input_key = malloc(key_size); | |
113 sec_servs |= sec_serv_conf; | |
114 break; | |
115 case 't': | |
116 tag_size = atoi(optarg_s); | |
117 if (tag_size != 8 && tag_size != 16) { | |
118 fprintf(stderr, "error: GCM tag size must be 8 or 16 (%d)\n", tag_size); | |
119 //exit(1); | |
120 } | |
121 break; | |
122 case 'a': | |
123 sec_servs |= sec_serv_auth; | |
124 break; | |
125 case 'g': | |
126 gcm_on = 1; | |
127 sec_servs |= sec_serv_auth; | |
128 break; | |
129 case 'd': | |
130 status = crypto_kernel_set_debug_module(optarg_s, 1); | |
131 if (status) { | |
132 fprintf(stderr, "error: set debug module (%s) failed\n", optarg_s); | |
133 exit(1); | |
134 } | |
135 break; | |
136 case 'f': | |
137 if(strlen(optarg_s) > MAX_FILTER){ | |
138 fprintf(stderr, "error: filter bigger than %d characters\n", MAX_FILTER)
; | |
139 exit(1); | |
140 } | |
141 fprintf(stderr, "Setting filter as %s\n", optarg_s); | |
142 strcpy(filter_exp, optarg_s); | |
143 break; | |
144 case 'l': | |
145 do_list_mods = 1; | |
146 break; | |
147 default: | |
148 usage(argv[0]); | |
149 } | |
150 } | |
151 | |
152 if (do_list_mods) { | |
153 status = crypto_kernel_list_debug_modules(); | |
154 if (status) { | |
155 fprintf(stderr, "error: list of debug modules failed\n"); | |
156 exit(1); | |
157 } | |
158 return 0; | |
159 } | |
160 | |
161 if ((sec_servs && !input_key) || (!sec_servs && input_key)) { | |
162 /* | |
163 * a key must be provided if and only if security services have | |
164 * been requested | |
165 */ | |
166 if(input_key == NULL){ | |
167 fprintf(stderr, "key not provided\n"); | |
168 } | |
169 if(!sec_servs){ | |
170 fprintf(stderr, "no secservs\n"); | |
171 } | |
172 fprintf(stderr, "provided\n"); | |
173 usage(argv[0]); | |
174 } | |
175 | |
176 | |
177 | |
178 /* report security services selected on the command line */ | |
179 fprintf(stderr, "security services: "); | |
180 if (sec_servs & sec_serv_conf) | |
181 fprintf(stderr, "confidentiality "); | |
182 if (sec_servs & sec_serv_auth) | |
183 fprintf(stderr, "message authentication"); | |
184 if (sec_servs == sec_serv_none) | |
185 fprintf(stderr, "none"); | |
186 fprintf(stderr, "\n"); | |
187 | |
188 /* set up the srtp policy and master key */ | |
189 memset(&policy, 0, sizeof(policy)); | |
190 if (sec_servs) { | |
191 /* | |
192 * create policy structure, using the default mechanisms but | |
193 * with only the security services requested on the command line, | |
194 * using the right SSRC value | |
195 */ | |
196 switch (sec_servs) { | |
197 case sec_serv_conf_and_auth: | |
198 if (gcm_on) { | |
199 #ifdef OPENSSL | |
200 switch (key_size) { | |
201 case 128: | |
202 crypto_policy_set_aes_gcm_128_8_auth(&policy.rtp); | |
203 crypto_policy_set_aes_gcm_128_8_auth(&policy.rtcp); | |
204 break; | |
205 case 256: | |
206 crypto_policy_set_aes_gcm_256_8_auth(&policy.rtp); | |
207 crypto_policy_set_aes_gcm_256_8_auth(&policy.rtcp); | |
208 break; | |
209 } | |
210 #else | |
211 fprintf(stderr, "error: GCM mode only supported when using the OpenSSL c
rypto engine.\n"); | |
212 return 0; | |
213 #endif | |
214 } else { | |
215 switch (key_size) { | |
216 case 128: | |
217 crypto_policy_set_rtp_default(&policy.rtp); | |
218 crypto_policy_set_rtcp_default(&policy.rtcp); | |
219 break; | |
220 case 256: | |
221 crypto_policy_set_aes_cm_256_hmac_sha1_80(&policy.rtp); | |
222 crypto_policy_set_rtcp_default(&policy.rtcp); | |
223 break; | |
224 } | |
225 } | |
226 break; | |
227 case sec_serv_conf: | |
228 if (gcm_on) { | |
229 fprintf(stderr, "error: GCM mode must always be used with auth enabled
\n"); | |
230 return -1; | |
231 } else { | |
232 switch (key_size) { | |
233 case 128: | |
234 crypto_policy_set_aes_cm_128_null_auth(&policy.rtp); | |
235 crypto_policy_set_rtcp_default(&policy.rtcp); | |
236 break; | |
237 case 256: | |
238 crypto_policy_set_aes_cm_256_null_auth(&policy.rtp); | |
239 crypto_policy_set_rtcp_default(&policy.rtcp); | |
240 break; | |
241 } | |
242 } | |
243 break; | |
244 case sec_serv_auth: | |
245 if (gcm_on) { | |
246 #ifdef OPENSSL | |
247 switch (key_size) { | |
248 case 128: | |
249 crypto_policy_set_aes_gcm_128_8_only_auth(&policy.rtp); | |
250 crypto_policy_set_aes_gcm_128_8_only_auth(&policy.rtcp); | |
251 break; | |
252 case 256: | |
253 crypto_policy_set_aes_gcm_256_8_only_auth(&policy.rtp); | |
254 crypto_policy_set_aes_gcm_256_8_only_auth(&policy.rtcp); | |
255 break; | |
256 } | |
257 #else | |
258 printf("error: GCM mode only supported when using the OpenSSL crypto eng
ine.\n"); | |
259 return 0; | |
260 #endif | |
261 } else { | |
262 crypto_policy_set_null_cipher_hmac_sha1_80(&policy.rtp); | |
263 crypto_policy_set_rtcp_default(&policy.rtcp); | |
264 } | |
265 break; | |
266 default: | |
267 fprintf(stderr, "error: unknown security service requested\n"); | |
268 return -1; | |
269 } | |
270 | |
271 policy.key = (uint8_t *) key; | |
272 policy.ekt = NULL; | |
273 policy.next = NULL; | |
274 policy.window_size = 128; | |
275 policy.allow_repeat_tx = 0; | |
276 policy.rtp.sec_serv = sec_servs; | |
277 policy.rtcp.sec_serv = sec_servs; //sec_serv_none; /* we don't do RTCP anyw
ay */ | |
278 fprintf(stderr, "setting tag len %d\n", tag_size); | |
279 policy.rtp.auth_tag_len = tag_size; | |
280 | |
281 if (gcm_on && tag_size != 8) { | |
282 fprintf(stderr, "setted tag len %d\n", tag_size); | |
283 policy.rtp.auth_tag_len = tag_size; | |
284 } | |
285 | |
286 /* | |
287 * read key from hexadecimal or base64 on command line into an octet string | |
288 */ | |
289 if (b64_input) { | |
290 int pad; | |
291 expected_len = policy.rtp.cipher_key_len*4/3; | |
292 len = base64_string_to_octet_string(key, &pad, input_key, expected_len); | |
293 if (pad != 0) { | |
294 fprintf(stderr, "error: padding in base64 unexpected\n"); | |
295 exit(1); | |
296 } | |
297 } else { | |
298 expected_len = policy.rtp.cipher_key_len*2; | |
299 len = hex_string_to_octet_string(key, input_key, expected_len); | |
300 } | |
301 /* check that hex string is the right length */ | |
302 if (len < expected_len) { | |
303 fprintf(stderr, | |
304 "error: too few digits in key/salt " | |
305 "(should be %d digits, found %d)\n", | |
306 expected_len, len); | |
307 exit(1); | |
308 } | |
309 if (strlen(input_key) > policy.rtp.cipher_key_len*2) { | |
310 fprintf(stderr, | |
311 "error: too many digits in key/salt " | |
312 "(should be %d hexadecimal digits, found %u)\n", | |
313 policy.rtp.cipher_key_len*2, (unsigned)strlen(input_key)); | |
314 exit(1); | |
315 } | |
316 | |
317 fprintf(stderr, "set master key/salt to %s/", octet_string_hex_string(key, 1
6)); | |
318 fprintf(stderr, "%s\n", octet_string_hex_string(key+16, 14)); | |
319 | |
320 } else { | |
321 /* | |
322 * we're not providing security services, so set the policy to the | |
323 * null policy | |
324 * | |
325 * Note that this policy does not conform to the SRTP | |
326 * specification, since RTCP authentication is required. However, | |
327 * the effect of this policy is to turn off SRTP, so that this | |
328 * application is now a vanilla-flavored RTP application. | |
329 */ | |
330 policy.key = (uint8_t *)key; | |
331 policy.ssrc.type = ssrc_specific; | |
332 policy.rtp.cipher_type = NULL_CIPHER; | |
333 policy.rtp.cipher_key_len = 0; | |
334 policy.rtp.auth_type = NULL_AUTH; | |
335 policy.rtp.auth_key_len = 0; | |
336 policy.rtp.auth_tag_len = 0; | |
337 policy.rtp.sec_serv = sec_serv_none; | |
338 policy.rtcp.cipher_type = NULL_CIPHER; | |
339 policy.rtcp.cipher_key_len = 0; | |
340 policy.rtcp.auth_type = NULL_AUTH; | |
341 policy.rtcp.auth_key_len = 0; | |
342 policy.rtcp.auth_tag_len = 0; | |
343 policy.rtcp.sec_serv = sec_serv_none; | |
344 policy.window_size = 0; | |
345 policy.allow_repeat_tx = 0; | |
346 policy.ekt = NULL; | |
347 policy.next = NULL; | |
348 } | |
349 | |
350 pcap_handle = pcap_open_offline("-", errbuf); | |
351 | |
352 if (!pcap_handle) { | |
353 fprintf(stderr, "libpcap failed to open file '%s'\n", errbuf); | |
354 exit(1); | |
355 } | |
356 assert(pcap_handle != NULL); | |
357 if ((pcap_compile(pcap_handle, &fp, filter_exp, 1, pcap_net)) == -1){ | |
358 fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, | |
359 pcap_geterr(pcap_handle)); | |
360 return (2); | |
361 } | |
362 if (pcap_setfilter(pcap_handle, &fp) == -1){ | |
363 fprintf(stderr, "couldn't install filter %s: %s\n", filter_exp, | |
364 pcap_geterr(pcap_handle)); | |
365 return (2); | |
366 } | |
367 dec = rtp_decoder_alloc(); | |
368 if (dec == NULL) { | |
369 fprintf(stderr, "error: malloc() failed\n"); | |
370 exit(1); | |
371 } | |
372 fprintf(stderr, "Starting decoder\n"); | |
373 rtp_decoder_init(dec, policy); | |
374 | |
375 pcap_loop(pcap_handle, 0, rtp_decoder_handle_pkt, (u_char *)dec); | |
376 | |
377 rtp_decoder_deinit_srtp(dec); | |
378 rtp_decoder_dealloc(dec); | |
379 | |
380 status = srtp_shutdown(); | |
381 if (status) { | |
382 fprintf(stderr, "error: srtp shutdown failed with error code %d\n", status); | |
383 exit(1); | |
384 } | |
385 | |
386 return 0; | |
387 } | |
388 | |
389 | |
390 void | |
391 usage(char *string) { | |
392 | |
393 fprintf(stderr, "usage: %s [-d <debug>]* [[-k][-b] <key> [-a][-e]]\n" | |
394 "or %s -l\n" | |
395 "where -a use message authentication\n" | |
396 " -e <key size> use encryption (use 128 or 256 for key size)\n" | |
397 " -g Use AES-GCM mode (must be used with -e)\n" | |
398 " -t <tag size> Tag size to use in GCM mode (use 8 or 16)\n" | |
399 " -k <key> sets the srtp master key given in hexadecimal\n" | |
400 " -b <key> sets the srtp master key given in base64\n" | |
401 " -l list debug modules\n" | |
402 " -f \"<pcap filter>\" to filter only the desired SRTP packets\n" | |
403 " -d <debug> turn on debugging for module <debug>\n", | |
404 string, string); | |
405 exit(1); | |
406 | |
407 } | |
408 | |
409 rtp_decoder_t | |
410 rtp_decoder_alloc(void) { | |
411 return (rtp_decoder_t)malloc(sizeof(rtp_decoder_ctx_t)); | |
412 } | |
413 | |
414 void | |
415 rtp_decoder_dealloc(rtp_decoder_t rtp_ctx) { | |
416 free(rtp_ctx); | |
417 } | |
418 | |
419 err_status_t | |
420 rtp_decoder_init_srtp(rtp_decoder_t decoder, unsigned int ssrc) { | |
421 decoder->policy.ssrc.value = htonl(ssrc); | |
422 return srtp_create(&decoder->srtp_ctx, &decoder->policy); | |
423 } | |
424 | |
425 int | |
426 rtp_decoder_deinit_srtp(rtp_decoder_t decoder) { | |
427 return srtp_dealloc(decoder->srtp_ctx); | |
428 } | |
429 | |
430 int | |
431 rtp_decoder_init(rtp_decoder_t dcdr, srtp_policy_t policy){ | |
432 dcdr->rtp_offset = DEFAULT_RTP_OFFSET; | |
433 dcdr->srtp_ctx = NULL; | |
434 dcdr->start_tv.tv_usec = 0; | |
435 dcdr->start_tv.tv_sec = 0; | |
436 dcdr->frame_nr = -1; | |
437 dcdr->policy = policy; | |
438 dcdr->policy.ssrc.type = ssrc_specific; | |
439 return 0; | |
440 } | |
441 | |
442 /* | |
443 * decodes key as base64 | |
444 */ | |
445 | |
446 void hexdump(const void *ptr, size_t size) { | |
447 int i, j; | |
448 const unsigned char *cptr = ptr; | |
449 | |
450 for (i = 0; i < size; i += 16) { | |
451 fprintf(stdout, "%04x ", i); | |
452 for (j = 0; j < 16 && i+j < size; j++) { | |
453 fprintf(stdout, "%02x ", cptr[i+j]); | |
454 } | |
455 fprintf(stdout, "\n"); | |
456 } | |
457 } | |
458 | |
459 void | |
460 rtp_decoder_handle_pkt(u_char *arg, const struct pcap_pkthdr *hdr, | |
461 const u_char *bytes){ | |
462 rtp_decoder_t dcdr = (rtp_decoder_t)arg; | |
463 int pktsize; | |
464 struct timeval delta; | |
465 int octets_recvd; | |
466 err_status_t status; | |
467 dcdr->frame_nr++; | |
468 | |
469 if (dcdr->start_tv.tv_sec == 0 && dcdr->start_tv.tv_sec == 0) { | |
470 dcdr->start_tv = hdr->ts; | |
471 } | |
472 | |
473 if (hdr->caplen < dcdr->rtp_offset) { | |
474 return; | |
475 } | |
476 const void *rtp_packet = bytes + dcdr->rtp_offset; | |
477 | |
478 memcpy((void *)&dcdr->message, rtp_packet, hdr->caplen - dcdr->rtp_offset); | |
479 pktsize = hdr->caplen - dcdr->rtp_offset; | |
480 octets_recvd = pktsize; | |
481 | |
482 if (octets_recvd == -1) { | |
483 return; | |
484 } | |
485 | |
486 /* verify rtp header */ | |
487 if (dcdr->message.header.version != 2) { | |
488 return; //return -1; | |
489 } | |
490 if(dcdr->srtp_ctx == NULL){ | |
491 status = rtp_decoder_init_srtp(dcdr, dcdr->message.header.ssrc); | |
492 if (status) { | |
493 exit(1); | |
494 } | |
495 } | |
496 if(dcdr->srtp_ctx != NULL){ | |
497 } | |
498 status = srtp_unprotect(dcdr->srtp_ctx, &dcdr->message, &octets_recvd); | |
499 if (status){ | |
500 return; | |
501 } | |
502 timersub(&hdr->ts, &dcdr->start_tv, &delta); | |
503 fprintf(stdout, "%02ld:%02ld.%06lu\n", delta.tv_sec/60, delta.tv_sec%60, delta
.tv_usec); | |
504 hexdump(&dcdr->message, pktsize); | |
505 } | |
506 | |
507 void rtp_print_error(err_status_t status, char *message){ | |
508 fprintf(stderr, | |
509 "error: %s %d%s\n", message, status, | |
510 status == err_status_replay_fail ? " (replay check failed)" : | |
511 status == err_status_bad_param ? " (bad param)" : | |
512 status == err_status_no_ctx ? " (no context)" : | |
513 status == err_status_cipher_fail ? " (cipher failed)" : | |
514 status == err_status_key_expired ? " (key expired)" : | |
515 status == err_status_auth_fail ? " (auth check failed)" : ""); | |
516 } | |
OLD | NEW |