stream: add SRTP support
authorWim Taymans <wtaymans@redhat.com>
Thu, 13 Mar 2014 16:35:21 +0000 (17:35 +0100)
committerWim Taymans <wtaymans@redhat.com>
Tue, 25 Mar 2014 09:31:21 +0000 (10:31 +0100)
Install srtp encoder and decoder elements in rtpbin
Add MIKEY in SDP

configure.ac
examples/test-video.c
gst/rtsp-server/rtsp-sdp.c
gst/rtsp-server/rtsp-stream.c
tests/check/Makefile.am

index 4b12b26..9f61fc7 100644 (file)
@@ -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")
 
index 9bfb9d6..044fbbf 100644 (file)
@@ -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);
index 53fc22c..fa8bd65 100644 (file)
@@ -26,6 +26,8 @@
 
 #include <string.h>
 
+#include <gst/sdp/gstmikey.h>
+
 #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 */
index 9ec187c..ef6581f 100644 (file)
@@ -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);
 
index 7ecc2bc..82e6d42 100644 (file)
@@ -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