Various encryption-related stuff.
authorCarl Hetherington <cth@carlh.net>
Thu, 19 Sep 2013 19:44:42 +0000 (20:44 +0100)
committerCarl Hetherington <cth@carlh.net>
Thu, 19 Sep 2013 19:44:42 +0000 (20:44 +0100)
23 files changed:
examples/make_dcp.cc
src/certificates.cc
src/certificates.h
src/cpl.cc
src/dcp.cc
src/kdm.cc
src/kdm.h
src/key.cc [new file with mode: 0644]
src/key.h [new file with mode: 0644]
src/mxf_asset.cc
src/mxf_asset.h
src/picture_asset.cc
src/picture_asset.h
src/reel.cc
src/sound_asset.cc
src/sound_asset.h
src/wscript
test/certificates_test.cc
test/dcp_test.cc
test/encryption_test.cc
test/error_test.cc
test/kdm_test.cc
test/recovery_test.cc

index 6da9cf20f8499174f0eb8c5af499822ba503c419..9222caaa07fc469c8b76a804dd5b058885ea6c91 100644 (file)
@@ -75,7 +75,7 @@ main ()
           for 2K projectors.
        */
        boost::shared_ptr<libdcp::MonoPictureAsset> picture_asset (
-               new libdcp::MonoPictureAsset (video_frame, "My Film DCP", "video.mxf", 0, 24, 48, false, libdcp::Size (1998, 1080), false)
+               new libdcp::MonoPictureAsset (video_frame, "My Film DCP", "video.mxf", 0, 24, 48, libdcp::Size (1998, 1080), false)
                );
 
        /* Now we will create a `sound asset', which is made up of a WAV file for each channel of audio.  Here we're using
@@ -95,7 +95,7 @@ main ()
 
        /* Now we can create the sound asset using these files */
        boost::shared_ptr<libdcp::SoundAsset> sound_asset (
-               new libdcp::SoundAsset (sound_files, "My Film DCP", "audio.mxf", 0, 24, 48, false, false)
+               new libdcp::SoundAsset (sound_files, "My Film DCP", "audio.mxf", 0, 24, 48, false)
                );
 
        /* Now that we have the assets, we can create a Reel to put them in and add it to the CPL */
index 3566fba01e4ee2a5a4c7473f26eaba5812aa36fb..6d9c449dab371f2995a46ca2a64d23b5c59a6a21 100644 (file)
@@ -43,7 +43,7 @@ Certificate::Certificate (X509* c)
        
 }
 
-Certificate::Certificate (string const & filename)
+Certificate::Certificate (boost::filesystem::path filename)
        : _certificate (0)
 {
        FILE* f = fopen (filename.c_str(), "r");
@@ -56,13 +56,52 @@ Certificate::Certificate (string const & filename)
        }
 }
 
+Certificate::Certificate (string cert)
+{
+       read_string (cert);
+}
+
+Certificate::Certificate (Certificate const & other)
+{
+       read_string (other.certificate (true));
+}
+
+void
+Certificate::read_string (string cert)
+{
+       BIO* bio = BIO_new_mem_buf (const_cast<char *> (cert.c_str ()), -1);
+       if (!bio) {
+               throw MiscError ("could not create memory BIO");
+       }
+
+       _certificate = PEM_read_bio_X509 (bio, 0, 0, 0);
+       if (!_certificate) {
+               throw MiscError ("could not read X509 certificate from memory BIO");
+       }
+
+       BIO_free (bio);
+}
+
 Certificate::~Certificate ()
 {
        X509_free (_certificate);
 }
 
+Certificate &
+Certificate::operator= (Certificate const & other)
+{
+       if (this == &other) {
+               return *this;
+       }
+       
+       X509_free (_certificate);
+       read_string (other.certificate ());
+
+       return *this;
+}
+
 string
-Certificate::certificate () const
+Certificate::certificate (bool with_begin_end) const
 {
        assert (_certificate);
        
@@ -82,8 +121,11 @@ Certificate::certificate () const
 
        BIO_free (bio);
 
-       boost::replace_all (s, "-----BEGIN CERTIFICATE-----\n", "");
-       boost::replace_all (s, "\n-----END CERTIFICATE-----\n", "");
+       if (!with_begin_end) {
+               boost::replace_all (s, "-----BEGIN CERTIFICATE-----\n", "");
+               boost::replace_all (s, "\n-----END CERTIFICATE-----\n", "");
+       }
+       
        return s;
 }
 
index 9caea5a786cd8e8a15e1acde2c8c48922c27079d..ef2e63b411b0f0c9d939fb48b0ec6d803b4a423d 100644 (file)
@@ -24,6 +24,7 @@
 #include <list>
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
+#include <boost/filesystem.hpp>
 #undef X509_NAME
 #include <openssl/x509.h>
 
