[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