Tweaks to management of crypto information.
authorCarl Hetherington <cth@carlh.net>
Fri, 11 Jan 2013 00:25:11 +0000 (00:25 +0000)
committerCarl Hetherington <cth@carlh.net>
Fri, 11 Jan 2013 00:25:11 +0000 (00:25 +0000)
src/certificates.cc
src/certificates.h
src/crypt_chain.cc
src/dcp.cc
src/dcp.h
src/wscript
test/tests.cc
wscript

index 6ed32dcadf1d74e71d7e282c5fa0c8a1370627c0..c1b15c51ac0e9cd18974b565994e0349ff6e116c 100644 (file)
@@ -42,6 +42,19 @@ Certificate::Certificate (X509* c)
        
 }
 
+Certificate::Certificate (string const & filename)
+       : _certificate (0)
+{
+       FILE* f = fopen (filename.c_str(), "r");
+       if (!f) {
+               throw FileError ("could not open file", filename);
+       }
+       
+       if (!PEM_read_X509 (f, &_certificate, 0, 0)) {
+               throw MiscError ("could not read X509 certificate");
+       }
+}
+
 Certificate::~Certificate ()
 {
        X509_free (_certificate);
@@ -147,32 +160,6 @@ Certificate::thumbprint () const
        return Kumu::base64encode (digest, 20, digest_base64, 64);
 }
 
-/** @param filename Text file of PEM-format certificates,
- *  in the order:
- *
- *  1. self-signed root certificate
- *  2. intermediate certificate signed by root certificate
- *  ...
- *  n. leaf certificate signed by previous intermediate.
- */
-
-CertificateChain::CertificateChain (string const & filename)
-{
-       FILE* f = fopen (filename.c_str(), "r");
-       if (!f) {
-               throw FileError ("could not open file", filename);
-       }
-       
-       while (1) {
-               X509* c = 0;
-               if (!PEM_read_X509 (f, &c, 0, 0)) {
-                       break;
-               }
-
-               _certificates.push_back (shared_ptr<Certificate> (new Certificate (c)));
-       }
-}
-
 shared_ptr<Certificate>
 CertificateChain::root () const
 {
@@ -195,3 +182,8 @@ CertificateChain::leaf_to_root () const
        return c;
 }
 
+void
+CertificateChain::add (shared_ptr<Certificate> c)
+{
+       _certificates.push_back (c);
+}
index db19a9db1553f031de0d78219225d64b8e6ee749..e1a572ec0ecc03ce9c6361d912b508abe13335ee 100644 (file)
@@ -40,7 +40,8 @@ public:
        Certificate ()
                : _certificate (0)
        {}
-       
+
+       Certificate (std::string const &);
        Certificate (X509 *);
        ~Certificate ();
 
