You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
673 lines
19 KiB
673 lines
19 KiB
/*****************************************************************************
|
|
* 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_configuration.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_demux, 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_demux, 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_demux, this->p_xmlReader, s_node ) ) > 0 )
|
|
{
|
|
switch( i_type )
|
|
{
|
|
case XML_READER_STARTELEM:
|
|
if( s_node != "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 != "EncryptedKey" )
|
|
goto error;
|
|
|
|
while( ( i_type = XmlFile::ReadNextNode( this->p_demux, p_xml_reader, s_node ) ) > 0 )
|
|
{
|
|
switch( i_type )
|
|
{
|
|
case XML_READER_STARTELEM:
|
|
if( s_node == "CipherValue" )
|
|
{
|
|
if( XmlFile::ReadEndNode( this->p_demux, 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 static_cast<size_t>( -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] = { NULL };
|
|
gcry_error_t err;
|
|
|
|
/* 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( int 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( int 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( int 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;
|
|
}
|
|
|