cisco / libsrtp

Library for SRTP (Secure Realtime Transport Protocol)
Other
1.21k stars 474 forks source link

Allow testing with pcap file or capture #45

Closed bernardotorres closed 9 years ago

bernardotorres commented 10 years ago

A good test would be allowing to capture on the wire or offline a SRTP stream.

I have derived code from https://github.com/gteissier/srtp-decrypt (which caused me a few headaches because it didn't work in all cases) and joined it with rtpw test code to make it possible.

There is room for improvement, if you have any particular request or opinion, please express so I can improve the code for it to be approved.

Thanks for libsrtp, btw.

Makefile: CFLAGS=-g -Os -Wall

all: $(CC) -g decrypt-srtp.c -o decrypt-srtp -I /usr/local/include/srtp/ -l pcap -lgcrypt -lsrtp check: ./decrypt-srtp -k aSBrbm93IGFsbCB5b3VyIGxpdHRsZSBzZWNyZXRz < ./marseillaise-srtp.pcap | text2pcap -t "%M:%S." -u 10000,10000 - - > ./marseillaise-rtp.pcap

The code is below:

include <sys/types.h>

include

include

include

include

include

include <arpa/inet.h>

include

include

include "rtp.h"

include "rtp_priv.h"

include "srtp.h"

include

define PRINT_DEBUG 1

define VERBOSE_DEBUG 1

define DICT_FILE "/usr/share/dict/words"

define USEC_RATE (5e5)

define MAX_WORD_LEN 128

define ADDR_IS_MULTICAST(a) IN_MULTICAST(htonl(a))

define MAX_KEY_LEN 96

srtp_t srtp_ctx = NULL;

rtp_msg_t message;

/*

static const char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static unsigned char shiftb64(unsigned char c) { char *p = strchr(b64chars, c); assert(p); return p-b64chars; }

static void decode_block(unsigned char in, unsigned char out) { unsigned char shifts[4]; int i;

for (i = 0; i < 4; i++) { shifts[i] = shiftb64(in[i]); }

out[0] = (shifts[0]<<2)|(shifts[1]>>4); out[1] = (shifts[1]<<4)|(shifts[2]>>2); out[2] = (shifts[2]<<6)|shifts[3]; }

char packet_string[MTU];

char srtp_packet_to_string(srtp_hdr_t hdr, int pkt_octet_len) { int octets_in_rtp_header = 12; uint8_t data = ((uint8_t )hdr)+octets_in_rtp_header; int hex_len = pkt_octet_len-octets_in_rtp_header;

/* sanity checking */ if ((hdr == NULL) || (pkt_octet_len > MTU)) return NULL;

/* write packet into string */ sprintf(packet_string, "(s)rtp packet: {\n" " version:\t%d\n" " p:\t\t%d\n" " x:\t\t%d\n" " cc:\t\t%d\n" " m:\t\t%d\n" " pt:\t\t%x\n" " seq:\t\t%x\n" " ts:\t\t%x\n" " ssrc:\t%x\n" " data:\t%s\n" "} (%d octets in total)\n", hdr->version, hdr->p, hdr->x, hdr->cc, hdr->m, hdr->pt, hdr->seq, hdr->ts, hdr->ssrc, octet_string_hex_string(data, hex_len), pkt_octet_len);

return packet_string; }

void usage(char *string) {

printf("usage: %s [-d ]* [-k [-a][-e]]\n" "or %s -l\n" "where -a use message authentication\n" " -e use encryption (use 128 or 256 for key size)\n" " -g Use AES-GCM mode (must be used with -e)\n" " -k sets the srtp master key\n" " -l list debug modules\n" " -d turn on debugging for module \n", string, string); exit(1);

}

static void decode_sdes(unsigned char in, unsigned char key) { int i; size_t len = strlen((char *) in); assert(len == 40); unsigned char raw[30];

for (i = 0; 4_i < len; i++) { decode_block(in+4_i, raw+3*i); }

memcpy(key, octet_string_hex_string(raw, 30), 60); }

static void hexdump(const void ptr, size_t size) { int i, j; const unsigned char cptr = ptr;

for (i = 0; i < size; i += 16) { printf("%04x ", i); for (j = 0; j < 16 && i+j < size; j++) { printf("%02x ", cptr[i+j]); } printf("\n"); } }

static int rtp_offset = 42; /* 14 + 20 + 8 */; static int frame_nr = -1; static struct timeval start_tv = {0, 0};

