summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlivier CrĂȘte <olivier.crete@collabora.com>2014-05-05 21:26:39 -0400
committerNicolas Dufresne <nicolas.dufresne@collabora.co.uk>2014-09-18 13:38:01 -0400
commit344468a3b7a6828c0c16f95c81b99ed3af8c001a (patch)
tree1487a7fc1958db79cbb9dd309261aba382dda792
parent8989360664f68944ca6c00e69b2d88194fed4c38 (diff)
downloadfarstream-344468a3b7a6828c0c16f95c81b99ed3af8c001a.tar.gz
rtp: Implement setting SRTP decryption key
-rw-r--r--gst/fsrtpconference/fs-rtp-session.c188
-rw-r--r--gst/fsrtpconference/fs-rtp-stream.c166
-rw-r--r--gst/fsrtpconference/fs-rtp-stream.h7
3 files changed, 302 insertions, 59 deletions
diff --git a/gst/fsrtpconference/fs-rtp-session.c b/gst/fsrtpconference/fs-rtp-session.c
index 662420ec..f45c93ee 100644
--- a/gst/fsrtpconference/fs-rtp-session.c
+++ b/gst/fsrtpconference/fs-rtp-session.c
@@ -392,6 +392,11 @@ static gboolean
fs_rtp_session_set_encryption_parameters (FsSession *session,
GstStructure *parameters, GError **error);
+static GstCaps *
+_srtpdec_request_key (GstElement *srtpdec, guint ssrc, gpointer user_data);
+static gboolean
+_stream_decrypt_clear_locked_cb (FsRtpStream *stream, gpointer user_data);
+
//static guint signals[LAST_SIGNAL] = { 0 };
@@ -1329,8 +1334,11 @@ fs_rtp_session_constructed (GObject *object)
tmp = g_strdup_printf ("srtpdec_%u", self->id);
self->priv->srtpdec = gst_element_factory_make ("srtpdec", tmp);
g_free (tmp);
- if (self->priv->srtpdec)
+ if (self->priv->srtpdec) {
gst_object_ref_sink (self->priv->srtpdec);
+ g_signal_connect_object (self->priv->srtpdec, "request-key",
+ G_CALLBACK (_srtpdec_request_key), self, 0);
+ }
request_rtp_encoder_id =
g_signal_connect (self->priv->conference->rtpbin, "request-rtp-encoder",
@@ -1836,6 +1844,23 @@ GET_MEMBER (GObject, rtpbin_internal_session)
GET_MEMBER (GstElement, rtpmuxer)
#undef GET_MEMBER
+static gboolean
+fs_rtp_session_add_ssrc_stream_locked (FsRtpSession *self, guint32 ssrc,
+ FsRtpStream *stream)
+{
+
+ if (!g_hash_table_lookup (self->priv->ssrc_streams, GUINT_TO_POINTER (ssrc)))
+ {
+ g_hash_table_insert (self->priv->ssrc_streams, GUINT_TO_POINTER (ssrc),
+ stream);
+ if (self->priv->srtpdec)
+ g_signal_emit_by_name (self->priv->srtpdec, "remove-key", ssrc);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
static void
_stream_known_source_packet_received (FsRtpStream *stream, guint component,
GstBuffer *buffer, gpointer user_data)
@@ -1890,12 +1915,8 @@ _stream_known_source_packet_received (FsRtpStream *stream, guint component,
FS_RTP_SESSION_LOCK (self);
- if (!g_hash_table_lookup (self->priv->ssrc_streams, GUINT_TO_POINTER (ssrc)))
+ if (fs_rtp_session_add_ssrc_stream_locked (self, ssrc, stream))
{
- GST_DEBUG ("Associating SSRC %x in session %d", ssrc, self->id);
- g_hash_table_insert (self->priv->ssrc_streams, GUINT_TO_POINTER (ssrc),
- stream);
-
FS_RTP_SESSION_UNLOCK (self);
fs_rtp_session_associate_free_substreams (self, stream, ssrc);
@@ -1943,8 +1964,7 @@ _stream_ssrc_added_cb (FsRtpStream *stream, guint32 ssrc, gpointer user_data)
return;
FS_RTP_SESSION_LOCK (self);
- g_hash_table_insert (self->priv->ssrc_streams, GUINT_TO_POINTER (ssrc),
- stream);
+ fs_rtp_session_add_ssrc_stream_locked (self, ssrc, stream);
g_hash_table_insert (self->priv->ssrc_streams_manual, GUINT_TO_POINTER (ssrc),
stream);
FS_RTP_SESSION_UNLOCK (self);
@@ -2027,6 +2047,7 @@ fs_rtp_session_new_stream (FsSession *session,
_stream_sending_changed_locked,
_stream_ssrc_added_cb,
_stream_get_new_stream_transmitter,
+ _stream_decrypt_clear_locked_cb,
self));
if (new_stream)
@@ -4392,10 +4413,7 @@ fs_rtp_session_associate_ssrc_cname (FsRtpSession *session,
return;
}
- if (!g_hash_table_lookup (session->priv->ssrc_streams,
- GUINT_TO_POINTER (ssrc)))
- g_hash_table_insert (session->priv->ssrc_streams, GUINT_TO_POINTER (ssrc),
- stream);
+ fs_rtp_session_add_ssrc_stream_locked (session, ssrc, stream);
g_object_ref (stream);
FS_RTP_SESSION_UNLOCK (session);
@@ -5352,6 +5370,66 @@ invalid:
}
static gboolean
+fs_rtp_session_set_allowed_caps (FsSession *session, GstCaps *sink_caps,
+ GstCaps *src_caps, GError **error)
+{
+ FsRtpSession *self = FS_RTP_SESSION (session);
+ GstCaps *old_input_caps = NULL;
+ GstCaps *old_output_caps = NULL;
+ gboolean ret;
+ guint current_generation;
+
+ if (fs_rtp_session_has_disposed_enter (self, error))
+ return FALSE;
+
+ FS_RTP_SESSION_LOCK (self);
+ if (sink_caps)
+ {
+ old_input_caps = gst_caps_ref (self->priv->input_caps);
+ gst_caps_replace (&self->priv->input_caps, sink_caps);
+ }
+ if (src_caps)
+ {
+ old_output_caps = gst_caps_ref (self->priv->output_caps);
+ gst_caps_replace (&self->priv->output_caps, src_caps);
+ }
+ current_generation = self->priv->caps_generation;
+ self->priv->caps_generation++;
+ FS_RTP_SESSION_UNLOCK (self);
+
+ ret = fs_rtp_session_update_codecs (self, NULL, NULL, error);
+
+ if (ret)
+ {
+ if (sink_caps)
+ g_object_notify ((GObject *) self, "allowed-sink-caps");
+ if (src_caps)
+ g_object_notify ((GObject *) self, "allowed-src-caps");
+ }
+ else
+ {
+ FS_RTP_SESSION_LOCK (self);
+ if (self->priv->caps_generation == current_generation)
+ {
+ if (old_input_caps)
+ gst_caps_replace (&self->priv->input_caps, old_input_caps);
+ if (old_output_caps)
+ gst_caps_replace (&self->priv->output_caps, old_output_caps);
+
+ self->priv->caps_generation++;
+ }
+ FS_RTP_SESSION_UNLOCK (self);
+ GST_WARNING ("Invalid new codec preferences");
+ }
+
+ gst_caps_replace (&old_input_caps, NULL);
+ gst_caps_replace (&old_output_caps, NULL);
+
+ fs_rtp_session_has_disposed_exit (self);
+ return ret;
+}
+
+static gboolean
fs_rtp_session_set_encryption_parameters (FsSession *session,
GstStructure *parameters, GError **error)
{
@@ -5404,62 +5482,60 @@ done:
return ret;
}
-static gboolean
-fs_rtp_session_set_allowed_caps (FsSession *session, GstCaps *sink_caps,
- GstCaps *src_caps, GError **error)
+static GstCaps *
+_srtpdec_request_key (GstElement *srtpdec, guint ssrc, gpointer user_data)
{
- FsRtpSession *self = FS_RTP_SESSION (session);
- GstCaps *old_input_caps = NULL;
- GstCaps *old_output_caps = NULL;
- gboolean ret;
- guint current_generation;
+ FsRtpSession *self = FS_RTP_SESSION (user_data);
+ FsRtpStream *stream;
+ GstCaps *caps = NULL;
- if (fs_rtp_session_has_disposed_enter (self, error))
- return FALSE;
+ if (fs_rtp_session_has_disposed_enter (self, NULL))
+ return NULL;
FS_RTP_SESSION_LOCK (self);
- if (sink_caps)
- {
- old_input_caps = gst_caps_ref (self->priv->input_caps);
- gst_caps_replace (&self->priv->input_caps, sink_caps);
- }
- if (src_caps)
+ stream = fs_rtp_session_get_stream_by_ssrc_locked (self, ssrc);
+
+ if (stream)
{
- old_output_caps = gst_caps_ref (self->priv->output_caps);
- gst_caps_replace (&self->priv->output_caps, src_caps);
+ caps = fs_rtp_stream_get_srtp_caps_locked (stream);
+ g_object_unref (stream);
}
- current_generation = self->priv->caps_generation;
- self->priv->caps_generation++;
+
FS_RTP_SESSION_UNLOCK (self);
- ret = fs_rtp_session_update_codecs (self, NULL, NULL, error);
+ fs_rtp_session_has_disposed_exit (self);
- if (ret)
- {
- if (sink_caps)
- g_object_notify ((GObject *) self, "allowed-sink-caps");
- if (src_caps)
- g_object_notify ((GObject *) self, "allowed-src-caps");
- }
+ if (caps)
+ return caps;
else
+ return gst_caps_new_simple ("application/x-srtp",
+ "srtp-cipher", G_TYPE_STRING, "null",
+ "srtcp-cipher", G_TYPE_STRING, "null",
+ "srtp-auth", G_TYPE_STRING, "null",
+ "srtcp-auth", G_TYPE_STRING, "null",
+ NULL);
+}
+
+static gboolean
+_stream_decrypt_clear_locked_cb (FsRtpStream *stream, gpointer user_data)
+{
+ FsRtpSession *self = FS_RTP_SESSION (user_data);
+ GHashTableIter iter;
+ gpointer key, value;
+
+ if (!self->priv->srtpdec)
+ return FALSE;
+
+ g_hash_table_iter_init (&iter, self->priv->ssrc_streams);
+
+ while (g_hash_table_iter_next (&iter, &key, &value))
{
- FS_RTP_SESSION_LOCK (self);
- if (self->priv->caps_generation == current_generation)
- {
- if (old_input_caps)
- gst_caps_replace (&self->priv->input_caps, old_input_caps);
- if (old_output_caps)
- gst_caps_replace (&self->priv->output_caps, old_output_caps);
+ guint32 ssrc = GPOINTER_TO_UINT (key);
+ FsRtpStream *tmp_stream = value;
- self->priv->caps_generation++;
- }
- FS_RTP_SESSION_UNLOCK (self);
- GST_WARNING ("Invalid new codec preferences");
+ if (tmp_stream == stream)
+ g_signal_emit_by_name (self->priv->srtpdec, "remove-key", ssrc);
}
- gst_caps_replace (&old_input_caps, NULL);
- gst_caps_replace (&old_output_caps, NULL);
-
- fs_rtp_session_has_disposed_exit (self);
- return ret;
+ return TRUE;
}
diff --git a/gst/fsrtpconference/fs-rtp-stream.c b/gst/fsrtpconference/fs-rtp-stream.c
index 9b242c81..1e707fc3 100644
--- a/gst/fsrtpconference/fs-rtp-stream.c
+++ b/gst/fsrtpconference/fs-rtp-stream.c
@@ -29,6 +29,20 @@
* This is the conjunction of a #FsRtpParticipant and a #FsRtpSession,
* it is created by calling fs_session_new_stream() on a
* #FsRtpSession.
+ *
+ * <refsect2><title>SRTP authentication & decryption</title>
+ * <para>
+ *
+ * To tell #FsRtpStream to authenticate and decrypt the media it is
+ * receiving using SRTP, one must set the parameters using a
+ * #GstStructure named "FarstreamSRTP" and pass it to
+ * fs_stream_set_decryption_parameters().
+ *
+ * The cipher, auth, and key must be specified, refer to the <link
+ * linkend="FarstreamSRTP">FsRtpSession
+ * documentation</link> for details.
+ *
+ * </para> </refsect2>
*/
#ifdef HAVE_CONFIG_H
@@ -61,7 +75,8 @@ enum
PROP_DIRECTION,
PROP_PARTICIPANT,
PROP_SESSION,
- PROP_RTP_HEADER_EXTENSIONS
+ PROP_RTP_HEADER_EXTENSIONS,
+ PROP_DECRYPTION_PARAMETERS
};
struct _FsRtpStreamPrivate
@@ -76,8 +91,12 @@ struct _FsRtpStreamPrivate
stream_sending_changed_locked_cb sending_changed_locked_cb;
stream_ssrc_added_cb ssrc_added_cb;
stream_get_new_stream_transmitter_cb get_new_stream_transmitter_cb;
+ stream_decrypt_clear_locked_cb decrypt_clear_locked_cb;
gpointer user_data_for_cb;
+ /* protected by session lock */
+ GstStructure *decryption_parameters;
+
gulong local_candidates_prepared_handler_id;
gulong new_active_candidate_pair_handler_id;
gulong new_local_candidate_handler_id;
@@ -123,9 +142,11 @@ static gboolean fs_rtp_stream_set_transmitter (FsStream *stream,
guint stream_transmitter_n_parameters,
GError **error);
-
static void fs_rtp_stream_add_id (FsStream *stream, guint id);
+static gboolean fs_rtp_stream_set_decryption_parameters (FsStream *stream,
+ GstStructure *parameters, GError **error);
+
static void _local_candidates_prepared (
FsStreamTransmitter *stream_transmitter,
gpointer user_data);
@@ -175,6 +196,8 @@ fs_rtp_stream_class_init (FsRtpStreamClass *klass)
stream_class->force_remote_candidates = fs_rtp_stream_force_remote_candidates;
stream_class->add_id = fs_rtp_stream_add_id;
stream_class->set_transmitter = fs_rtp_stream_set_transmitter;
+ stream_class->set_decryption_parameters =
+ fs_rtp_stream_set_decryption_parameters;
g_type_class_add_private (klass, sizeof (FsRtpStreamPrivate));
@@ -196,6 +219,10 @@ fs_rtp_stream_class_init (FsRtpStreamClass *klass)
g_object_class_override_property (gobject_class,
PROP_SESSION,
"session");
+ g_object_class_override_property (gobject_class,
+ PROP_DECRYPTION_PARAMETERS,
+ "decryption-parameters");
+
g_object_class_install_property (gobject_class,
PROP_RTP_HEADER_EXTENSIONS,
g_param_spec_boxed ("rtp-header-extensions",
@@ -338,6 +365,9 @@ fs_rtp_stream_finalize (GObject *object)
fs_codec_list_destroy (self->remote_codecs);
fs_codec_list_destroy (self->negotiated_codecs);
+ if (self->priv->decryption_parameters)
+ gst_structure_free (self->priv->decryption_parameters);
+
g_mutex_clear (&self->priv->mutex);
G_OBJECT_CLASS (fs_rtp_stream_parent_class)->finalize (object);
@@ -419,6 +449,11 @@ fs_rtp_stream_get_property (GObject *object,
g_value_set_boxed (value, self->hdrext);
FS_RTP_SESSION_UNLOCK (session);
break;
+ case PROP_DECRYPTION_PARAMETERS:
+ FS_RTP_SESSION_LOCK (session);
+ g_value_set_boxed (value, self->priv->decryption_parameters);
+ FS_RTP_SESSION_UNLOCK (session);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -679,6 +714,7 @@ fs_rtp_stream_new (FsRtpSession *session,
stream_sending_changed_locked_cb sending_changed_locked_cb,
stream_ssrc_added_cb ssrc_added_cb,
stream_get_new_stream_transmitter_cb get_new_stream_transmitter_cb,
+ stream_decrypt_clear_locked_cb decrypt_clear_locked_cb,
gpointer user_data_for_cb)
{
FsRtpStream *self;
@@ -699,6 +735,7 @@ fs_rtp_stream_new (FsRtpSession *session,
self->priv->sending_changed_locked_cb = sending_changed_locked_cb;
self->priv->ssrc_added_cb = ssrc_added_cb;
self->priv->get_new_stream_transmitter_cb = get_new_stream_transmitter_cb;
+ self->priv->decrypt_clear_locked_cb = decrypt_clear_locked_cb;
self->priv->user_data_for_cb = user_data_for_cb;
@@ -1334,3 +1371,128 @@ validate_srtp_parameters (GstStructure *parameters,
return TRUE;
}
+
+
+static gboolean
+fs_rtp_stream_set_decryption_parameters (FsStream *stream,
+ GstStructure *parameters, GError **error)
+{
+ FsRtpStream *self = FS_RTP_STREAM (stream);
+ GstBuffer *key;
+ gint rtp_cipher;
+ gint rtcp_cipher;
+ gint rtp_auth;
+ gint rtcp_auth;
+ guint replay_window_size;
+ FsRtpSession *session;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (FS_IS_RTP_STREAM (stream), FALSE);
+ g_return_val_if_fail (parameters == NULL ||
+ GST_IS_STRUCTURE (parameters), FALSE);
+
+ if (!validate_srtp_parameters (parameters, &rtp_cipher, &rtcp_cipher,
+ &rtp_auth, &rtcp_auth, &key, &replay_window_size, error))
+ return FALSE;
+
+ session = fs_rtp_stream_get_session (self, error);
+ if (!session)
+ return FALSE;
+
+
+ FS_RTP_SESSION_LOCK (session);
+ if (self->priv->decryption_parameters != parameters &&
+ (!parameters || !self->priv->decryption_parameters ||
+ !gst_structure_is_equal (self->priv->decryption_parameters,
+ parameters)))
+ {
+ if (!self->priv->decrypt_clear_locked_cb (self,
+ self->priv->user_data_for_cb))
+ {
+ g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS,
+ "Can't set encryption because srtpdec is not installed");
+ goto done;
+ }
+
+ if (self->priv->decryption_parameters)
+ gst_structure_free (self->priv->decryption_parameters);
+
+ if (parameters)
+ self->priv->decryption_parameters = gst_structure_copy (parameters);
+ else
+ self->priv->decryption_parameters = NULL;
+
+ }
+
+ ret = TRUE;
+
+done:
+ FS_RTP_SESSION_UNLOCK (session);
+ g_object_unref (session);
+
+ return ret;
+}
+
+GstCaps *
+fs_rtp_stream_get_srtp_caps_locked (FsRtpStream *self)
+{
+ const gchar *srtp_cipher;
+ const gchar *srtcp_cipher;
+ const gchar *srtp_auth;
+ const gchar *srtcp_auth;
+ const GValue *v;
+ GstBuffer *key;
+
+ if (!self->priv->decryption_parameters)
+ return NULL;
+
+ /* This is always TRUE for now, but when we expand to DTLS-SRTP, it may
+ * not be.
+ */
+ if (!gst_structure_has_name (self->priv->decryption_parameters,
+ "FarstreamSRTP"))
+ return NULL;
+
+ srtp_cipher = gst_structure_get_string (self->priv->decryption_parameters,
+ "rtp-cipher");
+ if (!srtp_cipher)
+ srtp_cipher = gst_structure_get_string (self->priv->decryption_parameters,
+ "cipher");
+ if (!srtp_cipher)
+ srtp_cipher = "null";
+
+ srtcp_cipher = gst_structure_get_string (self->priv->decryption_parameters,
+ "rtcp-cipher");
+ if (!srtcp_cipher)
+ srtcp_cipher = gst_structure_get_string (self->priv->decryption_parameters,
+ "cipher");
+ if (!srtcp_cipher)
+ srtcp_cipher = "null";
+
+ srtp_auth = gst_structure_get_string (self->priv->decryption_parameters,
+ "rtp-auth");
+ if (!srtp_auth)
+ srtp_auth = gst_structure_get_string (self->priv->decryption_parameters,
+ "auth");
+ if (!srtp_auth)
+ srtp_auth = "null";
+
+ srtcp_auth = gst_structure_get_string (self->priv->decryption_parameters,
+ "rtcp-auth");
+ if (!srtcp_auth)
+ srtcp_auth = gst_structure_get_string (self->priv->decryption_parameters,
+ "auth");
+ if (!srtcp_auth)
+ srtcp_auth = "null";
+
+ v = gst_structure_get_value (self->priv->decryption_parameters, "key");
+ key = gst_value_get_buffer (v);
+
+ return gst_caps_new_simple ("application/x-srtp",
+ "srtp-key", GST_TYPE_BUFFER, key,
+ "srtp-cipher", G_TYPE_STRING, srtp_cipher,
+ "srtcp-cipher", G_TYPE_STRING, srtcp_cipher,
+ "srtp-auth", G_TYPE_STRING, srtp_auth,
+ "srtcp-auth", G_TYPE_STRING, srtcp_auth,
+ NULL);
+}
diff --git a/gst/fsrtpconference/fs-rtp-stream.h b/gst/fsrtpconference/fs-rtp-stream.h
index 5fa1c7b8..a2e7d18f 100644
--- a/gst/fsrtpconference/fs-rtp-stream.h
+++ b/gst/fsrtpconference/fs-rtp-stream.h
@@ -101,7 +101,8 @@ typedef FsStreamTransmitter* (*stream_get_new_stream_transmitter_cb) (
FsRtpStream *stream, FsParticipant *participant,
const gchar *transmitter_name, GParameter *parameters, guint n_parameters,
GError **error, gpointer user_data);
-
+typedef gboolean (*stream_decrypt_clear_locked_cb) (FsRtpStream *stream,
+ gpointer user_data);
FsRtpStream *fs_rtp_stream_new (FsRtpSession *session,
FsRtpParticipant *participant,
@@ -111,6 +112,7 @@ FsRtpStream *fs_rtp_stream_new (FsRtpSession *session,
stream_sending_changed_locked_cb sending_changed_locked_cb,
stream_ssrc_added_cb ssrc_added_cb,
stream_get_new_stream_transmitter_cb get_new_stream_transmitter_cb,
+ stream_decrypt_clear_locked_cb decrypt_clear_locked_cb,
gpointer user_data_for_cb);
gboolean fs_rtp_stream_add_substream_unlock (FsRtpStream *stream,
@@ -126,6 +128,9 @@ validate_srtp_parameters (GstStructure *parameters,
gint *srtp_cipher, gint *srtcp_cipher, gint *srtp_auth, gint *srtcp_auth,
GstBuffer **key, guint *replay_window, GError **error);
+GstCaps *
+fs_rtp_stream_get_srtp_caps_locked (FsRtpStream *self);
+
G_END_DECLS
#endif /* __FS_RTP_STREAM_H__ */