[vlc-devel] [PATCH] HLS output: add encryption support
Rafaël Carré
funman at videolan.org
Tue Mar 6 23:35:37 CET 2012
Le 2012-03-06 16:45, Rafaël Carré a écrit :
> Set --sout-livehttp-key-uri to the URI which will be written in the m3u8
> VLC will fetch the key and use it for encryption
>
> The key is per stream and not per segment
> ---
> Note, it was suggested that we provide a way to set the key without fetching it.
> i.e. have a --key=0x12345678 (up to 32 digits), in case the key is fetched from
> a keyserver which is accessible only to the client.
>
>
> modules/access_output/Modules.am | 12 +++-
> modules/access_output/livehttp.c | 152 ++++++++++++++++++++++++++++++++++++--
> 2 files changed, 155 insertions(+), 9 deletions(-)
>
> diff --git a/modules/access_output/Modules.am b/modules/access_output/Modules.am
> index 631b600..43bb5c2 100644
> --- a/modules/access_output/Modules.am
> +++ b/modules/access_output/Modules.am
> @@ -1,6 +1,5 @@
> SOURCES_access_output_dummy = dummy.c
> SOURCES_access_output_file = file.c
> -SOURCES_access_output_livehttp = livehttp.c
> SOURCES_access_output_udp = udp.c
> SOURCES_access_output_http = http.c bonjour.c bonjour.h
> SOURCES_access_output_shout = shout.c
> @@ -17,10 +16,19 @@ libaccess_output_rtmp_plugin_la_DEPENDENCIES =
> libvlc_LTLIBRARIES += \
> libaccess_output_dummy_plugin.la \
> libaccess_output_file_plugin.la \
> - libaccess_output_livehttp_plugin.la \
> libaccess_output_udp_plugin.la \
> libaccess_output_http_plugin.la \
> $(NULL)
> +
> +if HAVE_GCRYPT
> +libaccess_output_livehttp_plugin_la_SOURCES = livehttp.c
> +libaccess_output_livehttp_plugin_la_CFLAGS = $(AM_CFLAGS) $(GCRYPT_CFLAGS)
> +libaccess_output_livehttp_plugin_la_LIBADD = $(AM_LIBADD) $(GCRYPT_LIBS) -lgpg-error
> +libaccess_output_livehttp_plugin_la_DEPENDENCIES =
> +libvlc_LTLIBRARIES += libaccess_output_livehttp_plugin.la
> +endif
> +
> +
> EXTRA_LTLIBRARIES += \
> libaccess_output_rtmp_plugin.la \
> $(NULL)
> diff --git a/modules/access_output/livehttp.c b/modules/access_output/livehttp.c
> index d335a64..727ea50 100644
> --- a/modules/access_output/livehttp.c
> +++ b/modules/access_output/livehttp.c
> @@ -37,6 +37,8 @@
> # include <unistd.h>
> #endif
>
> +#include <gcrypt.h>
> +
> #include <vlc_common.h>
> #include <vlc_plugin.h>
> #include <vlc_sout.h>
> @@ -45,6 +47,10 @@
> #include <vlc_strings.h>
> #include <vlc_charset.h>
>
> +#include <vlc_rand.h>
> +#include <vlc_gcrypt.h>
> +#include <vlc_stream.h>
> +
> #ifndef O_LARGEFILE
> # define O_LARGEFILE 0
> #endif
> @@ -82,6 +88,8 @@ static void Close( vlc_object_t * );
>
> #define RATECONTROL_TEXT N_("Use muxers rate control mechanism")
>
> +#define KEY_TEXT N_("AES key URI for encryption")
> +
> vlc_module_begin ()
> set_description( N_("HTTP Live streaming output") )
> set_shortname( N_("LiveHTTP" ))
> @@ -97,6 +105,8 @@ vlc_module_begin ()
> DELSEGS_TEXT, DELSEGS_LONGTEXT, true )
> add_bool( SOUT_CFG_PREFIX "ratecontrol", false,
> RATECONTROL_TEXT, RATECONTROL_TEXT, true )
> + add_string( SOUT_CFG_PREFIX "key-uri", NULL,
> + KEY_TEXT, KEY_TEXT, true )
> add_string( SOUT_CFG_PREFIX "index", NULL,
> INDEX_TEXT, INDEX_LONGTEXT, true )
> add_string( SOUT_CFG_PREFIX "index-url", NULL,
> @@ -116,6 +126,7 @@ static const char *const ppsz_sout_options[] = {
> "index",
> "index-url",
> "ratecontrol",
> + "key-uri",
> NULL
> };
>
> @@ -137,9 +148,70 @@ struct sout_access_out_sys_t
> bool b_delsegs;
> bool b_ratecontrol;
> bool b_splitanywhere;
> +
> + uint8_t aes_ivs[16];
> + gcry_cipher_hd_t aes_ctx;
> + char *key_uri;
> };
>
> /*****************************************************************************
> + * CryptSetup:
> + *****************************************************************************/
> +static int CryptSetup( sout_access_out_t *p_access )
> +{
> + sout_access_out_sys_t *p_sys = p_access->p_sys;
> +
> + p_sys->key_uri = var_GetNonEmptyString( p_access, SOUT_CFG_PREFIX "key-uri" );
> + if (!p_sys->key_uri) /* no encryption requested */
> + return VLC_SUCCESS;
> +
> + uint8_t key[16];
> + vlc_gcrypt_init();
> +
> + stream_t *s = stream_UrlNew(p_access, p_sys->key_uri);
> + if (!s)
> + {
> + msg_Err( p_access, "Couldn't open key URI %s", p_sys->key_uri);
> + return VLC_EGENERIC;
> + }
> +
> + int ret = stream_Read(s, key, 16);
> + stream_Delete(s);
> + if (ret != 16)
> + {
> + msg_Err(p_access, "The AES key loaded doesn't have the right size (%d)",
> + ret);
> + return VLC_EGENERIC;
> + }
> +
> + gcry_error_t err = gcry_cipher_open(&p_sys->aes_ctx, GCRY_CIPHER_AES,
> + GCRY_CIPHER_MODE_CBC, 0);
> + if (err) {
> + msg_Err(p_access, "Opening AES cipher failed: %s", gpg_strerror(err));
> + return VLC_EGENERIC;
> + }
> + err = gcry_cipher_setkey(p_sys->aes_ctx, key, 16);
> + if (err) {
> + msg_Err(p_access, "Setting AES key failed: %s", gpg_strerror(err));
> + goto encrypt_fail;
> + }
> +
> + vlc_rand_bytes(p_sys->aes_ivs, 16);
> +
> + err = gcry_cipher_setiv(p_sys->aes_ctx, p_sys->aes_ivs, 16);
> + if (err) {
> + msg_Err(p_access, "Setting AES IVs failed: %s", gpg_strerror(err));
> + goto encrypt_fail;
> + }
> +
> + return VLC_SUCCESS;
> +
> +encrypt_fail:
> + gcry_cipher_close(p_sys->aes_ctx);
> + return VLC_EGENERIC;
> +}
> +
> +/*****************************************************************************
> * Open: open the file
> *****************************************************************************/
> static int Open( vlc_object_t *p_this )
> @@ -195,6 +267,15 @@ static int Open( vlc_object_t *p_this )
> p_access->pf_seek = Seek;
> p_access->pf_control = Control;
>
> + if (CryptSetup(p_access))
> + {
> + free( p_sys->key_uri );
> + free( p_sys->psz_indexUrl );
> + free( p_sys->psz_indexPath );
> + free(p_sys);
> + return VLC_EGENERIC;
> + }
> +
> return VLC_SUCCESS;
> }
>
> @@ -267,6 +348,25 @@ static int updateIndexAndDel( sout_access_out_t *p_access, sout_access_out_sys_t
> return -1;
> }
>
> + if (p_sys->key_uri) {
> + unsigned long long iv_hi = 0, iv_lo = 0;
> + for (int i = 0; i < 8; i++)
> + {
> + iv_hi |= p_sys->aes_ivs[i] & 0xff;
> + iv_hi <<= 8;
> + iv_lo |= p_sys->aes_ivs[8+i] & 0xff;
> + iv_lo <<= 8;
> + }
> + if ( fprintf(fp,
> + "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\",IV=0X%16.16llx%16.16llx\n",
> + p_sys->key_uri, iv_hi, iv_lo
> + ) < 0 ) {
> + free( psz_idxTmp );
> + fclose( fp );
> + return -1;
> + }
> + }
> +
> char *psz_idxFormat = p_sys->psz_indexUrl ? p_sys->psz_indexUrl : p_access->psz_path;
> for ( uint32_t i = i_firstseg; i <= p_sys->i_segment; i++ )
> {
> @@ -356,9 +456,12 @@ static void Close( vlc_object_t * p_this )
> closeCurrentSegment( p_access, p_sys, true );
> free( p_sys->psz_indexUrl );
> free( p_sys->psz_indexPath );
> + if (p_sys->key_uri)
> + {
> + gcry_cipher_close(p_sys->aes_ctx);
> + free(p_sys->key_uri);
> + }
> free( p_sys );
> -
> - msg_Dbg( p_access, "livehttp access output closed" );
> }
>
> static int Control( sout_access_out_t *p_access, int i_query, va_list args )
> @@ -419,9 +522,37 @@ static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer )
> {
> size_t i_write = 0;
> sout_access_out_sys_t *p_sys = p_access->p_sys;
> + bool b_encrypt = p_sys->key_uri != NULL;
> +
> + uint8_t *buf = NULL;
> + size_t size = 0;
> +
> + if (p_buffer && !b_encrypt)
> + {
> + buf = p_buffer->p_buffer;
> + size = p_buffer->i_buffer;
> + }
>
> while( p_buffer )
> {
> + if (b_encrypt) {
> + size = (p_buffer->i_buffer + 15) & ~15;
> + block_t *padded = block_Realloc(p_buffer, 0, size);
Found a little problem here after fixing VLC input to also accept
0Xabcdef for IVs: I forgot to actually fill the padding.
The stream is anyway accepted by avplay, mplayer, and quicktime on iOS 4.3.
> + if (!padded || !(buf = malloc(size)))
> + {
> + errno = ENOMEM;
> + return -1;
> + }
> +
> + gcry_error_t err = gcry_cipher_encrypt (p_sys->aes_ctx,
> + buf, size, p_buffer->p_buffer, p_buffer->i_buffer);
> + if (err) {
> + msg_Err(p_access, "Couldn't encrypt %zu bytes: %s", p_buffer->i_buffer, gpg_strerror(err));
> + return -1;
> + }
> + b_encrypt = false;
> + }
> +
> if ( p_sys->i_handle >= 0 && ( p_sys->b_splitanywhere || ( p_buffer->i_flags & BLOCK_FLAG_TYPE_I ) ) && ( p_buffer->i_dts-p_sys->i_opendts ) > p_sys->i_seglenm )
> {
> closeCurrentSegment( p_access, p_sys, false );
> @@ -432,8 +563,7 @@ static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer )
> if ( openNextFile( p_access, p_sys ) < 0 )
> return -1;
> }
> - ssize_t val = write ( p_sys->i_handle,
> - p_buffer->p_buffer, p_buffer->i_buffer );
> + ssize_t val = write ( p_sys->i_handle, buf, size );
> if ( val == -1 )
> {
> if ( errno == EINTR )
> @@ -442,16 +572,24 @@ static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer )
> return -1;
> }
>
> - if ( (size_t)val >= p_buffer->i_buffer )
> + if ( (size_t)val >= size )
> {
> block_t *p_next = p_buffer->p_next;
> block_Release (p_buffer);
> p_buffer = p_next;
> + b_encrypt = p_sys->key_uri != NULL;
> + if (b_encrypt)
> + free(buf);
> + else if (p_buffer)
> + {
> + buf = p_buffer->p_buffer;
> + size = p_buffer->i_buffer;
> + }
> }
> else
> {
> - p_buffer->p_buffer += val;
> - p_buffer->i_buffer -= val;
> + buf += val;
> + size -= val;
> }
> i_write += val;
> }
More information about the vlc-devel
mailing list