Considerable re-work of KDM class to express the difference between encrypted and...
authorCarl Hetherington <cth@carlh.net>
Wed, 19 Mar 2014 21:46:01 +0000 (21:46 +0000)
committerCarl Hetherington <cth@carlh.net>
Wed, 19 Mar 2014 21:46:01 +0000 (21:46 +0000)
35 files changed:
examples/make_dcp.cc
src/cpl.cc
src/cpl.h
src/dcp.cc
src/dcp.h
src/decrypted_kdm.cc [new file with mode: 0644]
src/decrypted_kdm.h [new file with mode: 0644]
src/decrypted_kdm_key.cc [new file with mode: 0644]
src/decrypted_kdm_key.h [new file with mode: 0644]
src/encrypted_kdm.cc [new file with mode: 0644]
src/encrypted_kdm.h [new file with mode: 0644]
src/exceptions.cc
src/exceptions.h
src/file.cc
src/kdm.cc [deleted file]
src/kdm.h [deleted file]
src/local_time.cc [new file with mode: 0644]
src/local_time.h [new file with mode: 0644]
src/metadata.cc
src/mxf.cc
src/reel.cc
src/reel.h
src/reel_picture_asset.cc
src/rgb_xyz.cc
src/util.cc
src/util.h
src/wscript
test/decryption_test.cc
test/encryption_test.cc
test/kdm_key_test.cc [deleted file]
test/kdm_test.cc
test/local_time_test.cc [new file with mode: 0644]
test/round_trip_test.cc
test/utc_offset_to_string_test.cc [deleted file]
test/wscript

index d86cd30a3b5be016adfd1ddc74b22d6002f2394d..910fb8cb7263293046cb5d44cd98ecf153c2c0bd 100644 (file)
@@ -41,6 +41,7 @@
 #include "file.h"
 #include "reel_mono_picture_asset.h"
 #include "reel_sound_asset.h"
+#include <cmath>
 
 int
 main ()
index 9eae09ad51d456168af3d37f18f2a8f3b9b4a4ae..612ff4796a340cbf79779b367f8d3803aeb66f4a 100644 (file)
@@ -32,6 +32,7 @@
 #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;
@@ -55,9 +56,7 @@ CPL::CPL (string annotation_text, ContentKind content_kind)
        /* 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;
 }
 
@@ -226,7 +225,7 @@ CPL::encrypted () const
  *  @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);
index 8458a028c022be17ab2e663b922b1afcfee53b93..3bff4c4eabcd9677974bc191c389ac9f4766b571 100644 (file)
--- a/src/cpl.h
+++ b/src/cpl.h
@@ -28,7 +28,6 @@
 #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>
@@ -40,7 +39,7 @@ class Reel;
 class XMLMetadata;
 class MXFMetadata;
 class Signer;
-class KDM;
+class DecryptedKDM;
        
 /** @class CPL
  *  @brief A Composition Playlist.
@@ -58,7 +57,7 @@ public:
                ) const;
 
        void add (boost::shared_ptr<Reel> reel);
-       void add (KDM const &);
+       void add (DecryptedKDM const &);
 
        /** @return contents of the &lt;AnnotationText&gt; node */
        std::string annotation_text () const {
index 88e365062f305d3d542eb1b9ffe3970f3c7f990d..817cf36e14c24f80c7b6317c5f39247752a8894c 100644 (file)
 #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>
@@ -194,13 +195,13 @@ DCP::encrypted () const
 }
 
 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);
                        }                               
index d6275037c1582ce4719639b98ae4b4237c87c72f..450a3c00a4b72db6d39a8f6a0c62bd15c8fc0c88 100644 (file)
--- a/src/dcp.h
+++ b/src/dcp.h
@@ -46,7 +46,7 @@ class Reel;
 class CPL;
 class XMLMetadata;
 class Signer;
