Support header extensions encryption (RFC 6904).
Header ids to encrypt can be specified through the "srtp_policy_t".
Added validation testcase using test vectors from RFC. Implements issue #124.
diff --git a/test/srtp_driver.c b/test/srtp_driver.c
index 0406489..b48030d 100644
--- a/test/srtp_driver.c
+++ b/test/srtp_driver.c
@@ -64,6 +64,9 @@
 srtp_validate(void);
 
 srtp_err_status_t
+srtp_validate_encrypted_extensions_headers(void);
+
+srtp_err_status_t
 srtp_validate_aes_256(void);
 
 srtp_err_status_t
@@ -88,7 +91,7 @@
 srtp_do_rejection_timing(const srtp_policy_t *policy);
 
 srtp_err_status_t
-srtp_test(const srtp_policy_t *policy);
+srtp_test(const srtp_policy_t *policy, int extension_header);
 
 srtp_err_status_t
 srtcp_test(const srtp_policy_t *policy);
@@ -235,7 +238,14 @@
         /* loop over policy array, testing srtp and srtcp for each policy */
         while (*policy != NULL) {
             printf("testing srtp_protect and srtp_unprotect\n");
-            if (srtp_test(*policy) == srtp_err_status_ok) {
+            if (srtp_test(*policy, 0) == srtp_err_status_ok) {
+                printf("passed\n\n");
+            } else{
+                printf("failed\n");
+                exit(1);
+            }
+            printf("testing srtp_protect and srtp_unprotect with encrypted extensions headers\n");
+            if (srtp_test(*policy, 1) == srtp_err_status_ok) {
                 printf("passed\n\n");
             } else{
                 printf("failed\n");
@@ -258,7 +268,14 @@
             exit(1);
         }
         printf("testing srtp_protect and srtp_unprotect with big policy\n");
-        if (srtp_test(big_policy) == srtp_err_status_ok) {
+        if (srtp_test(big_policy, 0) == srtp_err_status_ok) {
+            printf("passed\n\n");
+        } else{
+            printf("failed\n");
+            exit(1);
+        }
+        printf("testing srtp_protect and srtp_unprotect with big policy and encrypted extensions headers\n");
+        if (srtp_test(big_policy, 1) == srtp_err_status_ok) {
             printf("passed\n\n");
         } else{
             printf("failed\n");
@@ -273,7 +290,15 @@
         /* run test on wildcard policy */
         printf("testing srtp_protect and srtp_unprotect on "
                "wildcard ssrc policy\n");
-        if (srtp_test(&wildcard_policy) == srtp_err_status_ok) {
+        if (srtp_test(&wildcard_policy, 0) == srtp_err_status_ok) {
+            printf("passed\n\n");
+        } else{
+            printf("failed\n");
+            exit(1);
+        }
+        printf("testing srtp_protect and srtp_unprotect on "
+               "wildcard ssrc policy and encrypted extensions headers\n");
+        if (srtp_test(&wildcard_policy, 1) == srtp_err_status_ok) {
             printf("passed\n\n");
         } else{
             printf("failed\n");
@@ -292,6 +317,14 @@
             printf("failed\n");
             exit(1);
         }
+        printf("testing srtp_protect and srtp_unprotect against "
+               "reference packets with encrypted extensions headers\n");
+        if (srtp_validate_encrypted_extensions_headers() == srtp_err_status_ok)
+            printf("passed\n\n");
+        else {
+            printf("failed\n");
+            exit(1);
+        }
 
 //FIXME: need to get this working with the OpenSSL AES module
 #ifndef OPENSSL
@@ -348,6 +381,7 @@
         int ignore;
         double mips = mips_estimate(1000000000, &ignore);
 
+        memset(&policy, 0, sizeof(policy));
         srtp_crypto_policy_set_rtp_default(&policy.rtp);
         srtp_crypto_policy_set_rtcp_default(&policy.rtcp);
         policy.ssrc.type  = ssrc_specific;
@@ -453,6 +487,66 @@
     return hdr;
 }
 
+srtp_hdr_t *
+srtp_create_test_packet_ext_hdr(int pkt_octet_len, uint32_t ssrc) {
+  int i;
+  uint8_t *buffer;
+  srtp_hdr_t *hdr;
+  int bytes_in_hdr = 12;
+  uint8_t extension_header[12] = {
+    /* one-byte header */
+    0xbe, 0xde,
+    /* size */
+    0x00, 0x02,
+    /* id 1, length 1 (i.e. 2 bytes) */
+    0x11,
+    /* payload */
+    0xca,
+    0xfe,
+    /* padding */
+    0x00,
+    /* id 2, length 0 (i.e. 1 byte) */
+    0x20,
+    /* payload */
+    0xba,
+    /* padding */
+    0x00,
+    0x00
+  };
+
+  /* allocate memory for test packet */
+  hdr = (srtp_hdr_t*) malloc(pkt_octet_len + bytes_in_hdr
+           + sizeof(extension_header) + SRTP_MAX_TRAILER_LEN + 4);
+  if (!hdr)
+    return NULL;
+
+  hdr->version = 2;              /* RTP version two     */
+  hdr->p    = 0;                 /* no padding needed   */
+  hdr->x    = 1;                 /* no header extension */
+  hdr->cc   = 0;                 /* no CSRCs            */
+  hdr->m    = 0;                 /* marker bit          */
+  hdr->pt   = 0xf;               /* payload type        */
+  hdr->seq  = htons(0x1234);     /* sequence number     */
+  hdr->ts   = htonl(0xdecafbad); /* timestamp           */
+  hdr->ssrc = htonl(ssrc);       /* synch. source       */
+
+  buffer = (uint8_t *)hdr;
+  buffer += bytes_in_hdr;
+
+  memcpy(buffer, extension_header, sizeof(extension_header));
+  buffer += sizeof(extension_header);
+
+  /* set RTP data to 0xab */
+  for (i=0; i < pkt_octet_len; i++)
+    *buffer++ = 0xab;
+
+  /* set post-data value to 0xffff to enable overrun checking */
+  for (i=0; i < SRTP_MAX_TRAILER_LEN+4; i++)
+    *buffer++ = 0xff;
+
+  return hdr;
+}
+
 void
 srtp_do_timing (const srtp_policy_t *policy)
 {
@@ -633,7 +727,7 @@
 }
 
 srtp_err_status_t
-srtp_test (const srtp_policy_t *policy)
+srtp_test (const srtp_policy_t *policy, int extension_header)
 {
     int i;
     srtp_t srtp_sender;
@@ -647,8 +741,17 @@
     int tag_length = policy->rtp.auth_tag_len;
     uint32_t ssrc;
     srtp_policy_t *rcvr_policy;
+    srtp_policy_t tmp_policy;
+    int header = 1;
 
-    err_check(srtp_create(&srtp_sender, policy));
+    if (extension_header) {
+        memcpy(&tmp_policy, policy, sizeof(srtp_policy_t));
+        tmp_policy.enc_xtn_hdr = &header;
+        tmp_policy.enc_xtn_hdr_count = 1;
+        err_check(srtp_create(&srtp_sender, &tmp_policy));
+    } else {
+        err_check(srtp_create(&srtp_sender, policy));
+    }
 
     /* print out policy */
     err_check(srtp_session_print_policy(srtp_sender));
@@ -664,12 +767,18 @@
         ssrc = policy->ssrc.value;
     }
     msg_len_octets = 28;
-    hdr = srtp_create_test_packet(msg_len_octets, ssrc);
+    if (extension_header) {
+        hdr = srtp_create_test_packet_ext_hdr(msg_len_octets, ssrc);
+        hdr2 = srtp_create_test_packet_ext_hdr(msg_len_octets, ssrc);
+    } else {
+        hdr = srtp_create_test_packet(msg_len_octets, ssrc);
+        hdr2 = srtp_create_test_packet(msg_len_octets, ssrc);
+    }
 
     if (hdr == NULL) {
+        free(hdr2);
         return srtp_err_status_alloc_fail;
     }
-    hdr2 = srtp_create_test_packet(msg_len_octets, ssrc);
     if (hdr2 == NULL) {
         free(hdr);
         return srtp_err_status_alloc_fail;
@@ -677,6 +786,9 @@
 
     /* set message length */
     len = msg_len_octets;
+    if (extension_header) {
+        len += 12;
+    }
 
     debug_print(mod_driver, "before protection:\n%s",
                 srtp_packet_to_string(hdr, len));
@@ -707,6 +819,9 @@
      */
     pkt_end = (uint8_t*)hdr + sizeof(srtp_hdr_t)
               + msg_len_octets + tag_length;
+    if (extension_header) {
+        pkt_end += 12;
+    }
     for (i = 0; i < 4; i++) {
         if (pkt_end[i] != 0xff) {
             fprintf(stdout, "overwrite in srtp_protect() function "
@@ -757,9 +872,16 @@
         free(hdr2);
         return srtp_err_status_alloc_fail;
     }
-    memcpy(rcvr_policy, policy, sizeof(srtp_policy_t));
-    if (policy->ssrc.type == ssrc_any_outbound) {
-        rcvr_policy->ssrc.type = ssrc_any_inbound;
+    if (extension_header) {
+        memcpy(rcvr_policy, &tmp_policy, sizeof(srtp_policy_t));
+        if (tmp_policy.ssrc.type == ssrc_any_outbound) {
+            rcvr_policy->ssrc.type = ssrc_any_inbound;
+        }
+    } else {
+        memcpy(rcvr_policy, policy, sizeof(srtp_policy_t));
+        if (policy->ssrc.type == ssrc_any_outbound) {
+            rcvr_policy->ssrc.type = ssrc_any_inbound;
+        }
     }
 
     err_check(srtp_create(&srtp_rcvr, rcvr_policy));
@@ -813,6 +935,9 @@
 
         /* set message length */
         len = msg_len_octets;
+        if (extension_header) {
+            len += 12;
+        }
 
         /* apply protection */
         err_check(srtp_protect(srtp_sender, hdr, &len));
@@ -1096,6 +1221,20 @@
                serv_descr[stream->rtcp_services],
                srtp_rdbx_get_window_size(&stream->rtp_rdbx),
                stream->allow_repeat_tx ? "true" : "false");
+
+        printf("# Encrypted extension headers: ");
+        if (stream->enc_xtn_hdr && stream->enc_xtn_hdr_count > 0) {
+            int* enc_xtn_hdr = stream->enc_xtn_hdr;
+            int count = stream->enc_xtn_hdr_count;
+            while (count > 0) {
+                printf("%d ", *enc_xtn_hdr);
+                enc_xtn_hdr++;
+                count--;
+            }
+            printf("\n");
+        } else {
+            printf("none\n");
+        }
     }
 
     /* loop over streams in session, printing the policy of each */
@@ -1124,6 +1263,20 @@
                srtp_rdbx_get_window_size(&stream->rtp_rdbx),
                stream->allow_repeat_tx ? "true" : "false");
 
+        printf("# Encrypted extension headers: ");
+        if (stream->enc_xtn_hdr && stream->enc_xtn_hdr_count > 0) {
+            int* enc_xtn_hdr = stream->enc_xtn_hdr;
+            int count = stream->enc_xtn_hdr_count;
+            while (count > 0) {
+                printf("%d ", *enc_xtn_hdr);
+                enc_xtn_hdr++;
+                count--;
+            }
+            printf("\n");
+        } else {
+            printf("none\n");
+        }
+
         /* advance to next stream in the list */
         stream = stream->next;
     }
@@ -1274,6 +1427,7 @@
      * create a session with a single stream using the default srtp
      * policy and with the SSRC value 0xcafebabe
      */
+    memset(&policy, 0, sizeof(policy));
     srtp_crypto_policy_set_rtp_default(&policy.rtp);
     srtp_crypto_policy_set_rtcp_default(&policy.rtcp);
     policy.ssrc.type  = ssrc_specific;
@@ -1342,6 +1496,121 @@
     return srtp_err_status_ok;
 }
 
+/*
+ * Test vectors taken from RFC 6904, Appendix A
+ */
+srtp_err_status_t
+srtp_validate_encrypted_extensions_headers() {
+    unsigned char test_key_ext_headers[30] = {
+        0xe1, 0xf9, 0x7a, 0x0d, 0x3e, 0x01, 0x8b, 0xe0,
+        0xd6, 0x4f, 0xa3, 0x2c, 0x06, 0xde, 0x41, 0x39,
+        0x0e, 0xc6, 0x75, 0xad, 0x49, 0x8a, 0xfe, 0xeb,
+        0xb6, 0x96, 0x0b, 0x3a, 0xab, 0xe6
+    };
+    uint8_t srtp_plaintext_ref[56] = {
+        0x90, 0x0f, 0x12, 0x34, 0xde, 0xca, 0xfb, 0xad,
+        0xca, 0xfe, 0xba, 0xbe, 0xBE, 0xDE, 0x00, 0x06,
+        0x17, 0x41, 0x42, 0x73, 0xA4, 0x75, 0x26, 0x27,
+        0x48, 0x22, 0x00, 0x00, 0xC8, 0x30, 0x8E, 0x46,
+        0x55, 0x99, 0x63, 0x86, 0xB3, 0x95, 0xFB, 0x00,
+        0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+        0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab
+    };
+    uint8_t srtp_plaintext[66] = {
+        0x90, 0x0f, 0x12, 0x34, 0xde, 0xca, 0xfb, 0xad,
+        0xca, 0xfe, 0xba, 0xbe, 0xBE, 0xDE, 0x00, 0x06,
+        0x17, 0x41, 0x42, 0x73, 0xA4, 0x75, 0x26, 0x27,
+        0x48, 0x22, 0x00, 0x00, 0xC8, 0x30, 0x8E, 0x46,
+        0x55, 0x99, 0x63, 0x86, 0xB3, 0x95, 0xFB, 0x00,
+        0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+        0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00
+    };
+    uint8_t srtp_ciphertext[66] = {
+        0x90, 0x0f, 0x12, 0x34, 0xde, 0xca, 0xfb, 0xad,
+        0xca, 0xfe, 0xba, 0xbe, 0xBE, 0xDE, 0x00, 0x06,
+        0x17, 0x58, 0x8A, 0x92, 0x70, 0xF4, 0xE1, 0x5E,
+        0x1C, 0x22, 0x00, 0x00, 0xC8, 0x30, 0x95, 0x46,
+        0xA9, 0x94, 0xF0, 0xBC, 0x54, 0x78, 0x97, 0x00,
+        0x4e, 0x55, 0xdc, 0x4c, 0xe7, 0x99, 0x78, 0xd8,
+        0x8c, 0xa4, 0xd2, 0x15, 0x94, 0x9d, 0x24, 0x02,
+        0x5a, 0x46, 0xb3, 0xca, 0x35, 0xc5, 0x35, 0xa8,
+        0x91, 0xc7
+    };
+    srtp_t srtp_snd, srtp_recv;
+    srtp_err_status_t status;
+    int len;
+    srtp_policy_t policy;
+    int headers[3] = {1, 3, 4};
+
+    /*
+     * create a session with a single stream using the default srtp
+     * policy and with the SSRC value 0xcafebabe
+     */
+    memset(&policy, 0, sizeof(policy));
+    srtp_crypto_policy_set_rtp_default(&policy.rtp);
+    srtp_crypto_policy_set_rtcp_default(&policy.rtcp);
+    policy.ssrc.type  = ssrc_specific;
+    policy.ssrc.value = 0xcafebabe;
+    policy.key  = test_key_ext_headers;
+    policy.ekt = NULL;
+    policy.window_size = 128;
+    policy.allow_repeat_tx = 0;
+    policy.enc_xtn_hdr = headers;
+    policy.enc_xtn_hdr_count = sizeof(headers) / sizeof(headers[0]);
+    policy.next = NULL;
+
+    status = srtp_create(&srtp_snd, &policy);
+    if (status)
+        return status;
+
+    /*
+     * protect plaintext, then compare with ciphertext
+     */
+    len = sizeof(srtp_plaintext_ref);
+    status = srtp_protect(srtp_snd, srtp_plaintext, &len);
+    if (status || (len != sizeof(srtp_plaintext)))
+        return srtp_err_status_fail;
+
+    debug_print(mod_driver, "ciphertext:\n  %s",
+                srtp_octet_string_hex_string(srtp_plaintext, len));
+    debug_print(mod_driver, "ciphertext reference:\n  %s",
+                srtp_octet_string_hex_string(srtp_ciphertext, len));
+
+    if (octet_string_is_eq(srtp_plaintext, srtp_ciphertext, len))
+        return srtp_err_status_fail;
+
+    /*
+     * create a receiver session context comparable to the one created
+     * above - we need to do this so that the replay checking doesn't
+     * complain
+     */
+    status = srtp_create(&srtp_recv, &policy);
+    if (status)
+        return status;
+
+    /*
+     * unprotect ciphertext, then compare with plaintext
+     */
+    status = srtp_unprotect(srtp_recv, srtp_ciphertext, &len);
+    if (status || (len != 28))
+        return status;
+
+    if (octet_string_is_eq(srtp_ciphertext, srtp_plaintext_ref, len))
+        return srtp_err_status_fail;
+
+    status = srtp_dealloc(srtp_snd);
+    if (status)
+        return status;
+
+    status = srtp_dealloc(srtp_recv);
+    if (status)
+        return status;
+
+    return srtp_err_status_ok;
+}
+
 
 /*
  * srtp_validate_aes_256() verifies the correctness of libsrtp by comparing
@@ -1391,6 +1660,7 @@
      * create a session with a single stream using the default srtp
      * policy and with the SSRC value 0xcafebabe
      */
+    memset(&policy, 0, sizeof(policy));
     srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80(&policy.rtp);
     srtp_crypto_policy_set_aes_cm_256_hmac_sha1_80(&policy.rtcp);
     policy.ssrc.type  = ssrc_specific;
@@ -1572,6 +1842,7 @@
     }
 
     /* Now test adding and removing a single stream */
+    memset(&policy, 0, sizeof(policy));
     srtp_crypto_policy_set_rtp_default(&policy.rtp);
     srtp_crypto_policy_set_rtcp_default(&policy.rtcp);
     policy.ssrc.type  = ssrc_specific;
@@ -1641,6 +1912,8 @@
     NULL,      /* indicates that EKT is not in use */
     128,       /* replay window size */
     0,         /* retransmission not allowed */
+    NULL,      /* no encrypted extension headers */
+    0,         /* list of encrypted extension headers is empty */
     NULL
 };
 
@@ -1666,6 +1939,8 @@
     NULL,      /* indicates that EKT is not in use */
     128,       /* replay window size */
     0,         /* retransmission not allowed */
+    NULL,      /* no encrypted extension headers */
+    0,         /* list of encrypted extension headers is empty */
     NULL
 };
 
