Various KDM work.
authorCarl Hetherington <cth@carlh.net>
Tue, 24 Sep 2013 17:54:09 +0000 (18:54 +0100)
committerCarl Hetherington <cth@carlh.net>
Tue, 24 Sep 2013 17:54:09 +0000 (18:54 +0100)
13 files changed:
src/cpl.cc
src/cpl.h
src/kdm.cc
src/kdm.h
src/mxf_asset.cc
src/mxf_asset.h
src/signer.cc
src/signer.h
src/wscript
src/xml/kdm_smpte.cc
src/xml/kdm_smpte.h
test/encryption_test.cc
test/kdm_test.cc

index 072304b810331db129b03a783a7d9e50cde2b87f..4c4d2e756adaea268c197f65ef167752d3f85195 100644 (file)
@@ -344,166 +344,6 @@ CPL::equals (CPL const & other, EqualityOptions opt, boost::function<void (NoteT
        return true;
 }
 
-shared_ptr<xmlpp::Document>
-CPL::make_kdm (
-       shared_ptr<const Signer> signer,
-       shared_ptr<const Certificate> recipient_cert,
-       boost::posix_time::ptime from,
-       boost::posix_time::ptime until,
-       bool interop,
-       MXFMetadata const & mxf_metadata,
-       XMLMetadata const & xml_metadata
-       ) const
-{
-       assert (recipient_cert);
-       
-       shared_ptr<xmlpp::Document> doc (new xmlpp::Document);
-       xmlpp::Element* root = doc->create_root_node ("DCinemaSecurityMessage");
-       root->set_namespace_declaration ("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");
-
-       {
-               xmlpp::Element* authenticated_public = root->add_child("AuthenticatedPublic");
-               authenticated_public->set_attribute("Id", "ID_AuthenticatedPublic");
-               xmlAddID (0, doc->cobj(), (const xmlChar *) "ID_AuthenticatedPublic", authenticated_public->get_attribute("Id")->cobj());
-               
-               authenticated_public->add_child("MessageId")->add_child_text ("urn:uuid:" + make_uuid());
-               /* XXX: this should probably be different if interop == true */
-               authenticated_public->add_child("MessageType")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#kdm-key-type");
-               authenticated_public->add_child("AnnotationText")->add_child_text (mxf_metadata.product_name);
-               authenticated_public->add_child("IssueDate")->add_child_text (xml_metadata.issue_date);
-
-               {
-                       xmlpp::Element* xml_signer = authenticated_public->add_child("Signer");
-                       xml_signer->add_child("X509IssuerName", "ds")->add_child_text (signer->certificates().leaf()->issuer());
-                       xml_signer->add_child("X509SerialNumber", "ds")->add_child_text (signer->certificates().leaf()->serial());
-               }
-
-               {
-                       xmlpp::Element* required_extensions = authenticated_public->add_child("RequiredExtensions");
-
-                       {
-                               xmlpp::Element* kdm_required_extensions = required_extensions->add_child("KDMRequiredExtensions");
-                               kdm_required_extensions->set_namespace_declaration ("http://www.smpte-ra.org/schemas/430-1/2006/KDM");
-                               {
-                                       xmlpp::Element* recipient = kdm_required_extensions->add_child("Recipient");
-                                       {
-                                               xmlpp::Element* serial_element = recipient->add_child("X509IssuerSerial");
-                                               serial_element->add_child("X509IssuerName", "ds")->add_child_text (recipient_cert->issuer());
-                                               serial_element->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:" + _id);
-                               kdm_required_extensions->add_child("ContentTitleText")->add_child_text(_name);
-                               kdm_required_extensions->add_child("ContentAuthenticator")->add_child_text(signer->certificates().leaf()->thumbprint());
-                               kdm_required_extensions->add_child("ContentKeysNotValidBefore")->add_child_text(ptime_to_string (from));
-                               kdm_required_extensions->add_child("ContentKeysNotValidAfter")->add_child_text(ptime_to_string (until));
-
-                               {
-                                       xmlpp::Element* authorized_device_info = kdm_required_extensions->add_child("AuthorizedDeviceInfo");
-                                       authorized_device_info->add_child("DeviceListIdentifier")->add_child_text("urn:uuid:" + make_uuid());
-                                       authorized_device_info->add_child("DeviceListDescription")->add_child_text(recipient_cert->subject());
-                                       {
-                                               xmlpp::Element* device_list = authorized_device_info->add_child("DeviceList");
-                                               device_list->add_child("CertificateThumbprint")->add_child_text(recipient_cert->thumbprint());
-                                       }
-                               }
-
-                               {
-                                       xmlpp::Element* key_id_list = kdm_required_extensions->add_child("KeyIdList");
-                                       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) {
-                                                       mxf->add_typed_key_id (key_id_list);
-                                               }
-                                       }
-                               }
-
-                               {
-                                       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"
-                                               );
-                               }
-                       }
-               }
-                                        
-               authenticated_public->add_child("NonCriticalExtensions");
-       }
-
-       {
-               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());
-
-               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) {
-                               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 = encrypted_key->add_child ("CipherData", "enc");
-
-                       KDMKey kkey (signer, _id, mxf->key_id (), from, until, mxf->key ());
-                       cipher_data->add_child("CipherValue", "enc")->add_child_text (kkey.base64 ());
-               }
-       }
-       
-       {
-               xmlpp::Element* signature = root->add_child("Signature", "ds");
-               
-               {
-                       xmlpp::Element* signed_info = signature->add_child("SignedInfo", "ds");
-                       signed_info->add_child("CanonicalizationMethod", "ds")->set_attribute(
-                               "Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
-                               );
-
-                       if (interop) {
-                               signed_info->add_child("SignatureMethod", "ds")->set_attribute(
-                                       "Algorithm", "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
-                                       );
-                       } else {
-                               signed_info->add_child("SignatureMethod", "ds")->set_attribute(
-                                       "Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
-                                       );
-                       }
-                       
-                       {
-                               xmlpp::Element* reference = signed_info->add_child("Reference", "ds");
-                               reference->set_attribute("URI", "#ID_AuthenticatedPublic");
-                               reference->add_child("DigestMethod", "ds")->set_attribute("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256");
-                               reference->add_child("DigestValue", "ds");
-                       }
-                       
-                       {                               
-                               xmlpp::Element* reference = signed_info->add_child("Reference", "ds");
-                               reference->set_attribute("URI", "#ID_AuthenticatedPrivate");
-                               reference->add_child("DigestMethod", "ds")->set_attribute("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256");
-                               reference->add_child("DigestValue", "ds");
-                       }
-               }
-               
-               signer->add_signature_value (signature, "ds");
-       }
-
-       return doc;
-}
-
 /** @return true if we have any encrypted content */
 bool
 CPL::encrypted () const