-class KDM;
+class DecryptedKDM;
 class Asset;
 
 namespace parse {
@@ -89,7 +89,7 @@ public:
 
        bool encrypted () const;
 
-       void add (KDM const &);
+       void add (DecryptedKDM const &);
 
        void write_xml (
                Standard standard,
diff --git a/src/decrypted_kdm.cc b/src/decrypted_kdm.cc
new file mode 100644 (file)
index 0000000..7e9e146
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+    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
+               );
+}
diff --git a/src/decrypted_kdm.h b/src/decrypted_kdm.h
new file mode 100644 (file)
index 0000000..0bed341
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+    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;
+};
+
+}
diff --git a/src/decrypted_kdm_key.cc b/src/decrypted_kdm_key.cc
new file mode 100644 (file)
index 0000000..aac1253
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+    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();
+}
diff --git a/src/decrypted_kdm_key.h b/src/decrypted_kdm_key.h
new file mode 100644 (file)
index 0000000..58448b7
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+    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
diff --git a/src/encrypted_kdm.cc b/src/encrypted_kdm.cc
new file mode 100644 (file)
index 0000000..5330490
--- /dev/null
@@ -0,0 +1,591 @@
+/*
+    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;
+}
diff --git a/src/encrypted_kdm.h b/src/encrypted_kdm.h
new file mode 100644 (file)
index 0000000..8d13a25
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+    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;
+};
+
+}
index 7cfcd6991d6b3c60ba50eec9491c18650d0c451a..48da4fcd34077aafc00b8ef2ba45f64b0331df91 100644 (file)
@@ -41,3 +41,10 @@ UnresolvedRefError::UnresolvedRefError (string id)
 
 }
 
+TimeFormatError::TimeFormatError (string bad_time)
+       : _message (String::compose ("Bad time string %1", bad_time))
+{
+
+}
+
+                           
index bc5e83d17e3855326799650ef31037aa3ea34715..7446e352522fefaee4bdd2a4f7de860b2b3696f2 100644 (file)
@@ -146,7 +146,25 @@ public:
 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
index aabda166fe84934c80645312a4aea400f754d7ab..f4a91add29be8441c5f3e9ab2fc11ac916b9aa2c 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "file.h"
 #include "util.h"
+#include <stdio.h>
 
 using namespace dcp;
 
diff --git a/src/kdm.cc b/src/kdm.cc
deleted file mode 100644 (file)
index 80521b8..0000000
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
-    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
-               );
-}
diff --git a/src/kdm.h b/src/kdm.h
deleted file mode 100644 (file)
index 2a340c3..0000000
--- a/src/kdm.h
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
-    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 &lt;AnnotationText&gt; node.
-        *  @param issue_date Text for the &lt;IssueDate&gt; 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
diff --git a/src/local_time.cc b/src/local_time.cc
new file mode 100644 (file)
index 0000000..665d5d4
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+    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;
+}
diff --git a/src/local_time.h b/src/local_time.h
new file mode 100644 (file)
index 0000000..9c634fe
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+    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
index e33826139d82a1d42886ce3b4d4e58446de35127..e2e19f3c014b05955a2014107fdf75c566dbacf2 100644 (file)
@@ -23,9 +23,7 @@
 
 #include "metadata.h"
 #include "util.h"
-#ifdef LIBDCP_WINDOWS
-#include <windows.h>
-#endif
+#include "local_time.h"
 #include <sstream>
 #include <iomanip>
 #include <time.h>
@@ -52,7 +50,5 @@ XMLMetadata::XMLMetadata ()
 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 ();
 }
index f8ca4fe5d3d0cb2690bc8e454dc3e3a4f3d4b485..3f8ab5d38c5a2d6e403da712cb957579d6ca52c1 100644 (file)
@@ -28,7 +28,6 @@
 #include "util.h"
 #include "metadata.h"
 #include "exceptions.h"
-#include "kdm.h"
 #include "compose.hpp"
 #include <libxml++/nodes/element.h>
 #include <boost/filesystem.hpp>
@@ -38,7 +37,6 @@ using std::string;
 using std::list;
 using std::pair;
 using boost::shared_ptr;
-using boost::lexical_cast;
 using boost::dynamic_pointer_cast;
 using namespace dcp;
 
index 4bdbcb5fc432633d88b55cb21432c65fe7558fc4..34b520b49365b08db4047ff46dc686f167f5545f 100644 (file)
@@ -28,7 +28,8 @@
 #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;
@@ -133,15 +134,15 @@ Reel::encrypted () const
 }
 
 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 ());
                }
        }
index 14def9c80593fc1f03f9218882b35ab55df42352..5bb19eb01020f18ba158d78ca257804d89850a87 100644 (file)
@@ -34,7 +34,7 @@ namespace cxml {
 
 namespace dcp {
 
-class KDM;
+class DecryptedKDM;
 class ReelAsset;
 class ReelPictureAsset;
 class ReelSoundAsset;
@@ -81,7 +81,7 @@ public:
        
        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> >);
 
index d1910bb3d2028e8304fdd7d513b8472c1fa4e6e2..1a3d47b99e40240c2d79947e3ad703679c1c011e 100644 (file)
@@ -26,6 +26,7 @@
 #include "picture_mxf.h"
 #include "compose.hpp"
 #include <libcxml/cxml.h>
+#include <iomanip>
 
 using std::bad_cast;
 using std::string;
index 92997441f14beb7480d2cb5db9fc57bd7eeb19cc..72ee38c1c5802a91d0ea5a5d3aae392c78779d7b 100644 (file)
@@ -23,6 +23,7 @@
 #include "gamma_lut.h"
 #include "image.h"
 #include "colour_matrix.h"
+#include <cmath>
 
 using std::min;
 using std::max;
index 1e32fbc9f6ce36ea95eaaa36b0751bc373a0c667..80c3756b00ff5e16f4a58ac8fff13a8b662ba470 100644 (file)
@@ -314,66 +314,6 @@ dcp::base64_decode (string const & in, unsigned char* out, int out_length)
        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.
index ca00ecb9147a1a232760426636d9cc16b35e667b..d9f69b957f1dbf453bd308bace0e04afd4a5cb02 100644 (file)
@@ -27,7 +27,6 @@
 #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>
@@ -87,9 +86,6 @@ extern void add_signer (xmlpp::Element* parent, CertificateChain const & certifi
 
 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>
index c06ca1bd4795d34fae0762910a6717d40e7ec4a1..a54a40d5fcbe2e1706899f264c1e4a4cc3de1a84 100644 (file)
@@ -20,14 +20,17 @@ def build(bld):
                  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
@@ -71,10 +74,11 @@ def build(bld):
               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
index 8dfeffa658d36a3e498345cb01c3cb65454e1bb2..f80bb900b18907cecc07b7bf8e348b7dbbc3525e 100644 (file)
 */
 
 #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"
