From 0a0f714cdd66b1404790c05c952751511c8b33c8 Mon Sep 17 00:00:00 2001 From: Johan Pascal Date: Fri, 15 Apr 2016 11:46:43 +0200 Subject: [PATCH] improve ZRTP hash management - start ZRTP on incoming zrtp-hash even when not selected locally --- coreapi/linphonecall.c | 33 ++++++++++++--- coreapi/offeranswer.c | 3 +- coreapi/sal.c | 15 +++++++ include/sal/sal.h | 1 + tester/call_tester.c | 92 ++++++++++++++++++++++++++++++++++++------ 5 files changed, 123 insertions(+), 21 deletions(-) diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 1e95540ca..18d494896 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -524,12 +524,16 @@ static void setup_encryption_keys(LinphoneCall *call, SalMediaDescription *md){ static void setup_zrtp_hash(LinphoneCall *call, SalMediaDescription *md) { int i; - if (call->params->media_encryption==LinphoneMediaEncryptionZRTP) { + if (ms_zrtp_available()) { /* set the hello hash for all streams */ for(i=0; istreams[i])) continue; if (call->sessions[i].zrtp_context!=NULL) { ms_zrtp_getHelloHash(call->sessions[i].zrtp_context, md->streams[i].zrtphash, 128); - md->streams[i].haveZrtpHash = 1; + if (call->params->media_encryption==LinphoneMediaEncryptionZRTP) { /* turn on the flag to use it if ZRTP is set */ + md->streams[i].haveZrtpHash = 1; + } else { + md->streams[i].haveZrtpHash = 0; + } } else { md->streams[i].haveZrtpHash = 0; } @@ -1179,7 +1183,9 @@ void linphone_call_set_compatible_incoming_call_parameters(LinphoneCall *call, S call->params->avpf_rr_interval = linphone_core_get_avpf_rr_interval(call->core)*1000; } } - if ((sal_media_description_has_dtls(md) == TRUE) && (media_stream_dtls_supported() == TRUE)) { + if ((sal_media_description_has_zrtp(md) == TRUE) && (ms_zrtp_available() == TRUE)) { + call->params->media_encryption = LinphoneMediaEncryptionZRTP; + }else if ((sal_media_description_has_dtls(md) == TRUE) && (media_stream_dtls_supported() == TRUE)) { call->params->media_encryption = LinphoneMediaEncryptionDTLS; }else if ((sal_media_description_has_srtp(md) == TRUE) && (ms_srtp_supported() == TRUE)) { call->params->media_encryption = LinphoneMediaEncryptionSRTP; @@ -1815,7 +1821,12 @@ const LinphoneCallParams * linphone_call_get_current_params(LinphoneCall *call){ }//else don't update the state if all streams are shutdown. break; case LinphoneMediaEncryptionNone: - call->current_params->media_encryption=LinphoneMediaEncryptionNone; + /* check if we actually switched to ZRTP */ + if (at_least_one_stream_started(call) && (all_streams_encrypted = linphone_call_all_streams_encrypted(call)) && linphone_call_get_authentication_token(call)) { + call->current_params->media_encryption=LinphoneMediaEncryptionZRTP; + } else { + call->current_params->media_encryption=LinphoneMediaEncryptionNone; + } break; } call->current_params->avpf_enabled = linphone_call_all_streams_avpf_enabled(call) && sal_media_description_has_avpf(md); @@ -3360,6 +3371,10 @@ static void linphone_call_start_video_stream(LinphoneCall *call, LinphoneCallSta } cam = linphone_call_get_video_device(call); if (!is_inactive){ + /* get remote stream description to check for zrtp-hash presence */ + SalMediaDescription *remote_desc = sal_call_get_remote_media_description(call->op); + const SalStreamDescription *remote_stream = sal_media_description_find_best_stream(remote_desc, SalVideo); + if (sal_stream_description_has_srtp(vstream) == TRUE) { int crypto_idx = find_crypto_index_from_tag(local_st_desc->crypto, vstream->crypto_local_tag); if (crypto_idx >= 0) { @@ -3412,11 +3427,17 @@ static void linphone_call_start_video_stream(LinphoneCall *call, LinphoneCallSta ms_media_stream_sessions_set_encryption_mandatory(&call->videostream->ms.sessions,call->current_params->encryption_mandatory); _linphone_call_set_next_video_frame_decoded_trigger(call); - /* start ZRTP if needed */ - if (call->params->media_encryption==LinphoneMediaEncryptionZRTP) { + /* start ZRTP engine if needed : set here or remote have a zrtp-hash attribute */ + if (call->params->media_encryption==LinphoneMediaEncryptionZRTP || remote_stream->haveZrtpHash==1) { /*audio stream is already encrypted and video stream is active*/ if (media_stream_secured((MediaStream *)call->audiostream) && media_stream_get_state((MediaStream *)call->videostream) == MSStreamStarted) { video_stream_start_zrtp(call->videostream); + if (remote_stream->haveZrtpHash == 1) { + int retval; + if ((retval = ms_zrtp_setPeerHelloHash(call->videostream->ms.sessions.zrtp_context, (uint8_t *)remote_stream->zrtphash, strlen((const char *)(remote_stream->zrtphash)))) != 0) { + ms_error("video stream ZRTP hash mismatch 0x%x", retval); + } + } } } } diff --git a/coreapi/offeranswer.c b/coreapi/offeranswer.c index 6aad0b2bf..0676e2a74 100644 --- a/coreapi/offeranswer.c +++ b/coreapi/offeranswer.c @@ -489,11 +489,10 @@ static void initiate_incoming(MSFactory *factory, const SalStreamDescription *lo } if (remote_offer->haveZrtpHash == 1) { - if (local_cap->haveZrtpHash == 1) { + if (ms_zrtp_available()) { /* if ZRTP is available, set the zrtp hash even if it is not selected */ strncpy((char *)(result->zrtphash), (char *)(local_cap->zrtphash), sizeof(local_cap->zrtphash)); result->haveZrtpHash = 1; } - /* TODO: what if remote offer a zrtp-hash but we don't have it in local */ } strcpy(result->ice_pwd, local_cap->ice_pwd); diff --git a/coreapi/sal.c b/coreapi/sal.c index 2b04dbb1d..a1620d7c0 100644 --- a/coreapi/sal.c +++ b/coreapi/sal.c @@ -257,6 +257,11 @@ bool_t sal_stream_description_has_dtls(const SalStreamDescription *sd) { return FALSE; } +bool_t sal_stream_description_has_zrtp(const SalStreamDescription *sd) { + if (sd->haveZrtpHash==1) return TRUE; + return FALSE; +} + bool_t sal_media_description_has_avpf(const SalMediaDescription *md) { int i; if (md->nb_streams == 0) return FALSE; @@ -297,6 +302,16 @@ bool_t sal_media_description_has_dtls(const SalMediaDescription *md) { return TRUE; } +bool_t sal_media_description_has_zrtp(const SalMediaDescription *md) { + int i; + if (md->nb_streams == 0) return FALSE; + for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) { + if (!sal_stream_description_active(&md->streams[i])) continue; + if (sal_stream_description_has_zrtp(&md->streams[i]) != TRUE) return FALSE; + } + return TRUE; +} + /* static bool_t fmtp_equals(const char *p1, const char *p2){ if (p1 && p2 && strcmp(p1,p2)==0) return TRUE; diff --git a/include/sal/sal.h b/include/sal/sal.h index 8d1e1f0ee..0806b2707 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -330,6 +330,7 @@ bool_t sal_media_description_has_avpf(const SalMediaDescription *md); bool_t sal_media_description_has_implicit_avpf(const SalMediaDescription *md); bool_t sal_media_description_has_srtp(const SalMediaDescription *md); bool_t sal_media_description_has_dtls(const SalMediaDescription *md); +bool_t sal_media_description_has_zrtp(const SalMediaDescription *md); int sal_media_description_get_nb_active_streams(const SalMediaDescription *md); struct SalOpBase; diff --git a/tester/call_tester.c b/tester/call_tester.c index 83ca92b88..cbeb51e78 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -331,13 +331,22 @@ bool_t call_with_params2(LinphoneCoreManager* caller_mgr || linphone_core_get_media_encryption(callee_mgr->lc) != LinphoneMediaEncryptionNone) { /*wait for encryption to be on, in case of zrtp or dtls, it can take a few seconds*/ if ( (linphone_core_get_media_encryption(caller_mgr->lc) == LinphoneMediaEncryptionZRTP) + || (linphone_core_get_media_encryption(callee_mgr->lc) == LinphoneMediaEncryptionZRTP) /* is callee is ZRTP, wait for it */ || (linphone_core_get_media_encryption(caller_mgr->lc) == LinphoneMediaEncryptionDTLS)) wait_for(callee_mgr->lc,caller_mgr->lc,&caller_mgr->stat.number_of_LinphoneCallEncryptedOn,initial_caller.number_of_LinphoneCallEncryptedOn+1); if ((linphone_core_get_media_encryption(callee_mgr->lc) == LinphoneMediaEncryptionZRTP) || (linphone_core_get_media_encryption(callee_mgr->lc) == LinphoneMediaEncryptionDTLS) + || (linphone_core_get_media_encryption(caller_mgr->lc) == LinphoneMediaEncryptionZRTP) || (linphone_core_get_media_encryption(caller_mgr->lc) == LinphoneMediaEncryptionDTLS) /*also take care of caller policy*/ ) wait_for(callee_mgr->lc,caller_mgr->lc,&callee_mgr->stat.number_of_LinphoneCallEncryptedOn,initial_callee.number_of_LinphoneCallEncryptedOn+1); - { + + /* when caller is encryptionNone but callee is ZRTP, we expect ZRTP to take place */ + if ((linphone_core_get_media_encryption(caller_mgr->lc) == LinphoneMediaEncryptionNone) && (linphone_core_get_media_encryption(callee_mgr->lc) == LinphoneMediaEncryptionZRTP)) { + const LinphoneCallParams* call_param = linphone_call_get_current_params(callee_call); + BC_ASSERT_EQUAL(linphone_call_params_get_media_encryption(call_param), LinphoneMediaEncryptionZRTP, int, "%d"); + call_param = linphone_call_get_current_params(linphone_core_get_current_call(caller_mgr->lc)); + BC_ASSERT_EQUAL(linphone_call_params_get_media_encryption(call_param), LinphoneMediaEncryptionZRTP, int, "%d"); + }else { /* otherwise, final status shall stick to caller core parameter */ const LinphoneCallParams* call_param = linphone_call_get_current_params(callee_call); BC_ASSERT_EQUAL(linphone_call_params_get_media_encryption(call_param),linphone_core_get_media_encryption(caller_mgr->lc), int, "%d"); call_param = linphone_call_get_current_params(linphone_core_get_current_call(caller_mgr->lc)); @@ -3134,8 +3143,9 @@ void call_base_with_configfile(LinphoneMediaEncryption mode, bool_t enable_video BC_ASSERT_TRUE((call_ok=call(pauline,marie))); if (!call_ok) goto end; - if (linphone_core_get_media_encryption(pauline->lc) == LinphoneMediaEncryptionZRTP - && linphone_core_get_media_encryption(pauline->lc) == LinphoneMediaEncryptionZRTP) { + /* if caller set ZRTP or (callee set ZRTP and caller has no encryption requested), ZRTP shall take place, wait for the SAS */ + if ((linphone_core_get_media_encryption(pauline->lc) == LinphoneMediaEncryptionZRTP) + || ((linphone_core_get_media_encryption(marie->lc) == LinphoneMediaEncryptionZRTP) && (linphone_core_get_media_encryption(pauline->lc) == LinphoneMediaEncryptionNone))) { /*wait for SAS*/ int i; for (i=0;i<100;i++) { @@ -6078,21 +6088,45 @@ static void call_with_ice_and_rtcp_mux_without_reinvite(void){ } static void call_with_zrtp_configured_calling_base(LinphoneCoreManager *marie, LinphoneCoreManager *pauline) { - bool_t call_ok; + if (ms_zrtp_available()) { + bool_t call_ok; - linphone_core_set_media_encryption(marie->lc, LinphoneMediaEncryptionZRTP); - BC_ASSERT_TRUE((call_ok=call(pauline,marie))); + linphone_core_set_media_encryption(pauline->lc, LinphoneMediaEncryptionZRTP); + BC_ASSERT_TRUE((call_ok=call(pauline,marie))); - liblinphone_tester_check_rtcp(marie,pauline); + liblinphone_tester_check_rtcp(marie,pauline); - BC_ASSERT_EQUAL(linphone_call_params_get_media_encryption(linphone_call_get_current_params(linphone_core_get_current_call(marie->lc))) - , LinphoneMediaEncryptionNone, int, "%i"); - BC_ASSERT_EQUAL(linphone_call_params_get_media_encryption(linphone_call_get_current_params(linphone_core_get_current_call(pauline->lc))) - , LinphoneMediaEncryptionNone, int, "%i"); - end_call(pauline, marie); + BC_ASSERT_EQUAL(linphone_call_params_get_media_encryption(linphone_call_get_current_params(linphone_core_get_current_call(marie->lc))) + , LinphoneMediaEncryptionZRTP, int, "%i"); + BC_ASSERT_EQUAL(linphone_call_params_get_media_encryption(linphone_call_get_current_params(linphone_core_get_current_call(pauline->lc))) + , LinphoneMediaEncryptionZRTP, int, "%i"); + end_call(pauline, marie); + } else { + ms_warning("Test skipped, ZRTP not available"); + } } +static void call_with_zrtp_configured_callee_base(LinphoneCoreManager *marie, LinphoneCoreManager *pauline) { + if (ms_zrtp_available()) { + bool_t call_ok; + + linphone_core_set_media_encryption(marie->lc, LinphoneMediaEncryptionZRTP); + BC_ASSERT_TRUE((call_ok=call(pauline,marie))); + + liblinphone_tester_check_rtcp(marie,pauline); + + BC_ASSERT_EQUAL(linphone_call_params_get_media_encryption(linphone_call_get_current_params(linphone_core_get_current_call(marie->lc))) + , LinphoneMediaEncryptionZRTP, int, "%i"); + BC_ASSERT_EQUAL(linphone_call_params_get_media_encryption(linphone_call_get_current_params(linphone_core_get_current_call(pauline->lc))) + , LinphoneMediaEncryptionZRTP, int, "%i"); + end_call(pauline, marie); + } else { + ms_warning("Test skipped, ZRTP not available"); + } +} + + /* * this test checks the 'dont_default_to_stun_candidates' mode, where the c= line is left to host * ip instead of stun candidate when ice is enabled*/ @@ -6137,6 +6171,16 @@ static void call_with_zrtp_configured_calling_side(void) { call_with_zrtp_configured_calling_base(marie,pauline); + /* now set other encryptions mode for receiver(marie), we shall always fall back to caller preference: ZRTP */ + linphone_core_set_media_encryption(marie->lc, LinphoneMediaEncryptionDTLS); + call_with_zrtp_configured_calling_base(marie,pauline); + + linphone_core_set_media_encryption(marie->lc, LinphoneMediaEncryptionSRTP); + call_with_zrtp_configured_calling_base(marie,pauline); + + + linphone_core_set_media_encryption(marie->lc, LinphoneMediaEncryptionNone); + linphone_core_set_user_agent(pauline->lc, "Natted Linphone", NULL); linphone_core_set_user_agent(marie->lc, "Natted Linphone", NULL); call_with_zrtp_configured_calling_base(marie,pauline); @@ -6150,6 +6194,27 @@ static void call_with_zrtp_configured_calling_side(void) { } + +static void call_with_zrtp_configured_callee_side(void) { + LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); + + call_with_zrtp_configured_callee_base(marie,pauline); + + linphone_core_set_user_agent(pauline->lc, "Natted Linphone", NULL); + linphone_core_set_user_agent(marie->lc, "Natted Linphone", NULL); + call_with_zrtp_configured_callee_base(marie,pauline); + + linphone_core_set_firewall_policy(marie->lc,LinphonePolicyUseIce); + linphone_core_set_firewall_policy(pauline->lc,LinphonePolicyUseIce); + call_with_zrtp_configured_callee_base(marie,pauline); + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); + + +} + test_t call_tests[] = { TEST_NO_TAG("Early declined call", early_declined_call), TEST_NO_TAG("Call declined", call_declined), @@ -6331,7 +6396,8 @@ test_t call_tests[] = { TEST_ONE_TAG("Call with ICE and rtcp-mux without ICE re-invite", call_with_ice_and_rtcp_mux_without_reinvite, "ICE"), TEST_ONE_TAG("Call with ICE with default candidate not stun", call_with_ice_with_default_candidate_not_stun, "ICE"), TEST_ONE_TAG("Call with ICE without stun server", call_with_ice_without_stun, "ICE"), - TEST_NO_TAG("call with ZRTP configured calling side only", call_with_zrtp_configured_calling_side) + TEST_NO_TAG("call with ZRTP configured calling side only", call_with_zrtp_configured_calling_side), + TEST_NO_TAG("call with ZRTP configured receiver side only", call_with_zrtp_configured_callee_side) }; test_suite_t call_test_suite = {"Single Call", NULL, NULL, liblinphone_tester_before_each, liblinphone_tester_after_each,