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
};