index 726fdda264d3d8648d3867c0bd8d4f694d5036d0..ee0da9a01ac7feabf4cd21c6afe19154416dbefe 100644 (file)
--- a/src/cpl.h
+++ b/src/cpl.h
@@ -96,24 +96,6 @@ public:
        void write_to_assetmap (xmlpp::Node *) const;
        void write_to_pkl (xmlpp::Node *) const;
 
-       /** Make a KDM for this CPL.
-        *  @param signer Details of the certificates and private key to sign the KDM with.
-        *  @param recipient_cert The certificate of the projector that this KDM is targeted at.  This will contain the
-        *  projector's public key which is used to encrypt the content keys.
-        *  @param from Time that the KDM should be valid from.
-        *  @param until Time that the KDM should be valid until.
-        *  @param interop true to generate an interop KDM, false for SMPTE.
-        */
-       boost::shared_ptr<xmlpp::Document> make_kdm (
-               boost::shared_ptr<const Signer> signer,
-               boost::shared_ptr<const Certificate> recipient_cert,
-               boost::posix_time::ptime from,
-               boost::posix_time::ptime until,
-               bool interop,
-               MXFMetadata const &,
-               XMLMetadata const &
-               ) const;
-
        void add_kdm (KDM const &);
        
 private:
index b35c34b5483d840972007b495ceb530614955379..9e49fcabea5d4c52525b8322ebf36b929e9451c3 100644 (file)
 */
 
 #include <iomanip>
+#include <algorithm>
 #include <boost/algorithm/string.hpp>
 #include <openssl/rsa.h>
 #include <openssl/pem.h>
 #include <openssl/err.h>
 #include <libcxml/cxml.h>
+#include "AS_DCP.h"
+#include "KM_util.h"
 #include "util.h"
 #include "kdm.h"
 #include "compose.hpp"
 #include "exceptions.h"
+#include "signer.h"
+#include "cpl.h"
+#include "mxf_asset.h"
+#include "xml/kdm_smpte.h"
 
 using std::list;
 using std::string;
@@ -39,6 +46,7 @@ using boost::shared_ptr;
 using namespace libdcp;
 
 KDM::KDM (boost::filesystem::path kdm, boost::filesystem::path private_key)
+       : xml_kdm (new xml::DCinemaSecurityMessage (kdm))
 {
        /* Read the private key */
           
@@ -53,24 +61,15 @@ KDM::KDM (boost::filesystem::path kdm, boost::filesystem::path private_key)
                throw FileError ("could not read RSA private key file", private_key);
        }
 
