Sort-of generates a signed CPL.
authorCarl Hetherington <cth@carlh.net>
Thu, 3 Jan 2013 20:41:32 +0000 (20:41 +0000)
committerCarl Hetherington <cth@carlh.net>
Thu, 3 Jan 2013 20:41:32 +0000 (20:41 +0000)
README
src/dcp.cc
src/dcp.h
src/util.cc
src/util.h
src/wscript
test/tests.cc
test/wscript
wscript

diff --git a/README b/README
index 6fe5521b09c9ad69c1e58a6c0dc5204f352019eb..deb4b528ef04a9eaf67f91427af4ac36efcba5f9 100644 (file)
--- a/README
+++ b/README
@@ -20,10 +20,12 @@ nasty details of encryption.
 
 == Dependencies
 
-boost filesystem library
-OpenSSL
+boost filesystem, signals2 and unit testing libraries
+openssl
 libsigc++
 libxml++
+xmlsec
+openjpeg (1.5.0 or above)
 
 
 == Documentation
index bd3cc80bcdf14212389acf24b7c20b34c313bd60..2a1a156b3fcf5ba7f2c36fb71a172cf73f792de2 100644 (file)
@@ -28,6 +28,8 @@
 #include <iostream>
 #include <boost/filesystem.hpp>
 #include <libxml++/libxml++.h>
+#include <xmlsec/xmldsig.h>
+#include <xmlsec/app.h>
 #include "dcp.h"
 #include "asset.h"
 #include "sound_asset.h"
@@ -60,7 +62,7 @@ void
 DCP::write_xml () const
 {
        for (list<shared_ptr<const CPL> >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) {
-               (*i)->write_xml (_encrypted, _certificates);
+               (*i)->write_xml (_encrypted, _certificates, _signer_key);
        }
 
        string pkl_uuid = make_uuid ();
@@ -424,7 +426,7 @@ CPL::add_reel (shared_ptr<const Reel> reel)
 }
 
 void
-CPL::write_xml (bool encrypted, CertificateChain const & certificates) const
+CPL::write_xml (bool encrypted, CertificateChain const & certificates, string const & signer_key) const
 {
        boost::filesystem::path p;
        p /= _directory;
@@ -435,6 +437,10 @@ CPL::write_xml (bool encrypted, CertificateChain const & certificates) const
        xmlpp::Document doc;
        xmlpp::Element* cpl = doc.create_root_node("CompositionPlaylist", "http://www.smpte-ra.org/schemas/429-7/2006/CPL");
 
+       if (encrypted) {
+               cpl->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "dsig");
+       }
+
        cpl->add_child("Id")->add_child_text ("urn:uuid:" + _uuid);
        cpl->add_child("AnnotationText")->add_child_text (_name);
        cpl->add_child("IssueDate")->add_child_text (Metadata::instance()->issue_date);
@@ -474,12 +480,13 @@ CPL::write_xml (bool encrypted, CertificateChain const & certificates) const
                }
 
                xmlpp::Element* signature = cpl->add_child("Signature", "dsig");
+               
                {
                        xmlpp::Element* signed_info = signature->add_child ("SignedInfo", "dsig");
                        signed_info->add_child("CanonicalizationMethod", "dsig")->set_attribute ("Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315");
                        signed_info->add_child("SignatureMethod", "dsig")->set_attribute("Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
                        {
-                               xmlpp::Element* reference = signature->add_child("Reference", "dsig");
+                               xmlpp::Element* reference = signed_info->add_child("Reference", "dsig");
                                reference->set_attribute ("URI", "");
                                {
                                        xmlpp::Element* transforms = reference->add_child("Transforms", "dsig");
@@ -488,21 +495,56 @@ CPL::write_xml (bool encrypted, CertificateChain const & certificates) const
                                                );
                                }
                                reference->add_child("DigestMethod", "dsig")->set_attribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
+                               /* This will be filled in by the signing later */
+                               reference->add_child("DigestValue", "dsig");
                        }
                }
 
