Commit 2c132ae3 authored by Rafaël Carré's avatar Rafaël Carré Committed by Ilkka Ollakka

livehttp: stream encryption

Uses segment number for IV and takes key-file where to read key for
encryption.
Modified-by: default avatarIlkka Ollakka <ileoo@videolan.org>
parent 90ea97f7
SOURCES_access_output_dummy = dummy.c SOURCES_access_output_dummy = dummy.c
SOURCES_access_output_file = file.c SOURCES_access_output_file = file.c
SOURCES_access_output_livehttp = livehttp.c
SOURCES_access_output_udp = udp.c SOURCES_access_output_udp = udp.c
SOURCES_access_output_http = http.c bonjour.c bonjour.h SOURCES_access_output_http = http.c bonjour.c bonjour.h
SOURCES_access_output_shout = shout.c SOURCES_access_output_shout = shout.c
...@@ -8,7 +7,13 @@ SOURCES_access_output_shout = shout.c ...@@ -8,7 +7,13 @@ SOURCES_access_output_shout = shout.c
libvlc_LTLIBRARIES += \ libvlc_LTLIBRARIES += \
libaccess_output_dummy_plugin.la \ libaccess_output_dummy_plugin.la \
libaccess_output_file_plugin.la \ libaccess_output_file_plugin.la \
libaccess_output_livehttp_plugin.la \
libaccess_output_udp_plugin.la \ libaccess_output_udp_plugin.la \
libaccess_output_http_plugin.la \ libaccess_output_http_plugin.la \
$(NULL) $(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
libvlc_LTLIBRARIES += libaccess_output_livehttp_plugin.la
endif
...@@ -46,6 +46,9 @@ ...@@ -46,6 +46,9 @@
#include <vlc_strings.h> #include <vlc_strings.h>
#include <vlc_charset.h> #include <vlc_charset.h>
#include <gcrypt.h>
#include <vlc_gcrypt.h>
#ifndef O_LARGEFILE #ifndef O_LARGEFILE
# define O_LARGEFILE 0 # define O_LARGEFILE 0
#endif #endif
...@@ -86,6 +89,12 @@ static void Close( vlc_object_t * ); ...@@ -86,6 +89,12 @@ static void Close( vlc_object_t * );
#define RATECONTROL_TEXT N_("Use muxers rate control mechanism") #define RATECONTROL_TEXT N_("Use muxers rate control mechanism")
#define KEYURI_TEXT N_("AES key URI to place in playlist")
#define KEYURI_LONGTEXT N_("Location from where client will retrieve the stream decryption key")
#define KEYFILE_TEXT N_("AES key file")
#define KEYFILE_LONGTEXT N_("File containing the 16 bytes encryption key")
vlc_module_begin () vlc_module_begin ()
set_description( N_("HTTP Live streaming output") ) set_description( N_("HTTP Live streaming output") )
set_shortname( N_("LiveHTTP" )) set_shortname( N_("LiveHTTP" ))
...@@ -107,6 +116,10 @@ vlc_module_begin () ...@@ -107,6 +116,10 @@ vlc_module_begin ()
INDEX_TEXT, INDEX_LONGTEXT, false ) INDEX_TEXT, INDEX_LONGTEXT, false )
add_string( SOUT_CFG_PREFIX "index-url", NULL, add_string( SOUT_CFG_PREFIX "index-url", NULL,
INDEXURL_TEXT, INDEXURL_LONGTEXT, false ) INDEXURL_TEXT, INDEXURL_LONGTEXT, false )
add_string( SOUT_CFG_PREFIX "key-uri", NULL,
KEYURI_TEXT, KEYURI_TEXT, true )
add_loadfile( SOUT_CFG_PREFIX "key-file", NULL,
KEYFILE_TEXT, KEYFILE_LONGTEXT, true )
set_callbacks( Open, Close ) set_callbacks( Open, Close )
vlc_module_end () vlc_module_end ()
...@@ -123,6 +136,8 @@ static const char *const ppsz_sout_options[] = { ...@@ -123,6 +136,8 @@ static const char *const ppsz_sout_options[] = {
"index-url", "index-url",
"ratecontrol", "ratecontrol",
"caching", "caching",
"key-uri",
"key-file",
NULL NULL
}; };
...@@ -148,8 +163,12 @@ struct sout_access_out_sys_t ...@@ -148,8 +163,12 @@ struct sout_access_out_sys_t
bool b_ratecontrol; bool b_ratecontrol;
bool b_splitanywhere; bool b_splitanywhere;
bool b_caching; bool b_caching;
uint8_t aes_ivs[16];
gcry_cipher_hd_t aes_ctx;
char *key_uri;
}; };
static int CryptSetup( sout_access_out_t *p_access );
/***************************************************************************** /*****************************************************************************
* Open: open the file * Open: open the file
*****************************************************************************/ *****************************************************************************/
...@@ -216,6 +235,17 @@ static int Open( vlc_object_t *p_this ) ...@@ -216,6 +235,17 @@ static int Open( vlc_object_t *p_this )
p_sys->psz_indexUrl = var_GetNonEmptyString( p_access, SOUT_CFG_PREFIX "index-url" ); p_sys->psz_indexUrl = var_GetNonEmptyString( p_access, SOUT_CFG_PREFIX "index-url" );
p_access->p_sys = p_sys; p_access->p_sys = p_sys;
if( CryptSetup( p_access ) < 0 )
{
free( p_sys->psz_indexUrl );
free( p_sys->psz_indexPath );
free( p_sys->p_seglens );
free( p_sys );
msg_Err( p_access, "Encryption init failed" );
return VLC_EGENERIC;
}
p_sys->i_handle = -1; p_sys->i_handle = -1;
p_sys->i_segment = 0; p_sys->i_segment = 0;
p_sys->psz_cursegPath = NULL; p_sys->psz_cursegPath = NULL;
...@@ -227,6 +257,91 @@ static int Open( vlc_object_t *p_this ) ...@@ -227,6 +257,91 @@ static int Open( vlc_object_t *p_this )
return VLC_SUCCESS; return VLC_SUCCESS;
} }
/************************************************************************
* CryptSetup: Initialize encryption
************************************************************************/
static int CryptSetup( sout_access_out_t *p_access )
{
sout_access_out_sys_t *p_sys = p_access->p_sys;
uint8_t key[16];
p_sys->key_uri = var_GetNonEmptyString( p_access, SOUT_CFG_PREFIX "key-uri" );
if( !p_sys->key_uri ) /*No key uri, assume no encryption wanted*/
{
msg_Dbg( p_access, "No key uri, no encryption");
return VLC_SUCCESS;
}
vlc_gcrypt_init();
/*Setup encryption cipher*/
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, "Openin AES Cipher failed: %s", gpg_strerror(err));
return VLC_EGENERIC;
}
char *keyfile = var_InheritString( p_access, SOUT_CFG_PREFIX "key-file" );
if( unlikely(keyfile == NULL) )
{
msg_Err( p_access, "No key-file, no encryption" );
return VLC_EGENERIC;
}
int keyfd = vlc_open( keyfile, O_RDONLY | O_NONBLOCK );
if( unlikely( keyfd == -1 ) )
{
msg_Err( p_access, "Unable to open keyfile %s: %m", keyfile );
free( keyfile );
return VLC_EGENERIC;
}
free( keyfile );
ssize_t keylen = read( keyfd, key, 16 );
close( keyfd );
if( keylen < 16 )
{
msg_Err( p_access, "No key at least 16 octects (you provided %zd), no encryption", keylen );
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));
gcry_cipher_close( p_sys->aes_ctx);
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
/************************************************************************
* CryptKey: Set encryption IV to current segment number
************************************************************************/
static int CryptKey( sout_access_out_t *p_access, uint32_t i_segment )
{
sout_access_out_sys_t *p_sys = p_access->p_sys;
memset( p_sys->aes_ivs, 0, 16 * sizeof(uint8_t));
p_sys->aes_ivs[15] = i_segment & 0xff;
p_sys->aes_ivs[14] = (i_segment >> 8 ) & 0xff;
p_sys->aes_ivs[13] = (i_segment >> 16 ) & 0xff;
p_sys->aes_ivs[12] = (i_segment >> 24 ) & 0xff;
gcry_error_t 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) );
gcry_cipher_close( p_sys->aes_ctx);
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
#define SEG_NUMBER_PLACEHOLDER "#" #define SEG_NUMBER_PLACEHOLDER "#"
/***************************************************************************** /*****************************************************************************
* formatSegmentPath: create segment path name based on seg # * formatSegmentPath: create segment path name based on seg #
...@@ -240,7 +355,8 @@ static char *formatSegmentPath( char *psz_path, uint32_t i_seg, bool b_sanitize ...@@ -240,7 +355,8 @@ static char *formatSegmentPath( char *psz_path, uint32_t i_seg, bool b_sanitize
return NULL; return NULL;
psz_firstNumSign = psz_result + strcspn( psz_result, SEG_NUMBER_PLACEHOLDER ); psz_firstNumSign = psz_result + strcspn( psz_result, SEG_NUMBER_PLACEHOLDER );
if ( *psz_firstNumSign ) { if ( *psz_firstNumSign )
{
char *psz_newResult; char *psz_newResult;
int i_cnt = strspn( psz_firstNumSign, SEG_NUMBER_PLACEHOLDER ); int i_cnt = strspn( psz_firstNumSign, SEG_NUMBER_PLACEHOLDER );
int ret; int ret;
...@@ -307,6 +423,16 @@ static int updateIndexAndDel( sout_access_out_t *p_access, sout_access_out_sys_t ...@@ -307,6 +423,16 @@ static int updateIndexAndDel( sout_access_out_t *p_access, sout_access_out_sys_t
return -1; return -1;
} }
if( p_sys->key_uri )
{
if( fprintf( fp, "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"\n", p_sys->key_uri ) < 0 )
{
free( psz_idxTmp );
fclose( fp );
return -1;
}
}
char *psz_idxFormat = p_sys->psz_indexUrl ? p_sys->psz_indexUrl : p_access->psz_path; 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++ ) for ( uint32_t i = i_firstseg; i <= p_sys->i_segment; i++ )
{ {
...@@ -403,6 +529,27 @@ static void Close( vlc_object_t * p_this ) ...@@ -403,6 +529,27 @@ static void Close( vlc_object_t * p_this )
msg_Dbg( p_access, "Flushing buffer to last file"); msg_Dbg( p_access, "Flushing buffer to last file");
while( p_sys->block_buffer ) while( p_sys->block_buffer )
{ {
if( p_sys->key_uri )
{
size_t original = p_sys->block_buffer->i_buffer;
size_t padded = (p_sys->block_buffer->i_buffer + 15 ) & ~15;
if( padded == p_sys->block_buffer->i_buffer )
padded += 16;
p_sys->block_buffer = block_Realloc( p_sys->block_buffer, 0, padded );
if( !p_sys->block_buffer )
break;
int pad = padded - original;
memset( &p_sys->block_buffer->p_buffer[original], pad, pad );
gcry_error_t err = gcry_cipher_encrypt( p_sys->aes_ctx,
p_sys->block_buffer->p_buffer, padded, NULL, 0 );
if( err )
{
msg_Err( p_access, "Encryption failure: %s ", gpg_strerror(err) );
break;
}
}
ssize_t val = write( p_sys->i_handle, p_sys->block_buffer->p_buffer, p_sys->block_buffer->i_buffer ); ssize_t val = write( p_sys->i_handle, p_sys->block_buffer->p_buffer, p_sys->block_buffer->i_buffer );
if ( val == -1 ) if ( val == -1 )
{ {
...@@ -437,6 +584,11 @@ static void Close( vlc_object_t * p_this ) ...@@ -437,6 +584,11 @@ static void Close( vlc_object_t * p_this )
free( p_sys->p_seglens ); free( p_sys->p_seglens );
free( p_sys ); free( p_sys );
if( p_sys->key_uri )
{
gcry_cipher_close( p_sys->aes_ctx );
free( p_sys->key_uri );
}
msg_Dbg( p_access, "livehttp access output closed" ); msg_Dbg( p_access, "livehttp access output closed" );
} }
...@@ -482,6 +634,7 @@ static ssize_t openNextFile( sout_access_out_t *p_access, sout_access_out_sys_t ...@@ -482,6 +634,7 @@ static ssize_t openNextFile( sout_access_out_t *p_access, sout_access_out_sys_t
return -1; return -1;
} }
CryptKey( p_access, i_newseg );
msg_Dbg( p_access, "Successfully opened livehttp file: %s (%"PRIu32")" , psz_seg, i_newseg ); msg_Dbg( p_access, "Successfully opened livehttp file: %s (%"PRIu32")" , psz_seg, i_newseg );
//free( psz_seg ); //free( psz_seg );
...@@ -523,6 +676,27 @@ static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer ) ...@@ -523,6 +676,27 @@ static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer )
while( output ) while( output )
{ {
if( p_sys->key_uri )
{
size_t original = output->i_buffer;
size_t padded = (output->i_buffer + 15 ) & ~15;
if( padded == output->i_buffer )
padded += 16;
output = block_Realloc( output, 0, padded );
if( !output )
return VLC_ENOMEM;
int pad = padded - original;
memset( &output->p_buffer[original], pad, pad );
gcry_error_t err = gcry_cipher_encrypt( p_sys->aes_ctx,
output->p_buffer, padded, NULL, 0 );
if( err )
{
msg_Err( p_access, "Encryption failure: %s ", gpg_strerror(err) );
return -1;
}
}
ssize_t val = write( p_sys->i_handle, output->p_buffer, output->i_buffer ); ssize_t val = write( p_sys->i_handle, output->p_buffer, output->i_buffer );
if ( val == -1 ) if ( val == -1 )
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment