[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