[vlc-devel] [PATCH v2] HLS output: add encryption support

Rafaël Carré funman at videolan.org
Wed Mar 7 00:41:11 CET 2012


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.

v2:
    - set PKCS7 padding correctly (the padding is discarded for TS output)
    - fix possible use after free in case block_Realloc returns a different block

 modules/access_output/Modules.am |   12 +++-
 modules/access_output/livehttp.c |  159 ++++++++++++++++++++++++++++++++++++--
 2 files changed, 162 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..309cea5 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,44 @@ 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_t orig_size = p_buffer->i_buffer;
+            size = (orig_size + 15) & ~15;
+            if (size == orig_size)
+                size += 16;
+            p_buffer = block_Realloc(p_buffer, 0, size);
+            if (!p_buffer || !(buf = malloc(size)))
+            {
+                errno = ENOMEM;
+                return -1;
+            }
+
+            int pad = size - orig_size;
+            memset(&p_buffer->p_buffer[orig_size], pad, pad);
+
+            gcry_error_t err = gcry_cipher_encrypt(p_sys->aes_ctx,
+                    buf, size, p_buffer->p_buffer, size);
+            if (err) {
+                msg_Err(p_access, "Couldn't encrypt %zu bytes: %s", size,
+                        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 +570,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 +579,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;
     }
-- 
1.7.9



More information about the vlc-devel mailing list