@@ -1691,6 +1966,8 @@
     NULL,      /* indicates that EKT is not in use */
     128,       /* replay window size */
     0,         /* retransmission not allowed */
+    NULL,      /* no encrypted extension headers */
+    0,         /* list of encrypted extension headers is empty */
     NULL
 };
 
@@ -1717,6 +1994,8 @@
     NULL,        /* indicates that EKT is not in use */
     128,         /* replay window size */
     0,           /* retransmission not allowed */
+    NULL,        /* no encrypted extension headers */
+    0,           /* list of encrypted extension headers is empty */
     NULL
 };
 
@@ -1742,6 +2021,8 @@
     NULL,        /* indicates that EKT is not in use */
     128,         /* replay window size */
     0,           /* retransmission not allowed */
+    NULL,        /* no encrypted extension headers */
+    0,           /* list of encrypted extension headers is empty */
     NULL
 };
 
@@ -1767,6 +2048,8 @@
     NULL,        /* indicates that EKT is not in use */
     128,         /* replay window size */
     0,           /* retransmission not allowed */
+    NULL,        /* no encrypted extension headers */
+    0,           /* list of encrypted extension headers is empty */
     NULL
 };
 
@@ -1792,6 +2075,8 @@
     NULL,        /* indicates that EKT is not in use */
     128,         /* replay window size */
     0,           /* retransmission not allowed */