+               signature->add_child("SignatureValue", "dsig");
+
+               xmlpp::Element* key_info = signature->add_child("KeyInfo", "dsig");
                list<shared_ptr<Certificate> > c = certificates.leaf_to_root ();
                for (list<shared_ptr<Certificate> >::iterator i = c.begin(); i != c.end(); ++i) {
-                       xmlpp::Element* data = signature->add_child("X509Data", "dsig");
+                       xmlpp::Element* data = key_info->add_child("X509Data", "dsig");
                        {
-                               xmlpp::Element* serial = data->add_child("X509IssuerSerial", "data");
+                               xmlpp::Element* serial = data->add_child("X509IssuerSerial", "dsig");
                                serial->add_child("X509IssuerName", "dsig")->add_child_text(
                                        Certificate::name_for_xml ((*i)->issuer())
                                        );
                                serial->add_child("X509SerialNumber", "dsig")->add_child_text((*i)->serial());
-                               serial->add_child("X509Certificate", "dsig")->add_child_text("XXX");
                        }
                }
+
+               xmlSecKeysMngrPtr keys_manager = xmlSecKeysMngrCreate();
+               if (!keys_manager) {
+                       throw MiscError ("could not create keys manager");
+               }
+               if (xmlSecCryptoAppDefaultKeysMngrInit (keys_manager) < 0) {
+                       throw MiscError ("could not initialise keys manager");
+               }
+
+               xmlSecKeyPtr const key = xmlSecCryptoAppKeyLoad (signer_key.c_str(), xmlSecKeyDataFormatPem, 0, 0, 0);
+               if (key == 0) {
+                       throw MiscError ("could not load signer key");
+               }
+
+               if (xmlSecCryptoAppDefaultKeysMngrAdoptKey (keys_manager, key) < 0) {
+                       xmlSecKeyDestroy (key);
+                       throw MiscError ("could not use signer key");
+               }
+
+               xmlSecDSigCtx signature_context;
+
+               if (xmlSecDSigCtxInitialize (&signature_context, keys_manager) < 0) {
+                       throw MiscError ("could not initialise XMLSEC context");
+               }
+
+               if (xmlSecDSigCtxSign (&signature_context, signature->cobj()) < 0) {
+                       throw MiscError ("could not sign CPL");
+               }
+
+               xmlSecDSigCtxFinalize (&signature_context);
+               xmlSecKeysMngrDestroy (keys_manager);
        }
 
        doc.write_to_file_formatted (p.string(), "UTF-8");
index ca011c360cf9c5e212100896b11c0383844d8b80..e2e2314f8aded7b5c961605c3f04a8f0c00cb540 100644 (file)
--- a/src/dcp.h
+++ b/src/dcp.h
@@ -86,7 +86,7 @@ public:
        
        bool equals (CPL const & other, EqualityOptions options, std::list<std::string>& notes) const;
        
-       void write_xml (bool, CertificateChain const &) const;
+       void write_xml (bool, CertificateChain const &, std::string const &) const;
        void write_to_assetmap (std::ostream& s) const;
        void write_to_pkl (std::ostream& s) const;
        
@@ -151,6 +151,18 @@ public:
                return _cpls;
        }
 
+       void set_encrypted (bool e) {
+               _encrypted = e;
+       }
+
+       void set_certificates (CertificateChain const & c) {
+               _certificates = c;
+       }
+
+       void set_signer_key (std::string const & s) {
+               _signer_key = s;
+       }
+
        /** Emitted with a parameter between 0 and 1 to indicate progress
         *  for long jobs.
         */
@@ -186,6 +198,7 @@ private:
 
        bool _encrypted;
        CertificateChain _certificates;
+       std::string _signer_key;
 };
 
 }
index 2b3a6c8defac8567cb80593e180fc631924c670b..323fb1e46070df84345f98b9d833fa66a3b4f264 100644 (file)
@@ -27,6 +27,9 @@
 #include <iomanip>
 #include <boost/filesystem.hpp>
 #include <openssl/sha.h>
+#include <xmlsec/xmldsig.h>
+#include <xmlsec/dl.h>
+#include <xmlsec/app.h>
 #include "KM_util.h"
 #include "KM_fileio.h"
 #include "AS_DCP.h"
