From f0014ea42ee58516a674af30e490148bf69bf137 Mon Sep 17 00:00:00 2001
From: Clemence Him <clemence.him@belledonne-communications.com>
Date: Thu, 20 Mar 2025 11:26:10 +0100
Subject: [PATCH] Fix an unnecessary call to the encryption_changed callback

---
 src/conference/session/media-session.cpp |  70 ++++-----
 src/conference/session/streams-group.cpp |  11 +-
 tester/call_secure_tester.cpp            | 173 +++++++++++++----------
 tester/capability_negotiation_tester.cpp |   4 +
 tester/tester.c                          |   2 +-
 5 files changed, 150 insertions(+), 110 deletions(-)

diff --git a/src/conference/session/media-session.cpp b/src/conference/session/media-session.cpp
index c3a08e3a1e..6c20061c6f 100644
--- a/src/conference/session/media-session.cpp
+++ b/src/conference/session/media-session.cpp
@@ -18,20 +18,18 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "conference/session/media-session.h"
+#include "conference/session/media-session-p.h"
+
 #include <algorithm>
 
 #include "bctoolbox/defs.h"
 
-#include <mediastreamer2/mediastream.h>
-#include <mediastreamer2/msequalizer.h>
-#include <mediastreamer2/mseventqueue.h>
-#include <mediastreamer2/msfileplayer.h>
-#include <mediastreamer2/msrtt4103.h>
-#include <mediastreamer2/msvolume.h>
+#include "mediastreamer2/mediastream.h"
+#include "mediastreamer2/mseventqueue.h"
 
 #include "account/account.h"
 #include "address/address.h"
-#include "c-wrapper/c-wrapper.h"
 #include "call/call-stats.h"
 #include "call/call.h"
 #include "chat/chat-room/client-chat-room.h"
@@ -39,8 +37,6 @@
 #include "conference/params/media-session-params-p.h"
 #include "conference/participant.h"
 #include "conference/server-conference.h"
-#include "conference/session/media-session-p.h"
-#include "conference/session/media-session.h"
 #include "conference/session/streams.h"
 #include "core/core-p.h"
 #include "linphone/api/c-auth-info.h"
