hlsdemux: Prefer to use nettle for decryption but fall-back to libgcrypt

nettle is used by newer versions of gnutls, while older versions of gnutls
used libgcrypt. Support both for now as not every distro has nettle yet.

nettle is preferred as it is more efficient to use and much smaller.
This commit is contained in:
Sebastian Dröge 2014-02-09 18:49:49 +01:00
parent 6abed8da20
commit 6799e3b879
3 changed files with 78 additions and 26 deletions

View file

@ -2211,7 +2211,19 @@ AG_GST_CHECK_FEATURE(SNDIO, [sndio audio], sndio, [
dnl *** hls ***
translit(dnm, m, l) AM_CONDITIONAL(USE_HLS, true)
AG_GST_CHECK_FEATURE(HLS, [http live streaming plugin], hls, [
AM_PATH_LIBGCRYPT([1.2.0], [ HAVE_HLS="yes" ], [ HAVE_HLS="no" ])
PKG_CHECK_MODULES(NETTLE, nettle,
[
AC_DEFINE(HAVE_NETTLE, 1, [Define if nettle is available])
HAVE_HLS="yes"
], [
AM_PATH_LIBGCRYPT([1.2.0],
[
AC_DEFINE(HAVE_LIBGCRYPT, 1, [Define if libgcrypt is available])
HAVE_HLS="yes"
], [
HAVE_HLS="no"
])
])
])
else

View file

@ -8,11 +8,11 @@ libgstfragmented_la_SOURCES = \
gsthlssink.c \
gstm3u8playlist.c
libgstfragmented_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(GIO_CFLAGS) $(LIBGCRYPT_CFLAGS)
libgstfragmented_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(GIO_CFLAGS) $(LIBGCRYPT_CFLAGS) $(NETTLE_CFLAGS)
libgstfragmented_la_LIBADD = \
$(top_builddir)/gst-libs/gst/uridownloader/libgsturidownloader-@GST_API_VERSION@.la \
$(GST_PLUGINS_BASE_LIBS) -lgstpbutils-$(GST_API_VERSION) -lgstvideo-$(GST_API_VERSION) \
$(GST_BASE_LIBS) $(GST_LIBS) $(GIO_LIBS) $(LIBM) $(LIBGCRYPT_LIBS)
$(GST_BASE_LIBS) $(GST_LIBS) $(GIO_LIBS) $(LIBM) $(LIBGCRYPT_LIBS) $(NETTLE_LIBS)
libgstfragmented_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -no-undefined
libgstfragmented_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)

View file

@ -42,7 +42,12 @@
#endif
#include <string.h>
#ifdef HAVE_NETTLE
#include <nettle/aes.h>
#include <nettle/cbc.h>
#else
#include <gcrypt.h>
#endif
#include "gsthlsdemux.h"
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src_%u",
@ -1231,6 +1236,59 @@ gst_hls_demux_switch_playlist (GstHLSDemux * demux)
return gst_hls_demux_change_playlist (demux, bitrate * demux->bitrate_limit);
}
#ifdef HAVE_NETTLE
static gboolean
decrypt_fragment (GstHLSDemux * demux, gsize length,
const guint8 * encrypted_data, guint8 * decrypted_data,
const guint8 * key_data, const guint8 * iv_data)
{
struct CBC_CTX (struct aes_ctx, AES_BLOCK_SIZE) aes_ctx;
if (length % 16 != 0)
return FALSE;
aes_set_decrypt_key (&aes_ctx.ctx, 16, key_data);
CBC_SET_IV (&aes_ctx, iv_data);
CBC_DECRYPT (&aes_ctx, aes_decrypt, length, decrypted_data, encrypted_data);
return TRUE;
}
#else
static gboolean
decrypt_fragment (GstHLSDemux * demux, gsize length,
const guint8 * encrypted_data, guint8 * decrypted_data,
const guint8 * key_data, const guint8 * iv_data)
{
gcry_cipher_hd_t aes_ctx = NULL;
gcry_error_t err = 0;
gboolean ret = FALSE;
err =
gcry_cipher_open (&aes_ctx, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, 0);
if (err)
goto out;
err = gcry_cipher_setkey (aes_ctx, key_data, 16);
if (err)
goto out;
err = gcry_cipher_setiv (aes_ctx, iv_data, 16);
if (err)
goto out;
err = gcry_cipher_decrypt (aes_ctx, decrypted_data, length,
encrypted_data, length);
if (err)
goto out;
ret = TRUE;
out:
if (aes_ctx)
gcry_cipher_close (aes_ctx);
return ret;
}
#endif
static GstFragment *
gst_hls_demux_decrypt_fragment (GstHLSDemux * demux,
GstFragment * encrypted_fragment, const gchar * key, const guint8 * iv)
@ -1238,8 +1296,6 @@ gst_hls_demux_decrypt_fragment (GstHLSDemux * demux,
GstFragment *key_fragment, *ret = NULL;
GstBuffer *key_buffer, *encrypted_buffer, *decrypted_buffer;
GstMapInfo key_info, encrypted_info, decrypted_info;
gcry_cipher_hd_t aes_ctx = NULL;
gcry_error_t err = 0;
gsize unpadded_size;
GST_INFO_OBJECT (demux, "Fetching key %s", key);
@ -1257,21 +1313,9 @@ gst_hls_demux_decrypt_fragment (GstHLSDemux * demux,
gst_buffer_map (encrypted_buffer, &encrypted_info, GST_MAP_READ);
gst_buffer_map (decrypted_buffer, &decrypted_info, GST_MAP_WRITE);
err =
gcry_cipher_open (&aes_ctx, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, 0);
if (err)
goto gcry_error;
err = gcry_cipher_setkey (aes_ctx, key_info.data, 16);
if (err)
goto gcry_error;
err = gcry_cipher_setiv (aes_ctx, iv, 16);
if (err)
goto gcry_error;
err = gcry_cipher_decrypt (aes_ctx, decrypted_info.data, decrypted_info.size,
encrypted_info.data, encrypted_info.size);
if (err)
goto gcry_error;
gcry_cipher_close (aes_ctx);
if (!decrypt_fragment (demux, encrypted_info.size,
encrypted_info.data, decrypted_info.data, key_info.data, iv))
goto decrypt_error;
/* Handle pkcs7 unpadding here */
unpadded_size =
@ -1294,12 +1338,8 @@ key_failed:
g_object_unref (encrypted_fragment);
return ret;
gcry_error:
GST_ERROR_OBJECT (demux, "Failed to decrypt fragment: %s",
gpg_strerror (err));
if (aes_ctx)
gcry_cipher_close (aes_ctx);
decrypt_error:
GST_ERROR_OBJECT (demux, "Failed to decrypt fragment");
gst_buffer_unref (key_buffer);
gst_buffer_unref (encrypted_buffer);