diff --git a/include/srtp.h b/include/srtp.h index f5d872f08..b6b8d509f 100644 --- a/include/srtp.h +++ b/include/srtp.h @@ -351,6 +351,8 @@ typedef struct srtp_policy_t { uint8_t *enc_xtn_hdr; /**< List of header ids to encrypt. */ size_t enc_xtn_hdr_count; /**< Number of entries in list of header */ /**< ids. */ + bool use_cryptex; /**< Encrypt header block and CSRCS with */ + /**< cryptex. */ struct srtp_policy_t *next; /**< Pointer to next stream policy. */ } srtp_policy_t; diff --git a/include/srtp_priv.h b/include/srtp_priv.h index d5ff85bec..6dcde6861 100644 --- a/include/srtp_priv.h +++ b/include/srtp_priv.h @@ -125,6 +125,8 @@ typedef struct srtp_stream_ctx_t_ { uint8_t *enc_xtn_hdr; size_t enc_xtn_hdr_count; uint32_t pending_roc; + bool use_cryptex; + struct srtp_stream_ctx_t_ *next; /* linked list of streams */ } strp_stream_ctx_t_; /* diff --git a/srtp/srtp.c b/srtp/srtp.c index 711e4c322..c8977f24e 100644 --- a/srtp/srtp.c +++ b/srtp/srtp.c @@ -563,6 +563,8 @@ static srtp_err_status_t srtp_stream_alloc(srtp_stream_ctx_t **str_ptr, str->enc_xtn_hdr_count = 0; } + str->use_cryptex = p->use_cryptex; + return srtp_err_status_ok; } @@ -673,6 +675,7 @@ static srtp_err_status_t srtp_stream_clone( /* copy information about extensions header encryption */ str->enc_xtn_hdr = stream_template->enc_xtn_hdr; str->enc_xtn_hdr_count = stream_template->enc_xtn_hdr_count; + str->use_cryptex = stream_template->use_cryptex; return srtp_err_status_ok; } @@ -1856,6 +1859,12 @@ static srtp_err_status_t srtp_protect_aead(srtp_ctx_t *ctx, size_t tag_len; v128_t iv; size_t aad_len; + srtp_hdr_xtnd_t *xtn_hdr = NULL; + unsigned int mki_size = 0; + uint8_t *mki_location = NULL; + int xtn_hdr_length = 0; + int xtn_profile_specific = 0; + uint32_t xtn_hdr_profile_and_value = 0; debug_print0(mod_srtp, "function srtp_protect_aead"); @@ -1883,11 +1892,48 @@ static srtp_err_status_t srtp_protect_aead(srtp_ctx_t *ctx, * find starting point for encryption and length of data to be * encrypted - the encrypted portion starts after the rtp header * extension, if present; otherwise, it starts after the last csrc, - * if any are present + * if any are present. + * If cryptex is used, the encryption portions also covers the + * rtp heacer extension. */ - enc_start = srtp_get_rtp_hdr_len(hdr); + + /* Cryptex can only encrypt CSRCS if header extension is present*/ + if (stream->use_cryptex && hdr->cc && !hdr->x) { + return srtp_err_status_parse_err; + } + if (hdr->x == 1) { - enc_start += srtp_get_rtp_xtn_hdr_len(hdr, rtp); + xtn_hdr = (srtp_hdr_xtnd_t *)(uint32_t *)hdr + uint32s_in_rtp_header + + hdr->cc; + xtn_hdr_length = ntohs(xtn_hdr->length); + xtn_profile_specific = ntohs(xtn_hdr->profile_specific); + } + + /* If no header extension is present cryptex has no effect */ + if (stream->use_cryptex && hdr->x) { + /* Change profiles by cryptex values */ + if (xtn_profile_specific == 0xbede) { + xtn_hdr_profile_and_value = htonl(0xc0de << 16 | xtn_hdr_length); + } else if ((xtn_profile_specific & 0xfff0) == 0x1000) { + xtn_hdr_profile_and_value = htonl(0xc2de << 16 | xtn_hdr_length); + } else { + return srtp_err_status_parse_err; + } + /* Get CSRCs block position or profile if no CSRCs */ + uint32_t *csrcs = (uint32_t *)hdr + uint32s_in_rtp_header; + /* Move CSRCS so block is contiguous with extension header block */ + for (unsigned char i = hdr->cc; i > 0; --i) { + csrcs[i] = csrcs[i - 1]; + } + /* Move profile and length before the CSRCs */ + csrcs[0] = xtn_hdr_profile_and_value; + /* Start encrypting in the CSRCS block new position */ + enc_start = csrcs + 1; + } else { + enc_start = srtp_get_rtp_hdr_len(hdr); + if (hdr->x == 1) { + enc_start += srtp_get_rtp_xtn_hdr_len(hdr, rtp); + } } /* note: the passed size is without the auth tag */ @@ -1982,6 +2028,27 @@ static srtp_err_status_t srtp_protect_aead(srtp_ctx_t *ctx, return srtp_err_status_cipher_fail; } + /* Restore CSRCs block before sending if using cryptex */ + if (stream->use_cryptex && xtn_hdr && hdr->cc) { + /* Restore CSRCS to its original position */ + uint32_t *csrcs = (uint32_t *)hdr + uint32s_in_rtp_header; + for (unsigned char i = 0; i < hdr->cc; ++i) + csrcs[i] = csrcs[i + 1]; + /* Restore extension header profile and length */ + *(uint32_t *)xtn_hdr = xtn_hdr_profile_and_value; + } + + /* + * If we're doing GCM, we need to get the tag + * and append that to the output + */ + status = + srtp_cipher_get_tag(session_keys->rtp_cipher, + (uint8_t *)enc_start + enc_octet_len, &tag_len); + if (status) { + return (srtp_err_status_cipher_fail); + } + if (stream->use_mki) { srtp_inject_mki(srtp + enc_start + enc_octet_len, session_keys, stream->mki_size); @@ -2019,7 +2086,12 @@ static srtp_err_status_t srtp_unprotect_aead(srtp_ctx_t *ctx, v128_t iv; srtp_err_status_t status; size_t tag_len; - size_t aad_len; + size_t int aad_len; + srtp_hdr_xtnd_t *xtn_hdr = NULL; + size_t xtn_hdr_length = 0; + int xtn_profile_specific = 0; + bool use_cryptex = false; + uint32_t xtn_hdr_profile_and_value = 0; debug_print0(mod_srtp, "function srtp_unprotect_aead"); @@ -2045,9 +2117,40 @@ static srtp_err_status_t srtp_unprotect_aead(srtp_ctx_t *ctx, return srtp_err_status_cipher_fail; } - enc_start = srtp_get_rtp_hdr_len(hdr); + /* + * find starting point for decryption and length of data to be + * decrypted - the encrypted portion starts after the rtp header + * extension, if present; otherwise, it starts after the last csrc, + * if any are present. + * If cryptex is in use, the encrypted portion also covers the + * rtp header extension. + */ if (hdr->x == 1) { - enc_start += srtp_get_rtp_xtn_hdr_len(hdr, srtp); + xtn_hdr = (srtp_hdr_xtnd_t *)(uint32_t *)hdr + uint32s_in_rtp_header + + hdr->cc; + xtn_hdr_length = ntohs(xtn_hdr->length); + xtn_profile_specific = ntohs(xtn_hdr->profile_specific); + } + + /* Check if the profile is the one for cryptex */ + if (xtn_profile_specific == 0xc0de || xtn_profile_specific == 0xc2de) { + /* Get the 4 bytes of defined by profile and length */ + xtn_hdr_profile_and_value = *(uint32_t *)xtn_hdr; + /* Get CSRCs block position or profile if no CSRCs */ + uint32_t *csrcs = (uint32_t *)hdr + uint32s_in_rtp_header; + /* Move CSRCS so block is contiguous with extension header block */ + for (unsigned char i = hdr->cc; i > 0; --i) + csrcs[i] = csrcs[i - 1]; + /* Move defined by profile before the CSRCs block */ + csrcs[0] = xtn_hdr_profile_and_value; + /* Start encrypting in the CSRCS block new position */ + enc_start = csrcs + 1; + use_cryptex = 1; + } else { + enc_start = srtp_get_rtp_hdr_len(hdr); + if (hdr->x == 1) { + enc_start += srtp_get_rtp_xtn_hdr_len(hdr, srtp); + } } if (enc_start > srtp_len - tag_len - stream->mki_size) { @@ -2114,6 +2217,23 @@ static srtp_err_status_t srtp_unprotect_aead(srtp_ctx_t *ctx, return status; } + if (use_cryptex) { + uint32_t *csrcs = (uint32_t *)hdr + uint32s_in_rtp_header; + /* Restore CSRCS to its original position */ + for (unsigned char i = 0; i < hdr->cc; ++i) { + csrcs[i] = csrcs[i + 1]; + } + /* Restore extension header and change profiles by cryptex values*/ + xtn_hdr->length = htons(xtn_hdr_length); + if (xtn_profile_specific == 0xc0de) { + xtn_hdr->profile_specific = htons(0xbede); + } else if (xtn_profile_specific == 0xc2de) { + xtn_hdr->profile_specific = htons(0x1000); + } else { + return srtp_err_status_parse_err; + } + } + if (hdr->x == 1 && session_keys->rtp_xtn_hdr_cipher) { /* * extensions header encryption RFC 6904 @@ -2213,6 +2333,11 @@ srtp_err_status_t srtp_protect(srtp_t ctx, srtp_stream_ctx_t *stream; size_t prefix_len; srtp_session_keys_t *session_keys = NULL; + uint8_t *mki_location = NULL; + int advance_packet_index = 0; + size_t xtn_hdr_length = 0; + int xtn_profile_specific = 0; + uint32_t xtn_hdr_profile_and_value = 0; debug_print0(mod_srtp, "function srtp_protect"); @@ -2319,11 +2444,57 @@ srtp_err_status_t srtp_protect(srtp_t ctx, * find starting point for encryption and length of data to be * encrypted - the encrypted portion starts after the rtp header * extension, if present; otherwise, it starts after the last csrc, - * if any are present + * if any are present. + * If cryptex is in use, the encrypted portion also covers the + * rtp header extension. */ - enc_start = srtp_get_rtp_hdr_len(hdr); - if (hdr->x == 1) { - enc_start += srtp_get_rtp_xtn_hdr_len(hdr, rtp); + if (stream->rtp_services & sec_serv_conf) { + /* Cryptex can only encrypt CSRCS if header extension is present*/ + if (stream->use_cryptex && hdr->cc && !hdr->x) { + return srtp_err_status_parse_err; + } + if (hdr->x == 1) { + xtn_hdr = (srtp_hdr_xtnd_t *)(uint32_t *)hdr + + uint32s_in_rtp_header + hdr->cc; + xtn_hdr_length = ntohs(xtn_hdr->length); + xtn_profile_specific = ntohs(xtn_hdr->profile_specific); + } + /* If no header extension is present cryptex has no effect */ + if (stream->use_cryptex && hdr->x) { + /* Change profiles by cryptex values */ + if (xtn_profile_specific == 0xbede) { + xtn_hdr_profile_and_value = + htonl(0xc0de << 16 | xtn_hdr_length); + } else if (xtn_profile_specific == 0x1000) { + xtn_hdr_profile_and_value = + htonl(0xc2de << 16 | xtn_hdr_length); + } else { + return srtp_err_status_parse_err; + } + /* Get CSRCs block position or profile if no CSRCs */ + uint32_t *csrcs = (uint32_t *)hdr + uint32s_in_rtp_header; + /* Move CSRCS so block is contiguous with extension header block */ + for (unsigned char i = hdr->cc; i > 0; --i) + csrcs[i] = csrcs[i - 1]; + /* Move profile and length before the CSRCs */ + csrcs[0] = xtn_hdr_profile_and_value; + /* Start encrypting in the CSRCS block new position */ + enc_start = csrcs + 1; + } else { + enc_start = srtp_get_rtp_hdr_len(hdr); + if (hdr->x == 1) { + enc_start += srtp_get_rtp_xtn_hdr_len(hdr, rtp); + } + } + /* note: the passed size is without the auth tag */ + if (!((uint8_t *)enc_start <= (uint8_t *)hdr + *pkt_octet_len)) + return srtp_err_status_parse_err; + enc_octet_len = + (int)(*pkt_octet_len - ((uint8_t *)enc_start - (uint8_t *)hdr)); + if (enc_octet_len < 0) + return srtp_err_status_parse_err; + } else { + enc_start = NULL; } if (enc_start > rtp_len) { @@ -2457,10 +2628,20 @@ srtp_err_status_t srtp_protect(srtp_t ctx, &enc_octet_len); if (status) { return srtp_err_status_cipher_fail; + } else if (rtp != srtp) { + /* if no encryption and not-inplace then need to copy rest of packet */ + memcpy(srtp + enc_start, rtp + enc_start, enc_octet_len); + } + + /* Restore CSRCs block before sending if using cryptex */ + if (stream->use_cryptex && xtn_hdr && hdr->cc) { + /* Restore CSRCS to its original position */ + uint32_t *csrcs = (uint32_t *)hdr + uint32s_in_rtp_header; + for (unsigned char i = 0; i < hdr->cc; ++i) + csrcs[i] = csrcs[i + 1]; + /* Restore extension header profile and length */ + *(uint32_t *)xtn_hdr = xtn_hdr_profile_and_value; } - } else if (rtp != srtp) { - /* if no encryption and not-inplace then need to copy rest of packet */ - memcpy(srtp + enc_start, rtp + enc_start, enc_octet_len); } /* @@ -2524,6 +2705,10 @@ srtp_err_status_t srtp_unprotect(srtp_t ctx, bool advance_packet_index = false; uint32_t roc_to_set = 0; uint16_t seq_to_set = 0; + size_t xtn_hdr_length = 0; + int xtn_profile_specific = 0; + bool use_cryptex = 0; + uint32_t xtn_hdr_profile_and_value = 0; debug_print0(mod_srtp, "function srtp_unprotect"); @@ -2730,6 +2915,51 @@ srtp_err_status_t srtp_unprotect(srtp_t ctx, } } + /* + * find starting point for decryption and length of data to be + * decrypted - the encrypted portion starts after the rtp header + * extension, if present; otherwise, it starts after the last csrc, + * if any are present + * + * if we're not providing confidentiality, set enc_start to NULL + */ + if (stream->rtp_services & sec_serv_conf) { + if (hdr->x == 1) { + xtn_hdr = (srtp_hdr_xtnd_t *)(uint32_t *)hdr + + uint32s_in_rtp_header + hdr->cc; + xtn_hdr_length = ntohs(xtn_hdr->length); + xtn_profile_specific = ntohs(xtn_hdr->profile_specific); + } + + /* Check if the profile is the one for cryptex */ + if (xtn_profile_specific == 0xc0de || xtn_profile_specific == 0xc2de) { + /* Get the 4 bytes of defined by profile and length */ + xtn_hdr_profile_and_value = *(uint32_t *)xtn_hdr; + /* Get CSRCs block position or profile if no CSRCs */ + uint32_t *csrcs = (uint32_t *)hdr + uint32s_in_rtp_header; + /* Move CSRCS so block is contiguous with extension header block */ + for (unsigned char i = hdr->cc; i > 0; --i) + csrcs[i] = csrcs[i - 1]; + /* Move defined by profile before the CSRCs block */ + csrcs[0] = xtn_hdr_profile_and_value; + /* Start encrypting in the CSRCS block new position */ + enc_start = csrcs + 1; + use_cryptex = 1; + } else { + enc_start = (uint32_t *)hdr + uint32s_in_rtp_header + hdr->cc; + if (hdr->x == 1) { + enc_start += (xtn_hdr_length + 1); + } + } + if (!((uint8_t *)enc_start <= + (uint8_t *)hdr + (*pkt_octet_len - tag_len - mki_size))) + return srtp_err_status_parse_err; + enc_octet_len = (uint32_t)(*pkt_octet_len - tag_len - mki_size - + ((uint8_t *)enc_start - (uint8_t *)hdr)); + } else { + enc_start = NULL; + } + /* * update the key usage limit, and check it to make sure that we * didn't just hit either the soft limit or the hard limit, and call @@ -2765,10 +2995,28 @@ srtp_err_status_t srtp_unprotect(srtp_t ctx, if (status) { return srtp_err_status_cipher_fail; } + if (use_cryptex) { + uint32_t *csrcs = (uint32_t *)hdr + uint32s_in_rtp_header; + /* Restore CSRCS to its original position */ + for (unsigned char i = 0; i < hdr->cc; ++i) + csrcs[i] = csrcs[i + 1]; + /* Restore extension header and change profiles by cryptex values*/ + xtn_hdr->length = htons(xtn_hdr_length); + if (xtn_profile_specific == 0xc0de) { + xtn_hdr->profile_specific = htons(0xbede); + } else if (xtn_profile_specific == 0xc2de) { + xtn_hdr->profile_specific = htons(0x1000); + } else { + return srtp_err_status_parse_err; + } + } } else if (rtp != srtp) { /* if no encryption and not-inplace then need to copy rest of packet */ memcpy(rtp + enc_start, srtp + enc_start, enc_octet_len); - } + } + + + /* * verify that stream is for received traffic - this check will diff --git a/test/srtp_driver.c b/test/srtp_driver.c index 04f8ca964..93fd4c594 100644 --- a/test/srtp_driver.c +++ b/test/srtp_driver.c @@ -62,12 +62,16 @@ srtp_err_status_t srtp_validate(void); +srtp_err_status_t srtp_validate_cryptex(void); + srtp_err_status_t srtp_validate_mki(void); srtp_err_status_t srtp_validate_null(void); #ifdef GCM srtp_err_status_t srtp_validate_gcm(void); + +srtp_err_status_t srtp_validate_gcm_cryptex(void); #endif srtp_err_status_t srtp_validate_encrypted_extensions_headers(void); @@ -165,6 +169,12 @@ const uint8_t rtp_test_packet_extension_header[12] = { #define TEST_MKI_ID_SIZE 4 +typedef struct test_vectors_t { + const char *name; + const char *plaintext; + const char *ciphertext; +} test_vectors_t; + extern uint8_t test_key[46]; extern uint8_t test_key_2[46]; extern uint8_t test_mki_id[TEST_MKI_ID_SIZE]; @@ -679,6 +689,15 @@ int main(int argc, char *argv[]) * that this test only covers the default policy */ printf("testing srtp_protect and srtp_unprotect against " + "reference cryptex packet\n"); + if (srtp_validate_cryptex() == srtp_err_status_ok) { + printf("passed\n\n"); + } else { + printf("failed\n"); + exit(1); + } + + printf("testing srtp_protect and srtp_unprotect against " "reference packet using mki\n"); if (srtp_validate_mki() == srtp_err_status_ok) { printf("passed\n\n"); @@ -705,6 +724,15 @@ int main(int argc, char *argv[]) printf("failed\n"); exit(1); } + + printf("testing srtp_protect and srtp_unprotect against " + "reference cryptex packet using GCM\n"); + if (srtp_validate_gcm_cryptex() == srtp_err_status_ok) { + printf("passed\n\n"); + } else { + printf("failed\n"); + exit(1); + } #endif printf("testing srtp_protect and srtp_unprotect against " @@ -2360,6 +2388,302 @@ srtp_err_status_t srtp_validate(void) return srtp_err_status_ok; } +/* + * srtp_validate_cryptex() verifies the correctness of libsrtp by comparing + * some computed packets against some pre-computed reference values. + * These packets were made with the default SRTP policy. + */ +srtp_err_status_t srtp_validate_cryptex() +{ + // clang-format off + /* Plaintext packet with 1-byte header extension */ + const char *srtp_1bytehdrext_ref = + "900f1235" + "decafbad" + "cafebabe" + "bede0001" + "51000200" + "abababab" + "abababab" + "abababab" + "abababab"; + + /* AES-CTR/HMAC-SHA1 Ciphertext packet with 1-byte header extension */ + const char *srtp_1bytehdrext_cryptex = + "900f1235" + "decafbad" + "cafebabe" + "c0de0001" + "eb923652" + "51c3e036" + "f8de27e9" + "c27ee3e0" + "b4651d9f" + "bc4218a7" + "0244522f" + "34a5"; + + /* Plaintext packet with 2-byte header extension */ + const char *srtp_2bytehdrext_ref = + "900f1236" + "decafbad" + "cafebabe" + "10000001" + "05020002" + "abababab" + "abababab" + "abababab" + "abababab"; + + /* AES-CTR/HMAC-SHA1 Ciphertext packet with 2-byte header extension */ + const char *srtp_2bytehdrext_cryptex = + "900f1236" + "decafbad" + "cafebabe" + "c2de0001" + "4ed9cc4e" + "6a712b30" + "96c5ca77" + "339d4204" + "ce0d7739" + "6cab6958" + "5fbce381" + "94a5"; + + /* Plaintext packet with 1-byte header extension and CSRC fields. */ + const char *srtp_1bytehdrext_cc_ref = + "920f1238" + "decafbad" + "cafebabe" + "0001e240" + "0000b26e" + "bede0001" + "51000200" + "abababab" + "abababab" + "abababab" + "abababab"; + + const char *srtp_1bytehdrext_cc_cryptex = + "920f1238" + "decafbad" + "cafebabe" + "8bb6e12b" + "5cff16dd" + "c0de0001" + "92838c8c" + "09e58393" + "e1de3a9a" + "74734d67" + "45671338" + "c3acf11d" + "a2df8423" + "bee0"; + + /* Plaintext packet with 2-byte header extension and CSRC fields. */ + const char *srtp_2bytehdrext_cc_ref = + "920f1239" + "decafbad" + "cafebabe" + "0001e240" + "0000b26e" + "10000001" + "05020002" + "abababab" + "abababab" + "abababab" + "abababab"; + + const char *srtp_2bytehdrext_cc_cryptex = + "920f1239" + "decafbad" + "cafebabe" + "f70e513e" + "b90b9b25" + "c2de0001" + "bbed4848" + "faa64466" + "5f3d7f34" + "125914e9" + "f4d0ae92" + "3c6f479b" + "95a0f7b5" + "3133"; + + /* Plaintext packet with empty 1-byte header extension and CSRC fields. */ + const char *srtp_1byte_empty_hdrext_cc_ref = + "920f123a" + "decafbad" + "cafebabe" + "0001e240" + "0000b26e" + "bede0000" + "abababab" + "abababab" + "abababab" + "abababab"; + + const char *srtp_1byte_empty_hdrext_cc_cryptex = + "920f123a" + "decafbad" + "cafebabe" + "7130b6ab" + "fe2ab0e3" + "c0de0000" + "e3d9f64b" + "25c9e74c" + "b4cf8e43" + "fb92e378" + "1c2c0cea" + "b6b3a499" + "a14c"; + + /* Plaintext packet with empty 2-byte header extension and CSRC fields. */ + const char *srtp_2byte_empty_hdrext_cc_ref = + "920f123b" + "decafbad" + "cafebabe" + "0001e240" + "0000b26e" + "10000000" + "abababab" + "abababab" + "abababab" + "abababab"; + + const char *srtp_2byte_empty_hdrext_cc_cryptex = + "920f123b" + "decafbad" + "cafebabe" + "cbf24c12" + "4330e1c8" + "c2de0000" + "599dd45b" + "c9d687b6" + "03e8b59d" + "771fd38e" + "88b170e0" + "cd31e125" + "eabe"; + + // clang-format on + + struct test_vectors_t vectors[6] = { + { "Plaintext packet with 1-byte header extension", srtp_1bytehdrext_ref, + srtp_1bytehdrext_cryptex }, + { "Plaintext packet with 2-byte header extension", srtp_2bytehdrext_ref, + srtp_2bytehdrext_cryptex }, + { "Plaintext packet with 1-byte header extension and CSRC fields", + srtp_1bytehdrext_cc_ref, srtp_1bytehdrext_cc_cryptex }, + { "Plaintext packet with 2-byte header extension and CSRC fields", + srtp_2bytehdrext_cc_ref, srtp_2bytehdrext_cc_cryptex }, + { "Plaintext packet with empty 1-byte header extension and CSRC fields", + srtp_1byte_empty_hdrext_cc_ref, srtp_1byte_empty_hdrext_cc_cryptex }, + { "Plaintext packet with empty 2-byte header extension and CSRC fields", + srtp_2byte_empty_hdrext_cc_ref, srtp_2byte_empty_hdrext_cc_cryptex }, + }; + + srtp_t srtp_snd, srtp_recv; + srtp_err_status_t status; + int len, ref_len, enc_len; + srtp_policy_t policy; + + /* + * 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; + policy.deprecated_ekt = NULL; + policy.window_size = 128; + policy.allow_repeat_tx = 0; + policy.use_cryptex = 1; + policy.next = NULL; + + status = srtp_create(&srtp_snd, &policy); + if (status) { + return status; + } + + for (int i = 0; i < 6; ++i) { + uint8_t packet[1400]; + uint8_t reference[1400]; + uint8_t ciphertext[1400]; + + /* Initialize reference test vectors */ + ref_len = + hex_string_to_octet_string((char *)reference, vectors[i].plaintext, + sizeof(reference)) / + 2; + enc_len = hex_string_to_octet_string((char *)ciphertext, + vectors[i].ciphertext, + sizeof(ciphertext)) / + 2; + + /* Initialize test packet */ + len = ref_len; + memcpy(packet, reference, len); + printf("%s\n", vectors[i].name); + /* + * protect plaintext, then compare with ciphertext + */ + debug_print(mod_driver, "test vector: %s\n", vectors[i].name); + + status = srtp_protect(srtp_snd, packet, &len); + if (status || (len != enc_len)) { + return srtp_err_status_fail; + } + + debug_print(mod_driver, "ciphertext:\n %s", + octet_string_hex_string(packet, len)); + debug_print(mod_driver, "ciphertext reference:\n %s", + octet_string_hex_string(ciphertext, len)); + + if (srtp_octet_string_is_eq(packet, 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, packet, &len); + if (status || (len != ref_len)) { + return status; + } + + if (srtp_octet_string_is_eq(packet, reference, len)) { + return srtp_err_status_fail; + } + + status = srtp_dealloc(srtp_recv); + if (status) { + return status; + } + } + + status = srtp_dealloc(srtp_snd); + if (status) { + return status; + } + + return srtp_err_status_ok; +} + +#ifdef GCM /* * srtp_validate_mki() verifies the correctness of libsrtp by comparing * some computed packets against some pre-computed reference values. @@ -2536,6 +2860,316 @@ srtp_err_status_t srtp_validate_mki(void) return srtp_err_status_ok; } + +/* + * srtp_validate_gcm() verifies the correctness of libsrtp by comparing + * an computed packet against the known ciphertext for the plaintext. + */ +srtp_err_status_t srtp_validate_gcm_cryptex() +{ + // clang-format off + unsigned char test_key_gcm[28] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab + }; + + /* Plaintext packet with 1-byte header extension */ + const char *srtp_1bytehdrext_ref = + "900f1235" + "decafbad" + "cafebabe" + "bede0001" + "51000200" + "abababab" + "abababab" + "abababab" + "abababab"; + + /* GCM Ciphertext packet with 1-byte header extension */ + const char *srtp_1bytehdrext_cryptex_gcm = + "900f1235" + "decafbad" + "cafebabe" + "c0de0001" + "39972dc9" + "572c4d99" + "e8fc355d" + "e743fb2e" + "94f9d8ff" + "54e72f41" + "93bbc5c7" + "4ffab0fa" + "9fa0fbeb"; + + /* Plaintext packet with 2-byte header extension */ + const char *srtp_2bytehdrext_ref = + "900f1236" + "decafbad" + "cafebabe" + "10000001" + "05020002" + "abababab" + "abababab" + "abababab" + "abababab"; + + /* GCM Ciphertext packet with 2-byte header extension */ + const char *srtp_2bytehdrext_cryptex_gcm = + "900f1236" + "decafbad" + "cafebabe" + "c2de0001" + "bb75a4c5" + "45cd1f41" + "3bdb7daa" + "2b1e3263" + "de313667" + "c9632490" + "81b35a65" + "f5cb6c88" + "b394235f"; + + /* Plaintext packet with 1-byte header extension and CSRC fields. */ + const char *srtp_1bytehdrext_cc_ref = + "920f1238" + "decafbad" + "cafebabe" + "0001e240" + "0000b26e" + "bede0001" + "51000200" + "abababab" + "abababab" + "abababab" + "abababab"; + + const char *srtp_1bytehdrext_cc_cryptex_gcm = + "920f1238decafbad" + "cafebabe" + "63bbccc4" + "a7f695c4" + "c0de0001" + "8ad7c71f" + "ac70a80c" + "92866b4c" + "6ba98546" + "ef913586" + "e95ffaaf" + "fe956885" + "bb0647a8" + "bc094ac8"; + + + /* Plaintext packet with 2-byte header extension and CSRC fields. */ + const char *srtp_2bytehdrext_cc_ref = + "920f1239" + "decafbad" + "cafebabe" + "0001e240" + "0000b26e" + "10000001" + "05020002" + "abababab" + "abababab" + "abababab" + "abababab"; + + const char *srtp_2bytehdrext_cc_cryptex_gcm = + "920f1239" + "decafbad" + "cafebabe" + "3680524f" + "8d312b00" + "c2de0001" + "c78d1200" + "38422bc1" + "11a7187a" + "18246f98" + "0c059cc6" + "bc9df8b6" + "26394eca" + "344e4b05" + "d80fea83"; + + /* Plaintext packet with empty 1-byte header extension and CSRC fields. */ + const char *srtp_1byte_empty_hdrext_cc_ref = + "920f123a" + "decafbad" + "cafebabe" + "0001e240" + "0000b26e" + "bede0000" + "abababab" + "abababab" + "abababab" + "abababab"; + + const char *srtp_1byte_empty_hdrext_cc_cryptex_gcm = + "920f123a" + "decafbad" + "cafebabe" + "15b6bb43" + "37906fff" + "c0de0000" + "b7b96453" + "7a2b03ab" + "7ba5389c" + "e9331712" + "6b5d974d" + "f30c6884" + "dcb651c5" + "e120c1da"; + + /* Plaintext packet with empty 2-byte header extension and CSRC fields. */ + const char *srtp_2byte_empty_hdrext_cc_ref = + "920f123b" + "decafbad" + "cafebabe" + "0001e240" + "0000b26e" + "10000000" + "abababab" + "abababab" + "abababab" + "abababab"; + + const char *srtp_2byte_empty_hdrext_cc_cryptex_gcm = + "920f123b" + "decafbad" + "cafebabe" + "dcb38c9e" + "48bf95f4" + "c2de0000" + "61ee432c" + "f9203170" + "76613258" + "d3ce4236" + "c06ac429" + "681ad084" + "13512dc9" + "8b5207d8"; + // clang-format on + + struct test_vectors_t vectors[6] = { + { "Plaintext packet with 1-byte header extension", srtp_1bytehdrext_ref, + srtp_1bytehdrext_cryptex_gcm }, + { "Plaintext packet with 2-byte header extension", srtp_2bytehdrext_ref, + srtp_2bytehdrext_cryptex_gcm }, + { "Plaintext packet with 1-byte header extension and CSRC fields", + srtp_1bytehdrext_cc_ref, srtp_1bytehdrext_cc_cryptex_gcm }, + { "Plaintext packet with 2-byte header extension and CSRC fields", + srtp_2bytehdrext_cc_ref, srtp_2bytehdrext_cc_cryptex_gcm }, + { "Plaintext packet with empty 1-byte header extension and CSRC fields", + srtp_1byte_empty_hdrext_cc_ref, + srtp_1byte_empty_hdrext_cc_cryptex_gcm }, + { "Plaintext packet with empty 2-byte header extension and CSRC fields", + srtp_2byte_empty_hdrext_cc_ref, + srtp_2byte_empty_hdrext_cc_cryptex_gcm }, + }; + + srtp_t srtp_snd, srtp_recv; + srtp_err_status_t status; + int len, ref_len, enc_len; + srtp_policy_t policy; + + /* + * 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_gcm_128_16_auth(&policy.rtp); + srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtcp); + policy.ssrc.type = ssrc_specific; + policy.ssrc.value = 0xcafebabe; + policy.key = test_key_gcm; + policy.deprecated_ekt = NULL; + policy.window_size = 128; + policy.allow_repeat_tx = 0; + policy.use_cryptex = 1; + policy.next = NULL; + + status = srtp_create(&srtp_snd, &policy); + if (status) { + return status; + } + + for (int i = 0; i < 6; ++i) { + uint8_t packet[1400]; + uint8_t reference[1400]; + uint8_t ciphertext[1400]; + + /* Initialize reference test vectors */ + ref_len = + hex_string_to_octet_string((char *)reference, vectors[i].plaintext, + sizeof(reference)) / + 2; + enc_len = hex_string_to_octet_string((char *)ciphertext, + vectors[i].ciphertext, + sizeof(ciphertext)) / + 2; + + /* Initialize test packet */ + len = ref_len; + memcpy(packet, reference, len); + printf("%s\n", vectors[i].name); + /* + * protect plaintext, then compare with ciphertext + */ + debug_print(mod_driver, "test vector: %s\n", vectors[i].name); + + status = srtp_protect(srtp_snd, packet, &len); + if (status || (len != enc_len)) { + return srtp_err_status_fail; + } + + debug_print(mod_driver, "ciphertext:\n %s", + octet_string_hex_string(packet, len)); + debug_print(mod_driver, "ciphertext reference:\n %s", + octet_string_hex_string(ciphertext, len)); + + if (srtp_octet_string_is_eq(packet, 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, packet, &len); + if (status || (len != ref_len)) { + return status; + } + + if (srtp_octet_string_is_eq(packet, reference, len)) { + return srtp_err_status_fail; + } + + status = srtp_dealloc(srtp_recv); + if (status) { + return status; + } + } + + status = srtp_dealloc(srtp_snd); + if (status) { + return status; + } + + return srtp_err_status_ok; +} + + /* * srtp_validate_null() verifies the correctness of libsrtp by comparing * some computed packets against some pre-computed reference values. @@ -4738,6 +5372,7 @@ const srtp_policy_t default_policy = { 0, /* retransmission not allowed */ NULL, /* no encrypted extension headers */ 0, /* list of encrypted extension headers is empty */ + 0, NULL }; @@ -4768,6 +5403,7 @@ const srtp_policy_t aes_only_policy = { 0, /* retransmission not allowed */ NULL, /* no encrypted extension headers */ 0, /* list of encrypted extension headers is empty */ + 0, NULL }; @@ -4798,6 +5434,7 @@ const srtp_policy_t hmac_only_policy = { 0, /* retransmission not allowed */ NULL, /* no encrypted extension headers */ 0, /* list of encrypted extension headers is empty */ + 0, NULL }; @@ -4831,6 +5468,7 @@ const srtp_policy_t aes128_gcm_8_policy = { 0, /* retransmission not allowed */ NULL, /* no encrypted extension headers */ 0, /* list of encrypted extension headers is empty */ + 0, NULL }; @@ -4863,6 +5501,7 @@ const srtp_policy_t aes128_gcm_8_cauth_policy = { 0, /* retransmission not allowed */ NULL, /* no encrypted extension headers */ 0, /* list of encrypted extension headers is empty */ + 0, NULL }; @@ -4895,6 +5534,7 @@ const srtp_policy_t aes256_gcm_8_policy = { 0, /* retransmission not allowed */ NULL, /* no encrypted extension headers */ 0, /* list of encrypted extension headers is empty */ + 0, NULL }; @@ -4927,6 +5567,7 @@ const srtp_policy_t aes256_gcm_8_cauth_policy = { 0, /* retransmission not allowed */ NULL, /* no encrypted extension headers */ 0, /* list of encrypted extension headers is empty */ + 0, NULL }; #endif @@ -4958,6 +5599,7 @@ const srtp_policy_t null_policy = { 0, /* retransmission not allowed */ NULL, /* no encrypted extension headers */ 0, /* list of encrypted extension headers is empty */ + 0, NULL }; @@ -5026,6 +5668,7 @@ const srtp_policy_t aes_256_hmac_policy = { 0, /* retransmission not allowed */ NULL, /* no encrypted extension headers */ 0, /* list of encrypted extension headers is empty */ + 0, NULL }; @@ -5056,6 +5699,7 @@ const srtp_policy_t hmac_only_with_no_master_key = { false, /* retransmission not allowed */ NULL, /* no encrypted extension headers */ 0, /* list of encrypted extension headers is empty */ + 0, NULL }; @@ -5089,6 +5733,7 @@ const srtp_policy_t *policy_array[] = { // clang-format off const srtp_policy_t *invalid_policy_array[] = { &hmac_only_with_no_master_key, + 0, NULL }; // clang-format on @@ -5122,6 +5767,7 @@ const srtp_policy_t wildcard_policy = { 0, /* retransmission not allowed */ NULL, /* no encrypted extension headers */ 0, /* list of encrypted extension headers is empty */ + 0, NULL };