]> sjero.net Git - linphone/commitdiff
srtp: add SRTP support to Linphone
authorPierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@linphone.org>
Mon, 3 Oct 2011 12:44:40 +0000 (14:44 +0200)
committerPierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@linphone.org>
Tue, 4 Oct 2011 15:57:52 +0000 (17:57 +0200)
coreapi/callbacks.c
coreapi/linphonecall.c
coreapi/linphonecore.c
coreapi/linphonecore.h
coreapi/offeranswer.c
coreapi/private.h
coreapi/sal.h
coreapi/sal_eXosip2.c
coreapi/sal_eXosip2_sdp.c
mediastreamer2
oRTP

index a307a1c5f06ccdf19a953a2b244f2a96fc3cdead..59bf3a2f66ce379dfd39eeb971a594d7a556ed11 100644 (file)
@@ -491,6 +491,20 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de
                                        lc->vtable.display_status(lc,msg600);
                        break;
                        case SalReasonMedia:
+                       //media_encryption_mandatory
+                               if (call->params.media_encryption == LinphoneMediaEncryptionSRTP && 
+                                       !linphone_core_is_media_encryption_mandatory(lc)) {
+                                       int i;
+                                       ms_message("Outgoing call failed with SRTP (SAVP) enabled - retrying with AVP");
+                                       /* clear SRTP local params */
+                                       call->params.media_encryption = LinphoneMediaEncryptionNone;
+                                       for(i=0; i<call->localdesc->nstreams; i++) {
+                                               call->localdesc->streams[i].proto = SalProtoRtpAvp;
+                                               memset(call->localdesc->streams[i].crypto, 0, sizeof(call->localdesc->streams[i].crypto));
+                                       }
+                                       linphone_core_start_invite(lc, call, NULL);
+                                       return;
+                               }
                                msg=_("No common codecs");
                                if (lc->vtable.display_status)
                                        lc->vtable.display_status(lc,msg);
index 8ab925cf49c9645b45b67df71123d24ddb54c996..9998c02d31e4299323c69a7ab291f0051a00701b 100644 (file)
@@ -26,6 +26,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "lpconfig.h"
 #include "private.h"
 #include <ortp/event.h>
+#include <ortp/b64.h>
 
 
 #include "mediastreamer2/mediastream.h"
@@ -41,6 +42,27 @@ static MSWebCam *get_nowebcam_device(){
 }
 #endif
 
+static bool_t generate_b64_crypto_key(int key_length, char* key_out) {
+       int b64_size;
+       uint8_t* tmp = (uint8_t*) malloc(key_length);                   
+       if (crypto_get_random(tmp, key_length)) {
+               ms_error("Failed to generate random key");
+               free(tmp);
+               return FALSE;
+       }
+       
+       b64_size = b64_encode((const char*)tmp, key_length, NULL, 0);
+       if (b64_size == 0) {
+               ms_error("Failed to b64 encode key");
+               free(tmp);
+               return FALSE;
+       }
+       key_out[b64_size] = '\0';
+       b64_encode((const char*)tmp, key_length, key_out, 40);
+       free(tmp);
+       return TRUE;
+}
+
 LinphoneCore *linphone_call_get_core(const LinphoneCall *call){
        return call->core;
 }
