X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Fdecrypted_kdm.cc;h=8355e1c852ee917e772cb3f1a5a231ae42a63f43;hb=db86ed51ba8de94306c949d7b209a7f5f6b75075;hp=7e9e146d506fc44d15907e7e1a6d31cfbde2409d;hpb=7702e5d643440e75369078863b34f8a574ee4143;p=libdcp.git diff --git a/src/decrypted_kdm.cc b/src/decrypted_kdm.cc index 7e9e146d..8355e1c8 100644 --- a/src/decrypted_kdm.cc +++ b/src/decrypted_kdm.cc @@ -1,44 +1,62 @@ /* - Copyright (C) 2013-2014 Carl Hetherington + Copyright (C) 2013-2017 Carl Hetherington - This program is free software; you can redistribute it and/or modify + This file is part of libdcp. + + libdcp is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, + libdcp 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 General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + along with libdcp. If not, see . + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. */ #include "decrypted_kdm.h" #include "decrypted_kdm_key.h" #include "encrypted_kdm.h" +#include "reel_mxf.h" +#include "reel_asset.h" #include "util.h" #include "exceptions.h" #include "cpl.h" -#include "mxf.h" -#include "signer.h" -#include "AS_DCP.h" -#include "KM_util.h" +#include "certificate_chain.h" +#include "dcp_assert.h" #include "compose.hpp" +#include +#include #include #include #include +#include using std::list; +using std::vector; using std::string; -using std::stringstream; using std::setw; using std::setfill; using std::hex; using std::pair; +using std::map; using boost::shared_ptr; using namespace dcp; @@ -56,34 +74,37 @@ put (uint8_t ** d, uint8_t const * s, int N) (*d) += N; } -static void -put_uuid (uint8_t ** d, string id) +void +DecryptedKDM::put_uuid (uint8_t ** d, string id) { - id.erase (std::remove (id.begin(), id.end(), '-')); - for (int i = 0; i < 32; i += 2) { - stringstream s; - s << id[i] << id[i + 1]; - int h; - s >> hex >> h; - **d = h; - (*d)++; - } + /* 32 hex digits plus some hyphens */ + DCP_ASSERT (id.length() == 36); +#ifdef LIBDCP_WINDOWS + __mingw_sscanf ( +#else + sscanf ( +#endif + id.c_str(), + "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", + *d + 0, *d + 1, *d + 2, *d + 3, *d + 4, *d + 5, *d + 6, *d + 7, + *d + 8, *d + 9, *d + 10, *d + 11, *d + 12, *d + 13, *d + 14, *d + 15 + ); + + *d += 16; } -static string -get_uuid (unsigned char ** p) +string +DecryptedKDM::get_uuid (unsigned char ** p) { - stringstream g; - - for (int i = 0; i < 16; ++i) { - g << setw(2) << setfill('0') << hex << static_cast (**p); - (*p)++; - if (i == 3 || i == 5 || i == 7 || i == 9) { - g << '-'; - } - } + char buffer[37]; + snprintf ( + buffer, sizeof(buffer), "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", + (*p)[0], (*p)[1], (*p)[2], (*p)[3], (*p)[4], (*p)[5], (*p)[6], (*p)[7], + (*p)[8], (*p)[9], (*p)[10], (*p)[11], (*p)[12], (*p)[13], (*p)[14], (*p)[15] + ); - return g.str (); + *p += 16; + return buffer; } static string @@ -98,36 +119,33 @@ get (uint8_t ** p, int N) return g; } -DecryptedKDM::DecryptedKDM (EncryptedKDM const & kdm, boost::filesystem::path private_key) +DecryptedKDM::DecryptedKDM (EncryptedKDM const & kdm, string private_key) { /* Read the private key */ - - FILE* private_key_file = fopen_boost (private_key, "r"); - if (!private_key_file) { - throw FileError ("could not find RSA private key file", private_key, errno); + + BIO* bio = BIO_new_mem_buf (const_cast (private_key.c_str ()), -1); + if (!bio) { + throw MiscError ("could not create memory BIO"); } - - RSA* rsa = PEM_read_RSAPrivateKey (private_key_file, 0, 0, 0); - fclose (private_key_file); + + RSA* rsa = PEM_read_bio_RSAPrivateKey (bio, 0, 0, 0); if (!rsa) { throw FileError ("could not read RSA private key file", private_key, errno); } /* Use the private key to decrypt the keys */ - list const encrypted_keys = kdm.keys (); - for (list::const_iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) { - + BOOST_FOREACH (string const & i, kdm.keys ()) { /* Decode the base-64-encoded cipher value from the KDM */ unsigned char cipher_value[256]; - int const cipher_value_len = base64_decode (*i, cipher_value, sizeof (cipher_value)); + int const cipher_value_len = base64_decode (i, cipher_value, sizeof (cipher_value)); /* Decrypt it */ unsigned char * decrypted = new unsigned char[RSA_size(rsa)]; int const decrypted_len = RSA_private_decrypt (cipher_value_len, cipher_value, decrypted, rsa, RSA_PKCS1_OAEP_PADDING); if (decrypted_len == -1) { delete[] decrypted; - throw MiscError (String::compose ("Could not decrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0))); + throw KDMDecryptionError (ERR_error_string (ERR_get_error(), 0)); } unsigned char* p = decrypted; @@ -148,7 +166,7 @@ DecryptedKDM::DecryptedKDM (EncryptedKDM const & kdm, boost::filesystem::path pr /* 93 is not-valid-after (a string) [25 bytes] */ p += 25; /* 118 is the key [ASDCP::KeyLen bytes] */ - _keys.push_back (DecryptedKDMKey ("", key_id, Key (p), cpl_id)); + add_key ("", key_id, Key (p), cpl_id); break; } case 138: @@ -169,21 +187,25 @@ DecryptedKDM::DecryptedKDM (EncryptedKDM const & kdm, boost::filesystem::path pr /* 97 is not-valid-after (a string) [25 bytes] */ p += 25; /* 112 is the key [ASDCP::KeyLen bytes] */ - _keys.push_back (DecryptedKDMKey (key_type, key_id, Key (p), cpl_id)); + add_key (key_type, key_id, Key (p), cpl_id); break; } default: - assert (false); - } - + DCP_ASSERT (false); + } + delete[] decrypted; } RSA_free (rsa); + BIO_free (bio); + + _annotation_text = kdm.annotation_text (); + _content_title_text = kdm.content_title_text (); + _issue_date = kdm.issue_date (); } DecryptedKDM::DecryptedKDM ( - boost::shared_ptr cpl, LocalTime not_valid_before, LocalTime not_valid_after, string annotation_text, @@ -196,25 +218,83 @@ DecryptedKDM::DecryptedKDM ( , _content_title_text (content_title_text) , _issue_date (issue_date) { - /* Create DecryptedKDMKey objects for each MXF asset */ - list > content = cpl->content (); - for (list >::iterator i = content.begin(); i != content.end(); ++i) { - /* XXX: do non-MXF assets need keys? */ - shared_ptr mxf = boost::dynamic_pointer_cast (*i); - if (mxf) { - _keys.push_back (DecryptedKDMKey (mxf->key_type(), mxf->key_id(), mxf->key().get (), cpl->id ())); + +} + +DecryptedKDM::DecryptedKDM ( + string cpl_id, + map, Key> keys, + LocalTime not_valid_before, + LocalTime not_valid_after, + string annotation_text, + string content_title_text, + string issue_date + ) + : _not_valid_before (not_valid_before) + , _not_valid_after (not_valid_after) + , _annotation_text (annotation_text) + , _content_title_text (content_title_text) + , _issue_date (issue_date) +{ + for (map, Key>::const_iterator i = keys.begin(); i != keys.end(); ++i) { + add_key (i->first->key_type(), i->first->key_id().get(), i->second, cpl_id); + } +} + +DecryptedKDM::DecryptedKDM ( + shared_ptr cpl, + Key key, + LocalTime not_valid_before, + LocalTime not_valid_after, + string annotation_text, + string content_title_text, + string issue_date + ) + : _not_valid_before (not_valid_before) + , _not_valid_after (not_valid_after) + , _annotation_text (annotation_text) + , _content_title_text (content_title_text) + , _issue_date (issue_date) +{ + /* Create DecryptedKDMKey objects for each encryptable asset */ + bool did_one = false; + BOOST_FOREACH(shared_ptr i, cpl->reel_assets ()) { + shared_ptr mxf = boost::dynamic_pointer_cast (i); + if (mxf && mxf->key_id ()) { + add_key (mxf->key_type(), mxf->key_id().get(), key, cpl->id ()); + did_one = true; } } + + if (!did_one) { + throw NotEncryptedError (cpl->id ()); + } +} + +/** @param type (MDIK, MDAK etc.) + * @param key_id Key ID. + * @param key The actual symmetric key. + * @param cpl_id ID of CPL that the key is for. + */ +void +DecryptedKDM::add_key (string type, string key_id, Key key, string cpl_id) +{ + _keys.push_back (DecryptedKDMKey (type, key_id, key, cpl_id)); +} + +void +DecryptedKDM::add_key (DecryptedKDMKey key) +{ + _keys.push_back (key); } EncryptedKDM -DecryptedKDM::encrypt (shared_ptr signer, shared_ptr recipient) const +DecryptedKDM::encrypt (shared_ptr signer, Certificate recipient, vector trusted_devices, Formulation formulation) const { list > key_ids; list keys; - for (list::const_iterator i = _keys.begin(); i != _keys.end(); ++i) { - - key_ids.push_back (make_pair (i->type(), i->id ())); + BOOST_FOREACH (DecryptedKDMKey const & i, _keys) { + key_ids.push_back (make_pair (i.type(), i.id ())); /* XXX: SMPTE only */ uint8_t block[138]; @@ -224,40 +304,40 @@ DecryptedKDM::encrypt (shared_ptr signer, shared_ptrcertificates().leaf()->thumbprint (), p, 20); + base64_decode (signer->leaf().thumbprint (), p, 20); p += 20; - - put_uuid (&p, i->cpl_id ()); - put (&p, i->type ()); - put_uuid (&p, i->id ()); + + put_uuid (&p, i.cpl_id ()); + put (&p, i.type ()); + put_uuid (&p, i.id ()); put (&p, _not_valid_before.as_string ()); put (&p, _not_valid_after.as_string ()); - put (&p, i->key().value(), ASDCP::KeyLen); - + put (&p, i.key().value(), ASDCP::KeyLen); + /* Encrypt using the projector's public key */ - RSA* rsa = recipient->public_key (); + RSA* rsa = recipient.public_key (); unsigned char encrypted[RSA_size(rsa)]; int const encrypted_len = RSA_public_encrypt (p - block, block, encrypted, rsa, RSA_PKCS1_OAEP_PADDING); if (encrypted_len == -1) { throw MiscError (String::compose ("Could not encrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0))); } - + /* Lazy overallocation */ char out[encrypted_len * 2]; Kumu::base64encode (encrypted, encrypted_len, out, encrypted_len * 2); int const N = strlen (out); - stringstream lines; + string lines; for (int i = 0; i < N; ++i) { if (i > 0 && (i % 64) == 0) { - lines << "\n"; + lines += "\n"; } - lines << out[i]; + lines += out[i]; } - - keys.push_back (lines.str ()); + + keys.push_back (lines); } - string device_list_description = recipient->common_name (); + string device_list_description = recipient.subject_common_name (); if (device_list_description.find (".") != string::npos) { device_list_description = device_list_description.substr (device_list_description.find (".") + 1); } @@ -265,11 +345,13 @@ DecryptedKDM::encrypt (shared_ptr signer, shared_ptr