static void handle_pkt(u_char arg, const struct pcap_pkthdr hdr, const u_char bytes) { int pktsize; struct timeval delta; int octets_recvd; err_status_t stat; int len = NULL; void *msg = NULL; frame_nr += 1; if (start_tv.tv_sec == 0 && start_tv.tv_sec == 0) { start_tv = hdr->ts; }

if (hdr->caplen < rtp_offset) { fprintf(stderr, "frame %d dropped: too short\n", frame_nr); return; } void *rtp_packet = bytes + rtp_offset;

//memcpy(buffer, bytes + rtp_offset, hdr->caplen - rtpoffset); memcpy((void )&message, rtp_packet, hdr->caplen - rtpoffset); / message.header.seq = htons(message.header.seq); message.header.ssrc = htonl(message.header.ssrc); message.header.ts = htonl(message.header.ts); */ pktsize = hdr->caplen - rtp_offset; octets_recvd = pktsize;

if (octets_recvd == -1) { printf("octects less than 1.\n"); //*len = 0; //return -1; return; }

/* verify rtp header _/ if (message.header.version != 2) { printf("rtp version is not 2.\n"); //_len = 0; return; //return -1; }

if PRINT_DEBUG

// printf("%d octets received from SSRC %u\n", // octets_recvd, message.header.ssrc);

endif

if VERBOSE_DEBUG

//printf("PACKET: %s\n", ); // hexdump(bytes + rtp_offset, octets_recvd); // printf(srtp_packet_to_string(&message.header, octets_recvd));

endif

/* apply srtp */ stat = srtp_unprotect(srtp_ctx, &message.header, &octets_recvd); if (stat) { fprintf(stderr, "error: srtp unprotection failed with code %d%s\n", stat, stat == err_status_replay_fail ? " (replay check failed)" : stat == err_status_bad_param ? " (bad param)" : stat == err_status_no_ctx ? " (no context)" : stat == err_status_cipher_fail ? " (cipher failed)" : stat == err_status_key_expired ? " (key expired)" : stat == err_status_auth_fail ? " (auth check failed)" : ""); //return -1; return; } //strncpy(msg, rtp_packet, octets_recvd);

//printf(srtp_packet_to_string(&message.header, octets_recvd)); timersub(&hdr->ts, &start_tv, &delta); printf("%02ld:%02ld.%06lu\n", delta.tv_sec/60, delta.tv_sec%60, delta.tv_usec); hexdump(&message.header, pktsize); }

int main(int argc, char *argv) { int c; char errbuf[PCAP_ERRBUF_SIZE]; pcap_t pcap;

char _dictfile = DICT_FILE; FILE dict; char word[MAX_WORD_LEN]; int sock, ret; struct in_addr rcvr_addr; struct sockaddr_in name; struct ip_mreq mreq; struct sockaddr_in local; sec_serv_t sec_servs = sec_serv_none; unsigned char ttl = 5; int key_size = 128; int gcm_on = 0; char input_key = malloc(MAX_KEY_LEN); char address = NULL; char key[MAX_KEY_LEN]; unsigned short port = 0; rtp_sender_t snd; srtp_policy_t policy; // const struct srtp_policy_t policy; err_status_t status; int len; int do_list_mods = 0; uint32t ssrc = 0xdeadbeef; / ssrc value hardcoded for now /

status = srtp_init();

/* check args / while (1) { c = getopt(argc, argv, "b:k:rsae:ld:"); if (c == -1) { break; } switch (c) { case 'b': //input_key = (unsigned char ) optarg; decode_sdes(optarg, input_key); break; case 'k': input_key = (unsigned char *) optarg; break; case 'e': key_size = atoi(optarg); if (key_size != 128 && key_size != 256) { printf("error: encryption key size must be 128 or 256 (%d)\n", key_size); exit(1); } sec_servs |= sec_serv_conf; break; case 'a': sec_servs |= sec_serv_auth; break; case 'd': status = crypto_kernel_set_debug_module(optarg, 1); if (status) { printf("error: set debug module (%s) failed\n", optarg); exit(1); } break; case 'l': do_list_mods = 1; break; default: usage(argv[0]); } }

if (do_list_mods) { status = crypto_kernel_list_debug_modules(); if (status) { printf("error: list of debug modules failed\n"); exit(1); } return 0; } if ((sec_servs && !input_key) || (!sec_servs && input_key)) { /* * a key must be provided if and only if security services have * been requested */ fprintf(stderr, "Key was not provided!\n"); usage(argv[0]); }

/* report security services selected on the command line */ printf("security services: "); if (sec_servs & sec_serv_conf) printf("confidentiality "); if (sec_servs & sec_serv_auth) printf("message authentication"); if (sec_servs == sec_serv_none) printf("none"); printf("\n");

/* set up the srtp policy and master key _/ if (secservs) { / * create policy structure, using the default mechanisms but * with only the security services requested on the command line, * using the right SSRC value */ switch (sec_servs) { case sec_serv_conf_and_auth: if (gcm_on) {

ifdef OPENSSL

    switch (key_size) {
    case 128:
      crypto_policy_set_aes_gcm_128_8_auth(&policy.rtp);
      crypto_policy_set_aes_gcm_128_8_auth(&policy.rtcp);
      break;
    case 256:
      crypto_policy_set_aes_gcm_256_8_auth(&policy.rtp);
      crypto_policy_set_aes_gcm_256_8_auth(&policy.rtcp);
      break;
    }

else

    printf("error: GCM mode only supported when using the OpenSSL crypto engine.\n");
    return 0;

endif

  } else {
    switch (key_size) {
    case 128:
      printf("Setting default aes_cm_128_hmac_sha1_80 policy\n");
      crypto_policy_set_rtp_default(&policy.rtp);
      crypto_policy_set_rtcp_default(&policy.rtcp);
      break;
    case 256:
      crypto_policy_set_aes_cm_256_hmac_sha1_80(&policy.rtp);
      crypto_policy_set_rtcp_default(&policy.rtcp);
      break;
    }
  }
  break;
case sec_serv_conf:
  if (gcm_on) {
      printf("error: GCM mode must always be used with auth enabled\n");
      return -1;
  } else {
    switch (key_size) {
    case 128:
      crypto_policy_set_aes_cm_128_null_auth(&policy.rtp);
      crypto_policy_set_rtcp_default(&policy.rtcp);
      break;
    case 256:
      crypto_policy_set_aes_cm_256_null_auth(&policy.rtp);
      crypto_policy_set_rtcp_default(&policy.rtcp);
      break;
    }
  }
  break;
case sec_serv_auth:
  if (gcm_on) {

ifdef OPENSSL

    switch (key_size) {
    case 128:
      crypto_policy_set_aes_gcm_128_8_only_auth(&policy.rtp);
      crypto_policy_set_aes_gcm_128_8_only_auth(&policy.rtcp);
      break;
    case 256:
      crypto_policy_set_aes_gcm_256_8_only_auth(&policy.rtp);
      crypto_policy_set_aes_gcm_256_8_only_auth(&policy.rtcp);
      break;
    }

else

    printf("error: GCM mode only supported when using the OpenSSL crypto engine.\n");
    return 0;

endif

  } else {
    printf("Setting null_cipher_hmac_sha1_80 policy.\n");
    crypto_policy_set_null_cipher_hmac_sha1_80(&policy.rtp);
    crypto_policy_set_rtcp_default(&policy.rtcp);
  }
  break;
default:
  printf("error: unknown security service requested\n");
  return -1;
}
}
policy.ssrc.type  = ssrc_specific;
policy.ssrc.value = 1479099981;
policy.key  = (uint8_t *) key;
policy.ekt  = NULL;
policy.next = NULL;
policy.window_size = 128;
policy.allow_repeat_tx = 0;
policy.rtp.sec_serv = sec_servs;
policy.rtp.auth_tag_len = 0;
policy.rtcp.sec_serv = sec_servs;  /* we don't do RTCP anyway */

/*
 * read key from hexadecimal on command line into an octet string
 */
len = hex_string_to_octet_string(key, input_key, policy.rtp.cipher_key_len*2);

/* check that hex string is the right length */
if (len < policy.rtp.cipher_key_len*2) {
  fprintf(stderr,
          "error: too few digits in key/salt "
          "(should be %d hexadecimal digits, found %d)\n",
          policy.rtp.cipher_key_len*2, len);
  exit(1);
}
if (strlen(input_key) > policy.rtp.cipher_key_len*2) {
  fprintf(stderr,
          "error: too many digits in key/salt "
          "(should be %d hexadecimal digits, found %u)\n",
          policy.rtp.cipher_key_len*2, (unsigned)strlen(input_key));
  exit(1);
}

printf("set master key/salt to %s/", octet_string_hex_string(key, 16));
printf("%s\n", octet_string_hex_string(key+16, 14));

/* set header values */

/ rcvr->message.header.ssrc = htonl(ssrc); rcvr->message.header.ts = 0; rcvr->message.header.seq = 0; rcvr->message.header.m = 0; rcvr->message.header.pt = 0x1; rcvr->message.header.version = 2; rcvr->message.header.p = 0; rcvr->message.header.x = 0; rcvr->message.header.cc = 0; /

printf("Creating.\n");
srtp_create(&srtp_ctx, &policy);
printf("Created.\n");

pcap = pcap_open_offline("-", errbuf);
if (!pcap) {
    fprintf(stderr, "libpcap failed to open file '%s'\n", errbuf);
    exit(1);
}
assert(pcap != NULL);

pcap_loop(pcap, 0, handle_pkt, NULL);

return 0;

}

jfigus commented 10 years ago

It's not 100% clear what you're proposing. Are you looking to add an option to rtpw that dumps the decrypted data to a pcap file? It doesn't appear all the required code was provided. If you have a contribution, it's probably best to fork libsrtp, apply your changes to your fork, test, and then submit a pull request.

bernardotorres commented 10 years ago

Actually is extract rtp data from a pcap file and decrypt it. But yeah, I guess it's better to fork with a proposed code.

jfigus commented 9 years ago

Closing this issue for now. Please submit a pull request when the code is ready.