diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 3fd3b20e87b98627c4edca329e885f1dc57af5c0..6e386e19bc76a20c7cc956cad2b0d46a9e05ff2d 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -1384,11 +1384,13 @@ static int process_sdp_o(const char *o, struct sip_pvt *p);
 static int process_sdp_c(const char *c, struct ast_sockaddr *addr);
 static int process_sdp_a_sendonly(const char *a, int *sendonly);
 static int process_sdp_a_ice(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance);
+static int process_sdp_a_dtls(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance);
 static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newaudiortp, int *last_rtpmap_codec);
 static int process_sdp_a_video(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newvideortp, int *last_rtpmap_codec);
 static int process_sdp_a_text(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newtextrtp, char *red_fmtp, int *red_num_gen, int *red_data_pt, int *last_rtpmap_codec);
 static int process_sdp_a_image(const char *a, struct sip_pvt *p);
 static void add_ice_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf);
+static void add_dtls_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf);
 static void start_ice(struct ast_rtp_instance *instance);
 static void add_codec_to_sdp(const struct sip_pvt *p, struct ast_format *codec,
 			     struct ast_str **m_buf, struct ast_str **a_buf,
@@ -4925,6 +4927,8 @@ static void sip_destroy_peer(struct sip_peer *peer)
 	ast_string_field_free_memory(peer);
 
 	peer->caps = ast_format_cap_destroy(peer->caps);
+
+	ast_rtp_dtls_cfg_free(&peer->dtls_cfg);
 }
 
 /*! \brief Update peer data in database (if used) */
@@ -5512,6 +5516,29 @@ static void copy_socket_data(struct sip_socket *to_sock, const struct sip_socket
 	*to_sock = *from_sock;
 }
 
+/*! \brief Initialize DTLS-SRTP support on an RTP instance */
+static int dialog_initialize_dtls_srtp(const struct sip_pvt *dialog, struct ast_rtp_instance *rtp, struct sip_srtp **srtp)
+{
+	struct ast_rtp_engine_dtls *dtls;
+
+	if (!dialog->dtls_cfg.enabled) {
+		return 0;
+	}
+
+	if (!ast_rtp_engine_srtp_is_registered()) {
+		ast_log(LOG_ERROR, "No SRTP module loaded, can't setup SRTP session.\n");
+		return -1;
+	}
+
+	if (!(dtls = ast_rtp_instance_get_dtls(rtp)) ||
+	    dtls->set_configuration(rtp, &dialog->dtls_cfg) ||
+	    !(*srtp = sip_srtp_alloc())) {
+		return -1;
+	}
+
+	return 0;
+}
+
 /*! \brief Initialize RTP portion of a dialog
  * \return -1 on failure, 0 on success
  */
@@ -5533,6 +5560,10 @@ static int dialog_initialize_rtp(struct sip_pvt *dialog)
 		ice->stop(dialog->rtp);
 	}
 
+	if (dialog_initialize_dtls_srtp(dialog, dialog->rtp, &dialog->srtp)) {
+		return -1;
+	}
+
 	if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT_ALWAYS) ||
 			(ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) && (ast_format_cap_has_type(dialog->caps, AST_FORMAT_TYPE_VIDEO)))) {
 		if (!(dialog->vrtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr_tmp, NULL))) {
@@ -5543,6 +5574,10 @@ static int dialog_initialize_rtp(struct sip_pvt *dialog)
 			ice->stop(dialog->vrtp);
 		}
 
+		if (dialog_initialize_dtls_srtp(dialog, dialog->vrtp, &dialog->vsrtp)) {
+			return -1;
+		}
+
 		ast_rtp_instance_set_timeout(dialog->vrtp, dialog->rtptimeout);
 		ast_rtp_instance_set_hold_timeout(dialog->vrtp, dialog->rtpholdtimeout);
 		ast_rtp_instance_set_keepalive(dialog->vrtp, dialog->rtpkeepalive);
@@ -5559,6 +5594,10 @@ static int dialog_initialize_rtp(struct sip_pvt *dialog)
 			ice->stop(dialog->trtp);
 		}
 
+		if (dialog_initialize_dtls_srtp(dialog, dialog->trtp, &dialog->tsrtp)) {
+			return -1;
+		}
+
 		/* Do not timeout text as its not constant*/
 		ast_rtp_instance_set_keepalive(dialog->trtp, dialog->rtpkeepalive);
 
@@ -5615,6 +5654,8 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
 
 	ast_string_field_set(dialog, engine, peer->engine);
 
+	ast_rtp_dtls_cfg_copy(&peer->dtls_cfg, &dialog->dtls_cfg);
+
 	dialog->rtptimeout = peer->rtptimeout;
 	dialog->rtpholdtimeout = peer->rtpholdtimeout;
 	dialog->rtpkeepalive = peer->rtpkeepalive;
@@ -6260,6 +6301,8 @@ void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
 	p->redircaps = ast_format_cap_destroy(p->redircaps);
 	p->prefcaps = ast_format_cap_destroy(p->prefcaps);
 
+	ast_rtp_dtls_cfg_free(&p->dtls_cfg);
+
 	if (p->last_device_state_info) {
 		ao2_ref(p->last_device_state_info, -1);
 		p->last_device_state_info = NULL;
@@ -9574,6 +9617,16 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 				processed = TRUE;
 			}
 
+			if (process_sdp_a_dtls(value, p, p->rtp)) {
+				processed = TRUE;
+			}
+			if (process_sdp_a_dtls(value, p, p->vrtp)) {
+				processed = TRUE;
+			}
+			if (process_sdp_a_dtls(value, p, p->trtp)) {
+				processed = TRUE;
+			}
+
 			break;
 		}
 
@@ -9591,8 +9644,9 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 		int image = FALSE;
 		int text = FALSE;
 		int processed_crypto = FALSE;
-		char protocol[6] = {0,};
+		char protocol[18] = {0,};
 		int x;
+		struct ast_rtp_engine_dtls *dtls;
 
 		numberofports = 0;
 		len = -1;
