/*
- Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2017 Carl Hetherington <cth@carlh.net>
- 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 <http://www.gnu.org/licenses/>.
+
+ 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_asset.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 <asdcp/AS_DCP.h>
+#include <asdcp/KM_util.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
+#include <boost/foreach.hpp>
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 boost::optional;
using namespace dcp;
+/* Magic value specified by SMPTE S430-1-2006 */
+static uint8_t smpte_structure_id[] = { 0xf1, 0xdc, 0x12, 0x44, 0x60, 0x16, 0x9a, 0x0e, 0x85, 0xbc, 0x30, 0x06, 0x42, 0xf8, 0x66, 0xab };
+
static void
put (uint8_t ** d, string s)
{
(*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<int> (**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
if (!bio) {
throw MiscError ("could not create memory BIO");
}
-
+
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<string> const encrypted_keys = kdm.keys ();
- for (list<string>::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;
/* 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 (optional<string>(), key_id, Key (p), cpl_id);
break;
}
case 138:
{
/* SMPTE */
/* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
+ DCP_ASSERT (memcmp (p, smpte_structure_id, 16) == 0);
p += 16;
/* 16 is is signer thumbprint [20 bytes] */
p += 20;
/* 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 (
+ 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)
+{
+
}
DecryptedKDM::DecryptedKDM (
- boost::shared_ptr<const CPL> cpl,
+ string cpl_id,
+ map<shared_ptr<const ReelMXF>, 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<shared_ptr<const ReelMXF>, 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<const CPL> cpl,
Key key,
LocalTime not_valid_before,
LocalTime not_valid_after,
, _content_title_text (content_title_text)
, _issue_date (issue_date)
{
- /* Create DecryptedKDMKey objects for each MXF asset */
- list<shared_ptr<const ReelAsset> > assets = cpl->reel_assets ();
- for (list<shared_ptr<const ReelAsset> >::iterator i = assets.begin(); i != assets.end(); ++i) {
- /* XXX: do non-MXF assets need keys? */
- shared_ptr<const ReelMXFAsset> mxf = boost::dynamic_pointer_cast<const ReelMXFAsset> (*i);
- if (mxf) {
- if (mxf->key_id().empty ()) {
- throw NotEncryptedError (mxf->id());
- }
- _keys.push_back (DecryptedKDMKey (mxf->key_type(), mxf->key_id(), key, cpl->id ()));
+ /* Create DecryptedKDMKey objects for each encryptable asset */
+ bool did_one = false;
+ BOOST_FOREACH(shared_ptr<const ReelAsset> i, cpl->reel_assets ()) {
+ shared_ptr<const ReelMXF> mxf = boost::dynamic_pointer_cast<const ReelMXF> (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 (optional<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<const Signer> signer, Certificate recipient, Formulation formulation) const
+DecryptedKDM::encrypt (shared_ptr<const CertificateChain> signer, Certificate recipient, vector<Certificate> trusted_devices, Formulation formulation) const
{
list<pair<string, string> > key_ids;
list<string> keys;
- for (list<DecryptedKDMKey>::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) {
+ /* We're making SMPTE keys so we must have a type for each one */
+ DCP_ASSERT (i.type());
+ key_ids.push_back (make_pair (i.type().get(), i.id ()));
/* XXX: SMPTE only */
uint8_t block[138];
uint8_t* p = block;
- /* Magic value specified by SMPTE S430-1-2006 */
- uint8_t structure_id[] = { 0xf1, 0xdc, 0x12, 0x44, 0x60, 0x16, 0x9a, 0x0e, 0x85, 0xbc, 0x30, 0x06, 0x42, 0xf8, 0x66, 0xab };
- put (&p, structure_id, 16);
+ put (&p, smpte_structure_id, 16);
- base64_decode (signer->certificates().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().get());
+ 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 ();
unsigned char encrypted[RSA_size(rsa)];
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);
}
return EncryptedKDM (
signer,
recipient,
- device_list_description,
+ trusted_devices,
_keys.front().cpl_id (),
_content_title_text,
+ _annotation_text,
_not_valid_before,
_not_valid_after,
formulation,