@@ -165,6 +187,7 @@ static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, int bandw
 static SalMediaDescription *_create_local_media_description(LinphoneCore *lc, LinphoneCall *call, unsigned int session_id, unsigned int session_ver){
        MSList *l;
        PayloadType *pt;
+       int i;
        const char *me=linphone_core_get_identity(lc);
        LinphoneAddress *addr=linphone_address_new(me);
        const char *username=linphone_address_get_username (addr);
@@ -180,7 +203,8 @@ static SalMediaDescription *_create_local_media_description(LinphoneCore *lc, Li
        /*set audio capabilities */
        strncpy(md->streams[0].addr,call->localip,sizeof(md->streams[0].addr));
        md->streams[0].port=call->audio_port;
-       md->streams[0].proto=SalProtoRtpAvp;
+       md->streams[0].proto=(call->params.media_encryption == LinphoneMediaEncryptionSRTP) ? 
+               SalProtoRtpSavp : SalProtoRtpAvp;
        md->streams[0].type=SalAudio;
        md->streams[0].ptime=lc->net_conf.down_ptime;
        l=make_codec_list(lc,lc->codecs_conf.audio_codecs,call->params.audio_bw);
@@ -192,11 +216,26 @@ static SalMediaDescription *_create_local_media_description(LinphoneCore *lc, Li
        if (call->params.has_video){
                md->nstreams++;
                md->streams[1].port=call->video_port;
-               md->streams[1].proto=SalProtoRtpAvp;
+               md->streams[1].proto=md->streams[0].proto;
                md->streams[1].type=SalVideo;
                l=make_codec_list(lc,lc->codecs_conf.video_codecs,0);
                md->streams[1].payloads=l;
        }
+       
+       for(i=0; i<md->nstreams; i++) {
+               if (md->streams[i].proto == SalProtoRtpSavp) {
+                       md->streams[i].crypto[0].tag = 1;
+                       md->streams[i].crypto[0].algo = AES_128_SHA1_80;
+                       if (!generate_b64_crypto_key(30, md->streams[i].crypto[0].master_key))
+                               md->streams[i].crypto[0].algo = 0;
+                       md->streams[i].crypto[1].tag = 2;
+                       md->streams[i].crypto[1].algo = AES_128_SHA1_32;
+                       if (!generate_b64_crypto_key(30, md->streams[i].crypto[1].master_key))
+                               md->streams[i].crypto[1].algo = 0;
+                       md->streams[i].crypto[2].algo = 0;
+               }
+       }
+       
        linphone_address_destroy(addr);
        return md;
 }
@@ -315,6 +354,7 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro
        linphone_core_get_local_ip(lc,linphone_address_get_domain(from),call->localip);
        linphone_call_init_common(call, from, to);
        call->params.has_video=linphone_core_video_enabled(lc);
+       call->params.media_encryption=linphone_core_get_media_encryption(lc);
        call->localdesc=create_local_media_description (lc,call);
        call->camera_active=call->params.has_video;
        if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseStun)
@@ -973,7 +1013,12 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna
        LinphoneCore *lc=call->core;
        int jitt_comp=lc->rtp_conf.audio_jitt_comp;
        int used_pt=-1;
+       /* look for savp stream first */
        const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc,
+                                               SalProtoRtpSavp,SalAudio);
+       /* no savp audio stream, use avp */
+       if (!stream)
+               stream=sal_media_description_find_stream(call->resultdesc,
                                                SalProtoRtpAvp,SalAudio);
        
        if (stream && stream->dir!=SalStreamInactive && stream->port!=0){
@@ -1050,6 +1095,17 @@ static void linphone_call_start_audio_stream(LinphoneCall *call, const char *cna
                                /*transform the graph to connect it to the conference filter */
                                linphone_call_add_to_conf(call);
                        }
+                       
+                       if (stream->proto == SalProtoRtpSavp) {
+                               const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,
+                                               SalProtoRtpSavp,SalAudio);
+                                               
+                               audio_stream_enable_strp(
+                                       call->audiostream, 
+                                       stream->crypto[0].algo,
+                                       local_st_desc->crypto[0].master_key,
+                                       stream->crypto[0].master_key);                          
+                       }
                }else ms_warning("No audio stream accepted ?");
        }       
 }
@@ -1058,8 +1114,14 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna
 #ifdef VIDEO_ENABLED
        LinphoneCore *lc=call->core;
        int used_pt=-1;
+       /* look for savp stream first */
        const SalStreamDescription *vstream=sal_media_description_find_stream(call->resultdesc,
-                                                       SalProtoRtpAvp,SalVideo);
+                                               SalProtoRtpSavp,SalVideo);
+       /* no savp audio stream, use avp */
+       if (!vstream)
+               vstream=sal_media_description_find_stream(call->resultdesc,
+                                               SalProtoRtpAvp,SalVideo);
+                                               
        /* shutdown preview */
        if (lc->previewstream!=NULL) {
                video_preview_stop(lc->previewstream);
@@ -1114,6 +1176,18 @@ static void linphone_call_start_video_stream(LinphoneCall *call, const char *cna
                                        used_pt, lc->rtp_conf.audio_jitt_comp, cam);
                                video_stream_set_rtcp_information(call->videostream, cname,LINPHONE_RTCP_SDES_TOOL);
                        }
