From: Kaloyan Kovachev Date: Wed, 11 Jun 2008 15:22:14 +0000 (+0300) Subject: Dynamic two-keys CSA X-Git-Tag: 0.9.0-test0~15 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=320337e01ad4696b5f011389f72a3516468bd24b;p=vlc Dynamic two-keys CSA N.B.: I suspect there is a small race condition whereby the callbacks are invoked while being deleted, but this looks like yet another LibVLC objects insanity. Signed-off-by: RĂ©mi Denis-Courmont --- diff --git a/modules/demux/ts.c b/modules/demux/ts.c index b98666c0cf..a6b0fe9bac 100644 --- a/modules/demux/ts.c +++ b/modules/demux/ts.c @@ -87,6 +87,11 @@ * - ... */ +/***************************************************************************** + * Callback prototypes + *****************************************************************************/ +static int ChangeKeyCallback ( vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void * ); + /***************************************************************************** * Module descriptor *****************************************************************************/ @@ -113,6 +118,10 @@ static void Close ( vlc_object_t * ); #define CSA_TEXT N_("CSA ck") #define CSA_LONGTEXT N_("Control word for the CSA encryption algorithm") +#define CSA2_TEXT N_("Second CSA Key") +#define CSA2_LONGTEXT N_("The even CSA encryption key. This must be a " \ + "16 char string (8 hexadecimal bytes).") + #define SILENT_TEXT N_("Silent mode") #define SILENT_LONGTEXT N_("Do not complain on encrypted PES.") @@ -149,6 +158,7 @@ vlc_module_begin(); add_integer( "ts-out-mtu", 1400, NULL, MTUOUT_TEXT, MTUOUT_LONGTEXT, true ); add_string( "ts-csa-ck", NULL, NULL, CSA_TEXT, CSA_LONGTEXT, true ); + add_string( "ts-csa2-ck", NULL, NULL, CSA_TEXT, CSA_LONGTEXT, true ); add_integer( "ts-csa-pkt", 188, NULL, CPKT_TEXT, CPKT_LONGTEXT, true ); add_bool( "ts-silent", 0, NULL, SILENT_TEXT, SILENT_LONGTEXT, true ); @@ -311,6 +321,8 @@ typedef struct struct demux_sys_t { + vlc_mutex_t csa_lock; + /* TS packet size (188, 192, 204) */ int i_packet_size; @@ -539,6 +551,7 @@ static int Open( vlc_object_t *p_this ) return VLC_ENOMEM; memset( p_sys, 0, sizeof( demux_sys_t ) ); p_sys->i_packet_size = i_packet_size; + vlc_mutex_init( &p_sys->csa_lock ); /* Fill dump mode fields */ p_sys->i_write = 0; @@ -773,24 +786,42 @@ static int Open( vlc_object_t *p_this ) } free( val.psz_string ); - var_Create( p_demux, "ts-csa-ck", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_Create( p_demux, "ts-csa-ck", VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND ); var_Get( p_demux, "ts-csa-ck", &val ); if( val.psz_string && *val.psz_string ) { int i_res; + vlc_value_t csa2; p_sys->csa = csa_New(); - i_res = csa_SetCW( (vlc_object_t*)p_demux, p_sys->csa, val.psz_string, 1 ); - if( i_res != VLC_SUCCESS || csa_SetCW( (vlc_object_t*)p_demux, p_sys->csa, val.psz_string, 0 ) != VLC_SUCCESS ) + var_Create( p_demux, "ts-csa2-ck", VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND); + var_Get( p_demux, "ts-csa2-ck", &csa2 ); + i_res = csa_SetCW( (vlc_object_t*)p_demux, p_sys->csa, val.psz_string, true ); + if( i_res == VLC_SUCCESS && csa2.psz_string && *csa2.psz_string ) + { + if( csa_SetCW( (vlc_object_t*)p_demux, p_sys->csa, csa2.psz_string, false ) != VLC_SUCCESS ) + { + csa_SetCW( (vlc_object_t*)p_demux, p_sys->csa, val.psz_string, false ); + } + } + else if ( i_res == VLC_SUCCESS ) + { + csa_SetCW( (vlc_object_t*)p_demux, p_sys->csa, val.psz_string, false ); + } + else { csa_Delete( p_sys->csa ); + p_sys->csa = NULL; } if( p_sys->csa ) { vlc_value_t pkt_val; + var_AddCallback( p_demux, "ts-csa-ck", ChangeKeyCallback, (void *)1 ); + var_AddCallback( p_demux, "ts-csa2-ck", ChangeKeyCallback, NULL ); + var_Create( p_demux, "ts-csa-pkt", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); var_Get( p_demux, "ts-csa-pkt", &pkt_val ); if( pkt_val.i_int < 4 || pkt_val.i_int > 188 ) @@ -802,6 +833,7 @@ static int Open( vlc_object_t *p_this ) else p_sys->i_csa_pkt_size = pkt_val.i_int; msg_Dbg( p_demux, "decrypting %d bytes of packet", p_sys->i_csa_pkt_size ); } + free( csa2.psz_string ); } free( val.psz_string ); @@ -873,10 +905,15 @@ static void Close( vlc_object_t *p_this ) net_Close( p_sys->fd ); free( p_sys->buffer ); } + vlc_mutex_lock( &p_sys->csa_lock ); if( p_sys->csa ) { + var_DelCallback( p_demux, "ts-csa-ck", ChangeKeyCallback, NULL ); + var_DelCallback( p_demux, "ts-csa2-ck", ChangeKeyCallback, NULL ); csa_Delete( p_sys->csa ); + p_sys->csa = NULL; } + vlc_mutex_unlock( &p_sys->csa_lock ); if( p_sys->i_pmt ) free( p_sys->pmt ); @@ -905,9 +942,32 @@ static void Close( vlc_object_t *p_this ) free( p_sys->psz_file ); p_sys->psz_file = NULL; + vlc_mutex_destroy( &p_sys->csa_lock ); free( p_sys ); } +/***************************************************************************** + * ChangeKeyCallback: called when changing the odd encryption key on the fly. + *****************************************************************************/ +static int ChangeKeyCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void *p_data ) +{ + VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); + demux_t *p_demux = (demux_t*)p_this; + demux_sys_t *p_sys = p_demux->p_sys; + int i_tmp = (intptr_t)p_data; + + vlc_mutex_lock( &p_sys->csa_lock ); + if ( i_tmp ) + i_tmp = csa_SetCW( p_this, p_sys->csa, newval.psz_string, true ); + else + i_tmp = csa_SetCW( p_this, p_sys->csa, newval.psz_string, false ); + + vlc_mutex_unlock( &p_sys->csa_lock ); + return i_tmp; +} + /***************************************************************************** * DemuxFile: *****************************************************************************/ @@ -997,7 +1057,11 @@ static int DemuxFile( demux_t *p_demux ) /* Test if user wants to decrypt it first */ if( p_sys->csa ) + { + vlc_mutex_lock( &p_sys->csa_lock ); csa_Decrypt( p_demux->p_sys->csa, &p_buffer[i_pos], p_demux->p_sys->i_csa_pkt_size ); + vlc_mutex_unlock( &p_sys->csa_lock ); + } i_pos += p_sys->i_packet_size; } @@ -1789,7 +1853,9 @@ static bool GatherPES( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk ) if( p_demux->p_sys->csa ) { + vlc_mutex_lock( &p_demux->p_sys->csa_lock ); csa_Decrypt( p_demux->p_sys->csa, p_bk->p_buffer, p_demux->p_sys->i_csa_pkt_size ); + vlc_mutex_unlock( &p_demux->p_sys->csa_lock ); } if( !b_adaptation ) diff --git a/modules/mux/mpeg/csa.c b/modules/mux/mpeg/csa.c index ea7b5897d5..9550297473 100644 --- a/modules/mux/mpeg/csa.c +++ b/modules/mux/mpeg/csa.c @@ -50,6 +50,8 @@ struct csa_t int X, Y, Z; int D, E, F; int p, q, r; + + bool use_odd; }; static void csa_ComputeKey( uint8_t kk[57], uint8_t ck[8] ); @@ -81,12 +83,12 @@ void csa_Delete( csa_t *c ) /***************************************************************************** * csa_SetCW: *****************************************************************************/ -int csa_SetCW( vlc_object_t *p_caller, csa_t *c, char *psz_ck, int set_odd ) +int csa_SetCW( vlc_object_t *p_caller, csa_t *c, char *psz_ck, bool set_odd ) { if ( !c ) { msg_Dbg( p_caller, "no CSA found" ); - return VLC_EGENERIC; + return VLC_ENOOBJ; } /* skip 0x */ if( psz_ck[0] == '0' && ( psz_ck[1] == 'x' || psz_ck[1] == 'X' ) ) @@ -96,7 +98,7 @@ int csa_SetCW( vlc_object_t *p_caller, csa_t *c, char *psz_ck, int set_odd ) if( strlen( psz_ck ) != 16 ) { msg_Warn( p_caller, "invalid csa ck (it must be 16 chars long)" ); - return VLC_EGENERIC; + return VLC_EBADVAR; } else { @@ -113,23 +115,38 @@ int csa_SetCW( vlc_object_t *p_caller, csa_t *c, char *psz_ck, int set_odd ) ck[i] = ( i_ck >> ( 56 - 8*i) )&0xff; } #ifndef TS_NO_CSA_CK_MSG - msg_Dbg( p_caller, "using CSA (de)scrambling with %s key=%x:%x:%x:%x:%x:%x:%x:%x", ((set_odd == 1) ? "odd" : "even" ), + msg_Dbg( p_caller, "using CSA (de)scrambling with %s " + "key=%x:%x:%x:%x:%x:%x:%x:%x", set_odd ? "odd" : "even", ck[0], ck[1], ck[2], ck[3], ck[4], ck[5], ck[6], ck[7] ); #endif - if ( set_odd == 1 ) + if( set_odd ) { - memcpy( c->o_ck, ck, 8 ); - csa_ComputeKey( c->o_kk, ck ); + memcpy( c->o_ck, ck, 8 ); + csa_ComputeKey( c->o_kk, ck ); } else { - memcpy( c->e_ck , ck, 8 ); - csa_ComputeKey( c->e_kk , ck ); + memcpy( c->e_ck , ck, 8 ); + csa_ComputeKey( c->e_kk , ck ); } return VLC_SUCCESS; } } +/***************************************************************************** + * csa_UseKey: + *****************************************************************************/ +int csa_UseKey( vlc_object_t *p_caller, csa_t *c, bool use_odd ) +{ + if ( !c ) return VLC_ENOOBJ; + c->use_odd = use_odd; +#ifndef TS_NO_CSA_CK_MSG + msg_Dbg( p_caller, "using the %s key for scrambling", + use_odd ? "odd" : "even" ); +#endif + return VLC_SUCCESS; +} + /***************************************************************************** * csa_Decrypt: *****************************************************************************/ @@ -222,7 +239,7 @@ void csa_Decrypt( csa_t *c, uint8_t *pkt, int i_pkt_size ) /***************************************************************************** * csa_Encrypt: *****************************************************************************/ -void csa_Encrypt( csa_t *c, uint8_t *pkt, int i_pkt_size, int b_odd ) +void csa_Encrypt( csa_t *c, uint8_t *pkt, int i_pkt_size ) { uint8_t *ck; uint8_t *kk; @@ -234,13 +251,10 @@ void csa_Encrypt( csa_t *c, uint8_t *pkt, int i_pkt_size, int b_odd ) /* set transport scrambling control */ pkt[3] |= 0x80; - if( b_odd ) - { - pkt[3] |= 0x40; - } - if( b_odd ) + if( c->use_odd ) { + pkt[3] |= 0x40; ck = c->o_ck; kk = c->o_kk; } diff --git a/modules/mux/mpeg/csa.h b/modules/mux/mpeg/csa.h index 18c1ecc97b..288836607b 100644 --- a/modules/mux/mpeg/csa.h +++ b/modules/mux/mpeg/csa.h @@ -28,15 +28,17 @@ typedef struct csa_t csa_t; #define csa_New __csa_New #define csa_Delete __csa_Delete #define csa_SetCW __csa_SetCW +#define csa_UseKey __csa_UseKey #define csa_Decrypt __csa_decrypt #define csa_Encrypt __csa_encrypt csa_t *csa_New( void ); void csa_Delete( csa_t * ); -int csa_SetCW( vlc_object_t *p_caller, csa_t *c, char *psz_ck, int set_odd ); +int csa_SetCW( vlc_object_t *p_caller, csa_t *c, char *psz_ck, bool odd ); +int csa_UseKey( vlc_object_t *p_caller, csa_t *, bool use_odd ); void csa_Decrypt( csa_t *, uint8_t *pkt, int i_pkt_size ); -void csa_Encrypt( csa_t *, uint8_t *pkt, int i_pkt_size, int b_odd ); +void csa_Encrypt( csa_t *, uint8_t *pkt, int i_pkt_size ); #endif /* _CSA_H */ diff --git a/modules/mux/mpeg/ts.c b/modules/mux/mpeg/ts.c index c2007cf260..7061ddf55b 100644 --- a/modules/mux/mpeg/ts.c +++ b/modules/mux/mpeg/ts.c @@ -79,6 +79,13 @@ * if they arrive a bit late * (We cannot rely on the fact that the fifo should be full) */ + +/***************************************************************************** + * Callback prototypes + *****************************************************************************/ +static int ChangeKeyCallback ( vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void * ); +static int ActiveKeyCallback ( vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void * ); + /***************************************************************************** * Module descriptor *****************************************************************************/ @@ -160,6 +167,14 @@ static void Close ( vlc_object_t * ); #define CK_LONGTEXT N_("CSA encryption key. This must be a " \ "16 char string (8 hexadecimal bytes).") +#define CK2_TEXT N_("Second CSA Key") +#define CK2_LONGTEXT N_("The even CSA encryption key. This must be a " \ + "16 char string (8 hexadecimal bytes).") + +#define CU_TEXT N_("CSA Key in use") +#define CU_LONGTEXT N_("CSA encryption key used. It can be the odd/first/1 " \ + "(default) or the even/second/2 one.") + #define CPKT_TEXT N_("Packet size in bytes to encrypt") #define CPKT_LONGTEXT N_("Size of the TS packet to encrypt. " \ "The encryption routines subtract the TS-header from the value before " \ @@ -227,6 +242,10 @@ vlc_module_begin(); add_string( SOUT_CFG_PREFIX "csa-ck", NULL, NULL, CK_TEXT, CK_LONGTEXT, true ); + add_string( SOUT_CFG_PREFIX "csa2-ck", NULL, NULL, CK2_TEXT, CK2_LONGTEXT, + true ); + add_string( SOUT_CFG_PREFIX "csa-use", "1", NULL, CU_TEXT, CU_LONGTEXT, + true ); add_integer( SOUT_CFG_PREFIX "csa-pkt", 188, NULL, CPKT_TEXT, CPKT_LONGTEXT, true ); set_callbacks( Open, Close ); @@ -238,7 +257,7 @@ vlc_module_end(); static const char *const ppsz_sout_options[] = { "pid-video", "pid-audio", "pid-spu", "pid-pmt", "tsid", "netid", "es-id-pid", "shaping", "pcr", "bmin", "bmax", "use-key-frames", - "dts-delay", "csa-ck", "csa-pkt", "crypt-audio", "crypt-video", + "dts-delay", "csa-ck", "csa2-ck", "csa-use", "csa-pkt", "crypt-audio", "crypt-video", "muxpmt", "sdtdesc", "program-pmt", "alignment", NULL }; @@ -356,6 +375,8 @@ struct sout_mux_sys_t int i_pcr_pid; sout_input_t *p_pcr_input; + vlc_mutex_t csa_lock; + int i_audio_bound; int i_video_bound; @@ -493,6 +514,8 @@ static int Open( vlc_object_t *p_this ) p_sys->dvbpmt = NULL; memset( &p_sys->pmtmap, 0, sizeof(p_sys->pmtmap) ); + vlc_mutex_init( &p_sys->csa_lock ); + p_mux->pf_control = Control; p_mux->pf_addstream = AddStream; p_mux->pf_delstream = DelStream; @@ -768,22 +791,49 @@ static int Open( vlc_object_t *p_this ) p_sys->i_pcr = 0; p_sys->csa = NULL; + var_Create( p_mux, SOUT_CFG_PREFIX "csa-ck", VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND ); var_Get( p_mux, SOUT_CFG_PREFIX "csa-ck", &val ); if( val.psz_string && *val.psz_string ) { int i_res; + vlc_value_t csa2; p_sys->csa = csa_New(); - i_res = csa_SetCW( (vlc_object_t*)p_mux, p_sys->csa, val.psz_string, 1 ); - if( i_res != VLC_SUCCESS || csa_SetCW( (vlc_object_t*)p_mux, p_sys->csa, val.psz_string, 0 ) != VLC_SUCCESS ) + var_Create( p_mux, SOUT_CFG_PREFIX "csa2-ck", VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND ); + var_Get( p_mux, SOUT_CFG_PREFIX "csa2-ck", &csa2 ); + i_res = csa_SetCW( (vlc_object_t*)p_mux, p_sys->csa, val.psz_string, true ); + if( i_res == VLC_SUCCESS && csa2.psz_string && *csa2.psz_string ) + { + if( csa_SetCW( (vlc_object_t*)p_mux, p_sys->csa, csa2.psz_string, false ) != VLC_SUCCESS ) + { + csa_SetCW( (vlc_object_t*)p_mux, p_sys->csa, val.psz_string, false ); + } + } + else if( i_res == VLC_SUCCESS ) + { + csa_SetCW( (vlc_object_t*)p_mux, p_sys->csa, val.psz_string, false ); + } + else { csa_Delete( p_sys->csa ); } if( p_sys->csa ) { - vlc_value_t pkt_val; + vlc_value_t use_val, pkt_val; + + var_Create( p_mux, SOUT_CFG_PREFIX "csa-use", VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_ISCOMMAND ); + var_Get( p_mux, SOUT_CFG_PREFIX "csa-use", &use_val ); + var_AddCallback( p_mux, SOUT_CFG_PREFIX "csa-use", ActiveKeyCallback, NULL ); + var_AddCallback( p_mux, SOUT_CFG_PREFIX "csa-ck", ChangeKeyCallback, (void *)1 ); + var_AddCallback( p_mux, SOUT_CFG_PREFIX "csa2-ck", ChangeKeyCallback, NULL ); + + if ( var_Set( p_mux, SOUT_CFG_PREFIX "csa-use", use_val ) != VLC_SUCCESS ) + { + var_SetString( p_mux, SOUT_CFG_PREFIX "csa-use", "odd" ); + } + free( use_val.psz_string ); var_Get( p_mux, SOUT_CFG_PREFIX "csa-pkt", &pkt_val ); if( pkt_val.i_int < 12 || pkt_val.i_int > 188 ) @@ -795,6 +845,7 @@ static int Open( vlc_object_t *p_this ) else p_sys->i_csa_pkt_size = pkt_val.i_int; msg_Dbg( p_mux, "encrypting %d bytes of packet", p_sys->i_csa_pkt_size ); } + free( csa2.psz_string ); } free( val.psz_string ); @@ -816,10 +867,17 @@ static void Close( vlc_object_t * p_this ) sout_mux_sys_t *p_sys = p_mux->p_sys; int i; + vlc_mutex_lock( &p_sys->csa_lock ); if( p_sys->csa ) { + var_DelCallback( p_mux, SOUT_CFG_PREFIX "csa-ck", ChangeKeyCallback, NULL ); + var_DelCallback( p_mux, SOUT_CFG_PREFIX "csa2-ck", ChangeKeyCallback, NULL ); + var_DelCallback( p_mux, SOUT_CFG_PREFIX "csa-use", ActiveKeyCallback, NULL ); csa_Delete( p_sys->csa ); } + vlc_mutex_unlock( &p_sys->csa_lock ); + vlc_mutex_destroy( &p_sys->csa_lock ); + for( i = 0; i < MAX_PMT; i++ ) { free( p_sys->sdt_descriptors[i].psz_service_name ); @@ -830,6 +888,58 @@ static void Close( vlc_object_t * p_this ) free( p_sys ); } +/***************************************************************************** + * ChangeKeyCallback: called when changing the odd encryption key on the fly. + *****************************************************************************/ +static int ChangeKeyCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void *p_data ) +{ + VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); + sout_mux_t *p_mux = (sout_mux_t*)p_this; + sout_mux_sys_t *p_sys = p_mux->p_sys; + int i_tmp = (int)p_data; + + vlc_mutex_lock( &p_sys->csa_lock ); + if ( i_tmp ) + { + i_tmp = csa_SetCW( p_this, p_sys->csa, newval.psz_string, true ); + } + else + { + i_tmp = csa_SetCW( p_this, p_sys->csa, newval.psz_string, false ); + } + vlc_mutex_unlock( &p_sys->csa_lock ); + + return i_tmp; +} + +/***************************************************************************** + * ActiveKeyCallback: called when changing the active (in use) encryption key on the fly. + *****************************************************************************/ +static int ActiveKeyCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void *p_data ) +{ + VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data); + sout_mux_t *p_mux = (sout_mux_t*)p_this; + sout_mux_sys_t *p_sys = p_mux->p_sys; + int i_res = VLC_EBADVAR; + + vlc_mutex_lock( &p_sys->csa_lock ); + if( !strcmp(newval.psz_string, "odd" ) || !strcmp(newval.psz_string, "first" ) || !strcmp(newval.psz_string, "1" ) ) + { + i_res = csa_UseKey( (vlc_object_t*)p_mux, p_sys->csa, 1 ); + } + else if( !strcmp(newval.psz_string, "even" ) || !strcmp(newval.psz_string, "second" ) || !strcmp(newval.psz_string, "2" ) ) + { + i_res = csa_UseKey( (vlc_object_t*)p_mux, p_sys->csa, 0 ); + } + vlc_mutex_unlock( &p_sys->csa_lock ); + + return i_res; +} + /***************************************************************************** * Control: *****************************************************************************/ @@ -1869,7 +1979,9 @@ static void TSDate( sout_mux_t *p_mux, sout_buffer_chain_t *p_chain_ts, } if( p_ts->i_flags & BLOCK_FLAG_SCRAMBLED ) { - csa_Encrypt( p_sys->csa, p_ts->p_buffer, p_sys->i_csa_pkt_size, 0 ); + vlc_mutex_lock( &p_sys->csa_lock ); + csa_Encrypt( p_sys->csa, p_ts->p_buffer, p_sys->i_csa_pkt_size ); + vlc_mutex_unlock( &p_sys->csa_lock ); } /* latency */