@@ -35,18 +36,25 @@ namespace xmlpp {
 
 namespace libdcp {
 
-class Certificate : public boost::noncopyable
+class Certificate
 {
 public:
        Certificate ()
                : _certificate (0)
        {}
 
-       Certificate (std::string const &);
+       Certificate (boost::filesystem::path);
+       Certificate (std::string);
        Certificate (X509 *);
+       Certificate (Certificate const &);
        ~Certificate ();
 
-       std::string certificate () const;
+       Certificate& operator= (Certificate const &);
+
+       /** @param with_begin_end true to include BEGIN CERTIFICATE / END CERTIFICATE markers
+        *  @return the whole certificate as a string.
+        */
+       std::string certificate (bool with_begin_end = false) const;
        std::string issuer () const;
        std::string serial () const;
        std::string subject () const;
@@ -54,6 +62,8 @@ public:
        std::string thumbprint () const;
 
 private:
+       void read_string (std::string);
+       
        static std::string name_for_xml (X509_NAME *);
        static std::string asn_to_utf8 (ASN1_STRING *);
        static std::string get_name_part (X509_NAME *, int);
index 0791353a721d9e9c778864a8ffad975b76d0bb31..2c0bf6c4d1f5841290983a309894fc93a2031eb0 100644 (file)
@@ -444,21 +444,29 @@ CPL::make_kdm (
                xmlpp::Element* authenticated_private = root->add_child("AuthenticatedPrivate");
                authenticated_private->set_attribute ("Id", "ID_AuthenticatedPrivate");
                xmlAddID (0, doc->cobj(), (const xmlChar *) "ID_AuthenticatedPrivate", authenticated_private->get_attribute("Id")->cobj());
-               {
-                       xmlpp::Element* encrypted_key = authenticated_private->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");
-                               encryption_method->add_child("DigestMethod", "ds")->set_attribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
-                       }
 
+               /* Hex keys that we have already written into the node */
+               list<Key> written_keys;
+
+               list<shared_ptr<const Asset> > a = assets();
+               for (list<shared_ptr<const Asset> >::iterator i = a.begin(); i != a.end(); ++i) {
+                       /* XXX: non-MXF assets? */
+                       shared_ptr<const MXFAsset> mxf = boost::dynamic_pointer_cast<const MXFAsset> (*i);
+                       if (!mxf || find (written_keys.begin(), written_keys.end(), mxf->key ()) != written_keys.end ()) {
+                               continue;
+                       }
+                       
+                       xmlpp::Element* encrypted_key = authenticated_private->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");
+                       encryption_method->add_child("DigestMethod", "ds")->set_attribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
                        xmlpp::Element* cipher_data = authenticated_private->add_child ("CipherData", "enc");
-                       cipher_data->add_child("CipherValue", "enc")->add_child_text("XXX");
+                       cipher_data->add_child("CipherValue", "enc")->add_child_text(mxf->key()->hex());
+
+                       written_keys.push_back (mxf->key().get());
                }
        }
        
-       /* XXX: x2 one for each mxf? */
-
        {
                xmlpp::Element* signature = root->add_child("Signature", "ds");
                
index b5da1c2fb68089f76dc9aac5910b568dfe3a5339..d21b3b4ec1ed6104c43c8745495ca489793fd493 100644 (file)
@@ -349,10 +349,10 @@ DCP::encrypted () const
 void
 DCP::add_kdm (KDM const & kdm)
 {
-       list<KDMCipher> ciphers = kdm.ciphers ();
+       list<KDMKey> keys = kdm.keys ();
        
        for (list<shared_ptr<CPL> >::iterator i = _cpls.begin(); i != _cpls.end(); ++i) {
-               for (list<KDMCipher>::iterator j = ciphers.begin(); j != ciphers.end(); ++j) {
+               for (list<KDMKey>::iterator j = keys.begin(); j != keys.end(); ++j) {
                        if (j->cpl_id() == (*i)->id()) {
                                (*i)->add_kdm (kdm);
                        }                               
index 09935ca3c022308cac26fcdb2d54c7b71074b89c..bbfd3832ab86cdf9b48cb332e196e27818cb6c5f 100644 (file)
@@ -80,7 +80,7 @@ KDM::KDM (boost::filesystem::path kdm, boost::filesystem::path private_key)
                        throw MiscError (String::compose ("Could not decrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
                }
 
-               _ciphers.push_back (KDMCipher (decrypted, decrypted_len));
+               _keys.push_back (KDMKey (decrypted, decrypted_len));
                delete[] decrypted;
        }
 
@@ -88,7 +88,7 @@ KDM::KDM (boost::filesystem::path kdm, boost::filesystem::path private_key)
 }
 
 
-KDMCipher::KDMCipher (unsigned char const * raw, int len)
+KDMKey::KDMKey (unsigned char const * raw, int len)
 {
        switch (len) {
        case 134:
@@ -99,8 +99,7 @@ KDMCipher::KDMCipher (unsigned char const * raw, int len)
                _key_id = get_uuid (&raw, 16);
                _not_valid_before = get (&raw, 25);
                _not_valid_after = get (&raw, 25);
-               memcpy (_key_raw, raw, 16);
-               _key_string = get_hex (&raw, 16);
+               _key = Key (raw);
                break;
        case 138:
                /* SMPTE */
@@ -111,8 +110,7 @@ KDMCipher::KDMCipher (unsigned char const * raw, int len)
                _key_id = get_uuid (&raw, 16);
                _not_valid_before = get (&raw, 25);
                _not_valid_after = get (&raw, 25);
-               memcpy (_key_raw, raw, 16);
-               _key_string = get_hex (&raw, 16);
+               _key = Key (raw);
                break;
        default:
                assert (false);
@@ -120,7 +118,7 @@ KDMCipher::KDMCipher (unsigned char const * raw, int len)
 }
 
 string
-KDMCipher::get (unsigned char const ** p, int N) const
+KDMKey::get (unsigned char const ** p, int N) const
 {
        string g;
        for (int i = 0; i < N; ++i) {
@@ -132,7 +130,7 @@ KDMCipher::get (unsigned char const ** p, int N) const
 }
 
 string
-KDMCipher::get_uuid (unsigned char const ** p, int N) const
+KDMKey::get_uuid (unsigned char const ** p, int N) const
 {
        stringstream g;
        
@@ -146,16 +144,3 @@ KDMCipher::get_uuid (unsigned char const ** p, int N) const
 
        return g.str ();
 }
-
-string
-KDMCipher::get_hex (unsigned char const ** p, int N) const
-{
-       stringstream g;
-       
-       for (int i = 0; i < N; ++i) {
-               g << setw(2) << setfill('0') << hex << static_cast<int> (**p);
-               (*p)++;
-       }
-
-       return g.str ();
-}
index 06f15e33b08cc33b564d2324ecaaf9e362dbead8..c012c101f810d64410988f5e9991192186d7d665 100644 (file)
--- a/src/kdm.h
+++ b/src/kdm.h
 #define LIBDCP_KDM_H
 
 #include <boost/filesystem.hpp>
+#include "key.h"
 
 namespace libdcp {
 
-/** A single cipher for encrypting or decrypting an MXF.  One or more of these
+/** A single key for encrypting or decrypting an MXF.  One or more of these
  *  are delivered in a KDM.
  */
-class KDMCipher
+class KDMKey
 {
 public:
-       KDMCipher (unsigned char const *, int);
+       KDMKey (unsigned char const *, int);
 
        std::string structure_id () const {
                return _structure_id;
@@ -47,11 +48,11 @@ public:
        std::string key_type () const {
                return _key_type;
        }
-       
+
        std::string key_id () const {
                return _key_id;
        }
-
+       
        std::string not_valid_before () const {
                return _not_valid_before;
        }
@@ -60,30 +61,22 @@ public:
                return _not_valid_after;
        }
 
-       /** The key as a hex string */
-       std::string key_string () const {
-               return _key_string;
-       }
-
-       /** The key as 16 raw bytes */
-       unsigned char const * key_raw () const {
-               return _key_raw;
+       Key key () const {
+               return _key;
        }
        
 private:
        std::string get (unsigned char const **, int) const;
        std::string get_uuid (unsigned char const **, int) const;
-       std::string get_hex (unsigned char const **, int) const;
        
        std::string _structure_id;
        std::string _signer_thumbprint;
        std::string _cpl_id;
-       std::string _key_type;
-       std::string _key_id;
        std::string _not_valid_before;
        std::string _not_valid_after;
-       std::string _key_string;
-       unsigned char _key_raw[16];
+       std::string _key_type;
+       std::string _key_id;
+       Key _key;
 };
 
 class KDM
@@ -91,12 +84,12 @@ class KDM
 public:
        KDM (boost::filesystem::path, boost::filesystem::path);
 
-       std::list<KDMCipher> ciphers () const {
-               return _ciphers;
+       std::list<KDMKey> keys () const {
+               return _keys;
        }
 
 private:
-       std::list<KDMCipher> _ciphers;
+       std::list<KDMKey> _keys;
 };
 
 
diff --git a/src/key.cc b/src/key.cc
new file mode 100644 (file)
index 0000000..8cac04a
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+    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 <sstream>
+#include <string>
+#include <iomanip>
+#include "AS_DCP.h"
+#include "KM_prng.h"
+#include "KM_util.h"
+#include "key.h"
+
+using std::string;
+using std::stringstream;
+using std::setw;
+using std::setfill;
+using namespace libdcp;
+
+Key::Key ()
+       : _value (new uint8_t[ASDCP::KeyLen])
+{
+       Kumu::FortunaRNG rng;
+       rng.FillRandom (_value, ASDCP::KeyLen);
+}
+
+Key::Key (uint8_t const * value)
+       : _value (new uint8_t[ASDCP::KeyLen])
+{
+       memcpy (_value, value, ASDCP::KeyLen);
+}
+
+Key::Key (string value)
+       : _value (new uint8_t[ASDCP::KeyLen])
+{
+       unsigned int length;
+       Kumu::hex2bin (value.c_str(), _value, ASDCP::KeyLen, &length);
+}
+
+Key::Key (Key const & other)
+       : _value (new uint8_t[ASDCP::KeyLen])
+{
+       memcpy (_value, other._value, ASDCP::KeyLen);
+}
+
+Key::~Key ()
+{
+       delete[] _value;
+}
+
+Key &
+Key::operator= (Key const & other)
+{
+       if (this == &other) {
+               return *this;
+       }
+       
+       memcpy (_value, other._value, ASDCP::KeyLen);
+       return *this;
+}
+
+string
+Key::hex () const
+{
+       stringstream g;
+       
+       for (unsigned int i = 0; i < ASDCP::KeyLen; ++i) {
+               g << setw(2) << setfill('0') << std::hex << static_cast<int> (_value[i]);
+       }
+
+       return g.str ();
+}
+
+bool
+libdcp::operator== (Key const & a, Key const & b)
+{
+       return memcmp (a.value(), b.value(), ASDCP::KeyLen) == 0;
+}
+
+bool
+libdcp::operator!= (Key const & a, Key const & b)
+{
+       return !(a == b);
+}
+
diff --git a/src/key.h b/src/key.h
new file mode 100644 (file)
index 0000000..fbadfd3
--- /dev/null
+++ b/src/key.h
@@ -0,0 +1,61 @@
+/*
+    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.
+
+*/
+
+#ifndef LIBDCP_KEY_H
+#define LIBDCP_KEY_H
+
+#include <stdint.h>
+
+namespace libdcp {
+
+/** A key for encrypting MXFs */
+class Key
+{
+public:
+       /** Create a new, random key */
+       Key ();
+
+       /** Create a Key from a raw key value */
+       Key (uint8_t const *);
+
+       /** Create a Key from a hex key value */
+       Key (std::string);
+
+       Key (Key const &);
+       ~Key ();
+
+       Key& operator= (Key const &);
+
+       uint8_t* value () const {
+               return _value;
+       }
+
+       std::string hex () const;
+
+private:
+       /** Raw key value */
+       uint8_t* _value;
+};
+
+extern bool operator== (Key const & a, Key const & b);
+extern bool operator!= (Key const & a, Key const & b);
+
+}
+
+#endif
index 54c204e7ec67ffa9bcd9af88c427fb77b7c8b95e..bcfe60854500fe731c2f67acac54e23f543bc37b 100644 (file)
@@ -46,46 +46,25 @@ using namespace libdcp;
 MXFAsset::MXFAsset (string directory, string file_name)
        : Asset (directory, file_name)
        , _progress (0)
-       , _encrypted (false)
        , _encryption_context (0)
        , _decryption_context (0)
 {
 
 }
 
-MXFAsset::MXFAsset (string directory, string file_name, boost::signals2::signal<void (float)>* progress, int edit_rate, int intrinsic_duration, bool encrypted)
+MXFAsset::MXFAsset (string directory, string file_name, boost::signals2::signal<void (float)>* progress, int edit_rate, int intrinsic_duration
        : Asset (directory, file_name, edit_rate, intrinsic_duration)
        , _progress (progress)
-       , _encrypted (encrypted)
        , _encryption_context (0)
        , _decryption_context (0)
 {
-       if (_encrypted) {
-               /* Generate an encryption key and a UUID for it */
-               _key_id = make_uuid ();
-               uint8_t key_buffer[ASDCP::KeyLen];
-               Kumu::FortunaRNG rng;
-               rng.FillRandom (key_buffer, ASDCP::KeyLen);
-               char key_string[ASDCP::KeyLen * 4];
-               Kumu::bin2hex (key_buffer, ASDCP::KeyLen, key_string, ASDCP::KeyLen * 4);
-               _key_value = key_string;
-                       
-               _encryption_context = new ASDCP::AESEncContext;
-               if (ASDCP_FAILURE (_encryption_context->InitKey (key_buffer))) {
-                       throw MiscError ("could not set up encryption context");
-               }
-
-               uint8_t cbc_buffer[ASDCP::CBC_BLOCK_SIZE];
-               
-               if (ASDCP_FAILURE (_encryption_context->SetIVec (rng.FillRandom (cbc_buffer, ASDCP::CBC_BLOCK_SIZE)))) {
-                       throw MiscError ("could not set up CBC initialization vector");
-               }
-       }
+       
 }
 
 MXFAsset::~MXFAsset ()
 {
        delete _encryption_context;
+       delete _decryption_context;
 }
 
 void
@@ -104,7 +83,7 @@ MXFAsset::fill_writer_info (ASDCP::WriterInfo* writer_info, string uuid, bool in
        Kumu::hex2bin (uuid.c_str(), writer_info->AssetUUID, Kumu::UUID_Length, &c);
        assert (c == Kumu::UUID_Length);
 
-       if (_encrypted) {
+       if (_key) {
                Kumu::GenRandomUUID (writer_info->ContextID);
                writer_info->EncryptedEssence = true;
 
@@ -159,16 +138,35 @@ MXFAsset::write_to_cpl (xmlpp::Element* node, bool interop) const
        a->add_child ("IntrinsicDuration")->add_child_text (lexical_cast<string> (_intrinsic_duration));
        a->add_child ("EntryPoint")->add_child_text (lexical_cast<string> (_entry_point));
        a->add_child ("Duration")->add_child_text (lexical_cast<string> (_duration));
-       if (_encrypted) {
+       if (!_key_id.empty ()) {
                a->add_child("KeyId")->add_child_text ("urn:uuid:" + _key_id);
        }
 }
 
 void
-MXFAsset::set_kdm_cipher (KDMCipher cipher)
+MXFAsset::set_key (Key key)
 {
+       _key = key;
+
+       if (_key_id.empty ()) {
+               /* No key ID so far; we now need one */
+               _key_id = make_uuid ();
+       }
+       
        _decryption_context = new ASDCP::AESDecContext;
-       if (ASDCP_FAILURE (_decryption_context->InitKey (cipher.key_raw ()))) {
+       if (ASDCP_FAILURE (_decryption_context->InitKey (_key->value ()))) {
                throw MiscError ("could not set up decryption context");
        }
+
+       _encryption_context = new ASDCP::AESEncContext;
+       if (ASDCP_FAILURE (_encryption_context->InitKey (_key->value ()))) {
+               throw MiscError ("could not set up encryption context");
+       }
+       
+       uint8_t cbc_buffer[ASDCP::CBC_BLOCK_SIZE];
+       
+       Kumu::FortunaRNG rng;
+       if (ASDCP_FAILURE (_encryption_context->SetIVec (rng.FillRandom (cbc_buffer, ASDCP::CBC_BLOCK_SIZE)))) {
+               throw MiscError ("could not set up CBC initialization vector");
+       }
 }
index 3f6ecc59243d4b78cccb73bb1a0f8dbffbb8f02c..d681970eb24291281c22944753ead1c577127792 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <boost/signals2.hpp>
 #include "asset.h"
+#include "key.h"
 
 namespace ASDCP {
        class AESEncContext;
@@ -32,7 +33,6 @@ namespace libdcp
 {
 
 class MXFMetadata;     
-class KDMCipher;
 
 /** @brief Parent class for assets which have MXF files */     
 class MXFAsset : public Asset
@@ -55,7 +55,13 @@ public:
         *  @param edit_rate Edit rate in frames per second (usually equal to the video frame rate).
         *  @param intrinsic_duration Duration of the whole asset in frames.
         */
-       MXFAsset (std::string directory, std::string file_name, boost::signals2::signal<void (float)>* progress, int edit_rate, int intrinsic_duration, bool encrypted);
+       MXFAsset (
+               std::string directory,
+               std::string file_name,
+               boost::signals2::signal<void (float)>* progress,
+               int edit_rate,
+               int intrinsic_duration
+               );
 
        ~MXFAsset ();
 
@@ -72,19 +78,23 @@ public:
 
        void add_typed_key_id (xmlpp::Element *) const;
 
-       std::string key_id () const {
-               return _key_id;
+       bool encrypted () const {
+               return !_key_id.empty ();
        }
 
-       void set_key_id (std::string k) {
-               _key_id = k;
+       void set_key_id (std::string i) {
+               _key_id = i;
        }
 
-       bool encrypted () const {
-               return !_key_id.empty ();
+       std::string key_id () const {
+               return _key_id;
        }
+       
+       void set_key (Key);
 
-       void set_kdm_cipher (KDMCipher);
+       boost::optional<Key> key () const {
+               return _key;
+       }
        
 protected:
        virtual std::string key_type () const = 0;
@@ -95,13 +105,10 @@ protected:
        
        /** Signal to emit to report progress, or 0 */
        boost::signals2::signal<void (float)>* _progress;
-       bool _encrypted;
        ASDCP::AESEncContext* _encryption_context;
-       /** Key used to encrypt, or empty if _encryption is false */
-       std::string _key_value;
-       /** UUID of encryption key */
-       std::string _key_id;
        ASDCP::AESDecContext* _decryption_context;
+       std::string _key_id;
+       boost::optional<Key> _key;
 };
 
 }
index 1c48034844705677d50dc2630db3bfda60288784..5903b394304b0baf4fdd70ca2c6473f5b651cc1b 100644 (file)
@@ -54,8 +54,8 @@ using boost::dynamic_pointer_cast;
 using boost::lexical_cast;
 using namespace libdcp;
 
-PictureAsset::PictureAsset (string directory, string mxf_name, boost::signals2::signal<void (float)>* progress, int fps, int intrinsic_duration, bool encrypted, Size size)
-       : MXFAsset (directory, mxf_name, progress, fps, intrinsic_duration, encrypted)
+PictureAsset::PictureAsset (string directory, string mxf_name, boost::signals2::signal<void (float)>* progress, int fps, int intrinsic_duration, Size size)
+       : MXFAsset (directory, mxf_name, progress, fps, intrinsic_duration)
        , _size (size)
 {
 
@@ -195,12 +195,11 @@ MonoPictureAsset::MonoPictureAsset (
        boost::signals2::signal<void (float)>* progress,
        int fps,
        int intrinsic_duration,
-       bool encrypted,
        Size size,
        bool interop,
        MXFMetadata const & metadata
        )
-       : PictureAsset (directory, mxf_name, progress, fps, intrinsic_duration, encrypted, size)
+       : PictureAsset (directory, mxf_name, progress, fps, intrinsic_duration, size)
 {
        construct (get_path, interop, metadata);
 }
@@ -212,18 +211,17 @@ MonoPictureAsset::MonoPictureAsset (
        boost::signals2::signal<void (float)>* progress,
        int fps,
        int intrinsic_duration,
-       bool encrypted,
        Size size,
        bool interop,
        MXFMetadata const & metadata
        )
-       : PictureAsset (directory, mxf_name, progress, fps, intrinsic_duration, encrypted, size)
+       : PictureAsset (directory, mxf_name, progress, fps, intrinsic_duration, size)
 {
        construct (boost::bind (&MonoPictureAsset::path_from_list, this, _1, files), interop, metadata);
 }
 
-MonoPictureAsset::MonoPictureAsset (string directory, string mxf_name, int fps, Size size, bool encrypted)
-       : PictureAsset (directory, mxf_name, 0, fps, 0, encrypted, size)
+MonoPictureAsset::MonoPictureAsset (string directory, string mxf_name, int fps, Size size)
+       : PictureAsset (directory, mxf_name, 0, fps, 0, size)
 {
 
 }
@@ -437,7 +435,7 @@ PictureAsset::frame_buffer_equals (
 
 
 StereoPictureAsset::StereoPictureAsset (string directory, string mxf_name, int fps, int intrinsic_duration)
-       : PictureAsset (directory, mxf_name, 0, fps, intrinsic_duration, false, Size (0, 0))
+       : PictureAsset (directory, mxf_name, 0, fps, intrinsic_duration, Size (0, 0))
 {
        ASDCP::JP2K::MXFSReader reader;
        if (ASDCP_FAILURE (reader.OpenRead (path().string().c_str()))) {
@@ -472,8 +470,8 @@ PictureAsset::key_type () const
        return "MDIK";
 }
 
-StereoPictureAsset::StereoPictureAsset (string directory, string mxf_name, int fps, Size size, bool encrypted)
-       : PictureAsset (directory, mxf_name, 0, fps, 0, encrypted, size)
+StereoPictureAsset::StereoPictureAsset (string directory, string mxf_name, int fps, Size size)
+       : PictureAsset (directory, mxf_name, 0, fps, 0, size)
 {
 
 }
index 7fcc2efb666e4fb3f6981dfee20ead8b2afe653e..398f886840834e73e519a787e19ebe3cb28e683d 100644 (file)
@@ -64,7 +64,6 @@ public:
                boost::signals2::signal<void (float)>* progress,
                int fps,
                int intrinsic_duration,
-               bool encrypted,
                Size
                );
 
@@ -112,7 +111,6 @@ public:
         *  @param fps Video frames per second.
         *  @param intrinsic_duration Total number of frames in the asset.
         *  @param size Size of images in pixels.
-        *  @param encrypted true if asset should be encrypted.
         */
        MonoPictureAsset (
                std::vector<std::string> const & files,
@@ -121,7 +119,6 @@ public:
                boost::signals2::signal<void (float)>* progress,
                int fps,
                int intrinsic_duration,
-               bool encrypted,
                Size size,
                bool interop,
                MXFMetadata const & metadata = MXFMetadata ()
@@ -137,7 +134,6 @@ public:
         *  @param fps Video frames per second.
         *  @param intrinsic_duration Total number of frames in the asset.
         *  @param size Size of images in pixels.
-        *  @param encrypted true if asset should be encrypted.
         */
        MonoPictureAsset (
                boost::function<std::string (int)> get_path,
@@ -146,7 +142,6 @@ public:
                boost::signals2::signal<void (float)>* progress,
                int fps,
                int intrinsic_duration,
-               bool encrypted,
                Size size,
                bool interop,
                MXFMetadata const & metadata = MXFMetadata ()
@@ -166,7 +161,7 @@ public:
         *  @param fps Video frames per second.
         *  @param size Size in pixels that the picture frames will be.
         */
-       MonoPictureAsset (std::string directory, std::string mxf_name, int fps, Size size, bool encrypted);
+       MonoPictureAsset (std::string directory, std::string mxf_name, int fps, Size size);
 
        /** Start a progressive write to a MonoPictureAsset */
        boost::shared_ptr<PictureAssetWriter> start_write (bool, bool, MXFMetadata const & metadata = MXFMetadata ());
@@ -195,7 +190,7 @@ public:
         *  @param fps Video frames per second.
         *  @param size Size in pixels that the picture frames will be.
         */
-       StereoPictureAsset (std::string directory, std::string mxf_name, int fps, Size size, bool encrypted);
+       StereoPictureAsset (std::string directory, std::string mxf_name, int fps, Size size);
 
        /** Start a progressive write to a StereoPictureAsset */
        boost::shared_ptr<PictureAssetWriter> start_write (bool, bool, MXFMetadata const & metadata = MXFMetadata ());
index 43c934ea2f91ad7d28a8220eb1b494cf25b41380..ffb184ab67d0ebca5534822ce260337f0f010e7c 100644 (file)
@@ -93,14 +93,14 @@ Reel::encrypted () const
 void
 Reel::add_kdm (KDM const & kdm)
 {
-       list<KDMCipher> ciphers = kdm.ciphers ();
+       list<KDMKey> keys = kdm.keys ();
        
-       for (list<KDMCipher>::iterator i = ciphers.begin(); i != ciphers.end(); ++i) {
+       for (list<KDMKey>::iterator i = keys.begin(); i != keys.end(); ++i) {
                if (i->key_id() == _main_picture->key_id()) {
-                       _main_picture->set_kdm_cipher (*i);
+                       _main_picture->set_key (i->key ());
                }
                if (i->key_id() == _main_sound->key_id()) {
-                       _main_sound->set_kdm_cipher (*i);
+                       _main_sound->set_key (i->key ());
                }
        }
 }
index 29fbc0d7425e0d8faf9b8b405c84cc407762bb68..6f74e54a9c461c23fb428f36cdfc76eaf5faf87f 100644 (file)
@@ -49,11 +49,10 @@ SoundAsset::SoundAsset (
        boost::signals2::signal<void (float)>* progress,
        int fps,
        int intrinsic_duration,
-       bool encrypted,
        bool interop,
        MXFMetadata const & metadata
        )
-       : MXFAsset (directory, mxf_name, progress, fps, intrinsic_duration, encrypted)
+       : MXFAsset (directory, mxf_name, progress, fps, intrinsic_duration)
        , _channels (files.size ())
        , _sampling_rate (0)
 {
@@ -70,11 +69,10 @@ SoundAsset::SoundAsset (
        int fps,
        int intrinsic_duration,
        int channels,
-       bool encrypted,
        bool interop,
        MXFMetadata const & metadata
        )
-       : MXFAsset (directory, mxf_name, progress, fps, intrinsic_duration, encrypted)
+       : MXFAsset (directory, mxf_name, progress, fps, intrinsic_duration)
        , _channels (channels)
        , _sampling_rate (0)
 {
@@ -104,8 +102,8 @@ SoundAsset::SoundAsset (string directory, string mxf_name)
        _intrinsic_duration = desc.ContainerDuration;
 }
 
-SoundAsset::SoundAsset (string directory, string mxf_name, int fps, int channels, int sampling_rate, bool encrypted)
-       : MXFAsset (directory, mxf_name, 0, fps, 0, encrypted)
+SoundAsset::SoundAsset (string directory, string mxf_name, int fps, int channels, int sampling_rate)
+       : MXFAsset (directory, mxf_name, 0, fps, 0)
        , _channels (channels)
        , _sampling_rate (sampling_rate)
 {
index cf015c30cfccdf3aed1b6c4bf5c56f74ad68a1a5..339e67f8e27305aa4366b7c66e1a7bdd128b9a63 100644 (file)
@@ -79,7 +79,6 @@ public:
         *  @param length Length in frames.
         *  @param start_frame Frame in the source to start writing from.
         *  @param intrinsic_duration Length of the whole asset in frames.
-        *  @param encrypted true if asset should be encrypted.
         *  Note that this is different to entry_point in that the asset will contain no data before start_frame.
         */
        SoundAsset (
@@ -89,7 +88,6 @@ public:
                boost::signals2::signal<void (float)>* progress,
                int fps,
                int intrinsic_duration,
-               bool encrypted,
                bool interop,
                MXFMetadata const & metadata = MXFMetadata ()
                );
@@ -103,7 +101,6 @@ public:
         *  @param fps Frames per second.
         *  @param intrinsic_duration Length of the whole asset in frames.
         *  @param channels Number of audio channels.
-        *  @param encrypted true if asset should be encrypted.
         */
        SoundAsset (
                boost::function<std::string (Channel)> get_path,
@@ -113,7 +110,6 @@ public:
                int fps,
                int intrinsic_duration,
                int channels,
-               bool encrypted,
                bool interop,
                MXFMetadata const & metadata = MXFMetadata ()
                );
@@ -128,8 +124,7 @@ public:
                std::string mxf_name,
                int fps,
                int channels,
-               int sampling_rate,
-               bool encrypted
+               int sampling_rate
                );
 
        boost::shared_ptr<SoundAssetWriter> start_write (bool, MXFMetadata const & metadata = MXFMetadata ());
index 904b367da57ad6d300d402a940f24eb9343c455e..8634125c4a5f2d4ef7b03379c79e2cd967b670f2 100644 (file)
@@ -20,6 +20,7 @@ def build(bld):
                  gamma_lut.cc
                  image.cc
                  kdm.cc
+                 key.cc
                  metadata.cc
                  mxf_asset.cc
                  picture_asset.cc
@@ -55,6 +56,7 @@ def build(bld):
               gamma_lut.h
               image.h
               kdm.h
+              key.h
               lut.h
               lut_cache.h
               metadata.h
index 40e550cb5f61e7222d2b2d56afab710956bb8730..ba29549b345df9bc107a811fe280d5178f898228 100644 (file)
@@ -21,9 +21,9 @@ BOOST_AUTO_TEST_CASE (certificates)
 {
        libdcp::CertificateChain c;
 
-       c.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/ref/crypt/ca.self-signed.pem")));
-       c.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/ref/crypt/intermediate.signed.pem")));
-       c.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/ref/crypt/leaf.signed.pem")));
+       c.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (boost::filesystem::path ("test/ref/crypt/ca.self-signed.pem"))));
+       c.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (boost::filesystem::path ("test/ref/crypt/intermediate.signed.pem"))));
+       c.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (boost::filesystem::path ("test/ref/crypt/leaf.signed.pem"))));
 
        list<shared_ptr<libdcp::Certificate> > leaf_to_root = c.leaf_to_root ();
 
@@ -71,4 +71,7 @@ BOOST_AUTO_TEST_CASE (certificates)
                "dnQualifier=ndND9A/cODo2rTdrbLVmfQnoaSc=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
                );
 
+       /* Check that reconstruction from a string works */
+       libdcp::Certificate test (c.root()->certificate (true));
+       BOOST_CHECK_EQUAL (test.certificate(), c.root()->certificate());
 }
index 1b94369441e221418b8f08ceaf92e1686d5ebe41..d4207d5a357830cfb1d865bb317d955c1c740dfd 100644 (file)
@@ -45,7 +45,6 @@ BOOST_AUTO_TEST_CASE (dcp_test)
                                                         &d.Progress,
                                                         24,
                                                         24,
-                                                        false,
                                                         libdcp::Size (32, 32),
                                                         false,
                                                         mxf_meta
@@ -60,7 +59,6 @@ BOOST_AUTO_TEST_CASE (dcp_test)
                                                   24,
                                                   2,
                                                   false,
-                                                  false,
                                                   mxf_meta
                                                   ));
        
index 1b0de19a3973a4a821cc681c75df1b357322fc6a..3501befce283ca90b0d24c94fdb994eb96f84572 100644 (file)
@@ -39,9 +39,9 @@ BOOST_AUTO_TEST_CASE (encryption)
        libdcp::DCP d ("build/test/DCP/bar");
 
        libdcp::CertificateChain chain;
-       chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("build/test/signer/ca.self-signed.pem")));
-       chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("build/test/signer/intermediate.signed.pem")));
-       chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("build/test/signer/leaf.signed.pem")));
+       chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (boost::filesystem::path ("build/test/signer/ca.self-signed.pem"))));
+       chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (boost::filesystem::path ("build/test/signer/intermediate.signed.pem"))));
+       chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (boost::filesystem::path ("build/test/signer/leaf.signed.pem"))));
 
        shared_ptr<libdcp::Signer> signer (
                new libdcp::Signer (
@@ -59,12 +59,13 @@ BOOST_AUTO_TEST_CASE (encryption)
                                                         &d.Progress,
                                                         24,
                                                         24,
-                                                        true,
                                                         libdcp::Size (32, 32),
                                                         false,
                                                         mxf_metadata
                                                         ));
 
+       mp->set_key (libdcp::Key ());
+
        shared_ptr<libdcp::SoundAsset> ms (new libdcp::SoundAsset (
                                                   wav,
                                                   "build/test/bar",
@@ -73,10 +74,11 @@ BOOST_AUTO_TEST_CASE (encryption)
                                                   24,
                                                   24,
                                                   2,
-                                                  true,
                                                   false,
                                                   mxf_metadata
                                                   ));
+
+       ms->set_key (libdcp::Key ());
        
        cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (mp, ms, shared_ptr<libdcp::SubtitleAsset> ())));
        d.add_cpl (cpl);
index 380363b4a85690e9fbd6531374330fa6d6487ce1..5477ca5a189877f4df4b9f7de78192b93c73d8b7 100644 (file)
@@ -29,12 +29,12 @@ BOOST_AUTO_TEST_CASE (error_test)
 
        /* Trying to create video/audio MXFs using a non-existant file should throw an exception */
        BOOST_CHECK_THROW (
-               new libdcp::MonoPictureAsset (p, "build/test/fred", "video.mxf", &d.Progress, 24, 24, false, libdcp::Size (32, 32), false),
+               new libdcp::MonoPictureAsset (p, "build/test/fred", "video.mxf", &d.Progress, 24, 24, libdcp::Size (32, 32), false),
                libdcp::FileError
                );
        
        BOOST_CHECK_THROW (
-               new libdcp::SoundAsset (p, "build/test/fred", "audio.mxf", &d.Progress, 24, 24, false, false),
+               new libdcp::SoundAsset (p, "build/test/fred", "audio.mxf", &d.Progress, 24, 24, false),
                libdcp::FileError
                );
 }
index f536e189d143e6387805d3792a33f54a7457edad..a85bada787732751c9cc777f326f3d2176b5415b 100644 (file)
@@ -26,19 +26,19 @@ BOOST_AUTO_TEST_CASE (kdm_test)
                "test/data/private.key"
                );
 
