Browse code

zrtp: updated ZRTP support, compiles with the current zfone SDK

use the zfone SDK from https://github.com/traviscross/libzrtp

Stefan Sayer authored on 30/10/2014 22:31:46
Showing 11 changed files
... ...
@@ -104,6 +104,10 @@ bool         AmConfig::LogSessions             = false;
104 104
 bool         AmConfig::LogEvents               = false;
105 105
 int          AmConfig::UnhandledReplyLoglevel  = 0;
106 106
 
107
+#ifdef WITH_ZRTP
108
+bool         AmConfig::enable_zrtp             = true;
109
+#endif
110
+
107 111
 unsigned int AmConfig::SessionLimit            = 0;
108 112
 unsigned int AmConfig::SessionLimitErrCode     = 503;
109 113
 string       AmConfig::SessionLimitErrReason   = "Server overload";
... ...
@@ -588,6 +592,11 @@ int AmConfig::readConfiguration()
588 592
     }
589 593
   }
590 594
 
595
+#ifdef WITH_ZRTP
596
+  enable_zrtp = cfg.getParameter("enable_zrtp", "yes") == "yes";
597
+  INFO("ZRTP %sabled\n", enable_zrtp ? "en":"dis");
598
+#endif
599
+
591 600
   if(cfg.hasParameter("session_limit")){ 
592 601
     vector<string> limit = explode(cfg.getParameter("session_limit"), ";");
593 602
     if (limit.size() != 3) {
... ...
@@ -220,6 +220,10 @@ struct AmConfig
220 220
   /* this is regex->application mapping is used if  App_MAPPING */
221 221
   static RegexMappingVector AppMapping; 
222 222
 
223
+#ifdef WITH_ZRTP
224
+  static bool enable_zrtp;
225
+#endif
226
+
223 227
   static unsigned int SessionLimit;
224 228
   static unsigned int SessionLimitErrCode;
225 229
   static string SessionLimitErrReason;
... ...
@@ -57,7 +57,7 @@
57 57
 #include <netinet/in.h>
58 58
 
59 59
 #ifdef WITH_ZRTP
60
-#include "zrtp/zrtp.h"
60
+#include "libzrtp/zrtp.h"
61 61
 #endif
62 62
 
63 63
 #include "rtp/rtp.h"
... ...
@@ -260,10 +260,15 @@ int AmRtpStream::compile_and_send(const int payload, bool marker, unsigned int t
260 260
   rp.setAddr(&r_saddr);
261 261
 
262 262
 #ifdef WITH_ZRTP
263
-  if (session && session->zrtp_audio) {
264
-    zrtp_status_t status = zrtp_status_fail;
263
+  if (session && session->enable_zrtp){
264
+    if (NULL == session->zrtp_session_state.zrtp_audio) {
265
+      ERROR("ZRTP enabled on session, but no audio stream created\n");
266
+      return -1;
267
+    }
268
+
265 269
     unsigned int size = rp.getBufferSize();
266
-    status = zrtp_process_rtp(session->zrtp_audio, (char*)rp.getBuffer(), &size);
270
+    zrtp_status_t status = zrtp_process_rtp(session->zrtp_session_state.zrtp_audio,
271
+					    (char*)rp.getBuffer(), &size);
267 272
     switch (status) {
268 273
     case zrtp_status_drop: {
269 274
       DBG("ZRTP says: drop packet! %u - %u\n", size, rp.getBufferSize());
... ...
@@ -749,9 +754,11 @@ int AmRtpStream::init(const AmSdp& local,
749 754
   last_payload = payload;
750 755
 
751 756
 #ifdef WITH_ZRTP  
752
-  if( session->zrtp_audio  ) {
753
-    DBG("now starting zrtp stream...\n");
754
-    zrtp_start_stream( session->zrtp_audio );
757
+  if (session && session->enable_zrtp) {
758
+    if (session->zrtp_session_state.initSession(session))
759
+      return -1;
760
+
761
+    session->zrtp_session_state.startStreams(get_ssrc());
755 762
   }
756 763
 #endif
757 764
 
... ...
@@ -818,7 +825,7 @@ void AmRtpStream::bufferPacket(AmRtpPacket* p)
818 825
     return;
819 826
   }
820 827
 
821
-  if (relay_enabled) {
828
+  if (relay_enabled) { // todo: ZRTP
822 829
     if (force_receive_dtmf) {
823 830
       recvDtmfPacket(p);
824 831
     }
... ...
@@ -858,35 +865,37 @@ void AmRtpStream::bufferPacket(AmRtpPacket* p)
858 865
 #endif
859 866
 
860 867
   receive_mut.lock();
861
-  // NOTE: useless, as DTMF events are pushed into 'rtp_ev_qu'
862
-  // free packet on double packet for TS received
863
-  // if(p->payload == getLocalTelephoneEventPT()) {
864
-  //   if (receive_buf.find(p->timestamp) != receive_buf.end()) {
865
-  //     mem.freePacket(receive_buf[p->timestamp]);
866
-  //   }
867
-  // }  
868 868
 
869 869
 #ifdef WITH_ZRTP
870
-  if (session->zrtp_audio) {
870
+  if (session && session->enable_zrtp) {
871 871
 
872
-    zrtp_status_t status = zrtp_status_fail;
873
-    unsigned int size = p->getBufferSize();
874
-    
875
-    status = zrtp_process_srtp(session->zrtp_audio, (char*)p->getBuffer(), &size);
872
+    if (NULL == session->zrtp_session_state.zrtp_audio) {
873
+      WARN("dropping received packet, as there's no ZRTP stream initialized\n");
874
+      receive_mut.unlock();
875
+      mem.freePacket(p);
876
+      return;      
877
+    }
878
+ 
879
+    unsigned int size = p->getBufferSize();    
880
+    zrtp_status_t status = zrtp_process_srtp(session->zrtp_session_state.zrtp_audio, (char*)p->getBuffer(), &size);
876 881
     switch (status)
877 882
       {
878
-      case zrtp_status_forward:
879 883
       case zrtp_status_ok: {
880 884
 	p->setBufferSize(size);
881 885
 	if (p->parse() < 0) {
882 886
 	  ERROR("parsing decoded packet!\n");
883 887
 	  mem.freePacket(p);
884 888
 	} else {
889
+
885 890
           if(p->payload == getLocalTelephoneEventPT()) {
886 891
             rtp_ev_qu.push(p);
887 892
           } else {
888
-	    receive_buf[p->timestamp] = p;
893
+	    if(!receive_buf.insert(ReceiveBuffer::value_type(p->timestamp,p)).second) {
894
+	      // insert failed
895
+	      mem.freePacket(p);
896
+	    }
889 897
           }
898
+
890 899
 	}
891 900
       }	break;
892 901
 
... ...
@@ -1033,9 +1042,14 @@ void AmRtpStream::recvPacket(int fd)
1033 1042
 
1034 1043
     gettimeofday(&p->recv_time,NULL);
1035 1044
     
1036
-    if(!relay_raw)
1045
+    if(!relay_raw
1046
+#ifdef WITH_ZRTP
1047
+       && !(session && session->enable_zrtp)
1048
+#endif
1049
+       ) {
1037 1050
       parse_res = p->parse();
1038
- 
1051
+    }
1052
+
1039 1053
     if (parse_res == -1) {
1040 1054
       DBG("error while parsing RTP packet.\n");
1041 1055
       clearRTPTimeout(&p->recv_time);
... ...
@@ -76,7 +76,7 @@ AmSession::AmSession(AmSipDialog* p_dlg)
76 76
     refresh_method(REFRESH_UPDATE_FB_REINV),
77 77
     processing_status(SESSION_PROCESSING_EVENTS)
78 78
 #ifdef WITH_ZRTP
79
-  ,  zrtp_session(NULL), zrtp_audio(NULL), enable_zrtp(true)
79
+  , enable_zrtp(AmConfig::enable_zrtp)
80 80
 #endif
81 81
 
82 82
 #ifdef SESSION_THREADPOOL
... ...
@@ -98,7 +98,8 @@ AmSession::~AmSession()
98 98
   }
99 99
 
100 100
 #ifdef WITH_ZRTP
101
-  AmZRTP::freeSession(zrtp_session);
101
+  if (enable_zrtp)
102
+    zrtp_session_state.freeSession();
102 103
 #endif
103 104
 
104 105
   delete dlg;
... ...
@@ -280,34 +281,10 @@ void AmSession::run() {
280 281
 bool AmSession::startup() {
281 282
 #ifdef WITH_ZRTP
282 283
   if (enable_zrtp) {
283
-    zrtp_session = (zrtp_conn_ctx_t*)malloc(sizeof(zrtp_conn_ctx_t));
284
-    if (NULL == zrtp_session)  {
285
-      ERROR("allocating ZRTP session context mem.\n");
286
-    } else {
287
-      zrtp_profile_t profile;
288
-      zrtp_profile_autoload(&profile, &AmZRTP::zrtp_global);		
289
-      profile.active = false;
290
-      profile.allowclear = true;
291
-      profile.autosecure = true; // automatically go into secure mode at the beginning
292
-      
293
-      if (zrtp_status_ok != zrtp_init_session_ctx( zrtp_session,
294
-						   &AmZRTP::zrtp_global,
295
-						   &profile, 
296
-						   AmZRTP::zrtp_instance_zid) ) {
297
-	ERROR("initializing ZRTP session context\n");
298
-	return false;
299
-      }
300
-      
301
-      zrtp_audio = zrtp_attach_stream(zrtp_session, RTPStream()->get_ssrc());
302
-      zrtp_audio->stream_usr_data = this;
303
-      
304
-      if (NULL == zrtp_audio) {
305
-	ERROR("attaching zrtp stream.\n");
306
-	return false;
307
-      }
284
+    if (zrtp_session_state.initSession(this))
285
+      return -1;
308 286
       
309 287
       DBG("initialized ZRTP session context OK\n");
310
-    }
311 288
   }
312 289
 #endif
313 290
 
... ...
@@ -694,9 +671,15 @@ void AmSession::process(AmEvent* ev)
694 671
   }
695 672
 
696 673
 #ifdef WITH_ZRTP
697
-  AmZRTPEvent* zrtp_ev = dynamic_cast<AmZRTPEvent*>(ev);
698
-  if(zrtp_ev){
699
-    onZRTPEvent((zrtp_event_t)zrtp_ev->event_id, zrtp_ev->stream_ctx);
674
+  AmZRTPProtocolEvent* zrtp_p_ev = dynamic_cast<AmZRTPProtocolEvent*>(ev);
675
+  if(zrtp_p_ev){
676
+    onZRTPProtocolEvent((zrtp_protocol_event_t)zrtp_p_ev->event_id, zrtp_p_ev->stream_ctx);
677
+    return;
678
+  }
679
+
680
+  AmZRTPSecurityEvent* zrtp_s_ev = dynamic_cast<AmZRTPSecurityEvent*>(ev);
681
+  if(zrtp_s_ev){
682
+    onZRTPSecurityEvent((zrtp_security_event_t)zrtp_s_ev->event_id, zrtp_s_ev->stream_ctx);
700 683
     return;
701 684
   }
702 685
 #endif
... ...
@@ -1261,50 +1244,48 @@ bool AmSession::removeTimers() {
1261 1244
 
1262 1245
  
1263 1246
 #ifdef WITH_ZRTP
1264
-void AmSession::onZRTPEvent(zrtp_event_t event, zrtp_stream_ctx_t *stream_ctx) {
1265
-  DBG("AmSession::onZRTPEvent \n");
1247
+
1248
+void AmSession::onZRTPProtocolEvent(zrtp_protocol_event_t event, zrtp_stream_t *stream_ctx) {
1249
+  DBG("AmSession::onZRTPProtocolEvent\n");
1266 1250
   switch (event)
1267 1251
     {
1252
+    case ZRTP_EVENT_UNSUPPORTED:
1253
+      INFO("ZRTP_EVENT_UNSUPPORTED\n");
1254
+      break;
1255
+    case ZRTP_EVENT_IS_CLEAR:
1256
+      INFO("ZRTP_EVENT_IS_CLEAR\n");
1257
+      break;
1258
+    case ZRTP_EVENT_IS_INITIATINGSECURE:
1259
+      INFO("ZRTP_EVENT_IS_INITIATINGSECURE\n");
1260
+      break;
1261
+    case ZRTP_EVENT_IS_PENDINGSECURE:
1262
+      INFO("ZRTP_EVENT_PENDINGSECURE\n");
1263
+      break;
1264
+
1268 1265
     case ZRTP_EVENT_IS_SECURE: {
1269 1266
       INFO("ZRTP_EVENT_IS_SECURE \n");
1270 1267
       //         info->is_verified  = ctx->_session_ctx->secrets.verifieds & ZRTP_BIT_RS0;
1271 1268
  
1272
-      zrtp_conn_ctx_t *session = stream_ctx->_session_ctx;
1269
+      // zrtp_session_t *session = stream_ctx->_session_ctx;
1273 1270
  
1274
-      if (ZRTP_SAS_BASE32 == session->sas_values.rendering) {
1275
- 	DBG("Got SAS value <<<%.4s>>>\n", session->sas_values.str1.buffer);
1276
-      } else {
1277
- 	DBG("Got SAS values SAS1 '%s' and SAS2 '%s'\n", 
1278
- 	    session->sas_values.str1.buffer,
1279
- 	    session->sas_values.str2.buffer);
1280
-      } 
1271
+      // if (ZRTP_SAS_BASE32 == session->sas_values.rendering) {
1272
+      // 	DBG("Got SAS value <<<%.4s>>>\n", session->sas_values.str1.buffer);
1273
+      // } else {
1274
+      // 	DBG("Got SAS values SAS1 '%s' and SAS2 '%s'\n", 
1275
+      // 	    session->sas_values.str1.buffer,
1276
+      // 	    session->sas_values.str2.buffer);
1277
+      // } 
1281 1278
     } break;
1282 1279
  
1283 1280
     case ZRTP_EVENT_IS_PENDINGCLEAR:
1284 1281
       INFO("ZRTP_EVENT_IS_PENDINGCLEAR\n");
1285 1282
       INFO("other side requested goClear. Going clear.\n\n");
1286
-      zrtp_clear_stream(zrtp_audio);
1287
-      break;
1288
- 
1289
-    case ZRTP_EVENT_IS_CLEAR:
1290
-      INFO("ZRTP_EVENT_IS_CLEAR\n");
1291
-      break;
1292
- 
1293
-    case ZRTP_EVENT_UNSUPPORTED:
1294
-      INFO("ZRTP_EVENT_UNSUPPORTED\n");
1295
-      break;
1296
-    case ZRTP_EVENT_IS_INITIATINGSECURE:
1297
-      INFO("ZRTP_EVENT_IS_INITIATINGSECURE\n");
1298
-      break;
1299
-    case ZRTP_EVENT_IS_PENDINGSECURE:
1300
-      INFO("ZRTP_EVENT_PENDINGSECURE\n");
1283
+      //      zrtp_clear_stream(zrtp_audio);
1301 1284
       break;
1285
+  
1302 1286
     case ZRTP_EVENT_IS_SECURE_DONE:
1303 1287
       INFO("ZRTP_EVENT_IS_SECURE_DONE\n");
1304 1288
       break;
1305
-    case ZRTP_EVENT_ERROR:
1306
-      INFO("ZRTP_EVENT_ERROR\n");
1307
-      break;
1308 1289
     case ZRTP_EVENT_NO_ZRTP:
1309 1290
       INFO("ZRTP_EVENT_NO_ZRTP\n");
1310 1291
       break;
... ...
@@ -1339,12 +1320,21 @@ void AmSession::onZRTPEvent(zrtp_event_t event, zrtp_stream_ctx_t *stream_ctx) {
1339 1320
     case ZRTP_EVENT_WRONG_MESSAGE_HMAC:
1340 1321
       INFO("ZRTP_EVENT_WRONG_MESSAGE_HMAC\n");
1341 1322
       break;
1323
+
1324
+    case ZRTP_EVENT_IS_PASSIVE_RESTRICTION:
1325
+      INFO("ZRTP_EVENT_IS_PASSIVE_RESTRICTION\n");
1326
+      break;
1342 1327
      
1343 1328
     default: 
1344 1329
       INFO("unknown ZRTP_EVENT\n");
1345 1330
       break;
1346 1331
     } // end events case
1347 1332
 }
1333
+
1334
+void AmSession::onZRTPSecurityEvent(zrtp_security_event_t event, zrtp_stream_t *stream_ctx) {
1335
+  DBG("AmSession::onZRTPSecurityEvent\n");
1336
+
1337
+}
1348 1338
  
1349 1339
 #endif
1350 1340
 
... ...
@@ -41,9 +41,7 @@
41 41
 #include "AmSessionEventHandler.h"
42 42
 #include "AmMediaProcessor.h"
43 43
 
44
-#ifdef WITH_ZRTP
45
-#include "zrtp/zrtp.h"
46
-#endif
44
+#include "AmZRTP.h"
47 45
 
48 46
 #include <string>
49 47
 #include <vector>
... ...
@@ -187,11 +185,11 @@ public:
187 185
   bool hasRtpStream() { return _rtp_str.get() != NULL; }
188 186
 
189 187
 #ifdef WITH_ZRTP
190
-  zrtp_conn_ctx_t*    zrtp_session; // ZRTP session
191
-  zrtp_stream_ctx_t*  zrtp_audio;   // ZRTP stream for audio
188
+  AmZRTPSessionState zrtp_session_state;
192 189
 
193 190
   /** must be set before session is started! i.e. in constructor */
194 191
   bool enable_zrtp;
192
+
195 193
 #endif
196 194
 
197 195
   AmSipDialog* dlg;
... ...
@@ -541,7 +539,8 @@ public:
541 539
   /**
542 540
    * ZRTP events @see ZRTP
543 541
    */
544
-  virtual void onZRTPEvent(zrtp_event_t event, zrtp_stream_ctx_t *stream_ctx);
542
+  virtual void onZRTPProtocolEvent(zrtp_protocol_event_t event, zrtp_stream_t *stream_ctx);
543
+  virtual void onZRTPSecurityEvent(zrtp_security_event_t event, zrtp_stream_t *stream_ctx);
545 544
 #endif
546 545
 
547 546
   /** This callback is called if RTP timeout encountered */
... ...
@@ -38,126 +38,266 @@
38 38
 #define ZRTP_CACHE_SAVE_INTERVAL 1
39 39
 
40 40
 string AmZRTP::cache_path = "zrtp_cache.dat";
41
+#define SEMS_CLIENT_ID "SEMS"
42
+
41 43
 int AmZRTP::zrtp_cache_save_cntr = 0;
42 44
 AmMutex AmZRTP::zrtp_cache_mut;
43 45
 
44
-zrtp_global_ctx_t AmZRTP::zrtp_global;      // persistent storage for libzrtp data
46
+zrtp_global_t* AmZRTP::zrtp_global;      // persistent storage for libzrtp data
47
+zrtp_config_t AmZRTP::zrtp_config;
45 48
 zrtp_zid_t AmZRTP::zrtp_instance_zid = {"defaultsems"}; // todo: generate one
46 49
 
50
+void zrtp_log(int level, char *data, int len, int offset) {
51
+  int sems_lvl = L_DBG;
52
+  if (level==2)
53
+    sems_lvl = L_WARN; // ??
54
+  else if (level==1)
55
+    sems_lvl = L_INFO; // ??
56
+  
57
+  _LOG(sems_lvl, "%.*s", len, data);
58
+}
59
+
47 60
 int AmZRTP::init() {
61
+  zrtp_log_set_log_engine(zrtp_log);
62
+
48 63
   AmConfigReader cfg;
49 64
   string cfgname=add2path(AmConfig::ModConfigPath, 1,  "zrtp.conf");
50 65
   if(cfg.loadFile(cfgname)) {
51
-    ERROR("No %s config file present.\n", 
52
-	  cfgname.c_str());
66
+    ERROR("No %s config file present.\n", cfgname.c_str());
53 67
     return -1;
54 68
   }
69
+
55 70
   cache_path = cfg.getParameter("cache_path");
56 71
   string zid = cfg.getParameter("zid");
57 72
   if (zid.length() != sizeof(zrtp_zid_t)) {
58 73
     ERROR("ZID of this instance MUST be set for ZRTP.\n");
59
-    ERROR("ZID needs to be %u characters long.\n", 
74
+    ERROR("ZID needs to be %lu characters long.\n", 
60 75
 	  sizeof(zrtp_zid_t));
61 76
     return -1;
62 77
   }
63
-  for (int i=0;i<12;i++)
78
+
79
+  for (size_t i=0;i<zid.length();i++)
64 80
     zrtp_instance_zid[i]=zid[i];
65 81
 
66 82
   DBG("initializing ZRTP library with ZID '%s', cache path '%s'.\n",
67 83
       zid.c_str(), cache_path.c_str());
68
-  if ( zrtp_status_ok != zrtp_init(&zrtp_global, "zrtp_sems") ) {
69
-    ERROR("Some error during zrtp initialization\n");
84
+
85
+  zrtp_config_defaults(&zrtp_config);
86
+
87
+  strcpy(zrtp_config.client_id, SEMS_CLIENT_ID);
88
+  zrtp_config.lic_mode = ZRTP_LICENSE_MODE_UNLIMITED;
89
+
90
+  
91
+  strncpy(zrtp_config.cache_file_cfg.cache_path, cache_path.c_str(), 256);
92
+
93
+  zrtp_config.cb.misc_cb.on_send_packet           = AmZRTP::on_send_packet;
94
+  zrtp_config.cb.event_cb.on_zrtp_secure          = AmZRTP::on_zrtp_secure;
95
+  zrtp_config.cb.event_cb.on_zrtp_security_event  = AmZRTP::on_zrtp_security_event;
96
+  zrtp_config.cb.event_cb.on_zrtp_protocol_event  = AmZRTP::on_zrtp_protocol_event;
97
+
98
+  if ( zrtp_status_ok != zrtp_init(&zrtp_config, &zrtp_global) ) {
99
+    ERROR("Error during ZRTP initialization\n");
70 100
     return -1;
71 101
   }
72
-  zrtp_add_entropy(&zrtp_global, NULL, 0);
73
-  DBG("ZRTP initialized ok.\n");
74 102
 
75
-  return 0;
76
-}
103
+  size_t rand_bytes = cfg.getParameterInt("random_entropy_bytes", 172);
104
+  if (rand_bytes) {
105
+    INFO("adding %zd bytes entropy from /dev/random to ZRTP entropy pool\n", rand_bytes);
106
+    FILE* fd = fopen("/dev/random", "r");
107
+    if (!fd) {
108
+      ERROR("opening /dev/random for adding entropy to the pool\n");
109
+      return -1;
110
+    }
111
+    void* p = malloc(rand_bytes);
112
+    if (p==NULL)
113
+      return -1;
77 114
 
78
-void AmZRTP::freeSession(zrtp_conn_ctx_t* zrtp_session) {
79
-  zrtp_done_session_ctx(zrtp_session);
80
-  free(zrtp_session);
81
-  // save zrtp cache
82
-  zrtp_cache_mut.lock();
83
-  if (!((++zrtp_cache_save_cntr) % ZRTP_CACHE_SAVE_INTERVAL)) {
84
-    if (zrtp_cache_user_down() != zrtp_status_ok) {
85
-      ERROR("while writing ZRTP cache.\n");
115
+    size_t read_bytes = fread(p, 1, rand_bytes, fd);
116
+    if (read_bytes != rand_bytes) {
117
+      ERROR("reading %zd bytes from /dev/random\n", rand_bytes);
118
+      return -1;
86 119
     }
120
+    zrtp_entropy_add(zrtp_global, (const unsigned char*)p, read_bytes);
121
+    free(p);
87 122
   }
88
-  zrtp_cache_mut.unlock();
123
+
124
+
125
+  // zrtp_add_entropy(zrtp_global, NULL, 0); // fixme
126
+  DBG("ZRTP initialized ok.\n");
127
+
128
+  return 0;
89 129
 }
90 130
 
91
-void zrtp_get_cache_path(char *path, uint32_t length) {
92
-  snprintf(path, length, "%s", AmZRTP::cache_path.c_str());
131
+AmZRTPSessionState::AmZRTPSessionState()
132
+  : zrtp_session(NULL), zrtp_audio(NULL)
133
+{
134
+  // copy default profile
135
+  zrtp_profile_defaults(&zrtp_profile, AmZRTP::zrtp_global);
93 136
 }
94 137
 
138
+int AmZRTPSessionState::initSession(AmSession* session) {
95 139
 
96
-// void zrtp_get_cache_path(char *path, uint32_t length) {
97
-// }
140
+  DBG("starting ZRTP stream...\n");
141
+  //
142
+  // Allocate zrtp session with default parameters
143
+  //
144
+  zrtp_status_t status =
145
+    zrtp_session_init( AmZRTP::zrtp_global,
146
+		       &zrtp_profile,
147
+		       ZRTP_SIGNALING_ROLE_UNKNOWN, // fixme
148
+		       &zrtp_session);
149
+  if (zrtp_status_ok != status) {
150
+    // Check error code and debug logs
151
+    return status;
152
+  }
98 153
 
154
+  // Set call-back pointer to our parent structure
155
+  zrtp_session_set_userdata(zrtp_session, session);
99 156
 
100
-void zrtp_event_callback(zrtp_event_t event, zrtp_stream_ctx_t *stream_ctx)
101
-{
102
-  if (NULL==stream_ctx) {
103
-    ERROR("event received without stream context.\n");
104
-    return;
157
+  // 
158
+  // Attach Audio and Video Streams
159
+  //
160
+  status = zrtp_stream_attach(zrtp_session, &zrtp_audio);
161
+  if (zrtp_status_ok != status) {
162
+    // Check error code and debug logs
163
+    return status;
105 164
   }
165
+  zrtp_stream_set_userdata(zrtp_audio, session);
166
+  return 0;
167
+}
106 168
 
107
-  AmSession* sess = reinterpret_cast<AmSession*>(stream_ctx->stream_usr_data);
108
-  if (NULL==sess) {
109
-    ERROR("event received without session set up.\n");
110
-    return;
169
+int AmZRTPSessionState::startStreams(uint32_t ssrc){
170
+  zrtp_status_t status = zrtp_stream_start(zrtp_audio, ssrc);
171
+  if (zrtp_status_ok != status) {
172
+    ERROR("starting ZRTP stream\n");
173
+    return -1;
111 174
   }
175
+  return 0;
176
+}
112 177
 
113
-  sess->postEvent(new AmZRTPEvent(event, stream_ctx));
178
+int AmZRTPSessionState::stopStreams(){
179
+  zrtp_status_t status = zrtp_stream_stop(zrtp_audio);
180
+  if (zrtp_status_ok != status) {
181
+    ERROR("stopping ZRTP stream\n");
182
+    return -1;
183
+  }
184
+  return 0;
114 185
 }
115 186
 
116
-void zrtp_play_alert(zrtp_stream_ctx_t* ctx) {
117
-  INFO("zrtp_play_alert: ALERT!\n");
118
-  ctx->need_play_alert = zrtp_play_no;
187
+void AmZRTPSessionState::freeSession() {
188
+  if (NULL == zrtp_session)
189
+    return;
190
+
191
+  zrtp_session_down(zrtp_session);
192
+
193
+  // // save zrtp cache
194
+  // zrtp_cache_mut.lock();
195
+  // if (!((++zrtp_cache_save_cntr) % ZRTP_CACHE_SAVE_INTERVAL)) {
196
+  //   if (zrtp_cache_user_down() != zrtp_status_ok) {
197
+  //     ERROR("while writing ZRTP cache.\n");
198
+  //   }
199
+  // }
200
+  // zrtp_cache_mut.unlock();
119 201
 }
120 202
 
121
-int zrtp_send_rtp( const zrtp_stream_ctx_t* stream_ctx,
122
-		   char* packet, unsigned int length) {
123
-  if (NULL==stream_ctx) {
124
-    ERROR("trying to send packet without stream context.\n");
203
+AmZRTPSessionState::~AmZRTPSessionState() {
204
+
205
+}
206
+
207
+// void zrtp_get_cache_path(char *path, uint32_t length) {
208
+// }
209
+
210
+int AmZRTP::on_send_packet(const zrtp_stream_t *stream, char *packet, unsigned int length) {
211
+  DBG("on_send_packet(stream [%p], len=%u)\n", stream, length);
212
+  if (NULL==stream) {
213
+    ERROR("on_send_packet without stream context.\n");
125 214
     return -1;
126 215
   }
127 216
 
128
-  AmSession* sess = reinterpret_cast<AmSession*>(stream_ctx->stream_usr_data);
129
-  if (NULL==sess) {
130
-    ERROR("trying to send packet without session set up.\n");
217
+  void* udata = zrtp_stream_get_userdata(stream);
218
+  if (NULL == udata) {
219
+    ERROR("ZRTP on_send_packet without session context.\n");
131 220
     return -1;
132 221
   }
222
+  AmSession* sess = reinterpret_cast<AmSession*>(udata);
133 223
 
134
-  return sess->rtp_str.send_raw(packet, length);  
224
+  return sess->RTPStream()->send_raw(packet, length);
135 225
 }
136 226
 
227
+void AmZRTP::on_zrtp_secure(zrtp_stream_t *stream) {
228
+  DBG("on_zrtp_secure(stream [%p])\n", stream);
137 229
 
138
-#define BUFFER_LOG_SIZE 256
139
-void zrtp_print_log(log_level_t level, const char* format, ...)
140
-{
141
-	char buffer[BUFFER_LOG_SIZE];
142
-    va_list arg;
143
-
144
-    va_start(arg, format);
145
-    vsnprintf(buffer, BUFFER_LOG_SIZE, format, arg);
146
-    va_end( arg );
147
-    int sems_lvl = L_ERR;
148
-    switch(level) {
149
-    case ZRTP_LOG_DEBUG:   sems_lvl = L_DBG; break;
150
-    case ZRTP_LOG_INFO:    sems_lvl = L_INFO; break;
151
-    case ZRTP_LOG_WARNING: sems_lvl = L_WARN; break;
152
-    case ZRTP_LOG_ERROR:   sems_lvl = L_ERR; break;
153
-    case ZRTP_LOG_FATAL:   sems_lvl = L_ERR; break;
154
-    case ZRTP_LOG_ALL:   sems_lvl = L_ERR; break;
155
-    }
156
-    _LOG(sems_lvl, "*** %s", buffer);
230
+  // if (NULL==stream) {
231
+  //   ERROR("event received without stream context.\n");
232
+  //   return;
233
+  // }
234
+
235
+  // void* udata = zrtp_stream_get_userdata(stream);
236
+  // if (NULL == udata) {
237
+  //   ERROR("ZRTP on_send_packet without session set context.\n");
238
+  //   return;
239
+  // }
240
+  // AmSession* sess = reinterpret_cast<AmSession*>(udata);
241
+
242
+  // sess->onZrtpSecure();
157 243
 }
158 244
 
245
+void AmZRTP::on_zrtp_security_event(zrtp_stream_t *stream, zrtp_security_event_t event) {
246
+  DBG("on_zrtp_security_event(stream [%p])\n", stream);
247
+  if (NULL==stream) {
248
+    ERROR("event received without stream context.\n");
249
+    return;
250
+  }
251
+  void* udata = zrtp_stream_get_userdata(stream);
252
+  if (NULL == udata) {
253
+    ERROR("ZRTP on_send_packet without session set context.\n");
254
+    return;
255
+  }
256
+  AmSession* sess = reinterpret_cast<AmSession*>(udata);
257
+  sess->postEvent(new AmZRTPSecurityEvent(event, stream));
258
+}
159 259
 
160
-#endif
260
+void AmZRTP::on_zrtp_protocol_event(zrtp_stream_t *stream, zrtp_protocol_event_t event) {
261
+  DBG("on_zrtp_protocol_event(stream [%p])\n", stream);
262
+  if (NULL==stream) {
263
+    ERROR("event received without stream context.\n");
264
+    return;
265
+  }
266
+  void* udata = zrtp_stream_get_userdata(stream);
267
+  if (NULL == udata) {
268
+    ERROR("ZRTP on_send_packet without session set context.\n");
269
+    return;
270
+  }
271
+  AmSession* sess = reinterpret_cast<AmSession*>(udata);
272
+  sess->postEvent(new AmZRTPProtocolEvent(event, stream));
273
+}
274
+
275
+/*
276
+void zrtp_play_alert(zrtp_stream_t* ctx) {
277
+  INFO("zrtp_play_alert: ALERT!\n");
278
+  ctx->need_play_alert = zrtp_play_no;
279
+}
280
+*/
161 281
 
282
+// #define BUFFER_LOG_SIZE 256
283
+// void zrtp_print_log(log_level_t level, const char* format, ...)
284
+// {
285
+// 	char buffer[BUFFER_LOG_SIZE];
286
+//     va_list arg;
162 287
 
288
+//     va_start(arg, format);
289
+//     vsnprintf(buffer, BUFFER_LOG_SIZE, format, arg);
290
+//     va_end( arg );
291
+//     int sems_lvl = L_ERR;
292
+//     switch(level) {
293
+//     case ZRTP_LOG_DEBUG:   sems_lvl = L_DBG; break;
294
+//     case ZRTP_LOG_INFO:    sems_lvl = L_INFO; break;
295
+//     case ZRTP_LOG_WARNING: sems_lvl = L_WARN; break;
296
+//     case ZRTP_LOG_ERROR:   sems_lvl = L_ERR; break;
297
+//     case ZRTP_LOG_FATAL:   sems_lvl = L_ERR; break;
298
+//     case ZRTP_LOG_ALL:   sems_lvl = L_ERR; break;
299
+//     }
300
+//     _LOG(sems_lvl, "*** %s", buffer);
301
+// }
163 302
 
303
+#endif
... ...
@@ -30,33 +30,63 @@
30 30
 
31 31
 #ifdef WITH_ZRTP
32 32
 
33
-#include "zrtp/zrtp.h"
33
+#include "libzrtp/zrtp.h"
34 34
 #include "AmThread.h"
35 35
 #include "AmEvent.h"
36 36
 
37 37
 #include <string>
38 38
 
39
-#include "zrtp/zrtp.h"
40
-extern zrtp_global_ctx_t zrtp_global;      // persistent storage for libzrtp data
39
+class AmSession;
40
+
41
+extern zrtp_global_t zrtp_global;      // persistent storage for libzrtp data
41 42
 extern zrtp_zid_t zrtp_instance_zid;
42 43
 
43
-struct AmZRTPEvent : public AmEvent {
44
-  zrtp_stream_ctx_t* stream_ctx;
45
- AmZRTPEvent(int event_id, zrtp_stream_ctx_t* stream_ctx)
44
+struct AmZRTPSecurityEvent : public AmEvent {
45
+  zrtp_stream_t* stream_ctx;
46
+ AmZRTPSecurityEvent(int event_id, zrtp_stream_t* stream_ctx)
47
+   : AmEvent(event_id), stream_ctx(stream_ctx) { }
48
+  ~AmZRTPSecurityEvent() { }
49
+};
50
+
51
+struct AmZRTPProtocolEvent : public AmEvent {
52
+  zrtp_stream_t* stream_ctx;
53
+ AmZRTPProtocolEvent(int event_id, zrtp_stream_t* stream_ctx)
46 54
    : AmEvent(event_id), stream_ctx(stream_ctx) { }
47
-  ~AmZRTPEvent() { }
55
+  ~AmZRTPProtocolEvent() { }
48 56
 };
49 57
 
58
+
50 59
 struct AmZRTP { 
51
-   static int zrtp_cache_save_cntr;
52
-   static std::string cache_path;
53
-   static AmMutex zrtp_cache_mut;
54
-   static int init();
55
-   static zrtp_global_ctx_t zrtp_global;    
56
-   static zrtp_zid_t zrtp_instance_zid;
57
-   static void freeSession(zrtp_conn_ctx_t* zrtp_session);
60
+  static int zrtp_cache_save_cntr;
61
+  static std::string cache_path;
62
+  static AmMutex zrtp_cache_mut;
63
+  static int init();
64
+  static zrtp_global_t* zrtp_global;
65
+  static zrtp_config_t zrtp_config;
66
+  static zrtp_zid_t zrtp_instance_zid;
67
+
68
+  static int on_send_packet(const zrtp_stream_t *stream, char *packet, unsigned int length);
69
+  static void on_zrtp_secure(zrtp_stream_t *stream);
70
+  static void on_zrtp_security_event(zrtp_stream_t *stream, zrtp_security_event_t event);
71
+  static void on_zrtp_protocol_event(zrtp_stream_t *stream, zrtp_protocol_event_t event);
58 72
 }; 
59 73
 
74
+struct AmZRTPSessionState {
75
+
76
+  AmZRTPSessionState();
77
+  ~AmZRTPSessionState();
78
+
79
+  int initSession(AmSession* s);
80
+  void freeSession();
81
+
82
+  int startStreams(uint32_t ssrc);
83
+  int stopStreams();
84
+
85
+  zrtp_profile_t  zrtp_profile;
86
+  zrtp_session_t* zrtp_session; // ZRTP session
87
+  zrtp_stream_t*  zrtp_audio;   // ZRTP stream for audio
88
+};
89
+ 
60 90
 #if defined(__cplusplus)
61 91
 extern "C" {
62 92
 #endif
... ...
@@ -375,6 +375,11 @@ loglevel=2
375 375
 # Example:
376 376
 #  cps_limit="100;503;Server overload"
377 377
 
378
+###########################################################
379
+# if build with ZRTP support (see Makefile.defs)
380
+# enable ZRTP support in endpoint calls:
381
+#enable_zrtp=yes (default: yes)
382
+
378 383
 ############################################################
379 384
 # tuning
380 385
 
... ...
@@ -7,3 +7,13 @@ cache_path=zrtp_cache.dat
7 7
 # ZID - must be set to a unique identifier on installation.
8 8
 #
9 9
 #zid=012345678901
10
+
11
+# random_entropy_bytes - bytes to read from /dev/random to zrtp entropy pool
12
+# Warning: can stall the startup process if there's many bytes read. 
13
+# If there's too little, packets might get dropped. 
14
+#
15
+# default: 172
16
+#
17
+#random_entropy_bytes=512
18
+#
19
+#random_entropy_bytes=0 # disable
... ...
@@ -1,3 +1,4 @@
1
+   SEMS
1 2
      * [1]Main Page
2 3
      * [2]Related Pages
3 4
      * [3]Namespaces
... ...
@@ -6,50 +7,45 @@
6 7
      * [6]Directories
7 8
      * [7]Examples
8 9
 
9
-ZRTP encryption
10
+   ZRTP encryption
10 11
 
11 12
 Introduction
12 13
 
13 14
    ZRTP is a key agreement protocol to negotiate the keys for encryption
14
-   of RTP in phone calls. It is a proposed public standard: [8]ZRTP: Media
15
-   Path Key Agreement for Secure RTP.
15
+   of RTP in phone calls. It is an [8]informational RFC - RFC6189 ZRTP:
16
+   Media Path Key Agreement for Unicast Secure RTP.
16 17
 
17 18
    Even though it uses public key encryption, a PKI is not needed. Since
18 19
    the keys are negotiated in the media path, support for it in signaling
19 20
    is not necessary. ZRTP also offers opportunistic encryption, which
20 21
    means that calls between UAs that support it are encrypted, but calls
21 22
    to UAs not supporting it are still possible, but unencrypted. The
22
-   actual RTP encryption is done with [9]SRTP. For more information about
23
-   ZRTP, see the [10]Zfone project, the [11]draft and the [12]wikipedia
24
-   article.
23
+   actual RTP encryption is done with [9]SRTP.
24
+
25
+   ZRTP is one of the widest (if not the widest) supported end-to-end
26
+   encryption methods for VoIP. Popular SIP clients that support ZRTP are
27
+   [10]Jitsi, CSipSimple, Twinkle, Linphone.
28
+
29
+   For more information about ZRTP, see the [11]Zfone project, the [12]RFC
30
+   and the [13]wikipedia article.
25 31
 
26 32
 ZRTP in SEMS
27 33
 
28
-   Since the version 1.0 SEMS supports ZRTP with the use of the [13]Zfone
34
+   Since the version 1.0 SEMS supports ZRTP with the use of the [14]Zfone
29 35
    SDK.
30 36
 
37
+   Currrently, the newest version of the ZRTP SDK, and the one that works
38
+   with SEMS, is available at [15]https://github.com/traviscross/libzrtp.
39
+
31 40
    To build SEMS with ZRTP support, install the SDK and set WITH_ZRTP=yes
32 41
    in Makefile.defs, or build with
33 42
  $ make WITH_ZRTP=yes
34 43
 
35 44
    The conference application is enabled to tell the caller the SAS phrase
36 45
    if it is compiled with WITH_SAS_TTS option, set in
37
-   apps/conference/Makefile. For this to work, the [14]flite
46
+   apps/conference/Makefile. For this to work, the [16]flite
38 47
    text-to-speech synthesizer version 1.2 or 1.3 is needed.
39 48
 
40
-Online demo
41
-
42
-   Call
43
-sip:[15]secureconference@iptel.org
44
-
45
-   or
46
-sip:[16]zrtp@iptel.org
47
-
48
-   for a test drive of ZRTP conferencing. If you call that number with a
49
-   ZRTP enabled phone, you should be told the SAS string that is also
50
-   displayed in your phone. Press two times the hash (##) while in the
51
-   call to read out the SAS string again.
52
-
53 49
 How to use ZRTP in your application
54 50
 
55 51
    Have a look at the conference application on how to add ZRTP support in
... ...
@@ -76,32 +72,34 @@ Phones with ZRTP
76 72
 
77 73
      * [17]Zfone turns every softphone into a secure phone by tapping into
78 74
        the RTP sent and received
79
-     * [18]Twinkle is a very good free softphone for Linux. It can speak
80
-       ZRTP with the use of GNU [19]libzrtpcpp.
75
+     * [18]Jitsi
76
+     * [19]Twinkle is a very good free softphone for Linux. It can speak
77
+       ZRTP with the use of GNU [20]libzrtpcpp.
81 78
      __________________________________________________________________
82 79
 
83 80
 
84
-    Generated on Thu Feb 3 02:29:25 2011 for SEMS by  [20]doxygen 1.6.1
81
+    Generated by  [21] doxygen 1.7.6.1
85 82
 
86 83
 References
87 84
 
88
-   1. file://localhost/home/stefan/devel/sems/sems/doc/doxygen_doc/html/index.html
89
-   2. file://localhost/home/stefan/devel/sems/sems/doc/doxygen_doc/html/pages.html
90
-   3. file://localhost/home/stefan/devel/sems/sems/doc/doxygen_doc/html/namespaces.html
91
-   4. file://localhost/home/stefan/devel/sems/sems/doc/doxygen_doc/html/annotated.html
92
-   5. file://localhost/home/stefan/devel/sems/sems/doc/doxygen_doc/html/files.html
93
-   6. file://localhost/home/stefan/devel/sems/sems/doc/doxygen_doc/html/dirs.html
94
-   7. file://localhost/home/stefan/devel/sems/sems/doc/doxygen_doc/html/examples.html
95
-   8. http://tools.ietf.org/html/draft-zimmermann-avt-zrtp
85
+   1. file://localhost/home/stefan_plain/devel/sems/zrtp/sems/doc/doxygen_doc/html/index.html
86
+   2. file://localhost/home/stefan_plain/devel/sems/zrtp/sems/doc/doxygen_doc/html/pages.html
87
+   3. file://localhost/home/stefan_plain/devel/sems/zrtp/sems/doc/doxygen_doc/html/namespaces.html
88
+   4. file://localhost/home/stefan_plain/devel/sems/zrtp/sems/doc/doxygen_doc/html/annotated.html
89
+   5. file://localhost/home/stefan_plain/devel/sems/zrtp/sems/doc/doxygen_doc/html/files.html
90
+   6. file://localhost/home/stefan_plain/devel/sems/zrtp/sems/doc/doxygen_doc/html/dirs.html
91
+   7. file://localhost/home/stefan_plain/devel/sems/zrtp/sems/doc/doxygen_doc/html/examples.html
92
+   8. http://tools.ietf.org/html/rfc6189
96 93
    9. http://www.ietf.org/rfc/rfc3711.txt
97
-  10. http://zfoneproject.com/
98
-  11. http://tools.ietf.org/html/draft-zimmermann-avt-zrtp
99
-  12. http://en.wikipedia.org/wiki/ZRTP
100
-  13. http://zfoneproject.com/prod_sdk.html
101
-  14. http://cmuflite.org/
102
-  15. mailto:secureconference@iptel.org
103
-  16. mailto:zrtp@iptel.org
94
+  10. http://www.jitsi.org/
95
+  11. http://zfoneproject.com/
96
+  12. http://tools.ietf.org/html/rfc6189
97
+  13. http://en.wikipedia.org/wiki/ZRTP
98
+  14. http://zfoneproject.com/prod_sdk.html
99
+  15. https://github.com/traviscross/libzrtp
100
+  16. http://cmuflite.org/
104 101
   17. http://zfoneproject.com/
105
-  18. http://twinklephone.com/
106
-  19. http://www.gnutelephony.org/index.php/GNU_ZRTP
107
-  20. http://www.doxygen.org/index.html
102
+  18. http://www.jitsi.org/
103
+  19. http://twinklephone.com/
104
+  20. http://www.gnutelephony.org/index.php/GNU_ZRTP
105
+  21. http://www.doxygen.org/index.html
... ...
@@ -8,16 +8,19 @@
8 8
  *  \section intro Introduction
9 9
  * 
10 10
  *  <p>ZRTP is a key agreement protocol to negotiate the keys for encryption of RTP in phone calls.
11
- *  It is a proposed public standard: <a href="http://tools.ietf.org/html/draft-zimmermann-avt-zrtp"> 
12
- *  ZRTP: Media Path Key Agreement for Secure RTP</a>.</p>
11
+ *  It is an <a href="http://tools.ietf.org/html/rfc6189">informational RFC - RFC6189 ZRTP: Media Path Key Agreement for Unicast Secure RTP</a>.</p>
13 12
  *  <p>Even though it uses public key encryption, a PKI is not needed. Since the keys are negotiated 
14 13
  *  in the media path, support for it in signaling is not necessary. ZRTP also offers opportunistic 
15 14
  *  encryption, which means that calls between UAs that support it are encrypted, but calls to UAs 
16 15
  *  not supporting it are still possible, but unencrypted. The actual RTP encryption is done with 
17 16
  *  <a href="http://www.ietf.org/rfc/rfc3711.txt">SRTP</a>.
18
- *  For more information about ZRTP, see the 
17
+ *
18
+ *  <p>ZRTP is one of the widest (if not the widest) supported end-to-end encryption methods for VoIP. 
19
+ *  Popular SIP clients that support ZRTP are <a href="http://www.jitsi.org">Jitsi</a>, CSipSimple, Twinkle, Linphone.</p>
20
+ * 
21
+ *  <p>For more information about ZRTP, see the 
19 22
  *  <a href="http://zfoneproject.com/">Zfone project</a>, the 
20
- *  <a href="http://tools.ietf.org/html/draft-zimmermann-avt-zrtp">draft</a> and the 
23
+ *  <a href="http://tools.ietf.org/html/rfc6189">RFC</a> and the 
21 24
  *  <a href="http://en.wikipedia.org/wiki/ZRTP">wikipedia article</a>.</p>
22 25
  * 
23 26
  *  \section zinsems ZRTP in SEMS
... ...
@@ -25,6 +28,9 @@
25 28
  *  <p>Since the version 1.0 SEMS supports ZRTP with the use of the
26 29
  *   <a href="http://zfoneproject.com/prod_sdk.html"> Zfone SDK</a>.</p>
27 30
  *  
31
+ * <p>Currrently, the newest version of the ZRTP SDK, and the one that works with SEMS, is available at 
32
+ * <a href="https://github.com/traviscross/libzrtp">https://github.com/traviscross/libzrtp</a>.
33
+ *
28 34
  *  <p>To build SEMS with ZRTP support, install the SDK and set WITH_ZRTP=yes in Makefile.defs,
29 35
  *  or build with <br>
30 36
  *   <pre> $ make WITH_ZRTP=yes</pre>
... ...
@@ -34,13 +40,6 @@
34 40
  *  if it is compiled with WITH_SAS_TTS option, set in apps/conference/Makefile. For this to work,
35 41
  *  the <a href="http://cmuflite.org">flite text-to-speech synthesizer</a> version 1.2 or 1.3 is needed.</p>
36 42
  *  
37
- *  \section onlinedemo Online demo
38
- * 
39
- *  <p>Call <pre>sip:secureconference@iptel.org</pre> or <pre>sip:zrtp@iptel.org</pre> for a test drive 
40
- *  of ZRTP conferencing. If you call that number with a ZRTP enabled phone, you should be told the SAS string
41
- *  that is also displayed in your phone. Press two times the hash (##) while in the call to read out the 
42
- *  SAS string again.</p>
43
- *
44 43
  *  \section zinyourapp How to use ZRTP in your application 
45 44
  *
46 45
  *  Have a look at the conference application on how to add ZRTP support in your application. There is a 
... ...
@@ -63,6 +62,7 @@
63 62
  *  
64 63
  *   - <a href="http://zfoneproject.com/">Zfone</a> turns every softphone into a secure phone 
65 64
  *      by tapping into the RTP sent and received</li></ul>
65
+ *   - <a href="http://www.jitsi.org">Jitsi</a>
66 66
  *   - <a href="http://twinklephone.com/">Twinkle</a> is a very good free softphone for Linux. 
67 67
  *     It can speak ZRTP with the use of  GNU 
68 68
  *     <a href="http://www.gnutelephony.org/index.php/GNU_ZRTP">libzrtpcpp</a>.