+                       
+                       if (vstream->proto == SalProtoRtpSavp) {
+                               const SalStreamDescription *local_st_desc=sal_media_description_find_stream(call->localdesc,
+                                               SalProtoRtpSavp,SalVideo);
+                                               
+                               video_stream_enable_strp(
+                                       call->videostream, 
+                                       vstream->crypto[0].algo,
+                                       local_st_desc->crypto[0].master_key, 
+                                       vstream->crypto[0].master_key
+                                       );                              
+                       }
                }else ms_warning("No video stream accepted.");
        }else{
                ms_warning("No valid video stream defined.");
index cce64048ee8d1facb5b68b6073c8f10cc13e502d..db4088153bdfaf96229599cceb9450bb78d9971a 100644 (file)
@@ -35,6 +35,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #endif
 #endif
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
 /*#define UNSTANDART_GSM_11K 1*/
 
 static const char *liblinphone_version=LIBLINPHONE_VERSION;
@@ -515,7 +519,7 @@ static void sip_config_read(LinphoneCore *lc)
        }
 
        sal_root_ca(lc->sal, lp_config_get_string(lc->config,"sip","root_ca", "/etc/ssl/certs"));
-
+       
        tmp=lp_config_get_int(lc->config,"sip","guess_hostname",1);
        linphone_core_set_guess_hostname(lc,tmp);
 
@@ -4231,7 +4235,9 @@ LinphoneGlobalState linphone_core_get_global_state(const LinphoneCore *lc){
 
 LinphoneCallParams *linphone_core_create_default_call_parameters(LinphoneCore *lc){
        LinphoneCallParams *p=ms_new0(LinphoneCallParams,1);
-       p->has_video=linphone_core_video_enabled(lc);
+       p->has_video=linphone_core_video_enabled(lc); 
+       p->media_encryption=linphone_core_get_media_encryption(lc);
+       ms_message("%s : %d", __FUNCTION__, p->media_encryption);
        return p;
 }
 
@@ -4347,3 +4353,37 @@ const LinphoneCall* linphone_core_find_call_from_uri(LinphoneCore *lc, const cha
        }
        return NULL;
 }
+
+void linphone_core_set_srtp_enabled(LinphoneCore *lc, bool_t enabled) {
+       lp_config_set_int(lc->config,"sip","srtp",(int)enabled);
+}
+
+void linphone_core_set_media_encryption_enabled(LinphoneCore *lc, enum LinphoneMediaEncryption menc) {
+       if (menc == LinphoneMediaEncryptionSRTP)
+               lp_config_set_string(lc->config,"sip","media_encryption","srtp");
+       else if (menc == LinphoneMediaEncryptionZRTP)
+               lp_config_set_string(lc->config,"sip","media_encryption","zrtp");
+       else
+               lp_config_set_string(lc->config,"sip","media_encryption","none");
+}
+
+enum LinphoneMediaEncryption linphone_core_get_media_encryption(LinphoneCore *lc) {
+       const char* menc = lp_config_get_string(lc->config, "sip", "media_encryption", NULL);
+       
+       if (menc == NULL)
+               return LinphoneMediaEncryptionNone;
+       else if (strcmp(menc, "srtp")==0)
+               return LinphoneMediaEncryptionSRTP;
+       else if (strcmp(menc, "zrtp")==0)
+               return LinphoneMediaEncryptionZRTP;
+       else
+               return LinphoneMediaEncryptionNone;
+}
+
+bool_t linphone_core_is_media_encryption_mandatory(LinphoneCore *lc) {
+       return (bool_t)lp_config_get_int(lc->config, "sip", "media_encryption_mandatory", 0);
+}
+
+void linphone_core_set_media_encryption_mandatory(LinphoneCore *lc, bool_t m) {
+       lp_config_set_int(lc->config, "sip", "media_encryption_mandatory", (int)m);
+}
index 368746cda3cb783126fef2ea3f6eea0dd5f2e4a9..c2c02f0e5874d46732713c42c0b8924e488c4244 100644 (file)
@@ -156,7 +156,11 @@ typedef struct _LinphoneCallLog{
        struct _LinphoneCore *lc;
 } LinphoneCallLog;
 