-       list<libdcp::KDMCipher> ciphers = kdm.ciphers ();
+       list<libdcp::KDMKey> keys = kdm.keys ();
        
-       BOOST_CHECK_EQUAL (ciphers.size(), 2);
-
-       BOOST_CHECK_EQUAL (ciphers.front().cpl_id(), "eece17de-77e8-4a55-9347-b6bab5724b9f");
-       BOOST_CHECK_EQUAL (ciphers.front().key_id(), "4ac4f922-8239-4831-b23b-31426d0542c4");
-       BOOST_CHECK_EQUAL (ciphers.front().not_valid_before(), "2013-07-06T20:04:58+00:00");
-       BOOST_CHECK_EQUAL (ciphers.front().not_valid_after(), "2023-07-02T20:04:56+00:00");
-       BOOST_CHECK_EQUAL (ciphers.front().key_string(), "8a2729c3e5b65c45d78305462104c3fb");
-
-       BOOST_CHECK_EQUAL (ciphers.back().cpl_id(), "eece17de-77e8-4a55-9347-b6bab5724b9f");
-       BOOST_CHECK_EQUAL (ciphers.back().key_id(), "73baf5de-e195-4542-ab28-8a465f7d4079");
-       BOOST_CHECK_EQUAL (ciphers.back().not_valid_before(), "2013-07-06T20:04:58+00:00");
-       BOOST_CHECK_EQUAL (ciphers.back().not_valid_after(), "2023-07-02T20:04:56+00:00");
-       BOOST_CHECK_EQUAL (ciphers.back().key_string(), "5327fb7ec2e807bd57059615bf8a169d");
+       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().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().key().hex(), "5327fb7ec2e807bd57059615bf8a169d");
 }