-       
-       /* Read the KDM, decrypting it */
-
-       cxml::Document f ("DCinemaSecurityMessage");
-       f.read_file (kdm.string ());
-
-       shared_ptr<cxml::Node> authenticated_private = f.node_child ("AuthenticatedPrivate");
-       list<shared_ptr<cxml::Node> > encrypted_keys = authenticated_private->node_children ("EncryptedKey");
+       /* Use it to decrypt the keys */
 
-       for (list<shared_ptr<cxml::Node> >::iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) {
+       list<string> encrypted_keys = xml_kdm->authenticated_private.encrypted_keys;
 
-               /* Get the base-64-encoded cipher value from the KDM */
-               shared_ptr<cxml::Node> cipher_data = (*i)->node_child ("CipherData");
-               shared_ptr<cxml::Node> cipher_value_base64 = cipher_data->node_child ("CipherValue");
+       for (list<string>::iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) {
 
-               /* Decode it from base-64 */
+               /* Decode the base-64-encoded cipher value from the KDM */
                unsigned char cipher_value[256];
-               int const cipher_value_len = base64_decode (cipher_value_base64->content(), cipher_value, sizeof (cipher_value));
+               int const cipher_value_len = base64_decode (*i, cipher_value, sizeof (cipher_value));
 
                /* Decrypt it */
                unsigned char* decrypted = new unsigned char[RSA_size(rsa)];
@@ -87,17 +86,81 @@ KDM::KDM (boost::filesystem::path kdm, boost::filesystem::path private_key)
        RSA_free (rsa);
 }
 
+KDM::KDM (
+       shared_ptr<const CPL> cpl, shared_ptr<const Signer> signer, shared_ptr<const Certificate> recipient_cert,
+       boost::posix_time::ptime not_valid_before, boost::posix_time::ptime not_valid_after,
+       MXFMetadata mxf_metadata, XMLMetadata xml_metadata
+       )
+       : xml_kdm (new xml::DCinemaSecurityMessage)
+{
+       xml::AuthenticatedPublic& apu = xml_kdm->authenticated_public;
+
+       /* AuthenticatedPublic */
+       
+       apu.message_type = "urn:uuid:" + make_uuid ();
+       apu.annotation_text = mxf_metadata.product_name;
+       apu.issue_date = xml_metadata.issue_date;
+       apu.signer.x509_issuer_name = signer->certificates().leaf()->issuer ();
+       apu.signer.x509_serial_number = signer->certificates().leaf()->serial ();
+       apu.recipient.x509_issuer_serial.x509_issuer_name = recipient_cert->issuer ();
+       apu.recipient.x509_issuer_serial.x509_serial_number = recipient_cert->serial ();
+       apu.recipient.x509_subject_name = recipient_cert->subject ();
+       apu.composition_playlist_id = "urn:uuid:" + cpl->id ();
+       apu.content_title_text = cpl->name ();
+       apu.content_keys_not_valid_before = ptime_to_string (not_valid_before);
+       apu.content_keys_not_valid_after = ptime_to_string (not_valid_after);
+       apu.authorized_device_info.device_list_identifier = "urn:uuid:" + make_uuid ();
+       apu.authorized_device_info.device_list_description = recipient_cert->subject ();
+       apu.authorized_device_info.device_list.push_back (recipient_cert->thumbprint ());
+       
+       list<shared_ptr<const Asset> > assets = cpl->assets ();
+       for (list<shared_ptr<const Asset> >::iterator i = assets.begin(); i != assets.end(); ++i) {
+               /* XXX: non-MXF assets? */
+               shared_ptr<const MXFAsset> mxf = boost::dynamic_pointer_cast<const MXFAsset> (*i);
+               if (mxf) {
+                       apu.key_id_list.push_back (xml::TypedKeyId (mxf->key_type(), "urn:uuid:" + mxf->key_id()));
+               }
+       }
+
+       apu.forensic_mark_flag_list.push_back ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable");
+       apu.forensic_mark_flag_list.push_back ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable");
+
+       /* AuthenticatedPrivate */
+
+       for (list<shared_ptr<const Asset> >::iterator i = assets.begin(); i != assets.end(); ++i) {
+               /* XXX: non-MXF assets? */
+               shared_ptr<const MXFAsset> mxf = boost::dynamic_pointer_cast<const MXFAsset> (*i);
+               if (mxf) {
+                       xml_kdm->authenticated_private.encrypted_keys.push_back (
+                               KDMKey (signer, cpl->id (), mxf->key_id (), not_valid_before, not_valid_after, mxf->key().get()).base64 ()
+                               );
+               }
+       }
+
+       /* Signature */
+
+       shared_ptr<xmlpp::Document> doc = xml_kdm->as_xml ();
+       shared_ptr<cxml::Node> root (new cxml::Node (doc->get_root_node ()));
+       xmlpp::Node* signature = root->node_child("Signature")->node();
+       signer->add_signature_value (signature, "ds");
+       xml_kdm->signature = xml::Signature (shared_ptr<cxml::Node> (new cxml::Node (signature)));
+}
+
+void
+KDM::as_xml (boost::filesystem::path path) const
+{
+       shared_ptr<xmlpp::Document> doc = xml_kdm->as_xml ();
+       doc->write_to_file_formatted (path.string(), "UTF-8");
+}
+
 KDMKey::KDMKey (shared_ptr<const Signer> signer, string cpl_id, string key_id, boost::posix_time::ptime from, boost::posix_time::ptime until, Key key)
        : _cpl_id (cpl_id)
-       , _key_id (key_id),
+       , _key_id (key_id)
        , _not_valid_before (ptime_to_string (from))
        , _not_valid_after (ptime_to_string (until))
        , _key (key)
 {
-       /* Magic value specified by SMPTE S430-1-2006 */
-       _structure_id[] = { 0xf1, 0xdc, 0x12, 0x44, 0x60, 0x16, 0x9a, 0x0e, 0x85, 0xbc, 0x30, 0x06, 0x42, 0xf8, 0x66, 0xab };
-       
-       base64_decode (signer->certificates()->leaf()->thumbprint (), _signer_thumbprint, 20);
+       base64_decode (signer->certificates().leaf()->thumbprint (), _signer_thumbprint, 20);
 }
 
 KDMKey::KDMKey (uint8_t const * raw, int len)
@@ -131,6 +194,34 @@ KDMKey::KDMKey (uint8_t const * raw, int len)
        }
 }
 
