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 588
     }
589 589
   }
590 590
 
591
+#ifdef WITH_ZRTP
592
+  enable_zrtp = cfg.getParameter("enable_zrtp", "yes") == "yes";
593
+  INFO("ZRTP %sabled\n", enable_zrtp ? "en":"dis");
594
+#endif
595
+
591 596
   if(cfg.hasParameter("session_limit")){ 
592 597
     vector<string> limit = explode(cfg.getParameter("session_limit"), ";");
593 598
     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 749
   last_payload = payload;
750 750
 
751 751
 #ifdef WITH_ZRTP  
752
-  if( session->zrtp_audio  ) {
753
-    DBG("now starting zrtp stream...\n");
754
-    zrtp_start_stream( session->zrtp_audio );
752
+  if (session && session->enable_zrtp) {
753
+    if (session->zrtp_session_state.initSession(session))
754
+      return -1;
755
+
756
+    session->zrtp_session_state.startStreams(get_ssrc());
755 757
   }
756 758
 #endif
757 759
 
... ...
@@ -818,7 +825,7 @@ void AmRtpStream::bufferPacket(AmRtpPacket* p)
818 818
     return;
819 819
   }
820 820
 
821
-  if (relay_enabled) {
821
+  if (relay_enabled) { // todo: ZRTP
822 822
     if (force_receive_dtmf) {
823 823
       recvDtmfPacket(p);
824 824
     }
... ...
@@ -858,35 +865,37 @@ void AmRtpStream::bufferPacket(AmRtpPacket* p)
858 858
 #endif
859 859
 
860 860
   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 861
 
869 862
 #ifdef WITH_ZRTP
870
-  if (session->zrtp_audio) {
863
+  if (session && session->enable_zrtp) {
871 864
 
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);
865
+    if (NULL == session->zrtp_session_state.zrtp_audio) {
866
+      WARN("dropping received packet, as there's no ZRTP stream initialized\n");
867
+      receive_mut.unlock();
868
+      mem.freePacket(p);
869
+      return;      
870
+    }
871
+ 
872
+    unsigned int size = p->getBufferSize();    
873
+    zrtp_status_t status = zrtp_process_srtp(session->zrtp_session_state.zrtp_audio, (char*)p->getBuffer(), &size);
876 874
     switch (status)
877 875
       {
878
-      case zrtp_status_forward:
879 876
       case zrtp_status_ok: {
880 877
 	p->setBufferSize(size);
881 878
 	if (p->parse() < 0) {
882 879
 	  ERROR("parsing decoded packet!\n");
883 880
 	  mem.freePacket(p);
884 881
 	} else {
882
+
885 883
           if(p->payload == getLocalTelephoneEventPT()) {
886 884
             rtp_ev_qu.push(p);
887 885
           } else {
888
-	    receive_buf[p->timestamp] = p;
886
+	    if(!receive_buf.insert(ReceiveBuffer::value_type(p->timestamp,p)).second) {
887
+	      // insert failed
888
+	      mem.freePacket(p);
889
+	    }
889 890
           }
891
+
890 892
 	}
891 893
       }	break;
892 894
 
... ...
@@ -1033,9 +1042,14 @@ void AmRtpStream::recvPacket(int fd)
1033 1033
 
1034 1034
     gettimeofday(&p->recv_time,NULL);
1035 1035
     
1036
-    if(!relay_raw)
1036
+    if(!relay_raw
1037
+#ifdef WITH_ZRTP
1038
+       && !(session && session->enable_zrtp)
1039
+#endif
1040
+       ) {
1037 1041
       parse_res = p->parse();
1038
- 
1042
+    }
1043
+
1039 1044
     if (parse_res == -1) {
1040 1045
       DBG("error while parsing RTP packet.\n");
1041 1046
       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 280
 bool AmSession::startup() {
281 281
 #ifdef WITH_ZRTP
282 282
   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
-      }
283
+    if (zrtp_session_state.initSession(this))
284
+      return -1;
308 285
       
309 286
       DBG("initialized ZRTP session context OK\n");
310
-    }
311 287
   }
312 288
 #endif
313 289
 
... ...
@@ -694,9 +671,15 @@ void AmSession::process(AmEvent* ev)
694 694
   }
695 695
 
696 696
 #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);
697
+  AmZRTPProtocolEvent* zrtp_p_ev = dynamic_cast<AmZRTPProtocolEvent*>(ev);
698
+  if(zrtp_p_ev){
699
+    onZRTPProtocolEvent((zrtp_protocol_event_t)zrtp_p_ev->event_id, zrtp_p_ev->stream_ctx);
700
+    return;
701
+  }
702
+
703
+  AmZRTPSecurityEvent* zrtp_s_ev = dynamic_cast<AmZRTPSecurityEvent*>(ev);
704
+  if(zrtp_s_ev){
705
+    onZRTPSecurityEvent((zrtp_security_event_t)zrtp_s_ev->event_id, zrtp_s_ev->stream_ctx);
700 706
     return;
701 707
   }
702 708
 #endif
... ...
@@ -1261,50 +1244,48 @@ bool AmSession::removeTimers() {
1261 1261
 
1262 1262
  
1263 1263
 #ifdef WITH_ZRTP
1264
-void AmSession::onZRTPEvent(zrtp_event_t event, zrtp_stream_ctx_t *stream_ctx) {
1265
-  DBG("AmSession::onZRTPEvent \n");
1264
+
1265
+void AmSession::onZRTPProtocolEvent(zrtp_protocol_event_t event, zrtp_stream_t *stream_ctx) {
1266
+  DBG("AmSession::onZRTPProtocolEvent\n");
1266 1267
   switch (event)
1267 1268
     {
1269
+    case ZRTP_EVENT_UNSUPPORTED:
1270
+      INFO("ZRTP_EVENT_UNSUPPORTED\n");
1271
+      break;
1272
+    case ZRTP_EVENT_IS_CLEAR:
1273
+      INFO("ZRTP_EVENT_IS_CLEAR\n");
1274
+      break;
1275
+    case ZRTP_EVENT_IS_INITIATINGSECURE:
1276
+      INFO("ZRTP_EVENT_IS_INITIATINGSECURE\n");
1277
+      break;
1278
+    case ZRTP_EVENT_IS_PENDINGSECURE:
1279
+      INFO("ZRTP_EVENT_PENDINGSECURE\n");
1280
+      break;
1281
+
1268 1282
     case ZRTP_EVENT_IS_SECURE: {
1269 1283
       INFO("ZRTP_EVENT_IS_SECURE \n");
1270 1284
       //         info->is_verified  = ctx->_session_ctx->secrets.verifieds & ZRTP_BIT_RS0;
1271 1285
  
1272
-      zrtp_conn_ctx_t *session = stream_ctx->_session_ctx;
1286
+      // zrtp_session_t *session = stream_ctx->_session_ctx;
1273 1287
  
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
-      } 
1288
+      // if (ZRTP_SAS_BASE32 == session->sas_values.rendering) {
1289
+      // 	DBG("Got SAS value <<<%.4s>>>\n", session->sas_values.str1.buffer);
1290
+      // } else {
1291
+      // 	DBG("Got SAS values SAS1 '%s' and SAS2 '%s'\n", 
1292
+      // 	    session->sas_values.str1.buffer,
1293
+      // 	    session->sas_values.str2.buffer);
1294
+      // } 
1281 1295
     } break;
1282 1296
  
1283 1297
     case ZRTP_EVENT_IS_PENDINGCLEAR:
1284 1298
       INFO("ZRTP_EVENT_IS_PENDINGCLEAR\n");
1285 1299
       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");
1300
+      //      zrtp_clear_stream(zrtp_audio);
1301 1301
       break;
1302
+  
1302 1303
     case ZRTP_EVENT_IS_SECURE_DONE:
1303 1304
       INFO("ZRTP_EVENT_IS_SECURE_DONE\n");
1304 1305
       break;
1305
-    case ZRTP_EVENT_ERROR:
1306
-      INFO("ZRTP_EVENT_ERROR\n");
1307
-      break;
1308 1306
     case ZRTP_EVENT_NO_ZRTP:
1309 1307
       INFO("ZRTP_EVENT_NO_ZRTP\n");
1310 1308
       break;
... ...
@@ -1339,12 +1320,21 @@ void AmSession::onZRTPEvent(zrtp_event_t event, zrtp_stream_ctx_t *stream_ctx) {
1339 1339
     case ZRTP_EVENT_WRONG_MESSAGE_HMAC:
1340 1340
       INFO("ZRTP_EVENT_WRONG_MESSAGE_HMAC\n");
1341 1341
       break;
1342
+
1343
+    case ZRTP_EVENT_IS_PASSIVE_RESTRICTION:
1344
+      INFO("ZRTP_EVENT_IS_PASSIVE_RESTRICTION\n");
1345
+      break;
1342 1346
      
1343 1347
     default: 
1344 1348
       INFO("unknown ZRTP_EVENT\n");
1345 1349
       break;
1346 1350
     } // end events case
1347 1351
 }
1352
+
1353
+void AmSession::onZRTPSecurityEvent(zrtp_security_event_t event, zrtp_stream_t *stream_ctx) {
1354
+  DBG("AmSession::onZRTPSecurityEvent\n");
1355
+
1356
+}
1348 1357
  
1349 1358
 #endif
1350 1359
 
... ...
@@ -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 187
   bool hasRtpStream() { return _rtp_str.get() != NULL; }
188 188
 
189 189
 #ifdef WITH_ZRTP
190
-  zrtp_conn_ctx_t*    zrtp_session; // ZRTP session
191
-  zrtp_stream_ctx_t*  zrtp_audio;   // ZRTP stream for audio
190
+  AmZRTPSessionState zrtp_session_state;
192 191
 
193 192
   /** must be set before session is started! i.e. in constructor */
194 193
   bool enable_zrtp;
194
+
195 195
 #endif
196 196
 
197 197
   AmSipDialog* dlg;
... ...
@@ -541,7 +539,8 @@ public:
541 541
   /**
542 542
    * ZRTP events @see ZRTP
543 543
    */
544
-  virtual void onZRTPEvent(zrtp_event_t event, zrtp_stream_ctx_t *stream_ctx);
544
+  virtual void onZRTPProtocolEvent(zrtp_protocol_event_t event, zrtp_stream_t *stream_ctx);
545
+  virtual void onZRTPSecurityEvent(zrtp_security_event_t event, zrtp_stream_t *stream_ctx);
545 546
 #endif
546 547
 
547 548
   /** 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 6
      * [6]Directories
7 7
      * [7]Examples
8 8
 
9
-ZRTP encryption
9
+   ZRTP encryption
10 10
 
11 11
 Introduction
12 12
 
13 13
    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.
14
+   of RTP in phone calls. It is an [8]informational RFC - RFC6189 ZRTP:
15
+   Media Path Key Agreement for Unicast Secure RTP.
16 16
 
17 17
    Even though it uses public key encryption, a PKI is not needed. Since
18 18
    the keys are negotiated in the media path, support for it in signaling
19 19
    is not necessary. ZRTP also offers opportunistic encryption, which
20 20
    means that calls between UAs that support it are encrypted, but calls
21 21
    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.
22
+   actual RTP encryption is done with [9]SRTP.
23
+
24
+   ZRTP is one of the widest (if not the widest) supported end-to-end
25
+   encryption methods for VoIP. Popular SIP clients that support ZRTP are
26
+   [10]Jitsi, CSipSimple, Twinkle, Linphone.
27
+
28
+   For more information about ZRTP, see the [11]Zfone project, the [12]RFC
29
+   and the [13]wikipedia article.
25 30
 
26 31
 ZRTP in SEMS
27 32
 
28
-   Since the version 1.0 SEMS supports ZRTP with the use of the [13]Zfone
33
+   Since the version 1.0 SEMS supports ZRTP with the use of the [14]Zfone
29 34
    SDK.
30 35
 
36
+   Currrently, the newest version of the ZRTP SDK, and the one that works
37
+   with SEMS, is available at [15]https://github.com/traviscross/libzrtp.
38
+
31 39
    To build SEMS with ZRTP support, install the SDK and set WITH_ZRTP=yes
32 40
    in Makefile.defs, or build with
33 41
  $ make WITH_ZRTP=yes
34 42
 
35 43
    The conference application is enabled to tell the caller the SAS phrase
36 44
    if it is compiled with WITH_SAS_TTS option, set in
37
-   apps/conference/Makefile. For this to work, the [14]flite
45
+   apps/conference/Makefile. For this to work, the [16]flite
38 46
    text-to-speech synthesizer version 1.2 or 1.3 is needed.
39 47
 
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 48
 How to use ZRTP in your application
54 49
 
55 50
    Have a look at the conference application on how to add ZRTP support in
... ...
@@ -76,32 +72,34 @@ Phones with ZRTP
76 76
 
77 77
      * [17]Zfone turns every softphone into a secure phone by tapping into
78 78
        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.
79
+     * [18]Jitsi
80
+     * [19]Twinkle is a very good free softphone for Linux. It can speak
81
+       ZRTP with the use of GNU [20]libzrtpcpp.
81 82
      __________________________________________________________________
82 83
 
83 84
 
84
-    Generated on Thu Feb 3 02:29:25 2011 for SEMS by  [20]doxygen 1.6.1
85
+    Generated by  [21] doxygen 1.7.6.1
85 86
 
86 87
 References
87 88
 
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
89
+   1. file://localhost/home/stefan_plain/devel/sems/zrtp/sems/doc/doxygen_doc/html/index.html
90
+   2. file://localhost/home/stefan_plain/devel/sems/zrtp/sems/doc/doxygen_doc/html/pages.html
91
+   3. file://localhost/home/stefan_plain/devel/sems/zrtp/sems/doc/doxygen_doc/html/namespaces.html
92
+   4. file://localhost/home/stefan_plain/devel/sems/zrtp/sems/doc/doxygen_doc/html/annotated.html
93
+   5. file://localhost/home/stefan_plain/devel/sems/zrtp/sems/doc/doxygen_doc/html/files.html
94
+   6. file://localhost/home/stefan_plain/devel/sems/zrtp/sems/doc/doxygen_doc/html/dirs.html
95
+   7. file://localhost/home/stefan_plain/devel/sems/zrtp/sems/doc/doxygen_doc/html/examples.html
96
+   8. http://tools.ietf.org/html/rfc6189
96 97
    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
98
+  10. http://www.jitsi.org/
99
+  11. http://zfoneproject.com/
100
+  12. http://tools.ietf.org/html/rfc6189
101
+  13. http://en.wikipedia.org/wiki/ZRTP
102
+  14. http://zfoneproject.com/prod_sdk.html
103
+  15. https://github.com/traviscross/libzrtp
104
+  16. http://cmuflite.org/
104 105
   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
106
+  18. http://www.jitsi.org/
107
+  19. http://twinklephone.com/
108
+  20. http://www.gnutelephony.org/index.php/GNU_ZRTP
109
+  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 25
  *  <p>Since the version 1.0 SEMS supports ZRTP with the use of the
26 26
  *   <a href="http://zfoneproject.com/prod_sdk.html"> Zfone SDK</a>.</p>
27 27
  *  
28
+ * <p>Currrently, the newest version of the ZRTP SDK, and the one that works with SEMS, is available at 
29
+ * <a href="https://github.com/traviscross/libzrtp">https://github.com/traviscross/libzrtp</a>.
30
+ *
28 31
  *  <p>To build SEMS with ZRTP support, install the SDK and set WITH_ZRTP=yes in Makefile.defs,
29 32
  *  or build with <br>
30 33
  *   <pre> $ make WITH_ZRTP=yes</pre>
... ...
@@ -34,13 +40,6 @@
34 34
  *  if it is compiled with WITH_SAS_TTS option, set in apps/conference/Makefile. For this to work,
35 35
  *  the <a href="http://cmuflite.org">flite text-to-speech synthesizer</a> version 1.2 or 1.3 is needed.</p>
36 36
  *  
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 37
  *  \section zinyourapp How to use ZRTP in your application 
45 38
  *
46 39
  *  Have a look at the conference application on how to add ZRTP support in your application. There is a 
... ...
@@ -63,6 +62,7 @@
63 63
  *  
64 64
  *   - <a href="http://zfoneproject.com/">Zfone</a> turns every softphone into a secure phone 
65 65
  *      by tapping into the RTP sent and received</li></ul>
66
+ *   - <a href="http://www.jitsi.org">Jitsi</a>
66 67
  *   - <a href="http://twinklephone.com/">Twinkle</a> is a very good free softphone for Linux. 
67 68
  *     It can speak ZRTP with the use of  GNU 
68 69
  *     <a href="http://www.gnutelephony.org/index.php/GNU_ZRTP">libzrtpcpp</a>.