@@ -3479,29 +3475,32 @@ bool MediaSessionPrivate::isEncryptionMandatory() const {
 
 void MediaSessionPrivate::propagateEncryptionChanged() {
 	L_Q();
+	const auto oldEncryptionStatus = mEncryptionStatus;
 
-	auto oldEncryptionStatus = mEncryptionStatus;
-
-	string authToken = getStreamsGroup().getAuthenticationToken();
+	const string authToken = getStreamsGroup().getAuthenticationToken();
 	const auto conference = q->getCore()->findConference(q->getSharedFromThis(), false);
 	bool isInLocalConference = getParams()->getPrivate()->getInConference();
 	bool isInRemoteConference = conference && !isInLocalConference;
 	bool isInConference = (isInLocalConference || isInRemoteConference);
 	// If the media session is part of a conference, the client has no way to check the token, hence do not pass it on
 	// to the application
-	string callbackAuthToken = (conference) ? std::string() : authToken;
+	const string callbackAuthToken = (conference) ? std::string() : authToken;
 
 	if (callbackAuthToken.empty() && !authToken.empty()) {
 		getStreamsGroup().setAuthTokenVerified(true);
 	}
-	bool authTokenVerified = getStreamsGroup().getAuthenticationTokenVerified();
-	bool cacheMismatch = getStreamsGroup().getZrtpCacheMismatch();
-	bool zrtpCheckDone = getStreamsGroup().getAuthenticationTokenCheckDone();
+
 	if (!getStreamsGroup().allStreamsEncrypted()) {
 		lInfo() << "Some streams are not encrypted";
 		getCurrentParams()->setMediaEncryption(LinphoneMediaEncryptionNone);
-		q->notifyEncryptionChanged(false, callbackAuthToken);
+		// If the encryption status change from encrypted (SRTP/DTLS/ZRTP) to unencrypted, triggers a notification.
+		if (oldEncryptionStatus.getMediaEncryption() != LinphoneMediaEncryptionNone) {
+			q->notifyEncryptionChanged(false, callbackAuthToken);
+		}
 	} else {
+		const bool authTokenVerified = getStreamsGroup().getAuthenticationTokenVerified();
+		const bool cacheMismatch = getStreamsGroup().getZrtpCacheMismatch();
+		const bool zrtpCheckDone = getStreamsGroup().getAuthenticationTokenCheckDone();
 		if (!authToken.empty()) {
 			/* ZRTP only is using auth_token */
 			getCurrentParams()->setMediaEncryption(LinphoneMediaEncryptionZRTP);
@@ -3510,13 +3509,10 @@ void MediaSessionPrivate::propagateEncryptionChanged() {
 			}
 			auto encryptionEngine = q->getCore()->getEncryptionEngine();
 			if (encryptionEngine && authTokenVerified && !cacheMismatch) {
-				const SalAddress *remoteAddress = getOp()->getRemoteContactAddress();
-				if (remoteAddress) {
+				if (const SalAddress *remoteAddress = getOp()->getRemoteContactAddress()) {
 					char *peerDeviceId = sal_address_as_string_uri_only(remoteAddress);
-					Stream *stream = getStreamsGroup().lookupMainStream(SalAudio);
-					if (stream) {
-						MS2Stream *ms2s = dynamic_cast<MS2Stream *>(stream);
-						if (ms2s) {
+					if (Stream *stream = getStreamsGroup().lookupMainStream(SalAudio)) {
+						if (const auto ms2s = dynamic_cast<MS2Stream *>(stream)) {
 							encryptionEngine->authenticationVerified(ms2s->getZrtpContext(),
 							                                         op->getRemoteMediaDescription(), peerDeviceId);
 						} else {
@@ -3526,7 +3522,8 @@ void MediaSessionPrivate::propagateEncryptionChanged() {
 					ms_free(peerDeviceId);
 				} else {
 					/* This typically happens if the ZRTP session starts during early-media when receiving a 183
-					 * response. Indeed the Contact header is not mandatory in 183 (and liblinphone does not set it). */
+					 * response. Indeed, the Contact header is not mandatory in 183 (and liblinphone does not set it).
+					 */
 					lError() << "EncryptionEngine cannot be notified of verified status because remote contact address "
 					            "is unknown.";
 				}
@@ -3541,15 +3538,23 @@ void MediaSessionPrivate::propagateEncryptionChanged() {
 				}
 			}
 		} else {
-			/* Otherwise it must be DTLS as SDES doesn't go through this function */
-			getCurrentParams()->setMediaEncryption(LinphoneMediaEncryptionDTLS);
+			const int srtpRecvSource = getStreamsGroup().getEncryptionStatus().getSrtpRecvSource();
+			const int srtpSendSource = getStreamsGroup().getEncryptionStatus().getSrtpSendSource();
+			if (srtpRecvSource == srtpSendSource) {
+				if (srtpRecvSource == MSSrtpKeySourceDTLS) {
+					getCurrentParams()->setMediaEncryption(LinphoneMediaEncryptionDTLS);
+				} else if (srtpRecvSource == MSSrtpKeySourceSDES) {
+					getCurrentParams()->setMediaEncryption(LinphoneMediaEncryptionSRTP);
+				}
+			}
 		}
 
+		const auto mediaEncryption = q->getCurrentParams()->getMediaEncryption();
 		lInfo() << "All streams are encrypted, key exchanged using "
-		        << ((q->getCurrentParams()->getMediaEncryption() == LinphoneMediaEncryptionZRTP) ? "ZRTP"
-		            : (q->getCurrentParams()->getMediaEncryption() == LinphoneMediaEncryptionDTLS)
-		                ? "DTLS"
-		                : "Unknown mechanism");
+		        << (mediaEncryption == LinphoneMediaEncryptionZRTP   ? "ZRTP"
+		            : mediaEncryption == LinphoneMediaEncryptionDTLS ? "DTLS"
+		            : mediaEncryption == LinphoneMediaEncryptionSRTP ? "SRTP"
+		                                                             : "Unknown mechanism");
 		if (q->getCurrentParams()->getMediaEncryption() != LinphoneMediaEncryptionZRTP) {
 			q->notifyEncryptionChanged(true, callbackAuthToken);
 		}
@@ -3557,8 +3562,7 @@ void MediaSessionPrivate::propagateEncryptionChanged() {
 		Stream *videoStream = getStreamsGroup().lookupMainStream(SalVideo);
 		if (isEncryptionMandatory() && videoStream && videoStream->getState() == Stream::Running) {
 			/* Nothing could have been sent yet so generating key frame */
-			VideoControlInterface *vc = dynamic_cast<VideoControlInterface *>(videoStream);
-			if (vc) vc->sendVfu();
+			if (const auto vc = dynamic_cast<VideoControlInterface *>(videoStream)) vc->sendVfu();
 		}
 	}
 
@@ -5691,7 +5695,7 @@ void MediaSession::setAuthenticationTokenCheckDone(bool value) {
 
 void MediaSession::checkAuthenticationTokenSelected(const string &selectedValue, const string &halfAuthToken) {
 	L_D();
-	bool value = (selectedValue.compare(halfAuthToken) == 0) ? true : false;
+	const bool value = (selectedValue == halfAuthToken);
 	notifyAuthenticationTokenVerified(value);
 	setAuthenticationTokenCheckDone(true);
 	setAuthenticationTokenVerified(value);
diff --git a/src/conference/session/streams-group.cpp b/src/conference/session/streams-group.cpp
index eb7ca7acb5..cb68b8a5c8 100644
--- a/src/conference/session/streams-group.cpp
+++ b/src/conference/session/streams-group.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2022 Belledonne Communications SARL.
+ * Copyright (c) 2010-2025 Belledonne Communications SARL.
  *
  * This file is part of Liblinphone
  * (see https://gitlab.linphone.org/BC/public/liblinphone).
@@ -380,7 +380,7 @@ void StreamsGroup::propagateEncryptionChanged() {
 	// The task `mEncryptionChangedNotificationTask` ensures that the function
 	// `propagateEncryptionChanged()` is called only once to update the encryption
 	// status of all streams.
-	if (mEncryptionChangedNotificationTask == nullptr)
+	if (mEncryptionChangedNotificationTask == nullptr) {
 		mEncryptionChangedNotificationTask = getCore().createTimer(
 		    [this]() {
 			    getMediaSessionPrivate().propagateEncryptionChanged();
@@ -389,6 +389,7 @@ void StreamsGroup::propagateEncryptionChanged() {
 			    return true;
 		    },
 		    0, "Encryption changed notification task");
+	}
 }
 
 void StreamsGroup::authTokensReady(const list<string> &&incorrectAuthTokens,
@@ -406,9 +407,9 @@ void StreamsGroup::authTokensReady(const list<string> &&incorrectAuthTokens,
 	mAuthTokenVerified = verified;
 	mZrtpCacheMismatch = cacheMismatch;
 	mAuthTokenCheckDone = verified;
-	lInfo() << *this << ": Authentication token is " << mAuthToken << "("
-	        << (mAuthTokenVerified ? "verified" : "unverified") << " "
-	        << (mZrtpCacheMismatch ? "and cache mismatch" : "") << ")";
+	lInfo() << *this << ": Authentication token is " << mAuthToken << " ("
+	        << (mAuthTokenVerified ? "verified" : "unverified") << (mZrtpCacheMismatch ? " and cache mismatch" : "")
+	        << ")";
 }
 
 void StreamsGroup::setAuthTokenVerified(bool value) {
diff --git a/tester/call_secure_tester.cpp b/tester/call_secure_tester.cpp
index c7060c8c37..215fa4d7c4 100644
--- a/tester/call_secure_tester.cpp
+++ b/tester/call_secure_tester.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2022 Belledonne Communications SARL.
+ * Copyright (c) 2010-2025 Belledonne Communications SARL.
  *
  * This file is part of Liblinphone
  * (see https://gitlab.linphone.org/BC/public/liblinphone).
@@ -21,8 +21,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include <bctoolbox/crypto.hh>
-#include <bctoolbox/defs.h>
+#include "bctoolbox/crypto.hh"
 
 #include "belle-sip/sipstack.h"
 
@@ -1589,29 +1588,56 @@ void linphone_call_create_cbs_authentication_token_verified(LinphoneCall *call)
 	linphone_call_cbs_unref(call_cbs);
 }
 
+void validate_sas_authentication(LinphoneCoreManager *tested_mgr,
+                                 const LinphoneCoreManager *other_mgr,
+                                 const LinphoneCall *tested_mgr_call,
+                                 const bool correct_sas_selected,
+                                 const stats &tested_mgr_stats) {
+	if (correct_sas_selected) {
+		BC_ASSERT_TRUE(wait_for(other_mgr->lc, tested_mgr->lc, &tested_mgr->stat.number_of_LinphoneCallEncryptedOn, 2));
+		BC_ASSERT_EQUAL(tested_mgr_stats.number_of_LinphoneCallAuthenticationTokenVerified + 1,
+		                tested_mgr->stat.number_of_LinphoneCallAuthenticationTokenVerified, int, "%d");
+		BC_ASSERT_TRUE(linphone_call_get_authentication_token_verified(tested_mgr_call));
+	} else {
+		BC_ASSERT_EQUAL(tested_mgr_stats.number_of_LinphoneCallIncorrectAuthenticationTokenSelected + 1,
+		                tested_mgr->stat.number_of_LinphoneCallIncorrectAuthenticationTokenSelected, int, "%d");
+		BC_ASSERT_FALSE(linphone_call_get_authentication_token_verified(tested_mgr_call));
+	}
+}
+
 /*
  * @brief This test involves a call between Marie and Pauline and they check the SAS using
  * linphone_call_check_authentication_token_selected.
  * @param[in] marie 						The LinphoneCoreManager of Marie
  * @param[in] pauline 						The LinphoneCoreManager of Pauline
- * @param[in] marie_correct_sas_seleted 	TRUE if Marie selects the correct SAS
- * @param[in] pauline_correct_sas_seleted 	TRUE if Pauline selects the correct SAS
+ * @param[in] marie_correct_sas_selected 	TRUE if Marie selects the correct SAS
+ * @param[in] pauline_correct_sas_selected 	TRUE if Pauline selects the correct SAS
  */
 static void check_zrtp_short_code_base(LinphoneCoreManager *marie,
                                        LinphoneCoreManager *pauline,
-                                       bool_t marie_correct_sas_seleted,
-                                       bool_t pauline_correct_sas_seleted) {
-	LinphoneCall *marie_call = NULL;
-	LinphoneCall *pauline_call = NULL;
-	const bctbx_list_t *it;
-	const char *marie_local_auth_token = NULL;
-	const char *pauline_local_auth_token = NULL;
-	stats marie_stats;
-	stats pauline_stats;
+                                       const bool_t marie_correct_sas_selected,
+                                       const bool_t pauline_correct_sas_selected,
+                                       const bool_t enable_latency) {
+	LinphoneCall *marie_call = nullptr;
+	LinphoneCall *pauline_call = nullptr;
+	const char *marie_local_auth_token = nullptr;
+	const char *pauline_local_auth_token = nullptr;
 
 	linphone_core_set_media_encryption(marie->lc, LinphoneMediaEncryptionZRTP);
 	linphone_core_set_media_encryption(pauline->lc, LinphoneMediaEncryptionZRTP);
 
+	if (enable_latency) {
+		OrtpNetworkSimulatorParams simparams = {0};
+		simparams.mode = OrtpNetworkSimulatorOutbound;
+		simparams.enabled = TRUE;
+		simparams.latency = 50;
+		linphone_core_set_network_simulator_params(marie->lc, &simparams);
+		linphone_core_set_network_simulator_params(pauline->lc, &simparams);
+	}
+
+	stats marie_stats = marie->stat;
+	stats pauline_stats = pauline->stat;
+
 	BC_ASSERT_TRUE(call(pauline, marie));
 	marie_call = linphone_core_get_current_call(marie->lc);
 	pauline_call = linphone_core_get_current_call(pauline->lc);
@@ -1620,71 +1646,66 @@ static void check_zrtp_short_code_base(LinphoneCoreManager *marie,
 	BC_ASSERT_FALSE(linphone_call_get_zrtp_cache_mismatch_flag(marie_call));
 	BC_ASSERT_FALSE(linphone_call_get_zrtp_cache_mismatch_flag(pauline_call));
 
-	// Retrieve the local authentication tokens for both participants.
-	marie_local_auth_token = linphone_call_get_local_authentication_token(marie_call);
-	pauline_local_auth_token = linphone_call_get_local_authentication_token(pauline_call);
+	BC_ASSERT_TRUE(BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneCallEncryptedOn,
+	                                       marie_stats.number_of_LinphoneCallEncryptedOn + 1)));
+	BC_ASSERT_TRUE(BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &pauline->stat.number_of_LinphoneCallEncryptedOn,
+	                                       pauline_stats.number_of_LinphoneCallEncryptedOn + 1)));
+	if (enable_latency) {
+		// Check that latency does not cause an app notification indicating that there is no encryption because
+		// receiving the authentication token takes longer.
+		BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallEncryptedOff, 0, int, "%d");
+		BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallEncryptedOff, 0, int, "%d");
+	}
 
 	marie_stats = marie->stat;
 	pauline_stats = pauline->stat;
 
-	it = linphone_call_get_remote_authentication_tokens(marie_call);
-	if (marie_correct_sas_seleted) {
+	// Retrieve the local authentication tokens for both participants.
+	marie_local_auth_token = linphone_call_get_local_authentication_token(marie_call);
+	pauline_local_auth_token = linphone_call_get_local_authentication_token(pauline_call);
+
+	const bctbx_list_t *it = linphone_call_get_remote_authentication_tokens(marie_call);
+	if (marie_correct_sas_selected) {
 		// If Marie is expected to select the correct SAS, iterate until the correct token is found.
-		while (it && (strcmp((const char *)it->data, pauline_local_auth_token) != 0)) {
+		while (it && (strcmp(static_cast<const char *>(it->data), pauline_local_auth_token) != 0)) {
 			it = it->next;
 		}
 	} else {
 		// Otherwise, iterate until an incorrect token is found.
-		while (it && (strcmp((const char *)it->data, pauline_local_auth_token) == 0)) {
+		while (it && (strcmp(static_cast<const char *>(it->data), pauline_local_auth_token) == 0)) {
 			it = it->next;
 		}
 	}
 	BC_ASSERT_PTR_NOT_NULL(it);
-	linphone_call_check_authentication_token_selected(marie_call, (const char *)it->data);
+	linphone_call_check_authentication_token_selected(marie_call, static_cast<const char *>(it->data));
 
-	if (pauline_correct_sas_seleted) {
+	if (pauline_correct_sas_selected) {
 		// If Pauline is expected to select the correct SAS, iterate until the correct token is found.
 		it = linphone_call_get_remote_authentication_tokens(pauline_call);
-		while (it && (strcmp((const char *)it->data, marie_local_auth_token) != 0)) {
+		while (it && (strcmp(static_cast<const char *>(it->data), marie_local_auth_token) != 0)) {
 			it = it->next;
 		}
 		BC_ASSERT_PTR_NOT_NULL(it);
-		linphone_call_check_authentication_token_selected(pauline_call, (const char *)it->data);
+		linphone_call_check_authentication_token_selected(pauline_call, static_cast<const char *>(it->data));
 	} else {
 		// Otherwise, Pauline did not find the correct SAS, so she selects an empty token.
 		linphone_call_check_authentication_token_selected(pauline_call, "");
 	}
 
-	if (marie_correct_sas_seleted) {
-		BC_ASSERT_EQUAL(marie_stats.number_of_LinphoneCallAuthenticationTokenVerified + 1,
-		                marie->stat.number_of_LinphoneCallAuthenticationTokenVerified, int, "%d");
-		BC_ASSERT_TRUE(linphone_call_get_authentication_token_verified(marie_call));
-	} else {
-		BC_ASSERT_EQUAL(marie_stats.number_of_LinphoneCallIncorrectAuthenticationTokenSelected + 1,
-		                marie->stat.number_of_LinphoneCallIncorrectAuthenticationTokenSelected, int, "%d");
-		BC_ASSERT_FALSE(linphone_call_get_authentication_token_verified(marie_call));
-	}
-	if (pauline_correct_sas_seleted) {
-		BC_ASSERT_EQUAL(pauline_stats.number_of_LinphoneCallAuthenticationTokenVerified + 1,
-		                pauline->stat.number_of_LinphoneCallAuthenticationTokenVerified, int, "%d");
-		BC_ASSERT_TRUE(linphone_call_get_authentication_token_verified(pauline_call));
-	} else {
-		BC_ASSERT_EQUAL(pauline_stats.number_of_LinphoneCallIncorrectAuthenticationTokenSelected + 1,
-		                pauline->stat.number_of_LinphoneCallIncorrectAuthenticationTokenSelected, int, "%d");
-		BC_ASSERT_FALSE(linphone_call_get_authentication_token_verified(pauline_call));
-	}
+	validate_sas_authentication(marie, pauline, marie_call, marie_correct_sas_selected, marie_stats);
+	validate_sas_authentication(pauline, marie, pauline_call, pauline_correct_sas_selected, pauline_stats);
 }
 
 /*
  * This test involves a call between Marie and Pauline. SAS is checked by comparing the real SAS and the SAS selected by
  * the user.
  */
-static void check_correct_zrtp_short_code_test(void) {
+static void check_correct_zrtp_short_code_test() {
 	LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc");
 	LinphoneCoreManager *pauline = linphone_core_manager_new("pauline_tcp_rc");
 
 	if (BC_ASSERT_TRUE(linphone_core_media_encryption_supported(marie->lc, LinphoneMediaEncryptionZRTP))) {
-		check_zrtp_short_code_base(marie, pauline, TRUE, TRUE);
+		check_zrtp_short_code_base(marie, pauline, TRUE, TRUE, FALSE);
 		end_call(marie, pauline);
 	}
 
@@ -1693,25 +1714,37 @@ static void check_correct_zrtp_short_code_test(void) {
 }
 
 /*
- * This test involves a call between Marie and Pauline. Users select an incorrect SAS.
+ * This test simulates a call between Marie and Pauline with induced latency.
+ * SAS is checked by comparing the real SAS and the SAS selected by the user.
  */
-static void check_incorrect_zrtp_short_code_test(void) {
+static void check_correct_zrtp_short_code_with_latency_test() {
 	LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc");
 	LinphoneCoreManager *pauline = linphone_core_manager_new("pauline_tcp_rc");
 
 	if (BC_ASSERT_TRUE(linphone_core_media_encryption_supported(marie->lc, LinphoneMediaEncryptionZRTP))) {
-		LinphoneCall *marie_call = NULL;
-		LinphoneCall *pauline_call = NULL;
-		LinphoneCallStats *mstats = NULL;
-		LinphoneCallStats *pstats = NULL;
+		check_zrtp_short_code_base(marie, pauline, TRUE, TRUE, TRUE);
+		end_call(marie, pauline);
+	}
 
-		check_zrtp_short_code_base(marie, pauline, FALSE, FALSE);
+	linphone_core_manager_destroy(marie);
+	linphone_core_manager_destroy(pauline);
+}
 
-		BC_ASSERT_TRUE(wait_for_until(pauline->lc, marie->lc, NULL, 0, 2000));
-		marie_call = linphone_core_get_current_call(marie->lc);
-		mstats = linphone_call_get_audio_stats(marie_call);
-		pauline_call = linphone_core_get_current_call(pauline->lc);
-		pstats = linphone_call_get_audio_stats(pauline_call);
+/*
+ * This test involves a call between Marie and Pauline. Users select an incorrect SAS.
+ */
+static void check_incorrect_zrtp_short_code_test() {
+	LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc");
+	LinphoneCoreManager *pauline = linphone_core_manager_new("pauline_tcp_rc");
+
+	if (BC_ASSERT_TRUE(linphone_core_media_encryption_supported(marie->lc, LinphoneMediaEncryptionZRTP))) {
+		check_zrtp_short_code_base(marie, pauline, FALSE, FALSE, FALSE);
+
+		BC_ASSERT_TRUE(wait_for_until(pauline->lc, marie->lc, nullptr, 0, 2000));
+		LinphoneCall *marie_call = linphone_core_get_current_call(marie->lc);
+		LinphoneCallStats *mstats = linphone_call_get_audio_stats(marie_call);
+		LinphoneCall *pauline_call = linphone_core_get_current_call(pauline->lc);
+		LinphoneCallStats *pstats = linphone_call_get_audio_stats(pauline_call);
 
 		// Marie and Pauline selected a wrong SAS. We verify that they neither send nor receive anything.
 		BC_ASSERT_EQUAL(linphone_call_stats_get_download_bandwidth(mstats), 0.0, float, "%f");
@@ -1733,26 +1766,23 @@ static void check_incorrect_zrtp_short_code_test(void) {
  * This test involves a call between Marie and Pauline. Marie selects an incorrect SAS, and after that, Pauline attempts
  * to pause, resume and update the call.
  */
-static void check_incorrect_zrtp_short_code_pause_resume_update_test(void) {
+static void check_incorrect_zrtp_short_code_pause_resume_update_test() {
 	LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc");
 	LinphoneCoreManager *pauline = linphone_core_manager_new("pauline_tcp_rc");
 
 	if (BC_ASSERT_TRUE(linphone_core_media_encryption_supported(marie->lc, LinphoneMediaEncryptionZRTP))) {
-		LinphoneCall *marie_call = NULL;
-		LinphoneCall *pauline_call = NULL;
-		LinphoneCallStats *mstats = NULL;
-		LinphoneCallStats *pstats = NULL;
-		LinphoneCallParams *params = NULL;
+		LinphoneCallStats *pstats;
+		LinphoneCallParams *params;
 		stats marie_stats;
 		stats pauline_stats;
 
-		check_zrtp_short_code_base(marie, pauline, FALSE, TRUE);
+		check_zrtp_short_code_base(marie, pauline, FALSE, TRUE, FALSE);
 
-		marie_call = linphone_core_get_current_call(marie->lc);
-		pauline_call = linphone_core_get_current_call(pauline->lc);
+		LinphoneCall *marie_call = linphone_core_get_current_call(marie->lc);
+		LinphoneCall *pauline_call = linphone_core_get_current_call(pauline->lc);
 
-		BC_ASSERT_TRUE(wait_for_until(pauline->lc, marie->lc, NULL, 0, 2000));
-		mstats = linphone_call_get_audio_stats(marie_call);
+		BC_ASSERT_TRUE(wait_for_until(pauline->lc, marie->lc, nullptr, 0, 2000));
+		LinphoneCallStats *mstats = linphone_call_get_audio_stats(marie_call);
 		pstats = linphone_call_get_audio_stats(pauline_call);
 
 		// Marie selected a wrong SAS. We verify that she neither sends nor receives anything. Pauline, however, sends
@@ -1773,7 +1803,7 @@ static void check_incorrect_zrtp_short_code_pause_resume_update_test(void) {
 		BC_ASSERT_TRUE(wait_for_until(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneCallPausedByRemote,
 		                              marie_stats.number_of_LinphoneCallPausedByRemote + 1, 1000));
 
-		BC_ASSERT_TRUE(wait_for_until(pauline->lc, marie->lc, NULL, 0, 2000));
+		BC_ASSERT_TRUE(wait_for_until(pauline->lc, marie->lc, nullptr, 0, 2000));
 		mstats = linphone_call_get_audio_stats(marie_call);
 		pstats = linphone_call_get_audio_stats(pauline_call);
 		// We verify that we are still in the same state.
@@ -1789,7 +1819,7 @@ static void check_incorrect_zrtp_short_code_pause_resume_update_test(void) {
 		BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 2));
 		BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2));
 
-		BC_ASSERT_TRUE(wait_for_until(pauline->lc, marie->lc, NULL, 0, 2000));
+		BC_ASSERT_TRUE(wait_for_until(pauline->lc, marie->lc, nullptr, 0, 2000));
 		mstats = linphone_call_get_audio_stats(marie_call);
 		pstats = linphone_call_get_audio_stats(pauline_call);
 		// We verify that we are still in the same state.
@@ -1814,7 +1844,7 @@ static void check_incorrect_zrtp_short_code_pause_resume_update_test(void) {
 		linphone_call_params_unref(params);
 
 		// We verify that we are still in the same state.
-		wait_for_until(marie->lc, pauline->lc, NULL, 0, 1000);
+		wait_for_until(marie->lc, pauline->lc, nullptr, 0, 1000);
 		mstats = linphone_call_get_audio_stats(marie_call);
 		pstats = linphone_call_get_audio_stats(pauline_call);
 		BC_ASSERT_EQUAL(linphone_call_stats_get_download_bandwidth(mstats), 0.0, float, "%f");
@@ -2276,6 +2306,7 @@ static test_t call_secure2_tests[] = {
     TEST_NO_TAG("Recreate ZRTP db file when corrupted", recreate_zrtpdb_when_corrupted),
     TEST_NO_TAG("ZRTP cache mismatch", zrtp_cache_mismatch_test),
     TEST_NO_TAG("Check correct ZRTP short code", check_correct_zrtp_short_code_test),
+    TEST_NO_TAG("Check correct ZRTP short code with latency", check_correct_zrtp_short_code_with_latency_test),
     TEST_NO_TAG("Check incorrect ZRTP short code", check_incorrect_zrtp_short_code_test),
     TEST_NO_TAG("Check incorrect ZRTP short code and pause/resume/update",
                 check_incorrect_zrtp_short_code_pause_resume_update_test),
diff --git a/tester/capability_negotiation_tester.cpp b/tester/capability_negotiation_tester.cpp
index df8f495fd6..5d366573e5 100644
--- a/tester/capability_negotiation_tester.cpp
+++ b/tester/capability_negotiation_tester.cpp
@@ -3336,6 +3336,10 @@ void call_with_toggling_encryption_base(const LinphoneMediaEncryption encryption
 		BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallStreamsRunning,
 		                (marie_stat.number_of_LinphoneCallStreamsRunning + expectedStreamsRunning), int, "%d");
 
+		BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneCallEncryptedOff,
+		                        marie_stat.number_of_LinphoneCallEncryptedOff + 1));
+		BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &pauline->stat.number_of_LinphoneCallEncryptedOff,
+		                        pauline_stat.number_of_LinphoneCallEncryptedOff + 1));
 		BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneCallSecurityLevelDowngraded,
 		                        marie_stat.number_of_LinphoneCallSecurityLevelDowngraded + 1));
 		BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &pauline->stat.number_of_LinphoneCallSecurityLevelDowngraded,
diff --git a/tester/tester.c b/tester/tester.c
index ce182e978e..9c004db78f 100644
--- a/tester/tester.c
+++ b/tester/tester.c
@@ -4727,7 +4727,7 @@ bool_t call_with_params2(LinphoneCoreManager *caller_mgr,
 
 	BC_ASSERT_PTR_NULL(linphone_call_get_remote_params(
 	    caller_call)); /*assert that remote params are NULL when no response is received yet*/
-	// test ios simulator needs more time, 3s plus for connectng the network
+	// test ios simulator needs more time, 3s plus for connecting the network
 	did_receive_call =
 	    wait_for_until(callee_mgr->lc, caller_mgr->lc, &callee_mgr->stat.number_of_LinphoneCallIncomingReceived,
 	                   initial_callee.number_of_LinphoneCallIncomingReceived + 1, 12000);
-- 
GitLab