From e563727ee7b72881ee163db9b777559c8ceb5074 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 25 Sep 2013 17:19:59 +0100 Subject: [PATCH] Add round-trip KDM test. Fix various bugs in KDM generation. Some string -> path. --- examples/make_dcp.cc | 2 +- src/asset.cc | 2 +- src/asset.h | 4 +- src/kdm.cc | 18 ++++++-- src/kdm.h | 2 +- src/mxf_asset.cc | 4 +- src/mxf_asset.h | 4 +- src/picture_asset.cc | 30 +++++++------ src/picture_asset.h | 24 +++++------ src/sound_asset.cc | 20 ++++----- src/sound_asset.h | 16 +++---- test/error_test.cc | 2 +- test/round_trip_test.cc | 96 +++++++++++++++++++++++++++++++++++++++++ test/test.cc | 4 +- test/test.h | 4 +- test/wscript | 1 + 16 files changed, 171 insertions(+), 62 deletions(-) create mode 100644 test/round_trip_test.cc diff --git a/examples/make_dcp.cc b/examples/make_dcp.cc index 9222caaa..7e839867 100644 --- a/examples/make_dcp.cc +++ b/examples/make_dcp.cc @@ -89,7 +89,7 @@ main () Left surround Right surround */ - std::vector sound_files; + std::vector sound_files; sound_files.push_back ("examples/sine_440_-12dB.wav"); sound_files.push_back ("examples/sine_880_-12dB.wav"); diff --git a/src/asset.cc b/src/asset.cc index 06b87953..6e4901b5 100644 --- a/src/asset.cc +++ b/src/asset.cc @@ -37,7 +37,7 @@ using namespace std; using namespace boost; using namespace libdcp; -Asset::Asset (string directory, string file_name, int edit_rate, int intrinsic_duration) +Asset::Asset (boost::filesystem::path directory, string file_name, int edit_rate, int intrinsic_duration) : _directory (directory) , _file_name (file_name) , _uuid (make_uuid ()) diff --git a/src/asset.h b/src/asset.h index c6ff7e83..940ed548 100644 --- a/src/asset.h +++ b/src/asset.h @@ -53,7 +53,7 @@ public: * @param directory Directory where our XML or MXF file is. * @param file_name Name of our file within directory, or empty to make one up based on UUID. */ - Asset (std::string directory, std::string file_name = "", int edit_rate = 0, int intrinsic_duration = 0); + Asset (boost::filesystem::path directory, std::string file_name = "", int edit_rate = 0, int intrinsic_duration = 0); virtual ~Asset() {} @@ -130,7 +130,7 @@ protected: std::string digest () const; /** Directory that our MXF or XML file is in */ - std::string _directory; + boost::filesystem::path _directory; /** Name of our MXF or XML file */ std::string _file_name; /** Our UUID */ diff --git a/src/kdm.cc b/src/kdm.cc index 8f244500..fc0aaa75 100644 --- a/src/kdm.cc +++ b/src/kdm.cc @@ -133,7 +133,10 @@ KDM::KDM ( 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()).encrypted_base64 (recipient_cert) + KDMKey ( + signer, cpl->id (), mxf->key_type (), mxf->key_id (), + not_valid_before, not_valid_after, mxf->key().get() + ).encrypted_base64 (recipient_cert) ); } } @@ -161,8 +164,11 @@ KDM::as_xml () const return doc->write_to_string_formatted ("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) +KDMKey::KDMKey ( + shared_ptr signer, string cpl_id, string key_type, string key_id, boost::posix_time::ptime from, boost::posix_time::ptime until, Key key + ) : _cpl_id (cpl_id) + , _key_type (key_type) , _key_id (key_id) , _not_valid_before (ptime_to_string (from)) , _not_valid_after (ptime_to_string (until)) @@ -234,6 +240,10 @@ KDMKey::operator= (KDMKey const & other) string KDMKey::encrypted_base64 (shared_ptr recipient_cert) const { + assert (_key_type.length() == 4); + assert (_not_valid_before.length() == 25); + assert (_not_valid_after.length() == 25); + /* XXX: SMPTE only */ uint8_t block[138]; uint8_t* p = block; @@ -252,14 +262,14 @@ KDMKey::encrypted_base64 (shared_ptr recipient_cert) const /* Encrypt using the projector's public key */ RSA* rsa = recipient_cert->public_key (); unsigned char encrypted[RSA_size(rsa)]; - int const encrypted_len = RSA_public_encrypt (138, block, encrypted, rsa, RSA_PKCS1_OAEP_PADDING); + int const encrypted_len = RSA_public_encrypt (p - block, block, encrypted, rsa, RSA_PKCS1_OAEP_PADDING); if (encrypted_len == -1) { throw MiscError (String::compose ("Could not encrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0))); } /* Lazy overallocation */ char out[encrypted_len * 2]; - return Kumu::base64encode (block, 138, out, 138 * 2); + return Kumu::base64encode (encrypted, encrypted_len, out, encrypted_len * 2); } string diff --git a/src/kdm.h b/src/kdm.h index ba17ee6b..32269aed 100644 --- a/src/kdm.h +++ b/src/kdm.h @@ -46,7 +46,7 @@ public: 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 + std::string cpl_id, std::string key_type, std::string key_id, boost::posix_time::ptime from, boost::posix_time::ptime until, Key key ); KDMKey (KDMKey const &); diff --git a/src/mxf_asset.cc b/src/mxf_asset.cc index 98770d86..956d38f9 100644 --- a/src/mxf_asset.cc +++ b/src/mxf_asset.cc @@ -43,7 +43,7 @@ using boost::lexical_cast; using boost::dynamic_pointer_cast; using namespace libdcp; -MXFAsset::MXFAsset (string directory, string file_name) +MXFAsset::MXFAsset (boost::filesystem::path directory, string file_name) : Asset (directory, file_name) , _progress (0) , _encryption_context (0) @@ -52,7 +52,7 @@ MXFAsset::MXFAsset (string directory, string file_name) } -MXFAsset::MXFAsset (string directory, string file_name, boost::signals2::signal* progress, int edit_rate, int intrinsic_duration) +MXFAsset::MXFAsset (boost::filesystem::path directory, string file_name, boost::signals2::signal* progress, int edit_rate, int intrinsic_duration) : Asset (directory, file_name, edit_rate, intrinsic_duration) , _progress (progress) , _encryption_context (0) diff --git a/src/mxf_asset.h b/src/mxf_asset.h index e351db57..e566c32c 100644 --- a/src/mxf_asset.h +++ b/src/mxf_asset.h @@ -44,7 +44,7 @@ public: * @param directory Directory where MXF file is. * @param file_name Name of MXF file. */ - MXFAsset (std::string directory, std::string file_name); + MXFAsset (boost::filesystem::path directory, std::string file_name); /** Construct an MXFAsset. * This class will not write anything to disk in this constructor, but subclasses may. @@ -56,7 +56,7 @@ public: * @param intrinsic_duration Duration of the whole asset in frames. */ MXFAsset ( - std::string directory, + boost::filesystem::path directory, std::string file_name, boost::signals2::signal* progress, int edit_rate, diff --git a/src/picture_asset.cc b/src/picture_asset.cc index bc38a3f9..78d7576d 100644 --- a/src/picture_asset.cc +++ b/src/picture_asset.cc @@ -54,14 +54,16 @@ using boost::dynamic_pointer_cast; using boost::lexical_cast; using namespace libdcp; -PictureAsset::PictureAsset (string directory, string mxf_name, boost::signals2::signal* progress, int fps, int intrinsic_duration, Size size) +PictureAsset::PictureAsset ( + boost::filesystem::path directory, string mxf_name, boost::signals2::signal* progress, int fps, int intrinsic_duration, Size size + ) : MXFAsset (directory, mxf_name, progress, fps, intrinsic_duration) , _size (size) { } -PictureAsset::PictureAsset (string directory, string mxf_name) +PictureAsset::PictureAsset (boost::filesystem::path directory, string mxf_name) : MXFAsset (directory, mxf_name) { @@ -189,8 +191,8 @@ PictureAsset::equals (shared_ptr other, EqualityOptions opt, boost: MonoPictureAsset::MonoPictureAsset ( - boost::function get_path, - string directory, + boost::function get_path, + boost::filesystem::path directory, string mxf_name, boost::signals2::signal* progress, int fps, @@ -205,8 +207,8 @@ MonoPictureAsset::MonoPictureAsset ( } MonoPictureAsset::MonoPictureAsset ( - vector const & files, - string directory, + vector const & files, + boost::filesystem::path directory, string mxf_name, boost::signals2::signal* progress, int fps, @@ -220,13 +222,13 @@ MonoPictureAsset::MonoPictureAsset ( construct (boost::bind (&MonoPictureAsset::path_from_list, this, _1, files), interop, metadata); } -MonoPictureAsset::MonoPictureAsset (string directory, string mxf_name, int fps, Size size) +MonoPictureAsset::MonoPictureAsset (boost::filesystem::path directory, string mxf_name, int fps, Size size) : PictureAsset (directory, mxf_name, 0, fps, 0, size) { } -MonoPictureAsset::MonoPictureAsset (string directory, string mxf_name) +MonoPictureAsset::MonoPictureAsset (boost::filesystem::path directory, string mxf_name) : PictureAsset (directory, mxf_name) { ASDCP::JP2K::MXFReader reader; @@ -247,7 +249,7 @@ MonoPictureAsset::MonoPictureAsset (string directory, string mxf_name) } void -MonoPictureAsset::construct (boost::function get_path, bool interop, MXFMetadata const & metadata) +MonoPictureAsset::construct (boost::function get_path, bool interop, MXFMetadata const & metadata) { ASDCP::JP2K::CodestreamParser j2k_parser; ASDCP::JP2K::FrameBuffer frame_buffer (4 * Kumu::Megabyte); @@ -269,7 +271,7 @@ MonoPictureAsset::construct (boost::function get_path, bool intero for (int i = 0; i < _intrinsic_duration; ++i) { - string const path = get_path (i); + boost::filesystem::path const path = get_path (i); if (ASDCP_FAILURE (j2k_parser.OpenReadFrame (path.c_str(), frame_buffer))) { boost::throw_exception (FileError ("could not open JPEG2000 file for reading", path)); @@ -289,8 +291,8 @@ MonoPictureAsset::construct (boost::function get_path, bool intero } } -string -MonoPictureAsset::path_from_list (int f, vector const & files) const +boost::filesystem::path +MonoPictureAsset::path_from_list (int f, vector const & files) const { return files[f]; } @@ -434,7 +436,7 @@ PictureAsset::frame_buffer_equals ( } -StereoPictureAsset::StereoPictureAsset (string directory, string mxf_name, int fps, int intrinsic_duration) +StereoPictureAsset::StereoPictureAsset (boost::filesystem::path directory, string mxf_name, int fps, int intrinsic_duration) : PictureAsset (directory, mxf_name, 0, fps, intrinsic_duration, Size (0, 0)) { ASDCP::JP2K::MXFSReader reader; @@ -470,7 +472,7 @@ PictureAsset::key_type () const return "MDIK"; } -StereoPictureAsset::StereoPictureAsset (string directory, string mxf_name, int fps, Size size) +StereoPictureAsset::StereoPictureAsset (boost::filesystem::path directory, string mxf_name, int fps, Size size) : PictureAsset (directory, mxf_name, 0, fps, 0, size) { diff --git a/src/picture_asset.h b/src/picture_asset.h index 398f8868..495c6647 100644 --- a/src/picture_asset.h +++ b/src/picture_asset.h @@ -46,7 +46,7 @@ public: * @param directory Directory where MXF file is. * @param mxf_name Name of MXF file. */ - PictureAsset (std::string directory, std::string mxf_name); + PictureAsset (boost::filesystem::path directory, std::string mxf_name); /** Construct a PictureAsset. * This class will not write anything to disk in this constructor, but subclasses may. @@ -59,7 +59,7 @@ public: * @param size Size of video frame images in pixels. */ PictureAsset ( - std::string directory, + boost::filesystem::path directory, std::string mxf_name, boost::signals2::signal* progress, int fps, @@ -113,8 +113,8 @@ public: * @param size Size of images in pixels. */ MonoPictureAsset ( - std::vector const & files, - std::string directory, + std::vector const & files, + boost::filesystem::path directory, std::string mxf_name, boost::signals2::signal* progress, int fps, @@ -136,8 +136,8 @@ public: * @param size Size of images in pixels. */ MonoPictureAsset ( - boost::function get_path, - std::string directory, + boost::function get_path, + boost::filesystem::path directory, std::string mxf_name, boost::signals2::signal* progress, int fps, @@ -151,7 +151,7 @@ public: * @param directory Directory that the MXF is in. * @param mxf_name The filename of the MXF within `directory'. */ - MonoPictureAsset (std::string directory, std::string mxf_name); + MonoPictureAsset (boost::filesystem::path directory, std::string mxf_name); /** Construct a MonoPictureAsset for progressive writing using * start_write() and a MonoPictureAssetWriter. @@ -161,7 +161,7 @@ public: * @param fps Video frames per second. * @param size Size in pixels that the picture frames will be. */ - MonoPictureAsset (std::string directory, std::string mxf_name, int fps, Size size); + MonoPictureAsset (boost::filesystem::path directory, std::string mxf_name, int fps, Size size); /** Start a progressive write to a MonoPictureAsset */ boost::shared_ptr start_write (bool, bool, MXFMetadata const & metadata = MXFMetadata ()); @@ -170,8 +170,8 @@ public: bool equals (boost::shared_ptr other, EqualityOptions opt, boost::function note) const; private: - std::string path_from_list (int f, std::vector const & files) const; - void construct (boost::function, bool, MXFMetadata const &); + boost::filesystem::path path_from_list (int f, std::vector const & files) const; + void construct (boost::function, bool, MXFMetadata const &); std::string cpl_node_name () const; int edit_rate_factor () const; }; @@ -180,7 +180,7 @@ private: class StereoPictureAsset : public PictureAsset { public: - StereoPictureAsset (std::string directory, std::string mxf_name, int fps, int intrinsic_duration); + StereoPictureAsset (boost::filesystem::path directory, std::string mxf_name, int fps, int intrinsic_duration); /** Construct a StereoPictureAsset for progressive writing using * start_write() and a StereoPictureAssetWriter. @@ -190,7 +190,7 @@ public: * @param fps Video frames per second. * @param size Size in pixels that the picture frames will be. */ - StereoPictureAsset (std::string directory, std::string mxf_name, int fps, Size size); + StereoPictureAsset (boost::filesystem::path directory, std::string mxf_name, int fps, Size size); /** Start a progressive write to a StereoPictureAsset */ boost::shared_ptr start_write (bool, bool, MXFMetadata const & metadata = MXFMetadata ()); diff --git a/src/sound_asset.cc b/src/sound_asset.cc index 026c44ef..984d8f0f 100644 --- a/src/sound_asset.cc +++ b/src/sound_asset.cc @@ -43,8 +43,8 @@ using boost::lexical_cast; using namespace libdcp; SoundAsset::SoundAsset ( - vector const & files, - string directory, + vector const & files, + boost::filesystem::path directory, string mxf_name, boost::signals2::signal* progress, int fps, @@ -62,8 +62,8 @@ SoundAsset::SoundAsset ( } SoundAsset::SoundAsset ( - boost::function get_path, - string directory, + boost::function get_path, + boost::filesystem::path directory, string mxf_name, boost::signals2::signal* progress, int fps, @@ -81,7 +81,7 @@ SoundAsset::SoundAsset ( construct (get_path, interop, metadata); } -SoundAsset::SoundAsset (string directory, string mxf_name) +SoundAsset::SoundAsset (boost::filesystem::path directory, string mxf_name) : MXFAsset (directory, mxf_name) , _channels (0) { @@ -102,7 +102,7 @@ SoundAsset::SoundAsset (string directory, string mxf_name) _intrinsic_duration = desc.ContainerDuration; } -SoundAsset::SoundAsset (string directory, string mxf_name, int fps, int channels, int sampling_rate) +SoundAsset::SoundAsset (boost::filesystem::path directory, string mxf_name, int fps, int channels, int sampling_rate) : MXFAsset (directory, mxf_name, 0, fps, 0) , _channels (channels) , _sampling_rate (sampling_rate) @@ -110,8 +110,8 @@ SoundAsset::SoundAsset (string directory, string mxf_name, int fps, int channels } -string -SoundAsset::path_from_channel (Channel channel, vector const & files) +boost::filesystem::path +SoundAsset::path_from_channel (Channel channel, vector const & files) { unsigned int const c = int (channel); assert (c < files.size ()); @@ -119,7 +119,7 @@ SoundAsset::path_from_channel (Channel channel, vector const & files) } void -SoundAsset::construct (boost::function get_path, bool interop, MXFMetadata const & metadata) +SoundAsset::construct (boost::function get_path, bool interop, MXFMetadata const & metadata) { ASDCP::Rational asdcp_edit_rate (_edit_rate, 1); @@ -154,7 +154,7 @@ SoundAsset::construct (boost::function get_path, bool interop, for (int i = 0; i < _channels; ++i) { - string const path = get_path (channels[i]); + boost::filesystem::path const path = get_path (channels[i]); if (ASDCP_FAILURE (pcm_parser_channel[i].OpenRead (path.c_str(), asdcp_edit_rate))) { boost::throw_exception (FileError ("could not open WAV file for reading", path)); diff --git a/src/sound_asset.h b/src/sound_asset.h index 339e67f8..1d2a4755 100644 --- a/src/sound_asset.h +++ b/src/sound_asset.h @@ -82,8 +82,8 @@ public: * Note that this is different to entry_point in that the asset will contain no data before start_frame. */ SoundAsset ( - std::vector const & files, - std::string directory, + std::vector const & files, + boost::filesystem::path directory, std::string mxf_name, boost::signals2::signal* progress, int fps, @@ -103,8 +103,8 @@ public: * @param channels Number of audio channels. */ SoundAsset ( - boost::function get_path, - std::string directory, + boost::function get_path, + boost::filesystem::path directory, std::string mxf_name, boost::signals2::signal* progress, int fps, @@ -115,12 +115,12 @@ public: ); SoundAsset ( - std::string directory, + boost::filesystem::path directory, std::string mxf_name ); SoundAsset ( - std::string directory, + boost::filesystem::path directory, std::string mxf_name, int fps, int channels, @@ -143,8 +143,8 @@ public: private: std::string key_type () const; - void construct (boost::function get_path, bool interop, MXFMetadata const &); - std::string path_from_channel (Channel channel, std::vector const & files); + void construct (boost::function get_path, bool interop, MXFMetadata const &); + boost::filesystem::path path_from_channel (Channel channel, std::vector const & files); std::string cpl_node_name () const; /** Number of channels in the asset */ diff --git a/test/error_test.cc b/test/error_test.cc index 6992acff..a0f6a0f3 100644 --- a/test/error_test.cc +++ b/test/error_test.cc @@ -34,7 +34,7 @@ BOOST_AUTO_TEST_CASE (error_test) libdcp::DCP d ("build/test/fred"); /* Random filename that does not exist */ - vector p; + vector p; p.push_back ("frobozz"); /* Trying to create video/audio MXFs using a non-existant file should throw an exception */ diff --git a/test/round_trip_test.cc b/test/round_trip_test.cc new file mode 100644 index 00000000..96563ee8 --- /dev/null +++ b/test/round_trip_test.cc @@ -0,0 +1,96 @@ +/* + Copyright (C) 2013 Carl Hetherington + + 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 +#include +#include "certificates.h" +#include "kdm.h" +#include "signer.h" +#include "picture_asset.h" +#include "sound_asset.h" +#include "reel.h" +#include "test.h" +#include "cpl.h" +#include "picture_frame.h" +#include "argb_frame.h" + +using boost::shared_ptr; + +/* Build an encrypted picture MXF and a KDM for it and check that the KDM can be decrypted */ +BOOST_AUTO_TEST_CASE (round_trip_test) +{ + libdcp::CertificateChain chain; + chain.add (shared_ptr (new libdcp::Certificate (boost::filesystem::path ("build/test/signer/ca.self-signed.pem")))); + chain.add (shared_ptr (new libdcp::Certificate (boost::filesystem::path ("build/test/signer/intermediate.signed.pem")))); + chain.add (shared_ptr (new libdcp::Certificate (boost::filesystem::path ("build/test/signer/leaf.signed.pem")))); + + shared_ptr signer ( + new libdcp::Signer ( + chain, + "test/data/signer.key" + ) + ); + + boost::filesystem::path work_dir = "build/test/round_trip_test"; + boost::filesystem::create_directory (work_dir); + + libdcp::MXFMetadata mxf_metadata; + + shared_ptr asset_A ( + new libdcp::MonoPictureAsset (j2c, work_dir, "video.mxf", 0, 24, 24, libdcp::Size (32, 32), false, mxf_metadata) + ); + + libdcp::Key key; + + asset_A->set_key (key); + + shared_ptr cpl (new libdcp::CPL (work_dir, "A Test DCP", libdcp::FEATURE, 24, 24)); + cpl->add_reel (shared_ptr (new libdcp::Reel (asset_A, shared_ptr (), shared_ptr ()))); + + /* A KDM using our certificate chain's leaf key pair */ + libdcp::KDM kdm_A ( + cpl, + signer, + signer->certificates().leaf(), + boost::posix_time::time_from_string ("2013-01-01 00:00:00"), + boost::posix_time::time_from_string ("2013-01-08 00:00:00"), + "libdcp", + "2012-07-17T04:45:18+00:00" + ); + + boost::filesystem::path const kdm_file = work_dir / "kdm.xml"; + + kdm_A.as_xml (kdm_file); + + /* Reload the KDM, using our private key to decrypt it */ + libdcp::KDM kdm_B (kdm_file, "build/test/signer/leaf.key"); + + /* Reload the picture MXF */ + shared_ptr asset_B ( + new libdcp::MonoPictureAsset (work_dir, "video.mxf") + ); + + asset_B->set_key (kdm_B.keys().front().key()); + + shared_ptr frame_A = asset_A->get_frame(0)->argb_frame (); + shared_ptr frame_B = asset_B->get_frame(0)->argb_frame (); + BOOST_CHECK_EQUAL (frame_A->size().width, frame_B->size().width); + BOOST_CHECK_EQUAL (frame_A->size().height, frame_B->size().height); + BOOST_CHECK_EQUAL (memcmp (frame_A->data(), frame_B->data(), frame_A->size().width * frame_A->size().height), 0); +} diff --git a/test/test.cc b/test/test.cc index 926eb0fb..72d0c7dd 100644 --- a/test/test.cc +++ b/test/test.cc @@ -34,13 +34,13 @@ struct TestConfig BOOST_GLOBAL_FIXTURE (TestConfig); -string +boost::filesystem::path j2c (int) { return "test/data/32x32_red_square.j2c"; } -string +boost::filesystem::path wav (libdcp::Channel) { return "test/data/1s_24-bit_48k_silence.wav"; diff --git a/test/test.h b/test/test.h index de790364..d6691bc0 100644 --- a/test/test.h +++ b/test/test.h @@ -17,6 +17,6 @@ */ -extern std::string j2c (int); -extern std::string wav (libdcp::Channel); +extern boost::filesystem::path j2c (int); +extern boost::filesystem::path wav (libdcp::Channel); extern std::string test_corpus; diff --git a/test/wscript b/test/wscript index ec70288b..c0250255 100644 --- a/test/wscript +++ b/test/wscript @@ -36,6 +36,7 @@ def build(bld): kdm_test.cc subtitle_tests.cc util_test.cc + round_trip_test.cc """ obj.target = 'tests' obj.install_path = '' -- 2.30.2