@@ -9611,50 +9665,60 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 
 		/* Check for 'audio' media offer */
 		if (strncmp(m, "audio ", 6) == 0) {
-			if ((sscanf(m, "audio %30u/%30u RTP/%5s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
-			    (sscanf(m, "audio %30u RTP/%5s %n", &x, protocol, &len) == 2 && len > 0)) {
+			if ((sscanf(m, "audio %30u/%30u %17s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
+			    (sscanf(m, "audio %30u %17s %n", &x, protocol, &len) == 2 && len > 0)) {
 				codecs = m + len;
 				/* produce zero-port m-line since it may be needed later
-				 * length is "m=audio 0 RTP/" + protocol + " " + codecs + "\0" */
-				if (!(offer->decline_m_line = ast_malloc(14 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
+				 * length is "m=audio 0 " + protocol + " " + codecs + "\0" */
+				if (!(offer->decline_m_line = ast_malloc(10 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
 					ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
 					res = -1;
 					goto process_sdp_cleanup;
 				}
 				/* guaranteed to be exactly the right length */
-				sprintf(offer->decline_m_line, "m=audio 0 RTP/%s %s", protocol, codecs);
+				sprintf(offer->decline_m_line, "m=audio 0 %s %s", protocol, codecs);
 
 				if (x == 0) {
 					ast_log(LOG_WARNING, "Ignoring audio media offer because port number is zero\n");
 					continue;
 				}
 
+				if (has_media_stream(p, SDP_AUDIO)) {
+					ast_log(LOG_WARNING, "Declining non-primary audio stream: %s\n", m);
+					continue;
+				}
+
 				/* Check number of ports offered for stream */
 				if (numberofports > 1) {
 					ast_log(LOG_WARNING, "%d ports offered for audio media, not supported by Asterisk. Will try anyway...\n", numberofports);
 				}
 
-				if (!strcmp(protocol, "SAVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+				if ((!strcmp(protocol, "RTP/SAVPF") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
 					ast_log(LOG_WARNING, "Received SAVPF profle in audio offer but AVPF is not enabled: %s\n", m);
 					continue;
-				} else if (!strcmp(protocol, "SAVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+				} else if ((!strcmp(protocol, "RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVP")) && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
 					ast_log(LOG_WARNING, "Received SAVP profile in audio offer but AVPF is enabled: %s\n", m);
 					continue;
-				} else if (!strcmp(protocol, "SAVP") || !strcmp(protocol, "SAVPF")) {
+				} else if (!strcmp(protocol, "UDP/TLS/RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) {
 					secure_audio = 1;
-				} else if (!strcmp(protocol, "AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+
+					if (p->srtp) {
+						ast_set_flag(p->srtp, SRTP_CRYPTO_OFFER_OK);
+					}
+				} else if (!strcmp(protocol, "RTP/SAVP") || !strcmp(protocol, "RTP/SAVPF")) {
+					secure_audio = 1;
+				} else if (!strcmp(protocol, "RTP/AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
 					ast_log(LOG_WARNING, "Received AVPF profile in audio offer but AVPF is not enabled: %s\n", m);
 					continue;
-				} else if (!strcmp(protocol, "AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+				} else if (!strcmp(protocol, "RTP/AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
 					ast_log(LOG_WARNING, "Received AVP profile in audio offer but AVPF is enabled: %s\n", m);
 					continue;
-				} else if (strcmp(protocol, "AVP") && strcmp(protocol, "AVPF")) {
-					ast_log(LOG_WARNING, "Unknown RTP profile in audio offer: %s\n", m);
+				} else if ((!strcmp(protocol, "UDP/TLS/RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) &&
+					   (!(dtls = ast_rtp_instance_get_dtls(p->rtp)) || !dtls->active(p->rtp))) {
+					ast_log(LOG_WARNING, "Received UDP/TLS in audio offer but DTLS is not enabled: %s\n", m);
 					continue;
-				}
-
-				if (has_media_stream(p, SDP_AUDIO)) {
-					ast_log(LOG_WARNING, "Declining non-primary audio stream: %s\n", m);
+				} else if (strcmp(protocol, "RTP/AVP") && strcmp(protocol, "RTP/AVPF")) {
+					ast_log(LOG_WARNING, "Unknown RTP profile in audio offer: %s\n", m);
 					continue;
 				}
 
@@ -9683,18 +9747,18 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 		}
 		/* Check for 'video' media offer */
 		else if (strncmp(m, "video ", 6) == 0) {
-			if ((sscanf(m, "video %30u/%30u RTP/%5s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
-			    (sscanf(m, "video %30u RTP/%5s %n", &x, protocol, &len) == 2 && len > 0)) {
+			if ((sscanf(m, "video %30u/%30u %17s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
+			    (sscanf(m, "video %30u %17s %n", &x, protocol, &len) == 2 && len > 0)) {
 				codecs = m + len;
 				/* produce zero-port m-line since it may be needed later
-				 * length is "m=video 0 RTP/" + protocol + " " + codecs + "\0" */
-				if (!(offer->decline_m_line = ast_malloc(14 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
+				 * length is "m=video 0 " + protocol + " " + codecs + "\0" */
+				if (!(offer->decline_m_line = ast_malloc(10 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
 					ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
 					res = -1;
 					goto process_sdp_cleanup;
 				}
 				/* guaranteed to be exactly the right length */
-				sprintf(offer->decline_m_line, "m=video 0 RTP/%s %s", protocol, codecs);
+				sprintf(offer->decline_m_line, "m=video 0 %s %s", protocol, codecs);
 
 				if (x == 0) {
 					ast_log(LOG_WARNING, "Ignoring video stream offer because port number is zero\n");
@@ -9706,30 +9770,36 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 					ast_log(LOG_WARNING, "%d ports offered for video stream, not supported by Asterisk. Will try anyway...\n", numberofports);
 				}
 
-				if (!strcmp(protocol, "SAVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+				if (has_media_stream(p, SDP_VIDEO)) {
+					ast_log(LOG_WARNING, "Declining non-primary video stream: %s\n", m);
+					continue;
+				}
+
+				if ((!strcmp(protocol, "RTP/SAVPF") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
 					ast_log(LOG_WARNING, "Received SAVPF profle in video offer but AVPF is not enabled: %s\n", m);
 					continue;
-				} else if (!strcmp(protocol, "SAVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+				} else if ((!strcmp(protocol, "RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVP")) && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
 					ast_log(LOG_WARNING, "Received SAVP profile in video offer but AVPF is enabled: %s\n", m);
 					continue;
-				} else if (!strcmp(protocol, "SAVP") || !strcmp(protocol, "SAVPF")) {
+				} else if (!strcmp(protocol, "UDP/TLS/RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) {
+					secure_video = 1;
+
+					if (p->vsrtp || (p->vsrtp = sip_srtp_alloc())) {
+						ast_set_flag(p->vsrtp, SRTP_CRYPTO_OFFER_OK);
+					}
+				} else if (!strcmp(protocol, "RTP/SAVP") || !strcmp(protocol, "RTP/SAVPF")) {
 					secure_video = 1;
-				} else if (!strcmp(protocol, "AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+				} else if (!strcmp(protocol, "RTP/AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
 					ast_log(LOG_WARNING, "Received AVPF profile in video offer but AVPF is not enabled: %s\n", m);
 					continue;
-				} else if (!strcmp(protocol, "AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+				} else if (!strcmp(protocol, "RTP/AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
 					ast_log(LOG_WARNING, "Received AVP profile in video offer but AVPF is enabled: %s\n", m);
 					continue;
-				} else if (strcmp(protocol, "AVP") && strcmp(protocol, "AVPF")) {
+				} else if (strcmp(protocol, "RTP/AVP") && strcmp(protocol, "RTP/AVPF")) {
 					ast_log(LOG_WARNING, "Unknown RTP profile in video offer: %s\n", m);
 					continue;
 				}
 
-				if (has_media_stream(p, SDP_VIDEO)) {
-					ast_log(LOG_WARNING, "Declining non-primary video stream: %s\n", m);
-					continue;
-				}
-
 				video = TRUE;
 				p->novideo = FALSE;
 				offer->type = SDP_VIDEO;
@@ -9755,18 +9825,18 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 		}
 		/* Check for 'text' media offer */
 		else if (strncmp(m, "text ", 5) == 0) {
-			if ((sscanf(m, "text %30u/%30u RTP/%5s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
-			    (sscanf(m, "text %30u RTP/%5s %n", &x, protocol, &len) == 2 && len > 0)) {
+			if ((sscanf(m, "text %30u/%30u %17s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
+			    (sscanf(m, "text %30u %17s %n", &x, protocol, &len) == 2 && len > 0)) {
 				codecs = m + len;
 				/* produce zero-port m-line since it may be needed later
-				 * length is "m=text 0 RTP/" + protocol + " " + codecs + "\0" */
-				if (!(offer->decline_m_line = ast_malloc(13 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
+				 * length is "m=text 0 " + protocol + " " + codecs + "\0" */
+				if (!(offer->decline_m_line = ast_malloc(9 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
 					ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
 					res = -1;
 					goto process_sdp_cleanup;
 				}
 				/* guaranteed to be exactly the right length */
-				sprintf(offer->decline_m_line, "m=text 0 RTP/%s %s", protocol, codecs);
+				sprintf(offer->decline_m_line, "m=text 0 %s %s", protocol, codecs);
 
 				if (x == 0) {
 					ast_log(LOG_WARNING, "Ignoring text stream offer because port number is zero\n");
@@ -9778,13 +9848,13 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 					ast_log(LOG_WARNING, "%d ports offered for text stream, not supported by Asterisk. Will try anyway...\n", numberofports);
 				}
 
-				if (!strcmp(protocol, "AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+				if (!strcmp(protocol, "RTP/AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
 					ast_log(LOG_WARNING, "Received AVPF profile in text offer but AVPF is not enabled: %s\n", m);
 					continue;
-				} else if (!strcmp(protocol, "AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+				} else if (!strcmp(protocol, "RTP/AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
 					ast_log(LOG_WARNING, "Received AVP profile in text offer but AVPF is enabled: %s\n", m);
 					continue;
-				} else if (strcmp(protocol, "AVP") && strcmp(protocol, "AVPF")) {
+				} else if (strcmp(protocol, "RTP/AVP") && strcmp(protocol, "RTP/AVPF")) {
 					ast_log(LOG_WARNING, "Unknown RTP profile in text offer: %s\n", m);
 					continue;
 				}
@@ -9920,6 +9990,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 				if (audio) {
 					if (process_sdp_a_ice(value, p, p->rtp)) {
 						processed = TRUE;
+					} else if (process_sdp_a_dtls(value, p, p->rtp)) {
+						processed = TRUE;
 					} else if (process_sdp_a_sendonly(value, &sendonly)) {
 						processed = TRUE;
 					} else if (!processed_crypto && process_crypto(p, p->rtp, &p->srtp, value)) {
@@ -9933,6 +10005,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 				else if (video) {
 					if (process_sdp_a_ice(value, p, p->vrtp)) {
 						processed = TRUE;
+					} else if (process_sdp_a_dtls(value, p, p->vrtp)) {
+						processed = TRUE;
 					} else if (!processed_crypto && process_crypto(p, p->vrtp, &p->vsrtp, value)) {
 						processed_crypto = TRUE;
 						processed = TRUE;
@@ -9944,7 +10018,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
 				else if (text) {
 					if (process_sdp_a_ice(value, p, p->trtp)) {
 						processed = TRUE;
-					} if (process_sdp_a_text(value, p, &newtextrtp, red_fmtp, &red_num_gen, red_data_pt, &last_rtpmap_codec)) {
+					} else if (process_sdp_a_text(value, p, &newtextrtp, red_fmtp, &red_num_gen, red_data_pt, &last_rtpmap_codec)) {
 						processed = TRUE;
 					} else if (!processed_crypto && process_crypto(p, p->trtp, &p->tsrtp, value)) {
 						processed_crypto = TRUE;
@@ -10463,6 +10537,56 @@ static int process_sdp_a_ice(const char *a, struct sip_pvt *p, struct ast_rtp_in
 	return found;
 }
 
+static int process_sdp_a_dtls(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance)
+{
+	struct ast_rtp_engine_dtls *dtls;
+	int found = FALSE;
+	char value[256], hash[6];
+
+	if (!instance || !p->dtls_cfg.enabled || !(dtls = ast_rtp_instance_get_dtls(instance))) {
+		return found;
+	}
+
+	if (sscanf(a, "setup: %255s", value) == 1) {
+		found = TRUE;
+
+		if (!strcasecmp(value, "active")) {
+			dtls->set_setup(instance, AST_RTP_DTLS_SETUP_ACTIVE);
+		} else if (!strcasecmp(value, "passive")) {
+			dtls->set_setup(instance, AST_RTP_DTLS_SETUP_PASSIVE);
+		} else if (!strcasecmp(value, "actpass")) {
+			dtls->set_setup(instance, AST_RTP_DTLS_SETUP_ACTPASS);
+		} else if (!strcasecmp(value, "holdconn")) {
+			dtls->set_setup(instance, AST_RTP_DTLS_SETUP_HOLDCONN);
+		} else {
+			ast_log(LOG_WARNING, "Unsupported setup attribute value '%s' received on dialog '%s'\n",
+				value, p->callid);
+		}
+	} else if (sscanf(a, "connection: %255s", value) == 1) {
+		found = TRUE;
+
+		if (!strcasecmp(value, "new")) {
+			dtls->reset(instance);
+		} else if (!strcasecmp(value, "existing")) {
+			/* Since they want to just use what already exists we go on as if nothing happened */
+		} else {
+			ast_log(LOG_WARNING, "Unsupported connection attribute value '%s' received on dialog '%s'\n",
+				value, p->callid);
+		}
+	} else if (sscanf(a, "fingerprint: %5s %255s", hash, value) == 2) {
+		found = TRUE;
+
+		if (!strcasecmp(hash, "sha-1")) {
+			dtls->set_fingerprint(instance, AST_RTP_DTLS_HASH_SHA1, value);
+		} else {
+			ast_log(LOG_WARNING, "Unsupported fingerprint hash type '%s' received on dialog '%s'\n",
+				hash, p->callid);
+		}
+	}
+
+	return found;
+}
+
 static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newaudiortp, int *last_rtpmap_codec)
 {
 	int found = FALSE;
@@ -12050,6 +12174,49 @@ static void start_ice(struct ast_rtp_instance *instance)
 	ice->start(instance);
 }
 
+/*! \brief Add DTLS attributes to SDP */
+static void add_dtls_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf)
+{
+	struct ast_rtp_engine_dtls *dtls;
+	const char *fingerprint;
+
+	if (!instance || !(dtls = ast_rtp_instance_get_dtls(instance)) || !dtls->active(instance)) {
+		return;
+	}
+
+	switch (dtls->get_connection(instance)) {
+	case AST_RTP_DTLS_CONNECTION_NEW:
+		ast_str_append(a_buf, 0, "a=connection:new\r\n");
+		break;
+	case AST_RTP_DTLS_CONNECTION_EXISTING:
+		ast_str_append(a_buf, 0, "a=connection:existing\r\n");
+		break;
+	default:
+		break;
+	}
+
+	switch (dtls->get_setup(instance)) {
+	case AST_RTP_DTLS_SETUP_ACTIVE:
+		ast_str_append(a_buf, 0, "a=setup:active\r\n");
+		break;
+	case AST_RTP_DTLS_SETUP_PASSIVE:
+		ast_str_append(a_buf, 0, "a=setup:passive\r\n");
+		break;
+	case AST_RTP_DTLS_SETUP_ACTPASS:
+		ast_str_append(a_buf, 0, "a=setup:actpass\r\n");
+		break;
+	case AST_RTP_DTLS_SETUP_HOLDCONN:
+		ast_str_append(a_buf, 0, "a=setup:holdconn\r\n");
+		break;
+	default:
+		break;
+	}
+
+	if ((fingerprint = dtls->get_fingerprint(instance, AST_RTP_DTLS_HASH_SHA1))) {
+		ast_str_append(a_buf, 0, "a=fingerprint:SHA-1 %s\r\n", fingerprint);
+	}
+}
+
 /*! \brief Add codec offer to SDP offer/answer body in INVITE or 200 OK */
 static void add_codec_to_sdp(const struct sip_pvt *p,
 	struct ast_format *format,
@@ -12320,6 +12487,11 @@ static void get_crypto_attrib(struct sip_pvt *p, struct sip_srtp *srtp, const ch
 			srtp->crypto = sdp_crypto_setup();
 		}
 
+		if (p->dtls_cfg.enabled) {
+			/* If DTLS-SRTP is enabled the key details will be pulled from TLS */
+			return;
+		}
+
 		/* set the key length based on INVITE or settings */
 		if (ast_test_flag(srtp, SRTP_CRYPTO_TAG_80)) {
 			taglen = 80;
@@ -12338,12 +12510,18 @@ static void get_crypto_attrib(struct sip_pvt *p, struct sip_srtp *srtp, const ch
 	}
 }
 
-static char *get_sdp_rtp_profile(const struct sip_pvt *p, unsigned int secure)
+static char *get_sdp_rtp_profile(const struct sip_pvt *p, unsigned int secure, struct ast_rtp_instance *instance)
 {
-	if (ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
-		return secure ? "SAVPF" : "AVPF";
+	struct ast_rtp_engine_dtls *dtls;
+
+	if ((dtls = ast_rtp_instance_get_dtls(instance)) && dtls->active(instance)) {
+		return ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF) ? "UDP/TLS/RTP/SAVPF" : "UDP/TLS/RTP/SAVP";
 	} else {
-		return secure ? "SAVP" : "AVP";
+		if (ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+			return secure ? "RTP/SAVPF" : "RTP/AVPF";
+		} else {
+			return secure ? "RTP/SAVP" : "RTP/AVP";
+		}
 	}
 }
 
@@ -12506,8 +12684,8 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
 		   Video is handled differently than audio since we can not transcode. */
 		if (needvideo) {
 			get_crypto_attrib(p, p->vsrtp, &v_a_crypto);
-			ast_str_append(&m_video, 0, "m=video %d RTP/%s", ast_sockaddr_port(&vdest),
-				       get_sdp_rtp_profile(p, a_crypto ? 1 : 0));
+			ast_str_append(&m_video, 0, "m=video %d %s", ast_sockaddr_port(&vdest),
+				       get_sdp_rtp_profile(p, a_crypto ? 1 : 0, p->vrtp));
 
 			/* Build max bitrate string */
 			if (p->maxcallbitrate)
@@ -12516,8 +12694,12 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
 				ast_verbose("Video is at %s\n", ast_sockaddr_stringify(&vdest));
 			}
 
-			if (!doing_directmedia && ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
-				add_ice_to_sdp(p->vrtp, &a_video);
+			if (!doing_directmedia) {
+				if (ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
+					add_ice_to_sdp(p->vrtp, &a_video);
+				}
+
+				add_dtls_to_sdp(p->vrtp, &a_video);
 			}
 		}
 
@@ -12527,14 +12709,18 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
 			if (sipdebug_text)
 				ast_verbose("Lets set up the text sdp\n");
 			get_crypto_attrib(p, p->tsrtp, &t_a_crypto);
-			ast_str_append(&m_text, 0, "m=text %d RTP/%s", ast_sockaddr_port(&tdest),
-				       get_sdp_rtp_profile(p, a_crypto ? 1 : 0));
+			ast_str_append(&m_text, 0, "m=text %d %s", ast_sockaddr_port(&tdest),
+				       get_sdp_rtp_profile(p, a_crypto ? 1 : 0, p->trtp));
 			if (debug) {  /* XXX should I use tdest below ? */
 				ast_verbose("Text is at %s\n", ast_sockaddr_stringify(&taddr));
 			}
 
-			if (!doing_directmedia && ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
-				add_ice_to_sdp(p->trtp, &a_text);
+			if (!doing_directmedia) {
+				if (ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
+					add_ice_to_sdp(p->trtp, &a_text);
+				}
+
+				add_dtls_to_sdp(p->trtp, &a_text);
 			}
 		}
 
@@ -12544,8 +12730,8 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
 		   peer doesn't have to ast_gethostbyname() us */
 
 		get_crypto_attrib(p, p->srtp, &a_crypto);
-		ast_str_append(&m_audio, 0, "m=audio %d RTP/%s", ast_sockaddr_port(&dest),
-			       get_sdp_rtp_profile(p, a_crypto ? 1 : 0));
+		ast_str_append(&m_audio, 0, "m=audio %d %s", ast_sockaddr_port(&dest),
+			       get_sdp_rtp_profile(p, a_crypto ? 1 : 0, p->rtp));
 
 		/* Now, start adding audio codecs. These are added in this order:
 		   - First what was requested by the calling channel
@@ -12632,8 +12818,12 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
 		if (min_text_packet_size)
 			ast_str_append(&a_text, 0, "a=ptime:%d\r\n", min_text_packet_size);
 
-		if (!doing_directmedia && ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
-			add_ice_to_sdp(p->rtp, &a_audio);
+		if (!doing_directmedia) {
+			if (ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
+				add_ice_to_sdp(p->rtp, &a_audio);
+			}
+
+			add_dtls_to_sdp(p->rtp, &a_audio);
 		}
 
 		if (m_audio->len - m_audio->used < 2 || m_video->len - m_video->used < 2 ||
@@ -17345,6 +17535,8 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
 		set_t38_capabilities(p);
 	}
 
+	ast_rtp_dtls_cfg_copy(&peer->dtls_cfg, &p->dtls_cfg);
+
 	/* Copy SIP extensions profile to peer */
 	/* XXX is this correct before a successful auth ? */
 	if (p->sipoptions)
@@ -17391,6 +17583,8 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
 		ast_string_field_set(p, peername, peer->name);
 		ast_string_field_set(p, authname, peer->name);
 
+		ast_rtp_dtls_cfg_copy(&peer->dtls_cfg, &p->dtls_cfg);
+
 		if (sipmethod == SIP_INVITE) {
 			/* destroy old channel vars and copy in new ones. */
 			ast_variables_destroy(p->chanvars);
@@ -29965,9 +30159,14 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
 				ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_USE_AVPF);
 			} else if (!strcasecmp(v->name, "icesupport")) {
 				ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_ICE_SUPPORT);
+			} else {
+				ast_rtp_dtls_cfg_parse(&peer->dtls_cfg, v->name, v->value);
 			}
 		}
 
+		/* Apply the encryption tag length to the DTLS configuration, in case DTLS is in use */
+		peer->dtls_cfg.suite = (ast_test_flag(&peer->flags[2], SIP_PAGE3_SRTP_TAG_32) ? AST_AES_CM_128_HMAC_SHA1_32 : AST_AES_CM_128_HMAC_SHA1_80);
+
 		/* These apply to devstate lookups */
 		if (realtime && !strcasecmp(v->name, "lastms")) {
 			sscanf(v->value, "%30d", &peer->lastms);
@@ -32157,6 +32356,8 @@ static int setup_srtp(struct sip_srtp **srtp)
 
 static int process_crypto(struct sip_pvt *p, struct ast_rtp_instance *rtp, struct sip_srtp **srtp, const char *a)
 {
+	struct ast_rtp_engine_dtls *dtls;
+
 	/* If no RTP instance exists for this media stream don't bother processing the crypto line */
 	if (!rtp) {
 		ast_debug(3, "Received offer with crypto line for media stream that is not enabled\n");
@@ -32187,6 +32388,11 @@ static int process_crypto(struct sip_pvt *p, struct ast_rtp_instance *rtp, struc
 
 	ast_set_flag(*srtp, SRTP_CRYPTO_OFFER_OK);
 
+	if ((dtls = ast_rtp_instance_get_dtls(rtp))) {
+		dtls->stop(rtp);
+		p->dtls_cfg.enabled = 0;
+	}
+
 	return TRUE;
 }
 
diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h
index 047310fc4fa7ce2e7ec50e620ab0978f073c850f..48a5a3bb584a4e13de135c2d2a9e307516fca3aa 100644
--- a/channels/sip/include/sip.h
+++ b/channels/sip/include/sip.h
@@ -36,6 +36,7 @@
 #include "asterisk/security_events.h"
 #include "asterisk/features.h"
 #include "asterisk/http_websocket.h"
+#include "asterisk/rtp_engine.h"
 
 #ifndef FALSE
 #define FALSE    0
@@ -1212,6 +1213,8 @@ struct sip_pvt {
 	struct ast_cc_config_params *cc_params;
 	struct sip_epa_entry *epa_entry;
 	int fromdomainport;                 /*!< Domain port to show in from field */
+
+	struct ast_rtp_dtls_cfg dtls_cfg;
 };
 
 /*! \brief sip packet - raw format for outbound packets that are sent or scheduled for transmission
@@ -1361,6 +1364,8 @@ struct sip_peer {
 	enum sip_peer_type type; /*!< Distinguish between "user" and "peer" types. This is used solely for CLI and manager commands */
 	unsigned int disallowed_methods;
 	struct ast_cc_config_params *cc_params;
+
+	struct ast_rtp_dtls_cfg dtls_cfg;
 };
 
 /*!
diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample
index 81ca998d581bd0f21632e0c052bd5fcd7146b062..6c3df58b334a70be34c2aa82e00161599bd4e113 100644
--- a/configs/sip.conf.sample
+++ b/configs/sip.conf.sample
@@ -1240,6 +1240,38 @@ srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
 ; maxforwards
 ; encryption
 ; description		; Used to provide a description of the peer in console output
+; dtlsenable
+; dtlsverify
+; dtlsrekey
+; dtlscertfile
+; dtlsprivatekey
+; dtlscipher
+; dtlscafile
+; dtlscapath
+; dtlssetup
+;
+
+;------------------------------------------------------------------------------
+; DTLS-SRTP CONFIGURATION
+;
+; DTLS-SRTP support is available if the underlying RTP engine in use supports it.
+;
+; dtlsenable = yes                   ; Enable or disable DTLS-SRTP support
+; dtlsverify = yes                   ; Verify that the provided peer certificate is valid
+; dtlsrekey = 60                     ; Interval at which to renegotiate the TLS session and rekey the SRTP session
+;                                    ; If this is not set or the value provided is 0 rekeying will be disabled
+; dtlscertfile = file                ; Path to certificate file to present
+; dtlsprivatekey = file              ; Path to private key for certificate file
+; dtlscipher = <SSL cipher string>   ; Cipher to use for TLS negotiation
+;                                    ; A list of valid SSL cipher strings can be found at:
+;                                    ; http://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS
+; dtlscafile = file                  ; Path to certificate authority certificate
+; dtlscapath = path                  ; Path to a directory containing certificate authority certificates
+; dtlssetup = actpass                ; Whether we are willing to accept connections, connect to the other party, or both.
+;                                    ; Valid options are active (we want to connect to the other party), passive (we want to
+;                                    ; accept connections only), and actpass (we will do both). This value will be used in
+;                                    ; the outgoing SDP when offering and for incoming SDP offers when the remote party sends
+;                                    ; actpass
 
 ;[sip_proxy]
 ; For incoming calls only. Example: FWD (Free World Dialup)
diff --git a/configure b/configure
index d6d26367b9ae1e48f8413e2d5474260d3882539b..20b47bc3558448d5b654e598e1df328ca5c69d7c 100755
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
 #! /bin/sh
-# From configure.ac Revision: 371030 .
+# From configure.ac Revision: 373119 .
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.68 for asterisk trunk.
 #
@@ -1018,6 +1018,10 @@ PBX_DAHDI
 DAHDI_DIR
 DAHDI_INCLUDE
 DAHDI_LIB
+PBX_OPENSSL_SRTP
+OPENSSL_SRTP_DIR
+OPENSSL_SRTP_INCLUDE
+OPENSSL_SRTP_LIB
 PBX_CRYPTO
 CRYPTO_DIR
 CRYPTO_INCLUDE
@@ -8277,6 +8281,18 @@ fi
 
 
 
+OPENSSL_SRTP_DESCRIP="OpenSSL SRTP Extension Support"
+OPENSSL_SRTP_OPTION=crypto
+OPENSSL_SRTP_DIR=${CRYPTO_DIR}
+
+PBX_OPENSSL_SRTP=0
+
+
+
+
+
+
+
     DAHDI_DESCRIP="DAHDI"
     DAHDI_OPTION="dahdi"
     PBX_DAHDI=0
@@ -27448,6 +27464,110 @@ $as_echo "no" >&6; }
 		fi
 	fi
 
+
+if test "x${PBX_OPENSSL_SRTP}" != "x1" -a "${USE_OPENSSL_SRTP}" != "no"; then
+   pbxlibdir=""
+   # if --with-OPENSSL_SRTP=DIR has been specified, use it.
+   if test "x${OPENSSL_SRTP_DIR}" != "x"; then
+      if test -d ${OPENSSL_SRTP_DIR}/lib; then
+      	 pbxlibdir="-L${OPENSSL_SRTP_DIR}/lib"
+      else
+      	 pbxlibdir="-L${OPENSSL_SRTP_DIR}"
+      fi
+   fi
+   pbxfuncname="SSL_CTX_set_tlsext_use_srtp"
+   if test "x${pbxfuncname}" = "x" ; then   # empty lib, assume only headers
+      AST_OPENSSL_SRTP_FOUND=yes
+   else
+      ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
+      CFLAGS="${CFLAGS} "
+      as_ac_Lib=`$as_echo "ac_cv_lib_ssl_${pbxfuncname}" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lssl" >&5
+$as_echo_n "checking for ${pbxfuncname} in -lssl... " >&6; }
+if eval \${$as_ac_Lib+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lssl ${pbxlibdir} -lcrypto $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  eval "$as_ac_Lib=yes"
+else
+  eval "$as_ac_Lib=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+eval ac_res=\$$as_ac_Lib
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
+  AST_OPENSSL_SRTP_FOUND=yes
+else
+  AST_OPENSSL_SRTP_FOUND=no
+fi
+
+      CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
+   fi
+
+   # now check for the header.
+   if test "${AST_OPENSSL_SRTP_FOUND}" = "yes"; then
+      OPENSSL_SRTP_LIB="${pbxlibdir} -lssl -lcrypto"
+      # if --with-OPENSSL_SRTP=DIR has been specified, use it.
+      if test "x${OPENSSL_SRTP_DIR}" != "x"; then
+         OPENSSL_SRTP_INCLUDE="-I${OPENSSL_SRTP_DIR}/include"
+      fi
+      OPENSSL_SRTP_INCLUDE="${OPENSSL_SRTP_INCLUDE} "
+      if test "xopenssl/ssl.h" = "x" ; then	# no header, assume found
+         OPENSSL_SRTP_HEADER_FOUND="1"
+      else				# check for the header
+         ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
+         CPPFLAGS="${CPPFLAGS} ${OPENSSL_SRTP_INCLUDE}"
+         ac_fn_c_check_header_mongrel "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default"
+if test "x$ac_cv_header_openssl_ssl_h" = xyes; then :
+  OPENSSL_SRTP_HEADER_FOUND=1
+else
+  OPENSSL_SRTP_HEADER_FOUND=0
+fi
+
+
+         CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
+      fi
+      if test "x${OPENSSL_SRTP_HEADER_FOUND}" = "x0" ; then
+         OPENSSL_SRTP_LIB=""
+         OPENSSL_SRTP_INCLUDE=""
+      else
+         if test "x${pbxfuncname}" = "x" ; then		# only checking headers -> no library
+            OPENSSL_SRTP_LIB=""
+         fi
+         PBX_OPENSSL_SRTP=1
+         cat >>confdefs.h <<_ACEOF
+#define HAVE_OPENSSL_SRTP 1
+_ACEOF
+
+      fi
+   fi
+fi
+
+
 fi
 
 
diff --git a/configure.ac b/configure.ac
index 5d56dfa76b8a0d0234417dc8c5cdf85194e2037c..b6fe5ee1f5e80e0a087c659ddaab89ca7cde3d10 100644
--- a/configure.ac
+++ b/configure.ac
@@ -387,6 +387,7 @@ AST_EXT_LIB_SETUP([COROSYNC], [Corosync], [cpg])
 AST_EXT_LIB_SETUP_OPTIONAL([COROSYNC_CFG_STATE_TRACK], [A callback only in corosync 1.x], [COROSYNC], [cfg])
 AST_EXT_LIB_SETUP([CURSES], [curses], [curses])
 AST_EXT_LIB_SETUP([CRYPTO], [OpenSSL Cryptography], [crypto])
+AST_EXT_LIB_SETUP_OPTIONAL([OPENSSL_SRTP], [OpenSSL SRTP Extension Support], [CRYPTO], [crypto])
 AST_EXT_LIB_SETUP([DAHDI], [DAHDI], [dahdi])
 AST_EXT_LIB_SETUP([FFMPEG], [Ffmpeg and avcodec], [avcodec])
 AST_EXT_LIB_SETUP([GSM], [External GSM], [gsm], [, use 'internal' GSM otherwise])
@@ -2103,7 +2104,8 @@ fi
 
 if test "$PBX_OPENSSL" = "1";
 then
-    AST_CHECK_OSPTK([4], [0], [0])
+        AST_CHECK_OSPTK([4], [0], [0])
+        AST_EXT_LIB_CHECK([OPENSSL_SRTP], [ssl], [SSL_CTX_set_tlsext_use_srtp], [openssl/ssl.h], [-lcrypto])
 fi
 
 AST_EXT_LIB_CHECK([SRTP], [srtp], [srtp_init], [srtp/srtp.h])
diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in
index 9288cce545c276ccce244a98ead4f2c382251734..e6835a03040f0e45e1ffb3f2593fc1891722ef6c 100644
--- a/include/asterisk/autoconfig.h.in
+++ b/include/asterisk/autoconfig.h.in
@@ -527,6 +527,9 @@
 /* Define to 1 if you have the OpenSSL Secure Sockets Layer library. */
 #undef HAVE_OPENSSL
 
+/* Define to 1 if CRYPTO has the OpenSSL SRTP Extension Support feature. */
+#undef HAVE_OPENSSL_SRTP
+
 /* Define this to indicate the ${OSPTK_DESCRIP} library */
 #undef HAVE_OSPTK
 
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index 76031077cd680fc4758a6014b1522734d9087fdf..51455e92ac321bd257da30dc7a31dee872660b8c 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -353,6 +353,61 @@ struct ast_rtp_engine_ice {
 	void (*ice_lite)(struct ast_rtp_instance *instance);
 };
 
+/*! \brief DTLS setup types */
+enum ast_rtp_dtls_setup {
+	AST_RTP_DTLS_SETUP_ACTIVE,   /*!< Endpoint is willing to inititate connections */
+	AST_RTP_DTLS_SETUP_PASSIVE,  /*!< Endpoint is willing to accept connections */
+	AST_RTP_DTLS_SETUP_ACTPASS,  /*!< Endpoint is willing to both accept and initiate connections */
+	AST_RTP_DTLS_SETUP_HOLDCONN, /*!< Endpoint does not want the connection to be established right now */
+};
+
+/*! \brief DTLS connection states */
+enum ast_rtp_dtls_connection {
+        AST_RTP_DTLS_CONNECTION_NEW,      /*!< Endpoint wants to use a new connection */
+	AST_RTP_DTLS_CONNECTION_EXISTING, /*!< Endpoint wishes to use existing connection */
+};
+
+/*! \brief DTLS fingerprint hashes */
+enum ast_rtp_dtls_hash {
+	AST_RTP_DTLS_HASH_SHA1, /*!< SHA-1 fingerprint hash */
+};
+
+/*! \brief DTLS configuration structure */
+struct ast_rtp_dtls_cfg {
+	unsigned int enabled:1;                /*!< Whether DTLS support is enabled or not */
+	unsigned int verify:1;                 /*!< Whether to request and verify a client certificate when acting as server */
+	unsigned int rekey;                    /*!< Interval at which to renegotiate and rekey - defaults to 0 (off) */
+	enum ast_rtp_dtls_setup default_setup; /*!< Default setup type to use for outgoing */
+	enum ast_srtp_suite suite;             /*!< Crypto suite in use */
+	char *certfile;                        /*!< Certificate file */
+	char *pvtfile;                         /*!< Private key file */
+	char *cipher;                          /*!< Cipher to use */
+	char *cafile;                          /*!< Certificate authority file */
+	char *capath;                          /*!< Path to certificate authority */
+};
+
+/*! \brief Structure that represents the optional DTLS SRTP support within an RTP engine */
+struct ast_rtp_engine_dtls {
+	/*! Set the configuration of the DTLS support on the instance */
+	int (*set_configuration)(struct ast_rtp_instance *instance, const struct ast_rtp_dtls_cfg *dtls_cfg);
+	/*! Get if the DTLS SRTP support is active or not */
+	int (*active)(struct ast_rtp_instance *instance);
+	/*! Stop and terminate DTLS SRTP support */
+	void (*stop)(struct ast_rtp_instance *instance);
+	/*! Reset the connection and start fresh */
+	void (*reset)(struct ast_rtp_instance *instance);
+	/*! Get the current connection state */
+	enum ast_rtp_dtls_connection (*get_connection)(struct ast_rtp_instance *instance);
+	/*! Get the current setup state */
+	enum ast_rtp_dtls_setup (*get_setup)(struct ast_rtp_instance *instance);
+	/*! Set the remote setup state */
+	void (*set_setup)(struct ast_rtp_instance *instance, enum ast_rtp_dtls_setup setup);
+	/*! Set the remote fingerprint */
+	void (*set_fingerprint)(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash, const char *fingerprint);
+	/*! Get the local fingerprint */
+	const char *(*get_fingerprint)(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash);
+};
+
 /*! Structure that represents an RTP stack (engine) */
 struct ast_rtp_engine {
 	/*! Name of the RTP engine, used when explicitly requested */
@@ -426,6 +481,8 @@ struct ast_rtp_engine {
 	int (*sendcng)(struct ast_rtp_instance *instance, int level);
 	/*! Callback to pointer for optional ICE support */
 	struct ast_rtp_engine_ice *ice;
+	/*! Callback to pointer for optional DTLS SRTP support */
+	struct ast_rtp_engine_dtls *dtls;
 	/*! Linked list information */
 	AST_RWLIST_ENTRY(ast_rtp_engine) entry;
 };
@@ -2014,6 +2071,43 @@ int ast_rtp_engine_unload_format(const struct ast_format *format);
  */
 struct ast_rtp_engine_ice *ast_rtp_instance_get_ice(struct ast_rtp_instance *instance);
 
+/*!
+ * \brief Obtain a pointer to the DTLS support present on an RTP instance
+ *
+ * \param instance the RTP instance
+ *
+ * \retval DTLS support if present
+ * \retval NULL if no DTLS support available
+ */
+struct ast_rtp_engine_dtls *ast_rtp_instance_get_dtls(struct ast_rtp_instance *instance);
+
+/*!
+ * \brief Parse DTLS related configuration options
+ *
+ * \param dtls_cfg a DTLS configuration structure
+ * \param name name of the configuration option
+ * \param value value of the configuration option
+ *
+ * \retval 0 if handled
+ * \retval -1 if not handled
+ */
+int ast_rtp_dtls_cfg_parse(struct ast_rtp_dtls_cfg *dtls_cfg, const char *name, const char *value);
+
+/*!
+ * \brief Copy contents of a DTLS configuration structure
+ *
+ * \param src_cfg source DTLS configuration structure
+ * \param dst_cfg destination DTLS configuration structure
+ */
+void ast_rtp_dtls_cfg_copy(const struct ast_rtp_dtls_cfg *src_cfg, struct ast_rtp_dtls_cfg *dst_cfg);
+
+/*!
+ * \brief Free contents of a DTLS configuration structure
+ *
+ * \param dtls_cfg a DTLS configuration structure
+ */
+void ast_rtp_dtls_cfg_free(struct ast_rtp_dtls_cfg *dtls_cfg);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index 90e1abb45462bbacbe78cdaf327ab84d886bfe97..4bcdb138ae162daf76ef15780412fc33c3171371 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -2058,6 +2058,74 @@ struct ast_rtp_engine_ice *ast_rtp_instance_get_ice(struct ast_rtp_instance *ins
 	return instance->engine->ice;
 }
 
+struct ast_rtp_engine_dtls *ast_rtp_instance_get_dtls(struct ast_rtp_instance *instance)
+{
+	return instance->engine->dtls;
+}
+
+int ast_rtp_dtls_cfg_parse(struct ast_rtp_dtls_cfg *dtls_cfg, const char *name, const char *value)
+{
+	if (!strcasecmp(name, "dtlsenable")) {
+		dtls_cfg->enabled = ast_true(value) ? 1 : 0;
+	} else if (!strcasecmp(name, "dtlsverify")) {
+		dtls_cfg->verify = ast_true(value) ? 1 : 0;
+	} else if (!strcasecmp(name, "dtlsrekey")) {
+		if (sscanf(value, "%30u", &dtls_cfg->rekey) != 1) {
+			return -1;
+		}
+	} else if (!strcasecmp(name, "dtlscertfile")) {
+		ast_free(dtls_cfg->certfile);
+		dtls_cfg->certfile = ast_strdup(value);
+	} else if (!strcasecmp(name, "dtlsprivatekey")) {
+		ast_free(dtls_cfg->pvtfile);
+		dtls_cfg->pvtfile = ast_strdup(value);
+	} else if (!strcasecmp(name, "dtlscipher")) {
+		ast_free(dtls_cfg->cipher);
+		dtls_cfg->cipher = ast_strdup(value);
+	} else if (!strcasecmp(name, "dtlscafile")) {
+		ast_free(dtls_cfg->cafile);
+		dtls_cfg->cafile = ast_strdup(value);
+	} else if (!strcasecmp(name, "dtlscapath") || !strcasecmp(name, "dtlscadir")) {
+		ast_free(dtls_cfg->capath);
+		dtls_cfg->capath = ast_strdup(value);
+	} else if (!strcasecmp(name, "dtlssetup")) {
+		if (!strcasecmp(value, "active")) {
+			dtls_cfg->default_setup = AST_RTP_DTLS_SETUP_ACTIVE;
+		} else if (!strcasecmp(value, "passive")) {
+			dtls_cfg->default_setup = AST_RTP_DTLS_SETUP_PASSIVE;
+		} else if (!strcasecmp(value, "actpass")) {
+			dtls_cfg->default_setup = AST_RTP_DTLS_SETUP_ACTPASS;
+		}
+	} else {
+		return -1;
+	}
+
+	return 0;
+}
+
+void ast_rtp_dtls_cfg_copy(const struct ast_rtp_dtls_cfg *src_cfg, struct ast_rtp_dtls_cfg *dst_cfg)
+{
+	dst_cfg->enabled = src_cfg->enabled;
+	dst_cfg->verify = src_cfg->verify;
+	dst_cfg->rekey = src_cfg->rekey;
+	dst_cfg->suite = src_cfg->suite;
+	dst_cfg->certfile = ast_strdup(src_cfg->certfile);
+	dst_cfg->pvtfile = ast_strdup(src_cfg->pvtfile);
+	dst_cfg->cipher = ast_strdup(src_cfg->cipher);
+	dst_cfg->cafile = ast_strdup(src_cfg->cafile);
+	dst_cfg->capath = ast_strdup(src_cfg->capath);
+	dst_cfg->default_setup = src_cfg->default_setup;
+}
+
+void ast_rtp_dtls_cfg_free(struct ast_rtp_dtls_cfg *dtls_cfg)
+{
+	ast_free(dtls_cfg->certfile);
+	ast_free(dtls_cfg->pvtfile);
+	ast_free(dtls_cfg->cipher);
+	ast_free(dtls_cfg->cafile);
+	ast_free(dtls_cfg->capath);
+}
+
 static void set_next_mime_type(const struct ast_format *format, int rtp_code, char *type, char *subtype, unsigned int sample_rate)
 {
 	int x = mime_types_len;
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index 9283f8083760076b8dc788a39170745a4beaa20c..958d5831c9c65c6bde9809fe2a9ada51ada14017 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -40,6 +40,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include <signal.h>
 #include <fcntl.h>
 
+#ifdef HAVE_OPENSSL_SRTP
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/bio.h>
+#endif
+
 /* Asterisk discourages the use of bzero in favor of memset, in fact if you try to use bzero it will tell you to use memset. As a result bzero has to be undefined
  * here since it is used internally by pjlib. The only other option would be to modify pjlib... which won't happen. */
 #undef bzero
@@ -94,6 +100,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #define DEFAULT_LEARNING_MIN_SEQUENTIAL 4
 
+#define SRTP_MASTER_KEY_LEN 16
+#define SRTP_MASTER_SALT_LEN 14
+#define SRTP_MASTER_LEN (SRTP_MASTER_KEY_LEN + SRTP_MASTER_SALT_LEN)
+
 enum strict_rtp_state {
 	STRICT_RTP_OPEN = 0, /*! No RTP packets should be dropped, all sources accepted */
 	STRICT_RTP_LEARN,    /*! Accept next packet as source */
@@ -104,6 +114,8 @@ enum strict_rtp_state {
 #define DEFAULT_ICESUPPORT 1
 
 extern struct ast_srtp_res *res_srtp;
+extern struct ast_srtp_policy_res *res_srtp_policy;
+
 static int dtmftimeout = DEFAULT_DTMF_TIMEOUT;
 
 static int rtpstart = DEFAULT_RTP_START;			/*!< First port for RTP sessions (set in rtp.conf) */
@@ -253,6 +265,21 @@ struct ast_rtp {
 
 	struct ao2_container *local_candidates;   /*!< The local ICE candidates */
 	struct ao2_container *remote_candidates;  /*!< The remote ICE candidates */
+
+#ifdef HAVE_OPENSSL_SRTP
+	SSL_CTX *ssl_ctx; /*!< SSL context */
+	SSL *ssl;         /*!< SSL session */
+	BIO *read_bio;    /*!< Memory buffer for reading */
+	BIO *write_bio;   /*!< Memory buffer for writing */
+	enum ast_rtp_dtls_setup dtls_setup; /*!< Current setup state */
+	enum ast_srtp_suite suite;   /*!< SRTP crypto suite */
+	char local_fingerprint[160]; /*!< Fingerprint of our certificate */
+	unsigned char remote_fingerprint[EVP_MAX_MD_SIZE]; /*!< Fingerprint of the peer certificate */
+	enum ast_rtp_dtls_connection connection; /*!< Whether this is a new or existing connection */
+	unsigned int dtls_failure:1; /*!< Failure occurred during DTLS negotiation */
+	unsigned int rekey; /*!< Interval at which to renegotiate and rekey */
+	int rekeyid; /*!< Scheduled item id for rekeying */
+#endif
 };
 
 /*!
@@ -359,6 +386,12 @@ static void ast_rtp_stop(struct ast_rtp_instance *instance);
 static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, const char* desc);
 static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level);
 
+#ifdef HAVE_OPENSSL_SRTP
+static int ast_rtp_activate(struct ast_rtp_instance *instance);
+#endif
+
+static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice, int use_srtp);
+
 /*! \brief Destructor for locally created ICE candidates */
 static void ast_rtp_ice_candidate_destroy(void *obj)
 {
@@ -657,6 +690,305 @@ static struct ast_rtp_engine_ice ast_rtp_ice = {
 	.ice_lite = ast_rtp_ice_lite,
 };
 
+#ifdef HAVE_OPENSSL_SRTP
+static void dtls_info_callback(const SSL *ssl, int where, int ret)
+{
+	struct ast_rtp *rtp = SSL_get_ex_data(ssl, 0);
+
+	/* We only care about alerts */
+	if (!(where & SSL_CB_ALERT)) {
+		return;
+	}
+
+	rtp->dtls_failure = 1;
+}
+
+static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, const struct ast_rtp_dtls_cfg *dtls_cfg)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	if (!dtls_cfg->enabled) {
+		return 0;
+	}
+
+	if (!ast_rtp_engine_srtp_is_registered()) {
+		return -1;
+	}
+
+	if (!(rtp->ssl_ctx = SSL_CTX_new(DTLSv1_method()))) {
+		return -1;
+	}
+
+	SSL_CTX_set_verify(rtp->ssl_ctx, dtls_cfg->verify ? SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT : SSL_VERIFY_NONE, NULL);
+
+	if (dtls_cfg->suite == AST_AES_CM_128_HMAC_SHA1_80) {
+		SSL_CTX_set_tlsext_use_srtp(rtp->ssl_ctx, "SRTP_AES128_CM_SHA1_80");
+	} else if (dtls_cfg->suite == AST_AES_CM_128_HMAC_SHA1_32) {
+		SSL_CTX_set_tlsext_use_srtp(rtp->ssl_ctx, "SRTP_AES128_CM_SHA1_32");
+	} else {
+		ast_log(LOG_ERROR, "Unsupported suite specified for DTLS-SRTP on RTP instance '%p'\n", instance);
+		goto error;
+	}
+
+	if (!ast_strlen_zero(dtls_cfg->certfile)) {
+		char *private = ast_strlen_zero(dtls_cfg->pvtfile) ? dtls_cfg->certfile : dtls_cfg->pvtfile;
+		BIO *certbio;
+		X509 *cert;
+		unsigned int size, i;
+		unsigned char fingerprint[EVP_MAX_MD_SIZE];
+		char *local_fingerprint = rtp->local_fingerprint;
+
+		if (!SSL_CTX_use_certificate_file(rtp->ssl_ctx, dtls_cfg->certfile, SSL_FILETYPE_PEM)) {
+			ast_log(LOG_ERROR, "Specified certificate file '%s' for RTP instance '%p' could not be used\n",
+				dtls_cfg->certfile, instance);
+			goto error;
+		}
+
+		if (!SSL_CTX_use_PrivateKey_file(rtp->ssl_ctx, private, SSL_FILETYPE_PEM) ||
+		    !SSL_CTX_check_private_key(rtp->ssl_ctx)) {
+			ast_log(LOG_ERROR, "Specified private key file '%s' for RTP instance '%p' could not be used\n",
+				private, instance);
+			goto error;
+		}
+
+		if (!(certbio = BIO_new(BIO_s_file()))) {
+			ast_log(LOG_ERROR, "Failed to allocate memory for certificate fingerprinting on RTP instance '%p'\n",
+				instance);
+			goto error;
+		}
+
+		if (!BIO_read_filename(certbio, dtls_cfg->certfile) ||
+		    !(cert = PEM_read_bio_X509(certbio, NULL, 0, NULL)) ||
+		    !X509_digest(cert, EVP_sha1(), fingerprint, &size) ||
+		    !size) {
+			ast_log(LOG_ERROR, "Could not produce fingerprint from certificate '%s' for RTP instance '%p'\n",
+				dtls_cfg->certfile, instance);
+			BIO_free_all(certbio);
+			goto error;
+		}
+
+		for (i = 0; i < size; i++) {
+			sprintf(local_fingerprint, "%.2X:", fingerprint[i]);
+			local_fingerprint += 3;
+		}
+
+		*(local_fingerprint-1) = 0;
+
+		BIO_free_all(certbio);
+	}
+
+	if (!ast_strlen_zero(dtls_cfg->cipher)) {
+		if (!SSL_CTX_set_cipher_list(rtp->ssl_ctx, dtls_cfg->cipher)) {
+			ast_log(LOG_ERROR, "Invalid cipher specified in cipher list '%s' for RTP instance '%p'\n",
+				dtls_cfg->cipher, instance);
+			goto error;
+		}
+	}
+
+	if (!ast_strlen_zero(dtls_cfg->cafile) || !ast_strlen_zero(dtls_cfg->capath)) {
+		if (!SSL_CTX_load_verify_locations(rtp->ssl_ctx, S_OR(dtls_cfg->cafile, NULL), S_OR(dtls_cfg->capath, NULL))) {
+			ast_log(LOG_ERROR, "Invalid certificate authority file '%s' or path '%s' specified for RTP instance '%p'\n",
+				S_OR(dtls_cfg->cafile, ""), S_OR(dtls_cfg->capath, ""), instance);
+			goto error;
+		}
+	}
+
+	rtp->rekey = dtls_cfg->rekey;
+	rtp->dtls_setup = dtls_cfg->default_setup;
+	rtp->suite = dtls_cfg->suite;
+
+	if (!(rtp->ssl = SSL_new(rtp->ssl_ctx))) {
+		ast_log(LOG_ERROR, "Failed to allocate memory for SSL context on RTP instance '%p'\n",
+			instance);
+		goto error;
+	}
+
+	SSL_set_ex_data(rtp->ssl, 0, rtp);
+	SSL_set_info_callback(rtp->ssl, dtls_info_callback);
+
+	if (!(rtp->read_bio = BIO_new(BIO_s_mem()))) {
+		ast_log(LOG_ERROR, "Failed to allocate memory for inbound SSL traffic on RTP instance '%p'\n",
+			instance);
+		goto error;
+	}
+	BIO_set_mem_eof_return(rtp->read_bio, -1);
+
+	if (!(rtp->write_bio = BIO_new(BIO_s_mem()))) {
+		ast_log(LOG_ERROR, "Failed to allocate memory for outbound SSL traffic on RTP instance '%p'\n",
+			instance);
+		goto error;
+	}
+	BIO_set_mem_eof_return(rtp->write_bio, -1);
+
+	SSL_set_bio(rtp->ssl, rtp->read_bio, rtp->write_bio);
+
+	if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) {
+		SSL_set_accept_state(rtp->ssl);
+	} else {
+		SSL_set_connect_state(rtp->ssl);
+	}
+
+	rtp->connection = AST_RTP_DTLS_CONNECTION_NEW;
+
+	return 0;
+
+error:
+	if (rtp->read_bio) {
+		BIO_free(rtp->read_bio);
+		rtp->read_bio = NULL;
+	}
+
+	if (rtp->write_bio) {
+		BIO_free(rtp->write_bio);
+		rtp->write_bio = NULL;
+	}
+
+	if (rtp->ssl) {
+		SSL_free(rtp->ssl);
+		rtp->ssl = NULL;
+	}
+
+	SSL_CTX_free(rtp->ssl_ctx);
+	rtp->ssl_ctx = NULL;
+
+	return -1;
+}
+
+static int ast_rtp_dtls_active(struct ast_rtp_instance *instance)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	return !rtp->ssl_ctx ? 0 : 1;
+}
+
+static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	if (rtp->ssl_ctx) {
+		SSL_CTX_free(rtp->ssl_ctx);
+		rtp->ssl_ctx = NULL;
+	}
+
+	if (rtp->ssl) {
+		SSL_free(rtp->ssl);
+		rtp->ssl = NULL;
+	}
+}
+
+static void ast_rtp_dtls_reset(struct ast_rtp_instance *instance)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	/* If the SSL session is not yet finalized don't bother resetting */
+	if (!SSL_is_init_finished(rtp->ssl)) {
+		return;
+	}
+
+	SSL_shutdown(rtp->ssl);
+	rtp->connection = AST_RTP_DTLS_CONNECTION_NEW;
+}
+
+static enum ast_rtp_dtls_connection ast_rtp_dtls_get_connection(struct ast_rtp_instance *instance)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	return rtp->connection;
+}
+
+static enum ast_rtp_dtls_setup ast_rtp_dtls_get_setup(struct ast_rtp_instance *instance)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	return rtp->dtls_setup;
+}
+
+static void ast_rtp_dtls_set_setup(struct ast_rtp_instance *instance, enum ast_rtp_dtls_setup setup)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+	enum ast_rtp_dtls_setup old = rtp->dtls_setup;
+
+	switch (setup) {
+	case AST_RTP_DTLS_SETUP_ACTIVE:
+		rtp->dtls_setup = AST_RTP_DTLS_SETUP_PASSIVE;
+		break;
+	case AST_RTP_DTLS_SETUP_PASSIVE:
+		rtp->dtls_setup = AST_RTP_DTLS_SETUP_ACTIVE;
+		break;
+	case AST_RTP_DTLS_SETUP_ACTPASS:
+		/* We can't respond to an actpass setup with actpass ourselves... so respond with active, as we can initiate connections */
+		if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTPASS) {
+			rtp->dtls_setup = AST_RTP_DTLS_SETUP_ACTIVE;
+		}
+		break;
+	case AST_RTP_DTLS_SETUP_HOLDCONN:
+		rtp->dtls_setup = AST_RTP_DTLS_SETUP_HOLDCONN;
+		break;
+	default:
+		/* This should never occur... if it does exit early as we don't know what state things are in */
+		return;
+	}
+
+	/* If the setup state did not change we go on as if nothing happened */
+	if (old == rtp->dtls_setup) {
+		return;
+	}
+
+	/* If they don't want us to establish a connection wait until later */
+	if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_HOLDCONN) {
+		return;
+	}
+
+	if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTIVE) {
+		SSL_set_connect_state(rtp->ssl);
+	} else if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) {
+		SSL_set_accept_state(rtp->ssl);
+	} else {
+		return;
+	}
+}
+
+static void ast_rtp_dtls_set_fingerprint(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash, const char *fingerprint)
+{
+	char *tmp = ast_strdupa(fingerprint), *value;
+	int pos = 0;
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	if (hash != AST_RTP_DTLS_HASH_SHA1) {
+		return;
+	}
+
+	while ((value = strsep(&tmp, ":")) && (pos != (EVP_MAX_MD_SIZE - 1))) {
+		sscanf(value, "%02x", (unsigned int*)&rtp->remote_fingerprint[pos++]);
+	}
+}
+
+static const char *ast_rtp_dtls_get_fingerprint(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	if (hash != AST_RTP_DTLS_HASH_SHA1) {
+		return NULL;
+	}
+
+	return rtp->local_fingerprint;
+}
+
+/* DTLS RTP Engine interface declaration */
+static struct ast_rtp_engine_dtls ast_rtp_dtls = {
+	.set_configuration = ast_rtp_dtls_set_configuration,
+	.active = ast_rtp_dtls_active,
+	.stop = ast_rtp_dtls_stop,
+	.reset = ast_rtp_dtls_reset,
+	.get_connection = ast_rtp_dtls_get_connection,
+	.get_setup = ast_rtp_dtls_get_setup,
+	.set_setup = ast_rtp_dtls_set_setup,
+	.set_fingerprint = ast_rtp_dtls_set_fingerprint,
+	.get_fingerprint = ast_rtp_dtls_get_fingerprint,
+};
+
+#endif
+
 /* RTP Engine Declaration */
 static struct ast_rtp_engine asterisk_rtp_engine = {
 	.name = "asterisk",
@@ -685,6 +1017,10 @@ static struct ast_rtp_engine asterisk_rtp_engine = {
 	.qos = ast_rtp_qos_set,
 	.sendcng = ast_rtp_sendcng,
 	.ice = &ast_rtp_ice,
+#ifdef HAVE_OPENSSL_SRTP
+	.dtls = &ast_rtp_dtls,
+	.activate = ast_rtp_activate,
+#endif
 };
 
 static void ast_rtp_on_ice_rx_data(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len)
@@ -874,6 +1210,168 @@ static inline int rtcp_debug_test_addr(struct ast_sockaddr *addr)
 	return 1;
 }
 
+#ifdef HAVE_OPENSSL_SRTP
+static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp)
+{
+	size_t pending = BIO_ctrl_pending(rtp->write_bio);
+
+	if (pending > 0) {
+		char outgoing[pending];
+		size_t out;
+		struct ast_sockaddr remote_address = { {0, } };
+		int ice;
+
+		ast_rtp_instance_get_remote_address(instance, &remote_address);
+
+		/* If we do not yet know an address to send this to defer it until we do */
+		if (ast_sockaddr_isnull(&remote_address)) {
+			return;
+		}
+
+		out = BIO_read(rtp->write_bio, outgoing, sizeof(outgoing));
+
+		__rtp_sendto(instance, outgoing, out, 0, &remote_address, 0, &ice, 0);
+	}
+}
+
+static int dtls_srtp_renegotiate(const void *data)
+{
+	struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data;
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	SSL_renegotiate(rtp->ssl);
+	SSL_do_handshake(rtp->ssl);
+	dtls_srtp_check_pending(instance, rtp);
+
+	rtp->rekeyid = -1;
+	ao2_ref(instance, -1);
+
+	return 0;
+}
+
+static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct ast_rtp_instance *instance)
+{
+	unsigned char material[SRTP_MASTER_LEN * 2];
+	unsigned char *local_key, *local_salt, *remote_key, *remote_salt;
+	struct ast_srtp_policy *local_policy, *remote_policy = NULL;
+	struct ast_rtp_instance_stats stats = { 0, };
+
+	/* If a fingerprint is present in the SDP make sure that the peer certificate matches it */
+	if (SSL_CTX_get_verify_mode(rtp->ssl_ctx) != SSL_VERIFY_NONE) {
+		X509 *certificate;
+
+		if (!(certificate = SSL_get_peer_certificate(rtp->ssl))) {
+			ast_log(LOG_WARNING, "No certificate was provided by the peer on RTP instance '%p'\n", instance);
+			return -1;
+		}
+
+		/* If a fingerprint is present in the SDP make sure that the peer certificate matches it */
+		if (rtp->remote_fingerprint[0]) {
+			unsigned char fingerprint[EVP_MAX_MD_SIZE];
+			unsigned int size;
+
+			if (!X509_digest(certificate, EVP_sha1(), fingerprint, &size) ||
+			    !size ||
+			    memcmp(fingerprint, rtp->remote_fingerprint, size)) {
+				X509_free(certificate);
+				ast_log(LOG_WARNING, "Fingerprint provided by remote party does not match that of peer certificate on RTP instance '%p'\n",
+					instance);
+				return -1;
+			}
+		}
+
+		X509_free(certificate);
+	}
+
+	/* Ensure that certificate verification was successful */
+	if (SSL_get_verify_result(rtp->ssl) != X509_V_OK) {
+		ast_log(LOG_WARNING, "Peer certificate on RTP instance '%p' failed verification test\n",
+			instance);
+		return -1;
+	}
+
+	/* Produce key information and set up SRTP */
+	if (!SSL_export_keying_material(rtp->ssl, material, SRTP_MASTER_LEN * 2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) {
+		ast_log(LOG_WARNING, "Unable to extract SRTP keying material from DTLS-SRTP negotiation on RTP instance '%p'\n",
+			instance);
+		return -1;
+	}
+
+	/* Whether we are acting as a server or client determines where the keys/salts are */
+	if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTIVE) {
+		local_key = material;
+		remote_key = local_key + SRTP_MASTER_KEY_LEN;
+		local_salt = remote_key + SRTP_MASTER_KEY_LEN;
+		remote_salt = local_salt + SRTP_MASTER_SALT_LEN;
+	} else {
+		remote_key = material;
+		local_key = remote_key + SRTP_MASTER_KEY_LEN;
+		remote_salt = local_key + SRTP_MASTER_KEY_LEN;
+		local_salt = remote_salt + SRTP_MASTER_SALT_LEN;
+	}
+
+	if (!(local_policy = res_srtp_policy->alloc())) {
+		return -1;
+	}
+
+	if (res_srtp_policy->set_master_key(local_policy, local_key, SRTP_MASTER_KEY_LEN, local_salt, SRTP_MASTER_SALT_LEN) < 0) {
+		ast_log(LOG_WARNING, "Could not set key/salt information on local policy of '%p' when setting up DTLS-SRTP\n", rtp);
+		goto error;
+	}
+
+	if (res_srtp_policy->set_suite(local_policy, rtp->suite)) {
+		ast_log(LOG_WARNING, "Could not set suite to '%d' on local policy of '%p' when setting up DTLS-SRTP\n", rtp->suite, rtp);
+		goto error;
+	}
+
+	if (ast_rtp_instance_get_stats(instance, &stats, AST_RTP_INSTANCE_STAT_LOCAL_SSRC)) {
+		goto error;
+	}
+
+	res_srtp_policy->set_ssrc(local_policy, stats.local_ssrc, 0);
+
+	if (!(remote_policy = res_srtp_policy->alloc())) {
+		goto error;
+	}
+
+	if (res_srtp_policy->set_master_key(remote_policy, remote_key, SRTP_MASTER_KEY_LEN, remote_salt, SRTP_MASTER_SALT_LEN) < 0) {
+		ast_log(LOG_WARNING, "Could not set key/salt information on remote policy of '%p' when setting up DTLS-SRTP\n", rtp);
+		goto error;
+	}
+
+	if (res_srtp_policy->set_suite(remote_policy, rtp->suite)) {
+		ast_log(LOG_WARNING, "Could not set suite to '%d' on remote policy of '%p' when setting up DTLS-SRTP\n", rtp->suite, rtp);
+		goto error;
+	}
+
+	res_srtp_policy->set_ssrc(remote_policy, 0, 1);
+
+	if (ast_rtp_instance_add_srtp_policy(instance, remote_policy, local_policy)) {
+		ast_log(LOG_WARNING, "Could not set policies when setting up DTLS-SRTP on '%p'\n", rtp);
+		goto error;
+	}
+
+	if (rtp->rekey) {
+		ao2_ref(instance, +1);
+		if ((rtp->rekeyid = ast_sched_add(rtp->sched, rtp->rekey * 1000, dtls_srtp_renegotiate, instance)) < 0) {
+			ao2_ref(instance, -1);
+			goto error;
+		}
+	}
+
+	return 0;
+
+error:
+	res_srtp_policy->destroy(local_policy);
+
+	if (remote_policy) {
+		res_srtp_policy->destroy(remote_policy);
+	}
+
+	return -1;
+}
+#endif
+
 static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp)
 {
 	int len;
@@ -884,6 +1382,56 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
 	   return len;
 	}
 
+#ifdef HAVE_OPENSSL_SRTP
+	if (!rtcp) {
+		char *in = buf;
+
+		dtls_srtp_check_pending(instance, rtp);
+
+		/* If this is an SSL packet pass it to OpenSSL for processing */
+		if ((*in >= 20) && (*in <= 64)) {
+			int res = 0;
+
+			/* If no SSL session actually exists terminate things */
+			if (!rtp->ssl) {
+				ast_log(LOG_ERROR, "Received SSL traffic on RTP instance '%p' without an SSL session\n",
+					instance);
+				return -1;
+			}
+
+			/* If we don't yet know if we are active or passive and we receive a packet... we are obviously passive */
+			if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTPASS) {
+				rtp->dtls_setup = AST_RTP_DTLS_SETUP_PASSIVE;
+				SSL_set_accept_state(rtp->ssl);
+			}
+
+			dtls_srtp_check_pending(instance, rtp);
+
+			BIO_write(rtp->read_bio, buf, len);
+
+			len = SSL_read(rtp->ssl, buf, len);
+
+			dtls_srtp_check_pending(instance, rtp);
+
+			if (rtp->dtls_failure) {
+				ast_log(LOG_ERROR, "DTLS failure occurred on RTP instance '%p', terminating\n",
+					instance);
+				return -1;
+			}
+
+			if (SSL_is_init_finished(rtp->ssl)) {
+				/* Any further connections will be existing since this is now established */
+				rtp->connection = AST_RTP_DTLS_CONNECTION_EXISTING;
+
+				/* Use the keying material to set up key/salt information */
+				res = dtls_srtp_setup(rtp, srtp, instance);
+			}
+
+			return res;
+		}
+	}
+#endif
+
 	if (rtp->ice) {
 		pj_str_t combined = pj_str(ast_sockaddr_stringify(sa));
 		pj_sockaddr address;
@@ -927,7 +1475,7 @@ static int rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t siz
 	return __rtp_recvfrom(instance, buf, size, flags, sa, 0);
 }
 
-static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice)
+static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice, int use_srtp)
 {
 	int len = size;
 	void *temp = buf;
@@ -936,7 +1484,7 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz
 
 	*ice = 0;
 
-	if (res_srtp && srtp && res_srtp->protect(srtp, &temp, &len, rtcp) < 0) {
+	if (use_srtp && res_srtp && srtp && res_srtp->protect(srtp, &temp, &len, rtcp) < 0) {
 		return -1;
 	}
 
@@ -954,12 +1502,12 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz
 
 static int rtcp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int *ice)
 {
-	return __rtp_sendto(instance, buf, size, flags, sa, 1, ice);
+	return __rtp_sendto(instance, buf, size, flags, sa, 1, ice, 1);
 }
 
 static int rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int *ice)
 {
-	return __rtp_sendto(instance, buf, size, flags, sa, 0, ice);
+	return __rtp_sendto(instance, buf, size, flags, sa, 0, ice, 1);
 }
 
 static int rtp_get_rate(struct ast_format *format)
@@ -1223,6 +1771,10 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
 	/* Record any information we may need */
 	rtp->sched = sched;
 
+#ifdef HAVE_OPENSSL_SRTP
+	rtp->rekeyid = -1;
+#endif
+
 	return 0;
 }
 
@@ -1285,6 +1837,18 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance)
 		ao2_ref(rtp->remote_candidates, -1);
 	}
 
+#ifdef HAVE_OPENSSL_SRTP
+	/* Destroy the SSL context if present */
+	if (rtp->ssl_ctx) {
+		SSL_CTX_free(rtp->ssl_ctx);
+	}
+
+	/* Destroy the SSL session if present */
+	if (rtp->ssl) {
+		SSL_free(rtp->ssl);
+	}
+#endif
+
 	/* Destroy synchronization items */
 	ast_mutex_destroy(&rtp->lock);
 	ast_cond_destroy(&rtp->cond);
@@ -3507,6 +4071,10 @@ static void ast_rtp_stop(struct ast_rtp_instance *instance)
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 	struct ast_sockaddr addr = { {0,} };
 
+#ifdef HAVE_OPENSSL_SRTP
+	AST_SCHED_DEL_UNREF(rtp->sched, rtp->rekeyid, ao2_ref(instance, -1));
+#endif
+
 	if (rtp->rtcp && rtp->rtcp->schedid > 0) {
 		if (!ast_sched_del(rtp->sched, rtp->rtcp->schedid)) {
 			/* successfully cancelled scheduler entry. */
@@ -3586,6 +4154,23 @@ static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level)
 	return res;
 }
 
+#ifdef HAVE_OPENSSL_SRTP
+static int ast_rtp_activate(struct ast_rtp_instance *instance)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	if (!rtp->ssl) {
+		return 0;
+	}
+
+	SSL_do_handshake(rtp->ssl);
+
+	dtls_srtp_check_pending(instance, rtp);
+
+	return 0;
+}
+#endif
+
 static char *rtp_do_debug_ip(struct ast_cli_args *a)
 {
 	char *arg = ast_strdupa(a->argv[4]);