From 725c1f0ead60e73e9dbc17337c416ac4d2720c55 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 24 Sep 2013 18:54:09 +0100 Subject: [PATCH] Various KDM work. --- src/cpl.cc | 160 ---------------------------------------- src/cpl.h | 18 ----- src/kdm.cc | 151 ++++++++++++++++++++++++++++++------- src/kdm.h | 55 +++++++++++++- src/mxf_asset.cc | 8 -- src/mxf_asset.h | 5 +- src/signer.cc | 58 +++++++-------- src/signer.h | 2 +- src/wscript | 4 +- src/xml/kdm_smpte.cc | 78 ++++++++++++++------ src/xml/kdm_smpte.h | 41 +++++++--- test/encryption_test.cc | 9 ++- test/kdm_test.cc | 3 +- 13 files changed, 306 insertions(+), 286 deletions(-) diff --git a/src/cpl.cc b/src/cpl.cc index 072304b8..4c4d2e75 100644 --- a/src/cpl.cc +++ b/src/cpl.cc @@ -344,166 +344,6 @@ CPL::equals (CPL const & other, EqualityOptions opt, boost::function -CPL::make_kdm ( - shared_ptr signer, - shared_ptr 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 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 > 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()); - - 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) { - 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 diff --git a/src/cpl.h b/src/cpl.h index 726fdda2..ee0da9a0 100644 --- 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 make_kdm ( - boost::shared_ptr signer, - boost::shared_ptr 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: diff --git a/src/kdm.cc b/src/kdm.cc index b35c34b5..9e49fcab 100644 --- a/src/kdm.cc +++ b/src/kdm.cc @@ -18,15 +18,22 @@ */ #include +#include #include #include #include #include #include +#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 authenticated_private = f.node_child ("AuthenticatedPrivate"); - list > encrypted_keys = authenticated_private->node_children ("EncryptedKey"); + /* Use it to decrypt the keys */ - for (list >::iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) { + list encrypted_keys = xml_kdm->authenticated_private.encrypted_keys; - /* Get the base-64-encoded cipher value from the KDM */ - shared_ptr cipher_data = (*i)->node_child ("CipherData"); - shared_ptr cipher_value_base64 = cipher_data->node_child ("CipherValue"); + for (list::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 cpl, shared_ptr signer, shared_ptr 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 > assets = cpl->assets (); + for (list >::iterator i = assets.begin(); i != assets.end(); ++i) { + /* XXX: non-MXF assets? */ + shared_ptr mxf = boost::dynamic_pointer_cast (*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 >::iterator i = assets.begin(); i != assets.end(); ++i) { + /* XXX: non-MXF assets? */ + shared_ptr mxf = boost::dynamic_pointer_cast (*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 doc = xml_kdm->as_xml (); + shared_ptr 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 (new cxml::Node (signature))); +} + +void +KDM::as_xml (boost::filesystem::path path) const +{ + shared_ptr doc = xml_kdm->as_xml (); + doc->write_to_file_formatted (path.string(), "UTF-8"); +} + KDMKey::KDMKey (shared_ptr 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)++; } } diff --git a/src/kdm.h b/src/kdm.h index 253589fe..90ed3b9c 100644 --- a/src/kdm.h +++ b/src/kdm.h @@ -21,35 +21,73 @@ #define LIBDCP_KDM_H #include +#include #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 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 cpl, boost::shared_ptr, boost::shared_ptr recipient_cert, + boost::posix_time::ptime not_valid_before, boost::posix_time::ptime not_valid_after, + MXFMetadata mxf_metadata, XMLMetadata xml_metadata + ); + std::list keys () const { return _keys; } + void as_xml (boost::filesystem::path) const; + private: + std::string _message_id; std::list _keys; + + boost::shared_ptr xml_kdm; }; diff --git a/src/mxf_asset.cc b/src/mxf_asset.cc index bcfe6085..98770d86 100644 --- a/src/mxf_asset.cc +++ b/src/mxf_asset.cc @@ -116,14 +116,6 @@ MXFAsset::equals (shared_ptr 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 { diff --git a/src/mxf_asset.h b/src/mxf_asset.h index 97ce44b2..e351db57 100644 --- a/src/mxf_asset.h +++ b/src/mxf_asset.h @@ -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 cpl_node_attribute (bool) const { return std::make_pair ("", ""); diff --git a/src/signer.cc b/src/signer.cc index 999ee594..9aebd39d 100644 --- a/src/signer.cc +++ b/src/signer.cc @@ -21,6 +21,7 @@ #include #include #include +#include #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 > c = _certificates.leaf_to_root (); for (list >::iterator i = c.begin(); i != c.end(); ++i) { xmlpp::Element* data = key_info->add_child("X509Data", ns); diff --git a/src/signer.h b/src/signer.h index a4d10b2a..5ae7cf51 100644 --- a/src/signer.h +++ b/src/signer.h @@ -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; diff --git a/src/wscript b/src/wscript index 8634125c..31263b77 100644 --- a/src/wscript +++ b/src/wscript @@ -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') - diff --git a/src/xml/kdm_smpte.cc b/src/xml/kdm_smpte.cc index 310eb8a4..00d84005 100644 --- a/src/xml/kdm_smpte.cc +++ b/src/xml/kdm_smpte.cc @@ -17,15 +17,24 @@ */ +#include #include #include #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 +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 node) @@ -74,9 +90,12 @@ AuthenticatedPublic::AuthenticatedPublic (shared_ptr node) key_id_list.push_back (TypedKeyId (*i)); } - list > fmf = c->node_child("ForensicMarkFlagList")->node_children("ForensicMarkFlag"); - for (list >::iterator i = fmf.begin(); i != fmf.end(); ++i) { - forensic_mark_flag_list.push_back ((*i)->content ()); + shared_ptr fmfl = c->optional_node_child("ForensicMarkFlagList"); + if (fmfl) { + list > fmf = fmfl->node_children("ForensicMarkFlag"); + for (list >::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 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 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::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 node) { list > refs = node->node_child("SignedInfo")->node_children ("Reference"); for (list >::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 > data = node->node_child("KeyInfo")->node_children ("X509Data"); @@ -226,15 +251,22 @@ Signature::Signature (shared_ptr 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::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 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 node) diff --git a/src/xml/kdm_smpte.h b/src/xml/kdm_smpte.h index 989d05de..2e94a368 100644 --- a/src/xml/kdm_smpte.h +++ b/src/xml/kdm_smpte.h @@ -26,7 +26,16 @@ #include namespace libdcp { -namespace xml { +namespace xml { + +class Writer +{ +public: + Writer (); + + boost::shared_ptr document; + std::map 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); void as_xml (xmlpp::Element *) const; @@ -80,10 +95,10 @@ public: class AuthenticatedPublic { public: - AuthenticatedPublic () {} + AuthenticatedPublic (); AuthenticatedPublic (boost::shared_ptr); - 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); - void as_xml (xmlpp::Element *) const; + void as_xml (Writer &, xmlpp::Element *) const; std::list encrypted_keys; }; @@ -127,9 +142,13 @@ class Reference { public: Reference () {} + Reference (std::string u) + : uri (u) + {} + Reference (boost::shared_ptr); - 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); - void as_xml (xmlpp::Element *) const; - - std::list signed_info; + void as_xml (Writer &, xmlpp::Element *) const; + + Reference authenticated_public; + Reference authenticated_private; std::string signature_value; std::list 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 as_xml () const; AuthenticatedPublic authenticated_public; AuthenticatedPrivate authenticated_private; diff --git a/test/encryption_test.cc b/test/encryption_test.cc index 6e4a61d6..961d281b 100644 --- a/test/encryption_test.cc +++ b/test/encryption_test.cc @@ -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 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"); } diff --git a/test/kdm_test.cc b/test/kdm_test.cc index 5d071074..d9ba3745 100644 --- a/test/kdm_test.cc +++ b/test/kdm_test.cc @@ -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 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" ); -- 2.30.2