@@ -61,7 +62,8 @@ class CertificateChain
 {
 public:
        CertificateChain () {}
-       CertificateChain (std::string const &);
+
+       void add (boost::shared_ptr<Certificate>);
 
        boost::shared_ptr<Certificate> root () const;
        boost::shared_ptr<Certificate> leaf () const;
index 853d8c50c8ce652d5eb0697331ee645ec92b7e22..d07778599734ca82918601e4d92cea233f690fb9 100644 (file)
@@ -3,17 +3,29 @@
 #include <boost/filesystem.hpp>
 #include <boost/algorithm/string.hpp>
 #include "crypt_chain.h"
+#include "exceptions.h"
 
 using std::string;
 using std::ofstream;
 using std::ifstream;
 using std::stringstream;
+using std::cout;
+
+static void command (char const * c)
+{
+       int const r = system (c);
+       if (WEXITSTATUS (r)) {
+               stringstream s;
+               s << "error in " << c << "\n";
+               throw libdcp::MiscError (s.str());
+       }
+}
 
 void
 libdcp::make_crypt_chain (string directory)
 {
        boost::filesystem::current_path (directory);
-       system ("openssl genrsa -out ca.key 2048");
+       command ("openssl genrsa -out ca.key 2048");
 
        {
                ofstream f ("ca.cnf");
@@ -31,7 +43,7 @@ libdcp::make_crypt_chain (string directory)
                  << "CN = Entity and dnQualifier\n";
        }
 
-       system ("openssl rsa -outform PEM -pubout -in ca.key | openssl base64 -d | dd bs=1 skip=24 2>/dev/null | openssl sha1 -binary | openssl base64 > ca_dnq");
+       command ("openssl rsa -outform PEM -pubout -in ca.key | openssl base64 -d | dd bs=1 skip=24 2>/dev/null | openssl sha1 -binary | openssl base64 > ca_dnq");
 
        string ca_dnq;
 
@@ -46,10 +58,10 @@ libdcp::make_crypt_chain (string directory)
        {
                stringstream c;
                c << "openssl req -new -x509 -sha256 -config ca.cnf -days 3650 -set_serial 5 -subj " << ca_subject << " -key ca.key -outform PEM -out ca.self-signed.pem";
-               system (c.str().c_str());
+               command (c.str().c_str());
        }
 
-       system ("openssl genrsa -out intermediate.key 2048");
+       command ("openssl genrsa -out intermediate.key 2048");
 
        {
                ofstream f ("intermediate.cnf");
@@ -67,7 +79,7 @@ libdcp::make_crypt_chain (string directory)
                  << "CN = Entity and dnQualifier\n";
        }
 
-       system ("openssl rsa -outform PEM -pubout -in intermediate.key | openssl base64 -d | dd bs=1 skip=24 2>/dev/null | openssl sha1 -binary | openssl base64 > inter_dnq");
+       command ("openssl rsa -outform PEM -pubout -in intermediate.key | openssl base64 -d | dd bs=1 skip=24 2>/dev/null | openssl sha1 -binary | openssl base64 > inter_dnq");
        
        string inter_dnq;
 
@@ -82,13 +94,13 @@ libdcp::make_crypt_chain (string directory)
        {
                stringstream s;
                s << "openssl req -new -config intermediate.cnf -days 3649 -subj " << inter_subject << " -key intermediate.key -out intermediate.csr";
-               system (s.str().c_str());
+               command (s.str().c_str());
        }
 
        
-       system ("openssl x509 -req -sha256 -days 3649 -CA ca.self-signed.pem -CAkey ca.key -set_serial 6 -in intermediate.csr -extfile intermediate.cnf -extensions v3_ca -out intermediate.signed.pem");
+       command ("openssl x509 -req -sha256 -days 3649 -CA ca.self-signed.pem -CAkey ca.key -set_serial 6 -in intermediate.csr -extfile intermediate.cnf -extensions v3_ca -out intermediate.signed.pem");
 
-       system ("openssl genrsa -out leaf.key 2048");
+       command ("openssl genrsa -out leaf.key 2048");
 
        {
                ofstream f ("leaf.cnf");
@@ -106,7 +118,7 @@ libdcp::make_crypt_chain (string directory)
                  << "CN = Entity and dnQualifier\n";
        }
 
-       system ("openssl rsa -outform PEM -pubout -in leaf.key | openssl base64 -d | dd bs=1 skip=24 2>/dev/null | openssl sha1 -binary | openssl base64 > leaf_dnq");
+       command ("openssl rsa -outform PEM -pubout -in leaf.key | openssl base64 -d | dd bs=1 skip=24 2>/dev/null | openssl sha1 -binary | openssl base64 > leaf_dnq");
        
        string leaf_dnq;
 
@@ -121,8 +133,8 @@ libdcp::make_crypt_chain (string directory)
        {
                stringstream s;
                s << "openssl req -new -config leaf.cnf -days 3648 -subj " << leaf_subject << " -key leaf.key -outform PEM -out leaf.csr";
-               system (s.str().c_str());
+               command (s.str().c_str());
        }
 
-       system ("openssl x509 -req -sha256 -days 3648 -CA intermediate.signed.pem -CAkey intermediate.key -set_serial 7 -in leaf.csr -extfile leaf.cnf -extensions v3_ca -out leaf.signed.pem");
+       command ("openssl x509 -req -sha256 -days 3648 -CA intermediate.signed.pem -CAkey intermediate.key -set_serial 7 -in leaf.csr -extfile leaf.cnf -extensions v3_ca -out leaf.signed.pem");
 }
index 45ff69d1300aa49ca63a7f1dc8d6c8c0e43bf3c1..54ab6660c357d3150d6146b210b2934aa7f9d4ab 100644 (file)
@@ -54,27 +54,26 @@ using namespace libdcp;
 
 DCP::DCP (string directory)
        : _directory (directory)
-       , _encrypted (false)
 {
        boost::filesystem::create_directories (directory);
 }
 
 void
-DCP::write_xml () const
+DCP::write_xml (shared_ptr<Encryption> crypt) const
 {
        for (list<shared_ptr<const CPL> >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) {
-               (*i)->write_xml (_encrypted, _certificates, _signer_key);
+               (*i)->write_xml (crypt);
        }
 
        string pkl_uuid = make_uuid ();
-       string pkl_path = write_pkl (pkl_uuid);
+       string pkl_path = write_pkl (pkl_uuid, crypt);
        
        write_volindex ();
        write_assetmap (pkl_uuid, boost::filesystem::file_size (pkl_path));
 }
 
 std::string
-DCP::write_pkl (string pkl_uuid) const
+DCP::write_pkl (string pkl_uuid, shared_ptr<Encryption> crypt) const
 {
        assert (!_cpls.empty ());
        
@@ -86,7 +85,7 @@ DCP::write_pkl (string pkl_uuid) const
 
        xmlpp::Document doc;
        xmlpp::Element* pkl = doc.create_root_node("PackingList", "http://www.smpte-ra.org/schemas/429-8/2007/PKL");
-       if (_encrypted) {
+       if (crypt) {
                pkl->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "dsig");
        }
 
@@ -109,8 +108,8 @@ DCP::write_pkl (string pkl_uuid) const
                }
        }
 
