diff options
| -rw-r--r-- | doc/make_kdm.sh | 7 | ||||
| -rw-r--r-- | src/certificates.cc | 8 | ||||
| -rw-r--r-- | src/certificates.h | 1 | ||||
| -rw-r--r-- | src/dcp.cc | 4 | ||||
| -rw-r--r-- | src/dcp.h | 4 | ||||
| -rw-r--r-- | src/kdm.cc | 95 | ||||
| -rw-r--r-- | src/kdm.h | 11 | ||||
| -rw-r--r-- | src/signer.cc | 2 | ||||
| -rw-r--r-- | src/sound_asset.h | 4 | ||||
| -rw-r--r-- | src/util.cc | 1 | ||||
| -rw-r--r-- | src/wscript | 2 | ||||
| -rw-r--r-- | src/xml/kdm_smpte.h | 23 | ||||
| -rw-r--r-- | test/encryption_test.cc | 9 | ||||
| -rw-r--r-- | test/kdm_key_test.cc | 49 | ||||
| -rw-r--r-- | test/round_trip_test.cc | 13 | ||||
| -rw-r--r-- | test/wscript | 1 |
16 files changed, 198 insertions, 36 deletions
diff --git a/doc/make_kdm.sh b/doc/make_kdm.sh index 085fd2fd..60ea001f 100644 --- a/doc/make_kdm.sh +++ b/doc/make_kdm.sh @@ -1,9 +1,10 @@ #!/bin/bash CINEMASLIDES=/home/carl/src/digital_cinema_tools/cinemaslides -CPL=/home/carl/src/libdcp-test/TONEPLATES-SMPTE-ENCRYPTED_TST_F_XX-XX_ITL-TD_51-XX_2K_WOE_20111001_WOE_OV/cpl_eece17de-77e8-4a55-9347-b6bab5724b9f_.xml +#CPL=/home/carl/src/libdcp-test/TONEPLATES-SMPTE-ENCRYPTED_TST_F_XX-XX_ITL-TD_51-XX_2K_WOE_20111001_WOE_OV/cpl_eece17de-77e8-4a55-9347-b6bab5724b9f_.xml +CPL=/home/carl/DCP/kdmtest/KDMTEST_TST-1_F_51_2K_20130928/fbb1d2ce-fcd9-4765-8f01-2afcad274506_cpl.xml ls $CPL -export CINEMACERTSTORE=chain -$CINEMASLIDES -v debug --kdm --cpl $CPL --start 2013-07-06T20:04:58+00:00 --end 2023-07-02T20:04:56+00:00 --target target.pem --keysdir content_keys --formulation t1 +export CINEMACERTSTORE=/home/carl/.config/dcpomatic/crypt +$CINEMASLIDES -v debug --kdm --cpl $CPL --start 2013-09-28T01:41:51+00:00 --end 2014-09-28T01:41:51+00:00 --target target.pem --keysdir content_keys diff --git a/src/certificates.cc b/src/certificates.cc index 4c41ebba..f23dbc5d 100644 --- a/src/certificates.cc +++ b/src/certificates.cc @@ -194,6 +194,14 @@ Certificate::subject () const } string +Certificate::common_name () const +{ + assert (_certificate); + + return get_name_part (X509_get_subject_name (_certificate), NID_commonName); +} + +string Certificate::serial () const { assert (_certificate); diff --git a/src/certificates.h b/src/certificates.h index 198773f1..2bf8d0db 100644 --- a/src/certificates.h +++ b/src/certificates.h @@ -58,6 +58,7 @@ public: std::string issuer () const; std::string serial () const; std::string subject () const; + std::string common_name () const; /** @return RSA public key from this Certificate. Caller must not free the returned value. */ RSA* public_key () const; @@ -67,7 +67,7 @@ DCP::DCP (boost::filesystem::path directory) } void -DCP::write_xml (bool interop, XMLMetadata const & metadata, shared_ptr<Signer> signer) const +DCP::write_xml (bool interop, XMLMetadata const & metadata, shared_ptr<const Signer> signer) const { for (list<shared_ptr<CPL> >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) { (*i)->write_xml (interop, metadata, signer); @@ -81,7 +81,7 @@ DCP::write_xml (bool interop, XMLMetadata const & metadata, shared_ptr<Signer> s } std::string -DCP::write_pkl (string pkl_uuid, bool interop, XMLMetadata const & metadata, shared_ptr<Signer> signer) const +DCP::write_pkl (string pkl_uuid, bool interop, XMLMetadata const & metadata, shared_ptr<const Signer> signer) const { assert (!_cpls.empty ()); @@ -83,7 +83,7 @@ public: /** Write the required XML files to the directory that was * passed into the constructor. */ - void write_xml (bool interop, XMLMetadata const &, boost::shared_ptr<Signer> signer = boost::shared_ptr<Signer> ()) const; + void write_xml (bool interop, XMLMetadata const &, boost::shared_ptr<const Signer> signer = boost::shared_ptr<const Signer> ()) const; /** Compare this DCP with another, according to various options. * @param other DCP to compare this one to. @@ -126,7 +126,7 @@ private: /** Write the PKL file. * @param pkl_uuid UUID to use. */ - std::string write_pkl (std::string pkl_uuid, bool, XMLMetadata const &, boost::shared_ptr<Signer>) const; + std::string write_pkl (std::string pkl_uuid, bool, XMLMetadata const &, boost::shared_ptr<const Signer>) const; /** Write the VOLINDEX file */ void write_volindex () const; @@ -46,7 +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)) + : _xml_kdm (new xml::DCinemaSecurityMessage (kdm)) { /* Read the private key */ @@ -63,7 +63,7 @@ KDM::KDM (boost::filesystem::path kdm, boost::filesystem::path private_key) /* Use it to decrypt the keys */ - list<string> encrypted_keys = xml_kdm->authenticated_private.encrypted_keys; + list<string> encrypted_keys = _xml_kdm->authenticated_private.encrypted_keys; for (list<string>::iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) { @@ -91,9 +91,9 @@ KDM::KDM ( boost::posix_time::ptime not_valid_before, boost::posix_time::ptime not_valid_after, string annotation_text, string issue_date ) - : xml_kdm (new xml::DCinemaSecurityMessage) + : _xml_kdm (new xml::DCinemaSecurityMessage) { - xml::AuthenticatedPublic& apu = xml_kdm->authenticated_public; + xml::AuthenticatedPublic& apu = _xml_kdm->authenticated_public; /* AuthenticatedPublic */ @@ -107,13 +107,18 @@ KDM::KDM ( 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_authenticator = signer->certificates().leaf()->thumbprint (); 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 (); + string n = recipient_cert->common_name (); + if (n.find (".") != string::npos) { + n = n.substr (n.find (".") + 1); + } + apu.authorized_device_info.device_list_description = n; 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? */ @@ -132,36 +137,63 @@ KDM::KDM ( /* 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 ( + KDMKey kkey ( signer, cpl->id (), mxf->key_type (), mxf->key_id (), not_valid_before, not_valid_after, mxf->key().get() - ).encrypted_base64 (recipient_cert) ); + + _keys.push_back (kkey); + _xml_kdm->authenticated_private.encrypted_keys.push_back (kkey.encrypted_base64 (recipient_cert)); } } /* Signature */ - shared_ptr<xmlpp::Document> doc = xml_kdm->as_xml (); + 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))); + _xml_kdm->signature = xml::Signature (shared_ptr<cxml::Node> (new cxml::Node (signature))); } +KDM::KDM (KDM const & other) + : _keys (other._keys) + , _xml_kdm (new xml::DCinemaSecurityMessage (*other._xml_kdm.get())) +{ + +} + +KDM & +KDM::operator= (KDM const & other) +{ + if (this == &other) { + return *this; + } + + _keys = other._keys; + _xml_kdm.reset (new xml::DCinemaSecurityMessage (*other._xml_kdm.get ())); + + return *this; +} + 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"); + shared_ptr<xmlpp::Document> doc = _xml_kdm->as_xml (); + /* This must *not* be the _formatted version, otherwise the signature + will be wrong. + */ + doc->write_to_file (path.string(), "UTF-8"); } string KDM::as_xml () const { - shared_ptr<xmlpp::Document> doc = xml_kdm->as_xml (); - return doc->write_to_string_formatted ("UTF-8"); + shared_ptr<xmlpp::Document> doc = _xml_kdm->as_xml (); + /* This must *not* be the _formatted version, otherwise the signature + will be wrong. + */ + return doc->write_to_string ("UTF-8"); } KDMKey::KDMKey ( @@ -225,7 +257,7 @@ 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; @@ -269,7 +301,17 @@ KDMKey::encrypted_base64 (shared_ptr<const Certificate> recipient_cert) const /* Lazy overallocation */ char out[encrypted_len * 2]; - return Kumu::base64encode (encrypted, encrypted_len, out, encrypted_len * 2); + Kumu::base64encode (encrypted, encrypted_len, out, encrypted_len * 2); + int const N = strlen (out); + stringstream lines; + for (int i = 0; i < N; ++i) { + if (i > 0 && (i % 64) == 0) { + lines << "\n"; + } + lines << out[i]; + } + + return lines.str (); } string @@ -329,8 +371,25 @@ KDMKey::put_uuid (uint8_t ** d, string id) const stringstream s; s << id[i] << id[i + 1]; int h; - s >> h; + s >> hex >> h; **d = h; (*d)++; } } + +bool +libdcp::operator== (libdcp::KDMKey const & a, libdcp::KDMKey const & b) +{ + if (memcmp (a._signer_thumbprint, b._signer_thumbprint, 20) != 0) { + return false; + } + + return ( + a._cpl_id == b._cpl_id && + a._key_type == b._key_type && + a._key_id == b._key_id && + a._not_valid_before == b._not_valid_before && + a._not_valid_after == b._not_valid_after && + a._key == b._key + ); +} @@ -30,6 +30,8 @@ #include "key.h" #include "metadata.h" +class kdm_key_test; + namespace libdcp { namespace xml { @@ -114,12 +116,16 @@ public: std::string encrypted_base64 (boost::shared_ptr<const Certificate> cert) const; private: + friend class ::kdm_key_test; + 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; + + friend bool operator== (KDMKey const &, KDMKey const &); uint8_t _signer_thumbprint[20]; std::string _cpl_id; @@ -164,6 +170,9 @@ public: std::string annotation_text, std::string issue_date ); + KDM (KDM const &); + KDM & operator= (KDM const &); + /** @return The unencrypted content keys from this KDM */ std::list<KDMKey> keys () const { return _keys; @@ -184,7 +193,7 @@ private: std::list<KDMKey> _keys; /** The KDM's contents, mapped 1:1-ish to the XML */ - boost::shared_ptr<xml::DCinemaSecurityMessage> xml_kdm; + boost::shared_ptr<xml::DCinemaSecurityMessage> _xml_kdm; }; diff --git a/src/signer.cc b/src/signer.cc index 9aebd39d..f15f5325 100644 --- a/src/signer.cc +++ b/src/signer.cc @@ -21,12 +21,14 @@ #include <xmlsec/xmldsig.h> #include <xmlsec/dl.h> #include <xmlsec/app.h> +#include <xmlsec/crypto.h> #include <libcxml/cxml.h> #include "signer.h" #include "exceptions.h" using std::string; using std::list; +using std::cout; using boost::shared_ptr; using namespace libdcp; diff --git a/src/sound_asset.h b/src/sound_asset.h index d41b72d5..67d3e445 100644 --- a/src/sound_asset.h +++ b/src/sound_asset.h @@ -88,6 +88,10 @@ public: return _channels; } + void set_sampling_rate (int s) { + _sampling_rate = s; + } + int sampling_rate () const { return _sampling_rate; } diff --git a/src/util.cc b/src/util.cc index 4bcc61fb..11052df6 100644 --- a/src/util.cc +++ b/src/util.cc @@ -33,6 +33,7 @@ #include <xmlsec/xmldsig.h> #include <xmlsec/dl.h> #include <xmlsec/app.h> +#include <xmlsec/crypto.h> #include "KM_util.h" #include "KM_fileio.h" #include "AS_DCP.h" diff --git a/src/wscript b/src/wscript index d7e118c7..1d694ec3 100644 --- a/src/wscript +++ b/src/wscript @@ -67,6 +67,7 @@ def build(bld): lut.h lut_cache.h metadata.h + mono_picture_asset.h mono_picture_frame.h mxf_asset.h picture_asset.h @@ -80,6 +81,7 @@ def build(bld): sound_asset.h sound_frame.h srgb_linearised_gamma_lut.h + stereo_picture_asset.h stereo_picture_frame.h subtitle_asset.h types.h diff --git a/src/xml/kdm_smpte.h b/src/xml/kdm_smpte.h index 0139876f..32a297f8 100644 --- a/src/xml/kdm_smpte.h +++ b/src/xml/kdm_smpte.h @@ -170,6 +170,7 @@ public: c = c->node_child ("KDMRequiredExtensions"); recipient = Recipient (c->node_child ("Recipient")); composition_playlist_id = c->string_child ("CompositionPlaylistId"); + content_authenticator = c->optional_string_child ("ContentAuthenticator"); content_title_text = c->string_child ("ContentTitleText"); content_keys_not_valid_before = c->string_child ("ContentKeysNotValidBefore"); content_keys_not_valid_after = c->string_child ("ContentKeysNotValidAfter"); @@ -209,6 +210,9 @@ public: recipient.as_xml (kdm_required_extensions->add_child ("Recipient")); kdm_required_extensions->add_child("CompositionPlaylistId")->add_child_text (composition_playlist_id); + if (content_authenticator) { + kdm_required_extensions->add_child("ContentAuthenticator")->add_child_text (content_authenticator.get ()); + } kdm_required_extensions->add_child("ContentTitleText")->add_child_text (content_title_text); kdm_required_extensions->add_child("ContentKeysNotValidBefore")->add_child_text (content_keys_not_valid_before); kdm_required_extensions->add_child("ContentKeysNotValidAfter")->add_child_text (content_keys_not_valid_after); @@ -234,6 +238,7 @@ public: Signer signer; Recipient recipient; std::string composition_playlist_id; + boost::optional<std::string> content_authenticator; std::string content_title_text; std::string content_keys_not_valid_before; std::string content_keys_not_valid_after; @@ -312,16 +317,12 @@ public: node->done (); } - void as_xml (Writer& writer, xmlpp::Element* node) const + void as_xml (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 ()); - } } std::string uri; @@ -359,14 +360,14 @@ public: node->done (); } - void as_xml (Writer& writer, xmlpp::Element* node) const + void as_xml (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"); - authenticated_public.as_xml (writer, si); - authenticated_private.as_xml (writer, si); + authenticated_public.as_xml (si); + authenticated_private.as_xml (si); node->add_child("SignatureValue", "ds")->add_child_text (signature_value); @@ -408,7 +409,11 @@ public: 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")); + signature.as_xml (root->add_child ("Signature", "ds")); + + for (std::map<std::string, xmlpp::Attribute*>::const_iterator i = writer.references.begin(); i != writer.references.end(); ++i) { + xmlAddID (0, writer.document->cobj(), (const xmlChar *) i->first.c_str(), i->second->cobj ()); + } return writer.document; } diff --git a/test/encryption_test.cc b/test/encryption_test.cc index e2bf9698..c079acbe 100644 --- a/test/encryption_test.cc +++ b/test/encryption_test.cc @@ -66,7 +66,7 @@ BOOST_AUTO_TEST_CASE (encryption) shared_ptr<libdcp::Signer> signer ( new libdcp::Signer ( chain, - "test/data/signer.key" + "build/test/signer/leaf.key" ) ); @@ -111,4 +111,11 @@ BOOST_AUTO_TEST_CASE (encryption) 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"); + system ("xmlsec1 verify " + "--pubkey-cert-pem build/test/signer/leaf.signed.pem " + "--trusted-pem build/test/signer/intermediate.signed.pem " + "--trusted-pem build/test/signer/ca.self-signed.pem " + "--id-attr:Id http://www.smpte-ra.org/schemas/430-3/2006/ETM:AuthenticatedPublic " + "--id-attr:Id http://www.smpte-ra.org/schemas/430-3/2006/ETM:AuthenticatedPrivate " + "build/test/bar.kdm.xml"); } diff --git a/test/kdm_key_test.cc b/test/kdm_key_test.cc new file mode 100644 index 00000000..05b85312 --- /dev/null +++ b/test/kdm_key_test.cc @@ -0,0 +1,49 @@ +/* + Copyright (C) 2013 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <boost/test/unit_test.hpp> +#include "kdm.h" + +BOOST_AUTO_TEST_CASE (kdm_key_test) +{ + uint8_t foo[138]; + memset (foo, 0, 138); + libdcp::KDMKey kkey (foo, 138); + + uint8_t* raw = new uint8_t[16]; + uint8_t* p = raw; + kkey.put_uuid (&p, "5d51e8a1-b2a5-4da6-9b66-4615c3609440"); + BOOST_CHECK_EQUAL (raw[0], 0x5d); + BOOST_CHECK_EQUAL (raw[1], 0x51); + BOOST_CHECK_EQUAL (raw[2], 0xe8); + BOOST_CHECK_EQUAL (raw[3], 0xa1); + BOOST_CHECK_EQUAL (raw[4], 0xb2); + BOOST_CHECK_EQUAL (raw[5], 0xa5); + BOOST_CHECK_EQUAL (raw[6], 0x4d); + BOOST_CHECK_EQUAL (raw[7], 0xa6); + BOOST_CHECK_EQUAL (raw[8], 0x9b); + BOOST_CHECK_EQUAL (raw[9], 0x66); + BOOST_CHECK_EQUAL (raw[10], 0x46); + BOOST_CHECK_EQUAL (raw[11], 0x15); + BOOST_CHECK_EQUAL (raw[12], 0xc3); + BOOST_CHECK_EQUAL (raw[13], 0x60); + BOOST_CHECK_EQUAL (raw[14], 0x94); + BOOST_CHECK_EQUAL (raw[15], 0x40); + delete[] raw; +} diff --git a/test/round_trip_test.cc b/test/round_trip_test.cc index 2747a71f..fe720142 100644 --- a/test/round_trip_test.cc +++ b/test/round_trip_test.cc @@ -31,6 +31,7 @@ #include "argb_frame.h" #include "signer_chain.h" +using std::list; using boost::shared_ptr; /* Build an encrypted picture MXF and a KDM for it and check that the KDM can be decrypted */ @@ -86,6 +87,18 @@ BOOST_AUTO_TEST_CASE (round_trip_test) /* Reload the KDM, using our private key to decrypt it */ libdcp::KDM kdm_B (kdm_file, "build/test/signer/leaf.key"); + /* Check that the decrypted KDMKeys are the same as the ones we started with */ + BOOST_CHECK_EQUAL (kdm_A.keys().size(), kdm_B.keys().size()); + list<libdcp::KDMKey> keys_A = kdm_A.keys (); + list<libdcp::KDMKey> keys_B = kdm_B.keys (); + list<libdcp::KDMKey>::const_iterator i = keys_A.begin(); + list<libdcp::KDMKey>::const_iterator j = keys_B.begin(); + while (i != keys_A.end ()) { + BOOST_CHECK (*i == *j); + ++i; + ++j; + } + /* Reload the picture MXF */ shared_ptr<libdcp::MonoPictureAsset> asset_B ( new libdcp::MonoPictureAsset (work_dir, "video.mxf") diff --git a/test/wscript b/test/wscript index 1b2a1055..6b8b78d0 100644 --- a/test/wscript +++ b/test/wscript @@ -22,6 +22,7 @@ def build(bld): obj.use = 'libdcp' obj.source = """ test.cc + kdm_key_test.cc certificates_test.cc dcp_test.cc encryption_test.cc |