index 277ea67899e3f6949973ec99d5def1884c29a85a..d8ffea6ac6220122b7b6f90c399ccb829ad0f287 100644 (file)
@@ -41,7 +41,7 @@ BOOST_AUTO_TEST_CASE (recovery)
        
        boost::filesystem::remove_all ("build/test/baz");
        boost::filesystem::create_directories ("build/test/baz");
-       shared_ptr<libdcp::MonoPictureAsset> mp (new libdcp::MonoPictureAsset ("build/test/baz", "video1.mxf", 24, libdcp::Size (32, 32), false));
+       shared_ptr<libdcp::MonoPictureAsset> mp (new libdcp::MonoPictureAsset ("build/test/baz", "video1.mxf", 24, libdcp::Size (32, 32)));
        shared_ptr<libdcp::PictureAssetWriter> writer = mp->start_write (false, false);
 
        int written_size = 0;
@@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE (recovery)
        Kumu::ResetTestRNG ();
 #endif 
 
-       mp.reset (new libdcp::MonoPictureAsset ("build/test/baz", "video2.mxf", 24, libdcp::Size (32, 32), false));
+       mp.reset (new libdcp::MonoPictureAsset ("build/test/baz", "video2.mxf", 24, libdcp::Size (32, 32)));
        writer = mp->start_write (true, false);
 
        writer->write (data, size);