diff options
author | Olivier CrĂȘte <olivier.crete@collabora.com> | 2014-05-05 16:15:51 -0400 |
---|---|---|
committer | Nicolas Dufresne <nicolas.dufresne@collabora.co.uk> | 2014-09-18 13:38:00 -0400 |
commit | d95620da48f4c10127eaf32e3a52cdecd3af1f9e (patch) | |
tree | b377410968475e9d72875b0cc4229a1214418c98 | |
parent | 9d4e85377d991abeb01de9c9aac77d2c70fbeef3 (diff) | |
download | farstream-d95620da48f4c10127eaf32e3a52cdecd3af1f9e.tar.gz |
rtpsession: Implemnt setting SRTP encryption key
-rw-r--r-- | gst/fsrtpconference/fs-rtp-session.c | 279 |
1 files changed, 278 insertions, 1 deletions
diff --git a/gst/fsrtpconference/fs-rtp-session.c b/gst/fsrtpconference/fs-rtp-session.c index 10fa5e99..d82c4f2f 100644 --- a/gst/fsrtpconference/fs-rtp-session.c +++ b/gst/fsrtpconference/fs-rtp-session.c @@ -50,7 +50,59 @@ * Also, it is possible to declare profiles with only a decoding pipeline, * you will only be able to receive from this codec, the encoding may be a * secondary pad of some other codec. - * </para></refsect2> + * </para> + * </refsect2> + * <refsect2><title>SRTP signature and encryption</title> + * <para> + * + * To tell #FsRtpSession to authenticate encrypt the media it is + * sending using SRTP, one must set the parameters using a + * #GstStructure named "FarstreamSRTP" and passing it to + * fs_session_set_encryption_parameters(). + * + * The cipher, auth, and key must be specified: + * <refsect3 id="FarstreamSRTP"><title>FarstreamSRTP</title> + * <table> + * <tr> + * <td><code>"rtp-cipher" and "rtcp-cipher"</code></td> + * <td>gchar *</td> + * <td> + * <para>Encryption algorithm</para> + * <para>Possible values: "null", "aes-128-icm" or "aes-256-icm"</para> + * </td> + * </tr> + * <tr> + * <td><code>"cipher"</code></td> + * <td>gchar *</td> + * <td><para>Default value for "rtp-cipher" and "rtcp-cipher"</para> + * <para>Possible values: "null", "aes-128-icm" or "aes-256-icm"</para> + * </td> + * </tr> + * <tr> + * <td><code>"rtp-auth" and "rtcp-auth"</code></td> + * <td>gchar *</td> + * <td> + * <para>Authentication algorithm, can never be null</para> + * <para>Possible values: "hmac-sha1-32" or "hmac-sha1-80"</para> + * </td> + * </tr> + * <tr> + * <td><code>"auth"</code></td> + * <td>gchar *</td> + * <td><para>Default value for "rtp-auth" and "rtcp-auth"</para> + * <para>Possible values: "hmac-sha1-32" or "hmac-sha1-80"</para> + * </td> + * </tr> + * <tr> + * <td><code>"key"</code></td> + * <td>#GstBuffer</td> + * <td>Size must be 30 if cipher is "aes-128-icm" and 46 if cipher is + * "aes-256-icm" </td> + * </tr> + * </table> + * </refsect3> + * </para> + * </refsect2> */ #ifdef HAVE_CONFIG_H @@ -107,6 +159,7 @@ enum PROP_RTP_HEADER_EXTENSION_PREFERENCES, PROP_ALLOWED_SINK_CAPS, PROP_ALLOWED_SRC_CAPS, + PROP_ENCRYPTION_PARAMETERS }; #define DEFAULT_NO_RTCP_TIMEOUT (7000) @@ -218,6 +271,7 @@ struct _FsRtpSessionPrivate /* Protected by session mutex */ guint send_bitrate; + GstStructure *encryption_parameters; /* Protected by session mutex */ guint caps_generation; @@ -334,6 +388,11 @@ static gboolean fs_rtp_session_set_allowed_caps (FsSession *session, GstCaps *sink_caps, GstCaps *src_caps, GError **error); +static gboolean +fs_rtp_session_set_encryption_parameters (FsSession *session, + GstStructure *parameters, GError **error); + + //static guint signals[LAST_SIGNAL] = { 0 }; static void @@ -360,6 +419,8 @@ fs_rtp_session_class_init (FsRtpSessionClass *klass) fs_rtp_session_get_stream_transmitter_type; session_class->codecs_need_resend = fs_rtp_session_get_codecs_need_resend; session_class->set_allowed_caps = fs_rtp_session_set_allowed_caps; + session_class->set_encryption_parameters = + fs_rtp_session_set_encryption_parameters; g_object_class_override_property (gobject_class, PROP_CONFERENCE, "conference"); @@ -383,6 +444,8 @@ fs_rtp_session_class_init (FsRtpSessionClass *klass) PROP_ALLOWED_SINK_CAPS, "allowed-sink-caps"); g_object_class_override_property (gobject_class, PROP_ALLOWED_SRC_CAPS, "allowed-src-caps"); + g_object_class_override_property (gobject_class, + PROP_ENCRYPTION_PARAMETERS, "encryption-parameters"); g_object_class_install_property (gobject_class, PROP_NO_RTCP_TIMEOUT, @@ -814,6 +877,9 @@ fs_rtp_session_finalize (GObject *object) g_queue_foreach (&self->priv->telephony_events, (GFunc) gst_event_unref, NULL); + if (self->priv->encryption_parameters) + gst_structure_free (self->priv->encryption_parameters); + g_rw_lock_clear (&self->priv->disposed_lock); G_OBJECT_CLASS (fs_rtp_session_parent_class)->finalize (object); @@ -973,6 +1039,11 @@ fs_rtp_session_get_property (GObject *object, g_value_set_boxed (value, self->priv->output_caps); FS_RTP_SESSION_UNLOCK (self); break; + case PROP_ENCRYPTION_PARAMETERS: + FS_RTP_SESSION_LOCK (self); + g_value_set_boxed (value, self->priv->encryption_parameters); + FS_RTP_SESSION_UNLOCK (self); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -5280,6 +5351,212 @@ invalid: return TRUE; } +static gint +parse_enum (const gchar *name, const gchar *value, GError **error) +{ + GstElementFactory *factory; + GstPluginFeature *loaded_feature; + GType srtpenc_type; + GObjectClass *srtpenc_class; + GParamSpec *spec; + GParamSpecEnum *enumspec; + GEnumValue *enumvalue; + + if (value == NULL) + goto error; + + factory = gst_element_factory_find ("srtpenc"); + if (!factory) + goto error_not_installed; + + loaded_feature = gst_plugin_feature_load (GST_PLUGIN_FEATURE (factory)); + gst_object_unref (factory); + factory = GST_ELEMENT_FACTORY (loaded_feature); + + srtpenc_type = gst_element_factory_get_element_type (factory); + gst_object_unref (factory); + if (srtpenc_type == 0) + goto error_not_installed; + + srtpenc_class = g_type_class_ref (srtpenc_type); + if (!srtpenc_class) + goto error_not_installed; + + spec = g_object_class_find_property (srtpenc_class, name); + g_type_class_unref (srtpenc_class); + if (!spec) + goto error_internal; + + if (!G_IS_PARAM_SPEC_ENUM (spec)) + goto error_internal; + enumspec = G_PARAM_SPEC_ENUM (spec); + + enumvalue = g_enum_get_value_by_nick (enumspec->enum_class, value); + if (enumvalue) + return enumvalue->value; + + enumvalue = g_enum_get_value_by_name (enumspec->enum_class, value); + if (enumvalue) + return enumvalue->value; + +error: + g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, + "Invalid %s value: %s", name, value); + return -1; + +error_not_installed: + g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, + "Can't find srtpenc, no encryption possible"); + return -1; + +error_internal: + g_set_error (error, FS_ERROR, FS_ERROR_INTERNAL, + "Can't find srtpenc %s property or is not a GEnum type!", name); + return -1; +} + +static gboolean +fs_rtp_session_set_encryption_parameters (FsSession *session, + GstStructure *parameters, GError **error) +{ + FsRtpSession *self = FS_RTP_SESSION (session); + gboolean ret = FALSE; + const gchar *tmp; + GstBuffer *key = NULL; + gint cipher = 0; /* 0 is null cipher, no encryption */ + gint rtp_cipher = -1; + gint rtcp_cipher = -1; + gint auth = -1; + gint rtp_auth = -1; + gint rtcp_auth = -1; + guint replay_window_size = 0; + + g_return_val_if_fail (FS_IS_RTP_SESSION (session), FALSE); + g_return_val_if_fail (parameters == NULL || + GST_IS_STRUCTURE (parameters), FALSE); + + if (fs_rtp_session_has_disposed_enter (self, error)) + return FALSE; + + if (!self->priv->srtpenc) + { + g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, + "Can't set encryption because srtpenc is not installed"); + goto done; + } + + if (parameters) { + const GValue *v = NULL; + + if (!gst_structure_has_name (parameters, "FarstreamSRTPEncrypt")) + { + g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, + "The only structure accepted is FarstreamSRTPEncrypt"); + goto done; + } + if ((tmp = gst_structure_get_string (parameters, "cipher"))) + { + cipher = parse_enum ("rtp-cipher", tmp, error); + if (cipher == -1) + goto done; + } + if ((tmp = gst_structure_get_string (parameters, "rtp-cipher"))) + { + rtp_cipher = parse_enum ("rtp-cipher", tmp, error); + if (cipher == -1) + goto done; + } + if ((tmp = gst_structure_get_string (parameters, "rtcp-cipher"))) + { + rtcp_cipher = parse_enum ("rtcp-cipher", tmp, error); + if (cipher == -1) + goto done; + } + if ((tmp = gst_structure_get_string (parameters, "auth"))) + { + auth = parse_enum ("rtp-auth", tmp, error); + if (cipher == -1) + goto done; + } + if ((tmp = gst_structure_get_string (parameters, "rtp-auth"))) + { + rtp_auth = parse_enum ("rtp-auth", tmp, error); + if (cipher == -1) + goto done; + } + if ((tmp = gst_structure_get_string (parameters, "rtcp-auth"))) + { + rtcp_auth = parse_enum ("rtcp-auth", tmp, error); + if (cipher == -1) + goto done; + } + + if (rtp_cipher == -1) + rtp_cipher = cipher; + if (rtcp_cipher == -1) + rtcp_cipher = cipher; + + if (rtp_auth == -1) + rtp_auth = auth; + if (rtcp_auth == -1) + rtcp_auth = auth; + if (rtp_auth == -1 || rtcp_auth == -1) + { + g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, + "At least the authentication MUST be set, \"auth\" or \"rtp-auth\"" + " and \"rtcp-auth\" are required."); + goto done; + } + + v = gst_structure_get_value (parameters, "key"); + if (!v) { + g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, + "The argument \"key\" is required."); + goto done; + } + if (!GST_VALUE_HOLDS_BUFFER (v) || gst_value_get_buffer (v) == NULL) { + g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, + "The argument \"key\" MUST hold a GstBuffer."); + goto done; + } + key = gst_value_get_buffer (v); + + if (gst_structure_get_uint (parameters, "replay-window-size", + &replay_window_size)) + { + if (replay_window_size < 64 || replay_window_size >= 32768) { + g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, + "Reply window size must be between 64 and 32768"); + goto done; + } + } + } else { + rtcp_auth = rtp_auth = 0; /* 0 is NULL */ + } + + + FS_RTP_SESSION_LOCK (self); + if (self->priv->encryption_parameters) + gst_structure_free (self->priv->encryption_parameters); + + if (parameters) + self->priv->encryption_parameters = gst_structure_copy (parameters); + else + self->priv->encryption_parameters = NULL; + FS_RTP_SESSION_UNLOCK (self); + + g_object_set (self->priv->srtpenc, + "replay-window-size", replay_window_size, + "rtp-auth", rtp_auth, "rtcp-auth", rtcp_auth, + "rtp-cipher", rtp_cipher, "rtcp-cipher", rtcp_cipher, "key", key, NULL); + + ret = TRUE; + +done: + fs_rtp_session_has_disposed_exit (self); + return ret; +} + static gboolean fs_rtp_session_set_allowed_caps (FsSession *session, GstCaps *sink_caps, GstCaps *src_caps, GError **error) |