+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::base64 () const
@@ -144,10 +235,10 @@ KDMKey::base64 () const
        put (&p, structure_id, 16);
        put (&p, _signer_thumbprint, 20);
        put_uuid (&p, _cpl_id);
-       put (&p, _key_type, 4);
+       put (&p, _key_type);
        put_uuid (&p, _key_id);
-       put (&p, _not_valid_before.c_str(), 25);
-       put (&p, _not_valid_after.c_str(), 25);
+       put (&p, _not_valid_before);
+       put (&p, _not_valid_after);
        put (&p, _key.value(), ASDCP::KeyLen);
 
        /* Lazy overallocation */
@@ -168,7 +259,7 @@ KDMKey::get (uint8_t const ** p, int N) const
 }
 
 void
-KDMKey::get (uint8_t const * o, uint8_t const ** p, int N) const
+KDMKey::get (uint8_t* o, uint8_t const ** p, int N) const
 {
        memcpy (o, *p, N);
        *p += N;
@@ -197,13 +288,23 @@ KDMKey::put (uint8_t ** d, uint8_t const * s, int N) const
        (*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 (id.remove (id.begin(), id.end(), "-"));
+       id.erase (std::remove (id.begin(), id.end(), '-'));
        for (int i = 0; i < 32; i += 2) {
                stringstream s;
                s << id[i] << id[i + 1];
-               s >> *d++;
+               int h;
+               s >> h;
+               **d = h;
+               (*d)++;
        }
 }
index 253589fed63f2dcd906f8d607ea34b23b25987bc..90ed3b9cf4f74cdab180641ad4a98e754a1ca3cc 100644 (file)
--- a/src/kdm.h
+++ b/src/kdm.h
 #define LIBDCP_KDM_H
 
 #include <boost/filesystem.hpp>
+#include <boost/scoped_ptr.hpp>
 #include "key.h"
+#include "metadata.h"
 
 namespace libdcp {
 
+namespace xml {
+       class DCinemaSecurityMessage;
+};
+
+class Signer;
+class Certificate;
+class CPL;
+
 /** A single key for encrypting or decrypting an MXF.  One or more of these
  *  are delivered in a KDM.
  */
-class KDMKey : public boost::noncopyable
+class KDMKey
 {
 public:
        KDMKey (uint8_t const *, int);
 
+       KDMKey (
+               boost::shared_ptr<const Signer> signer,
+               std::string cpl_id, std::string key_id, boost::posix_time::ptime from, boost::posix_time::ptime until, Key key
+               );
+       
+       KDMKey (KDMKey const &);
+
+       KDMKey& operator= (KDMKey const &);
+
+       std::string cpl_id () const {
+               return _cpl_id;
+       }
+       
+       std::string key_id () const {
+               return _key_id;
+       }
+
+       std::string not_valid_before () const {
+               return _not_valid_before;
+       }
+
+       std::string not_valid_after () const {
+               return _not_valid_after;
+       }
+
        Key key () const {
                return _key;
        }
+
+       std::string base64 () const;
        
 private:
        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;
        
        uint8_t _signer_thumbprint[20];
        std::string _cpl_id;
-       std::string _not_valid_before;
-       std::string _not_valid_after;
        std::string _key_type;
        std::string _key_id;
+       std::string _not_valid_before;
+       std::string _not_valid_after;
        Key _key;
 };
 
@@ -58,12 +96,23 @@ class KDM
 public:
        KDM (boost::filesystem::path, boost::filesystem::path);
 
+       KDM (
+               boost::shared_ptr<const CPL> cpl, boost::shared_ptr<const Signer>, boost::shared_ptr<const Certificate> recipient_cert,
+               boost::posix_time::ptime not_valid_before, boost::posix_time::ptime not_valid_after,
+               MXFMetadata mxf_metadata, XMLMetadata xml_metadata
+               );
+
        std::list<KDMKey> keys () const {
                return _keys;
        }
 
+       void as_xml (boost::filesystem::path) const;
+
 private:
+       std::string _message_id;
        std::list<KDMKey> _keys;
+
+       boost::shared_ptr<xml::DCinemaSecurityMessage> xml_kdm;
 };
 
 
index bcfe60854500fe731c2f67acac54e23f543bc37b..98770d862b65031143473ffcb2450e400e4269eb 100644 (file)
@@ -116,14 +116,6 @@ MXFAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, boost::fun
        return true;
 }
 