-       if (_encrypted) {
-               sign (pkl, _certificates, _signer_key);
+       if (crypt) {
+               sign (pkl, crypt->certificates, crypt->signer_key);
        }
                
        doc.write_to_file_formatted (p.string(), "UTF-8");
@@ -435,7 +434,7 @@ CPL::add_reel (shared_ptr<const Reel> reel)
 }
 
 void
-CPL::write_xml (bool encrypted, CertificateChain const & certificates, string const & signer_key) const
+CPL::write_xml (shared_ptr<Encryption> crypt) const
 {
        boost::filesystem::path p;
        p /= _directory;
@@ -446,7 +445,7 @@ CPL::write_xml (bool encrypted, CertificateChain const & certificates, string co
        xmlpp::Document doc;
        xmlpp::Element* cpl = doc.create_root_node("CompositionPlaylist", "http://www.smpte-ra.org/schemas/429-7/2006/CPL");
 
-       if (encrypted) {
+       if (crypt) {
                cpl->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "dsig");
        }
 
@@ -470,8 +469,8 @@ CPL::write_xml (bool encrypted, CertificateChain const & certificates, string co
                (*i)->write_to_cpl (reel_list);
        }
 
-       if (encrypted) {
-               sign (cpl, certificates, signer_key);
+       if (crypt) {
+               sign (cpl, crypt->certificates, crypt->signer_key);
        }
 
        doc.write_to_file_formatted (p.string(), "UTF-8");
@@ -570,7 +569,13 @@ CPL::equals (CPL const & other, EqualityOptions opt, list<string>& notes) const
 }
 
 shared_ptr<xmlpp::Document>
-CPL::make_kdm (CertificateChain const & certificates, string const & signer_key, shared_ptr<const Certificate> recipient_cert) const
+CPL::make_kdm (
+       CertificateChain const & certificates,
+       string const & signer_key,
+       shared_ptr<const Certificate> recipient_cert,
+       boost::posix_time::ptime from,
+       boost::posix_time::ptime until
+       ) const
 {
        shared_ptr<xmlpp::Document> doc (new xmlpp::Document);
        xmlpp::Element* root = doc->create_root_node ("DCinemaSecurityMessage");
index 96b28090a84c8cea3d9c6d8725c20b9ab37e81aa..63e579de9af134b07ffca07c9d734353e83f7dc1 100644 (file)
--- a/src/dcp.h
+++ b/src/dcp.h
@@ -28,6 +28,7 @@
 #include <vector>
 #include <boost/shared_ptr.hpp>
 #include <boost/signals2.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
 #include "types.h"
 #include "certificates.h"
 
@@ -47,6 +48,18 @@ class SubtitleAsset;
 class Reel;
 class AssetMap;
 
+class Encryption
+{
+public:
+       Encryption (CertificateChain c, std::string const & k)
+               : certificates (c)
+               , signer_key (k)
+       {}
+
+       CertificateChain certificates;
+       std::string signer_key;
+};
+
 class CPL
 {
 public:
@@ -87,11 +100,17 @@ public:
        
        bool equals (CPL const & other, EqualityOptions options, std::list<std::string>& notes) const;
        
-       void write_xml (bool, CertificateChain const &, std::string const &) const;
+       void write_xml (boost::shared_ptr<Encryption>) const;
        void write_to_assetmap (std::ostream& s) const;
        void write_to_pkl (xmlpp::Element* p) const;
 
-       boost::shared_ptr<xmlpp::Document> make_kdm (CertificateChain const &, std::string const &, boost::shared_ptr<const Certificate>) const;
+       boost::shared_ptr<xmlpp::Document> make_kdm (
+               CertificateChain const &,
+               std::string const &,
+               boost::shared_ptr<const Certificate>,
+               boost::posix_time::ptime from,
+               boost::posix_time::ptime until
+               ) const;
        
 private:
        std::string _directory;
@@ -138,7 +157,7 @@ public:
        /** Write the required XML files to the directory that was
         *  passed into the constructor.
         */
-       void write_xml () const;
+       void write_xml (boost::shared_ptr<Encryption> crypt = boost::shared_ptr<Encryption> ()) const;
 
        /** Compare this DCP with another, according to various options.
         *  @param other DCP to compare this one to.
@@ -154,26 +173,6 @@ public:
                return _cpls;
        }
 
-       void set_encrypted (bool e) {
-               _encrypted = e;
-       }
-
-       void set_certificates (CertificateChain const & c) {
-               _certificates = c;
-       }
-
-       CertificateChain certificates () const {
-               return _certificates;
-       }
-
-       void set_signer_key (std::string const & s) {
-               _signer_key = s;
-       }
-
-       std::string signer_key () const {
-               return _signer_key;
-       }
-
        /** Emitted with a parameter between 0 and 1 to indicate progress
         *  for long jobs.
         */
@@ -184,7 +183,7 @@ private:
        /** Write the PKL file.
         *  @param pkl_uuid UUID to use.
         */
-       std::string write_pkl (std::string pkl_uuid) const;
+       std::string write_pkl (std::string pkl_uuid, boost::shared_ptr<Encryption>) const;
        
        /** Write the VOLINDEX file */
        void write_volindex () const;
@@ -206,10 +205,6 @@ private:
        /** the directory that we are writing to */
        std::string _directory;
        std::list<boost::shared_ptr<const CPL> > _cpls;
-
-       bool _encrypted;
-       CertificateChain _certificates;
-       std::string _signer_key;
 };
 
 }
index 41ebf7a760aab7d538025856a5bd8d6da2ac539f..0922cb56c7de682ddd37344631505fc87d55eef5 100644 (file)
@@ -7,7 +7,7 @@ def build(bld):
     obj.name = 'libdcp'
     obj.target = 'dcp'
     obj.export_includes = ['.']
-    obj.uselib = 'BOOST_FILESYSTEM BOOST_SIGNALS2 OPENSSL SIGC++ LIBXML++ OPENJPEG XMLSEC1'
+    obj.uselib = 'BOOST_FILESYSTEM BOOST_SIGNALS2 BOOST_DATETIME OPENSSL SIGC++ LIBXML++ OPENJPEG XMLSEC1'
     obj.use = 'libkumu-libdcp libasdcp-libdcp'
     obj.source = """
                  asset.cc
index 7c3144a1445952334ebbd8aff069e3d32776a112..b15556c1dc47d6bdd5f7c110d0c36fc247bb43b2 100644 (file)
@@ -31,6 +31,7 @@
 #include "sound_asset.h"
 #include "reel.h"
 #include "certificates.h"
+#include "crypt_chain.h"
 
 #define BOOST_TEST_DYN_LINK
 #define BOOST_TEST_MODULE libdcp_test
@@ -601,9 +602,19 @@ BOOST_AUTO_TEST_CASE (encryption)
        boost::filesystem::remove_all ("build/test/bar");
        boost::filesystem::create_directories ("build/test/bar");
        libdcp::DCP d ("build/test/bar");
-       d.set_encrypted (true);
-       d.set_certificates (libdcp::CertificateChain ("test/data/certificate_chain"));
-       d.set_signer_key ("test/data/signer.key");
+
+       libdcp::CertificateChain chain;
+       chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/data/ca.self-signed.pem")));
+       chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/data/intermediate.signed.pem")));
+       chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/data/leaf.signed.pem")));
+
+       shared_ptr<libdcp::Encryption> crypt (
+               new libdcp::Encryption (
+                       chain,
+                       "test/data/signer.key"
+                       )
+               );
+
        shared_ptr<libdcp::CPL> cpl (new libdcp::CPL ("build/test/bar", "A Test DCP", libdcp::FEATURE, 24, 24));
        
        shared_ptr<libdcp::MonoPictureAsset> mp (new libdcp::MonoPictureAsset (
@@ -632,16 +643,27 @@ BOOST_AUTO_TEST_CASE (encryption)
        cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (mp, ms, shared_ptr<libdcp::SubtitleAsset> ())));
        d.add_cpl (cpl);
 
-       d.write_xml ();
+       d.write_xml (crypt);
 
-       cpl->make_kdm(d.certificates(), d.signer_key(), d.certificates().leaf())->write_to_file_formatted ("build/test/bar.kdm.xml", "UTF-8");
+       shared_ptr<xmlpp::Document> kdm = cpl->make_kdm (
+               crypt->certificates,
+               crypt->signer_key,
+               crypt->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")
+               );
+
+       kdm->write_to_file_formatted ("build/test/bar.kdm.xml", "UTF-8");
 }
 
 BOOST_AUTO_TEST_CASE (certificates)
 {
-       libdcp::CertificateChain c ("test/data/certificate_chain");
-       BOOST_CHECK_EQUAL (c._certificates.size(), 3);
+       libdcp::CertificateChain c;
 
+       c.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/data/ca.self-signed.pem")));
+       c.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/data/intermediate.signed.pem")));
+       c.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate ("test/data/leaf.signed.pem")));
+       
        BOOST_CHECK_EQUAL (
                c.root()->issuer(),
                "/O=example.org/OU=example.org/CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION/dnQualifier=rTeK7x+nopFkyphflooz6p2ZM7A="
@@ -659,3 +681,10 @@ BOOST_AUTO_TEST_CASE (certificates)
                "dnQualifier=rTeK7x\\+nopFkyphflooz6p2ZM7A=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org"
                );
 }
+
+BOOST_AUTO_TEST_CASE (crypt_chain)
+{
+       boost::filesystem::remove_all ("build/test/crypt");
+       boost::filesystem::create_directory ("build/test/crypt");
+       libdcp::make_crypt_chain ("build/test/crypt");
+}
diff --git a/wscript b/wscript
index c119367a0cdaba109c5d6acf9da497a23d47950e..ce58427db67d49c1ed711b8c7f540008a15d5a17 100644 (file)
--- a/wscript
+++ b/wscript
@@ -73,6 +73,15 @@ def configure(conf):
                    msg = 'Checking for boost signals2 library',
                    uselib_store = 'BOOST_SIGNALS2')
 
+    conf.check_cxx(fragment = """
+                             #include <boost/date_time.hpp>\n
+                             int main() { boost::gregorian::day_clock::local_day(); }\n
+                             """,
+                   msg = 'Checking for boost datetime library',
+                   libpath = '/usr/local/lib',
+                   lib = ['boost_date_time%s' % boost_lib_suffix, 'boost_system%s' % boost_lib_suffix],
+                   uselib_store = 'BOOST_DATETIME')
+
     lut.make_luts()
 
     conf.recurse('test')