#include "file.h"
#include "reel_mono_picture_asset.h"
#include "reel_sound_asset.h"
+#include <cmath>
int
main ()
#include "reel_picture_asset.h"
#include "reel_sound_asset.h"
#include "reel_subtitle_asset.h"
+#include "local_time.h"
#include <libxml/parser.h>
using std::string;
/* default _content_version_id to and _content_version_label to
a random ID and the current time.
*/
- time_t now = time (0);
- struct tm* tm = localtime (&now);
- _content_version_id = "urn:uuid:" + make_uuid() + tm_to_string (tm);
+ _content_version_id = "urn:uuid:" + make_uuid() + LocalTime().as_string ();
_content_version_label_text = _content_version_id;
}
* @param kdm KDM.
*/
void
-CPL::add (KDM const & kdm)
+CPL::add (DecryptedKDM const & kdm)
{
for (list<shared_ptr<Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) {
(*i)->add (kdm);
#include <libxml++/libxml++.h>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
-#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/optional.hpp>
#include <boost/filesystem.hpp>
#include <list>
class XMLMetadata;
class MXFMetadata;
class Signer;
-class KDM;
+class DecryptedKDM;
/** @class CPL
* @brief A Composition Playlist.
) const;
void add (boost::shared_ptr<Reel> reel);
- void add (KDM const &);
+ void add (DecryptedKDM const &);
/** @return contents of the <AnnotationText> node */
std::string annotation_text () const {
#include "reel.h"
#include "cpl.h"
#include "signer.h"
-#include "kdm.h"
#include "compose.hpp"
#include "AS_DCP.h"
+#include "decrypted_kdm.h"
+#include "decrypted_kdm_key.h"
#include <xmlsec/xmldsig.h>
#include <xmlsec/app.h>
#include <libxml++/libxml++.h>
}
void
-DCP::add (KDM const & kdm)
+DCP::add (DecryptedKDM const & kdm)
{
- list<KDMKey> keys = kdm.keys ();
+ list<DecryptedKDMKey> keys = kdm.keys ();
list<shared_ptr<CPL> > cpl = cpls ();
for (list<shared_ptr<CPL> >::iterator i = cpl.begin(); i != cpl.end(); ++i) {
- for (list<KDMKey>::iterator j = keys.begin(); j != keys.end(); ++j) {
+ for (list<DecryptedKDMKey>::iterator j = keys.begin(); j != keys.end(); ++j) {
if (j->cpl_id() == (*i)->id()) {
(*i)->add (kdm);
}
class CPL;
class XMLMetadata;
class Signer;
-class KDM;
+class DecryptedKDM;
class Asset;
namespace parse {
bool encrypted () const;
- void add (KDM const &);
+ void add (DecryptedKDM const &);
void write_xml (
Standard standard,
--- /dev/null
+/*
+ Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+
+ This program 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,
+ 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.
+
+*/
+
+#include "decrypted_kdm.h"
+#include "decrypted_kdm_key.h"
+#include "encrypted_kdm.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 "compose.hpp"
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+
+using std::list;
+using std::string;
+using std::stringstream;
+using std::setw;
+using std::setfill;
+using std::hex;
+using std::pair;
+using boost::shared_ptr;
+using namespace dcp;
+
+static void
+put (uint8_t ** d, string s)
+{
+ memcpy (*d, s.c_str(), s.length());
+ (*d) += s.length();
+}
+
+static void
+put (uint8_t ** d, uint8_t const * s, int N)
+{
+ memcpy (*d, s, N);
+ (*d) += N;
+}
+
+static void
+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)++;
+ }
+}
+
+static string
+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 << '-';
+ }
+ }
+
+ return g.str ();
+}
+
+static string
+get (uint8_t ** p, int N)
+{
+ string g;
+ for (int i = 0; i < N; ++i) {
+ g += **p;
+ (*p)++;
+ }
+
+ return g;
+}
+
+DecryptedKDM::DecryptedKDM (EncryptedKDM const & kdm, boost::filesystem::path 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);
+ }
+
+ RSA* rsa = PEM_read_RSAPrivateKey (private_key_file, 0, 0, 0);
+ fclose (private_key_file);
+ 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) {
+
+ /* 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));
+
+ /* 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)));
+ }
+
+ unsigned char* p = decrypted;
+ switch (decrypted_len) {
+ case 134:
+ {
+ /* Inter-op */
+ /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
+ p += 16;
+ /* 16 is is signer thumbprint [20 bytes] */
+ p += 20;
+ /* 36 is CPL id [16 bytes] */
+ string const cpl_id = get_uuid (&p);
+ /* 52 is key id [16 bytes] */
+ string const key_id = get_uuid (&p);
+ /* 68 is not-valid-before (a string) [25 bytes] */
+ p += 25;
+ /* 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));
+ break;
+ }
+ case 138:
+ {
+ /* SMPTE */
+ /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
+ p += 16;
+ /* 16 is is signer thumbprint [20 bytes] */
+ p += 20;
+ /* 36 is CPL id [16 bytes] */
+ string const cpl_id = get_uuid (&p);
+ /* 52 is key type [4 bytes] */
+ string const key_type = get (&p, 4);
+ /* 56 is key id [16 bytes] */
+ string const key_id = get_uuid (&p);
+ /* 72 is not-valid-before (a string) [25 bytes] */
+ p += 25;
+ /* 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));
+ break;
+ }
+ default:
+ assert (false);
+ }
+
+ delete[] decrypted;
+ }
+
+ RSA_free (rsa);
+}
+
+DecryptedKDM::DecryptedKDM (
+ boost::shared_ptr<const CPL> cpl,
+ 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 MXF asset */
+ list<shared_ptr<const Content> > content = cpl->content ();
+ for (list<shared_ptr<const Content> >::iterator i = content.begin(); i != content.end(); ++i) {
+ /* XXX: do non-MXF assets need keys? */
+ shared_ptr<const MXF> mxf = boost::dynamic_pointer_cast<const MXF> (*i);
+ if (mxf) {
+ _keys.push_back (DecryptedKDMKey (mxf->key_type(), mxf->key_id(), mxf->key().get (), cpl->id ()));
+ }
+ }
+}
+
+EncryptedKDM
+DecryptedKDM::encrypt (shared_ptr<const Signer> signer, shared_ptr<const Certificate> recipient) 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 ()));
+
+ /* 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);
+
+ base64_decode (signer->certificates().leaf()->thumbprint (), p, 20);
+ p += 20;
+
+ 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);
+
+ /* Encrypt using the projector's 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;
+ for (int i = 0; i < N; ++i) {
+ if (i > 0 && (i % 64) == 0) {
+ lines << "\n";
+ }
+ lines << out[i];
+ }
+
+ keys.push_back (lines.str ());
+ }
+
+ string device_list_description = recipient->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,
+ _keys.front().cpl_id (),
+ _content_title_text,
+ _not_valid_before,
+ _not_valid_after,
+ key_ids,
+ keys
+ );
+}
--- /dev/null
+/*
+ Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+
+ This program 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,
+ 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.
+
+*/
+
+#include "key.h"
+#include "local_time.h"
+#include "decrypted_kdm_key.h"
+#include <boost/filesystem.hpp>
+
+namespace dcp {
+
+class DecryptedKDMKey;
+class EncryptedKDM;
+class Signer;
+class Certificate;
+class CPL;
+
+class DecryptedKDM
+{
+public:
+ /** @param kdm Encrypted KDM.
+ * @param private_key Private key file name.
+ */
+ DecryptedKDM (EncryptedKDM const & kdm, boost::filesystem::path private_key);
+
+ DecryptedKDM (
+ boost::shared_ptr<const CPL> cpl,
+ LocalTime not_valid_before,
+ LocalTime not_valid_after,
+ std::string annotation_text,
+ std::string content_title_text,
+ std::string issue_date
+ );
+
+ void add_key (std::string type, std::string id, Key key);
+ EncryptedKDM encrypt (boost::shared_ptr<const Signer>, boost::shared_ptr<const Certificate>) const;
+
+ std::list<DecryptedKDMKey> keys () const {
+ return _keys;
+ }
+
+private:
+ LocalTime _not_valid_before;
+ LocalTime _not_valid_after;
+ std::string _annotation_text;
+ std::string _content_title_text;
+ std::string _issue_date;
+ std::list<DecryptedKDMKey> _keys;
+};
+
+}
--- /dev/null
+/*
+ Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+
+ This program 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,
+ 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.
+
+*/
+
+#include "decrypted_kdm_key.h"
+
+using namespace dcp;
+
+bool
+dcp::operator== (dcp::DecryptedKDMKey const & a, dcp::DecryptedKDMKey const & b)
+{
+ return a.type() == b.type() && a.id() == b.id() && a.key() == b.key() && a.cpl_id() == b.cpl_id();
+}
--- /dev/null
+/*
+ Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+
+ This program 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,
+ 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.
+
+*/
+
+#ifndef LIBDCP_DECRYPTED_KDM_KEY_H
+#define LIBDCP_DECRYPTED_KDM_KEY_H
+
+#include "key.h"
+
+namespace dcp {
+
+class DecryptedKDMKey
+{
+public:
+ DecryptedKDMKey (std::string type, std::string id, Key key, std::string cpl_id)
+ : _type (type)
+ , _id (id)
+ , _key (key)
+ , _cpl_id (cpl_id)
+ {}
+
+ std::string type () const {
+ return _type;
+ }
+
+ std::string id () const {
+ return _id;
+ }
+
+ Key key () const {
+ return _key;
+ }
+
+ std::string cpl_id () const {
+ return _cpl_id;
+ }
+
+private:
+ std::string _type;
+ std::string _id;
+ Key _key;
+ std::string _cpl_id;
+};
+
+bool operator== (DecryptedKDMKey const &, DecryptedKDMKey const &);
+
+}
+
+#endif
--- /dev/null
+/*
+ Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+
+ This program 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,
+ 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.
+
+*/
+
+#include "encrypted_kdm.h"
+#include "util.h"
+#include "signer.h"
+#include <libcxml/cxml.h>
+#include <libxml++/document.h>
+#include <libxml++/nodes/element.h>
+#include <libxml/parser.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+using std::list;
+using std::string;
+using std::map;
+using std::pair;
+using boost::shared_ptr;
+using namespace dcp;
+
+namespace dcp {
+
+/** Namespace for classes used to hold our data; they are internal to this .cc file */
+namespace data {
+
+class Signer
+{
+public:
+ Signer () {}
+
+ Signer (shared_ptr<const cxml::Node> node)
+ : x509_issuer_name (node->string_child ("X509IssuerName"))
+ , x509_serial_number (node->string_child ("X509SerialNumber"))
+ {
+
+ }
+
+ void as_xml (xmlpp::Element* node) const
+ {
+ node->add_child("X509IssuerName", "ds")->add_child_text (x509_issuer_name);
+ node->add_child("X509SerialNumber", "ds")->add_child_text (x509_serial_number);
+ }
+
+ string x509_issuer_name;
+ string x509_serial_number;
+};
+
+class X509Data
+{
+public:
+ X509Data () {}
+
+ X509Data (boost::shared_ptr<const cxml::Node> node)
+ : x509_issuer_serial (Signer (node->node_child ("X509IssuerSerial")))
+ , x509_certificate (node->string_child ("X509Certificate"))
+ {
+ node->done ();
+ }
+
+ void as_xml (xmlpp::Element* node) const
+ {
+ x509_issuer_serial.as_xml (node->add_child ("X509IssuerSerial", "ds"));
+ node->add_child("X509Certificate", "ds")->add_child_text (x509_certificate);
+ }
+
+ Signer x509_issuer_serial;
+ std::string x509_certificate;
+};
+
+class Reference
+{
+public:
+ Reference () {}
+
+ Reference (string u)
+ : uri (u)
+ {}
+
+ Reference (shared_ptr<const cxml::Node> node)
+ : uri (node->string_attribute ("URI"))
+ , digest_value (node->string_child ("DigestValue"))
+ {
+
+ }
+
+ void as_xml (xmlpp::Element* node) const
+ {
+ node->set_attribute ("URI", uri);
+ node->add_child("DigestMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256");
+ node->add_child("DigestValue", "ds")->add_child_text (digest_value);
+ }
+
+ string uri;
+ string digest_value;
+};
+
+class SignedInfo
+{
+public:
+ SignedInfo ()
+ : authenticated_public ("#ID_AuthenticatedPublic")
+ , authenticated_private ("#ID_AuthenticatedPrivate")
+ {}
+
+ SignedInfo (shared_ptr<const cxml::Node> node)
+ {
+ list<shared_ptr<cxml::Node> > references = node->node_children ("Reference");
+ for (list<shared_ptr<cxml::Node> >::const_iterator i = references.begin(); i != references.end(); ++i) {
+ if ((*i)->string_attribute ("URI") == "#ID_AuthenticatedPublic") {
+ authenticated_public = Reference (*i);
+ } else if ((*i)->string_attribute ("URI") == "#ID_AuthenticatedPrivate") {
+ authenticated_private = Reference (*i);
+ }
+
+ /* XXX: do something if we don't recognise the node */
+ }
+ }
+
+ void as_xml (xmlpp::Element* node) const
+ {
+ node->add_child ("CanonicalizationMethod", "ds")->set_attribute (
+ "Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
+ );
+
+ node->add_child ("SignatureMethod", "ds")->set_attribute (
+ "Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
+ );
+
+ authenticated_public.as_xml (node->add_child ("Reference", "ds"));
+ authenticated_private.as_xml (node->add_child ("Reference", "ds"));
+ }
+
+private:
+ Reference authenticated_public;
+ Reference authenticated_private;
+};
+
+class Signature
+{
+public:
+ Signature () {}
+
+ Signature (shared_ptr<const cxml::Node> node)
+ : signed_info (node->node_child ("SignedInfo"))
+ , signature_value (node->string_child ("SignatureValue"))
+ {
+ list<shared_ptr<cxml::Node> > x509_data_nodes = node->node_child("KeyInfo")->node_children ("X509Data");
+ for (list<shared_ptr<cxml::Node> >::const_iterator i = x509_data_nodes.begin(); i != x509_data_nodes.end(); ++i) {
+ x509_data.push_back (X509Data (*i));
+ }
+ }
+
+ void as_xml (xmlpp::Node* node) const
+ {
+ signed_info.as_xml (node->add_child ("SignedInfo", "ds"));
+ node->add_child("SignatureValue", "ds")->add_child_text (signature_value);
+
+ xmlpp::Element* key_info_node = node->add_child ("KeyInfo", "ds");
+ for (std::list<X509Data>::const_iterator i = x509_data.begin(); i != x509_data.end(); ++i) {
+ i->as_xml (key_info_node->add_child ("X509Data", "ds"));
+ }
+ }
+
+ SignedInfo signed_info;
+ string signature_value;
+ list<X509Data> x509_data;
+};
+
+class AuthenticatedPrivate
+{
+public:
+ AuthenticatedPrivate () {}
+
+ AuthenticatedPrivate (shared_ptr<const cxml::Node> node)
+ {
+ list<shared_ptr<cxml::Node> > encrypted_key_nodes = node->node_children ("EncryptedKey");
+ for (list<shared_ptr<cxml::Node> >::const_iterator i = encrypted_key_nodes.begin(); i != encrypted_key_nodes.end(); ++i) {
+ encrypted_key.push_back ((*i)->node_child("CipherData")->string_child ("CipherValue"));
+ }
+ }
+
+ void as_xml (xmlpp::Element* node, map<string, xmlpp::Attribute *>& references) const
+ {
+ references["ID_AuthenticatedPrivate"] = node->set_attribute ("Id", "ID_AuthenticatedPrivate");
+
+ for (list<string>::const_iterator i = encrypted_key.begin(); i != encrypted_key.end(); ++i) {
+ xmlpp::Element* encrypted_key = node->add_child ("EncryptedKey", "enc");
+ xmlpp::Element* encryption_method = encrypted_key->add_child ("EncryptionMethod", "enc");
+ encryption_method->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p");
+ xmlpp::Element* digest_method = encryption_method->add_child ("DigestMethod", "ds");
+ digest_method->set_attribute ("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
+ xmlpp::Element* cipher_data = encrypted_key->add_child ("CipherData", "enc");
+ cipher_data->add_child("CipherValue", "enc")->add_child_text (*i);
+ }
+ }
+
+ list<string> encrypted_key;
+};
+
+class TypedKeyId
+{
+public:
+ TypedKeyId () {}
+
+ TypedKeyId (shared_ptr<const cxml::Node> node)
+ : key_type (node->string_child ("KeyType"))
+ , key_id (node->string_child ("KeyId").substr (9))
+ {
+
+ }
+
+ TypedKeyId (string type, string id)
+ : key_type (type)
+ , key_id (id)
+ {}
+
+ void as_xml (xmlpp::Element* node) const
+ {
+ node->add_child("KeyType")->add_child_text (key_type);
+ node->add_child("KeyId")->add_child_text ("urn:uuid:" + key_id);
+ }
+
+ string key_type;
+ string key_id;
+};
+
+class KeyIdList
+{
+public:
+ KeyIdList () {}
+
+ KeyIdList (shared_ptr<const cxml::Node> node)
+ {
+ list<shared_ptr<cxml::Node> > typed_key_id_nodes = node->node_children ("TypedKeyId");
+ for (list<shared_ptr<cxml::Node> >::const_iterator i = typed_key_id_nodes.begin(); i != typed_key_id_nodes.end(); ++i) {
+ typed_key_id.push_back (TypedKeyId (*i));
+ }
+ }
+
+ void as_xml (xmlpp::Element* node) const
+ {
+ for (list<TypedKeyId>::const_iterator i = typed_key_id.begin(); i != typed_key_id.end(); ++i) {
+ i->as_xml (node->add_child("TypedKeyId"));
+ }
+ }
+
+ list<TypedKeyId> typed_key_id;
+};
+
+class AuthorizedDeviceInfo
+{
+public:
+ AuthorizedDeviceInfo ()
+ : device_list_identifier (make_uuid ())
+ /* Sometimes digital_cinema_tools uses this magic thumbprint instead of that from an actual
+ recipient certificate. KDMs delivered to City Screen appear to use the same thing.
+ */
+ , certificate_thumbprint ("2jmj7l5rSw0yVb/vlWAYkK/YBwk=")
+ {}
+
+ AuthorizedDeviceInfo (shared_ptr<const cxml::Node> node)
+ : device_list_identifier (node->string_child ("DeviceListIdentifier").substr (9))
+ , device_list_description (node->string_child ("DeviceListDescription"))
+ , certificate_thumbprint (node->node_child("DeviceList")->string_child ("CertificateThumbprint"))
+ {
+
+ }
+
+ void as_xml (xmlpp::Element* node) const
+ {
+ node->add_child ("DeviceListIdentifier")->add_child_text ("urn:uuid:" + device_list_identifier);
+ node->add_child ("DeviceListDescription")->add_child_text (device_list_description);
+ xmlpp::Element* device_list = node->add_child ("DeviceList");
+ device_list->add_child("CertificateThumbprint")->add_child_text (certificate_thumbprint);
+ }
+
+ string device_list_identifier;
+ string device_list_description;
+ string certificate_thumbprint;
+};
+
+class X509IssuerSerial
+{
+public:
+ X509IssuerSerial () {}
+
+ X509IssuerSerial (shared_ptr<const cxml::Node> node)
+ : x509_issuer_name (node->string_child ("X509IssuerName"))
+ , x509_serial_number (node->string_child ("X509SerialNumber"))
+ {
+
+ }
+
+ void as_xml (xmlpp::Element* node) const
+ {
+ node->add_child("X509IssuerName", "ds")->add_child_text (x509_issuer_name);
+ node->add_child("X509SerialNumber", "ds")->add_child_text (x509_serial_number);
+ }
+
+ string x509_issuer_name;
+ string x509_serial_number;
+};
+
+class Recipient
+{
+public:
+ Recipient () {}
+
+ Recipient (shared_ptr<const cxml::Node> node)
+ : x509_issuer_serial (node->node_child ("X509IssuerSerial"))
+ , x509_subject_name (node->string_child ("X509SubjectName"))
+ {
+
+ }
+
+ void as_xml (xmlpp::Element* node) const
+ {
+ x509_issuer_serial.as_xml (node->add_child ("X509IssuerSerial"));
+ node->add_child("X509SubjectName")->add_child_text (x509_subject_name);
+ }
+
+ X509IssuerSerial x509_issuer_serial;
+ string x509_subject_name;
+};
+
+class KDMRequiredExtensions
+{
+public:
+ KDMRequiredExtensions () {}
+
+ KDMRequiredExtensions (shared_ptr<const cxml::Node> node)
+ : recipient (node->node_child ("Recipient"))
+ , composition_playlist_id (node->string_child ("CompositionPlaylistId").substr (9))
+ , content_title_text (node->string_child ("ContentTitleText"))
+ , not_valid_before (node->string_child ("ContentKeysNotValidBefore"))
+ , not_valid_after (node->string_child ("ContentKeysNotValidAfter"))
+ , authorized_device_info (node->node_child ("AuthorizedDeviceInfo"))
+ , key_id_list (node->node_child ("KeyIdList"))
+ {
+
+ }
+
+ void as_xml (xmlpp::Element* node) const
+ {
+ node->set_attribute ("xmlns", "http://www.smpte-ra.org/schemas/430-1/2006/KDM");
+
+ recipient.as_xml (node->add_child ("Recipient"));
+ node->add_child("CompositionPlaylistId")->add_child_text ("urn:uuid:" + composition_playlist_id);
+ /* XXX: no ContentAuthenticator */
+ node->add_child("ContentTitleText")->add_child_text (content_title_text);
+ node->add_child("ContentKeysNotValidBefore")->add_child_text (not_valid_before.as_string ());
+ node->add_child("ContentKeysNotValidAfter")->add_child_text (not_valid_after.as_string ());
+ authorized_device_info.as_xml (node->add_child ("AuthorizedDeviceInfo"));
+ key_id_list.as_xml (node->add_child ("KeyIdList"));
+
+ xmlpp::Element* forensic_mark_flag_list = node->add_child ("ForensicMarkFlagList");
+ forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable");
+ forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable");
+ }
+
+ Recipient recipient;
+ string composition_playlist_id;
+ string content_title_text;
+ LocalTime not_valid_before;
+ LocalTime not_valid_after;
+ AuthorizedDeviceInfo authorized_device_info;
+ KeyIdList key_id_list;
+};
+
+class RequiredExtensions
+{
+public:
+ RequiredExtensions () {}
+
+ RequiredExtensions (shared_ptr<const cxml::Node> node)
+ : kdm_required_extensions (node->node_child ("KDMRequiredExtensions"))
+ {
+
+ }
+
+ void as_xml (xmlpp::Element* node) const
+ {
+ kdm_required_extensions.as_xml (node->add_child ("KDMRequiredExtensions"));
+ }
+
+ KDMRequiredExtensions kdm_required_extensions;
+};
+
+class AuthenticatedPublic
+{
+public:
+ AuthenticatedPublic ()
+ : message_id (make_uuid ())
+ , issue_date (LocalTime().as_string ())
+ {}
+
+ AuthenticatedPublic (shared_ptr<const cxml::Node> node)
+ : message_id (node->string_child ("MessageId").substr (9))
+ , annotation_text (node->string_child ("AnnotationText"))
+ , issue_date (node->string_child ("IssueDate"))
+ , signer (node->node_child ("Signer"))
+ , required_extensions (node->node_child ("RequiredExtensions"))
+ {
+
+ }
+
+ void as_xml (xmlpp::Element* node, map<string, xmlpp::Attribute *>& references) const
+ {
+ references["ID_AuthenticatedPublic"] = node->set_attribute ("Id", "ID_AuthenticatedPublic");
+
+ node->add_child("MessageId")->add_child_text ("urn:uuid:" + message_id);
+ node->add_child("MessageType")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#kdm-key-type");
+ node->add_child("AnnotationText")->add_child_text (annotation_text);
+ node->add_child("IssueDate")->add_child_text (issue_date);
+
+ signer.as_xml (node->add_child ("Signer"));
+ required_extensions.as_xml (node->add_child ("RequiredExtensions"));
+
+ node->add_child ("NonCriticalExtensions");
+ }
+
+ string message_id;
+ string annotation_text;
+ string issue_date;
+ Signer signer;
+ RequiredExtensions required_extensions;
+};
+
+/** Class to describe our data. We use a class hierarchy as it's a bit nicer
+ * for XML data than a flat description.
+ */
+class EncryptedKDMData
+{
+public:
+ EncryptedKDMData ()
+ {
+
+ }
+
+ EncryptedKDMData (shared_ptr<const cxml::Node> node)
+ : authenticated_public (node->node_child ("AuthenticatedPublic"))
+ , authenticated_private (node->node_child ("AuthenticatedPrivate"))
+ , signature (node->node_child ("Signature"))
+ {
+
+ }
+
+ shared_ptr<xmlpp::Document> as_xml () const
+ {
+ shared_ptr<xmlpp::Document> document (new xmlpp::Document ());
+ xmlpp::Element* root = document->create_root_node ("DCinemaSecurityMessage", "http://www.smpte-ra.org/schemas/430-3/2006/ETM");
+ root->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "ds");
+ root->set_namespace_declaration ("http://www.w3.org/2001/04/xmlenc#", "enc");
+ map<string, xmlpp::Attribute *> references;
+ authenticated_public.as_xml (root->add_child ("AuthenticatedPublic"), references);
+ authenticated_private.as_xml (root->add_child ("AuthenticatedPrivate"), references);
+ signature.as_xml (root->add_child ("Signature", "ds"));
+
+ for (map<string, xmlpp::Attribute*>::const_iterator i = references.begin(); i != references.end(); ++i) {
+ xmlAddID (0, document->cobj(), (const xmlChar *) i->first.c_str(), i->second->cobj ());
+ }
+
+ return document;
+ }
+
+ AuthenticatedPublic authenticated_public;
+ AuthenticatedPrivate authenticated_private;
+ Signature signature;
+};
+
+}
+}
+
+EncryptedKDM::EncryptedKDM (boost::filesystem::path file)
+ : _data (new data::EncryptedKDMData (shared_ptr<cxml::Node> (new cxml::Document ("DCinemaSecurityMessage", file))))
+{
+
+}
+
+EncryptedKDM::EncryptedKDM (
+ shared_ptr<const Signer> signer,
+ shared_ptr<const Certificate> recipient,
+ string device_list_description,
+ string cpl_id,
+ string content_title_text,
+ LocalTime not_valid_before,
+ LocalTime not_valid_after,
+ list<pair<string, string> > key_ids,
+ list<string> keys
+ )
+ : _data (new data::EncryptedKDMData)
+{
+ /* Fill our XML-ish description in with the juicy bits that the caller has given */
+
+ data::AuthenticatedPublic& aup = _data->authenticated_public;
+ aup.signer.x509_issuer_name = signer->certificates().leaf()->issuer ();
+ aup.signer.x509_serial_number = signer->certificates().leaf()->serial ();
+
+ data::KDMRequiredExtensions& kre = _data->authenticated_public.required_extensions.kdm_required_extensions;
+ kre.recipient.x509_issuer_serial.x509_issuer_name = recipient->issuer ();
+ kre.recipient.x509_issuer_serial.x509_serial_number = recipient->serial ();
+ kre.recipient.x509_subject_name = recipient->subject ();
+ kre.authorized_device_info.device_list_description = device_list_description;
+ kre.composition_playlist_id = cpl_id;
+ kre.content_title_text = content_title_text;
+ kre.not_valid_before = not_valid_before;
+ kre.not_valid_after = not_valid_after;
+
+ for (list<pair<string, string> >::const_iterator i = key_ids.begin(); i != key_ids.end(); ++i) {
+ kre.key_id_list.typed_key_id.push_back (data::TypedKeyId (i->first, i->second));
+ }
+
+ _data->authenticated_private.encrypted_key = keys;
+
+ /* Read the XML so far and sign it */
+ shared_ptr<xmlpp::Document> doc = _data->as_xml ();
+ xmlpp::Node::NodeList children = doc->get_root_node()->get_children ();
+ for (xmlpp::Node::NodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
+ if ((*i)->get_name() == "Signature") {
+ signer->add_signature_value (*i, "ds");
+ }
+ }
+
+ /* Read the bits that add_signature_value did back into our variables */
+ shared_ptr<cxml::Node> signed_doc (new cxml::Node (doc->get_root_node ()));
+ _data->signature = data::Signature (signed_doc->node_child ("Signature"));
+}
+
+EncryptedKDM::EncryptedKDM (EncryptedKDM const & other)
+ : _data (new data::EncryptedKDMData (*other._data))
+{
+
+}
+
+EncryptedKDM &
+EncryptedKDM::operator= (EncryptedKDM const & other)
+{
+ if (this == &other) {
+ return *this;
+ }
+
+ delete _data;
+ _data = new data::EncryptedKDMData (*other._data);
+ return *this;
+}
+
+EncryptedKDM::~EncryptedKDM ()
+{
+ delete _data;
+}
+
+void
+EncryptedKDM::as_xml (boost::filesystem::path path) const
+{
+ FILE* f = fopen_boost (path, "w");
+ string const x = as_xml ();
+ fwrite (x.c_str(), 1, x.length(), f);
+ fclose (f);
+}
+
+string
+EncryptedKDM::as_xml () const
+{
+ xmlpp::Document document;
+ xmlpp::Element* root = document.create_root_node ("DCinemaSecurityMessage", "http://www.smpte-ra.org/schemas/430-3/2006/ETM");
+ root->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "ds");
+ root->set_namespace_declaration ("http://www.w3.org/2001/04/xmlenc#", "enc");
+
+ return _data->as_xml()->write_to_string ("UTF-8");
+}
+
+list<string>
+EncryptedKDM::keys () const
+{
+ return _data->authenticated_private.encrypted_key;
+}
--- /dev/null
+/*
+ Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+
+ This program 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,
+ 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.
+
+*/
+
+#include "local_time.h"
+#include <boost/filesystem.hpp>
+#include <boost/date_time/local_time/local_time.hpp>
+
+namespace cxml {
+ class Node;
+}
+
+namespace dcp {
+
+namespace data {
+ class EncryptedKDMData;
+}
+
+class Signer;
+class Certificate;
+
+class EncryptedKDM
+{
+public:
+ /** Read a KDM from an XML file */
+ EncryptedKDM (boost::filesystem::path file);
+
+ /** Construct an EncryptedKDM from a set of details */
+ EncryptedKDM (
+ boost::shared_ptr<const Signer> signer,
+ boost::shared_ptr<const Certificate> recipient,
+ std::string device_list_description,
+ std::string cpl_id,
+ std::string cpl_content_title_text,
+ LocalTime _not_valid_before,
+ LocalTime _not_valid_after,
+ std::list<std::pair<std::string, std::string> > key_ids,
+ std::list<std::string> keys
+ );
+
+ EncryptedKDM (EncryptedKDM const & kdm);
+ EncryptedKDM & operator= (EncryptedKDM const &);
+ ~EncryptedKDM ();
+
+ void as_xml (boost::filesystem::path) const;
+ std::string as_xml () const;
+
+ std::list<std::string> keys () const;
+
+private:
+ data::EncryptedKDMData* _data;
+};
+
+}
}
+TimeFormatError::TimeFormatError (string bad_time)
+ : _message (String::compose ("Bad time string %1", bad_time))
+{
+
+}
+
+
private:
std::string _message;
};
-
+
+/** @class TimeFormatError
+ * @brief A an error with a string passed to LocalTime.
+ */
+class TimeFormatError : public std::exception
+{
+public:
+ TimeFormatError (std::string bad_time);
+ ~TimeFormatError () throw () {}
+
+ /** @return error message */
+ char const * what () const throw () {
+ return _message.c_str ();
+ }
+
+private:
+ std::string _message;
+};
+
}
#endif
#include "file.h"
#include "util.h"
+#include <stdio.h>
using namespace dcp;
+++ /dev/null
-/*
- Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
-
- This program 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,
- 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.
-
-*/
-
-/** @file src/kdm.cc
- * @brief KDM and KDMKey classes.
- */
-
-#include "util.h"
-#include "kdm.h"
-#include "compose.hpp"
-#include "exceptions.h"
-#include "signer.h"
-#include "cpl.h"
-#include "mxf.h"
-#include "kdm_smpte_xml.h"
-#include "AS_DCP.h"
-#include "KM_util.h"
-#include <libcxml/cxml.h>
-#include <openssl/rsa.h>
-#include <openssl/pem.h>
-#include <openssl/err.h>
-#include <boost/algorithm/string.hpp>
-#include <iomanip>
-#include <algorithm>
-
-using std::list;
-using std::string;
-using std::stringstream;
-using std::map;
-using std::hex;
-using std::setw;
-using std::setfill;
-using std::cout;
-using boost::shared_ptr;
-using namespace dcp;
-
-KDM::KDM (boost::filesystem::path kdm, boost::filesystem::path 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);
- }
-
- RSA* rsa = PEM_read_RSAPrivateKey (private_key_file, 0, 0, 0);
- fclose (private_key_file);
- if (!rsa) {
- throw FileError ("could not read RSA private key file", private_key, errno);
- }
-
- /* Read the encrypted keys from the XML */
- /* XXX: this should be reading more stuff from the XML to fill our member variables */
-
- list<string> encrypted_keys;
- cxml::Document doc ("DCinemaSecurityMessage");
- doc.read_file (kdm.string ());
-
- shared_ptr<cxml::Node> authenticated_private = doc.node_child ("AuthenticatedPrivate");
- list<shared_ptr<cxml::Node> > encrypted_key_tags = authenticated_private->node_children ("EncryptedKey");
- for (list<shared_ptr<cxml::Node> >::const_iterator i = encrypted_key_tags.begin(); i != encrypted_key_tags.end(); ++i) {
- encrypted_keys.push_back ((*i)->node_child("CipherData")->string_child ("CipherValue"));
- }
-
- /* Use the private key to decrypt the keys */
-
- for (list<string>::iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) {
-
- /* 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));
-
- /* 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)));
- }
-
- _keys.push_back (KDMKey (decrypted, decrypted_len));
- delete[] decrypted;
- }
-
- RSA_free (rsa);
-}
-
-KDM::KDM (
- boost::shared_ptr<const CPL> cpl,
- boost::shared_ptr<const Signer> signer,
- boost::shared_ptr<const Certificate> recipient_cert,
- boost::posix_time::ptime not_valid_before, boost::posix_time::ptime not_valid_after,
- string annotation_text, string issue_date
- )
- : _id (make_uuid ())
- , _annotation_text (annotation_text)
- , _issue_date (issue_date)
- , _recipient_cert (recipient_cert)
- , _cpl (cpl)
- , _signer (signer)
- , _not_valid_before (not_valid_before)
- , _not_valid_after (not_valid_after)
- , _device_list_identifier_id (make_uuid ())
-{
- /* Set up our KDMKey objects. This extracts Key objects from each MXF asset and
- puts them (with other stuff) into KDMKey objects.
- */
- list<shared_ptr<const Content> > content = cpl->content ();
- for (list<shared_ptr<const Content> >::iterator i = content.begin(); i != content.end(); ++i) {
- /* XXX: non-MXF assets? */
- shared_ptr<const MXF> mxf = boost::dynamic_pointer_cast<const MXF> (*i);
- if (mxf) {
- _keys.push_back (
- KDMKey (
- signer, cpl->id (), mxf->key_type (), mxf->key_id (),
- not_valid_before, not_valid_after, mxf->key().get()
- )
- );
- }
- }
-}
-
-void
-KDM::authenticated_public (xmlpp::Element* node, map<string, xmlpp::Attribute *>& references) const
-{
- references["ID_AuthenticatedPublic"] = node->set_attribute ("Id", "ID_AuthenticatedPublic");
- node->add_child("MessageId")->add_child_text ("urn:uuid:" + _id);
- node->add_child("MessageType")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#kdm-key-type");
- node->add_child("AnnotationText")->add_child_text (_annotation_text);
- node->add_child("IssueDate")->add_child_text (_issue_date);
-
- /* Signer */
- xmlpp::Element* signer = node->add_child ("Signer");
- signer->add_child("X509IssuerName", "ds")->add_child_text (_signer->certificates().leaf()->issuer ());
- signer->add_child("X509SerialNumber", "ds")->add_child_text (_signer->certificates().leaf()->serial ());
-
- /* Everything else is in RequiredExtensions/KDMRequiredExtensions */
- xmlpp::Element* kdm_required_extensions = node->add_child("RequiredExtensions")->add_child("KDMRequiredExtensions");
- kdm_required_extensions->set_attribute ("xmlns", "http://www.smpte-ra.org/schemas/430-1/2006/KDM");
-
- /* Recipient */
- xmlpp::Element* recipient = kdm_required_extensions->add_child ("Recipient");
- xmlpp::Element* x509_issuer_serial = recipient->add_child ("X509IssuerSerial");
- x509_issuer_serial->add_child("X509IssuerName", "ds")->add_child_text (_recipient_cert->issuer ());
- x509_issuer_serial->add_child("X509SerialNumber", "ds")->add_child_text (_recipient_cert->serial ());
- recipient->add_child("X509SubjectName")->add_child_text (_recipient_cert->subject ());
-
- kdm_required_extensions->add_child("CompositionPlaylistId")->add_child_text ("urn:uuid:" + _cpl->id ());
- /* XXX: no ContentAuthenticator */
- kdm_required_extensions->add_child("ContentTitleText")->add_child_text (_cpl->content_title_text ());
- kdm_required_extensions->add_child("ContentKeysNotValidBefore")->add_child_text (ptime_to_string (_not_valid_before));
- kdm_required_extensions->add_child("ContentKeysNotValidAfter")->add_child_text (ptime_to_string (_not_valid_after));
-
- /* AuthorizedDeviceInfo */
- xmlpp::Element* authorized_device_info = kdm_required_extensions->add_child("AuthorizedDeviceInfo");
- authorized_device_info->add_child ("DeviceListIdentifier")->add_child_text ("urn:uuid:" + _device_list_identifier_id);
- string n = _recipient_cert->common_name ();
- if (n.find (".") != string::npos) {
- n = n.substr (n.find (".") + 1);
- }
- authorized_device_info->add_child ("DeviceListDescription")->add_child_text (n);
- xmlpp::Element* device_list = authorized_device_info->add_child ("DeviceList");
- /* Sometimes digital_cinema_tools uses this magic thumbprint instead of that from an actual
- recipient certificate. KDMs delivered to City Screen appear to use the same thing.
- */
- device_list->add_child("CertificateThumbprint")->add_child_text ("2jmj7l5rSw0yVb/vlWAYkK/YBwk=");
-
- /* KeyIdList */
- xmlpp::Element* key_id_list = kdm_required_extensions->add_child("KeyIdList");
- list<shared_ptr<const Content> > content = _cpl->content ();
- for (list<shared_ptr<const Content> >::iterator i = content.begin(); i != content.end(); ++i) {
- /* XXX: non-MXF assets? */
- shared_ptr<const MXF> mxf = boost::dynamic_pointer_cast<const MXF> (*i);
- if (mxf) {
- xmlpp::Element* typed_key_id = key_id_list->add_child ("TypedKeyId");
- typed_key_id->add_child("KeyType")->add_child_text (mxf->key_type ());
- typed_key_id->add_child("KeyId")->add_child_text ("urn:uuid:" + mxf->key_id ());
- }
- }
-
- /* ForensicMarkFlagList */
- xmlpp::Element* forensic_mark_flag_list = kdm_required_extensions->add_child ("ForensicMarkFlagList");
- forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable");
- forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable");
-
- node->add_child ("NonCriticalExtensions");
-}
-
-void
-KDM::authenticated_private (xmlpp::Element* node, map<string, xmlpp::Attribute *>& references) const
-{
- references["ID_AuthenticatedPrivate"] = node->set_attribute ("Id", "ID_AuthenticatedPrivate");
-
- for (list<KDMKey>::const_iterator i = _keys.begin(); i != _keys.end(); ++i) {
- xmlpp::Element* encrypted_key = node->add_child ("EncryptedKey", "enc");
- xmlpp::Element* encryption_method = encrypted_key->add_child ("EncryptionMethod", "enc");
- encryption_method->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p");
- xmlpp::Element* digest_method = encryption_method->add_child ("DigestMethod", "ds");
- digest_method->set_attribute ("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
- xmlpp::Element* cipher_data = encrypted_key->add_child ("CipherData", "enc");
- cipher_data->add_child("CipherValue", "enc")->add_child_text (i->encrypted_base64 (_recipient_cert));
- }
-}
-
-void
-KDM::signature (xmlpp::Element* node, map<string, xmlpp::Attribute *> const & references) const
-{
- xmlpp::Element* signed_info = node->add_child ("SignedInfo", "ds");
- signed_info->add_child ("CanonicalizationMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments");
- signed_info->add_child ("SignatureMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
-
- for (map<string, xmlpp::Attribute *>::const_iterator i = references.begin(); i != references.end(); ++i) {
- xmlpp::Element* reference = signed_info->add_child ("Reference", "ds");
- reference->set_attribute ("URI", "#" + i->first);
- reference->add_child("DigestMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256");
- reference->add_child("DigestValue", "ds")->add_child_text ("");
- }
-
- node->add_child("SignatureValue", "ds")->add_child_text ("");
- node->add_child("KeyInfo", "ds");
-}
-
-void
-KDM::as_xml (boost::filesystem::path path) const
-{
- FILE* f = fopen_boost (path, "w");
- string const x = as_xml ();
- fwrite (x.c_str(), 1, x.length(), f);
- fclose (f);
-}
-
-string
-KDM::as_xml () const
-{
- xmlpp::Document document;
- xmlpp::Element* root = document.create_root_node ("DCinemaSecurityMessage", "http://www.smpte-ra.org/schemas/430-3/2006/ETM");
- root->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "ds");
- root->set_namespace_declaration ("http://www.w3.org/2001/04/xmlenc#", "enc");
-
- map<string, xmlpp::Attribute *> references;
- authenticated_public (root->add_child ("AuthenticatedPublic"), references);
- authenticated_private (root->add_child ("AuthenticatedPrivate"), references);
-
- xmlpp::Element* signature_node = root->add_child ("Signature", "ds");
- signature (signature_node, references);
-
- for (map<string, xmlpp::Attribute*>::const_iterator i = references.begin(); i != references.end(); ++i) {
- xmlAddID (0, document.cobj(), (const xmlChar *) i->first.c_str(), i->second->cobj ());
- }
-
- _signer->add_signature_value (signature_node, "ds");
-
- /* This must *not* be the _formatted version, otherwise the signature
- will be wrong.
- */
- return document.write_to_string ("UTF-8");
-}
-
-KDMKey::KDMKey (
- boost::shared_ptr<const Signer> signer,
- string cpl_id,
- string key_type,
- string key_id,
- boost::posix_time::ptime from,
- boost::posix_time::ptime until,
- Key key
- )
- : _cpl_id (cpl_id)
- , _key_type (key_type)
- , _key_id (key_id)
- , _not_valid_before (ptime_to_string (from))
- , _not_valid_after (ptime_to_string (until))
- , _key (key)
-{
- base64_decode (signer->certificates().leaf()->thumbprint (), _signer_thumbprint, 20);
-}
-
-KDMKey::KDMKey (uint8_t const * raw, int len)
-{
- switch (len) {
- case 134:
- /* interop */
- /* [0-15] is structure id (fixed sequence specified by standard) */
- raw += 16;
- get (_signer_thumbprint, &raw, 20);
- _cpl_id = get_uuid (&raw);
- _key_id = get_uuid (&raw);
- _not_valid_before = get (&raw, 25);
- _not_valid_after = get (&raw, 25);
- _key = Key (raw);
- break;
- case 138:
- /* SMPTE */
- /* [0-15] is structure id (fixed sequence specified by standard) */
- raw += 16;
- get (_signer_thumbprint, &raw, 20);
- _cpl_id = get_uuid (&raw);
- _key_type = get (&raw, 4);
- _key_id = get_uuid (&raw);
- _not_valid_before = get (&raw, 25);
- _not_valid_after = get (&raw, 25);
- _key = Key (raw);
- break;
- default:
- assert (false);
- }
-}
-
-KDMKey::KDMKey (KDMKey const & other)
- : _cpl_id (other._cpl_id)
- , _key_type (other._key_type)
- , _key_id (other._key_id)
- , _not_valid_before (other._not_valid_before)
- , _not_valid_after (other._not_valid_after)
- , _key (other._key)
-{
- memcpy (_signer_thumbprint, other._signer_thumbprint, 20);
-}
-
-KDMKey &
-KDMKey::operator= (KDMKey const & other)
-{
- if (&other == this) {
- return *this;
- }
-
- _cpl_id = other._cpl_id;
- _key_type = other._key_type;
- _key_id = other._key_id;
- _not_valid_before = other._not_valid_before;
- _not_valid_after = other._not_valid_after;
- _key = other._key;
- memcpy (_signer_thumbprint, other._signer_thumbprint, 20);
-
- return *this;
-}
-
-string
-KDMKey::encrypted_base64 (boost::shared_ptr<const Certificate> recipient_cert) const
-{
- assert (_key_type.length() == 4);
- assert (_not_valid_before.length() == 25);
- assert (_not_valid_after.length() == 25);
-
- /* 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, _signer_thumbprint, 20);
- put_uuid (&p, _cpl_id);
- put (&p, _key_type);
- put_uuid (&p, _key_id);
- put (&p, _not_valid_before);
- put (&p, _not_valid_after);
- put (&p, _key.value(), ASDCP::KeyLen);
-
- /* Encrypt using the projector's public key */
- RSA* rsa = recipient_cert->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;
- for (int i = 0; i < N; ++i) {
- if (i > 0 && (i % 64) == 0) {
- lines << "\n";
- }
- lines << out[i];
- }
-
- return lines.str ();
-}
-
-string
-KDMKey::get (uint8_t const ** p, int N) const
-{
- string g;
- for (int i = 0; i < N; ++i) {
- g += **p;
- (*p)++;
- }
-
- return g;
-}
-
-void
-KDMKey::get (uint8_t* o, uint8_t const ** p, int N) const
-{
- memcpy (o, *p, N);
- *p += N;
-}
-
-string
-KDMKey::get_uuid (unsigned char const ** p) const
-{
- 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 << '-';
- }
- }
-
- return g.str ();
-}
-
-void
-KDMKey::put (uint8_t ** d, uint8_t const * s, int N) const
-{
- memcpy (*d, s, N);
- (*d) += N;
-}
-
-void
-KDMKey::put (uint8_t ** d, string s) const
-{
- memcpy (*d, s.c_str(), s.length());
- (*d) += s.length();
-}
-
-void
-KDMKey::put_uuid (uint8_t ** d, string id) const
-{
- 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)++;
- }
-}
-
-bool
-dcp::operator== (dcp::KDMKey const & a, dcp::KDMKey const & b)
-{
- if (memcmp (a._signer_thumbprint, b._signer_thumbprint, 20) != 0) {
- return false;
- }
-
- return (
- a._cpl_id == b._cpl_id &&
- a._key_type == b._key_type &&
- a._key_id == b._key_id &&
- a._not_valid_before == b._not_valid_before &&
- a._not_valid_after == b._not_valid_after &&
- a._key == b._key
- );
-}
+++ /dev/null
-/*
- Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
-
- This program 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,
- 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.
-
-*/
-
-/** @file src/kdm.h
- * @brief KDM and KDMKey classes.
- */
-
-#ifndef LIBDCP_KDM_H
-#define LIBDCP_KDM_H
-
-#include "key.h"
-#include "metadata.h"
-#include <boost/filesystem.hpp>
-#include <boost/scoped_ptr.hpp>
-#include <boost/date_time/posix_time/posix_time.hpp>
-
-class kdm_key_test;
-
-namespace xmlpp {
- class Element;
- class Attribute;
-}
-
-namespace dcp {
-
-namespace xml {
- class DCinemaSecurityMessage;
-}
-
-class Signer;
-class Certificate;
-class CPL;
-
-/** @class KDMKey
- * @brief A single key (and associated metadata) for encrypting or decrypting an MXF.
- *
- * One or more of these are delivered (themselves encrypted) in a KDM. The following
- * data is collected into a block:
- *
- * A structure ID (a magic value specified by the standard)
- * The thumbprint of the KDM signer's certificate.
- * The CPL ID.
- * The key ID.
- * Validity start and end times.
- * The key itself
- *
- * This data block is then encrypted using the projector's public key, so that
- * only the target projector can decrypt block.
- */
-class KDMKey
-{
-public:
- /** Create a KDMKey from the raw block that is encrypted in the KDM's CipherData.
- * @param raw Pointer to data block (134 bytes for interop, 138 bytes for SMPTE).
- * @param len Length of the data block in bytes.
- */
- KDMKey (uint8_t const * raw, int len);
-
- /** Create a KDMKey from its constituent parts.
- * @param signer Signer for the KDM.
- * @param cpl_id ID of the CPL that the KDM is for.
- * @param key_type Type of data that this key is for (MDIK for image, MDAK for audio, ...)
- * @param key_id ID of this key.
- * @param from Valid-from time.
- * @param until Valid-until time.
- * @param key The key itself.
- */
- KDMKey (
- boost::shared_ptr<const Signer> signer,
- std::string cpl_id, std::string key_type, std::string key_id, boost::posix_time::ptime from, boost::posix_time::ptime until, Key key
- );
-
- KDMKey (KDMKey const &);
-
- KDMKey& operator= (KDMKey const &);
-
- /** @return ID of the CPL that the KDM is for */
- std::string cpl_id () const {
- return _cpl_id;
- }
-
- /** @return ID of the key */
- std::string key_id () const {
- return _key_id;
- }
-
- /** @return start of the validity period as a string */
- std::string not_valid_before () const {
- return _not_valid_before;
- }
-
- /** @return end of the validity period as a string */
- std::string not_valid_after () const {
- return _not_valid_after;
- }
-
- /** @return the key itself */
- Key key () const {
- return _key;
- }
-
- /** @param cert Cerfificate.
- * @return The data block encrypted with a certificate's public key and converted to base 64.
- */
- std::string encrypted_base64 (boost::shared_ptr<const Certificate> cert) const;
-
-private:
- friend class ::kdm_key_test;
-
- void get (uint8_t *, uint8_t const **, int) const;
- std::string get (uint8_t const **, int) const;
- std::string get_uuid (uint8_t const **) const;
- void put (uint8_t **, uint8_t const *, int) const;
- void put (uint8_t **, std::string) const;
- void put_uuid (uint8_t **, std::string) const;
-
- friend bool operator== (KDMKey const &, KDMKey const &);
-
- uint8_t _signer_thumbprint[20];
- std::string _cpl_id;
- std::string _key_type;
- std::string _key_id;
- std::string _not_valid_before;
- std::string _not_valid_after;
- Key _key;
-};
-
-/** @class KDM
- * @brief A class representing a Key Delivery Message (KDM).
- *
- * A KDM wraps one or more content keys (which we wrap into KDMKey objects) and various
- * other metadata. This class can read and decrypt existing KDMs (provided you have
- * the private key that the KDM was targeted at). It can also create new KDMs for
- * a given CPL.
- */
-class KDM
-{
-public:
- /** Load and decrypt a KDM. After this constructor the KDMKeys can be read
- * and used to decrypt MXFs.
- *
- * @param kdm KDM file name.
- * @param private_key Private key file name.
- */
- KDM (boost::filesystem::path kdm, boost::filesystem::path private_key);
-
- /** Create a new KDM.
- * @param cpl CPL that the KDM is for.
- * @param signer Certificate chain to sign the KDM with.
- * @param recipient_cert Certificate of the projector that this KDM is targeted at.
- * @param not_valid_before Start of validity period.
- * @param not_valid_after End of validity period.
- * @param annotation_text Text for the <AnnotationText> node.
- * @param issue_date Text for the <IssueDate> node.
- */
- KDM (
- boost::shared_ptr<const CPL> cpl, boost::shared_ptr<const Signer> signer, boost::shared_ptr<const Certificate> recipient_cert,
- boost::posix_time::ptime not_valid_before, boost::posix_time::ptime not_valid_after,
- std::string annotation_text, std::string issue_date
- );
-
- KDM (KDM const &);
- KDM & operator= (KDM const &);
-
- /** @return The unencrypted content keys from this KDM */
- std::list<KDMKey> keys () const {
- return _keys;
- }
-
- /** Write this KDM to a file.
- * @param file File to write to.
- */
- void as_xml (boost::filesystem::path file) const;
-
- /** Obtain this KDM as an XML string.
- * @return XML string.
- */
- std::string as_xml () const;
-
-private:
- void authenticated_public (xmlpp::Element *, std::map<std::string, xmlpp::Attribute *>& references) const;
- void authenticated_private (xmlpp::Element *, std::map<std::string, xmlpp::Attribute *>& references) const;
- void signature (xmlpp::Element *, std::map<std::string, xmlpp::Attribute *> const & references) const;
-
- /** Unencrypted MXF content keys */
- std::list<KDMKey> _keys;
-
- /** AuthenticatedPublic/MessageId (without the urn:uuid: prefix) */
- std::string _id;
- /** AuthenticatedPublic/AnnotationText */
- std::string _annotation_text;
- /** AuthenticatedPublic/IssueDate */
- std::string _issue_date;
- /** Certificate of recipient */
- boost::shared_ptr<const Certificate> _recipient_cert;
- /** CPL that this KDM is for, or 0 if we do not have a CPL object */
- boost::shared_ptr<const CPL> _cpl;
- boost::shared_ptr<const Signer> _signer;
- /** Start time for this KDM */
- boost::posix_time::ptime _not_valid_before;
- /** End time for this KDM */
- boost::posix_time::ptime _not_valid_after;
- /** KDMRequiredExtensions/AuthorizedDeviceInfo/DeviceListIdentifier */
- std::string _device_list_identifier_id;
-};
-
-
-}
-
-#endif
--- /dev/null
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program 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,
+ 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.
+
+*/
+
+#include "local_time.h"
+#include "exceptions.h"
+#include <boost/lexical_cast.hpp>
+#include <cstdio>
+
+using std::string;
+using boost::lexical_cast;
+using namespace dcp;
+
+LocalTime::LocalTime ()
+{
+ time_t now = time (0);
+ struct tm* tm = localtime (&now);
+ _year = tm->tm_year + 1900;
+ _month = tm->tm_mon + 1;
+ _day = tm->tm_mday + 1;
+ _hour = tm->tm_hour;
+ _minute = tm->tm_min;
+ _second = tm->tm_sec;
+
+ int offset = 0;
+
+#ifdef LIBDCP_POSIX
+ offset = tm->tm_gmtoff / 60;
+#else
+ TIME_ZONE_INFORMATION tz;
+ GetTimeZoneInformation (&tz);
+ offset = tz.Bias;
+#endif
+
+ bool const negative = offset < 0;
+ offset = negative ? -offset : offset;
+
+ _tz_hour = offset / 60;
+ _tz_minute = offset % 60;
+
+ if (negative) {
+ _tz_hour = -_tz_hour;
+ }
+}
+
+/** @param s A string of the form 2013-01-05T18:06:59+04:00 */
+LocalTime::LocalTime (string s)
+{
+ /* 2013-01-05T18:06:59+04:00 */
+ /* 0123456789012345678901234 */
+
+ if (s.length() < 25) {
+ throw TimeFormatError (s);
+ }
+
+ /* Check incidental characters */
+ if (s[4] != '-' || s[7] != '-' || s[10] != 'T' || s[13] != ':' || s[16] != ':' || s[22] != ':') {
+ throw TimeFormatError (s);
+ }
+
+ _year = lexical_cast<int> (s.substr (0, 4));
+ _month = lexical_cast<int> (s.substr (5, 2));
+ _day = lexical_cast<int> (s.substr (8, 2));
+ _hour = lexical_cast<int> (s.substr (11, 2));
+ _minute = lexical_cast<int> (s.substr (14, 2));
+ _second = lexical_cast<int> (s.substr (17, 2));
+ _tz_hour = lexical_cast<int> (s.substr (20, 2));
+ _tz_minute = lexical_cast<int> (s.substr (23, 2));
+
+ if (s[19] == '-') {
+ _tz_hour = -_tz_hour;
+ } else if (s[19] != '+') {
+ throw TimeFormatError (s);
+ }
+}
+
+string
+LocalTime::as_string () const
+{
+ char buffer[32];
+ snprintf (
+ buffer, sizeof (buffer),
+ "%04d-%02d-%02dT%02d:%02d:%02d%s%02d:%02d",
+ _year, _month, _day, _hour, _minute, _second, (_tz_hour >= 0 ? "+" : "-"), abs (_tz_hour), _tz_minute
+ );
+ return buffer;
+}
--- /dev/null
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program 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,
+ 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.
+
+*/
+
+#ifndef LIBDCP_LOCAL_TIME_H
+#define LIBDCP_LOCAL_TIME_H
+
+#include <string>
+
+class local_time_test;
+
+namespace dcp {
+
+/** I tried to use boost for this, really I did */
+class LocalTime
+{
+public:
+ LocalTime ();
+ LocalTime (std::string);
+
+ std::string as_string () const;
+
+private:
+ friend class ::local_time_test;
+
+ int _year;
+ int _month;
+ int _day;
+ int _hour;
+ int _minute;
+ int _second;
+ int _tz_hour;
+ int _tz_minute;
+};
+
+}
+
+#endif
#include "metadata.h"
#include "util.h"
-#ifdef LIBDCP_WINDOWS
-#include <windows.h>
-#endif
+#include "local_time.h"
#include <sstream>
#include <iomanip>
#include <time.h>
void
XMLMetadata::set_issue_date_now ()
{
- time_t now = time (0);
- struct tm* tm = localtime (&now);
- issue_date = tm_to_string (tm);
+ issue_date = LocalTime().as_string ();
}
#include "util.h"
#include "metadata.h"
#include "exceptions.h"
-#include "kdm.h"
#include "compose.hpp"
#include <libxml++/nodes/element.h>
#include <boost/filesystem.hpp>
using std::list;
using std::pair;
using boost::shared_ptr;
-using boost::lexical_cast;
using boost::dynamic_pointer_cast;
using namespace dcp;
#include "reel_stereo_picture_asset.h"
#include "reel_sound_asset.h"
#include "reel_subtitle_asset.h"
-#include "kdm.h"
+#include "decrypted_kdm_key.h"
+#include "decrypted_kdm.h"
#include <libxml++/nodes/element.h>
using std::string;
}
void
-Reel::add (KDM const & kdm)
+Reel::add (DecryptedKDM const & kdm)
{
- list<KDMKey> keys = kdm.keys ();
+ list<DecryptedKDMKey> keys = kdm.keys ();
- for (list<KDMKey>::iterator i = keys.begin(); i != keys.end(); ++i) {
- if (i->key_id() == _main_picture->key_id()) {
+ for (list<DecryptedKDMKey>::iterator i = keys.begin(); i != keys.end(); ++i) {
+ if (i->id() == _main_picture->key_id()) {
_main_picture->mxf()->set_key (i->key ());
}
- if (i->key_id() == _main_sound->key_id()) {
+ if (i->id() == _main_sound->key_id()) {
_main_sound->mxf()->set_key (i->key ());
}
}
namespace dcp {
-class KDM;
+class DecryptedKDM;
class ReelAsset;
class ReelPictureAsset;
class ReelSoundAsset;
bool equals (boost::shared_ptr<const Reel> other, EqualityOptions opt, boost::function<void (NoteType, std::string)> notes) const;
- void add (KDM const &);
+ void add (DecryptedKDM const &);
void resolve_refs (std::list<boost::shared_ptr<Object> >);
#include "picture_mxf.h"
#include "compose.hpp"
#include <libcxml/cxml.h>
+#include <iomanip>
using std::bad_cast;
using std::string;
#include "gamma_lut.h"
#include "image.h"
#include "colour_matrix.h"
+#include <cmath>
using std::min;
using std::max;
return N;
}
-/** Convert a struct tm to a string of the form
- * 2014-01-26T21:39:00+01:00
- * @param tm struct tm.
- * @return Time as a string.
- */
-string
-dcp::tm_to_string (struct tm* tm)
-{
- char buffer[64];
- strftime (buffer, 64, "%Y-%m-%dT%H:%M:%S", tm);
-
- int offset = 0;
-
-#ifdef LIBDCP_POSIX
- offset = tm->tm_gmtoff / 60;
-#else
- TIME_ZONE_INFORMATION tz;
- GetTimeZoneInformation (&tz);
- offset = tz.Bias;
-#endif
-
- return string (buffer) + utc_offset_to_string (offset);
-}
-
-/** @param b Offset from UTC to local time in minutes.
- * @return string of the form e.g. -01:00.
- */
-string
-dcp::utc_offset_to_string (int b)
-{
- bool const negative = (b < 0);
- b = negative ? -b : b;
-
- int const hours = b / 60;
- int const minutes = b % 60;
-
- stringstream o;
- if (negative) {
- o << "-";
- } else {
- o << "+";
- }
-
- o << setw(2) << setfill('0') << hours << ":" << setw(2) << setfill('0') << minutes;
- return o.str ();
-}
-
-/** Convert a boost::posix_time::ptime to a string of the form
- * 2014-01-26T21:39:00+01:00.
- * @param t boost::posix_time::ptime.
- * @return Time as a string.
- */
-string
-dcp::ptime_to_string (boost::posix_time::ptime t)
-{
- struct tm t_tm = boost::posix_time::to_tm (t);
- return tm_to_string (&t_tm);
-}
-
-
/** @param p Path to open.
* @param t mode flags, as for fopen(3).
* @return FILE pointer or 0 on error.
#include "types.h"
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
-#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem.hpp>
#include <boost/optional.hpp>
#include <openjpeg.h>
extern int base64_decode (std::string const & in, unsigned char* out, int out_length);
extern boost::optional<boost::filesystem::path> relative_to_root (boost::filesystem::path root, boost::filesystem::path file);
-extern std::string tm_to_string (struct tm *);
-extern std::string utc_offset_to_string (int);
-extern std::string ptime_to_string (boost::posix_time::ptime);
extern FILE * fopen_boost (boost::filesystem::path, std::string);
template <class F, class T>
cpl.cc
dcp.cc
dcp_time.cc
+ decrypted_kdm.cc
+ decrypted_kdm_key.cc
+ encrypted_kdm.cc
exceptions.cc
file.cc
font.cc
gamma_lut.cc
image.cc
- kdm.cc
key.cc
load_font.cc
+ local_time.cc
metadata.cc
mono_picture_mxf.cc
mono_picture_mxf_writer.cc
content.h
dcp.h
dcp_time.h
+ decrypted_kdm.h
+ encrypted_kdm.h
exceptions.h
gamma_lut.h
image.h
- kdm.h
key.h
lut_cache.h
metadata.h
*/
#include <boost/test/unit_test.hpp>
-#include "kdm.h"
#include "dcp.h"
#include "mono_picture_frame.h"
#include "cpl.h"
+#include "decrypted_kdm.h"
+#include "encrypted_kdm.h"
#include "argb_frame.h"
#include "mono_picture_mxf.h"
#include "reel_picture_asset.h"
encrypted.read ();
BOOST_CHECK_EQUAL (encrypted.encrypted (), true);
- dcp::KDM kdm (
- "test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml",
+ dcp::DecryptedKDM kdm (
+ dcp::EncryptedKDM ("test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml"),
"test/data/private.key"
);
-
+
encrypted.add (kdm);
shared_ptr<const dcp::ARGBFrame> plaintext_frame = get_frame (plaintext);
/** Load in a KDM that didn't work at first */
BOOST_AUTO_TEST_CASE (failing_kdm_test)
{
- dcp::KDM kdm (
- "test/data/target.pem.crt.de5d4eba-e683-41ca-bdda-aa4ad96af3f4.kdm.xml",
+ dcp::DecryptedKDM kdm (
+ dcp::EncryptedKDM ("test/data/target.pem.crt.de5d4eba-e683-41ca-bdda-aa4ad96af3f4.kdm.xml"),
"test/data/private.key"
);
}
*/
-#include "kdm.h"
#include "KM_util.h"
#include "metadata.h"
#include "certificates.h"
#include "subtitle_content.h"
#include "reel_mono_picture_asset.h"
#include "reel_sound_asset.h"
+#include "encrypted_kdm.h"
+#include "decrypted_kdm.h"
#include <sndfile.h>
#include <boost/test/unit_test.hpp>
#include <boost/shared_ptr.hpp>
d.add (cpl);
d.write_xml (dcp::SMPTE, xml_metadata, signer);
- dcp::KDM kdm (
+ dcp::DecryptedKDM kdm (
cpl,
- signer,
- signer->certificates().leaf(),
- boost::posix_time::time_from_string ("2013-01-01 00:00:00"),
- boost::posix_time::time_from_string ("2017-01-08 00:00:00"),
+ dcp::LocalTime ("2013-01-01T00:00:00+00:00"),
+ dcp::LocalTime ("2017-01-08T00:00:00+00:00"),
"libdcp",
+ "test",
"2012-07-17T04:45:18+00:00"
);
- kdm.as_xml ("build/test/bar.kdm.xml");
+ kdm.encrypt(signer, signer->certificates().leaf()).as_xml ("build/test/bar.kdm.xml");
int r = system (
"xmllint --path schema --nonet --noout --schema schema/SMPTE-430-1-2006-Amd-1-2009-KDM.xsd build/test/bar.kdm.xml "
+++ /dev/null
-/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
- This program 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,
- 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.
-
-*/
-
-#include <boost/test/unit_test.hpp>
-#include "kdm.h"
-
-BOOST_AUTO_TEST_CASE (kdm_key_test)
-{
- uint8_t foo[138];
- memset (foo, 0, 138);
- dcp::KDMKey kkey (foo, 138);
-
- uint8_t* raw = new uint8_t[16];
- uint8_t* p = raw;
- kkey.put_uuid (&p, "5d51e8a1-b2a5-4da6-9b66-4615c3609440");
- BOOST_CHECK_EQUAL (raw[0], 0x5d);
- BOOST_CHECK_EQUAL (raw[1], 0x51);
- BOOST_CHECK_EQUAL (raw[2], 0xe8);
- BOOST_CHECK_EQUAL (raw[3], 0xa1);
- BOOST_CHECK_EQUAL (raw[4], 0xb2);
- BOOST_CHECK_EQUAL (raw[5], 0xa5);
- BOOST_CHECK_EQUAL (raw[6], 0x4d);
- BOOST_CHECK_EQUAL (raw[7], 0xa6);
- BOOST_CHECK_EQUAL (raw[8], 0x9b);
- BOOST_CHECK_EQUAL (raw[9], 0x66);
- BOOST_CHECK_EQUAL (raw[10], 0x46);
- BOOST_CHECK_EQUAL (raw[11], 0x15);
- BOOST_CHECK_EQUAL (raw[12], 0xc3);
- BOOST_CHECK_EQUAL (raw[13], 0x60);
- BOOST_CHECK_EQUAL (raw[14], 0x94);
- BOOST_CHECK_EQUAL (raw[15], 0x40);
- delete[] raw;
-}
/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
*/
#include <boost/test/unit_test.hpp>
-#include "kdm.h"
+#include <libxml++/libxml++.h>
+#include "encrypted_kdm.h"
+#include "decrypted_kdm.h"
using std::list;
+using std::stringstream;
using boost::shared_ptr;
BOOST_AUTO_TEST_CASE (kdm_test)
{
- dcp::KDM kdm (
- "test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml",
+ dcp::DecryptedKDM kdm (
+ dcp::EncryptedKDM ("test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml"),
"test/data/private.key"
);
- list<dcp::KDMKey> keys = kdm.keys ();
+ list<dcp::DecryptedKDMKey> keys = kdm.keys ();
BOOST_CHECK_EQUAL (keys.size(), 2);
BOOST_CHECK_EQUAL (keys.front().cpl_id(), "eece17de-77e8-4a55-9347-b6bab5724b9f");
- BOOST_CHECK_EQUAL (keys.front().key_id(), "4ac4f922-8239-4831-b23b-31426d0542c4");
- BOOST_CHECK_EQUAL (keys.front().not_valid_before(), "2013-07-06T20:04:58+00:00");
- BOOST_CHECK_EQUAL (keys.front().not_valid_after(), "2023-07-02T20:04:56+00:00");
+ BOOST_CHECK_EQUAL (keys.front().id(), "4ac4f922-8239-4831-b23b-31426d0542c4");
BOOST_CHECK_EQUAL (keys.front().key().hex(), "8a2729c3e5b65c45d78305462104c3fb");
BOOST_CHECK_EQUAL (keys.back().cpl_id(), "eece17de-77e8-4a55-9347-b6bab5724b9f");
- BOOST_CHECK_EQUAL (keys.back().key_id(), "73baf5de-e195-4542-ab28-8a465f7d4079");
- BOOST_CHECK_EQUAL (keys.back().not_valid_before(), "2013-07-06T20:04:58+00:00");
- BOOST_CHECK_EQUAL (keys.back().not_valid_after(), "2023-07-02T20:04:56+00:00");
+ BOOST_CHECK_EQUAL (keys.back().id(), "73baf5de-e195-4542-ab28-8a465f7d4079");
BOOST_CHECK_EQUAL (keys.back().key().hex(), "5327fb7ec2e807bd57059615bf8a169d");
}
/* Check that we can read in a KDM and then write it back out again the same */
BOOST_AUTO_TEST_CASE (kdm_passthrough_test)
{
- dcp::xml::DCinemaSecurityMessage kdm (
+ dcp::EncryptedKDM kdm (
"test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml"
);
- shared_ptr<xmlpp::Document> doc = kdm.as_xml ();
- doc->write_to_file_formatted ("build/kdm.xml", "UTF-8");
+ shared_ptr<xmlpp::DomParser> parser (new xmlpp::DomParser ());
+ stringstream s;
+ s << kdm.as_xml ();
+ parser->parse_stream (s);
+ parser->get_document()->write_to_file_formatted ("build/kdm.xml", "UTF-8");
int const r = system (
"xmldiff -c test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml build/kdm.xml"
);
--- /dev/null
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program 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,
+ 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.
+
+*/
+
+#include <boost/test/unit_test.hpp>
+#include "local_time.h"
+#include "exceptions.h"
+
+/** Check that dcp::LocalTime works */
+BOOST_AUTO_TEST_CASE (local_time_test)
+{
+ /* Badly-formatted times */
+ BOOST_CHECK_THROW (dcp::LocalTime (""), dcp::TimeFormatError);
+ BOOST_CHECK_THROW (dcp::LocalTime ("XXX"), dcp::TimeFormatError);
+ BOOST_CHECK_THROW (dcp::LocalTime ("2013-01-05T18:06:59+04:0"), dcp::TimeFormatError);
+ BOOST_CHECK_THROW (dcp::LocalTime ("2013-01-05T18:06:59X04:00"), dcp::TimeFormatError);
+ BOOST_CHECK_THROW (dcp::LocalTime ("2013-01-05T18-06:59+04:00"), dcp::TimeFormatError);
+ BOOST_CHECK_THROW (dcp::LocalTime ("2013!01-05T18:06:59+04:00"), dcp::TimeFormatError);
+
+ /* Correctly-formatted */
+
+ {
+ dcp::LocalTime t ("2013-01-05T18:06:59+04:00");
+ BOOST_CHECK_EQUAL (t._year, 2013);
+ BOOST_CHECK_EQUAL (t._month, 1);
+ BOOST_CHECK_EQUAL (t._day, 5);
+ BOOST_CHECK_EQUAL (t._hour, 18);
+ BOOST_CHECK_EQUAL (t._minute, 6);
+ BOOST_CHECK_EQUAL (t._second, 59);
+ BOOST_CHECK_EQUAL (t._tz_hour, 4);
+ BOOST_CHECK_EQUAL (t._tz_minute, 0);
+ BOOST_CHECK_EQUAL (t.as_string(), "2013-01-05T18:06:59+04:00");
+ }
+
+ {
+ dcp::LocalTime t ("2011-11-20T01:06:59-09:30");
+ BOOST_CHECK_EQUAL (t._year, 2011);
+ BOOST_CHECK_EQUAL (t._month, 11);
+ BOOST_CHECK_EQUAL (t._day, 20);
+ BOOST_CHECK_EQUAL (t._hour, 1);
+ BOOST_CHECK_EQUAL (t._minute, 6);
+ BOOST_CHECK_EQUAL (t._second, 59);
+ BOOST_CHECK_EQUAL (t._tz_hour, -9);
+ BOOST_CHECK_EQUAL (t._tz_minute, 30);
+ BOOST_CHECK_EQUAL (t.as_string(), "2011-11-20T01:06:59-09:30");
+ }
+}
+
*/
#include "certificates.h"
-#include "kdm.h"
+#include "decrypted_kdm.h"
+#include "encrypted_kdm.h"
#include "signer.h"
#include "mono_picture_mxf.h"
#include "sound_mxf.h"
cpl->add (reel);
/* A KDM using our certificate chain's leaf key pair */
- dcp::KDM kdm_A (
+ dcp::DecryptedKDM kdm_A (
cpl,
- signer,
- signer->certificates().leaf(),
- boost::posix_time::time_from_string ("2013-01-01 00:00:00"),
- boost::posix_time::time_from_string ("2013-01-08 00:00:00"),
+ dcp::LocalTime ("2013-01-01T00:00:00+00:00"),
+ dcp::LocalTime ("2013-01-08T00:00:00+00:00"),
"libdcp",
+ "test",
"2012-07-17T04:45:18+00:00"
);
boost::filesystem::path const kdm_file = work_dir / "kdm.xml";
- kdm_A.as_xml (kdm_file);
+ kdm_A.encrypt(signer, signer->certificates().leaf()).as_xml (kdm_file);
/* Reload the KDM, using our private key to decrypt it */
- dcp::KDM kdm_B (kdm_file, "build/test/signer/leaf.key");
+ dcp::DecryptedKDM kdm_B (dcp::EncryptedKDM (kdm_file), "build/test/signer/leaf.key");
/* Check that the decrypted KDMKeys are the same as the ones we started with */
BOOST_CHECK_EQUAL (kdm_A.keys().size(), kdm_B.keys().size());
- list<dcp::KDMKey> keys_A = kdm_A.keys ();
- list<dcp::KDMKey> keys_B = kdm_B.keys ();
- list<dcp::KDMKey>::const_iterator i = keys_A.begin();
- list<dcp::KDMKey>::const_iterator j = keys_B.begin();
+ list<dcp::DecryptedKDMKey> keys_A = kdm_A.keys ();
+ list<dcp::DecryptedKDMKey> keys_B = kdm_B.keys ();
+ list<dcp::DecryptedKDMKey>::const_iterator i = keys_A.begin();
+ list<dcp::DecryptedKDMKey>::const_iterator j = keys_B.begin();
while (i != keys_A.end ()) {
BOOST_CHECK (*i == *j);
++i;
+++ /dev/null
-/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
- This program 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,
- 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.
-
-*/
-
-#include <boost/test/unit_test.hpp>
-#include "metadata.h"
-#include "util.h"
-
-/** Test dcp::utc_offset_to_string */
-BOOST_AUTO_TEST_CASE (utc_offset_to_string_test)
-{
- BOOST_CHECK_EQUAL (dcp::utc_offset_to_string (30), "+00:30");
- BOOST_CHECK_EQUAL (dcp::utc_offset_to_string (60), "+01:00");
- BOOST_CHECK_EQUAL (dcp::utc_offset_to_string (61), "+01:01");
- BOOST_CHECK_EQUAL (dcp::utc_offset_to_string (7 * 60), "+07:00");
- BOOST_CHECK_EQUAL (dcp::utc_offset_to_string (-11 * 60), "-11:00");
-}
decryption_test.cc
encryption_test.cc
frame_info_test.cc
- kdm_key_test.cc
+ local_time_test.cc
kdm_test.cc
read_dcp_test.cc
recovery_test.cc
round_trip_test.cc
subtitle_tests.cc
test.cc
- utc_offset_to_string_test.cc
util_test.cc
"""
obj.target = 'tests'