From 9486066c29b91a8d9ac25be1c596cad62387208f Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 14 Aug 2016 23:59:16 +0100 Subject: [PATCH] Support reading of encrypted subtitles. --- src/asset_reader.cc | 13 +--- src/asset_reader.h | 10 ++- src/mono_picture_frame.cc | 5 +- src/mono_picture_frame.h | 3 +- src/mxf.h | 2 +- src/reel.cc | 9 ++- src/reel_subtitle_asset.cc | 26 ++++++++ src/reel_subtitle_asset.h | 8 ++- src/smpte_subtitle_asset.cc | 117 +++++++++++++++++++++++++----------- src/smpte_subtitle_asset.h | 4 ++ src/sound_frame.cc | 7 ++- src/sound_frame.h | 4 +- src/stereo_picture_frame.cc | 5 +- src/stereo_picture_frame.h | 3 +- src/wscript | 1 + 15 files changed, 151 insertions(+), 66 deletions(-) diff --git a/src/asset_reader.cc b/src/asset_reader.cc index 9ada75f0..73c5d010 100644 --- a/src/asset_reader.cc +++ b/src/asset_reader.cc @@ -34,22 +34,13 @@ #include "asset_reader.h" #include "mxf.h" #include "exceptions.h" +#include "decryption_context.h" #include using namespace dcp; AssetReader::AssetReader (MXF const * mxf) - : _decryption_context (0) + : _decryption_context (new DecryptionContext (mxf->key ())) { - if (mxf->key()) { - _decryption_context = new ASDCP::AESDecContext; - if (ASDCP_FAILURE (_decryption_context->InitKey (mxf->key()->value ()))) { - throw MiscError ("could not set up decryption context"); - } - } -} -AssetReader::~AssetReader () -{ - delete _decryption_context; } diff --git a/src/asset_reader.h b/src/asset_reader.h index 716e854d..09169a8e 100644 --- a/src/asset_reader.h +++ b/src/asset_reader.h @@ -35,23 +35,21 @@ #define LIBDCP_ASSET_READER_H #include - -namespace ASDCP { - class AESDecContext; -} +#include namespace dcp { class MXF; +class DecryptionContext; class AssetReader : public boost::noncopyable { public: explicit AssetReader (MXF const * mxf); - virtual ~AssetReader (); + virtual ~AssetReader () {} protected: - ASDCP::AESDecContext* _decryption_context; + boost::shared_ptr _decryption_context; }; } diff --git a/src/mono_picture_frame.cc b/src/mono_picture_frame.cc index fd095a1f..79527280 100644 --- a/src/mono_picture_frame.cc +++ b/src/mono_picture_frame.cc @@ -42,6 +42,7 @@ #include "colour_conversion.h" #include "compose.hpp" #include "j2k.h" +#include "decryption_context.h" #include #include @@ -73,12 +74,12 @@ MonoPictureFrame::MonoPictureFrame (boost::filesystem::path path) * @param n Frame within the asset, not taking EntryPoint into account. * @param c Context for decryption, or 0. */ -MonoPictureFrame::MonoPictureFrame (ASDCP::JP2K::MXFReader* reader, int n, ASDCP::AESDecContext* c) +MonoPictureFrame::MonoPictureFrame (ASDCP::JP2K::MXFReader* reader, int n, shared_ptr c) { /* XXX: unfortunate guesswork on this buffer size */ _buffer = new ASDCP::JP2K::FrameBuffer (4 * Kumu::Megabyte); - if (ASDCP_FAILURE (reader->ReadFrame (n, *_buffer, c))) { + if (ASDCP_FAILURE (reader->ReadFrame (n, *_buffer, c->decryption()))) { boost::throw_exception (DCPReadError (String::compose ("could not read video frame %1", n))); } } diff --git a/src/mono_picture_frame.h b/src/mono_picture_frame.h index ea15f6a3..cb4afd6f 100644 --- a/src/mono_picture_frame.h +++ b/src/mono_picture_frame.h @@ -54,6 +54,7 @@ namespace ASDCP { namespace dcp { class OpenJPEGImage; +class DecryptionContext; /** @class MonoPictureFrame * @brief A single frame of a 2D (monoscopic) picture asset. @@ -74,7 +75,7 @@ public: private: friend class MonoPictureAssetReader; - MonoPictureFrame (ASDCP::JP2K::MXFReader* reader, int n, ASDCP::AESDecContext *); + MonoPictureFrame (ASDCP::JP2K::MXFReader* reader, int n, boost::shared_ptr); ASDCP::JP2K::FrameBuffer* _buffer; }; diff --git a/src/mxf.h b/src/mxf.h index 89e4f635..73ada433 100644 --- a/src/mxf.h +++ b/src/mxf.h @@ -80,7 +80,7 @@ public: return _key_id; } - void set_key (Key); + virtual void set_key (Key); /** @return encryption/decryption key, if one has been set */ boost::optional key () const { diff --git a/src/reel.cc b/src/reel.cc index 78be2293..50c780f4 100644 --- a/src/reel.cc +++ b/src/reel.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2014-2015 Carl Hetherington + Copyright (C) 2014-2016 Carl Hetherington This file is part of libdcp. @@ -45,6 +45,7 @@ #include "decrypted_kdm_key.h" #include "decrypted_kdm.h" #include "interop_subtitle_asset.h" +#include "smpte_subtitle_asset.h" #include "reel_atmos_asset.h" #include @@ -184,6 +185,12 @@ Reel::add (DecryptedKDM const & kdm) if (_main_sound && i->id() == _main_sound->key_id()) { _main_sound->asset()->set_key (i->key ()); } + if (_main_subtitle && i->id() == _main_subtitle->key_id()) { + shared_ptr s = dynamic_pointer_cast (_main_subtitle->asset()); + if (s) { + s->set_key (i->key ()); + } + } if (_atmos && i->id() == _atmos->key_id()) { _atmos->asset()->set_key (i->key ()); } diff --git a/src/reel_subtitle_asset.cc b/src/reel_subtitle_asset.cc index 0553f4ba..c7e40420 100644 --- a/src/reel_subtitle_asset.cc +++ b/src/reel_subtitle_asset.cc @@ -37,19 +37,25 @@ #include "subtitle_asset.h" #include "reel_subtitle_asset.h" +#include "smpte_subtitle_asset.h" +#include using std::string; using boost::shared_ptr; +using boost::dynamic_pointer_cast; +using boost::optional; using namespace dcp; ReelSubtitleAsset::ReelSubtitleAsset (boost::shared_ptr asset, Fraction edit_rate, int64_t intrinsic_duration, int64_t entry_point) : ReelAsset (asset, edit_rate, intrinsic_duration, entry_point) + , ReelMXF (dynamic_pointer_cast(asset) ? dynamic_pointer_cast(asset)->key_id() : optional()) { } ReelSubtitleAsset::ReelSubtitleAsset (boost::shared_ptr node) : ReelAsset (node) + , ReelMXF (node) { node->ignore_child ("Language"); node->done (); @@ -60,3 +66,23 @@ ReelSubtitleAsset::cpl_node_name () const { return "MainSubtitle"; } + +string +ReelSubtitleAsset::key_type () const +{ + return "MDSK"; +} + +void +ReelSubtitleAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const +{ + ReelAsset::write_to_cpl (node, standard); + + if (key_id ()) { + /* Find */ + xmlpp::Node* ms = find_child (node, cpl_node_name ()); + /* Find */ + xmlpp::Node* hash = find_child (ms, "Hash"); + ms->add_child_before (hash, "KeyId")->add_child_text ("urn:uuid:" + key_id().get ()); + } +} diff --git a/src/reel_subtitle_asset.h b/src/reel_subtitle_asset.h index 91dcd53b..1d76ba9e 100644 --- a/src/reel_subtitle_asset.h +++ b/src/reel_subtitle_asset.h @@ -39,6 +39,7 @@ #define LIBDCP_REEL_SUBTITLE_ASSET_H #include "reel_asset.h" +#include "reel_mxf.h" #include "subtitle_asset.h" namespace dcp { @@ -46,19 +47,22 @@ namespace dcp { class SubtitleAsset; /** @class ReelSubtitleAsset - * @brief Part of a Reel's description which refers to a subtitle XML file. + * @brief Part of a Reel's description which refers to a subtitle XML/MXF file. */ -class ReelSubtitleAsset : public ReelAsset +class ReelSubtitleAsset : public ReelAsset, public ReelMXF { public: ReelSubtitleAsset (boost::shared_ptr asset, Fraction edit_rate, int64_t instrinsic_duration, int64_t entry_point); explicit ReelSubtitleAsset (boost::shared_ptr); + void write_to_cpl (xmlpp::Node* node, Standard standard) const; + boost::shared_ptr asset () const { return asset_of_type (); } private: + std::string key_type () const; std::string cpl_node_name () const; }; diff --git a/src/smpte_subtitle_asset.cc b/src/smpte_subtitle_asset.cc index e0934ab5..239fff74 100644 --- a/src/smpte_subtitle_asset.cc +++ b/src/smpte_subtitle_asset.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2015 Carl Hetherington + Copyright (C) 2012-2016 Carl Hetherington This file is part of libdcp. @@ -45,6 +45,7 @@ #include "util.h" #include "compose.hpp" #include "encryption_context.h" +#include "decryption_context.h" #include #include #include @@ -79,19 +80,26 @@ SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file) shared_ptr xml (new cxml::Document ("SubtitleReel")); shared_ptr reader (new ASDCP::TimedText::MXFReader ()); - Kumu::Result_t r = reader->OpenRead (file.string().c_str ()); - + Kumu::Result_t r = reader->OpenRead (_file.string().c_str ()); if (!ASDCP_FAILURE (r)) { - string s; - reader->ReadTimedTextResource (s, 0, 0); - xml->read_string (s); + /* MXF-wrapped */ ASDCP::WriterInfo info; reader->FillWriterInfo (info); _id = read_writer_info (info); + if (!_key_id) { + /* Not encrypted; read it in now */ + string s; + reader->ReadTimedTextResource (s); + xml->read_string (s); + parse_xml (xml); + read_mxf_descriptor (reader, shared_ptr (new DecryptionContext ())); + } } else { - reader.reset (); + /* Plain XML */ try { + xml.reset (new cxml::Document ("SubtitleReel")); xml->read_file (file); + parse_xml (xml); _id = remove_urn_uuid (xml->string_child ("Id")); } catch (cxml::Error& e) { boost::throw_exception ( @@ -101,7 +109,11 @@ SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file) ); } } +} +void +SMPTESubtitleAsset::parse_xml (shared_ptr xml) +{ _load_font_nodes = type_children (xml, "LoadFont"); _content_title_text = xml->string_child ("ContentTitleText"); @@ -141,45 +153,80 @@ SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file) parse_subtitles (xml, font_nodes, subtitle_nodes); - if (reader) { - ASDCP::TimedText::TimedTextDescriptor descriptor; - reader->FillTimedTextDescriptor (descriptor); + /* Guess intrinsic duration */ + _intrinsic_duration = latest_subtitle_out().as_editable_units (_edit_rate.numerator / _edit_rate.denominator); +} + +void +SMPTESubtitleAsset::read_mxf_descriptor (shared_ptr reader, shared_ptr dec) +{ + ASDCP::TimedText::TimedTextDescriptor descriptor; + reader->FillTimedTextDescriptor (descriptor); - /* Load fonts */ + /* Load fonts */ - for ( - ASDCP::TimedText::ResourceList_t::const_iterator i = descriptor.ResourceList.begin(); - i != descriptor.ResourceList.end(); - ++i) { + for ( + ASDCP::TimedText::ResourceList_t::const_iterator i = descriptor.ResourceList.begin(); + i != descriptor.ResourceList.end(); + ++i) { - if (i->Type == ASDCP::TimedText::MT_OPENTYPE) { - ASDCP::TimedText::FrameBuffer buffer; - buffer.Capacity (10 * 1024 * 1024); - reader->ReadAncillaryResource (i->ResourceID, buffer); + if (i->Type == ASDCP::TimedText::MT_OPENTYPE) { + ASDCP::TimedText::FrameBuffer buffer; + buffer.Capacity (10 * 1024 * 1024); + reader->ReadAncillaryResource (i->ResourceID, buffer, dec->decryption()); - char id[64]; - Kumu::bin2UUIDhex (i->ResourceID, ASDCP::UUIDlen, id, sizeof (id)); + char id[64]; + Kumu::bin2UUIDhex (i->ResourceID, ASDCP::UUIDlen, id, sizeof (id)); - shared_array data (new uint8_t[buffer.Size()]); - memcpy (data.get(), buffer.RoData(), buffer.Size()); + shared_array data (new uint8_t[buffer.Size()]); + memcpy (data.get(), buffer.RoData(), buffer.Size()); - list >::const_iterator j = _load_font_nodes.begin (); - while (j != _load_font_nodes.end() && (*j)->urn != id) { - ++j; - } + list >::const_iterator j = _load_font_nodes.begin (); + while (j != _load_font_nodes.end() && (*j)->urn != id) { + ++j; + } - if (j != _load_font_nodes.end ()) { - _fonts.push_back (Font ((*j)->id, (*j)->urn, Data (data, buffer.Size ()))); - } + if (j != _load_font_nodes.end ()) { + _fonts.push_back (Font ((*j)->id, (*j)->urn, Data (data, buffer.Size ()))); } } + } - /* Get intrinsic duration */ - _intrinsic_duration = descriptor.ContainerDuration; - } else { - /* Guess intrinsic duration */ - _intrinsic_duration = latest_subtitle_out().as_editable_units (_edit_rate.numerator / _edit_rate.denominator); + /* Get intrinsic duration */ + _intrinsic_duration = descriptor.ContainerDuration; +} + +void +SMPTESubtitleAsset::set_key (Key key) +{ + MXF::set_key (key); + + if (!_key_id || _file.empty()) { + /* Either we don't have any data to read, or it wasn't + encrypted, so we don't need to do anything else. + */ + return; } + + /* Our data was encrypted; now we can decrypt it */ + + shared_ptr reader (new ASDCP::TimedText::MXFReader ()); + Kumu::Result_t r = reader->OpenRead (_file.string().c_str ()); + if (ASDCP_FAILURE (r)) { + boost::throw_exception ( + DCPReadError ( + String::compose ("Could not read encrypted subtitle MXF (%1)", _file, static_cast (r)) + ) + ); + } + + string s; + shared_ptr dec (new DecryptionContext (key)); + reader->ReadTimedTextResource (s, dec->decryption()); + shared_ptr xml (new cxml::Document ("SubtitleReel")); + xml->read_string (s); + parse_xml (xml); + read_mxf_descriptor (reader, dec); } list > diff --git a/src/smpte_subtitle_asset.h b/src/smpte_subtitle_asset.h index ffe5b6d9..004531a2 100644 --- a/src/smpte_subtitle_asset.h +++ b/src/smpte_subtitle_asset.h @@ -49,6 +49,7 @@ namespace ASDCP { namespace dcp { class SMPTELoadFontNode; +class DecryptionContext; /** @class SMPTESubtitleAsset * @brief A set of subtitles to be read and/or written in the SMPTE format. @@ -74,6 +75,7 @@ public: void write (boost::filesystem::path path) const; void add (SubtitleString); void add_font (std::string id, boost::filesystem::path file); + void set_key (Key key); void set_content_title_text (std::string t) { _content_title_text = t; @@ -159,6 +161,8 @@ protected: private: void read_fonts (boost::shared_ptr); + void parse_xml (boost::shared_ptr xml); + void read_mxf_descriptor (boost::shared_ptr reader, boost::shared_ptr dec); /** The total length of this content in video frames. The amount of * content presented may be less than this. diff --git a/src/sound_frame.cc b/src/sound_frame.cc index 83b053b2..132d25bd 100644 --- a/src/sound_frame.cc +++ b/src/sound_frame.cc @@ -37,18 +37,19 @@ #include "sound_frame.h" #include "exceptions.h" +#include "decryption_context.h" #include #include -using namespace std; using namespace dcp; +using boost::shared_ptr; -SoundFrame::SoundFrame (ASDCP::PCM::MXFReader* reader, int n, ASDCP::AESDecContext* c) +SoundFrame::SoundFrame (ASDCP::PCM::MXFReader* reader, int n, shared_ptr c) { /* XXX: unfortunate guesswork on this buffer size */ _buffer = new ASDCP::PCM::FrameBuffer (1 * Kumu::Megabyte); - if (ASDCP_FAILURE (reader->ReadFrame (n, *_buffer, c))) { + if (ASDCP_FAILURE (reader->ReadFrame (n, *_buffer, c->decryption()))) { boost::throw_exception (DCPReadError ("could not read audio frame")); } } diff --git a/src/sound_frame.h b/src/sound_frame.h index 4c061fdc..3cd3246b 100644 --- a/src/sound_frame.h +++ b/src/sound_frame.h @@ -53,6 +53,8 @@ namespace ASDCP { namespace dcp { +class DecryptionContext; + /** @class SoundFrame * @brief One ‘frame’ of sound data from a SoundAsset. */ @@ -67,7 +69,7 @@ public: private: friend class SoundAssetReader; - SoundFrame (ASDCP::PCM::MXFReader* reader, int n, ASDCP::AESDecContext *); + SoundFrame (ASDCP::PCM::MXFReader* reader, int n, boost::shared_ptr); /** a buffer to hold the frame */ ASDCP::PCM::FrameBuffer* _buffer; diff --git a/src/stereo_picture_frame.cc b/src/stereo_picture_frame.cc index 82f84f3f..e645c7df 100644 --- a/src/stereo_picture_frame.cc +++ b/src/stereo_picture_frame.cc @@ -38,6 +38,7 @@ #include "colour_conversion.h" #include "compose.hpp" #include "j2k.h" +#include "decryption_context.h" #include #include @@ -49,12 +50,12 @@ using namespace dcp; * @param reader Reader for the MXF file. * @param n Frame within the asset, not taking EntryPoint into account. */ -StereoPictureFrame::StereoPictureFrame (ASDCP::JP2K::MXFSReader* reader, int n, ASDCP::AESDecContext* c) +StereoPictureFrame::StereoPictureFrame (ASDCP::JP2K::MXFSReader* reader, int n, shared_ptr c) { /* XXX: unfortunate guesswork on this buffer size */ _buffer = new ASDCP::JP2K::SFrameBuffer (4 * Kumu::Megabyte); - if (ASDCP_FAILURE (reader->ReadFrame (n, *_buffer, c))) { + if (ASDCP_FAILURE (reader->ReadFrame (n, *_buffer, c->decryption()))) { boost::throw_exception (DCPReadError (String::compose ("could not read video frame %1 of %2", n))); } } diff --git a/src/stereo_picture_frame.h b/src/stereo_picture_frame.h index fd0e70fd..65e1cea3 100644 --- a/src/stereo_picture_frame.h +++ b/src/stereo_picture_frame.h @@ -49,6 +49,7 @@ namespace ASDCP { namespace dcp { class OpenJPEGImage; +class DecryptionContext; /** A single frame of a 3D (stereoscopic) picture asset */ class StereoPictureFrame : public boost::noncopyable @@ -70,7 +71,7 @@ public: private: friend class StereoPictureAssetReader; - StereoPictureFrame (ASDCP::JP2K::MXFSReader* reader, int n, ASDCP::AESDecContext *); + StereoPictureFrame (ASDCP::JP2K::MXFSReader* reader, int n, boost::shared_ptr); ASDCP::JP2K::SFrameBuffer* _buffer; }; diff --git a/src/wscript b/src/wscript index d76b1a50..be7d4621 100644 --- a/src/wscript +++ b/src/wscript @@ -47,6 +47,7 @@ def build(bld): dcp.cc dcp_time.cc decrypted_kdm.cc + decryption_context.cc decrypted_kdm_key.cc encrypted_kdm.cc encryption_context.cc -- 2.30.2