From 86ba0ef6439100fa5054ba4cd1ae6a1a6a997b00 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Wed, 19 Jan 2022 13:03:29 +0100 Subject: [PATCH] tests: support test for SRTP the test will check that the same Exported Keying Material is generated between client and server --- examples/client/client.c | 82 ++++++++++++++ examples/server/server.c | 73 ++++++++++++ tests/include.am | 2 + tests/suites.c | 40 ++++++- tests/test-dtls-srtp-fails.conf | 11 ++ tests/test-dtls-srtp.conf | 192 ++++++++++++++++++++++++++++++++ wolfssl/test.h | 90 +++++++++++++++ 7 files changed, 489 insertions(+), 1 deletion(-) create mode 100644 tests/test-dtls-srtp-fails.conf create mode 100644 tests/test-dtls-srtp.conf diff --git a/examples/client/client.c b/examples/client/client.c index 793dd6521..0a71b6960 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -1759,6 +1759,69 @@ static void Usage(void) #endif } +#ifdef WOLFSSL_SRTP +/** + * client_srtp_test() - test that the computed ekm matches with the server one + * @ssl: ssl context + * @srtp_helper: srtp_test_helper struct shared with the server + * + * if @srtp_helper is NULL no check is made, but the ekm is printed. + * + * calls srtp_helper_get_ekm() to wait and then get the ekm computed by the + * server, then check if it matches the one computed by itself. + */ +static int client_srtp_test(WOLFSSL *ssl, struct srtp_test_helper *srtp_helper) +{ + uint8_t *srtp_secret, *other_secret, *p; + size_t srtp_secret_length, other_size; + int ret; + + ret = wolfSSL_export_dtls_srtp_keying_material(ssl, NULL, + &srtp_secret_length); + if (ret != LENGTH_ONLY_E) { + printf("SRTP: can't get dtsl_srtp keying material"); + return ret; + } + + srtp_secret = (uint8_t*)XMALLOC(srtp_secret_length, + NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (srtp_secret == NULL) + err_sys("SRTP: low memory"); + + ret = wolfSSL_export_dtls_srtp_keying_material(ssl, srtp_secret, + &srtp_secret_length); + if (ret != WOLFSSL_SUCCESS) { + printf("SRTP: can't get dtsl_srtp keying material"); + return ret; + } + + printf("DTLS-SRTP exported key material:\n"); + for (p = srtp_secret; p < srtp_secret + srtp_secret_length; p++) + printf("%02X", *p); + printf("\n"); + + if (srtp_helper != NULL) { + srtp_helper_get_ekm(srtp_helper, &other_secret, &other_size); + + if (other_size != srtp_secret_length || + (XMEMCMP(other_secret, srtp_secret, srtp_secret_length) != 0)) { + + /* we are delegated from server to free this buffer */ + XFREE(other_secret, NULL, DYNAMIC_TYPE_TMP_BUFFER); + printf("SRTP: Exported Keying Material mismatch"); + return WOLFSSL_UNKNOWN; + } + + /* we are delegated from server to free this buffer */ + XFREE(other_secret, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + + XFREE(srtp_secret, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + return 0; +} +#endif + THREAD_RETURN WOLFSSL_THREAD client_test(void* args) { SOCKET_T sockfd = WOLFSSL_SOCKET_INVALID; @@ -3909,6 +3972,22 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) TEST_DELAY(); #endif /* WOLFSSL_SESSION_EXPORT_DEBUG */ +#ifdef WOLFSSL_SRTP + if (dtlsSrtpProfiles != NULL) { + err = client_srtp_test(ssl, ((func_args*)args)->srtp_test_helper); + if (err != 0) { + if (exitWithRet) { + ((func_args*)args)->return_code = err; + wolfSSL_free(ssl); ssl = NULL; + wolfSSL_CTX_free(ctx); ctx = NULL; + goto exit; + } + /* else */ + err_sys("SRTP check failed"); + } + } +#endif + #ifdef WOLFSSL_TLS13 if (updateKeysIVs) wolfSSL_update_keys(ssl); @@ -4260,6 +4339,9 @@ exit: StartTCP(); +#ifdef WOLFSSL_SRTP + args.srtp_test_helper = NULL; +#endif args.argc = argc; args.argv = argv; args.return_code = 0; diff --git a/examples/server/server.c b/examples/server/server.c index 7bc3082e2..8e6434005 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -1279,6 +1279,59 @@ static void Usage(void) #endif } +#ifdef WOLFSSL_SRTP +/** + * server_srtp_test() - print the ekm and share it with the client + * @ssl: ssl context + * @srtp_helper: srtp_test_helper shared struct with the client + * + * if @srtp_helper is NULL the ekm isn't shared, but it is still printed. + * + * calls srtp_helper_set_ekm() to wake the client and share the ekm with + * him. The client will check that the ekm matches the one computed by itself. + */ +static int server_srtp_test(WOLFSSL *ssl, struct srtp_test_helper *srtp_helper) +{ + size_t srtp_secret_length; + uint8_t *srtp_secret, *p; + int ret; + + ret = wolfSSL_export_dtls_srtp_keying_material(ssl, NULL, + &srtp_secret_length); + if (ret != LENGTH_ONLY_E) { + printf("SRTP: can't get dtsl_srtp keying material"); + return ret; + } + + srtp_secret = (uint8_t*)XMALLOC(srtp_secret_length, + NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (srtp_secret == NULL) + err_sys("SRTP: low memory"); + + ret = wolfSSL_export_dtls_srtp_keying_material(ssl, srtp_secret, + &srtp_secret_length); + if (ret != WOLFSSL_SUCCESS) { + printf("SRTP: can't get dtsl_srtp keying material"); + return ret; + } + + printf("DTLS-SRTP exported key material:\n"); + for (p = srtp_secret; p < srtp_secret + srtp_secret_length; p++) + printf("%02X", *p); + printf("\n"); + + if (srtp_helper != NULL) { + srtp_helper_set_ekm(srtp_helper, srtp_secret, srtp_secret_length); + /* client code will free srtp_sercret buffer after checking for + correctness */ + } else { + XFREE(srtp_secret, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + + return 0; +} +#endif + THREAD_RETURN WOLFSSL_THREAD server_test(void* args) { SOCKET_T sockfd = WOLFSSL_SOCKET_INVALID; @@ -3088,6 +3141,23 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) } #endif +#ifdef WOLFSSL_SRTP + if (dtlsSrtpProfiles != NULL) { + err = server_srtp_test(ssl, ((func_args*)args)->srtp_test_helper); + if (err != 0) { + if (exitWithRet) { + ((func_args*)args)->return_code = err; + wolfSSL_free(ssl); ssl = NULL; + wolfSSL_CTX_free(ctx); ctx = NULL; + goto exit; + } + /* else */ + err_sys("SRTP check failed"); + } + } + +#endif + #ifdef HAVE_ALPN if (alpnList != NULL) { char *protocol_name = NULL, *list = NULL; @@ -3351,6 +3421,9 @@ exit: args.argv = argv; args.signal = &ready; args.return_code = 0; +#ifdef WOLFSSL_SRTP + args.srtp_test_helper = NULL; +#endif InitTcpReady(&ready); #if defined(DEBUG_WOLFSSL) && !defined(WOLFSSL_MDK_SHELL) diff --git a/tests/include.am b/tests/include.am index 6c5331600..daf96c814 100644 --- a/tests/include.am +++ b/tests/include.am @@ -37,6 +37,8 @@ EXTRA_DIST += tests/unit.h \ tests/test-dtls-reneg-server.conf \ tests/test-dtls-resume.conf \ tests/test-dtls-sha2.conf \ + tests/test-dtls-srtp.conf \ + tests/test-dtls-srtp-fails.conf \ tests/test-sctp.conf \ tests/test-sctp-sha2.conf \ tests/test-sig.conf \ diff --git a/tests/suites.c b/tests/suites.c index f410fb5d6..422bc5b36 100644 --- a/tests/suites.c +++ b/tests/suites.c @@ -294,7 +294,7 @@ static int execute_test_case(int svr_argc, char** svr_argv, int addDisableEMS, int forceSrvDefCipherList, int forceCliDefCipherList) { -#ifdef WOLFSSL_TIRTOS +#if defined(WOLFSSL_TIRTOS) || defined(WOLFSSL_SRTP) func_args cliArgs = {0}; func_args svrArgs = {0}; cliArgs.argc = cli_argc; @@ -321,6 +321,9 @@ static int execute_test_case(int svr_argc, char** svr_argv, int reqClientCert; #endif +#ifdef WOLFSSL_SRTP + struct srtp_test_helper srtp_helper; +#endif /* Is Valid Cipher and Version Checks */ /* build command list for the Is checks below */ commandLine[0] = '\0'; @@ -449,6 +452,11 @@ static int execute_test_case(int svr_argc, char** svr_argv, InitTcpReady(&ready); +#ifdef WOLFSSL_SRTP + srtp_helper_init(&srtp_helper); + cliArgs.srtp_test_helper = &srtp_helper; + svrArgs.srtp_test_helper = &srtp_helper; +#endif #ifdef WOLFSSL_TIRTOS fdOpenSession(Task_self()); #endif @@ -562,6 +570,10 @@ static int execute_test_case(int svr_argc, char** svr_argv, #endif FreeTcpReady(&ready); +#ifdef WOLFSSL_SRTP + srtp_helper_free(&srtp_helper); +#endif + /* only run the first test for expected failure cases */ /* the example server/client are not designed to handle expected failure in all cases, such as non-blocking, etc... */ @@ -1041,6 +1053,32 @@ int SuiteTest(int argc, char** argv) } strcpy(argv0[2], ""); #endif + +#ifdef WOLFSSL_SRTP + args.argc = 2; + strcpy(argv0[1], "tests/test-dtls-srtp.conf"); + printf("starting dtls srtp suite tests\n"); + test_harness(&args); + if (args.return_code != 0) { + printf("error from script %d\n", args.return_code); + args.return_code = EXIT_FAILURE; + goto exit; + } + + /* failure tests */ + args.argc = 3; + strcpy(argv0[1], "tests/test-dtls-srtp-fails.conf"); + strcpy(argv0[2], "expFail"); /* tests are expected to fail */ + printf("starting dtls srtp profile mismatch tests that expect failure\n"); + test_harness(&args); + if (args.return_code != 0) { + printf("error from script %d\n", args.return_code); + args.return_code = EXIT_FAILURE; + goto exit; + } + strcpy(argv0[2], ""); +#endif + #endif #ifdef WOLFSSL_SCTP /* add dtls-sctp extra suites */ diff --git a/tests/test-dtls-srtp-fails.conf b/tests/test-dtls-srtp-fails.conf new file mode 100644 index 000000000..3cde23308 --- /dev/null +++ b/tests/test-dtls-srtp-fails.conf @@ -0,0 +1,11 @@ +# server DTLSv1.2 all but SRTP_AEAD_AES_256_GCM +-u +-d +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32:SRTP_NULL_SHA1_80:SRTP_NULL_SHA1_32:SRTP_AEAD_AES_128_GCM + +# client DTLSv1.2 SRTP_AEAD_AES_256_GCM profile +-u +-x +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp SRTP_AEAD_AES_256_GCM diff --git a/tests/test-dtls-srtp.conf b/tests/test-dtls-srtp.conf new file mode 100644 index 000000000..071da218c --- /dev/null +++ b/tests/test-dtls-srtp.conf @@ -0,0 +1,192 @@ +# server DTLSv1.2 SRTP default profile +-u +-d +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp + +# client DTLSv1.2 SRTP default profile +-u +-x +--srtp +-l ECDHE-RSA-AES256-GCM-SHA384 + +# server DTLSv1.0 SRTP default profile +-u +-d +-v 2 +-l TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA +--srtp + +# client DTLSv1.0 SRTP default profile +-u +-x +-v 2 +-l TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA +--srtp + +# server DTLSv1.2 SRTP_AES128_CM_SHA1_32 profile +-u +-d +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp SRTP_AES128_CM_SHA1_32 + +# client DTLSv1.2 SRTP_AES128_CM_SHA1_32 profile +-u +-x +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp SRTP_AES128_CM_SHA1_32 + +# server DTLSv1.0 SRTP_AES128_CM_SHA1_32 profile +-u +-d +-v 2 +-l TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA +--srtp SRTP_AES128_CM_SHA1_32 + +# client DTLSv1.0 SRTP_AES128_CM_SHA1_32 profile +-u +-x +-v 2 +-l TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA +--srtp SRTP_AES128_CM_SHA1_32 + + +# server DTLSv1.2 SRTP_NULL_SHA1_32 profile +-u +-d +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp SRTP_NULL_SHA1_32 + +# client DTLSv1.2 SRTP_NULL_SHA1_32 profile +-u +-x +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp SRTP_NULL_SHA1_32 + +# server DTLSv1.0 SRTP_NULL_SHA1_32 profile +-u +-d +-v 2 +-l TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA +--srtp SRTP_NULL_SHA1_32 + +# client DTLSv1.0 SRTP_NULL_SHA1_32 profile +-u +-x +-v 2 +-l TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA +--srtp SRTP_NULL_SHA1_32 + +# server DTLSv1.2 SRTP_NULL_SHA1_80 profile +-u +-d +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp SRTP_NULL_SHA1_80 + +# client DTLSv1.2 SRTP_NULL_SHA1_80 profile +-u +-x +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp SRTP_NULL_SHA1_80 + +# server DTLSv1.0 SRTP_NULL_SHA1_80 profile +-u +-d +-v 2 +-l TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA +--srtp SRTP_NULL_SHA1_80 + +# client DTLSv1.0 SRTP_NULL_SHA1_80 profile +-u +-x +-v 2 +-l TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA +--srtp SRTP_NULL_SHA1_80 + +# server DTLSv1.2 SRTP_AEAD_AES_128_GCM profile +-u +-d +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp SRTP_AEAD_AES_128_GCM + +# client DTLSv1.2 SRTP_AEAD_AES_128_GCM profile +-u +-x +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp SRTP_AEAD_AES_128_GCM + +# server DTLSv1.0 SRTP_AEAD_AES_128_GCM profile +-u +-d +-v 2 +-l TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA +--srtp SRTP_AEAD_AES_128_GCM + +# client DTLSv1.0 SRTP_AEAD_AES_128_GCM profile +-u +-x +-v 2 +-l TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA +--srtp SRTP_AEAD_AES_128_GCM + +# server DTLSv1.2 SRTP_AEAD_AES_256_GCM profile +-u +-d +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp SRTP_AEAD_AES_256_GCM + +# client DTLSv1.2 SRTP_AEAD_AES_256_GCM profile +-u +-x +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp SRTP_AEAD_AES_256_GCM + +# server DTLSv1.0 SRTP_AEAD_AES_256_GCM profile +-u +-d +-v 2 +-l TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA +--srtp SRTP_AEAD_AES_256_GCM + +# client DTLSv1.0 SRTP_AEAD_AES_256_GCM profile +-u +-x +-v 2 +-l TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA +--srtp SRTP_AEAD_AES_256_GCM + +# server DTLSv1.2 SRTP_AEAD_AES_256_GCM profile +-u +-d +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp SRTP_AEAD_AES_256_GCM:SRTP_NULL_SHA1_32 + +# client DTLSv1.2 SRTP_AEAD_AES_256_GCM profile +-u +-x +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp SRTP_NULL_SHA1_32 + +# server DTLSv1.2 SRTP_AEAD_AES_256_GCM profile +-u +-d +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32:SRTP_NULL_SHA1_80:SRTP_NULL_SHA1_32:SRTP_AEAD_AES_128_GCM:SRTP_AEAD_AES_256_GCM + +# client DTLSv1.2 SRTP_AEAD_AES_256_GCM profile +-u +-x +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp SRTP_NULL_SHA1_32 + +# server DTLSv1.2 SRTP_AEAD_AES_256_GCM profile +-u +-d +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32:SRTP_NULL_SHA1_80:SRTP_NULL_SHA1_32:SRTP_AEAD_AES_128_GCM:SRTP_AEAD_AES_256_GCM + +# client DTLSv1.2 SRTP_AEAD_AES_256_GCM profile +-u +-x +-l ECDHE-RSA-AES256-GCM-SHA384 +--srtp SRTP_AEAD_AES_256_GCM diff --git a/wolfssl/test.h b/wolfssl/test.h index 155741750..65ad2f042 100644 --- a/wolfssl/test.h +++ b/wolfssl/test.h @@ -520,12 +520,26 @@ typedef struct callback_functions { unsigned char loadToSSL:1; } callback_functions; +#ifdef WOLFSSL_SRTP +struct srtp_test_helper { +#if defined(_POSIX_THREADS) && !defined(__MINGW32__) + pthread_mutex_t mutex; + pthread_cond_t cond; +#endif + uint8_t *server_srtp_ekm; + size_t server_srtp_ekm_size; +}; +#endif + typedef struct func_args { int argc; char** argv; int return_code; tcp_ready* signal; callback_functions *callbacks; +#ifdef WOLFSSL_SRTP + struct srtp_test_helper *srtp_test_helper; +#endif } func_args; #ifdef NETOS @@ -629,6 +643,82 @@ err_sys_with_errno(const char* msg) extern int myoptind; extern char* myoptarg; +#ifdef WOLFSSL_SRTP + +static WC_INLINE void srtp_helper_init(struct srtp_test_helper *srtp) +{ + srtp->server_srtp_ekm_size = 0; + srtp->server_srtp_ekm = NULL; +#if defined(_POSIX_THREADS) && !defined(__MINGW32__) + pthread_mutex_init(&srtp->mutex, 0); + pthread_cond_init(&srtp->cond, 0); +#endif +} + +/** + * strp_helper_get_ekm() - get exported key material of other peer + * @srtp: srtp_test_helper struct shared with other peer [in] + * @ekm: where to store the shared buffer pointer [out] + * @size: size of the shared buffer returned [out] + * + * This function wait that the other peer calls strp_helper_set_ekm() and then + * store the buffer pointer/size in @ekm and @size. + */ +static WC_INLINE void srtp_helper_get_ekm(struct srtp_test_helper *srtp, + uint8_t **ekm, size_t *size) +{ +#if defined(_POSIX_THREADS) && !defined(__MINGW32__) + pthread_mutex_lock(&srtp->mutex); + + if (srtp->server_srtp_ekm == NULL) + pthread_cond_wait(&srtp->cond, &srtp->mutex); + + *ekm = srtp->server_srtp_ekm; + *size = srtp->server_srtp_ekm_size; + + /* reset */ + srtp->server_srtp_ekm = NULL; + srtp->server_srtp_ekm_size = 0; + + pthread_mutex_unlock(&srtp->mutex); +#endif +} + +/** + * strp_helper_set_ekm() - set exported key material of other peer + * @srtp: srtp_test_helper struct shared with other peer [in] + * @ekm: pointer to the shared buffer [in] + * @size: size of the shared buffer [in] + * + * This function set the @ekm and wakes up a peer waiting in + * srtp_helper_get_ekm(). + * + * used in client_srtp_test()/server_srtp_test() + */ +static WC_INLINE void srtp_helper_set_ekm(struct srtp_test_helper *srtp, + uint8_t *ekm, size_t size) +{ +#if defined(_POSIX_THREADS) && !defined(__MINGW32__) + pthread_mutex_lock(&srtp->mutex); + + srtp->server_srtp_ekm_size = size; + srtp->server_srtp_ekm = ekm; + pthread_cond_signal(&srtp->cond); + + pthread_mutex_unlock(&srtp->mutex); +#endif +} + +static WC_INLINE void srtp_helper_free(struct srtp_test_helper *srtp) +{ +#if defined(_POSIX_THREADS) && !defined(__MINGW32__) + pthread_mutex_destroy(&srtp->mutex); + pthread_cond_destroy(&srtp->cond); +#endif +} + +#endif + /** * * @param argc Number of argv strings