1 /*****************************************************************************
2 * Copyright (C) 2013 VLC authors and VideoLAN
5 * Simona-Marinela Prodea <simona dot marinela dot prodea at gmail dot com>
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
20 *****************************************************************************/
23 * The code used for reading a DER-encoded private key, that is,
24 * RSAKey::parseTag function and RSAKey::readDER function,
25 * is taken almost as is from libgcrypt tests/fipsdrv.c
29 * @file dcpdecrypt.cpp
30 * @brief Handle encrypted DCPs
37 /* VLC core API headers */
38 #include <vlc_common.h>
40 #include <vlc_strings.h>
46 #include "dcpparser.h"
48 /* creates a printable, RFC 4122-conform UUID, from a given array of bytes
50 static string createUUID( unsigned char *ps_string )
61 s_uuid.append( "urn:uuid:" );
62 for( i = 0; i < 16; i++ )
64 ret = snprintf( h, 3, "%02hhx", ps_string[i] ); /* each byte can be written as 2 hex digits */
68 if( i == 3 || i == 5 || i == 7 || i == 9 )
86 string s_node, s_value;
87 const string s_root_node = "DCinemaSecurityMessage";
90 AESKeyList *_p_key_list = NULL;
95 msg_Err( p_demux, "failed to initialize KDM XML parser" );
99 msg_Dbg( this->p_demux, "parsing KDM..." );
101 /* read first node and check if it is a KDM */
102 if( ! ( ( XML_READER_STARTELEM == XmlFile::ReadNextNode( this->p_xmlReader, s_node ) ) && ( s_node == s_root_node ) ) )
104 msg_Err( this->p_demux, "not a valid XML KDM" );
108 while( ( type = XmlFile::ReadNextNode( this->p_xmlReader, s_node ) ) > 0 )
109 if( type == XML_READER_STARTELEM && s_node == "AuthenticatedPrivate" )
111 _p_key_list = new (nothrow) AESKeyList;
112 if( unlikely( _p_key_list == NULL ) )
114 p_dcp->p_key_list = _p_key_list;
115 if( this->ParsePrivate( s_node, type ) )
118 /* keys found, so break */
122 if ( (_p_key_list == NULL) || (_p_key_list->size() == 0) )
124 msg_Err( p_demux, "Key list empty" );
136 int KDM::ParsePrivate( const string _s_node, int _i_type )
142 /* check that we are where we're supposed to be */
143 if( _i_type != XML_READER_STARTELEM )
145 if( _s_node != "AuthenticatedPrivate" )
148 /* loop on EncryptedKey nodes */
149 while( ( i_type = XmlFile::ReadNextNode( this->p_xmlReader, s_node ) ) > 0 )
153 case XML_READER_STARTELEM:
154 if( s_node != "enc:EncryptedKey" )
156 p_key = new (nothrow) AESKey( this->p_demux );
157 if( unlikely( p_key == NULL ) )
159 if( p_key->Parse( p_xmlReader, s_node, i_type ) )
164 p_dcp->p_key_list->push_back( p_key );
167 case XML_READER_ENDELEM:
168 if( s_node == _s_node )
172 case XML_READER_TEXT:
177 /* shouldn't get here */
179 msg_Err( p_demux, "error while parsing AuthenticatedPrivate portion of KDM" );
187 int AESKey::Parse( xml_reader_t *p_xml_reader, string _s_node, int _i_type)
193 if( _i_type != XML_READER_STARTELEM)
195 if( _s_node != "enc:EncryptedKey" )
198 while( ( i_type = XmlFile::ReadNextNode( p_xml_reader, s_node ) ) > 0 )
202 case XML_READER_STARTELEM:
203 if( s_node == "enc:CipherValue" )
205 if( XmlFile::ReadEndNode( p_xml_reader, s_node, i_type, s_value ) )
207 if( this->decryptRSA( s_value ) )
211 case XML_READER_ENDELEM:
212 if( s_node == _s_node )
216 case XML_READER_TEXT:
221 /* shouldn't get here */
223 msg_Err( this->p_demux, "error while parsing EncryptedKey" );
227 /* decrypts the RSA encrypted text read from the XML file,
228 * and saves the AES key and the other needed info
229 * uses libgcrypt for decryption
231 int AESKey::decryptRSA( string s_cipher_text_b64 )
233 RSAKey rsa_key( this->p_demux );
234 unsigned char *ps_cipher_text = NULL;
235 unsigned char *ps_plain_text = NULL;
236 gcry_mpi_t cipher_text_mpi = NULL;
237 gcry_sexp_t cipher_text_sexp = NULL;
238 gcry_sexp_t plain_text_sexp = NULL;
239 gcry_mpi_t plain_text_mpi = NULL;
240 gcry_sexp_t tmp_sexp = NULL;
243 int i_ret = VLC_EGENERIC;
245 /* get RSA private key file path */
246 if( rsa_key.setPath() )
249 /* read private key from file */
250 if( rsa_key.readPEM() )
253 /* remove spaces and newlines from encoded cipher text
254 * (usually added for indentation in XML files)
258 s_cipher_text_b64.erase( remove_if( s_cipher_text_b64.begin(), s_cipher_text_b64.end(), static_cast<int(*)(int)>(isspace) ),
259 s_cipher_text_b64.end() );
263 msg_Err( this->p_demux, "error while handling string" );
267 /* decode cipher from BASE64 to binary */
268 if( ! ( length = vlc_b64_decode_binary( &ps_cipher_text, s_cipher_text_b64.c_str() ) ) )
270 msg_Err( this->p_demux, "could not decode cipher from Base64" );
274 /* initialize libgcrypt */
277 /* create S-expression for ciphertext */
278 if( ( err = gcry_mpi_scan( &cipher_text_mpi, GCRYMPI_FMT_USG, ps_cipher_text, 256, NULL ) ) )
280 msg_Err( this->p_demux, "could not scan MPI from cipher text: %s", gcry_strerror( err ) );
283 if( ( err = gcry_sexp_build( &cipher_text_sexp, NULL, "(enc-val(flags oaep)(rsa(a %m)))", cipher_text_mpi ) ) )
285 msg_Err( this->p_demux, "could not build S-expression for cipher text: %s", gcry_strerror( err ) );
290 if( ( err = gcry_pk_decrypt( &plain_text_sexp, cipher_text_sexp, rsa_key.priv_key ) ) )
292 msg_Err( this->p_demux, "error while decrypting RSA encrypted info: %s", gcry_strerror( err ) );
296 /* extract plain-text from S-expression */
297 if( ! ( tmp_sexp = gcry_sexp_find_token( plain_text_sexp, "value", 0 ) ) )
298 /* when using padding flags, the decrypted S-expression is of the form
299 * "(value <plaintext>)", where <plaintext> is an MPI */
301 msg_Err( this->p_demux, "decrypted text is in an unexpected form; decryption may have failed" );
304 /* we could have used the gcry_sexp_nth_data to get the data directly,
305 * but as that function is newly introduced (libgcrypt v1.6),
306 * we prefer compatibility, even though that means passing the data through an MPI first */
307 if( ! ( plain_text_mpi = gcry_sexp_nth_mpi( tmp_sexp, 1, GCRYMPI_FMT_USG ) ) )
309 msg_Err( this->p_demux, "could not extract MPI from decrypted S-expression" );
313 if( ( err = gcry_mpi_aprint( GCRYMPI_FMT_USG, &ps_plain_text, &length, plain_text_mpi ) ) )
315 msg_Err( this->p_demux, "error while extracting plain text from MPI: %s", gcry_strerror( err ) );
319 /* interpret the plaintext data */
322 case 138: /* SMPTE DCP */
323 if( this->extractInfo( ps_plain_text, true ) )
326 case 134: /* Interop DCP */
327 if( this->extractInfo( ps_plain_text, false ) )
331 msg_Err( this->p_demux, "could not decrypt" );
334 msg_Err( this->p_demux, "CipherValue field length does not match SMPTE nor Interop standards" );
341 free( ps_cipher_text );
342 gcry_mpi_release( cipher_text_mpi );
343 gcry_sexp_release( cipher_text_sexp );
344 gcry_sexp_release( plain_text_sexp );
345 gcry_mpi_release( plain_text_mpi );
346 gcry_sexp_release( tmp_sexp );
347 gcry_free( ps_plain_text );
351 /* extracts and saves the AES key info from the plaintext;
352 * parameter smpte is true for SMPTE DCP, false for Interop;
353 * see SMPTE 430-1-2006, section 6.1.2 for the exact structure of the plaintext
355 int AESKey::extractInfo( unsigned char * ps_plain_text, bool smpte )
358 string s_rsa_structID( "f1dc124460169a0e85bc300642f866ab" ); /* unique Structure ID for all RSA-encrypted AES keys in a KDM */
361 int i_ret, i_pos = 0;
363 /* check for the structure ID */
366 i_ret = snprintf( psz_hex, 3, "%02hhx", ps_plain_text[i_pos] );
369 msg_Err( this->p_demux, "error while extracting structure ID from decrypted cipher" );
374 s_carrier.append( psz_hex );
378 msg_Err( this->p_demux, "error while handling string" );
383 if( s_carrier.compare( s_rsa_structID ) )
385 msg_Err( this->p_demux, "incorrect RSA structure ID: KDM may be broken" );
389 i_pos += 36; /* TODO thumbprint, CPL ID */
390 if( smpte ) /* only SMPTE DCPs have the 4-byte "KeyType" field */
393 /* extract the AES key UUID */
394 if( ( this->s_key_id = createUUID( ps_plain_text + i_pos ) ).empty() )
396 msg_Err( this->p_demux, "error while extracting AES Key UUID" );
401 i_pos += 50; /* TODO KeyEpoch */
403 /* extract the AES key */
404 memcpy( this->ps_key, ps_plain_text + i_pos, 16 );
415 * gets the private key path (always stored in the VLC config dir and called "priv.key" )
417 int RSAKey::setPath( )
419 char *psz_config_dir = NULL;
421 if( ! ( psz_config_dir = config_GetUserDir( VLC_CONFIG_DIR ) ) )
423 msg_Err( this->p_demux, "could not read user config dir" );
428 this->s_path.assign( psz_config_dir );
429 this->s_path.append( "/priv.key" );
433 msg_Err( this->p_demux, "error while handling string" );
437 free( psz_config_dir );
441 free( psz_config_dir );
446 * reads the RSA private key from file
447 * the file must be conform to PCKS#1, PEM-encoded, unencrypted
449 int RSAKey::readPEM( )
451 string s_header_tag( "-----BEGIN RSA PRIVATE KEY-----" );
452 string s_footer_tag( "-----END RSA PRIVATE KEY-----" );
455 unsigned char *ps_data_der = NULL;
459 ifstream file( this->s_path.c_str(), ios::in );
460 if( ! file.is_open() )
462 msg_Err( this->p_demux, "could not open private key file" );
466 /* check for header tag */
467 if( ! getline( file, s_line ) )
469 msg_Err( this->p_demux, "could not read private key file" );
472 if( s_line.compare( s_header_tag ) )
474 msg_Err( this->p_demux, "unexpected header tag found in private key file" );
478 /* read file until footer tag is found */
479 while( getline( file, s_line ) )
481 if( ! s_line.compare( s_footer_tag ) )
485 s_data_b64.append( s_line );
489 msg_Err( this->p_demux, "error while handling string" );
495 msg_Err( this->p_demux, "error while reading private key file; footer tag may be missing" );
499 /* decode data from Base64 */
500 if( ! ( length = vlc_b64_decode_binary( &ps_data_der, s_data_b64.c_str() ) ) )
502 msg_Err( this->p_demux, "could not decode from Base64" );
506 /* extract key S-expression from DER-encoded data */
507 if( this->readDER( ps_data_der, length ) )
520 * Parse the DER-encoded data at ps_data_der
521 * saving the key in an S-expression
523 int RSAKey::readDER( unsigned char const* ps_data_der, size_t length )
525 struct tag_info tag_inf;
526 gcry_mpi_t key_params[8];
530 /* parse the ASN1 structure */
531 if( parseTag( &ps_data_der, &length, &tag_inf )
532 || tag_inf.tag != TAG_SEQUENCE || tag_inf.class_ || !tag_inf.cons || tag_inf.ndef )
534 if( parseTag( &ps_data_der, &length, &tag_inf )
535 || tag_inf.tag != TAG_INTEGER || tag_inf.class_ || tag_inf.cons || tag_inf.ndef )
537 if( tag_inf.length != 1 || *ps_data_der )
538 goto bad_asn1; /* The value of the first integer is no 0. */
539 ps_data_der += tag_inf.length;
540 length -= tag_inf.length;
542 for( i = 0; i < 8; i++ )
544 if( parseTag( &ps_data_der, &length, &tag_inf )
545 || tag_inf.tag != TAG_INTEGER || tag_inf.class_ || tag_inf.cons || tag_inf.ndef )
547 err = gcry_mpi_scan( key_params + i, GCRYMPI_FMT_USG, ps_data_der, tag_inf.length, NULL );
550 msg_Err( this->p_demux, "error scanning RSA parameter %d: %s", i, gpg_strerror( err ) );
553 ps_data_der += tag_inf.length;
554 length -= tag_inf.length;
557 /* Convert from OpenSSL parameter ordering to the OpenPGP order.
558 * First check that p < q; if not swap p and q and recompute u.
560 if( gcry_mpi_cmp( key_params[3], key_params[4] ) > 0 )
562 gcry_mpi_swap( key_params[3], key_params[4] );
563 gcry_mpi_invm( key_params[7], key_params[3], key_params[4] );
566 /* Build the S-expression. */
567 err = gcry_sexp_build( & this->priv_key, NULL,
568 "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
569 key_params[0], key_params[1], key_params[2],
570 key_params[3], key_params[4], key_params[7] );
573 msg_Err( this->p_demux, "error building S-expression: %s", gpg_strerror( err ) );
578 for( i = 0; i < 8; i++ )
579 gcry_mpi_release( key_params[i] );
583 msg_Err( this->p_demux, "could not parse ASN1 structure; key might be corrupted" );
586 for( i = 0; i < 8; i++ )
587 gcry_mpi_release( key_params[i] );
592 * Parse the buffer at the address BUFFER which consists of the number
593 * of octets as stored at BUFLEN. Return the tag and the length part
594 * from the TLV triplet. Update BUFFER and BUFLEN on success. Checks
595 * that the encoded length does not exhaust the length of the provided
598 int RSAKey::parseTag( unsigned char const **buffer, size_t *buflen, struct tag_info *ti)
602 const unsigned char *buf = *buffer;
603 size_t length = *buflen;
611 return -1; /* Premature EOF. */
612 c = *buf++; length--;
615 ti->class_ = (c & 0xc0) >> 6;
616 ti->cons = !!(c & 0x20);
626 return -1; /* Premature EOF. */
627 c = *buf++; length--;
631 while ( (c & 0x80) );
637 return -1; /* Premature EOF. */
638 c = *buf++; length--;
646 return -1; /* Forbidden length value. */
649 unsigned long len = 0;
650 int count = c & 0x7f;
652 for (; count; count--)
656 return -1; /* Premature EOF. */
657 c = *buf++; length--;
664 if (ti->class_ == 0 && !ti->tag)
667 if (ti->length > length)
668 return -1; /* Data larger than buffer. */