[vlc-devel] [PATCHv2] dcp: read encrypted DCPs
Jean-Baptiste Kempf
jb at videolan.org
Mon Apr 21 19:23:57 CEST 2014
On 18 Apr, Simona-Marinela Prodea wrote :
> Allow read of encrypted DCP with KDM, using libgcrypt.
> /* VLC core API headers */
> #include <vlc_common.h>
> @@ -50,9 +52,6 @@
> #include <vlc_url.h>
> #include <vlc_aout.h>
>
> -/* ASDCP headers */
> -#include <AS_DCP.h>
> -
Why?
> + /* initialize AES context, if reel is encrypted */
> + if( 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;
> + }
missing {} is confusing.
Are you sure p_sys->p_dcp->video_reels[p_sys->i_video_reel].p_key is not
NULL-deref?
> + /* initialize AES context, if reel is encrypted */
> + if( 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;
> + }
Idem.
> diff --git a/modules/access/dcp/dcpparser.cpp b/modules/access/dcp/dcpparser.cpp
> index cd42922..371bc63 100644
> --- a/modules/access/dcp/dcpparser.cpp
> +++ b/modules/access/dcp/dcpparser.cpp
Most of the changes here should be in a new file.
> @@ -9,6 +9,7 @@
> * Anthony Giniers
> * Ludovic Hoareau
> * 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
> * under the terms of the GNU Lesser General Public License as published by
> @@ -28,6 +29,10 @@
> /**
> * @file dcpparser.cpp
> * @brief Parsing of DCP XML files
> + *
> + * 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
> */
> #ifdef HAVE_CONFIG_H
> # include "config.h"
> @@ -38,11 +43,15 @@
> #include <vlc_plugin.h>
> #include <vlc_xml.h>
> #include <vlc_url.h>
> +#include <vlc_fs.h>
> +#include <vlc_strings.h>
>
> #include <iostream>
> #include <string>
> #include <list>
> #include <vector>
> +#include <fstream>
> +#include <algorithm>
>
> #include "dcpparser.h"
>
> @@ -76,6 +85,43 @@ static int ReadEndNode( xml_reader_t *p_xmlReader, string p_node,
> return -1;
> }
>
> +/* 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;
> +}
> +
> +static bool isBlank( char c )
> +{
> + return ( c == '\n' || c == '\r' || c == '\t' || c == ' ' );
> +};
> +
> typedef enum {
> CHUNK_UNKNOWN = 0,
> CHUNK_PATH,
> @@ -230,11 +276,13 @@ AssetMap::~AssetMap() { }
> int AssetMap::Parse ( )
> {
> int type = 0;
> + int retval;
> int reel_nbr = 0;
> int index = 0;
> int sum_duration_vid = 0;
> int sum_duration_aud = 0;
> string node;
> + char *psz_kdm_path;
>
> CPL *cpl;
> Reel *reel;
> @@ -337,6 +385,29 @@ int AssetMap::Parse ( )
> 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();
> for(index = 0; index != reel_nbr; ++index)
> {
> @@ -356,6 +427,7 @@ int AssetMap::Parse ( )
> video.i_duration = asset->getDuration();
> video.i_correction = video.i_entrypoint - sum_duration_vid + video.i_duration;
> 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);
> msg_Dbg( this->p_demux, "Video Track: %s",asset->getPath().c_str());
> msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint());
> @@ -378,6 +450,7 @@ int AssetMap::Parse ( )
> audio.i_duration = asset->getDuration();
> audio.i_correction = audio.i_entrypoint - sum_duration_aud + audio.i_duration;
> 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);
> msg_Dbg( this->p_demux, "Audio Track: %s",asset->getPath().c_str());
> msg_Dbg( this->p_demux, "Entry point: %i",asset->getEntryPoint());
> @@ -629,6 +702,19 @@ int Asset::parseChunkList( xml_reader_t *p_xmlReader, string p_node, int p_type)
> return -1;
> }
>
> +AESKey * Asset::getAESKeyById( AESKeyList* p_key_list, const string s_id )
> +{
> + 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)
> {
> string node;
> @@ -1142,6 +1228,7 @@ int Reel::ParseAsset(string p_node, int p_type, TrackType_t e_track) {
> } else if (node == "KeyId") {
> if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
> return -1;
> + asset->setKeyId( s_value );
> } else if (node == "Hash") {
> if ( ReadEndNode(this->p_xmlReader, node, type, s_value))
> return -1;
> @@ -1409,3 +1496,604 @@ int CPL::DummyParse(string p_node, int p_type)
>
> return -1;
> }
> +
> +/*
> + * 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 == 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 = 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 = 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 = ReadNextNode( p_xml_reader, s_node ) ) > 0 )
> + {
> + switch( i_type )
> + {
> + case XML_READER_STARTELEM:
> + if( s_node == "enc:CipherValue" )
> + {
> + if( 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;
> +
> + /* get RSA private key file path */
> + if( rsa_key.setPath() )
> + goto error;
> +
> + /* read private key from file */
> + if( rsa_key.readPEM() )
> + goto error;
> +
> + /* 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(), isBlank ),
> + s_cipher_text_b64.end() );
> + }
> + catch( ... )
> + {
> + msg_Err( this->p_demux, "error while handling string" );
> + goto error;
> + }
> +
> + /* 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 error;
> + }
> +
> + /* 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 error;
> + }
> + 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 error;
> + }
> +
> + /* 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 error;
> + }
> +
> + /* 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 error;
> + }
> + /* 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 error;
> + }
> +
> + 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 error;
> + }
> +
> + /* interpret the plaintext data */
> + switch( length )
> + {
> + case 138: /* SMPTE DCP */
> + if( this->extractInfo( ps_plain_text, true ) )
> + goto error;
> + break;
> + case 136: /* Interop DCP */
> + if( this->extractInfo( ps_plain_text, false ) )
> + goto error;
> + break;
> + case -1:
> + msg_Err( this->p_demux, "could not decrypt" );
> + goto error;
> + default:
> + msg_Err( this->p_demux, "CipherValue field length does not match SMPTE nor Interop standards" );
> + goto error;
> + }
> +
> + 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 VLC_SUCCESS;
> +
> +error:
> + 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 VLC_EGENERIC;
> +}
> +
> +/* 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;
> +}
> diff --git a/modules/access/dcp/dcpparser.h b/modules/access/dcp/dcpparser.h
> index 32059f5..6d1f944 100644
> --- a/modules/access/dcp/dcpparser.h
> +++ b/modules/access/dcp/dcpparser.h
> @@ -8,6 +8,7 @@
> * Anthony Giniers
> * Ludovic Hoareau
> * 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
> * under the terms of the GNU Lesser General Public License as published by
> @@ -27,6 +28,10 @@
> /**
> * @file dcpparser.h
> * @brief Parse DCP XML files
> + *
> + * 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
> */
>
>
> @@ -42,6 +47,13 @@
> #include <vlc_demux.h>
> #include <vlc_plugin.h>
>
> +/* ASDCP header */
> +#include <AS_DCP.h>
> +
> +/* gcrypt headers */
> +#include <gcrypt.h>
> +#include <vlc_gcrypt.h>
> +
> #include <iostream>
> #include <string>
> #include <list>
> @@ -67,6 +79,8 @@ typedef enum {
> class Asset;
> class AssetList: public std::list<Asset *> {};
> class PKL;
> +class AESKey;
> +class AESKeyList: public std::list<AESKey *> {};
>
>
> /* This struct stores useful information about an MXF for demux() */
> @@ -77,6 +91,7 @@ struct info_reel
> int i_duration;
> int i_correction; /* entrypoint - sum of previous durations */
> uint32_t i_absolute_end; /* correction + duration */
> + AESKey * p_key;
> };
>
> /* This struct stores the most important information about the DCP */
> @@ -86,12 +101,13 @@ struct dcp_t
>
> vector<PKL *> pkls;
> AssetList *p_asset_list;
> + AESKeyList *p_key_list;
>
> vector<info_reel> audio_reels;
> vector<info_reel> video_reels;
>
> dcp_t():
> - p_asset_list(NULL) {};
> + p_asset_list(NULL), p_key_list(NULL) {};
>
> ~dcp_t( ) {
> vlc_delete_all(pkls);
> @@ -100,6 +116,10 @@ struct dcp_t
> delete(p_asset_list);
>
> }
> + if ( p_key_list != NULL ) {
> + vlc_delete_all(*p_key_list);
> + delete(p_key_list);
> + }
> }
> };
>
> @@ -164,6 +184,7 @@ public:
> else
> 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 setEntryPoint(int i_val) { this->i_entry_point = i_val; };
> void setDuration (int i_val) { this->i_duration = i_val; };
> @@ -172,6 +193,7 @@ public:
> string getPath() const { return this->s_path; };
> string getType() const { return this->s_type; };
> 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 getDuration() const { return this->i_duration; };
> int getIntrinsicDuration() const { return this->i_intrisic_duration; };
> @@ -181,6 +203,8 @@ public:
> int Parse( xml_reader_t *p_xmlReader, string node, int type);
> int ParsePKL( xml_reader_t *p_xmlReader);
>
> + static AESKey * getAESKeyById( AESKeyList* , const string s_id );
> +
> // TODO: remove
> void Dump();
>
> @@ -315,4 +339,76 @@ private:
>
> 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) {}
> + ~KDM() {};
uh, oh. No. you're missing a virtual here.
> + 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 ) { }
> +
> + 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 ) { }
> + ~RSAKey() { gcry_sexp_release( priv_key ); }
idem.
> +
> + /* 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 */
> --
> 1.7.9.5
>
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
--
With my kindest regards,
--
Jean-Baptiste Kempf
http://www.jbkempf.com/ - +33 672 704 734
Sent from my Electronic Device
More information about the vlc-devel
mailing list