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 &param
 	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 &params) : 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 &params) {
 			// 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 &params) {
 		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 &params, 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