From fd8a665cbb0a9a44995f88567747e2379a6f4098 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sat, 5 Jan 2013 22:02:24 +0000 Subject: [PATCH] Various probably quite untidy progress on KDMs. --- run-tests.sh | 2 +- src/asset.cc | 1 + src/certificates.cc | 2 +- src/certificates.h | 4 + src/dcp.cc | 238 ++++++++++++++++++++++++++----------------- src/dcp.h | 13 ++- src/mxf_asset.cc | 9 ++ src/mxf_asset.h | 3 + src/picture_asset.cc | 6 ++ src/picture_asset.h | 3 + src/sound_asset.cc | 6 ++ src/sound_asset.h | 2 + src/util.cc | 110 ++++++++++++++++++++ src/util.h | 11 +- test/tests.cc | 3 + 15 files changed, 315 insertions(+), 98 deletions(-) diff --git a/run-tests.sh b/run-tests.sh index e08dc86d..671da919 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -e # # Runs our test suite, which (amongst other things) diff --git a/src/asset.cc b/src/asset.cc index ac8f8b8e..62fdddf6 100644 --- a/src/asset.cc +++ b/src/asset.cc @@ -91,3 +91,4 @@ Asset::digest () const return _digest; } + diff --git a/src/certificates.cc b/src/certificates.cc index ba0ebaae..ac7e20f7 100644 --- a/src/certificates.cc +++ b/src/certificates.cc @@ -4,6 +4,7 @@ #include #include #include +#include #include "certificates.h" #include "exceptions.h" @@ -104,7 +105,6 @@ Certificate::serial () const return st; } - /** @param filename Text file of PEM-format certificates, * in the order: diff --git a/src/certificates.h b/src/certificates.h index a1a409ac..1c342acb 100644 --- a/src/certificates.h +++ b/src/certificates.h @@ -9,6 +9,10 @@ class certificates; +namespace xmlpp { + class Element; +} + namespace libdcp { class Certificate : public boost::noncopyable diff --git a/src/dcp.cc b/src/dcp.cc index 2ece0eac..a282c33e 100644 --- a/src/dcp.cc +++ b/src/dcp.cc @@ -471,7 +471,7 @@ CPL::write_xml (bool encrypted, CertificateChain const & certificates, string co } if (encrypted) { - DCP::sign (cpl, certificates, signer_key); + sign (cpl, certificates, signer_key); } doc.write_to_file_formatted (p.string(), "UTF-8"); @@ -480,98 +480,6 @@ CPL::write_xml (bool encrypted, CertificateChain const & certificates, string co _length = boost::filesystem::file_size (p.string ()); } -void -DCP::sign (xmlpp::Element* parent, CertificateChain const & certificates, string const & signer_key) -{ - xmlpp::Element* signer = parent->add_child("Signer"); - - { - xmlpp::Element* data = signer->add_child("X509Data", "dsig"); - { - xmlpp::Element* serial = data->add_child("X509IssuerSerial", "dsig"); - serial->add_child("X509IssuerName", "dsig")->add_child_text ( - Certificate::name_for_xml (certificates.leaf()->issuer()) - ); - serial->add_child("X509SerialNumber", "dsig")->add_child_text ( - certificates.leaf()->serial() - ); - } - data->add_child("X509SubjectName", "dsig")->add_child_text ( - Certificate::name_for_xml (certificates.leaf()->subject()) - ); - } - - 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"); - 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"); - - xmlpp::Element* key_info = signature->add_child("KeyInfo", "dsig"); - list > c = certificates.leaf_to_root (); - for (list >::iterator i = c.begin(); i != c.end(); ++i) { - xmlpp::Element* data = key_info->add_child("X509Data", "dsig"); - - { - 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()); - } - - data->add_child("X509Certificate", "dsig")->add_child_text((*i)->certificate()); - } - - 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); -} - void CPL::write_to_pkl (xmlpp::Element* p) const { @@ -660,3 +568,147 @@ CPL::equals (CPL const & other, EqualityOptions opt, list& notes) const return true; } + +shared_ptr +CPL::make_kdm (CertificateChain const & certificates, string const & signer_key, shared_ptr recipient_cert) const +{ + shared_ptr 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()); + 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(Metadata::instance()->product_name); + authenticated_public->add_child("IssueDate")->add_child_text(Metadata::instance()->issue_date); + + { + xmlpp::Element* signer = authenticated_public->add_child("Signer"); + signer->add_child("X509IssuerName", "ds")->add_child_text ( + Certificate::name_for_xml (recipient_cert->issuer()) + ); + signer->add_child("X509SerialNumber", "ds")->add_child_text ( + recipient_cert->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 ( + Certificate::name_for_xml (recipient_cert->issuer()) + ); + serial_element->add_child("X509SerialNumber", "ds")->add_child_text ( + recipient_cert->serial() + ); + } + + recipient->add_child("X509SubjectName")->add_child_text (Certificate::name_for_xml (recipient_cert->subject())); + } + + kdm_required_extensions->add_child("CompositionPlaylistId")->add_child_text("XXX"); + kdm_required_extensions->add_child("ContentTitleText")->add_child_text("XXX"); + kdm_required_extensions->add_child("ContentAuthenticator")->add_child_text("XXX"); + kdm_required_extensions->add_child("ContentKeysNotValidBefore")->add_child_text("XXX"); + kdm_required_extensions->add_child("ContentKeysNotValidAfter")->add_child_text("XXX"); + + { + 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("XXX"); + } + } + + { + xmlpp::Element* key_id_list = kdm_required_extensions->add_child("KeyIdList"); + list > a = assets(); + for (list >::iterator i = a.begin(); i != a.end(); ++i) { + /* XXX: non-MXF assets? */ + shared_ptr mxf = boost::dynamic_pointer_cast (*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()); + { + 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"); + } + } + + /* XXX: x2 one for each mxf? */ + + { + 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" + ); + 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"); + } + } + + add_signature_value (signature, certificates, signer_key, "ds"); + } + + return doc; +} diff --git a/src/dcp.h b/src/dcp.h index 0ee4a8fc..96b28090 100644 --- a/src/dcp.h +++ b/src/dcp.h @@ -32,6 +32,7 @@ #include "certificates.h" namespace xmlpp { + class Document; class Element; } @@ -89,6 +90,8 @@ public: void write_xml (bool, CertificateChain const &, std::string const &) const; void write_to_assetmap (std::ostream& s) const; void write_to_pkl (xmlpp::Element* p) const; + + boost::shared_ptr make_kdm (CertificateChain const &, std::string const &, boost::shared_ptr) const; private: std::string _directory; @@ -159,17 +162,23 @@ public: _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. */ boost::signals2::signal Progress; - static void sign (xmlpp::Element* parent, CertificateChain const & certificates, std::string const & signer_key); - private: /** Write the PKL file. diff --git a/src/mxf_asset.cc b/src/mxf_asset.cc index d229331b..6ba42d75 100644 --- a/src/mxf_asset.cc +++ b/src/mxf_asset.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include "AS_DCP.h" #include "KM_prng.h" #include "KM_util.h" @@ -128,3 +129,11 @@ MXFAsset::length () const { return _length; } + +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); +} diff --git a/src/mxf_asset.h b/src/mxf_asset.h index e45ab2d7..f68edc61 100644 --- a/src/mxf_asset.h +++ b/src/mxf_asset.h @@ -51,8 +51,11 @@ public: virtual bool equals (boost::shared_ptr other, EqualityOptions opt, std::list& notes) const; int length () const; + void add_typed_key_id (xmlpp::Element *) const; protected: + virtual std::string key_type () const = 0; + /** Fill in a ADSCP::WriteInfo struct. * @param w struct to fill in. */ diff --git a/src/picture_asset.cc b/src/picture_asset.cc index 295afc15..f783bb39 100644 --- a/src/picture_asset.cc +++ b/src/picture_asset.cc @@ -391,3 +391,9 @@ StereoPictureAsset::get_frame (int n) const { return shared_ptr (new StereoPictureFrame (path().string(), n + _entry_point)); } + +string +PictureAsset::key_type () const +{ + return "MDIK"; +} diff --git a/src/picture_asset.h b/src/picture_asset.h index 15764dab..96bf5659 100644 --- a/src/picture_asset.h +++ b/src/picture_asset.h @@ -64,6 +64,9 @@ protected: int _width; /** picture height in pixels */ int _height; + +private: + std::string key_type () const; }; /** A 2D (monoscopic) picture asset */ diff --git a/src/sound_asset.cc b/src/sound_asset.cc index b71d218f..77267cbd 100644 --- a/src/sound_asset.cc +++ b/src/sound_asset.cc @@ -285,3 +285,9 @@ SoundAsset::get_frame (int n) const { return shared_ptr (new SoundFrame (path().string(), n + _entry_point)); } + +string +SoundAsset::key_type () const +{ + return "MDAK"; +} diff --git a/src/sound_asset.h b/src/sound_asset.h index 0a6c8f4e..67115606 100644 --- a/src/sound_asset.h +++ b/src/sound_asset.h @@ -104,6 +104,8 @@ public: } private: + std::string key_type () const; + void construct (boost::function get_path); std::string path_from_channel (Channel channel, std::vector const & files); diff --git a/src/util.cc b/src/util.cc index 323fb1e4..ea6f6c0d 100644 --- a/src/util.cc +++ b/src/util.cc @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include @@ -38,12 +40,14 @@ #include "types.h" #include "argb_frame.h" #include "lut.h" +#include "certificates.h" using std::string; using std::cout; using std::stringstream; using std::min; using std::max; +using std::list; using boost::shared_ptr; using namespace libdcp; @@ -293,3 +297,109 @@ libdcp::init () throw MiscError ("could not initialise xmlsec-crypto"); } } + +void +libdcp::add_signature_value (xmlpp::Element* parent, CertificateChain const & certificates, string const & signer_key, string const & ns) +{ + parent->add_child("SignatureValue", ns); + + xmlpp::Element* key_info = parent->add_child("KeyInfo", ns); + list > c = certificates.leaf_to_root (); + for (list >::iterator i = c.begin(); i != c.end(); ++i) { + xmlpp::Element* data = key_info->add_child("X509Data", ns); + + { + xmlpp::Element* serial = data->add_child("X509IssuerSerial", ns); + serial->add_child("X509IssuerName", ns)->add_child_text( + Certificate::name_for_xml ((*i)->issuer()) + ); + serial->add_child("X509SerialNumber", ns)->add_child_text((*i)->serial()); + } + + data->add_child("X509Certificate", ns)->add_child_text((*i)->certificate()); + } + + 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, parent->cobj()) < 0) { + throw MiscError ("could not sign"); + } + + xmlSecDSigCtxFinalize (&signature_context); + xmlSecKeysMngrDestroy (keys_manager); +} + + +void +libdcp::add_signer (xmlpp::Element* parent, CertificateChain const & certificates, string const & ns) +{ + xmlpp::Element* signer = parent->add_child("Signer"); + + { + xmlpp::Element* data = signer->add_child("X509Data", ns); + + { + xmlpp::Element* serial_element = data->add_child("X509IssuerSerial", ns); + serial_element->add_child("X509IssuerName", ns)->add_child_text ( + Certificate::name_for_xml (certificates.leaf()->issuer()) + ); + serial_element->add_child("X509SerialNumber", ns)->add_child_text ( + certificates.leaf()->serial() + ); + } + + data->add_child("X509SubjectName", ns)->add_child_text (Certificate::name_for_xml (certificates.leaf()->subject())); + } +} + +void +libdcp::sign (xmlpp::Element* parent, CertificateChain const & certificates, string const & signer_key) +{ + add_signer (parent, certificates, "dsig"); + + 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"); + 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"); + } + } + + add_signature_value (signature, certificates, signer_key, "dsig"); +} + diff --git a/src/util.h b/src/util.h index 6426eb8d..3b60e0c7 100644 --- a/src/util.h +++ b/src/util.h @@ -27,9 +27,14 @@ #include #include "types.h" +namespace xmlpp { + class Element; +} + namespace libdcp { -class ARGBFrame; +class ARGBFrame; +class CertificateChain; /** Create a UUID. * @return UUID. @@ -55,4 +60,8 @@ extern boost::shared_ptr xyz_to_rgb (opj_image_t* xyz_frame); extern void init (); +extern void sign (xmlpp::Element* parent, CertificateChain const & certificates, std::string const & signer_key); +extern void add_signature_value (xmlpp::Element* parent, CertificateChain const & certificates, std::string const & signer_key, std::string const & ns); +extern void add_signer (xmlpp::Element* parent, CertificateChain const & certificates, std::string const & ns); + } diff --git a/test/tests.cc b/test/tests.cc index 3d0e4254..7c3144a1 100644 --- a/test/tests.cc +++ b/test/tests.cc @@ -19,6 +19,7 @@ #include #include +#include #include "KM_prng.h" #include "dcp.h" #include "util.h" @@ -632,6 +633,8 @@ BOOST_AUTO_TEST_CASE (encryption) d.add_cpl (cpl); d.write_xml (); + + cpl->make_kdm(d.certificates(), d.signer_key(), d.certificates().leaf())->write_to_file_formatted ("build/test/bar.kdm.xml", "UTF-8"); } BOOST_AUTO_TEST_CASE (certificates) -- 2.30.2