-
+enum LinphoneMediaEncryption {
+       LinphoneMediaEncryptionNone,
+       LinphoneMediaEncryptionSRTP,
+       LinphoneMediaEncryptionZRTP
+};
 
 /*public: */
 void linphone_call_log_set_user_pointer(LinphoneCallLog *cl, void *up);
@@ -1029,6 +1033,12 @@ int linphone_core_leave_conference(LinphoneCore *lc);
 int linphone_core_terminate_conference(LinphoneCore *lc);
 int linphone_core_get_conference_size(LinphoneCore *lc);
 
+void linphone_core_set_media_encryption_enabled(LinphoneCore *lc, enum LinphoneMediaEncryption menc);
+enum LinphoneMediaEncryption linphone_core_get_media_encryption(LinphoneCore *lc);
+
+bool_t linphone_core_is_media_encryption_mandatory(LinphoneCore *lc);
+void linphone_core_set_media_encryption_mandatory(LinphoneCore *lc, bool_t m);
+
 #ifdef __cplusplus
 }
 #endif
index 3e054508b1aa5a863287569b5ed9730957f49619..ab213a2391b591c3013603e26657145a6538d256 100644 (file)
@@ -128,6 +128,31 @@ static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t
        return res;
 }
 
+static bool_t match_crypto_algo(const SalSrtpCryptoAlgo* local, const SalSrtpCryptoAlgo* remote, 
+       SalSrtpCryptoAlgo* result, bool_t use_local_key) {
+       int i,j;
+       for(i=0; i<SAL_CRYPTO_ALGO_MAX; i++) {
+               if (remote[i].algo == 0)
+                       break;
+                       
+               for(j=0; j<SAL_CRYPTO_ALGO_MAX; j++) {
+                       if (remote[i].algo == local[j].algo) {
+                               result->algo = remote[i].algo;
+                               if (use_local_key) {
+                                       strncpy(result->master_key, local[j].master_key, 41);
+                                       result->tag = local[j].tag;
+                               } else {
+                                       strncpy(result->master_key, remote[j].master_key, 41);
+                                       result->tag = remote[j].tag;
+                               }
+                               result->master_key[40] = '\0';
+                               return TRUE;
+                       }
+               }
+       }
+       return FALSE;
+}
+
 
 
 static SalStreamDir compute_dir_outgoing(SalStreamDir local, SalStreamDir answered){
@@ -174,7 +199,7 @@ static void initiate_outgoing(const SalStreamDescription *local_offer,
                                        SalStreamDescription *result){
        if (remote_answer->port!=0)
                result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads,TRUE,FALSE);
-       result->proto=local_offer->proto;
+       result->proto=remote_answer->proto;
        result->type=local_offer->type;
        result->dir=compute_dir_outgoing(local_offer->dir,remote_answer->dir);
 
@@ -186,6 +211,12 @@ static void initiate_outgoing(const SalStreamDescription *local_offer,
        }else{
                result->port=0;
        }
+       if (result->proto == SalProtoRtpSavp) {
+               /* verify crypto algo */
+               memset(result->crypto, 0, sizeof(result->crypto));
+               if (!match_crypto_algo(local_offer->crypto, remote_answer->crypto, &result->crypto[0], FALSE))
+                       result->port = 0;
+       }
 }
 
 
@@ -193,7 +224,7 @@ static void initiate_incoming(const SalStreamDescription *local_cap,
                                        const SalStreamDescription *remote_offer,
                                        SalStreamDescription *result, bool_t one_matching_codec){
        result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads, FALSE, one_matching_codec);
