Commit a41a6d3d authored by Simona-Marinela Prodea's avatar Simona-Marinela Prodea Committed by Jean-Baptiste Kempf

DCP: read encrypted DCP with KDM files

Uses libgcrypt
Signed-off-by: default avatarJean-Baptiste Kempf <jb@videolan.org>
parent c7e0dfcc
...@@ -9,12 +9,16 @@ AM_CPPFLAGS += -I$(srcdir)/access ...@@ -9,12 +9,16 @@ AM_CPPFLAGS += -I$(srcdir)/access
libattachment_plugin_la_SOURCES = access/attachment.c libattachment_plugin_la_SOURCES = access/attachment.c
access_LTLIBRARIES += libattachment_plugin.la access_LTLIBRARIES += libattachment_plugin.la
libdcp_plugin_la_SOURCES = access/dcp/dcpparser.h access/dcp/dcp.cpp access/dcp/dcpparser.cpp libdcp_plugin_la_SOURCES = access/dcp/dcpparser.h access/dcp/dcp.cpp access/dcp/dcpparser.cpp access/dcp/dcpdecrypt.cpp
if HAVE_ASDCP if HAVE_ASDCP
libdcp_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) $(ASDCP_CFLAGS) libdcp_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) $(ASDCP_CFLAGS)
libdcp_plugin_la_LIBADD = $(AM_LIBADD) $(ASDCP_LIBS) libdcp_plugin_la_LIBADD = $(AM_LIBADD) $(ASDCP_LIBS)
if HAVE_GCRYPT
libdcp_plugin_la_CPPFLAGS += $(GCRYPT_CFLAGS)
libdcp_plugin_la_LIBADD += $(GCRYPT_LIBS)
access_LTLIBRARIES += libdcp_plugin.la access_LTLIBRARIES += libdcp_plugin.la
endif endif
endif
libfilesystem_plugin_la_SOURCES = access/fs.h access/file.c access/directory.c access/fs.c libfilesystem_plugin_la_SOURCES = access/fs.h access/file.c access/directory.c access/fs.c
libfilesystem_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) libfilesystem_plugin_la_CPPFLAGS = $(AM_CPPFLAGS)
......
...@@ -41,6 +41,8 @@ ...@@ -41,6 +41,8 @@
#endif #endif
#define __STDC_CONSTANT_MACROS 1 #define __STDC_CONSTANT_MACROS 1
#define KDM_HELP_TEXT "KDM file"
#define KDM_HELP_LONG_TEXT "Path to Key Delivery Message XML file"
/* VLC core API headers */ /* VLC core API headers */
#include <vlc_common.h> #include <vlc_common.h>
...@@ -72,6 +74,7 @@ static void Close( vlc_object_t * ); ...@@ -72,6 +74,7 @@ static void Close( vlc_object_t * );
vlc_module_begin() vlc_module_begin()
set_shortname( N_( "DCP" ) ) set_shortname( N_( "DCP" ) )
add_shortcut( "dcp" ) add_shortcut( "dcp" )
add_loadfile( "kdm", "", KDM_HELP_TEXT, KDM_HELP_LONG_TEXT, false )
set_description( N_( "Digital Cinema Package module" ) ) set_description( N_( "Digital Cinema Package module" ) )
set_capability( "access_demux", 0 ) set_capability( "access_demux", 0 )
set_category( CAT_INPUT ) set_category( CAT_INPUT )
...@@ -604,6 +607,7 @@ static int Demux( demux_t *p_demux ) ...@@ -604,6 +607,7 @@ static int Demux( demux_t *p_demux )
block_t *p_video_frame = NULL, *p_audio_frame = NULL; block_t *p_video_frame = NULL, *p_audio_frame = NULL;
PCM::FrameBuffer AudioFrameBuff( p_sys->i_audio_buffer); PCM::FrameBuffer AudioFrameBuff( p_sys->i_audio_buffer);
AESDecContext video_aes_ctx, audio_aes_ctx;
/* swaping video reels */ /* swaping video reels */
if ( p_sys->frame_no == p_sys->p_dcp->video_reels[p_sys->i_video_reel].i_absolute_end ) if ( p_sys->frame_no == p_sys->p_dcp->video_reels[p_sys->i_video_reel].i_absolute_end )
...@@ -632,6 +636,20 @@ static int Demux( demux_t *p_demux ) ...@@ -632,6 +636,20 @@ static int Demux( demux_t *p_demux )
} }
/* video frame */ /* video frame */
/* initialize AES context, if reel is encrypted */
if( p_sys &&
p_sys->p_dcp &&
p_sys->p_dcp->video_reels.size() > p_sys->i_video_reel &&
p_sys->p_dcp->video_reels[p_sys->i_video_reel].p_key )
{
if( ! ASDCP_SUCCESS( video_aes_ctx.InitKey( p_sys->p_dcp->video_reels[p_sys->i_video_reel].p_key->getKey() ) ) )
{
msg_Err( p_demux, "ASDCP failed to initialize AES key" );
goto error;
}
}
switch( p_sys->PictureEssType ) switch( p_sys->PictureEssType )
{ {
case ESS_JPEG_2000: case ESS_JPEG_2000:
...@@ -646,13 +664,13 @@ static int Demux( demux_t *p_demux ) ...@@ -646,13 +664,13 @@ static int Demux( demux_t *p_demux )
goto error_asdcp; goto error_asdcp;
if ( p_sys->PictureEssType == ESS_JPEG_2000_S ) { if ( p_sys->PictureEssType == ESS_JPEG_2000_S ) {
if ( ! ASDCP_SUCCESS( if ( ! ASDCP_SUCCESS(
p_sys->v_videoReader[p_sys->i_video_reel].p_PicMXFSReader->ReadFrame(nextFrame, JP2K::SP_LEFT, PicFrameBuff, 0, 0)) ) { p_sys->v_videoReader[p_sys->i_video_reel].p_PicMXFSReader->ReadFrame(nextFrame, JP2K::SP_LEFT, PicFrameBuff, &video_aes_ctx, 0)) ) {
PicFrameBuff.SetData(0,0); PicFrameBuff.SetData(0,0);
goto error_asdcp; goto error_asdcp;
} }
} else { } else {
if ( ! ASDCP_SUCCESS( if ( ! ASDCP_SUCCESS(
p_sys->v_videoReader[p_sys->i_video_reel].p_PicMXFReader->ReadFrame(nextFrame, PicFrameBuff, 0, 0)) ) { p_sys->v_videoReader[p_sys->i_video_reel].p_PicMXFReader->ReadFrame(nextFrame, PicFrameBuff, &video_aes_ctx, 0)) ) {
PicFrameBuff.SetData(0,0); PicFrameBuff.SetData(0,0);
goto error_asdcp; goto error_asdcp;
} }
...@@ -670,7 +688,7 @@ static int Demux( demux_t *p_demux ) ...@@ -670,7 +688,7 @@ static int Demux( demux_t *p_demux )
goto error_asdcp; goto error_asdcp;
if ( ! ASDCP_SUCCESS( if ( ! ASDCP_SUCCESS(
p_sys->v_videoReader[p_sys->i_video_reel].p_VideoMXFReader->ReadFrame(p_sys->frame_no + p_sys->p_dcp->video_reels[p_sys->i_video_reel].i_correction, VideoFrameBuff, 0, 0)) ) { p_sys->v_videoReader[p_sys->i_video_reel].p_VideoMXFReader->ReadFrame(p_sys->frame_no + p_sys->p_dcp->video_reels[p_sys->i_video_reel].i_correction, VideoFrameBuff, &video_aes_ctx, 0)) ) {
VideoFrameBuff.SetData(0,0); VideoFrameBuff.SetData(0,0);
goto error_asdcp; goto error_asdcp;
} }
...@@ -690,13 +708,27 @@ static int Demux( demux_t *p_demux ) ...@@ -690,13 +708,27 @@ static int Demux( demux_t *p_demux )
if ( ( p_audio_frame = block_Alloc( p_sys->i_audio_buffer )) == NULL ) { if ( ( p_audio_frame = block_Alloc( p_sys->i_audio_buffer )) == NULL ) {
goto error; goto error;
} }
/* initialize AES context, if reel is encrypted */
if( p_sys &&
p_sys->p_dcp &&
p_sys->p_dcp->audio_reels.size() > p_sys->i_audio_reel &&
p_sys->p_dcp->audio_reels[p_sys->i_audio_reel].p_key )
{
if( ! ASDCP_SUCCESS( audio_aes_ctx.InitKey( p_sys->p_dcp->audio_reels[p_sys->i_audio_reel].p_key->getKey() ) ) )
{
msg_Err( p_demux, "ASDCP failed to initialize AES key" );
goto error;
}
}
if ( ! ASDCP_SUCCESS( if ( ! ASDCP_SUCCESS(
AudioFrameBuff.SetData(p_audio_frame->p_buffer, p_sys->i_audio_buffer)) ) { AudioFrameBuff.SetData(p_audio_frame->p_buffer, p_sys->i_audio_buffer)) ) {
goto error_asdcp; goto error_asdcp;
} }
if ( ! ASDCP_SUCCESS( if ( ! ASDCP_SUCCESS(
p_sys->v_audioReader[p_sys->i_audio_reel].p_AudioMXFReader->ReadFrame(p_sys->frame_no + p_sys->p_dcp->audio_reels[p_sys->i_audio_reel].i_correction, AudioFrameBuff, 0, 0)) ) { p_sys->v_audioReader[p_sys->i_audio_reel].p_AudioMXFReader->ReadFrame(p_sys->frame_no + p_sys->p_dcp->audio_reels[p_sys->i_audio_reel].i_correction, AudioFrameBuff, &audio_aes_ctx, 0)) ) {
AudioFrameBuff.SetData(0,0); AudioFrameBuff.SetData(0,0);
goto error_asdcp; goto error_asdcp;
} }
......
/*****************************************************************************
* Copyright (C) 2013 VLC authors and VideoLAN
*
* Author:
* Simona-Marinela Prodea <simona dot marinela dot prodea at gmail dot com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/**
* The code used for reading a DER-encoded private key, that is,
* RSAKey::parseTag function and RSAKey::readDER function,
* is taken almost as is from libgcrypt tests/fipsdrv.c
*/
/**
* @file dcpdecrypt.cpp
* @brief Handle encrypted DCPs
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
/* VLC core API headers */
#include <vlc_common.h>
#include <vlc_xml.h>
#include <vlc_strings.h>
#include <fstream>
#include <algorithm>
#include <cctype>
#include "dcpparser.h"
/* creates a printable, RFC 4122-conform UUID, from a given array of bytes
*/
static string createUUID( unsigned char *ps_string )
{
string s_uuid;
char h[3];
int i, ret;
if( ! ps_string )
return "";
try
{
s_uuid.append( "urn:uuid:" );
for( i = 0; i < 16; i++ )
{
ret = snprintf( h, 3, "%02hhx", ps_string[i] ); /* each byte can be written as 2 hex digits */
if( ret != 2 )
return "";
s_uuid.append( h );
if( i == 3 || i == 5 || i == 7 || i == 9 )
s_uuid.append( "-" );
}
}
catch( ... )
{
return "";
}
return s_uuid;
}
/*
* KDM class
*/
int KDM::Parse()
{
string s_node, s_value;
const string s_root_node = "DCinemaSecurityMessage";
int type;
AESKeyList *_p_key_list = NULL;
/* init XML parser */
if( this->OpenXml() )
{
msg_Err( p_demux, "failed to initialize KDM XML parser" );
return VLC_EGENERIC;
}
msg_Dbg( this->p_demux, "parsing KDM..." );
/* read first node and check if it is a KDM */
if( ! ( ( XML_READER_STARTELEM == XmlFile::ReadNextNode( this->p_xmlReader, s_node ) ) && ( s_node == s_root_node ) ) )
{
msg_Err( this->p_demux, "not a valid XML KDM" );
goto error;
}
while( ( type = XmlFile::ReadNextNode( this->p_xmlReader, s_node ) ) > 0 )
if( type == XML_READER_STARTELEM && s_node == "AuthenticatedPrivate" )
{
_p_key_list = new (nothrow) AESKeyList;
if( unlikely( _p_key_list == NULL ) )
goto error;
p_dcp->p_key_list = _p_key_list;
if( this->ParsePrivate( s_node, type ) )
goto error;
/* keys found, so break */
break;
}
if ( (_p_key_list == NULL) || (_p_key_list->size() == 0) )
{
msg_Err( p_demux, "Key list empty" );
goto error;
}
/* close KDM XML */
this->CloseXml();
return VLC_SUCCESS;
error:
this->CloseXml();
return VLC_EGENERIC;
}
int KDM::ParsePrivate( const string _s_node, int _i_type )
{
string s_node;
int i_type;
AESKey *p_key;
/* check that we are where we're supposed to be */
if( _i_type != XML_READER_STARTELEM )
goto error;
if( _s_node != "AuthenticatedPrivate" )
goto error;
/* loop on EncryptedKey nodes */
while( ( i_type = XmlFile::ReadNextNode( this->p_xmlReader, s_node ) ) > 0 )
{
switch( i_type )
{
case XML_READER_STARTELEM:
if( s_node != "enc:EncryptedKey" )
goto error;
p_key = new (nothrow) AESKey( this->p_demux );
if( unlikely( p_key == NULL ) )
return VLC_EGENERIC;
if( p_key->Parse( p_xmlReader, s_node, i_type ) )
{
delete p_key;
return VLC_EGENERIC;
}
p_dcp->p_key_list->push_back( p_key );
break;
case XML_READER_ENDELEM:
if( s_node == _s_node )
return VLC_SUCCESS;
break;
default:
case XML_READER_TEXT:
goto error;
}
}
/* shouldn't get here */
error:
msg_Err( p_demux, "error while parsing AuthenticatedPrivate portion of KDM" );
return VLC_EGENERIC;
}
/*
* AESKey class
*/
int AESKey::Parse( xml_reader_t *p_xml_reader, string _s_node, int _i_type)
{
string s_node;
string s_value;
int i_type;
if( _i_type != XML_READER_STARTELEM)
goto error;
if( _s_node != "enc:EncryptedKey" )
goto error;
while( ( i_type = XmlFile::ReadNextNode( p_xml_reader, s_node ) ) > 0 )
{
switch( i_type )
{
case XML_READER_STARTELEM:
if( s_node == "enc:CipherValue" )
{
if( XmlFile::ReadEndNode( p_xml_reader, s_node, i_type, s_value ) )
goto error;
if( this->decryptRSA( s_value ) )
return VLC_EGENERIC;
}
break;
case XML_READER_ENDELEM:
if( s_node == _s_node )
return VLC_SUCCESS;
break;
default:
case XML_READER_TEXT:
goto error;
}
}
/* shouldn't get here */
error:
msg_Err( this->p_demux, "error while parsing EncryptedKey" );
return VLC_EGENERIC;
}
/* decrypts the RSA encrypted text read from the XML file,
* and saves the AES key and the other needed info
* uses libgcrypt for decryption
*/
int AESKey::decryptRSA( string s_cipher_text_b64 )
{
RSAKey rsa_key( this->p_demux );
unsigned char *ps_cipher_text = NULL;
unsigned char *ps_plain_text = NULL;
gcry_mpi_t cipher_text_mpi = NULL;
gcry_sexp_t cipher_text_sexp = NULL;
gcry_sexp_t plain_text_sexp = NULL;
gcry_mpi_t plain_text_mpi = NULL;
gcry_sexp_t tmp_sexp = NULL;
gcry_error_t err;
size_t length;
int i_ret = VLC_EGENERIC;
/* get RSA private key file path */
if( rsa_key.setPath() )
goto end;
/* read private key from file */
if( rsa_key.readPEM() )
goto end;
/* remove spaces and newlines from encoded cipher text
* (usually added for indentation in XML files)
* */
try
{
s_cipher_text_b64.erase( remove_if( s_cipher_text_b64.begin(), s_cipher_text_b64.end(), static_cast<int(*)(int)>(isspace) ),
s_cipher_text_b64.end() );
}
catch( ... )
{
msg_Err( this->p_demux, "error while handling string" );
goto end;
}
/* decode cipher from BASE64 to binary */
if( ! ( length = vlc_b64_decode_binary( &ps_cipher_text, s_cipher_text_b64.c_str() ) ) )
{
msg_Err( this->p_demux, "could not decode cipher from Base64" );
goto end;
}
/* initialize libgcrypt */
vlc_gcrypt_init ();
/* create S-expression for ciphertext */
if( ( err = gcry_mpi_scan( &cipher_text_mpi, GCRYMPI_FMT_USG, ps_cipher_text, 256, NULL ) ) )
{
msg_Err( this->p_demux, "could not scan MPI from cipher text: %s", gcry_strerror( err ) );
goto end;
}
if( ( err = gcry_sexp_build( &cipher_text_sexp, NULL, "(enc-val(flags oaep)(rsa(a %m)))", cipher_text_mpi ) ) )
{
msg_Err( this->p_demux, "could not build S-expression for cipher text: %s", gcry_strerror( err ) );
goto end;
}
/* decrypt */
if( ( err = gcry_pk_decrypt( &plain_text_sexp, cipher_text_sexp, rsa_key.priv_key ) ) )
{
msg_Err( this->p_demux, "error while decrypting RSA encrypted info: %s", gcry_strerror( err ) );
goto end;
}
/* extract plain-text from S-expression */
if( ! ( tmp_sexp = gcry_sexp_find_token( plain_text_sexp, "value", 0 ) ) )
/* when using padding flags, the decrypted S-expression is of the form
* "(value <plaintext>)", where <plaintext> is an MPI */
{
msg_Err( this->p_demux, "decrypted text is in an unexpected form; decryption may have failed" );
goto end;
}
/* we could have used the gcry_sexp_nth_data to get the data directly,
* but as that function is newly introduced (libgcrypt v1.6),
* we prefer compatibility, even though that means passing the data through an MPI first */
if( ! ( plain_text_mpi = gcry_sexp_nth_mpi( tmp_sexp, 1, GCRYMPI_FMT_USG ) ) )
{
msg_Err( this->p_demux, "could not extract MPI from decrypted S-expression" );
goto end;
}
if( ( err = gcry_mpi_aprint( GCRYMPI_FMT_USG, &ps_plain_text, &length, plain_text_mpi ) ) )
{
msg_Err( this->p_demux, "error while extracting plain text from MPI: %s", gcry_strerror( err ) );
goto end;
}
/* interpret the plaintext data */
switch( length )
{
case 138: /* SMPTE DCP */
if( this->extractInfo( ps_plain_text, true ) )
goto end;
break;
case 134: /* Interop DCP */
if( this->extractInfo( ps_plain_text, false ) )
goto end;
break;
case -1:
msg_Err( this->p_demux, "could not decrypt" );
goto end;
default:
msg_Err( this->p_demux, "CipherValue field length does not match SMPTE nor Interop standards" );
goto end;
}
i_ret = VLC_SUCCESS;
end:
free( ps_cipher_text );
gcry_mpi_release( cipher_text_mpi );
gcry_sexp_release( cipher_text_sexp );
gcry_sexp_release( plain_text_sexp );
gcry_mpi_release( plain_text_mpi );
gcry_sexp_release( tmp_sexp );
gcry_free( ps_plain_text );
return i_ret;
}
/* extracts and saves the AES key info from the plaintext;
* parameter smpte is true for SMPTE DCP, false for Interop;
* see SMPTE 430-1-2006, section 6.1.2 for the exact structure of the plaintext
*/
int AESKey::extractInfo( unsigned char * ps_plain_text, bool smpte )
{
string s_rsa_structID( "f1dc124460169a0e85bc300642f866ab" ); /* unique Structure ID for all RSA-encrypted AES keys in a KDM */
string s_carrier;
char psz_hex[3];
int i_ret, i_pos = 0;
/* check for the structure ID */
while( i_pos < 16 )
{
i_ret = snprintf( psz_hex, 3, "%02hhx", ps_plain_text[i_pos] );
if( i_ret != 2 )
{
msg_Err( this->p_demux, "error while extracting structure ID from decrypted cipher" );
return VLC_EGENERIC;
}
try
{
s_carrier.append( psz_hex );
}
catch( ... )
{
msg_Err( this->p_demux, "error while handling string" );
return VLC_EGENERIC;
}
i_pos++;
}
if( s_carrier.compare( s_rsa_structID ) )
{
msg_Err( this->p_demux, "incorrect RSA structure ID: KDM may be broken" );
return VLC_EGENERIC;
}
i_pos += 36; /* TODO thumbprint, CPL ID */
if( smpte ) /* only SMPTE DCPs have the 4-byte "KeyType" field */
i_pos += 4;
/* extract the AES key UUID */
if( ( this->s_key_id = createUUID( ps_plain_text + i_pos ) ).empty() )
{
msg_Err( this->p_demux, "error while extracting AES Key UUID" );
return VLC_EGENERIC;
}
i_pos += 16;
i_pos += 50; /* TODO KeyEpoch */
/* extract the AES key */
memcpy( this->ps_key, ps_plain_text + i_pos, 16 );
return VLC_SUCCESS;
}
/*
* RSAKey class
*/
/*
* gets the private key path (always stored in the VLC config dir and called "priv.key" )
*/
int RSAKey::setPath( )
{
char *psz_config_dir = NULL;
if( ! ( psz_config_dir = config_GetUserDir( VLC_CONFIG_DIR ) ) )
{
msg_Err( this->p_demux, "could not read user config dir" );
goto error;
}
try
{
this->s_path.assign( psz_config_dir );
this->s_path.append( "/priv.key" );
}
catch( ... )
{
msg_Err( this->p_demux, "error while handling string" );
goto error;
}
free( psz_config_dir );
return VLC_SUCCESS;
error:
free( psz_config_dir );
return VLC_EGENERIC;
}
/*
* reads the RSA private key from file
* the file must be conform to PCKS#1, PEM-encoded, unencrypted
*/
int RSAKey::readPEM( )
{
string s_header_tag( "-----BEGIN RSA PRIVATE KEY-----" );
string s_footer_tag( "-----END RSA PRIVATE KEY-----" );
string s_line;
string s_data_b64;
unsigned char *ps_data_der = NULL;
size_t length;
/* open key file */
ifstream file( this->s_path.c_str(), ios::in );
if( ! file.is_open() )
{
msg_Err( this->p_demux, "could not open private key file" );
goto error;
}
/* check for header tag */
if( ! getline( file, s_line ) )
{
msg_Err( this->p_demux, "could not read private key file" );
goto error;
}
if( s_line.compare( s_header_tag ) )
{
msg_Err( this->p_demux, "unexpected header tag found in private key file" );
goto error;
}
/* read file until footer tag is found */
while( getline( file, s_line ) )
{
if( ! s_line.compare( s_footer_tag ) )
break;
try
{
s_data_b64.append( s_line );
}
catch( ... )
{
msg_Err( this->p_demux, "error while handling string" );
goto error;
}
}
if( ! file )
{
msg_Err( this->p_demux, "error while reading private key file; footer tag may be missing" );
goto error;
}
/* decode data from Base64 */
if( ! ( length = vlc_b64_decode_binary( &ps_data_der, s_data_b64.c_str() ) ) )
{
msg_Err( this->p_demux, "could not decode from Base64" );
goto error;
}
/* extract key S-expression from DER-encoded data */
if( this->readDER( ps_data_der, length ) )
goto error;
/* clear data */
free( ps_data_der );
return VLC_SUCCESS;
error:
free( ps_data_der );
return VLC_EGENERIC;
}
/*
* Parse the DER-encoded data at ps_data_der
* saving the key in an S-expression
*/
int RSAKey::readDER( unsigned char const* ps_data_der, size_t length )
{
struct tag_info tag_inf;
gcry_mpi_t key_params[8];
gcry_error_t err;
int i;
/* parse the ASN1 structure */
if( parseTag( &ps_data_der, &length, &tag_inf )
|| tag_inf.tag != TAG_SEQUENCE || tag_inf.class_ || !tag_inf.cons || tag_inf.ndef )
goto bad_asn1;
if( parseTag( &ps_data_der, &length, &tag_inf )
|| tag_inf.tag != TAG_INTEGER || tag_inf.class_ || tag_inf.cons || tag_inf.ndef )
goto bad_asn1;
if( tag_inf.length != 1 || *ps_data_der )
goto bad_asn1; /* The value of the first integer is no 0. */
ps_data_der += tag_inf.length;
length -= tag_inf.length;
for( i = 0; i < 8; i++ )
{
if( parseTag( &ps_data_der, &length, &tag_inf )
|| tag_inf.tag != TAG_INTEGER || tag_inf.class_ || tag_inf.cons || tag_inf.ndef )
goto bad_asn1;
err = gcry_mpi_scan( key_params + i, GCRYMPI_FMT_USG, ps_data_der, tag_inf.length, NULL );
if( err )
{
msg_Err( this->p_demux, "error scanning RSA parameter %d: %s", i, gpg_strerror( err ) );
goto error;
}
ps_data_der += tag_inf.length;
length -= tag_inf.length;
}
/* Convert from OpenSSL parameter ordering to the OpenPGP order.
* First check that p < q; if not swap p and q and recompute u.
*/
if( gcry_mpi_cmp( key_params[3], key_params[4] ) > 0 )
{
gcry_mpi_swap( key_params[3], key_params[4] );
gcry_mpi_invm( key_params[7], key_params[3], key_params[4] );
}
/* Build the S-expression. */
err = gcry_sexp_build( & this->priv_key, NULL,
"(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
key_params[0], key_params[1], key_params[2],
key_params[3], key_params[4], key_params[7] );
if( err )
{
msg_Err( this->p_demux, "error building S-expression: %s", gpg_strerror( err ) );
goto error;
}
/* clear data */
for( i = 0; i < 8; i++ )
gcry_mpi_release( key_params[i] );
return VLC_SUCCESS;
bad_asn1:
msg_Err( this->p_demux, "could not parse ASN1 structure; key might be corrupted" );
error:
for( i = 0; i < 8; i++ )
gcry_mpi_release( key_params[i] );
return VLC_EGENERIC;
}
/*
* Parse the buffer at the address BUFFER which consists of the number
* of octets as stored at BUFLEN. Return the tag and the length part
* from the TLV triplet. Update BUFFER and BUFLEN on success. Checks
* that the encoded length does not exhaust the length of the provided
* buffer.
*/
int RSAKey::parseTag( unsigned char const **buffer, size_t *buflen, struct tag_info *ti)
{
int c;
unsigned long tag;
const unsigned char *buf = *buffer;
size_t length = *buflen;
ti->length = 0;
ti->ndef = 0;
ti->nhdr = 0;
/* Get the tag */
if (!length)
return -1; /* Premature EOF. */
c = *buf++; length--;
ti->nhdr++;
ti->class_ = (c & 0xc0) >> 6;
ti->cons = !!(c & 0x20);
tag = (c & 0x1f);
if (tag == 0x1f)
{
tag = 0;
do
{
tag <<= 7;
if (!length)
return -1; /* Premature EOF. */
c = *buf++; length--;
ti->nhdr++;
tag |= (c & 0x7f);
}
while ( (c & 0x80) );
}
ti->tag = tag;
/* Get the length */
if (!length)
return -1; /* Premature EOF. */
c = *buf++; length--;
ti->nhdr++;
if ( !(c & 0x80) )
ti->length = c;
else if (c == 0x80)
ti->ndef = 1;
else if (c == 0xff)
return -1; /* Forbidden length value. */
else
{
unsigned long len = 0;
int count = c & 0x7f;
for (; count; count--)
{
len <<= 8;
if (!length)
return -1; /* Premature EOF. */
c = *buf++; length--;
ti->nhdr++;
len |= (c & 0xff);
}
ti->length = len;
}
if (ti->class_ == 0 && !ti->tag)
ti->length = 0;
if (ti->length > length)
return -1; /* Data larger than buffer. */
*buffer = buf;
*buflen = length;
return 0;
}
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
* Anthony Giniers * Anthony Giniers
* Ludovic Hoareau * Ludovic Hoareau
* Loukmane Dessai * Loukmane Dessai
* Simona-Marinela Prodea <simona dot marinela dot prodea at gmail dot com>
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by * under the terms of the GNU Lesser General Public License as published by
...@@ -48,34 +49,6 @@ ...@@ -48,34 +49,6 @@
using namespace std; using namespace std;
static int ReadNextNode(xml_reader_t *p_xmlReader, string& p_node) {
const char * c_node;
int i;
i = xml_ReaderNextNode( p_xmlReader, &c_node );
p_node = c_node;
return i;
}
static int ReadEndNode( xml_reader_t *p_xmlReader, string p_node,
int p_type, string &s_value) {
string node;
if ( xml_ReaderIsEmptyElement( p_xmlReader) )
return 0;
if (p_type != XML_READER_STARTELEM)
return -1;
if (ReadNextNode(p_xmlReader, node) == XML_READER_TEXT)
{
s_value = node;
if((ReadNextNode(p_xmlReader, node) == XML_READER_ENDELEM) &&
node == p_node)
return 0;
}
return -1;
}
typedef enum { typedef enum {
CHUNK_UNKNOWN = 0, CHUNK_UNKNOWN = 0,
CHUNK_PATH, CHUNK_PATH,
...@@ -161,7 +134,7 @@ int Chunk::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type){ ...@@ -161,7 +134,7 @@ int Chunk::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type){
if( p_node != "Chunk") if( p_node != "Chunk")
return -1; return -1;
/* loop on Chunks Node */ /* loop on Chunks Node */
while( ( type = ReadNextNode( p_xmlReader, node ) ) > 0 ) { while( ( type = XmlFile::ReadNextNode( p_xmlReader, node ) ) > 0 ) {
switch (type) { switch (type) {
case XML_READER_STARTELEM: case XML_READER_STARTELEM:
{ {
...@@ -169,7 +142,7 @@ int Chunk::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type){ ...@@ -169,7 +142,7 @@ int Chunk::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type){
for(ChunkTag_t i = CHUNK_PATH; i <= CHUNK_LENGTH; i = ChunkTag_t(i+1)) { for(ChunkTag_t i = CHUNK_PATH; i <= CHUNK_LENGTH; i = ChunkTag_t(i+1)) {
if( node == names[i-1]) { if( node == names[i-1]) {
chunk_tag = i; chunk_tag = i;
if ( ReadEndNode(p_xmlReader, node, type, s_value)) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value))
return -1; return -1;
switch (chunk_tag) { switch (chunk_tag) {
case CHUNK_PATH: case CHUNK_PATH:
...@@ -230,11 +203,13 @@ AssetMap::~AssetMap() { } ...@@ -230,11 +203,13 @@ AssetMap::~AssetMap() { }
int AssetMap::Parse ( ) int AssetMap::Parse ( )
{ {
int type = 0; int type = 0;
int retval;
int reel_nbr = 0; int reel_nbr = 0;
int index = 0; int index = 0;
int sum_duration_vid = 0; int sum_duration_vid = 0;
int sum_duration_aud = 0; int sum_duration_aud = 0;
string node; string node;
char *psz_kdm_path;
CPL *cpl; CPL *cpl;
Reel *reel; Reel *reel;
...@@ -251,7 +226,7 @@ int AssetMap::Parse ( ) ...@@ -251,7 +226,7 @@ int AssetMap::Parse ( )
/* reading ASSETMAP file to get the asset_list */ /* reading ASSETMAP file to get the asset_list */
msg_Dbg( p_demux, "reading ASSETMAP file..." ); msg_Dbg( p_demux, "reading ASSETMAP file..." );
while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) { while( ( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
if ( (type == XML_READER_STARTELEM) && ( node =="AssetList")) { if ( (type == XML_READER_STARTELEM) && ( node =="AssetList")) {
_p_asset_list = new (nothrow) AssetList(); _p_asset_list = new (nothrow) AssetList();
if ( unlikely(_p_asset_list == NULL) ) { if ( unlikely(_p_asset_list == NULL) ) {
...@@ -337,6 +312,29 @@ int AssetMap::Parse ( ) ...@@ -337,6 +312,29 @@ int AssetMap::Parse ( )
return -1; return -1;
} }
/* KDM, if needed */
for( AssetList::iterator iter = _p_asset_list->begin(); iter != _p_asset_list->end(); ++iter )
if( ! (*iter)->getKeyId().empty() )
{
msg_Dbg( p_demux, "DCP is encrypted, searching KDM file...");
psz_kdm_path = var_InheritString( p_demux, "kdm" );
if( !psz_kdm_path || !*psz_kdm_path )
{
msg_Err( p_demux, "cryptographic key IDs found in CPL and no path to KDM given");
free( psz_kdm_path );
this->CloseXml();
return VLC_EGENERIC;
}
KDM p_kdm( p_demux, psz_kdm_path, p_dcp );
free( psz_kdm_path );
if( ( retval = p_kdm.Parse() ) )
{
this->CloseXml();
return retval;
}
break;
}
reel_nbr = cpl->getReelList().size(); reel_nbr = cpl->getReelList().size();
for(index = 0; index != reel_nbr; ++index) for(index = 0; index != reel_nbr; ++index)
{ {
...@@ -356,6 +354,7 @@ int AssetMap::Parse ( ) ...@@ -356,6 +354,7 @@ int AssetMap::Parse ( )
video.i_duration = asset->getDuration(); video.i_duration = asset->getDuration();
video.i_correction = video.i_entrypoint - sum_duration_vid + video.i_duration; video.i_correction = video.i_entrypoint - sum_duration_vid + video.i_duration;
video.i_absolute_end = sum_duration_vid; video.i_absolute_end = sum_duration_vid;
video.p_key = asset->getAESKeyById( p_dcp->p_key_list, asset->getKeyId() );
p_dcp->video_reels.push_back(video); p_dcp->video_reels.push_back(video);
msg_Dbg( this->p_demux, "Video Track: %s",asset->getPath().c_str()); msg_Dbg( this->p_demux, "Video Track: %s",asset->getPath().c_str());
msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint()); msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint());
...@@ -378,6 +377,7 @@ int AssetMap::Parse ( ) ...@@ -378,6 +377,7 @@ int AssetMap::Parse ( )
audio.i_duration = asset->getDuration(); audio.i_duration = asset->getDuration();
audio.i_correction = audio.i_entrypoint - sum_duration_aud + audio.i_duration; audio.i_correction = audio.i_entrypoint - sum_duration_aud + audio.i_duration;
audio.i_absolute_end = sum_duration_aud; audio.i_absolute_end = sum_duration_aud;
audio.p_key = asset->getAESKeyById( p_dcp->p_key_list, asset->getKeyId() );
p_dcp->audio_reels.push_back(audio); p_dcp->audio_reels.push_back(audio);
msg_Dbg( this->p_demux, "Audio Track: %s",asset->getPath().c_str()); msg_Dbg( this->p_demux, "Audio Track: %s",asset->getPath().c_str());
msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint()); msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint());
...@@ -407,7 +407,7 @@ int Asset::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type) ...@@ -407,7 +407,7 @@ int Asset::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type)
if( p_node != s_root_node) if( p_node != s_root_node)
return -1; return -1;
/* loop on Assets Node */ /* loop on Assets Node */
while( ( type = ReadNextNode( p_xmlReader, node ) ) > 0 ) { while( ( type = XmlFile::ReadNextNode( p_xmlReader, node ) ) > 0 ) {
switch (type) { switch (type) {
case XML_READER_STARTELEM: case XML_READER_STARTELEM:
{ {
...@@ -421,7 +421,7 @@ int Asset::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type) ...@@ -421,7 +421,7 @@ int Asset::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type)
/* case of <PackinkList/> tag, bur not compliant with SMPTE-429-9 2007*/ /* case of <PackinkList/> tag, bur not compliant with SMPTE-429-9 2007*/
if (xml_ReaderIsEmptyElement( p_xmlReader)) if (xml_ReaderIsEmptyElement( p_xmlReader))
this->b_is_packing_list = true; this->b_is_packing_list = true;
else if ( ReadEndNode(p_xmlReader, node, type, s_value) ) else if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
return -1; return -1;
if ( s_value == "true" ) if ( s_value == "true" )
this->b_is_packing_list = true; this->b_is_packing_list = true;
...@@ -432,12 +432,12 @@ int Asset::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type) ...@@ -432,12 +432,12 @@ int Asset::Parse( xml_reader_t *p_xmlReader, string p_node, int p_type)
this->s_path = this->chunk_vec[0].getPath(); this->s_path = this->chunk_vec[0].getPath();
break; break;
case ASSET_ID: case ASSET_ID:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
return -1; return -1;
this->s_id = s_value; this->s_id = s_value;
break; break;
case ASSET_ANNOTATION_TEXT: case ASSET_ANNOTATION_TEXT:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
return -1; return -1;
this->s_annotation = s_value; this->s_annotation = s_value;
break; break;
...@@ -493,7 +493,7 @@ int Asset::ParsePKL( xml_reader_t *p_xmlReader) ...@@ -493,7 +493,7 @@ int Asset::ParsePKL( xml_reader_t *p_xmlReader)
string s_value; string s_value;
const string s_root_node = "Asset"; const string s_root_node = "Asset";
while( ( type = ReadNextNode( p_xmlReader, node ) ) > 0 ) { while( ( type = XmlFile::ReadNextNode( p_xmlReader, node ) ) > 0 ) {
switch (type) { switch (type) {
case XML_READER_STARTELEM: case XML_READER_STARTELEM:
{ {
...@@ -503,7 +503,7 @@ int Asset::ParsePKL( xml_reader_t *p_xmlReader) ...@@ -503,7 +503,7 @@ int Asset::ParsePKL( xml_reader_t *p_xmlReader)
_tag = i; _tag = i;
switch(_tag) { switch(_tag) {
case ASSET_ANNOTATION_TEXT: case ASSET_ANNOTATION_TEXT:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
return -1; return -1;
if ( this->s_annotation.empty() ) if ( this->s_annotation.empty() )
this->s_annotation = s_value; this->s_annotation = s_value;
...@@ -511,22 +511,22 @@ int Asset::ParsePKL( xml_reader_t *p_xmlReader) ...@@ -511,22 +511,22 @@ int Asset::ParsePKL( xml_reader_t *p_xmlReader)
this->s_annotation = this->s_annotation + "--" + s_value; this->s_annotation = this->s_annotation + "--" + s_value;
break; break;
case ASSET_HASH: case ASSET_HASH:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
return -1; return -1;
this->s_hash = s_value; this->s_hash = s_value;
break; break;
case ASSET_SIZE: case ASSET_SIZE:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
return -1; return -1;
this->ui_size = atol(s_value.c_str()); this->ui_size = atol(s_value.c_str());
break; break;
case ASSET_TYPE: case ASSET_TYPE:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
return -1; return -1;
this->s_type = s_value; this->s_type = s_value;
break; break;
case ASSET_ORIGINAL_FILENAME: case ASSET_ORIGINAL_FILENAME:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
return -1; return -1;
this->s_original_filename = s_value; this->s_original_filename = s_value;
break; break;
...@@ -602,7 +602,7 @@ int Asset::parseChunkList( xml_reader_t *p_xmlReader, string p_node, int p_type) ...@@ -602,7 +602,7 @@ int Asset::parseChunkList( xml_reader_t *p_xmlReader, string p_node, int p_type)
if( p_node != "ChunkList" ) if( p_node != "ChunkList" )
return -1; return -1;
/* loop on Assets Node */ /* loop on Assets Node */
while( ( type = ReadNextNode( p_xmlReader, node ) ) > 0 ) { while( ( type = XmlFile::ReadNextNode( p_xmlReader, node ) ) > 0 ) {
switch (type) { switch (type) {
case XML_READER_STARTELEM: case XML_READER_STARTELEM:
{ {
...@@ -629,6 +629,20 @@ int Asset::parseChunkList( xml_reader_t *p_xmlReader, string p_node, int p_type) ...@@ -629,6 +629,20 @@ int Asset::parseChunkList( xml_reader_t *p_xmlReader, string p_node, int p_type)
return -1; return -1;
} }
AESKey * Asset::getAESKeyById( AESKeyList* p_key_list, const string s_id )
{
/* return NULL if DCP is not encrypted */
if( !p_key_list || s_id.empty() )
return NULL;
for( AESKeyList::iterator index = p_key_list->begin(); index != p_key_list->end(); ++index )
if( (*index)->getKeyId() == s_id )
return *index;
return NULL;
}
int AssetMap::ParseAssetList (xml_reader_t *p_xmlReader, const string p_node, int p_type) int AssetMap::ParseAssetList (xml_reader_t *p_xmlReader, const string p_node, int p_type)
{ {
string node; string node;
...@@ -640,7 +654,7 @@ int AssetMap::ParseAssetList (xml_reader_t *p_xmlReader, const string p_node, in ...@@ -640,7 +654,7 @@ int AssetMap::ParseAssetList (xml_reader_t *p_xmlReader, const string p_node, in
if( p_node != "AssetList" ) if( p_node != "AssetList" )
return -1; return -1;
/* loop on AssetList nodes */ /* loop on AssetList nodes */
while( ( type = ReadNextNode( p_xmlReader, node ) ) > 0 ) { while( ( type = XmlFile::ReadNextNode( p_xmlReader, node ) ) > 0 ) {
switch (type) { switch (type) {
case XML_READER_STARTELEM: case XML_READER_STARTELEM:
if (node != "Asset" ) if (node != "Asset" )
...@@ -707,6 +721,35 @@ int XmlFile::OpenXml() ...@@ -707,6 +721,35 @@ int XmlFile::OpenXml()
return 0; return 0;
} }
int XmlFile::ReadNextNode( xml_reader_t *p_xmlReader, string& p_node )
{
const char * c_node;
int i;
i = xml_ReaderNextNode( p_xmlReader, &c_node );
p_node = c_node;
return i;
}
int XmlFile::ReadEndNode( xml_reader_t *p_xmlReader, string p_node, int p_type, string &s_value)
{
string node;
if ( xml_ReaderIsEmptyElement( p_xmlReader) )
return 0;
if (p_type != XML_READER_STARTELEM)
return -1;
if ( XmlFile::ReadNextNode( p_xmlReader, node ) == XML_READER_TEXT )
{
s_value = node;
if( ( XmlFile::ReadNextNode( p_xmlReader, node ) == XML_READER_ENDELEM ) &&
node == p_node)
return 0;
}
return -1;
}
void XmlFile::CloseXml() { void XmlFile::CloseXml() {
if( this->p_stream ) if( this->p_stream )
stream_Delete( this->p_stream ); stream_Delete( this->p_stream );
...@@ -756,12 +799,12 @@ int PKL::Parse() ...@@ -756,12 +799,12 @@ int PKL::Parse()
return -1; return -1;
/* read 1st node and verify that is a PKL*/ /* read 1st node and verify that is a PKL*/
if (! ( ( XML_READER_STARTELEM == ReadNextNode(this->p_xmlReader, node) ) && if (! ( ( XML_READER_STARTELEM == XmlFile::ReadNextNode(this->p_xmlReader, node) ) &&
(node == s_root_node) ) ) { (node == s_root_node) ) ) {
msg_Err( this->p_demux, "Not a valid XML Packing List"); msg_Err( this->p_demux, "Not a valid XML Packing List");
goto error; goto error;
} }
while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) { while( ( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
switch (type) { switch (type) {
case XML_READER_STARTELEM: { case XML_READER_STARTELEM: {
PKLTag_t _tag = PKL_UNKNOWN; PKLTag_t _tag = PKL_UNKNOWN;
...@@ -784,37 +827,37 @@ int PKL::Parse() ...@@ -784,37 +827,37 @@ int PKL::Parse()
break; break;
/* Parse simple/end nodes */ /* Parse simple/end nodes */
case PKL_ID: case PKL_ID:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
goto error; goto error;
this->s_id = s_value; this->s_id = s_value;
break; break;
case PKL_ISSUE_DATE: case PKL_ISSUE_DATE:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
goto error; goto error;
this->s_issue_date = s_value; this->s_issue_date = s_value;
break; break;
case PKL_ISSUER: case PKL_ISSUER:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
goto error; goto error;
this->s_issuer = s_value; this->s_issuer = s_value;
break; break;
case PKL_CREATOR: case PKL_CREATOR:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
goto error; goto error;
this->s_creator = s_value; this->s_creator = s_value;
break; break;
case PKL_ANNOTATION_TEXT: case PKL_ANNOTATION_TEXT:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
goto error; goto error;
this->s_annotation = s_value; this->s_annotation = s_value;
break; break;
case PKL_ICON_ID: case PKL_ICON_ID:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
goto error; goto error;
this->s_icon_id = s_value; this->s_icon_id = s_value;
break; break;
case PKL_GROUP_ID: case PKL_GROUP_ID:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
goto error; goto error;
this->s_group_id = s_value; this->s_group_id = s_value;
break; break;
...@@ -890,7 +933,7 @@ int PKL::ParseAssetList(string p_node, int p_type) { ...@@ -890,7 +933,7 @@ int PKL::ParseAssetList(string p_node, int p_type) {
return -1; return -1;
if( p_node != "AssetList") if( p_node != "AssetList")
return -1; return -1;
while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) { while( ( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
switch (type) { switch (type) {
case XML_READER_STARTELEM: case XML_READER_STARTELEM:
if( node =="Asset") { if( node =="Asset") {
...@@ -923,16 +966,16 @@ int PKL::ParseAsset(string p_node, int p_type) { ...@@ -923,16 +966,16 @@ int PKL::ParseAsset(string p_node, int p_type) {
return -1; return -1;
/* 1st node shall be Id" */ /* 1st node shall be Id" */
if (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0) if (( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0)
if ( ! ((type == XML_READER_STARTELEM) && (node == "Id"))) if ( ! ((type == XML_READER_STARTELEM) && (node == "Id")))
return -1; return -1;
if (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0) if (( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0)
if (type == XML_READER_TEXT) { if (type == XML_READER_TEXT) {
s_value = node; s_value = node;
if (unlikely(node.empty())) if (unlikely(node.empty()))
return -1; return -1;
} }
if (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0) if (( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0)
if (type == XML_READER_ENDELEM) { if (type == XML_READER_ENDELEM) {
asset = AssetMap::getAssetById(this->asset_list, s_value); asset = AssetMap::getAssetById(this->asset_list, s_value);
if (asset == NULL) if (asset == NULL)
...@@ -955,7 +998,7 @@ int PKL::ParseSigner(string p_node, int p_type) ...@@ -955,7 +998,7 @@ int PKL::ParseSigner(string p_node, int p_type)
if( p_node != "Signer") if( p_node != "Signer")
return -1; return -1;
while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) { while( ( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
/* TODO not implemented. Just parse until end of Signer node */ /* TODO not implemented. Just parse until end of Signer node */
if ((node == p_node) && (type = XML_READER_ENDELEM)) if ((node == p_node) && (type = XML_READER_ENDELEM))
return 0; return 0;
...@@ -975,7 +1018,7 @@ int PKL::ParseSignature(string p_node, int p_type) ...@@ -975,7 +1018,7 @@ int PKL::ParseSignature(string p_node, int p_type)
if( p_node != "ds:Signature") if( p_node != "ds:Signature")
return -1; return -1;
while (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) { while (( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
/* TODO not implemented. Just parse until end of Signature node */ /* TODO not implemented. Just parse until end of Signature node */
if ((node == p_node) && (type = XML_READER_ENDELEM)) if ((node == p_node) && (type = XML_READER_ENDELEM))
return 0; return 0;
...@@ -997,15 +1040,15 @@ int Reel::Parse(string p_node, int p_type) { ...@@ -997,15 +1040,15 @@ int Reel::Parse(string p_node, int p_type) {
if( p_node != "Reel") if( p_node != "Reel")
return -1; return -1;
while (( type = ReadNextNode(this->p_xmlReader, node ) ) > 0 ) { while (( type = XmlFile::ReadNextNode(this->p_xmlReader, node ) ) > 0 ) {
switch (type) { switch (type) {
case XML_READER_STARTELEM: case XML_READER_STARTELEM:
if (node =="Id") { if (node =="Id") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value)) if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1; return -1;
this->s_id = s_value; this->s_id = s_value;
} else if (node == "AnnotationText") { } else if (node == "AnnotationText") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value)) if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1; return -1;
this->s_annotation = s_value; this->s_annotation = s_value;
} else if ( node =="AssetList" ) { } else if ( node =="AssetList" ) {
...@@ -1060,7 +1103,7 @@ int Reel::ParseAssetList(string p_node, int p_type) { ...@@ -1060,7 +1103,7 @@ int Reel::ParseAssetList(string p_node, int p_type) {
if( p_node != "AssetList") if( p_node != "AssetList")
return -1; return -1;
while (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) { while (( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
switch (type) { switch (type) {
case XML_READER_STARTELEM: case XML_READER_STARTELEM:
if (node =="MainPicture") { if (node =="MainPicture") {
...@@ -1105,11 +1148,11 @@ int Reel::ParseAsset(string p_node, int p_type, TrackType_t e_track) { ...@@ -1105,11 +1148,11 @@ int Reel::ParseAsset(string p_node, int p_type, TrackType_t e_track) {
return -1; return -1;
/* 1st node shall be Id */ /* 1st node shall be Id */
if (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0) if (( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0)
if ( ! ((type == XML_READER_STARTELEM) && (node == "Id"))) if ( ! ((type == XML_READER_STARTELEM) && (node == "Id")))
return -1; return -1;
if ( ReadEndNode(this->p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value) )
return -1; return -1;
asset = AssetMap::getAssetById(this->p_asset_list, s_value); asset = AssetMap::getAssetById(this->p_asset_list, s_value);
...@@ -1117,42 +1160,43 @@ int Reel::ParseAsset(string p_node, int p_type, TrackType_t e_track) { ...@@ -1117,42 +1160,43 @@ int Reel::ParseAsset(string p_node, int p_type, TrackType_t e_track) {
return -1; return -1;
while( (! b_stop_parse) && while( (! b_stop_parse) &&
(( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) ) { (( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) ) {
switch (type) { switch (type) {
case XML_READER_STARTELEM: case XML_READER_STARTELEM:
if (node =="EditRate") { if (node =="EditRate") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value)) if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1; return -1;
} else if (node == "AnnotationText") { } else if (node == "AnnotationText") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value)) if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1; return -1;
asset->setAnnotation(s_value); asset->setAnnotation(s_value);
} else if (node == "IntrinsicDuration") { } else if (node == "IntrinsicDuration") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value)) if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1; return -1;
asset->setIntrinsicDuration(atoi(s_value.c_str())); asset->setIntrinsicDuration(atoi(s_value.c_str()));
} else if (node == "EntryPoint") { } else if (node == "EntryPoint") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value)) if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1; return -1;
asset->setEntryPoint(atoi(s_value.c_str())); asset->setEntryPoint(atoi(s_value.c_str()));
} else if (node == "Duration") { } else if (node == "Duration") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value)) if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1; return -1;
asset->setDuration(atoi(s_value.c_str())); asset->setDuration(atoi(s_value.c_str()));
} else if (node == "KeyId") { } else if (node == "KeyId") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value)) if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1; return -1;
asset->setKeyId( s_value );
} else if (node == "Hash") { } else if (node == "Hash") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value)) if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1; return -1;
} else if (node == "FrameRate") { } else if (node == "FrameRate") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value)) if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1; return -1;
} else if (node == "ScreenAspectRatio") { } else if (node == "ScreenAspectRatio") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value)) if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1; return -1;
} else if (node == "Language") { } else if (node == "Language") {
if ( ReadEndNode(this->p_xmlReader, node, type, s_value)) if ( XmlFile::ReadEndNode(this->p_xmlReader, node, type, s_value))
return -1; return -1;
} else { } else {
/* unknown tag */ /* unknown tag */
...@@ -1208,7 +1252,7 @@ CPL::CPL(demux_t * p_demux, string s_path, AssetList *_asset_list) ...@@ -1208,7 +1252,7 @@ CPL::CPL(demux_t * p_demux, string s_path, AssetList *_asset_list)
} }
/* read 1st node and verify that is a CPL */ /* read 1st node and verify that is a CPL */
if ( (type = ReadNextNode(p_xmlReader, node)) > 0) { if ( (type = XmlFile::ReadNextNode(p_xmlReader, node)) > 0) {
if ( (type == XML_READER_STARTELEM) && (node == "CompositionPlaylist") ) { if ( (type == XML_READER_STARTELEM) && (node == "CompositionPlaylist") ) {
this->type = XML_CPL; this->type = XML_CPL;
} }
...@@ -1248,13 +1292,13 @@ int CPL::Parse() ...@@ -1248,13 +1292,13 @@ int CPL::Parse()
return -1; return -1;
/* read 1st node and verify that is a CPL*/ /* read 1st node and verify that is a CPL*/
if (! ( ( XML_READER_STARTELEM == ReadNextNode(this->p_xmlReader, node) ) && if (! ( ( XML_READER_STARTELEM == XmlFile::ReadNextNode(this->p_xmlReader, node) ) &&
(node == s_root_node) ) ) { (node == s_root_node) ) ) {
msg_Err( this->p_demux, "Not a valid XML Packing List"); msg_Err( this->p_demux, "Not a valid XML Packing List");
goto error; goto error;
} }
while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) { while( ( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
switch (type) { switch (type) {
case XML_READER_STARTELEM: { case XML_READER_STARTELEM: {
CPLTag_t _tag = CPL_UNKNOWN; CPLTag_t _tag = CPL_UNKNOWN;
...@@ -1276,42 +1320,42 @@ int CPL::Parse() ...@@ -1276,42 +1320,42 @@ int CPL::Parse()
break; break;
/* Parse simple/end nodes */ /* Parse simple/end nodes */
case CPL_ID: case CPL_ID:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
goto error; goto error;
this->s_id = s_value; this->s_id = s_value;
break; break;
case CPL_ANNOTATION_TEXT: case CPL_ANNOTATION_TEXT:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
goto error; goto error;
this->s_annotation = s_value; this->s_annotation = s_value;
break; break;
case CPL_ICON_ID: case CPL_ICON_ID:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
goto error; goto error;
this->s_icon_id = s_value; this->s_icon_id = s_value;
break; break;
case CPL_ISSUE_DATE: case CPL_ISSUE_DATE:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
goto error; goto error;
this->s_issue_date= s_value; this->s_issue_date= s_value;
break; break;
case CPL_ISSUER: case CPL_ISSUER:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
goto error; goto error;
this->s_issuer = s_value; this->s_issuer = s_value;
break; break;
case CPL_CREATOR: case CPL_CREATOR:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
goto error; goto error;
this->s_creator = s_value; this->s_creator = s_value;
break; break;
case CPL_CONTENT_TITLE: case CPL_CONTENT_TITLE:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
goto error; goto error;
this->s_content_title = s_value; this->s_content_title = s_value;
break; break;
case CPL_CONTENT_KIND: case CPL_CONTENT_KIND:
if ( ReadEndNode(p_xmlReader, node, type, s_value) ) if ( XmlFile::ReadEndNode(p_xmlReader, node, type, s_value) )
goto error; goto error;
this->s_content_kind = s_value; this->s_content_kind = s_value;
break; break;
...@@ -1358,7 +1402,7 @@ int CPL::ParseReelList(string p_node, int p_type) { ...@@ -1358,7 +1402,7 @@ int CPL::ParseReelList(string p_node, int p_type) {
return -1; return -1;
if( p_node != "ReelList") if( p_node != "ReelList")
return -1; return -1;
while( ( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) { while( ( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
switch (type) { switch (type) {
case XML_READER_STARTELEM: { case XML_READER_STARTELEM: {
Reel *p_reel = new (nothrow) Reel( this->p_demux, this->asset_list, this->p_xmlReader); Reel *p_reel = new (nothrow) Reel( this->p_demux, this->asset_list, this->p_xmlReader);
...@@ -1401,7 +1445,7 @@ int CPL::DummyParse(string p_node, int p_type) ...@@ -1401,7 +1445,7 @@ int CPL::DummyParse(string p_node, int p_type)
if (xml_ReaderIsEmptyElement( this->p_xmlReader)) if (xml_ReaderIsEmptyElement( this->p_xmlReader))
return 0; return 0;
while (( type = ReadNextNode( this->p_xmlReader, node ) ) > 0 ) { while (( type = XmlFile::ReadNextNode( this->p_xmlReader, node ) ) > 0 ) {
/* TODO not implemented. Just pase until end of input node */ /* TODO not implemented. Just pase until end of input node */
if ((node == p_node) && (type = XML_READER_ENDELEM)) if ((node == p_node) && (type = XML_READER_ENDELEM))
return 0; return 0;
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
* Anthony Giniers * Anthony Giniers
* Ludovic Hoareau * Ludovic Hoareau
* Loukmane Dessai * Loukmane Dessai
* Simona-Marinela Prodea <simona dot marinela dot prodea at gmail dot com>
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by * under the terms of the GNU Lesser General Public License as published by
...@@ -42,6 +43,10 @@ ...@@ -42,6 +43,10 @@
#include <vlc_demux.h> #include <vlc_demux.h>
#include <vlc_plugin.h> #include <vlc_plugin.h>
/* gcrypt headers */
#include <gcrypt.h>
#include <vlc_gcrypt.h>
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <list> #include <list>
...@@ -67,7 +72,8 @@ typedef enum { ...@@ -67,7 +72,8 @@ typedef enum {
class Asset; class Asset;
class AssetList: public std::list<Asset *> {}; class AssetList: public std::list<Asset *> {};
class PKL; class PKL;
class AESKey;
class AESKeyList: public std::list<AESKey *> {};
/* This struct stores useful information about an MXF for demux() */ /* This struct stores useful information about an MXF for demux() */
struct info_reel struct info_reel
...@@ -77,6 +83,7 @@ struct info_reel ...@@ -77,6 +83,7 @@ struct info_reel
int i_duration; int i_duration;
int i_correction; /* entrypoint - sum of previous durations */ int i_correction; /* entrypoint - sum of previous durations */
uint32_t i_absolute_end; /* correction + duration */ uint32_t i_absolute_end; /* correction + duration */
AESKey * p_key;
}; };
/* This struct stores the most important information about the DCP */ /* This struct stores the most important information about the DCP */
...@@ -86,12 +93,13 @@ struct dcp_t ...@@ -86,12 +93,13 @@ struct dcp_t
vector<PKL *> pkls; vector<PKL *> pkls;
AssetList *p_asset_list; AssetList *p_asset_list;
AESKeyList *p_key_list;
vector<info_reel> audio_reels; vector<info_reel> audio_reels;
vector<info_reel> video_reels; vector<info_reel> video_reels;
dcp_t(): dcp_t():
p_asset_list(NULL) {}; p_asset_list(NULL), p_key_list(NULL) {};
~dcp_t( ) { ~dcp_t( ) {
vlc_delete_all(pkls); vlc_delete_all(pkls);
...@@ -100,6 +108,10 @@ struct dcp_t ...@@ -100,6 +108,10 @@ struct dcp_t
delete(p_asset_list); delete(p_asset_list);
} }
if ( p_key_list != NULL ) {
vlc_delete_all(*p_key_list);
delete(p_key_list);
}
} }
}; };
...@@ -117,6 +129,9 @@ public: ...@@ -117,6 +129,9 @@ public:
virtual int Parse() = 0; virtual int Parse() = 0;
static int ReadNextNode( xml_reader_t *p_xmlReader, string& s_node );
static int ReadEndNode( xml_reader_t *p_xmlReader, string s_node, int i_type, string &s_value );
bool IsCPL() { return type == XML_CPL; } bool IsCPL() { return type == XML_CPL; }
protected: protected:
demux_t *p_demux; demux_t *p_demux;
...@@ -164,6 +179,7 @@ public: ...@@ -164,6 +179,7 @@ public:
else else
this->s_annotation = this->s_annotation + "--" + p_string; this->s_annotation = this->s_annotation + "--" + p_string;
}; };
void setKeyId(string p_string) { this->s_key_id = p_string; };
void setPackingList(bool p_bool) { this->s_path = p_bool; }; void setPackingList(bool p_bool) { this->s_path = p_bool; };
void setEntryPoint(int i_val) { this->i_entry_point = i_val; }; void setEntryPoint(int i_val) { this->i_entry_point = i_val; };
void setDuration (int i_val) { this->i_duration = i_val; }; void setDuration (int i_val) { this->i_duration = i_val; };
...@@ -172,6 +188,7 @@ public: ...@@ -172,6 +188,7 @@ public:
string getPath() const { return this->s_path; }; string getPath() const { return this->s_path; };
string getType() const { return this->s_type; }; string getType() const { return this->s_type; };
string getOriginalFilename() const { return this->s_original_filename; }; string getOriginalFilename() const { return this->s_original_filename; };
string getKeyId() const { return this->s_key_id; }
int getEntryPoint() const { return this->i_entry_point; }; int getEntryPoint() const { return this->i_entry_point; };
int getDuration() const { return this->i_duration; }; int getDuration() const { return this->i_duration; };
int getIntrinsicDuration() const { return this->i_intrisic_duration; }; int getIntrinsicDuration() const { return this->i_intrisic_duration; };
...@@ -181,6 +198,8 @@ public: ...@@ -181,6 +198,8 @@ public:
int Parse( xml_reader_t *p_xmlReader, string node, int type); int Parse( xml_reader_t *p_xmlReader, string node, int type);
int ParsePKL( xml_reader_t *p_xmlReader); int ParsePKL( xml_reader_t *p_xmlReader);
static AESKey * getAESKeyById( AESKeyList* , const string s_id );
// TODO: remove // TODO: remove
void Dump(); void Dump();
...@@ -315,4 +334,76 @@ private: ...@@ -315,4 +334,76 @@ private:
int ParseAssetList (xml_reader_t *p_xmlReader, const string p_node, int p_type); int ParseAssetList (xml_reader_t *p_xmlReader, const string p_node, int p_type);
}; };
class KDM : public XmlFile {
public:
KDM( demux_t * p_demux, string s_path, dcp_t *_p_dcp )
: XmlFile( p_demux, s_path ), p_dcp(_p_dcp) {}
virtual int Parse();
private:
dcp_t *p_dcp;
int ParsePrivate( const string s_node, int i_type );
};
class AESKey
{
public:
AESKey( demux_t *demux ): p_demux( demux ) { }
virtual ~AESKey() {};
const string getKeyId() { return this->s_key_id; };
const unsigned char * getKey() { return this->ps_key; };
int Parse( xml_reader_t *p_xml_reader, string s_node, int i_type );
private:
demux_t *p_demux;
string s_key_id;
unsigned char ps_key[16];
int decryptRSA( string s_cipher_text_b64 );
int extractInfo( unsigned char * ps_plain_text, bool smpte );
};
class RSAKey
{
public:
RSAKey( demux_t *demux ):
priv_key( NULL ), p_demux( demux ) { }
virtual ~RSAKey() { gcry_sexp_release( priv_key ); }
/* some ASN.1 tags. */
enum
{
TAG_INTEGER = 2,
TAG_SEQUENCE = 16,
};
/* ASN.1 Parser object. */
struct tag_info
{
int class_; /* Object class. */
unsigned long tag; /* The tag of the object. */
unsigned long length; /* Length of the values. */
int nhdr; /* Length of the header (TL). */
unsigned int ndef:1; /* The object has an indefinite length. */
unsigned int cons:1; /* This is a constructed object. */
};
int setPath();
int readPEM();
int readDER( unsigned char const *ps_data_der, size_t length );
int parseTag( unsigned char const **buffer, size_t *buflen, struct tag_info *ti);
gcry_sexp_t priv_key;
private:
demux_t *p_demux;
string s_path;
};
#endif /* _DCPPARSER_H */ #endif /* _DCPPARSER_H */
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