From c10afcc9b713d802f1be4e5695ee6093f1cae88c Mon Sep 17 00:00:00 2001 From: Andrea Gianarda <andrea.gianarda@belledonne-communications.com> Date: Mon, 21 Feb 2022 12:01:57 +0100 Subject: [PATCH] Rework how encryption contexts are destroyed following encryption change Do not destroy unused SRTP, ZRTP and DTLS SRTP contexts as they may be needed in the future or after a different encryptin scheme is negotiationed due to network latencies Implement GoClear capability for ZRTP to be able to reenable it later during the call --- CMakeLists.txt | 18 +++++- config.h.cmake | 1 + coreapi/linphonecore.c | 22 +++++++ coreapi/private_functions.h | 2 + coreapi/vtables.c | 5 ++ include/linphone/api/c-call-cbs.h | 14 +++++ include/linphone/api/c-call.h | 7 +++ include/linphone/api/c-callbacks.h | 6 ++ include/linphone/callbacks.h | 7 +++ include/linphone/core.h | 32 ++++++++++ src/c-wrapper/api/c-call-cbs.cpp | 8 +++ src/c-wrapper/api/c-call.cpp | 11 ++++ src/call/call.cpp | 8 +++ src/call/call.h | 4 ++ src/conference/session/audio-stream.cpp | 3 +- .../session/call-session-listener.h | 4 +- src/conference/session/media-session.cpp | 10 +++ src/conference/session/media-session.h | 3 + src/conference/session/ms2-stream.cpp | 41 ++++++++++--- src/conference/session/ms2-streams.h | 12 +++- src/conference/session/stream.cpp | 6 ++ src/conference/session/streams-group.cpp | 11 ++++ src/conference/session/streams.h | 5 ++ src/conference/session/video-stream.cpp | 5 +- tester/capability_negotiation_tester.cpp | 61 ++++++++++++++++++- tester/liblinphone_tester.h | 2 + tester/tester.c | 8 +++ tester/zrtp_capability_negotiation_tester.cpp | 2 + 28 files changed, 301 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 77346199df..f64287524a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,10 +79,13 @@ option(ENABLE_VIDEO "Build with video support." YES) option(ENABLE_ASSETS "Package sound assets." YES) option(ENABLE_PACKAGE_SOURCE "Create 'package_source' target for source archive making (CMake >= 3.11)" OFF) - cmake_dependent_option(ENABLE_NOTIFY "Enable libnotify support." YES "ENABLE_GTK_UI;NOT APPLE" NO) cmake_dependent_option(ENABLE_ASSISTANT "Turn on assistant compiling." YES "ENABLE_GTK_UI" NO) +option(ENABLE_SRTP "Build with the SRTP transport support." YES) +cmake_dependent_option(ENABLE_ZRTP "Build with ZRTP support." YES "ENABLE_SRTP" NO) +cmake_dependent_option(ENABLE_GOCLEAR "Build with ZRTP GoClear message support (RFC 6189 - section 5.11)." YES "ENABLE_ZRTP" NO) + # Hidden non-cache options: # * DISABLE_BC_PACKAGE_SEARCH: skip find_package() for every BC package (bctoolbox, ortp, etc.) # * DISABLE_SOCI_PACKAGE_SEARCH: skip find_package() for Soci. @@ -152,6 +155,17 @@ if(ENABLE_ADVANCED_IM) set(XercesC_TARGET "XercesC::XercesC") endif() +if(ENABLE_SRTP) + set(HAVE_SRTP 1) +endif() + +if(ENABLE_ZRTP) + set(HAVE_ZRTP 1) + if(ENABLE_GOCLEAR) + set(HAVE_GOCLEAR 1) + endif() +endif() + find_package(Sqlite3 REQUIRED) find_package(XML2 REQUIRED) @@ -224,7 +238,7 @@ if(ENABLE_CXX_WRAPPER OR ENABLE_CSHARP_WRAPPER OR ENABLE_JAVA_WRAPPER OR ENABLE_ endif() endif() if(ENABLE_LDAP) - find_package(OpenLDAP REQUIRED) + find_package(OpenLDAP REQUIRED) endif() if (ENABLE_FLEXIAPI) diff --git a/config.h.cmake b/config.h.cmake index b7a2b6305a..9887a2c6d2 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -52,3 +52,4 @@ #cmakedefine HAVE_DB_STORAGE #cmakedefine ENABLE_UPDATE_CHECK 1 #cmakedefine HAVE_GETIFADDRS +#cmakedefine HAVE_GOCLEAR diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 51e9c2ae46..963bbc63b1 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -381,6 +381,14 @@ void linphone_core_cbs_set_refer_received(LinphoneCoreCbs *cbs, LinphoneCoreCbsR cbs->vtable->refer_received = cb; } +LinphoneCoreCbsCallGoClearAckSentCb linphone_core_cbs_get_call_goclear_ack_sent(LinphoneCoreCbs *cbs) { + return cbs->vtable->call_goclear_ack_sent; +} + +void linphone_core_cbs_set_call_goclear_ack_sent(LinphoneCoreCbs *cbs, LinphoneCoreCbsCallGoClearAckSentCb cb) { + cbs->vtable->call_goclear_ack_sent = cb; +} + LinphoneCoreCbsCallEncryptionChangedCb linphone_core_cbs_get_call_encryption_changed(LinphoneCoreCbs *cbs) { return cbs->vtable->call_encryption_changed; } @@ -8019,6 +8027,20 @@ void linphone_core_set_srtp_enabled(LinphoneCore *lc, bool_t enabled) { linphone_config_set_int(lc->config,"sip","srtp",(int)enabled); } +void linphone_core_enable_zrtp_go_clear(LinphoneCore *lc, bool_t enabled) { +#ifdef HAVE_GOCLEAR + linphone_config_set_int(linphone_core_get_config(lc), "sip", "enable_go_clear", (int)enabled); +#endif // HAVE_GOCLEAR +} + +bool_t linphone_core_zrtp_go_clear_enabled(const LinphoneCore *lc) { +#ifdef HAVE_GOCLEAR + return linphone_config_get_bool(linphone_core_get_config(lc), "sip", "enable_go_clear", FALSE); +#else + return FALSE; +#endif // HAVE_GOCLEAR +} + int linphone_media_encryption_from_string(const char * value){ if (strcmp(value, "LinphoneMediaEncryptionSRTP") == 0) { return LinphoneMediaEncryptionSRTP; diff --git a/coreapi/private_functions.h b/coreapi/private_functions.h index 422f6f892e..afa2cb4ea7 100644 --- a/coreapi/private_functions.h +++ b/coreapi/private_functions.h @@ -45,6 +45,7 @@ LinphoneConferenceCbs *_linphone_conference_cbs_new(void); void linphone_call_notify_state_changed(LinphoneCall *call, LinphoneCallState cstate, const char *message); void linphone_call_notify_dtmf_received(LinphoneCall *call, int dtmf); +void linphone_call_notify_goclear_ack_sent (LinphoneCall *call); void linphone_call_notify_encryption_changed(LinphoneCall *call, bool_t on, const char *authentication_token); void linphone_call_notify_transfer_state_changed(LinphoneCall *call, LinphoneCallState cstate); void linphone_call_notify_stats_updated(LinphoneCall *call, const LinphoneCallStats *stats); @@ -605,6 +606,7 @@ LINPHONE_PUBLIC LinphoneVideoActivationPolicy *linphone_video_activation_policy_ void linphone_core_notify_global_state_changed(LinphoneCore *lc, LinphoneGlobalState gstate, const char *message); void linphone_core_notify_call_state_changed(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message); +void linphone_core_notify_call_goclear_ack_sent(LinphoneCore *lc, LinphoneCall *call); void linphone_core_notify_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t on, const char *authentication_token); void linphone_core_notify_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState cstate, const char *message); void linphone_core_notify_account_registration_state_changed(LinphoneCore *core, LinphoneAccount *account, LinphoneRegistrationState state, const char *message); diff --git a/coreapi/vtables.c b/coreapi/vtables.c index 79f5cc867d..76739b25a1 100644 --- a/coreapi/vtables.c +++ b/coreapi/vtables.c @@ -123,6 +123,11 @@ void linphone_core_notify_audio_devices_list_updated(LinphoneCore *lc) { cleanup_dead_vtable_refs(lc); } +void linphone_core_notify_call_goclear_ack_sent(LinphoneCore *lc, LinphoneCall *call) { + NOTIFY_IF_EXIST(call_goclear_ack_sent, lc,call); + cleanup_dead_vtable_refs(lc); +} + void linphone_core_notify_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t on, const char *authentication_token) { NOTIFY_IF_EXIST(call_encryption_changed, lc,call,on,authentication_token); cleanup_dead_vtable_refs(lc); diff --git a/include/linphone/api/c-call-cbs.h b/include/linphone/api/c-call-cbs.h index dcd6ce1246..8e170f90e6 100644 --- a/include/linphone/api/c-call-cbs.h +++ b/include/linphone/api/c-call-cbs.h @@ -75,6 +75,20 @@ LINPHONE_PUBLIC LinphoneCallCbsDtmfReceivedCb linphone_call_cbs_get_dtmf_receive */ LINPHONE_PUBLIC void linphone_call_cbs_set_dtmf_received (LinphoneCallCbs *cbs, LinphoneCallCbsDtmfReceivedCb cb); +/** + * Get the GoClear Ack sent callback. + * @param cbs #LinphoneCallCbs object. @notnil + * @return The GoClear Ack sent callback. + */ +LINPHONE_PUBLIC LinphoneCallCbsGoClearAckSentCb linphone_call_cbs_get_goclear_ack_sent (LinphoneCallCbs *cbs); + +/** + * Set the GoClear Ack sent callback. + * @param cbs #LinphoneCallCbs object. @notnil + * @param[in] cb The GoClear Ack sent callback to be used. + */ +LINPHONE_PUBLIC void linphone_call_cbs_set_goclear_ack_sent (LinphoneCallCbs *cbs, LinphoneCallCbsGoClearAckSentCb cb); + /** * Get the encryption changed callback. * @param cbs #LinphoneCallCbs object. @notnil diff --git a/include/linphone/api/c-call.h b/include/linphone/api/c-call.h index fa9831b450..235a73db23 100644 --- a/include/linphone/api/c-call.h +++ b/include/linphone/api/c-call.h @@ -932,6 +932,13 @@ LINPHONE_PUBLIC void linphone_call_set_video_source(LinphoneCall *call, const Li */ LINPHONE_PUBLIC const LinphoneVideoSourceDescriptor *linphone_call_get_video_source(const LinphoneCall *call); +/** + * Method to be called after the user confirm that he/she is notifed of the on going Go Clear procedure. + * @warning this operation must be imperatevely initiate by a user action on sending of the GoClear ACK + * @param call The #LinphoneCall @notnil + */ +LINPHONE_PUBLIC void linphone_call_confirm_go_clear(const LinphoneCall *call); + /************ */ /* DEPRECATED */ /* ********** */ diff --git a/include/linphone/api/c-callbacks.h b/include/linphone/api/c-callbacks.h index fea0e185d8..f71ae30a6c 100644 --- a/include/linphone/api/c-callbacks.h +++ b/include/linphone/api/c-callbacks.h @@ -59,6 +59,12 @@ typedef void (*LinphoneAccountCbsRegistrationStateChangedCb)(LinphoneAccount *ac */ typedef void (*LinphoneCallCbsDtmfReceivedCb)(LinphoneCall *call, int dtmf); +/** + * GoClear ACK sent callback. + * @param call the #LinphoneCall on which the GoClear ACK was sent. @notnil + */ +typedef void (*LinphoneCallCbsGoClearAckSentCb)(LinphoneCall *call); + /** * Call encryption changed callback. * @param call #LinphoneCall object whose encryption is changed. @notnil diff --git a/include/linphone/callbacks.h b/include/linphone/callbacks.h index 9a9d495290..56e616b645 100644 --- a/include/linphone/callbacks.h +++ b/include/linphone/callbacks.h @@ -73,6 +73,13 @@ typedef void (*LinphoneCoreCbsCallStateChangedCb)(LinphoneCore *core, LinphoneCa */ typedef LinphoneCoreCbsCallStateChangedCb LinphoneCoreCallStateChangedCb; +/** + * GoClear ACK sent on call callback. + * @param core the #LinphoneCore @notnil + * @param call the #LinphoneCall on which the GoClear ACK was sent. @notnil + */ +typedef void (*LinphoneCoreCbsCallGoClearAckSentCb)(LinphoneCore *core, LinphoneCall *call); + /** * Call encryption changed callback. * @param core the #LinphoneCore @notnil diff --git a/include/linphone/core.h b/include/linphone/core.h index 7f340a9ab6..8d5900cba8 100644 --- a/include/linphone/core.h +++ b/include/linphone/core.h @@ -232,6 +232,7 @@ typedef struct _LinphoneCoreVTable{ LinphoneCoreIsComposingReceivedCb is_composing_received; /**< An is-composing notification has been received */ LinphoneCoreDtmfReceivedCb dtmf_received; /**< A dtmf has been received received */ LinphoneCoreReferReceivedCb refer_received; /**< An out of call refer was received */ + LinphoneCoreCbsCallGoClearAckSentCb call_goclear_ack_sent; /**<Notifies on sending of GoClear Ack */ LinphoneCoreCallEncryptionChangedCb call_encryption_changed; /**<Notifies on change in the encryption of call streams */ LinphoneCoreTransferStateChangedCb transfer_state_changed; /**<Notifies when a transfer is in progress */ LinphoneCoreBuddyInfoUpdatedCb buddy_info_updated; /**< a LinphoneFriend's BuddyInfo has changed*/ @@ -620,6 +621,20 @@ LINPHONE_PUBLIC void linphone_core_cbs_set_refer_received(LinphoneCoreCbs *cbs, */ LINPHONE_PUBLIC LinphoneCoreCbsReferReceivedCb linphone_core_cbs_get_refer_received(LinphoneCoreCbs *cbs); +/** + * Set the #LinphoneCoreCbsCallGoClearAckSentCb callback. + * @param cbs A #LinphoneCoreCbs. @notnil + * @param cb The callback. + */ +LINPHONE_PUBLIC void linphone_core_cbs_set_call_goclear_ack_sent(LinphoneCoreCbs *cbs, LinphoneCoreCbsCallGoClearAckSentCb cb); + +/** + * Get the #LinphoneCoreCbsCallGoClearAckSentCb callback. + * @param cbs A #LinphoneCoreCbs. @notnil + * @return The callback. + */ +LINPHONE_PUBLIC LinphoneCoreCbsCallGoClearAckSentCb linphone_core_cbs_get_call_goclear_ack_sent(LinphoneCoreCbs *cbs); + /** * Set the #LinphoneCoreCbsCallEncryptionChangedCb callback. * @param cbs A #LinphoneCoreCbs. @notnil @@ -4510,6 +4525,23 @@ LINPHONE_PUBLIC void linphone_core_set_srtp_crypto_suites(LinphoneCore *core, co **/ LINPHONE_PUBLIC const char *linphone_core_get_srtp_crypto_suites(LinphoneCore *core); +/** + * Check if the ZRTP go clear is enabled or not. + * @param core #LinphoneCore object. @notnil + * @return TRUE if ZTRP go clear is enabled; FALSE otherwise. + * @ingroup initializing + */ +LINPHONE_PUBLIC bool_t linphone_core_zrtp_go_clear_enabled(const LinphoneCore *core); + +/** + * Define whether ZRTP go clear is enabled + * @param core #LinphoneCore object. @notnil + * @param enable TRUE to enable ZRTP go clear; FALSE otherwise. + * @ingroup initializing + */ +LINPHONE_PUBLIC void linphone_core_enable_zrtp_go_clear(LinphoneCore *core, bool_t enabled); + + /** * Check if a media encryption type is supported * @param core core @notnil diff --git a/src/c-wrapper/api/c-call-cbs.cpp b/src/c-wrapper/api/c-call-cbs.cpp index 6478d21dca..53422f36cc 100644 --- a/src/c-wrapper/api/c-call-cbs.cpp +++ b/src/c-wrapper/api/c-call-cbs.cpp @@ -52,6 +52,14 @@ void linphone_call_cbs_set_dtmf_received (LinphoneCallCbs *cbs, LinphoneCallCbsD CallCbs::toCpp(cbs)->dtmfReceivedCb = cb; } +LinphoneCallCbsGoClearAckSentCb linphone_call_cbs_get_goclear_ack_sent (LinphoneCallCbs *cbs) { + return CallCbs::toCpp(cbs)->goClearAckSentCb; +} + +void linphone_call_cbs_set_goclear_ack_sent (LinphoneCallCbs *cbs, LinphoneCallCbsGoClearAckSentCb cb) { + CallCbs::toCpp(cbs)->goClearAckSentCb = cb; +} + LinphoneCallCbsEncryptionChangedCb linphone_call_cbs_get_encryption_changed (LinphoneCallCbs *cbs) { return CallCbs::toCpp(cbs)->encryptionChangedCb; } diff --git a/src/c-wrapper/api/c-call.cpp b/src/c-wrapper/api/c-call.cpp index 19ab703464..633475dfe1 100644 --- a/src/c-wrapper/api/c-call.cpp +++ b/src/c-wrapper/api/c-call.cpp @@ -89,6 +89,11 @@ void linphone_call_notify_dtmf_received (LinphoneCall *call, int dtmf) { linphone_core_notify_dtmf_received(linphone_call_get_core(call), call, dtmf); } +void linphone_call_notify_goclear_ack_sent (LinphoneCall *call) { + LINPHONE_HYBRID_OBJECT_INVOKE_CBS_NO_ARG(Call, Call::toCpp(call), linphone_call_cbs_get_goclear_ack_sent); + linphone_core_notify_call_goclear_ack_sent(linphone_call_get_core(call), call); +} + void linphone_call_notify_encryption_changed (LinphoneCall *call, bool_t on, const char *authentication_token) { LINPHONE_HYBRID_OBJECT_INVOKE_CBS(Call, Call::toCpp(call), linphone_call_cbs_get_encryption_changed, on, authentication_token); linphone_core_notify_call_encryption_changed(linphone_call_get_core(call), call, on, authentication_token); @@ -673,3 +678,9 @@ const LinphoneVideoSourceDescriptor *linphone_call_get_video_source(const Linpho auto descriptor = Call::toCpp(call)->getVideoSource(); return descriptor == nullptr ? NULL : descriptor->toC(); } + +void linphone_call_confirm_go_clear(const LinphoneCall *call) { +#ifdef HAVE_GOCLEAR + Call::toCpp(call)->confirmGoClear(); +#endif // HAVE_GOCLEAR +} diff --git a/src/call/call.cpp b/src/call/call.cpp index 7f6f677948..cf19805339 100644 --- a/src/call/call.cpp +++ b/src/call/call.cpp @@ -644,6 +644,10 @@ void Call::onEncryptionChanged (const shared_ptr<CallSession> &session, bool act linphone_call_notify_encryption_changed(this->toC(), activated, authToken.empty() ? nullptr : authToken.c_str()); } +void Call::onGoClearAckSent() { + linphone_call_notify_goclear_ack_sent(this->toC()); +} + void Call::onCallSessionStateChangedForReporting (const shared_ptr<CallSession> &session) { linphone_reporting_call_state_updated(this->toC()); } @@ -1278,6 +1282,10 @@ const std::list<LinphoneMediaEncryption> Call::getSupportedEncryptions() const { return getActiveSession()->getSupportedEncryptions(); } +void Call::confirmGoClear() const { + getMediaSession()->confirmGoClear(); +} + // ----------------------------------------------------------------------------- LinphoneConference *Call::getConference () const{ diff --git a/src/call/call.h b/src/call/call.h index c3d677b684..3b978df41b 100644 --- a/src/call/call.h +++ b/src/call/call.h @@ -57,6 +57,7 @@ namespace MediaConference { class CallCbs : public bellesip::HybridObject<LinphoneCallCbs, CallCbs>, public Callbacks{ public: LinphoneCallCbsDtmfReceivedCb dtmfReceivedCb; + LinphoneCallCbsGoClearAckSentCb goClearAckSentCb; LinphoneCallCbsEncryptionChangedCb encryptionChangedCb; LinphoneCallCbsInfoMessageReceivedCb infoMessageReceivedCb; LinphoneCallCbsStateChangedCb stateChangedCb; @@ -282,6 +283,7 @@ public: void onInfoReceived (const std::shared_ptr<CallSession> &session, const LinphoneInfoMessage *im) override; void onLossOfMediaDetected (const std::shared_ptr<CallSession> &session) override; void onEncryptionChanged (const std::shared_ptr<CallSession> &session, bool activated, const std::string &authToken) override; + void onGoClearAckSent() override; void onCallSessionStateChangedForReporting (const std::shared_ptr<CallSession> &session) override; void onRtcpUpdateForReporting (const std::shared_ptr<CallSession> &session, SalStreamType type) override; void onStatsUpdated (const std::shared_ptr<CallSession> &session, const LinphoneCallStats *stats) override; @@ -300,6 +302,8 @@ public: void onStartRingtone(const std::shared_ptr<CallSession> &session) override; void onRemoteRecording(const std::shared_ptr<CallSession> &session, bool recording) override; + void confirmGoClear() const override; + LinphoneConference *getConference () const; void reenterLocalConference(const std::shared_ptr<CallSession> &session); void setConference (LinphoneConference *ref); diff --git a/src/conference/session/audio-stream.cpp b/src/conference/session/audio-stream.cpp index a97a568a1b..3477a696a2 100644 --- a/src/conference/session/audio-stream.cpp +++ b/src/conference/session/audio-stream.cpp @@ -53,7 +53,7 @@ MS2AudioStream::MS2AudioStream(StreamsGroup &sg, const OfferAnswerContext ¶m mStream->disable_record_on_mute = getCCore()->sound_conf.disable_record_on_mute; /* initialize ZRTP if it supported as default encryption or as optional encryption and capability negotiation is enabled */ - if (getMediaSessionPrivate().isMediaEncryptionAccepted(LinphoneMediaEncryptionZRTP)) { + if (!mSessions.zrtp_context && getMediaSessionPrivate().isMediaEncryptionAccepted(LinphoneMediaEncryptionZRTP)) { initZrtp(); } initializeSessions((MediaStream*)mStream); @@ -93,6 +93,7 @@ void MS2AudioStream::initZrtp() { zrtpParams.zidCacheDBMutex = zrtpCacheInfo.dbMutex; zrtpParams.peerUri = peerUri; zrtpParams.selfUri = selfUri; + zrtpParams.acceptGoClear = !!linphone_core_zrtp_go_clear_enabled(getCCore()); /* Get key lifespan from config file, default is 0:forever valid */ zrtpParams.limeKeyTimeSpan = bctbx_time_string_to_sec(linphone_config_get_string(linphone_core_get_config(getCCore()), "sip", "lime_key_validity", "0")); setZrtpCryptoTypesParameters(&zrtpParams, isOfferer); diff --git a/src/conference/session/call-session-listener.h b/src/conference/session/call-session-listener.h index d80edf8325..5876926b94 100644 --- a/src/conference/session/call-session-listener.h +++ b/src/conference/session/call-session-listener.h @@ -60,6 +60,7 @@ public: virtual void onRemoteRecording(const std::shared_ptr<CallSession> &session, bool recording){} virtual void onEncryptionChanged (const std::shared_ptr<CallSession> &session, bool activated, const std::string &authToken) {} + virtual void onGoClearAckSent() {} virtual void onCallSessionStateChangedForReporting (const std::shared_ptr<CallSession> &session) {} virtual void onRtcpUpdateForReporting (const std::shared_ptr<CallSession> &session, SalStreamType type) {} @@ -79,7 +80,8 @@ public: virtual LinphoneConference * getCallSessionConference (const std::shared_ptr<CallSession> &session) const { return nullptr; } virtual void onRealTimeTextCharacterReceived (const std::shared_ptr<CallSession> &session, RealtimeTextReceivedCharacter *data) {} - + + virtual void confirmGoClear() const {}; }; LINPHONE_END_NAMESPACE diff --git a/src/conference/session/media-session.cpp b/src/conference/session/media-session.cpp index 1005c89e3c..5871f09919 100644 --- a/src/conference/session/media-session.cpp +++ b/src/conference/session/media-session.cpp @@ -4632,6 +4632,12 @@ void MediaSession::notifyMutedDevice(uint32_t ssrc, bool muted) { } } +void MediaSession::onGoClearAckSent() { + L_D(); + if (d->listener) + d->listener->onGoClearAckSent(); +} + void * MediaSession::getParticipantWindowId(const std::string label) { L_D(); @@ -4669,4 +4675,8 @@ std::shared_ptr<const VideoSourceDescriptor> MediaSession::getVideoSource () con #endif } +void MediaSession::confirmGoClear() { + getStreamsGroup().confirmGoClear(); +} + LINPHONE_END_NAMESPACE diff --git a/src/conference/session/media-session.h b/src/conference/session/media-session.h index 51ddc82fa8..cff595317d 100644 --- a/src/conference/session/media-session.h +++ b/src/conference/session/media-session.h @@ -144,12 +144,15 @@ public: bool isTerminator()const; void notifySpeakingDevice(uint32_t ssrc, bool isSpeaking); void notifyMutedDevice(uint32_t ssrc, bool muted); + void onGoClearAckSent(); void queueIceCompletionTask(const std::function<LinphoneStatus()> &lambda); void setVideoSource (const std::shared_ptr<const VideoSourceDescriptor> &descriptor); std::shared_ptr<const VideoSourceDescriptor> getVideoSource () const; + void confirmGoClear(); + private: L_DECLARE_PRIVATE(MediaSession); L_DISABLE_COPY(MediaSession); diff --git a/src/conference/session/ms2-stream.cpp b/src/conference/session/ms2-stream.cpp index 6811a9bd4e..33a7913a82 100644 --- a/src/conference/session/ms2-stream.cpp +++ b/src/conference/session/ms2-stream.cpp @@ -33,7 +33,6 @@ #include "linphone/core.h" - using namespace::std; LINPHONE_BEGIN_NAMESPACE @@ -74,6 +73,7 @@ MS2Stream::MS2Stream(StreamsGroup &sg, const OfferAnswerContext ¶ms) : Strea * interaction between streams from different connected participants. */ sg.installSharedService<BandwithControllerService>(); + mZrtpState = ZrtpState::Off; } void MS2Stream::removeFromBundle(){ @@ -260,6 +260,7 @@ void MS2Stream::fillLocalMediaDescription(OfferAnswerContext & ctx){ /* set the hello hash */ uint8_t enableZrtpHash = false; uint8_t zrtphash[128]; + mZrtpState = ZrtpState::Started; // Initialize ZRTP if not already done // This may happen when adding a stream through a reINVITE if (!mSessions.zrtp_context) { @@ -345,6 +346,7 @@ void MS2Stream::fillPotentialCfgGraph(OfferAnswerContext & ctx){ MS2AudioStream *msa = dynamic_cast<MS2AudioStream*>(stream); msa->initZrtp(); } + mZrtpState = ZrtpState::Started; // Copy newly created zrtp context into mSessions media_stream_reclaim_sessions(ms, &mSessions); } @@ -929,8 +931,19 @@ void MS2Stream::updateCryptoParameters(const OfferAnswerContext ¶ms) { // Copy newly created zrtp context into mSessions media_stream_reclaim_sessions(ms, &mSessions); } - } else if (mSessions.zrtp_context) { - media_stream_reset_zrtp_context(ms); + if (mZrtpState == ZrtpState::TurnedOff) { + ms_zrtp_back_to_secure_mode(mSessions.zrtp_context); + mZrtpState = ZrtpState::Restarted; + } else { + mZrtpState = ZrtpState::Started; + } + } else { + if ((mZrtpState == ZrtpState::Started) || (mZrtpState == ZrtpState::Restarted)) { + if (mSessions.zrtp_context) { + ms_zrtp_send_go_clear(mSessions.zrtp_context); + } + mZrtpState = ZrtpState::TurnedOff; + } } if (resultStreamDesc.hasDtls()) { @@ -944,9 +957,6 @@ void MS2Stream::updateCryptoParameters(const OfferAnswerContext ¶ms) { startDtls(params); } else { mDtlsStarted = false; - if (mSessions.dtls_context) { - ms_dtls_srtp_reset_context(mSessions.dtls_context); - } } } @@ -1232,7 +1242,17 @@ void MS2Stream::updateIceInStats(){ } } -void MS2Stream::dtlsEncryptionChanged(){ +void MS2Stream::goClearAckSent(){ + getGroup().goClearAckSent(); +} + +void MS2Stream::confirmGoClear(){ + if (mSessions.zrtp_context) { + ms_zrtp_confirm_go_clear(mSessions.zrtp_context); + } +} + +void MS2Stream::encryptionChanged(){ getGroup().propagateEncryptionChanged(); } @@ -1280,11 +1300,11 @@ void MS2Stream::handleEvents () { switch(evt){ case ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED: if (getType() != SalAudio || !isMain()){ - getGroup().propagateEncryptionChanged(); + encryptionChanged(); } break; case ORTP_EVENT_DTLS_ENCRYPTION_CHANGED: - dtlsEncryptionChanged(); + encryptionChanged(); break; case ORTP_EVENT_ICE_CHECK_LIST_DEFAULT_CANDIDATE_VERIFIED: mInternalStats.number_of_ice_check_list_relay_pair_verified++; @@ -1304,6 +1324,9 @@ void MS2Stream::handleEvents () { case ORTP_EVENT_ICE_RESTART_NEEDED: isIceEvent = true; break; + case ORTP_EVENT_ZRTP_PEER_ACK_GOCLEAR: + goClearAckSent(); + break; } if (isIceEvent){ /* ICE events are deferred to the IceService asynchronously because some ICE events can indirectly diff --git a/src/conference/session/ms2-streams.h b/src/conference/session/ms2-streams.h index 9904c10dfe..05c0f1eeb8 100644 --- a/src/conference/session/ms2-streams.h +++ b/src/conference/session/ms2-streams.h @@ -36,6 +36,13 @@ class MS2VideoMixer; */ class MS2Stream : public Stream, public RtpInterface { public: + enum class ZrtpState { + Off = 0, + Started = 1, + TurnedOff = 2, + Restarted = 3 + }; + virtual void fillLocalMediaDescription(OfferAnswerContext & ctx) override; virtual bool prepare() override; virtual void finishPrepare() override; @@ -58,6 +65,8 @@ public: virtual float getCpuUsage()const override; virtual void setIceCheckList(IceCheckList *cl) override; virtual void iceStateChanged() override; + virtual void goClearAckSent() override; + virtual void confirmGoClear() override; virtual void connectToMixer(StreamMixer *mixer) override; virtual void disconnectFromMixer()override; @@ -91,7 +100,7 @@ protected: int rtpPort, rtcpPort; }; void getRtpDestination(const OfferAnswerContext ¶ms, RtpAddressInfo *info); - void dtlsEncryptionChanged(); + void encryptionChanged(); std::string mDtlsFingerPrint; RtpProfile *mRtpProfile = nullptr; RtpProfile *mRtpIoProfile = nullptr; @@ -130,6 +139,7 @@ private: RtpBundle *mRtpBundle = nullptr; MS2Stream *mBundleOwner = nullptr; bool mOwnsBundle = false; + ZrtpState mZrtpState = ZrtpState::Off; static OrtpJitterBufferAlgorithm jitterBufferNameToAlgo(const std::string &name); static constexpr const int sEventPollIntervalMs = 20; }; diff --git a/src/conference/session/stream.cpp b/src/conference/session/stream.cpp index 45dc521e83..608da02cf7 100644 --- a/src/conference/session/stream.cpp +++ b/src/conference/session/stream.cpp @@ -105,6 +105,12 @@ void Stream::setIceCheckList(IceCheckList *cl){ void Stream::iceStateChanged(){ } +void Stream::goClearAckSent(){ +} + +void Stream::confirmGoClear(){ +} + void Stream::connectToMixer(StreamMixer *mixer){ mMixer = mixer; } diff --git a/src/conference/session/streams-group.cpp b/src/conference/session/streams-group.cpp index 64808270a7..5b354e2bf4 100644 --- a/src/conference/session/streams-group.cpp +++ b/src/conference/session/streams-group.cpp @@ -318,6 +318,10 @@ bool StreamsGroup::allStreamsEncrypted () const { return activeStreamsCount > 0; } +void StreamsGroup::goClearAckSent () { + getMediaSession().onGoClearAckSent(); +} + void StreamsGroup::propagateEncryptionChanged () { getMediaSessionPrivate().propagateEncryptionChanged(); } @@ -570,4 +574,11 @@ void StreamsGroup::unjoinMixerSession(){ mMixerSession = nullptr; } +void StreamsGroup::confirmGoClear() { + for (auto & stream : mStreams){ + if (!stream) continue; + stream->confirmGoClear(); + } +} + LINPHONE_END_NAMESPACE diff --git a/src/conference/session/streams.h b/src/conference/session/streams.h index 055bab8b6a..821da95b5b 100644 --- a/src/conference/session/streams.h +++ b/src/conference/session/streams.h @@ -119,6 +119,8 @@ public: * Called by the IceService to notify the stream of a state change in the ICE check list or the ICE session. */ virtual void iceStateChanged(); + virtual void goClearAckSent(); + virtual void confirmGoClear(); virtual bool isEncrypted() const = 0; virtual void tryEarlyMediaForking(const OfferAnswerContext &ctx) = 0; virtual void finishEarlyMediaForking() = 0; @@ -442,6 +444,9 @@ public: int getAvpfRrInterval()const; void tryEarlyMediaForking(const OfferAnswerContext &ctx); void finishEarlyMediaForking(); + void goClearAckSent(); + void confirmGoClear(); + /* * Iterates over streams, trying to cast them to the _requestedInterface type. If they do cast, * invoke the lambda expression on them. diff --git a/src/conference/session/video-stream.cpp b/src/conference/session/video-stream.cpp index c439b738b0..100aa0cb0d 100644 --- a/src/conference/session/video-stream.cpp +++ b/src/conference/session/video-stream.cpp @@ -36,6 +36,7 @@ #include "linphone/core.h" #include "mediastreamer2/msitc.h" +#include "bzrtp/bzrtp.h" using namespace::std; @@ -161,9 +162,11 @@ void MS2VideoStream::initZrtp() { if (audioStream){ MS2AudioStream *msa = dynamic_cast<MS2AudioStream*>(audioStream); video_stream_enable_zrtp(mStream, (AudioStream*)msa->getMediaStream()); - // Copy newly created zrtp context into mSessions media_stream_reclaim_sessions((MediaStream*)mStream, &mSessions); + if (mSessions.zrtp_context) { + ms_zrtp_enable_go_clear(mSessions.zrtp_context, linphone_core_zrtp_go_clear_enabled(getCCore())); + } } else { lError() << "Unable to initiate ZRTP session because no audio stream is attached to video stream " << this << "."; } diff --git a/tester/capability_negotiation_tester.cpp b/tester/capability_negotiation_tester.cpp index fd7a5be84a..1bc4537e80 100644 --- a/tester/capability_negotiation_tester.cpp +++ b/tester/capability_negotiation_tester.cpp @@ -2465,7 +2465,6 @@ void simple_call_with_capability_negotiations_with_different_encryption_after_re BC_ASSERT_TRUE(wait_for(callee->lc,caller->lc,&caller->stat.number_of_LinphoneCallStreamsRunning,caller_stat.number_of_LinphoneCallStreamsRunning + 1)); bool potentialConfigurationChosen = false; - get_expected_encryption_from_call_params(calleeCall, callerCall, &encryption, &potentialConfigurationChosen); int expectedStreamsRunning = 1 + ((potentialConfigurationChosen) ? 1 : 0); @@ -2600,6 +2599,10 @@ void simple_call_with_capability_negotiations_with_resume_and_media_change_base( BC_ASSERT_TRUE( wait_for(callee->lc,caller->lc,&callee->stat.number_of_LinphoneCallStreamsRunning,(callee_stat.number_of_LinphoneCallStreamsRunning+1))); BC_ASSERT_TRUE( wait_for(callee->lc,caller->lc,&caller->stat.number_of_LinphoneCallStreamsRunning,(caller_stat.number_of_LinphoneCallStreamsRunning+1))); + if (optionalEncryption == LinphoneMediaEncryptionZRTP) { + BC_ASSERT_TRUE(wait_for_until(callee->lc,caller->lc,&callee->stat.number_of_LinphoneCallGoClearAckSent,callee_stat.number_of_LinphoneCallGoClearAckSent+1,10000)); + } + bool potentialConfigurationChosen = false; LinphoneMediaEncryption encryption = LinphoneMediaEncryptionNone; get_expected_encryption_from_call_params(calleeCall, callerCall, &encryption, &potentialConfigurationChosen); @@ -2622,7 +2625,6 @@ void simple_call_with_capability_negotiations_with_resume_and_media_change_base( linphone_call_stats_unref(calleeStats); calleeStats = NULL; - BC_ASSERT_EQUAL(encryptionAfterResume, encryption, int, "%i"); if (calleeCall) { check_stream_encryption(calleeCall); BC_ASSERT_EQUAL(linphone_call_params_get_media_encryption(linphone_call_get_current_params(calleeCall)), encryption, int, "%i"); @@ -2636,6 +2638,61 @@ void simple_call_with_capability_negotiations_with_resume_and_media_change_base( const rtp_stats_t * stats = rtp_session_get_stats(linphone_call_get_stream(calleeCall, LinphoneStreamTypeAudio)->sessions.rtp_session); BC_ASSERT_LOWER((int)stats->cum_packet_loss, 10, int, "%d"); + callerParams = linphone_core_create_call_params(caller->lc, callerCall); + encryption_list = bctbx_list_append(NULL, LINPHONE_INT_TO_PTR(optionalEncryption)); + linphone_call_params_set_supported_encryptions(callerParams,encryption_list); + if (encryption_list) { + bctbx_list_free(encryption_list); + encryption_list = NULL; + } + linphone_call_update(callerCall, callerParams); + linphone_call_params_unref(callerParams); + BC_ASSERT_TRUE( wait_for(callee->lc,caller->lc,&caller->stat.number_of_LinphoneCallUpdating,(caller_stat.number_of_LinphoneCallUpdating+1))); + BC_ASSERT_TRUE( wait_for(callee->lc,caller->lc,&callee->stat.number_of_LinphoneCallUpdatedByRemote,(callee_stat.number_of_LinphoneCallUpdatedByRemote+1))); + + BC_ASSERT_TRUE( wait_for(callee->lc,caller->lc,&callee->stat.number_of_LinphoneCallStreamsRunning,(callee_stat.number_of_LinphoneCallStreamsRunning+1))); + BC_ASSERT_TRUE( wait_for(callee->lc,caller->lc,&caller->stat.number_of_LinphoneCallStreamsRunning,(caller_stat.number_of_LinphoneCallStreamsRunning+1))); + + potentialConfigurationChosen = false; + encryption = LinphoneMediaEncryptionNone; + get_expected_encryption_from_call_params(callerCall, calleeCall, &encryption, &potentialConfigurationChosen); + + expectedStreamsRunning = 1 + ((potentialConfigurationChosen) ? 1 : 0); + + /*wait for reINVITEs to complete*/ + BC_ASSERT_TRUE(wait_for(caller->lc,callee->lc,&caller->stat.number_of_LinphoneCallStreamsRunning,(caller_stat.number_of_LinphoneCallStreamsRunning+expectedStreamsRunning))); + BC_ASSERT_TRUE(wait_for(caller->lc,callee->lc,&callee->stat.number_of_LinphoneCallStreamsRunning,(callee_stat.number_of_LinphoneCallStreamsRunning+expectedStreamsRunning))); + if ((encryptionAfterResume == LinphoneMediaEncryptionNone) && ((encryption == LinphoneMediaEncryptionDTLS) || (encryption == LinphoneMediaEncryptionZRTP))) { + BC_ASSERT_TRUE(wait_for_until(callee->lc,caller->lc,&caller->stat.number_of_LinphoneCallEncryptedOn,caller_stat.number_of_LinphoneCallEncryptedOn+1,10000)); + BC_ASSERT_TRUE(wait_for_until(callee->lc,caller->lc,&callee->stat.number_of_LinphoneCallEncryptedOn,callee_stat.number_of_LinphoneCallEncryptedOn+1,10000)); + } + + BC_ASSERT_EQUAL(optionalEncryption, encryption, int, "%i"); + + wait_for_until(callee->lc, caller->lc, NULL, 5, 10000); + + liblinphone_tester_check_rtcp(caller, callee); + + BC_ASSERT_GREATER(linphone_core_manager_get_max_audio_down_bw(caller),70,int,"%i"); + calleeStats = linphone_call_get_audio_stats(linphone_core_get_current_call(callee->lc)); + BC_ASSERT_GREATER((int)linphone_call_stats_get_download_bandwidth(calleeStats),70,int,"%i"); + linphone_call_stats_unref(calleeStats); + calleeStats = NULL; + + if (calleeCall) { + check_stream_encryption(calleeCall); + BC_ASSERT_EQUAL(linphone_call_params_get_media_encryption(linphone_call_get_current_params(calleeCall)), encryption, int, "%i"); + } + if (callerCall) { + check_stream_encryption(callerCall); + BC_ASSERT_EQUAL(linphone_call_params_get_media_encryption(linphone_call_get_current_params(callerCall)), encryption, int, "%i"); + } + + /*since RTCP streams are reset when call is paused/resumed, there should be no loss at all*/ + stats = rtp_session_get_stats(linphone_call_get_stream(calleeCall, LinphoneStreamTypeAudio)->sessions.rtp_session); + BC_ASSERT_LOWER((int)stats->cum_packet_loss, 10, int, "%d"); + + end_call(caller, callee); } diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index 48908664a1..12efcaa2a8 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -357,6 +357,7 @@ typedef struct _stats { int number_of_LinphoneConfiguringFailed; int number_of_LinphoneConfiguringSuccessful; + int number_of_LinphoneCallGoClearAckSent; int number_of_LinphoneCallEncryptedOn; int number_of_LinphoneCallEncryptedOff; int number_of_NetworkReachableTrue; @@ -549,6 +550,7 @@ void linphone_publish_state_changed(LinphoneCore *lc, LinphoneEvent *ev, Linphon void linphone_notify_received(LinphoneCore *lc, LinphoneEvent *lev, const char *eventname, const LinphoneContent *content); void linphone_subscribe_received(LinphoneCore *lc, LinphoneEvent *lev, const char *eventname, const LinphoneContent *content); void linphone_configuration_status(LinphoneCore *lc, LinphoneConfiguringState status, const char *message); +void linphone_call_goclear_ack_sent(LinphoneCore *lc, LinphoneCall *call); void linphone_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t on, const char *authentication_token); void dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf); void call_stats_updated(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallStats *stats); diff --git a/tester/tester.c b/tester/tester.c index 6718108fb9..57fd721efc 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -1956,6 +1956,7 @@ void linphone_core_manager_init2(LinphoneCoreManager *mgr, const char* rc_file, linphone_core_cbs_set_publish_state_changed(mgr->cbs, linphone_publish_state_changed); linphone_core_cbs_set_configuring_status(mgr->cbs, linphone_configuration_status); linphone_core_cbs_set_call_encryption_changed(mgr->cbs, linphone_call_encryption_changed); + linphone_core_cbs_set_call_goclear_ack_sent(mgr->cbs, linphone_call_goclear_ack_sent); linphone_core_cbs_set_network_reachable(mgr->cbs, network_reachable); linphone_core_cbs_set_dtmf_received(mgr->cbs, dtmf_received); linphone_core_cbs_set_call_stats_updated(mgr->cbs, call_stats_updated); @@ -2899,6 +2900,13 @@ void linphone_configuration_status(LinphoneCore *lc, LinphoneConfiguringState st } } +void linphone_call_goclear_ack_sent(LinphoneCore *lc, LinphoneCall *call) { + stats* counters; + counters = get_stats(lc); + counters->number_of_LinphoneCallGoClearAckSent++; + linphone_call_confirm_go_clear(call); +} + void linphone_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t on, const char *authentication_token) { LinphoneCallLog *calllog = linphone_call_get_call_log(call); char* to=linphone_address_as_string(linphone_call_log_get_to_address(calllog)); diff --git a/tester/zrtp_capability_negotiation_tester.cpp b/tester/zrtp_capability_negotiation_tester.cpp index ccdd961ebc..f9d7737a43 100644 --- a/tester/zrtp_capability_negotiation_tester.cpp +++ b/tester/zrtp_capability_negotiation_tester.cpp @@ -78,7 +78,9 @@ static void simple_zrtp_call_with_capability_negotiations_with_dtls_srtp_encrypt static void simple_zrtp_call_with_capability_negotiations_with_resume_and_media_change(void) { LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); + linphone_core_enable_zrtp_go_clear(marie->lc, TRUE); LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); + linphone_core_enable_zrtp_go_clear(pauline->lc, TRUE); simple_call_with_capability_negotiations_with_resume_and_media_change_base(marie, pauline, LinphoneMediaEncryptionZRTP, LinphoneMediaEncryptionNone); linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); -- GitLab