From 3d6175c745997c3126acc8c563ff18978966f245 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 13 Mar 2014 17:35:21 +0100 Subject: [PATCH] stream: add SRTP support Install srtp encoder and decoder elements in rtpbin Add MIKEY in SDP --- configure.ac | 6 ++ examples/test-video.c | 1 + gst/rtsp-server/rtsp-sdp.c | 93 ++++++++++++++++++++++++++++++ gst/rtsp-server/rtsp-stream.c | 103 ++++++++++++++++++++++++++++++++++ tests/check/Makefile.am | 4 +- 5 files changed, 205 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 4b12b26900..9f61fc753b 100644 --- a/configure.ac +++ b/configure.ac @@ -59,6 +59,7 @@ dnl *** required versions of GStreamer stuff *** GST_REQ=1.3.0.1 GSTPB_REQ=1.3.0.1 GSTPG_REQ=1.3.0.1 +GSTPD_REQ=1.3.0.1 dnl *** autotools stuff **** @@ -176,6 +177,11 @@ GSTPG_PLUGINS_DIR=`$PKG_CONFIG gstreamer-plugins-good-$GST_API_VERSION --variabl AC_SUBST(GSTPG_PLUGINS_DIR) AC_MSG_NOTICE(Using GStreamer Good Plugins in $GSTPG_PLUGINS_DIR) +AG_GST_CHECK_GST_PLUGINS_BAD($GST_API_VERSION, [$GSTPD_REQ], [yes]) +GSTPD_PLUGINS_DIR=`$PKG_CONFIG gstreamer-plugins-bad-$GST_API_VERSION --variable pluginsdir` +AC_SUBST(GSTPD_PLUGINS_DIR) +AC_MSG_NOTICE(Using GStreamer Bad Plugins in $GSTPD_PLUGINS_DIR) + AG_GST_CHECK_GST_CHECK($GST_API_VERSION, [$GST_REQ], no) AM_CONDITIONAL(HAVE_CHECK, test "x$HAVE_GST_CHECK" = "xyes") diff --git a/examples/test-video.c b/examples/test-video.c index 9bfb9d6427..044fbbf519 100644 --- a/examples/test-video.c +++ b/examples/test-video.c @@ -136,6 +136,7 @@ main (int argc, char *argv[]) gst_rtsp_media_factory_set_permissions (factory, permissions); gst_rtsp_permissions_unref (permissions); #endif + gst_rtsp_media_factory_set_profiles (factory, GST_RTSP_PROFILE_SAVP); /* attach the test factory to the /test url */ gst_rtsp_mount_points_add_factory (mounts, "/test", factory); diff --git a/gst/rtsp-server/rtsp-sdp.c b/gst/rtsp-server/rtsp-sdp.c index 53fc22c7b2..fa8bd6536e 100644 --- a/gst/rtsp-server/rtsp-sdp.c +++ b/gst/rtsp-server/rtsp-sdp.c @@ -26,6 +26,8 @@ #include +#include + #include "rtsp-sdp.h" static gboolean @@ -171,6 +173,93 @@ make_media (GstSDPMessage * sdp, GstSDPInfo * info, GstRTSPMedia * media, gst_sdp_media_add_attribute (smedia, "control", tmp); g_free (tmp); + + /* check for srtp */ + do { + GstBuffer *srtpkey; + const GValue *val; + const gchar *srtpcipher, *srtpauth, *srtcpcipher, *srtcpauth; + GstMIKEYMessage *msg; + GstMIKEYPayload *payload; + GBytes *bytes; + GstMapInfo info; + const guint8 *data; + gsize size; + gchar *base64; + guint8 byte; + guint32 ssrc; + + val = gst_structure_get_value (s, "srtp-key"); + if (val == NULL) + break; + + srtpkey = gst_value_get_buffer (val); + if (srtpkey == NULL) + break; + + srtpcipher = gst_structure_get_string (s, "srtp-cipher"); + srtpauth = gst_structure_get_string (s, "srtp-auth"); + srtcpcipher = gst_structure_get_string (s, "srtcp-cipher"); + srtcpauth = gst_structure_get_string (s, "srtcp-auth"); + + if (srtpcipher == NULL || srtpauth == NULL || srtcpcipher == NULL || + srtcpauth == NULL) + break; + + msg = gst_mikey_message_new (); + /* unencrypted MIKEY message, we send this over TLS so this is allowed */ + gst_mikey_message_set_info (msg, GST_MIKEY_VERSION, GST_MIKEY_TYPE_PSK_INIT, + FALSE, GST_MIKEY_PRF_MIKEY_1, 0, GST_MIKEY_MAP_TYPE_SRTP); + /* add policy '0' for our SSRC */ + gst_rtsp_stream_get_ssrc (stream, &ssrc); + gst_mikey_message_add_cs_srtp (msg, 0, ssrc, 0); + /* timestamp is now */ + gst_mikey_message_add_t_now_ntp_utc (msg); + /* add some random data */ + gst_mikey_message_add_rand_len (msg, 16); + + /* the policy '0' is SRTP with the above discovered algorithms */ + payload = gst_mikey_payload_new (GST_MIKEY_PT_SP); + gst_mikey_payload_sp_set (payload, 0, GST_MIKEY_SEC_PROTO_SRTP); + + /* only AES-CM is supported */ + byte = 1; + gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_ENC_ALG, 1, + &byte); + /* only HMAC-SHA1 */ + gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_AUTH_ALG, 1, + &byte); + /* we enable encryption on RTP and RTCP */ + gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_SRTP_ENC, 1, + &byte); + gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_SRTCP_ENC, 1, + &byte); + /* we enable authentication on RTP and RTCP */ + gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_SRTP_AUTH, 1, + &byte); + gst_mikey_message_add_payload (msg, payload); + + /* add the key in KEMAC */ + gst_buffer_map (srtpkey, &info, GST_MAP_READ); + gst_mikey_message_add_kemac (msg, GST_MIKEY_ENC_NULL, info.size, info.data, + GST_MIKEY_MAC_NULL, NULL); + gst_buffer_unmap (srtpkey, &info); + + /* now serialize this to bytes */ + bytes = gst_mikey_message_to_bytes (msg); + gst_mikey_message_free (msg); + /* and make it into base64 */ + data = g_bytes_get_data (bytes, &size); + base64 = g_base64_encode (data, size); + g_bytes_unref (bytes); + + tmp = g_strdup_printf ("mikey %s", base64); + g_free (base64); + + gst_sdp_media_add_attribute (smedia, "key-mgmt", tmp); + g_free (tmp); + } while (FALSE); + /* collect all other properties and add them to fmtp or attributes */ fmtp = g_string_new (""); g_string_append_printf (fmtp, "%d ", caps_pt); @@ -198,6 +287,10 @@ make_media (GstSDPMessage * sdp, GstSDPInfo * info, GstRTSPMedia * media, continue; if (!strcmp (fname, "seqnum-base")) continue; + if (g_str_has_prefix (fname, "srtp-")) + continue; + if (g_str_has_prefix (fname, "srtcp-")) + continue; if (g_str_has_prefix (fname, "a-")) { /* attribute */ diff --git a/gst/rtsp-server/rtsp-stream.c b/gst/rtsp-server/rtsp-stream.c index 9ec187c838..ef6581f0c1 100644 --- a/gst/rtsp-server/rtsp-stream.c +++ b/gst/rtsp-server/rtsp-stream.c @@ -79,6 +79,10 @@ struct _GstRTSPStreamPrivate /* the RTPSession object */ GObject *session; + /* SRTP encoder/decoder */ + GstElement *srtpenc; + GstElement *srtpdec; + /* sinks used for sending and receiving RTP and RTCP over ipv4, they share * sockets */ GstElement *udpsrc_v4[2]; @@ -1503,6 +1507,87 @@ static GstAppSinkCallbacks sink_cb = { handle_new_sample, }; +static GstElement * +get_rtp_encoder (GstRTSPStream * stream, guint session) +{ + GstRTSPStreamPrivate *priv = stream->priv; + + if (priv->srtpenc == NULL) { + gchar *name; + + name = g_strdup_printf ("srtpenc_%u", session); + priv->srtpenc = gst_element_factory_make ("srtpenc", name); + g_free (name); + + g_object_set (priv->srtpenc, "random-key", TRUE, NULL); + } + return gst_object_ref (priv->srtpenc); +} + +static GstElement * +request_rtp_encoder (GstElement * rtpbin, guint session, GstRTSPStream * stream) +{ + GstRTSPStreamPrivate *priv = stream->priv; + GstElement *enc; + GstPad *pad; + gchar *name; + + if (priv->idx != session) + return NULL; + + GST_DEBUG_OBJECT (stream, "make RTP encoder for session %u", session); + + enc = get_rtp_encoder (stream, session); + name = g_strdup_printf ("rtp_sink_%d", session); + pad = gst_element_get_request_pad (enc, name); + g_free (name); + gst_object_unref (pad); + + return enc; +} + +static GstElement * +request_rtcp_encoder (GstElement * rtpbin, guint session, + GstRTSPStream * stream) +{ + GstRTSPStreamPrivate *priv = stream->priv; + GstElement *enc; + GstPad *pad; + gchar *name; + + if (priv->idx != session) + return NULL; + + GST_DEBUG_OBJECT (stream, "make RTCP encoder for session %u", session); + + enc = get_rtp_encoder (stream, session); + name = g_strdup_printf ("rtcp_sink_%d", session); + pad = gst_element_get_request_pad (enc, name); + g_free (name); + gst_object_unref (pad); + + return enc; +} + +static GstElement * +request_rtcp_decoder (GstElement * rtpbin, guint session, + GstRTSPStream * stream) +{ + GstRTSPStreamPrivate *priv = stream->priv; + + if (priv->idx != session) + return NULL; + + if (priv->srtpdec == NULL) { + gchar *name; + + name = g_strdup_printf ("srtpdec_%u", session); + priv->srtpdec = gst_element_factory_make ("srtpdec", name); + g_free (name); + } + return gst_object_ref (priv->srtpdec); +} + /** * gst_rtsp_stream_join_bin: * @stream: a #GstRTSPStream @@ -1549,6 +1634,21 @@ gst_rtsp_stream_join_bin (GstRTSPStream * stream, GstBin * bin, /* update the dscp qos field in the sinks */ update_dscp_qos (stream); + if (priv->profiles & GST_RTSP_PROFILE_SAVP + || priv->profiles & GST_RTSP_PROFILE_SAVPF) { + /* For SRTP */ + g_signal_connect (rtpbin, "request-rtp-encoder", + (GCallback) request_rtp_encoder, stream); + g_signal_connect (rtpbin, "request-rtcp-encoder", + (GCallback) request_rtcp_encoder, stream); +#if 0 + g_signal_connect (rtpbin, "request-rtp-decoder", + (GCallback) request_rtp_decoder, stream); +#endif + g_signal_connect (rtpbin, "request-rtcp-decoder", + (GCallback) request_rtcp_decoder, stream); + } + /* get a pad for sending RTP */ name = g_strdup_printf ("send_rtp_sink_%u", idx); priv->send_rtp_sink = gst_element_get_request_pad (rtpbin, name); @@ -1873,6 +1973,9 @@ gst_rtsp_stream_leave_bin (GstRTSPStream * stream, GstBin * bin, gst_caps_unref (priv->caps); priv->caps = NULL; + if (priv->srtpenc) + gst_object_unref (priv->srtpenc); + priv->is_joined = FALSE; g_mutex_unlock (&priv->lock); diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 7ecc2bc11e..82e6d42081 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -11,8 +11,8 @@ TESTS_ENVIRONMENT = \ GST_STATE_IGNORE_ELEMENTS="$(STATE_IGNORE_ELEMENTS)" \ $(REGISTRY_ENVIRONMENT) \ GST_PLUGIN_SYSTEM_PATH_1_0= \ - GST_PLUGIN_PATH_1_0=$(GST_PLUGINS_DIR):$(GSTPB_PLUGINS_DIR):$(GSTPG_PLUGINS_DIR) \ - GST_PLUGIN_LOADING_WHITELIST="gstreamer:gst-plugins-base:gst-plugins-good" + GST_PLUGIN_PATH_1_0=$(GST_PLUGINS_DIR):$(GSTPB_PLUGINS_DIR):$(GSTPG_PLUGINS_DIR):$(GSTPD_PLUGINS_DIR) \ + GST_PLUGIN_LOADING_WHITELIST="gstreamer:gst-plugins-base:gst-plugins-good:gst-plugins-bad" # ths core dumps of some machines have PIDs appended