diff options
author | Olivier CrĂȘte <olivier.crete@collabora.com> | 2014-05-05 21:26:39 -0400 |
---|---|---|
committer | Nicolas Dufresne <nicolas.dufresne@collabora.co.uk> | 2014-09-18 13:38:01 -0400 |
commit | 344468a3b7a6828c0c16f95c81b99ed3af8c001a (patch) | |
tree | 1487a7fc1958db79cbb9dd309261aba382dda792 /gst | |
parent | 8989360664f68944ca6c00e69b2d88194fed4c38 (diff) | |
download | farstream-344468a3b7a6828c0c16f95c81b99ed3af8c001a.tar.gz |
rtp: Implement setting SRTP decryption key
Diffstat (limited to 'gst')
-rw-r--r-- | gst/fsrtpconference/fs-rtp-session.c | 188 | ||||
-rw-r--r-- | gst/fsrtpconference/fs-rtp-stream.c | 166 | ||||
-rw-r--r-- | gst/fsrtpconference/fs-rtp-stream.h | 7 |
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__ */ |