@@ -58,11 +59,11 @@ BOOST_AUTO_TEST_CASE (decryption_test)
        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);
@@ -78,8 +79,8 @@ BOOST_AUTO_TEST_CASE (decryption_test)
 /** 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"
                );
 }
index 8fe34ddda15b77e62aa4dab6734fc72eb16b8070..a4bc0600beced0d66b2a55dbf1a935f7c75dfa43 100644 (file)
@@ -17,7 +17,6 @@
 
 */
 
-#include "kdm.h"
 #include "KM_util.h"
 #include "metadata.h"
 #include "certificates.h"
@@ -35,6 +34,8 @@
 #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>
@@ -123,17 +124,16 @@ BOOST_AUTO_TEST_CASE (encryption_test)
        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 "
diff --git a/test/kdm_key_test.cc b/test/kdm_key_test.cc
deleted file mode 100644 (file)
index 4a84fe3..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
-    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;
-}
index 5fff4fde74dc39cea984dde16fef84f12e0661cd..7de62f5a2aac9290b68ae96ed0ea394c99dd4f15 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    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"
                );
diff --git a/test/local_time_test.cc b/test/local_time_test.cc
new file mode 100644 (file)
index 0000000..b7f49a3
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+    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");
+       }       
+}
+
index e94d8c728ef865c5c8db12c19f2ba91f4f3800eb..ef1f1f412018e911bc8d4cffe9ded78ecb82a65c 100644 (file)
@@ -18,7 +18,8 @@
 */
 
 #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"
@@ -78,29 +79,28 @@ BOOST_AUTO_TEST_CASE (round_trip_test)
        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;
diff --git a/test/utc_offset_to_string_test.cc b/test/utc_offset_to_string_test.cc
deleted file mode 100644 (file)
index 3b4e0b4..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
-    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");
-}
index 407765afff3eab742ee7fe27dbdb19fa627286ba..7e7122b178481d18fade0725bca64eb244409725 100644 (file)
@@ -29,14 +29,13 @@ def build(bld):
                  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'