-void
-MXFAsset::add_typed_key_id (xmlpp::Element* parent) const
-{
-       xmlpp::Element* typed_key_id = parent->add_child("TypedKeyId");
-       typed_key_id->add_child("KeyType")->add_child_text(key_type ());
-       typed_key_id->add_child("KeyId")->add_child_text("urn:uuid:" + _key_id);
-}
-
 void
 MXFAsset::write_to_cpl (xmlpp::Element* node, bool interop) const
 {
index 97ce44b291247d2c9e37f7db3df44e58a3862e76..e351db57f7e5d2953620088c35a665dc465c37c2 100644 (file)
@@ -76,8 +76,6 @@ public:
         */
        void fill_writer_info (ASDCP::WriterInfo* w, std::string uuid, bool interop, MXFMetadata const & metadata);
 
-       void add_typed_key_id (xmlpp::Element *) const;
-
        bool encrypted () const {
                return !_key_id.empty ();
        }
@@ -99,9 +97,10 @@ public:
        ASDCP::AESEncContext* encryption_context () const {
                return _encryption_context;
        }
+
+       virtual std::string key_type () const = 0;
        
 protected:
-       virtual std::string key_type () const = 0;
        virtual std::string cpl_node_name () const = 0;
        virtual std::pair<std::string, std::string> cpl_node_attribute (bool) const {
                return std::make_pair ("", "");
index 999ee59465e7874d7b22bb328844f9fade6958e6..9aebd39dacf9a119b73c4fa37908050d2d6eab48 100644 (file)
@@ -21,6 +21,7 @@
 #include <xmlsec/xmldsig.h>
 #include <xmlsec/dl.h>
 #include <xmlsec/app.h>
+#include <libcxml/cxml.h>
 #include "signer.h"
 #include "exceptions.h"
 
@@ -37,50 +38,45 @@ Signer::sign (xmlpp::Element* parent, bool interop) const
 
        xmlpp::Element* signature = parent->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");
-
-               if (interop) {
-                       signed_info->add_child("SignatureMethod", "dsig")->set_attribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#rsa-sha1");
-               } else {
-                       signed_info->add_child("SignatureMethod", "dsig")->set_attribute("Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
-               }
-               
-               {
-                       xmlpp::Element* reference = signed_info->add_child("Reference", "dsig");
-                       reference->set_attribute ("URI", "");
-                       {
-                               xmlpp::Element* transforms = reference->add_child("Transforms", "dsig");
-                               transforms->add_child("Transform", "dsig")->set_attribute (
-                                       "Algorithm", "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
-                                       );
-                       }
-                       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");
-               }
+       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");
+       
+       if (interop) {
+               signed_info->add_child("SignatureMethod", "dsig")->set_attribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#rsa-sha1");
+       } else {
+               signed_info->add_child("SignatureMethod", "dsig")->set_attribute("Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
        }
        
+       xmlpp::Element* reference = signed_info->add_child("Reference", "dsig");
+       reference->set_attribute ("URI", "");
+
+       xmlpp::Element* transforms = reference->add_child("Transforms", "dsig");
+       transforms->add_child("Transform", "dsig")->set_attribute (
+               "Algorithm", "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
+               );
+
+       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");
+       signature->add_child("KeyInfo", "dsig");
        add_signature_value (signature, "dsig");
 }
 
 
-/** Sign an XML node.  This function takes a certificate chain (to prove that the sender is bona fide) and
- *  a private key with which to sign the node.
+/** Sign an XML node.
  *
  *  @param parent Node to sign.
- *  @param certificates Certificate chain for the signer.
- *  @param signer_key Filename of the private key of the signer.
  *  @param ns Namespace to use for the signature XML nodes.
  */
 void
-Signer::add_signature_value (xmlpp::Element* parent, string ns) const
+Signer::add_signature_value (xmlpp::Node* parent, string ns) const
 {
-       parent->add_child("SignatureValue", ns);
+       cxml::Node cp (parent);
+       xmlpp::Node* key_info = cp.node_child("KeyInfo")->node ();
 
-       /* Add the certificate chain to a KeyInfo child node of parent */
-       xmlpp::Element* key_info = parent->add_child("KeyInfo", ns);
+       /* Add the certificate chain to the KeyInfo child node of parent */
        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 = key_info->add_child("X509Data", ns);
index a4d10b2afe798da713da4f8689725bafd8540ac0..5ae7cf510907753bdf03f0932c1d154836760613 100644 (file)
@@ -31,7 +31,7 @@ public:
        {}
 
        void sign (xmlpp::Element* parent, bool interop) const;
-       void add_signature_value (xmlpp::Element* parent, std::string ns) const;
+       void add_signature_value (xmlpp::Node* parent, std::string ns) const;
 
        CertificateChain const & certificates () const {
                return _certificates;
index 8634125c4a5f2d4ef7b03379c79e2cd967b670f2..31263b77c70ba1f6d35c89a847379728c7d1b17d 100644 (file)
@@ -1,3 +1,5 @@
+from waflib import TaskGen
+
 def build(bld):
     if bld.env.STATIC:
         obj = bld(features = 'cxx cxxstlib')
@@ -43,6 +45,7 @@ def build(bld):
                  parse/cpl.cc
                  parse/pkl.cc
                  parse/subtitle.cc
+                 xml/kdm_smpte.cc
                  """
 
     headers = """
@@ -83,4 +86,3 @@ def build(bld):
     bld.install_files('${PREFIX}/include/libdcp', headers)
     if bld.env.STATIC:
         bld.install_files('${PREFIX}/lib', 'libdcp.a')
-
index 310eb8a4c5ee53071a23dbc0df8b16d06ba36afd..00d8400555794ccdaf82426e6343bb16b541d7a2 100644 (file)
 
 */
 
+#include <libxml/parser.h>
 #include <libxml++/libxml++.h>
 #include <libcxml/cxml.h>
 #include "kdm_smpte.h"
+#include "../exceptions.h"
+#include "../util.h"
 
 using std::list;
 using std::string;
 using boost::shared_ptr;
 using namespace libdcp::xml;
 
+Writer::Writer ()
+       : document (new xmlpp::Document)
+{
+
+}
+
 DCinemaSecurityMessage::DCinemaSecurityMessage (boost::filesystem::path file)
 {
        cxml::Document f ("DCinemaSecurityMessage");
@@ -38,19 +47,26 @@ DCinemaSecurityMessage::DCinemaSecurityMessage (boost::filesystem::path file)
        f.done ();
 }
 
-void
-DCinemaSecurityMessage::as_xml (boost::filesystem::path file) const
+shared_ptr<xmlpp::Document>
+DCinemaSecurityMessage::as_xml () const
 {
-       xmlpp::Document doc;
-       xmlpp::Element* root = doc.create_root_node ("DCinemaSecurityMessage", "http://www.smpte-ra.org/schemas/430-3/2006/ETM");
+       Writer writer;
+       
+       xmlpp::Element* root = writer.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");
 
-       authenticated_public.as_xml (root->add_child ("AuthenticatedPublic"));
-       authenticated_private.as_xml (root->add_child ("AuthenticatedPrivate"));
-       signature.as_xml (root->add_child ("Signature", "ds"));
+       authenticated_public.as_xml (writer, root->add_child ("AuthenticatedPublic"));
+       authenticated_private.as_xml (writer, root->add_child ("AuthenticatedPrivate"));
+       signature.as_xml (writer, root->add_child ("Signature", "ds"));
+
+       return writer.document;
+}
+
+AuthenticatedPublic::AuthenticatedPublic ()
+       : message_id ("urn:uuid:" + make_uuid ())
+{
 
-       doc.write_to_file_formatted (file.string (), "UTF-8");
 }
 
 AuthenticatedPublic::AuthenticatedPublic (shared_ptr<const cxml::Node> node)
@@ -74,9 +90,12 @@ AuthenticatedPublic::AuthenticatedPublic (shared_ptr<const cxml::Node> node)
                key_id_list.push_back (TypedKeyId (*i));
        }
 
-       list<shared_ptr<cxml::Node> > fmf = c->node_child("ForensicMarkFlagList")->node_children("ForensicMarkFlag");
-       for (list<shared_ptr<cxml::Node> >::iterator i = fmf.begin(); i != fmf.end(); ++i) {
-               forensic_mark_flag_list.push_back ((*i)->content ());
+       shared_ptr<cxml::Node> fmfl = c->optional_node_child("ForensicMarkFlagList");
+       if (fmfl) {
+               list<shared_ptr<cxml::Node> > fmf = fmfl->node_children("ForensicMarkFlag");
+               for (list<shared_ptr<cxml::Node> >::iterator i = fmf.begin(); i != fmf.end(); ++i) {
+                       forensic_mark_flag_list.push_back ((*i)->content ());
+               }
        }
 
        node->ignore_child ("NonCriticalExtensions");
@@ -84,9 +103,9 @@ AuthenticatedPublic::AuthenticatedPublic (shared_ptr<const cxml::Node> node)
 }
 
 void
-AuthenticatedPublic::as_xml (xmlpp::Element* node) const
+AuthenticatedPublic::as_xml (Writer& writer, xmlpp::Element* node) const
 {
-       node->set_attribute ("Id", "ID_AuthenticatedPublic");
+       writer.references["ID_AuthenticatedPublic"] = node->set_attribute ("Id", "ID_AuthenticatedPublic");
        
        node->add_child("MessageId")->add_child_text (message_id);
        node->add_child("MessageType")->add_child_text (message_type);
@@ -195,9 +214,9 @@ AuthenticatedPrivate::AuthenticatedPrivate (shared_ptr<const cxml::Node> node)
 }
 
 void
-AuthenticatedPrivate::as_xml (xmlpp::Element* node) const
+AuthenticatedPrivate::as_xml (Writer& writer, xmlpp::Element* node) const
 {
-       node->set_attribute ("Id", "ID_AuthenticatedPrivate");
+       writer.references["ID_AuthenticatedPrivate"] = node->set_attribute ("Id", "ID_AuthenticatedPrivate");
        
        for (list<string>::const_iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) {
                xmlpp::Element* encrypted_key = node->add_child ("EncryptedKey", "enc");
@@ -215,7 +234,13 @@ Signature::Signature (shared_ptr<const cxml::Node> node)
 {
        list<shared_ptr<cxml::Node> > refs = node->node_child("SignedInfo")->node_children ("Reference");
        for (list<shared_ptr<cxml::Node> >::const_iterator i = refs.begin(); i != refs.end(); ++i) {
-               signed_info.push_back (Reference (*i));
+               if ((*i)->string_attribute("URI") == "#ID_AuthenticatedPublic") {
+                       authenticated_public = Reference (*i);
+               } else if ((*i)->string_attribute("URI") == "#ID_AuthenticatedPrivate") {
+                       authenticated_private = Reference (*i);
+               } else {
+                       throw XMLError ("unrecognised reference URI");
+               }
        }
 
        list<shared_ptr<cxml::Node> > data = node->node_child("KeyInfo")->node_children ("X509Data");
@@ -226,15 +251,22 @@ Signature::Signature (shared_ptr<const cxml::Node> node)
        node->done ();
 }
 
+Signature::Signature ()
+       : authenticated_public ("#ID_AuthenticatedPublic")
+       , authenticated_private ("#ID_AuthenticatedPrivate")
+{
+
+}
+
 void
-Signature::as_xml (xmlpp::Element* node) const
+Signature::as_xml (Writer& writer, xmlpp::Element* node) const
 {
        xmlpp::Element* si = node->add_child ("SignedInfo", "ds");
        si->add_child ("CanonicalizationMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments");
        si->add_child ("SignatureMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
-       for (list<Reference>::const_iterator i = signed_info.begin(); i != signed_info.end(); ++i) {
-               i->as_xml (si);
-       }
+
+       authenticated_public.as_xml (writer, si);
+       authenticated_private.as_xml (writer, si);
        
        node->add_child("SignatureValue", "ds")->add_child_text (signature_value);
 
@@ -254,12 +286,16 @@ Reference::Reference (shared_ptr<const cxml::Node> node)
 }
 
 void
-Reference::as_xml (xmlpp::Element* node) const
+Reference::as_xml (Writer& writer, xmlpp::Element* node) const
 {
        xmlpp::Element* reference = node->add_child ("Reference", "ds");
        reference->set_attribute ("URI", uri);
        reference->add_child("DigestMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256");
        reference->add_child("DigestValue", "ds")->add_child_text (digest_value);
+
+       if (!uri.empty ()) {
+               xmlAddID (0, writer.document->cobj(), (const xmlChar *) uri.substr(1).c_str(), writer.references[uri.substr(1)]->cobj ());
+       }
 }
 
 X509Data::X509Data (shared_ptr<const cxml::Node> node)
index 989d05de514f8122d4808079ecaaa4c4bdd887b8..2e94a368264b1f56a0f56ea6d7cd9b662ae6cae6 100644 (file)
 #include <boost/filesystem.hpp>
 
 namespace libdcp {
-namespace xml {        
+namespace xml {
+
+class Writer
+{
+public:
+       Writer ();
+       
+       boost::shared_ptr<xmlpp::Document> document;
+       std::map<std::string, xmlpp::Attribute *> references;
+};
 
 class Signer
 {
@@ -69,6 +78,12 @@ class TypedKeyId
 {
 public:
        TypedKeyId () {}
+
+       TypedKeyId (std::string t, std::string i)
+               : key_type (t)
+               , key_id (i)
+       {}
+       
        TypedKeyId (boost::shared_ptr<const cxml::Node>);
        
        void as_xml (xmlpp::Element *) const;
@@ -80,10 +95,10 @@ public:
 class AuthenticatedPublic
 {
 public:
-       AuthenticatedPublic () {}
+       AuthenticatedPublic ();
        AuthenticatedPublic (boost::shared_ptr<const cxml::Node>);
 
-       void as_xml (xmlpp::Element *) const;
+       void as_xml (Writer &, xmlpp::Element *) const;
        
        std::string message_id;
        std::string message_type;
@@ -106,7 +121,7 @@ public:
        AuthenticatedPrivate () {}
        AuthenticatedPrivate (boost::shared_ptr<const cxml::Node>);
        
-       void as_xml (xmlpp::Element *) const;
+       void as_xml (Writer &, xmlpp::Element *) const;
        
        std::list<std::string> encrypted_keys;
 };
@@ -127,9 +142,13 @@ class Reference
 {
 public:
        Reference () {}
+       Reference (std::string u)
+               : uri (u)
+       {}
+                 
        Reference (boost::shared_ptr<const cxml::Node>);
 
-       void as_xml (xmlpp::Element *) const;
+       void as_xml (Writer& writer, xmlpp::Element *) const;
        
        std::string uri;
        std::string digest_value;
@@ -138,12 +157,13 @@ public:
 class Signature
 {
 public:
-       Signature () {}
+       Signature ();
        Signature (boost::shared_ptr<const cxml::Node>);
 
-       void as_xml (xmlpp::Element *) const;
-       
-       std::list<Reference> signed_info;
+       void as_xml (Writer &, xmlpp::Element *) const;
+
+       Reference authenticated_public;
+       Reference authenticated_private;
        std::string signature_value;
        std::list<X509Data> key_info;
 };
@@ -151,9 +171,10 @@ public:
 class DCinemaSecurityMessage
 {
 public:
+       DCinemaSecurityMessage () {}
        DCinemaSecurityMessage (boost::filesystem::path);
 
-       void as_xml (boost::filesystem::path) const;
+       boost::shared_ptr<xmlpp::Document> as_xml () const;
        
        AuthenticatedPublic authenticated_public;
        AuthenticatedPrivate authenticated_private;
index 6e4a61d66df470d2dd5c3301e485c59a4a41c187..961d281bee64bd2f18d95c95fab02e0dac2642f9 100644 (file)
@@ -17,6 +17,8 @@
 
 */
 
+#include "kdm.h"
+
 /* Load a certificate chain from build/test/data/ *.pem and then build
    an encrypted DCP and a KDM using it.
 */
@@ -87,17 +89,16 @@ BOOST_AUTO_TEST_CASE (encryption)
 
        d.write_xml (false, xml_metadata, signer);
 
-       shared_ptr<xmlpp::Document> kdm = cpl->make_kdm (
+       libdcp::KDM kdm (
+               cpl,
                signer,
                signer->certificates().leaf(),
-               key,
                boost::posix_time::time_from_string ("2013-01-01 00:00:00"),
                boost::posix_time::time_from_string ("2013-01-08 00:00:00"),
-               false,
                mxf_metadata,
                xml_metadata
                );
 
-       kdm->write_to_file_formatted ("build/test/bar.kdm.xml", "UTF-8");
+       kdm.as_xml ("build/test/bar.kdm.xml");
        system ("xmllint --path schema --nonet --noout --schema schema/SMPTE-430-1-2006-Amd-1-2009-KDM.xsd build/test/bar.kdm.xml");
 }
index 5d0710749d7efe5709e23ed0fe97115757767dd1..d9ba3745e09ccd6f873db5ce8615fbfa42756274 100644 (file)
@@ -51,7 +51,8 @@ BOOST_AUTO_TEST_CASE (kdm_passthrough_test)
                "test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml"
                );
 
-       kdm.as_xml ("build/kdm.xml");
+       shared_ptr<xmlpp::Document> doc = kdm.as_xml ();
+       doc->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"
                );