-       result->proto=local_cap->proto;
+       result->proto=remote_offer->proto;
        result->type=local_cap->type;
        result->dir=compute_dir_incoming(local_cap->dir,remote_offer->dir);
        if (result->payloads && !only_telephone_event(result->payloads)){
@@ -205,6 +236,13 @@ static void initiate_incoming(const SalStreamDescription *local_cap,
        }else{
                result->port=0;
        }
+       if (result->proto == SalProtoRtpSavp) {
+               /* select crypto algo */
+               memset(result->crypto, 0, sizeof(result->crypto));
+               if (!match_crypto_algo(local_cap->crypto, remote_offer->crypto, &result->crypto[0], TRUE))
+                       result->port = 0; 
+               
+       }
 }
 
 /**
@@ -215,6 +253,7 @@ int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer,
                                                                        const SalMediaDescription *remote_answer,
                                                        SalMediaDescription *result){
     int i,j;
+    
        const SalStreamDescription *ls,*rs;
     for(i=0,j=0;i<local_offer->nstreams;++i){
                ms_message("Processing for stream %i",i);
@@ -246,10 +285,18 @@ int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities
     for(i=0;i<remote_offer->nstreams;++i){
                rs=&remote_offer->streams[i];
                ms_message("Processing for stream %i",i);
+               
                ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,rs->proto,rs->type);
+               ms_message("remote proto: %s => %p", (rs->proto == SalProtoRtpAvp)?"AVP":"SAVP", ls);
+               /* if matching failed, and remote proposes Avp only, ask for local Savp streams */ 
+               if (!ls && rs->proto == SalProtoRtpAvp) {
+                       ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,SalProtoRtpSavp,rs->type);
+                       ms_message("retry with AVP => %p", ls);
+               }
                if (ls){
                initiate_incoming(ls,rs,&result->streams[i],one_matching_codec);
-               } else {
+               }
+               else {
                        /* create an inactive stream for the answer, as there where no matching stream a local capability */
                        result->streams[i].dir=SalStreamInactive;
                        result->streams[i].port=0;
index b8aba9d1ff7595e59352b1259d2e0678712320c7..1dfeda65d6fe7f40106267f2d5fdbbcafa4c1923 100644 (file)
@@ -57,7 +57,6 @@
 #endif
 #endif
 
-
 struct _LinphoneCallParams{
        LinphoneCall *referer; /*in case this call creation is consecutive to an incoming transfer, this points to the original call */
        int audio_bw; /* bandwidth limit for audio stream */
@@ -65,6 +64,7 @@ struct _LinphoneCallParams{
        bool_t real_early_media; /*send real media even during early media (for outgoing calls)*/
        bool_t in_conference; /*in conference mode */
        bool_t pad;
+       enum LinphoneMediaEncryption media_encryption;
 };
 
 struct _LinphoneCall
index 140c14d045dd2732cb1e0b94512638601529e85a..e4d10fe86b503ff00474de72b50585d436841be8 100644 (file)
@@ -27,6 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #define sal_h
 
 #include "mediastreamer2/mscommon.h"
+#include "ortp/srtp.h"
 
 /*Dirty hack, keep in sync with mediastreamer2/include/mediastream.h */
 #ifndef PAYLOAD_TYPE_FLAG_CAN_RECV
@@ -111,6 +112,15 @@ typedef struct SalEndpointCandidate{
 
 #define SAL_ENDPOINT_CANDIDATE_MAX 2
 
+typedef struct SalSrtpCryptoAlgo {
+       unsigned int tag;
+       enum ortp_srtp_crypto_suite_t algo;
+       /* 41= 40 max(key_length for all algo) + '\0' */
+       char master_key[41];
+} SalSrtpCryptoAlgo;
+
+#define SAL_CRYPTO_ALGO_MAX 4
+
 typedef struct SalStreamDescription{
        SalMediaProto proto;
        SalStreamType type;
@@ -122,6 +132,7 @@ typedef struct SalStreamDescription{
        int ptime;
        SalEndpointCandidate candidates[SAL_ENDPOINT_CANDIDATE_MAX];
        SalStreamDir dir;
+       SalSrtpCryptoAlgo crypto[SAL_CRYPTO_ALGO_MAX];
 } SalStreamDescription;
 
 #define SAL_MEDIA_DESCRIPTION_MAX_STREAMS 4
index 5b9500509538313bbd43750521ac442cf2bb97bc..e4f4547ac82d67349a560ef10bca865cb19b0218 100644 (file)
@@ -514,12 +514,18 @@ static void sdp_process(SalOp *h){
                 It should contains media parameters constraint from the remote offer, not our response*/
                strcpy(h->result->addr,h->base.remote_media->addr);
                h->result->bandwidth=h->base.remote_media->bandwidth;
+               
+               //remplacer la cle
                for(i=0;i<h->result->nstreams;++i){
                        if (h->result->streams[i].port>0){
                                strcpy(h->result->streams[i].addr,h->base.remote_media->streams[i].addr);
                                h->result->streams[i].ptime=h->base.remote_media->streams[i].ptime;
                                h->result->streams[i].bandwidth=h->base.remote_media->streams[i].bandwidth;
                                h->result->streams[i].port=h->base.remote_media->streams[i].port;
+                               
+                               if (h->result->streams[i].proto == SalProtoRtpSavp) {
+                                       h->result->streams[i].crypto[0] = h->base.remote_media->streams[i].crypto[0]; 
+                               }
                        }
                }
        }
@@ -545,6 +551,8 @@ int sal_call(SalOp *h, const char *from, const char *to){
        sal_op_set_from(h,from);
        sal_op_set_to(h,to);
        sal_exosip_fix_route(h);
+       
+       h->terminated = FALSE;
        err=eXosip_call_build_initial_invite(&invite,to,from,sal_op_get_route(h),"Phone call");
        if (err!=0){
                ms_error("Could not create call.");
index 3993e66f8179514c9b2e2b8b742d4791e9dbd9e4..813e9da1aafab66c279d9ea73a25adc0fa011798 100644 (file)
@@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 
 #include "ortp/b64.h"
+#include "ortp/srtp.h"
 #include "sal.h"
 #include <eXosip2/eXosip.h>
 
@@ -233,9 +234,47 @@ static void add_line(sdp_message_t *msg, int lineno, const SalStreamDescription
                              osip_strdup ("IN"), inet6 ? osip_strdup ("IP6") : osip_strdup ("IP4"),
                              osip_strdup (addr), NULL, NULL);
        }
-       sdp_message_m_media_add (msg, osip_strdup (mt),
-                                int_2char (port), NULL,
-                                osip_strdup ("RTP/AVP"));
+       
+       if (desc->proto == SalProtoRtpSavp) {
+               int i;
+               
+               sdp_message_m_media_add (msg, osip_strdup (mt),
+                                        int_2char (port), NULL,
+                                        osip_strdup ("RTP/SAVP"));
+       
+               /* add crypto lines */
+               for(i=0; i<SAL_CRYPTO_ALGO_MAX; i++) {
+                       char buffer[1024];
+                       switch (desc->crypto[i].algo) {
+                               case AES_128_SHA1_80:
+                                       snprintf(buffer, 1024, "%d %s inline:%s",
+                                               desc->crypto[i].tag, "AES_CM_128_HMAC_SHA1_80", desc->crypto[i].master_key);
+                                       sdp_message_a_attribute_add(msg, lineno, osip_strdup("crypto"),
+                                               osip_strdup(buffer));
+                                       break;
+                               case AES_128_SHA1_32:
+                                       snprintf(buffer, 1024, "%d %s inline:%s",
+                                               desc->crypto[i].tag, "AES_CM_128_HMAC_SHA1_32", desc->crypto[i].master_key);
+                                       sdp_message_a_attribute_add(msg, lineno, osip_strdup("crypto"),
+                                               osip_strdup(buffer));
+                                       break;
+                               case AES_128_NO_AUTH:
+                                       ms_warning("Unsupported crypto suite: AES_128_NO_AUTH");
+                                       break;
+                               case NO_CIPHER_SHA1_80:
+                                       ms_warning("Unsupported crypto suite: NO_CIPHER_SHA1_80");
+                                       break; 
+                               default:
+                                       i = SAL_CRYPTO_ALGO_MAX;
+                       }
+               }
+               
+       } else {
+               sdp_message_m_media_add (msg, osip_strdup (mt),
+                                        int_2char (port), NULL,
+                                        osip_strdup ("RTP/AVP"));
+               
+       }
        if (desc->bandwidth>0) sdp_message_b_bandwidth_add (msg, lineno, osip_strdup ("AS"),
                                     int_2char(desc->bandwidth));
        if (desc->ptime>0) sdp_message_a_attribute_add(msg,lineno,osip_strdup("ptime"),
@@ -355,7 +394,7 @@ int sdp_to_media_description(sdp_message_t *msg, SalMediaDescription *desc){
                for(j=0;(sbw=sdp_message_bandwidth_get(msg,i,j))!=NULL;++j){
                        if (strcasecmp(sbw->b_bwtype,"AS")==0) stream->bandwidth=atoi(sbw->b_bandwidth);
                }
-               stream->dir=_sdp_message_get_mline_dir(msg,i);
+               stream->dir=_sdp_message_get_mline_dir(msg,i);          
                /* for each payload type */
                for (j=0;((number=sdp_message_m_payload_get (msg, i,j)) != NULL); j++){
                        const char *rtpmap,*fmtp;
@@ -373,6 +412,49 @@ int sdp_to_media_description(sdp_message_t *msg, SalMediaDescription *desc){
                                        pt->send_fmtp ? pt->send_fmtp : "");
                        }
                }
+               
+               /* read crypto lines if any */
+               if (stream->proto == SalProtoRtpSavp) {
+                       int k, valid_count = 0;
+                       sdp_attribute_t *attr;
+                               
+                       memset(&stream->crypto, 0, sizeof(stream->crypto));
+                       for (k=0;valid_count < SAL_CRYPTO_ALGO_MAX && (attr=sdp_message_attribute_get(msg,i,k))!=NULL;k++){
+                               char tmp[256], tmp2[256];
+                               if (keywordcmp("crypto",attr->a_att_field)==0 && attr->a_att_value!=NULL){
+                                       int nb = sscanf(attr->a_att_value, "%d %256s inline:%256s",
+                                               &stream->crypto[valid_count].tag,
+                                               tmp,
+                                               tmp2);
+                                               ms_message("Found valid crypto line (tag:%d algo:'%s' key:'%s'", 
+                                                               stream->crypto[valid_count].tag, 
+                                                               tmp, 
+                                                               tmp2);
+                                       if (nb == 3) {
+                                               if (strcmp(tmp, "AES_CM_128_HMAC_SHA1_80") == 0)
+                                                       stream->crypto[valid_count].algo = AES_128_SHA1_80;
+                                               else if (strcmp(tmp, "AES_CM_128_HMAC_SHA1_32") == 0)
+                                                       stream->crypto[valid_count].algo = AES_128_SHA1_32;
+                                               else {
+                                                       ms_warning("Failed to parse crypto-algo: '%s'", tmp);
+                                                       stream->crypto[valid_count].algo = 0;
+                                               }
+                                               if (stream->crypto[valid_count].algo) {
+                                                       strncpy(stream->crypto[valid_count].master_key, tmp2, 41);
+                                                       stream->crypto[valid_count].master_key[40] = '\0';
+                                                       ms_message("Found valid crypto line (tag:%d algo:'%s' key:'%s'", 
+                                                               stream->crypto[valid_count].tag, 
+                                                               tmp, 
+                                                               stream->crypto[valid_count].master_key);
+                                                       valid_count++;
+                                               }
+                                       } else {
+                                               ms_warning("sdp has a strange a= line (%s) nb=%i",attr->a_att_value,nb);
+                                       }
+                               }
+                       }
+                       ms_message("Found: %d valid crypto lines", valid_count);
+               }
        }
        desc->nstreams=i;
        return 0;
index 28a643d20f6d6384d96590ab6e988928597878cf..aadeaaa8b2de3b1e0cb9ffd5a0a22a85335e7951 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 28a643d20f6d6384d96590ab6e988928597878cf
+Subproject commit aadeaaa8b2de3b1e0cb9ffd5a0a22a85335e7951
diff --git a/oRTP b/oRTP
index 845e0cf4e33cc9964c48aa9191005d4108dfebbd..3e3ba3f4922a9f2eaddd782b571abf3968aa41b9 160000 (submodule)
--- a/oRTP
+++ b/oRTP
@@ -1 +1 @@
-Subproject commit 845e0cf4e33cc9964c48aa9191005d4108dfebbd
+Subproject commit 3e3ba3f4922a9f2eaddd782b571abf3968aa41b9