@@ -37,6 +40,7 @@
 #include "lut.h"
 
 using std::string;
+using std::cout;
 using std::stringstream;
 using std::min;
 using std::max;
@@ -267,3 +271,25 @@ libdcp::empty_or_white_space (string s)
 
        return true;
 }
+
+void
+libdcp::init ()
+{
+       if (xmlSecInit() < 0) {
+               throw MiscError ("could not initialise xmlsec");
+       }
+
+#ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
+       if (xmlSecCryptoDLLoadLibrary (BAD_CAST XMLSEC_CRYPTO) < 0) {
+               throw MiscError ("unable to load default xmlsec-crypto library");
+       }
+#endif
+       
+       if (xmlSecCryptoAppInit (0) < 0) {
+               throw MiscError ("could not initialise crypto library");
+       }
+       
+       if (xmlSecCryptoInit() < 0) {
+               throw MiscError ("could not initialise xmlsec-crypto");
+       }
+}
index 7056db6a372fe4def5fbe0cce8baa83b4a6a5e0a..6426eb8dc768b5dc6838da7638bf2910cf7eccfb 100644 (file)
@@ -53,4 +53,6 @@ extern bool empty_or_white_space (std::string s);
 extern opj_image_t* decompress_j2k (uint8_t* data, int64_t size, int reduce);
 extern boost::shared_ptr<ARGBFrame> xyz_to_rgb (opj_image_t* xyz_frame);
 
+extern void init ();
+
 }
index 56a99063b03300f410c86c8aecaffecb7c1cbc91..efb31d50279cd4af19cfed4077f638cbe5a80c39 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'
+    obj.uselib = 'BOOST_FILESYSTEM BOOST_SIGNALS2 OPENSSL SIGC++ LIBXML++ OPENJPEG XMLSEC1'
     obj.use = 'libkumu-libdcp libasdcp-libdcp'
     obj.source = """
                  asset.cc
index 71796f7205c9a112ac950732d36c63571c34cef3..d27b2ea47abaded5fc03d79276f885be33a8791b 100644 (file)
@@ -56,6 +56,8 @@ wav (libdcp::Channel)
 
 BOOST_AUTO_TEST_CASE (dcp_test)
 {
+       libdcp::init ();
+       
        Kumu::libdcp_test = true;
        
        libdcp::Metadata* t = libdcp::Metadata::instance ();
@@ -598,7 +600,11 @@ 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");
        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 (
                                                         j2c,
index 43ca7d732767b8d738fc975fc1c5994bd34e5e30..d22875e91ad8852d4e500b08a9e520d82d148f0c 100644 (file)
@@ -18,7 +18,7 @@ def configure(conf):
 def build(bld):
     obj = bld(features = 'cxx cxxprogram')
     obj.name   = 'tests'
-    obj.uselib = 'BOOST_TEST OPENJPEG'
+    obj.uselib = 'BOOST_TEST OPENJPEG XMLSEC1'
     obj.use    = 'libdcp'
     obj.source = 'tests.cc'
     obj.target = 'tests'
diff --git a/wscript b/wscript
index e3d209aef13894e7d2265a8d34c0093fb7e0e32e..c119367a0cdaba109c5d6acf9da497a23d47950e 100644 (file)
--- a/wscript
+++ b/wscript
@@ -28,6 +28,9 @@ def configure(conf):
 
     conf.check_cfg(package = 'openssl', args = '--cflags --libs', uselib_store = 'OPENSSL', mandatory = True)
     conf.check_cfg(package = 'libxml++-2.6', args = '--cflags --libs', uselib_store = 'LIBXML++', mandatory = True)
+    conf.check_cfg(package = 'xmlsec1', args = '--cflags --libs', uselib_store = 'XMLSEC1', mandatory = True)
+    # Remove erroneous escaping of quotes from xmlsec1 defines
+    conf.env.DEFINES_XMLSEC1 = [f.replace('\\', '') for f in conf.env.DEFINES_XMLSEC1]
 
     openjpeg_fragment = """
                        #include <stdio.h>\n