+    NULL,        /* no encrypted extension headers */
+    0,           /* list of encrypted extension headers is empty */
     NULL
 };
 #endif
@@ -1818,6 +2103,8 @@
     NULL,      /* indicates that EKT is not in use */
     128,       /* replay window size */
     0,         /* retransmission not allowed */
+    NULL,      /* no encrypted extension headers */
+    0,         /* list of encrypted extension headers is empty */
     NULL
 };
 
@@ -1853,6 +2140,8 @@
     NULL,      /* indicates that EKT is not in use */
     128,       /* replay window size */
     0,         /* retransmission not allowed */
+    NULL,      /* no encrypted extension headers */
+    0,         /* list of encrypted extension headers is empty */
     NULL
 };
 
@@ -1892,6 +2181,8 @@
     &ekt_test_policy,      /* indicates that EKT is not in use */
     128,                   /* replay window size */
     0,                     /* retransmission not allowed */
+    NULL,                  /* no encrypted extension headers */
+    0,                     /* list of encrypted extension headers is empty */
     NULL
 };
 
@@ -1945,5 +2236,7 @@
     NULL,
     128,                 /* replay window size */
     0,                   /* retransmission not allowed */
+    NULL,                /* no encrypted extension headers */
+    0,                   /* list of encrypted extension headers is empty */
     NULL
 };