diff options
| author | Carl Hetherington <cth@carlh.net> | 2025-10-31 00:03:49 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2025-11-05 00:43:19 +0100 |
| commit | e8ce097ce705446c27b51199a321a9918deaa0db (patch) | |
| tree | 0a52cbc9c52791d453fa4bd478609aedde98c831 | |
| parent | ef65a179e8c907029d0d9254863d4884581f3d60 (diff) | |
Allow specification of which parts of the DCP to encrypt (#3099).
| -rw-r--r-- | src/lib/copy_dcp_details_to_film.cc | 4 | ||||
| -rw-r--r-- | src/lib/create_cli.cc | 8 | ||||
| -rw-r--r-- | src/lib/dcp_content.cc | 20 | ||||
| -rw-r--r-- | src/lib/dcp_content.h | 25 | ||||
| -rw-r--r-- | src/lib/dcp_examiner.cc | 18 | ||||
| -rw-r--r-- | src/lib/dcp_examiner.h | 16 | ||||
| -rw-r--r-- | src/lib/film.cc | 44 | ||||
| -rw-r--r-- | src/lib/film.h | 25 | ||||
| -rw-r--r-- | src/lib/film_property.h | 4 | ||||
| -rw-r--r-- | src/lib/reel_writer.cc | 8 | ||||
| -rw-r--r-- | src/lib/writer.cc | 2 | ||||
| -rw-r--r-- | src/wx/dcp_panel.cc | 34 | ||||
| -rw-r--r-- | src/wx/dcp_panel.h | 6 | ||||
| -rw-r--r-- | src/wx/encryption_settings_dialog.cc | 64 | ||||
| -rw-r--r-- | src/wx/encryption_settings_dialog.h | 43 | ||||
| -rw-r--r-- | src/wx/wscript | 1 | ||||
| -rw-r--r-- | test/atmos_test.cc | 4 | ||||
| m--------- | test/data | 0 | ||||
| -rw-r--r-- | test/dcp_decoder_test.cc | 4 | ||||
| -rw-r--r-- | test/dcp_digest_file_test.cc | 6 | ||||
| -rw-r--r-- | test/encryption_test.cc | 106 | ||||
| -rw-r--r-- | test/import_dcp_test.cc | 3 | ||||
| -rw-r--r-- | test/kdm_cli_test.cc | 4 | ||||
| -rw-r--r-- | test/kdm_naming_test.cc | 6 | ||||
| -rw-r--r-- | test/player_test.cc | 4 | ||||
| -rw-r--r-- | test/recover_test.cc | 3 | ||||
| -rw-r--r-- | test/remake_id_test.cc | 3 | ||||
| -rw-r--r-- | test/vf_kdm_test.cc | 6 |
28 files changed, 419 insertions, 52 deletions
diff --git a/src/lib/copy_dcp_details_to_film.cc b/src/lib/copy_dcp_details_to_film.cc index 9e0ad79c1..67a207de1 100644 --- a/src/lib/copy_dcp_details_to_film.cc +++ b/src/lib/copy_dcp_details_to_film.cc @@ -45,7 +45,9 @@ copy_dcp_settings_to_film(shared_ptr<const DCPContent> dcp, shared_ptr<Film> fil if (dcp->content_kind()) { film->set_dcp_content_type(DCPContentType::from_libdcp_kind(dcp->content_kind().get())); } - film->set_encrypted(dcp->encrypted()); + film->set_encrypt_picture(dcp->picture_encrypted()); + film->set_encrypt_sound(dcp->sound_encrypted()); + film->set_encrypt_text(dcp->text_encrypted()); film->set_reel_type(ReelType::BY_VIDEO_CONTENT); film->set_interop(dcp->standard() == dcp::Standard::INTEROP); film->set_three_d(dcp->three_d()); diff --git a/src/lib/create_cli.cc b/src/lib/create_cli.cc index af2e90745..32834be23 100644 --- a/src/lib/create_cli.cc +++ b/src/lib/create_cli.cc @@ -473,9 +473,13 @@ CreateCLI::make_film(function<void (string)> error) const } film->set_use_isdcf_name(!_no_use_isdcf_name); if (_no_encrypt) { - film->set_encrypted(false); + film->set_encrypt_picture(false); + film->set_encrypt_sound(false); + film->set_encrypt_text(false); } else if (_encrypt) { - film->set_encrypted(true); + film->set_encrypt_picture(true); + film->set_encrypt_sound(true); + film->set_encrypt_text(true); } if (_twod) { film->set_three_d(false); diff --git a/src/lib/dcp_content.cc b/src/lib/dcp_content.cc index cff14deca..c75babfc6 100644 --- a/src/lib/dcp_content.cc +++ b/src/lib/dcp_content.cc @@ -66,7 +66,9 @@ using namespace dcpomatic; DCPContent::DCPContent(boost::filesystem::path p) - : _encrypted(false) + : _picture_encrypted(false) + , _sound_encrypted(false) + , _text_encrypted(false) , _needs_assets(false) , _kdm_valid(false) , _reference_video(false) @@ -103,7 +105,10 @@ DCPContent::DCPContent(cxml::ConstNodePtr node, boost::optional<boost::filesyste } _name = node->string_child("Name"); - _encrypted = node->bool_child("Encrypted"); + auto encrypted = node->optional_bool_child("Encrypted").get_value_or(false); + _picture_encrypted = node->optional_bool_child("PictureEncrypted").get_value_or(encrypted); + _sound_encrypted = node->optional_bool_child("SoundEncrypted").get_value_or(encrypted); + _text_encrypted = node->optional_bool_child("TextEncrypted").get_value_or(encrypted); _needs_assets = node->optional_bool_child("NeedsAssets").get_value_or(false); if (node->optional_node_child("KDM")) { _kdm = dcp::EncryptedKDM(node->string_child("KDM")); @@ -314,7 +319,9 @@ DCPContent::examine(shared_ptr<const Film> film, shared_ptr<Job> job, bool toler boost::mutex::scoped_lock lm(_mutex); text = new_text; _name = examiner->name(); - _encrypted = examiner->encrypted(); + _picture_encrypted = examiner->picture_encrypted(); + _sound_encrypted = examiner->sound_encrypted(); + _text_encrypted = examiner->text_encrypted(); _needs_assets = examiner->needs_assets(); _kdm_valid = examiner->kdm_valid(); _standard = examiner->standard(); @@ -396,7 +403,10 @@ DCPContent::as_xml(xmlpp::Element* element, bool with_paths, PathBehaviour path_ boost::mutex::scoped_lock lm(_mutex); cxml::add_text_child(element, "Name", _name); - cxml::add_text_child(element, "Encrypted", _encrypted ? "1" : "0"); + cxml::add_text_child(element, "Encrypted", (_picture_encrypted || _sound_encrypted || _text_encrypted) ? "1" : "0"); + cxml::add_text_child(element, "PictureEncrypted", _picture_encrypted ? "1" : "0"); + cxml::add_text_child(element, "SoundEncrypted", _sound_encrypted ? "1" : "0"); + cxml::add_text_child(element, "TextEncrypted", _text_encrypted ? "1" : "0"); cxml::add_text_child(element, "NeedsAssets", _needs_assets ? "1" : "0"); if (_kdm) { cxml::add_text_child(element, "KDM", _kdm->as_xml()); @@ -526,7 +536,7 @@ bool DCPContent::needs_kdm() const { boost::mutex::scoped_lock lm(_mutex); - return _encrypted && !_kdm_valid; + return (_picture_encrypted || _sound_encrypted || _text_encrypted) && !_kdm_valid; } bool diff --git a/src/lib/dcp_content.h b/src/lib/dcp_content.h index 1b58c8efc..97e4b3cc4 100644 --- a/src/lib/dcp_content.h +++ b/src/lib/dcp_content.h @@ -97,7 +97,25 @@ public: bool encrypted() const { boost::mutex::scoped_lock lm(_mutex); - return _encrypted; + return _picture_encrypted || _sound_encrypted || _text_encrypted; + } + + /** @return true if any picture asset in this DCP is encrypted */ + bool picture_encrypted() const { + boost::mutex::scoped_lock lm(_mutex); + return _picture_encrypted; + } + + /** @return true if any sound asset in this DCP is encrypted */ + bool sound_encrypted() const { + boost::mutex::scoped_lock lm(_mutex); + return _sound_encrypted; + } + + /** @return true if any text asset in this DCP is encrypted */ + bool text_encrypted() const { + boost::mutex::scoped_lock lm(_mutex); + return _text_encrypted; } void add_kdm(dcp::EncryptedKDM); @@ -217,8 +235,9 @@ private: bool overlaps(std::shared_ptr<const Film> film, std::function<bool (std::shared_ptr<const Content>)> part) const; std::string _name; - /** true if our DCP is encrypted */ - bool _encrypted; + bool _picture_encrypted; + bool _sound_encrypted; + bool _text_encrypted; /** true if this DCP needs more assets before it can be played */ bool _needs_assets; boost::optional<dcp::EncryptedKDM> _kdm; diff --git a/src/lib/dcp_examiner.cc b/src/lib/dcp_examiner.cc index 59bd47702..b94d88486 100644 --- a/src/lib/dcp_examiner.cc +++ b/src/lib/dcp_examiner.cc @@ -313,7 +313,23 @@ DCPExaminer::DCPExaminer(shared_ptr<const DCPContent> content, bool tolerant) ++reel_index; } - _encrypted = selected_cpl->any_encrypted(); + for (auto reel: selected_cpl->reels()) { + if (reel->main_picture() && reel->main_picture()->encrypted()) { + _picture_encrypted = true; + } + if (reel->main_sound() && reel->main_sound()->encrypted()) { + _sound_encrypted = true; + } + if (reel->main_subtitle() && reel->main_subtitle()->encrypted()) { + _text_encrypted = true; + } + for (auto cc: reel->closed_captions()) { + if (cc->encrypted()) { + _text_encrypted = true; + } + } + } + _kdm_valid = true; LOG_GENERAL_NC("Check that everything encrypted has a key"); diff --git a/src/lib/dcp_examiner.h b/src/lib/dcp_examiner.h index 0a6045ed0..6bc9793aa 100644 --- a/src/lib/dcp_examiner.h +++ b/src/lib/dcp_examiner.h @@ -76,8 +76,16 @@ public: return _name; } - bool encrypted() const { - return _encrypted; + bool picture_encrypted() const { + return _picture_encrypted; + } + + bool sound_encrypted() const { + return _sound_encrypted; + } + + bool text_encrypted() const { + return _text_encrypted; } bool needs_assets() const { @@ -216,7 +224,9 @@ private: std::vector<DCPTextTrack> _dcp_subtitle_tracks; /** the DCPTextTracks for each of our closed captions */ std::vector<DCPTextTrack> _dcp_caption_tracks; - bool _encrypted = false; + bool _picture_encrypted = false; + bool _sound_encrypted = false; + bool _text_encrypted = false; bool _needs_assets = false; bool _kdm_valid = false; boost::optional<dcp::Standard> _standard; diff --git a/src/lib/film.cc b/src/lib/film.cc index 8dc55c590..b8f983add 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -168,7 +168,9 @@ Film::Film(optional<boost::filesystem::path> dir) , _dcp_content_type(DCPContentType::from_isdcf_name("FTR")) , _container(Ratio::from_id("185")) , _resolution(Resolution::TWO_K) - , _encrypted(false) + , _encrypt_picture(false) + , _encrypt_sound(false) + , _encrypt_text(false) , _context_id(dcp::make_uuid()) , _video_frame_rate(24) , _audio_channels(6) @@ -418,7 +420,11 @@ Film::metadata(bool with_content_paths) const cxml::add_text_child(root, "Interop", _interop ? "1" : "0"); cxml::add_text_child(root, "VideoEncoding", video_encoding_to_string(_video_encoding)); cxml::add_text_child(root, "LimitToSMPTEBv20", _limit_to_smpte_bv20 ? "1" : "0"); - cxml::add_text_child(root, "Encrypted", _encrypted ? "1" : "0"); + /* We don't need this any more, but writing it makes the metadata backwards compatible */ + cxml::add_text_child(root, "Encrypted", encrypted() ? "1" : "0"); + cxml::add_text_child(root, "EncryptPicture", _encrypt_picture ? "1" : "0"); + cxml::add_text_child(root, "EncryptSound", _encrypt_sound ? "1" : "0"); + cxml::add_text_child(root, "EncryptText", _encrypt_text ? "1" : "0"); cxml::add_text_child(root, "Key", _key.hex()); cxml::add_text_child(root, "ContextID", _context_id); if (_audio_processor) { @@ -593,7 +599,10 @@ Film::read_metadata(optional<boost::filesystem::path> path) _video_bit_rate[VideoEncoding::MPEG2] = f.optional_number_child<int64_t>("MPEG2VideoBitRate").get_value_or(Config::instance()->default_video_bit_rate(VideoEncoding::MPEG2)); _video_frame_rate = f.number_child<int>("VideoFrameRate"); _audio_frame_rate = f.optional_number_child<int>("AudioFrameRate").get_value_or(48000); - _encrypted = f.bool_child("Encrypted"); + auto encrypted = f.optional_bool_child("Encrypted").get_value_or(false); + _encrypt_picture = f.optional_bool_child("EncryptPicture").get_value_or(encrypted); + _encrypt_sound = f.optional_bool_child("EncryptSound").get_value_or(encrypted); + _encrypt_text = f.optional_bool_child("EncryptText").get_value_or(encrypted); _audio_channels = f.number_child<int>("AudioChannels"); /* We used to allow odd numbers (and zero) channels, but it's just not worth the pain. @@ -1423,12 +1432,29 @@ Film::cpls() const } void -Film::set_encrypted(bool e) +Film::set_encrypt_picture(bool e) { - FilmChangeSignaller ch(this, FilmProperty::ENCRYPTED); - _encrypted = e; + FilmChangeSignaller ch(this, FilmProperty::ENCRYPT_PICTURE); + _encrypt_picture = e; } + +void +Film::set_encrypt_sound(bool e) +{ + FilmChangeSignaller ch(this, FilmProperty::ENCRYPT_SOUND); + _encrypt_sound = e; +} + + +void +Film::set_encrypt_text(bool e) +{ + FilmChangeSignaller ch(this, FilmProperty::ENCRYPT_TEXT); + _encrypt_text = e; +} + + ContentList Film::content() const { @@ -1859,7 +1885,7 @@ Film::active_area() const dcp::DecryptedKDM Film::make_kdm(boost::filesystem::path cpl_file, dcp::LocalTime from, dcp::LocalTime until) const { - if (!_encrypted) { + if (!encrypted()) { throw runtime_error(_("Cannot make a KDM as this project is not encrypted.")); } @@ -2102,7 +2128,9 @@ Film::use_template(optional<string> name) _video_bit_rate[encoding] = _template_film->_video_bit_rate[encoding]; } _video_frame_rate = _template_film->_video_frame_rate; - _encrypted = _template_film->_encrypted; + _encrypt_picture = _template_film->_encrypt_picture; + _encrypt_sound = _template_film->_encrypt_sound; + _encrypt_text = _template_film->_encrypt_text; _audio_channels = _template_film->_audio_channels; _sequence = _template_film->_sequence; _three_d = _template_film->_three_d; diff --git a/src/lib/film.h b/src/lib/film.h index 6d04da9bf..adaff3c52 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -231,7 +231,19 @@ public: } bool encrypted() const { - return _encrypted; + return _encrypt_picture || _encrypt_sound || _encrypt_text; + } + + bool encrypt_picture() const { + return _encrypt_picture; + } + + bool encrypt_sound() const { + return _encrypt_sound; + } + + bool encrypt_text() const { + return _encrypt_text; } dcp::Key key() const { @@ -392,7 +404,9 @@ public: void set_dcp_content_type(DCPContentType const *); void set_container(Ratio c, bool user_explicit = true); void set_resolution(Resolution, bool user_explicit = true); - void set_encrypted(bool); + void set_encrypt_picture(bool); + void set_encrypt_sound(bool); + void set_encrypt_text(bool); void set_video_bit_rate(VideoEncoding encoding, int64_t); void set_video_frame_rate(int rate, bool user_explicit = false); void set_audio_channels(int); @@ -512,7 +526,12 @@ private: Ratio _container; /** DCP resolution (2K or 4K) */ Resolution _resolution; - bool _encrypted; + /** Encrypt picture assets */ + bool _encrypt_picture; + /** Encrypt sound assets */ + bool _encrypt_sound; + /** Encrypt text (subtitle/closed-caption) assets */ + bool _encrypt_text; dcp::Key _key; /** context ID used when encrypting picture assets; we keep it so that we can * re-start picture MXF encodes. diff --git a/src/lib/film_property.h b/src/lib/film_property.h index ebda0e807..5c38ae986 100644 --- a/src/lib/film_property.h +++ b/src/lib/film_property.h @@ -38,7 +38,9 @@ enum class FilmProperty { DCP_CONTENT_TYPE, CONTAINER, RESOLUTION, - ENCRYPTED, + ENCRYPT_PICTURE, + ENCRYPT_SOUND, + ENCRYPT_TEXT, VIDEO_BIT_RATE, VIDEO_FRAME_RATE, AUDIO_FRAME_RATE, diff --git a/src/lib/reel_writer.cc b/src/lib/reel_writer.cc index 01a798676..1d5a16075 100644 --- a/src/lib/reel_writer.cc +++ b/src/lib/reel_writer.cc @@ -146,7 +146,7 @@ ReelWriter::ReelWriter( asset->set_size(film()->frame_size()); asset->set_metadata(mxf_metadata()); - if (film()->encrypted()) { + if (film()->encrypt_picture()) { asset->set_key(film()->key()); asset->set_context_id(film()->context_id()); } @@ -218,7 +218,7 @@ ReelWriter::ReelWriter( _sound_asset->set_metadata(mxf_metadata()); - if (film()->encrypted()) { + if (film()->encrypt_sound()) { _sound_asset->set_key(film()->key()); } @@ -310,7 +310,7 @@ ReelWriter::write(shared_ptr<const dcp::AtmosFrame> atmos, AtmosMetadata metadat { if (!_atmos_asset) { _atmos_asset = metadata.create(dcp::Fraction(film()->video_frame_rate(), 1)); - if (film()->encrypted()) { + if (film()->encrypt_sound()) { _atmos_asset->set_key(film()->key()); } _atmos_asset_writer = _atmos_asset->start_write( @@ -820,7 +820,7 @@ ReelWriter::empty_text_asset(TextType type, optional<DCPTextTrack> track, bool w s->set_reel_number(_reel_index + 1); s->set_time_code_rate(film()->video_frame_rate()); s->set_start_time(dcp::Time()); - if (film()->encrypted()) { + if (film()->encrypt_text()) { s->set_key(film()->key()); } asset = s; diff --git a/src/lib/writer.cc b/src/lib/writer.cc index 7121c594b..41e831e94 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -723,7 +723,7 @@ Writer::finish() bool Writer::can_fake_write(Frame frame) const { - if (film()->encrypted()) { + if (film()->encrypt_picture()) { /* We need to re-write the frame because the asset ID is embedded in the HMAC... I think... */ return false; } diff --git a/src/wx/dcp_panel.cc b/src/wx/dcp_panel.cc index 755ecfbaa..96b4b3cb5 100644 --- a/src/wx/dcp_panel.cc +++ b/src/wx/dcp_panel.cc @@ -27,6 +27,7 @@ #include "dcpomatic_button.h" #include "dcpomatic_choice.h" #include "dcpomatic_spin_ctrl.h" +#include "encryption_settings_dialog.h" #include "film_viewer.h" #include "focus_manager.h" #include "interop_metadata_dialog.h" @@ -103,6 +104,7 @@ DCPPanel::DCPPanel(wxNotebook* n, shared_ptr<Film> film, FilmViewer& viewer) _dcp_content_type = new Choice(_panel); _encrypted = new CheckBox(_panel, _("Encrypted")); + _encryption_settings = new Button(_panel, _("Encryption settings...")); wxClientDC dc(_panel); auto size = dc.GetTextExtent(char_to_wx("GGGGGGGG...")); @@ -126,6 +128,7 @@ DCPPanel::DCPPanel(wxNotebook* n, shared_ptr<Film> film, FilmViewer& viewer) _copy_isdcf_name_button->Bind(wxEVT_BUTTON, boost::bind(&DCPPanel::copy_isdcf_name_button_clicked, this)); _dcp_content_type->Bind(wxEVT_CHOICE, boost::bind(&DCPPanel::dcp_content_type_changed, this)); _encrypted->bind(&DCPPanel::encrypted_toggled, this); + _encryption_settings->bind(&DCPPanel::encryption_settings_clicked, this); _standard->Bind(wxEVT_CHOICE, boost::bind(&DCPPanel::standard_changed, this)); _markers->Bind(wxEVT_BUTTON, boost::bind(&DCPPanel::markers_clicked, this)); _metadata->Bind(wxEVT_BUTTON, boost::bind(&DCPPanel::metadata_clicked, this)); @@ -242,7 +245,8 @@ DCPPanel::add_to_grid() _grid->Add(_dcp_content_type, wxGBPosition(r, 1)); ++r; - _grid->Add(_encrypted, wxGBPosition(r, 0), wxGBSpan(1, 2)); + _grid->Add(_encrypted, wxGBPosition(r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + _grid->Add(_encryption_settings, wxGBPosition(r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); ++r; add_label_to_sizer(_grid, _standard_label, true, wxGBPosition(r, 0)); @@ -287,7 +291,23 @@ DCPPanel::encrypted_toggled() return; } - _film->set_encrypted(_encrypted->GetValue()); + auto new_value = !_film->encrypted(); + + _film->set_encrypt_picture(new_value); + _film->set_encrypt_sound(new_value); + _film->set_encrypt_text(new_value); +} + + +void +DCPPanel::encryption_settings_clicked() +{ + EncryptionSettingsDialog dialog(_panel, _film); + dialog.ShowModal(); + + _film->set_encrypt_picture(dialog.picture()); + _film->set_encrypt_sound(dialog.sound()); + _film->set_encrypt_text(dialog.text()); } @@ -394,8 +414,11 @@ DCPPanel::film_changed(FilmProperty p) setup_dcp_name(); break; } - case FilmProperty::ENCRYPTED: + case FilmProperty::ENCRYPT_PICTURE: + case FilmProperty::ENCRYPT_SOUND: + case FilmProperty::ENCRYPT_TEXT: checked_set(_encrypted, _film->encrypted()); + setup_sensitivity(); break; case FilmProperty::RESOLUTION: checked_set(_resolution, _film->resolution() == Resolution::TWO_K ? 0 : 1); @@ -624,7 +647,9 @@ DCPPanel::set_film(shared_ptr<Film> film) film_changed(FilmProperty::DCP_CONTENT_TYPE); film_changed(FilmProperty::CONTAINER); film_changed(FilmProperty::RESOLUTION); - film_changed(FilmProperty::ENCRYPTED); + film_changed(FilmProperty::ENCRYPT_PICTURE); + film_changed(FilmProperty::ENCRYPT_SOUND); + film_changed(FilmProperty::ENCRYPT_TEXT); film_changed(FilmProperty::VIDEO_BIT_RATE); film_changed(FilmProperty::VIDEO_FRAME_RATE); film_changed(FilmProperty::AUDIO_CHANNELS); @@ -664,6 +689,7 @@ DCPPanel::setup_sensitivity() _audio_language->Enable (_enable_audio_language->GetValue()); _edit_audio_language->Enable (_enable_audio_language->GetValue()); _encrypted->Enable (_generally_sensitive); + _encryption_settings->Enable (_generally_sensitive && _encrypted->GetValue()); _markers->Enable (_generally_sensitive && _film && !_film->interop()); _metadata->Enable (_generally_sensitive); _reels->Enable (_generally_sensitive && _film); diff --git a/src/wx/dcp_panel.h b/src/wx/dcp_panel.h index 2196e8927..29ad83eaf 100644 --- a/src/wx/dcp_panel.h +++ b/src/wx/dcp_panel.h @@ -81,6 +81,7 @@ private: void three_d_changed(); void standard_changed(); void encrypted_toggled(); + void encryption_settings_clicked(); void audio_processor_changed(); void show_audio_clicked(); void markers_clicked(); @@ -153,8 +154,9 @@ private: wxStaticText* _standard_label; Choice* _standard; CheckBox* _encrypted; - wxButton* _markers; - wxButton* _metadata; + Button* _encryption_settings; + Button* _markers; + Button* _metadata; Button* _reels; wxSizer* _audio_panel_sizer; diff --git a/src/wx/encryption_settings_dialog.cc b/src/wx/encryption_settings_dialog.cc new file mode 100644 index 000000000..2fdb689c9 --- /dev/null +++ b/src/wx/encryption_settings_dialog.cc @@ -0,0 +1,64 @@ +/* + Copyright (C) 2025 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic 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. + + DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "check_box.h" +#include "encryption_settings_dialog.h" +#include "lib/film.h" + + +using std::shared_ptr; + + +EncryptionSettingsDialog::EncryptionSettingsDialog(wxWindow* parent, shared_ptr<const Film> film) + : TableDialog(parent, _("Encryption settings"), 1, 0, true) +{ + _picture = add(new CheckBox(this, _("Encrypt picture"))); + _sound = add(new CheckBox(this, _("Encrypt sound"))); + _text = add(new CheckBox(this, _("Encrypt text"))); + + layout(); + + _picture->set(film->encrypt_picture()); + _sound->set(film->encrypt_sound()); + _text->set(film->encrypt_text()); +} + + +bool +EncryptionSettingsDialog::picture() const +{ + return _picture->get(); +} + + +bool +EncryptionSettingsDialog::sound() const +{ + return _sound->get(); +} + + +bool +EncryptionSettingsDialog::text() const +{ + return _text->get(); +} + diff --git a/src/wx/encryption_settings_dialog.h b/src/wx/encryption_settings_dialog.h new file mode 100644 index 000000000..4ea3151c9 --- /dev/null +++ b/src/wx/encryption_settings_dialog.h @@ -0,0 +1,43 @@ +/* + Copyright (C) 2025 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic 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. + + DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "table_dialog.h" + + +class CheckBox; +class Film; + + +class EncryptionSettingsDialog : public TableDialog +{ +public: + EncryptionSettingsDialog(wxWindow* parent, std::shared_ptr<const Film> film); + + bool picture() const; + bool sound() const; + bool text() const; + +private: + CheckBox* _picture; + CheckBox* _sound; + CheckBox* _text; +}; + diff --git a/src/wx/wscript b/src/wx/wscript index ace747514..d3f3db88a 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -80,6 +80,7 @@ sources = """ drive_wipe_warning_dialog.cc email_dialog.cc email_preferences_page.cc + encryption_settings_dialog.cc export_subtitles_dialog.cc export_video_file_dialog.cc extra_kdm_email_dialog.cc diff --git a/test/atmos_test.cc b/test/atmos_test.cc index 33a427321..776cc9310 100644 --- a/test/atmos_test.cc +++ b/test/atmos_test.cc @@ -68,7 +68,9 @@ BOOST_AUTO_TEST_CASE(atmos_encrypted_passthrough_test) auto content = content_factory(TestPaths::private_data() / "atmos_asset.mxf"); auto film = new_test_film("atmos_encrypted_passthrough_test", content, &cl); - film->set_encrypted(true); + film->set_encrypt_picture(true); + film->set_encrypt_sound(true); + film->set_encrypt_text(true); film->_key = dcp::Key("4fac12927eb122af1c2781aa91f3a4cc"); make_and_verify_dcp(film, { dcp::VerificationNote::Code::MISSING_CPL_METADATA }); diff --git a/test/data b/test/data -Subproject 024cb24f49525e0cc172d4e91d75e0c4d81ef6e +Subproject df51596458d402f40a5afaf943225ce7e95cdf6 diff --git a/test/dcp_decoder_test.cc b/test/dcp_decoder_test.cc index cf9950aae..87cea6691 100644 --- a/test/dcp_decoder_test.cc +++ b/test/dcp_decoder_test.cc @@ -64,7 +64,9 @@ BOOST_AUTO_TEST_CASE (check_reuse_old_data_test) auto encrypted = new_test_film("check_reuse_old_data_decrypted"); encrypted->examine_and_add_content(content_factory("test/data/flat_red.png")); BOOST_REQUIRE (!wait_for_jobs()); - encrypted->set_encrypted (true); + encrypted->set_encrypt_picture(true); + encrypted->set_encrypt_sound(true); + encrypted->set_encrypt_text(true); make_and_verify_dcp (encrypted); dcp::DCP encrypted_dcp (encrypted->dir(encrypted->dcp_name())); diff --git a/test/dcp_digest_file_test.cc b/test/dcp_digest_file_test.cc index 5c12fbb33..2e717896f 100644 --- a/test/dcp_digest_file_test.cc +++ b/test/dcp_digest_file_test.cc @@ -67,7 +67,8 @@ BOOST_AUTO_TEST_CASE (dcp_digest_file_test2) auto red = content_factory("test/data/flat_red.png"); auto ov = new_test_film("dcp_digest_file_test2_ov", red); - ov->set_encrypted (true); + ov->set_encrypt_picture(true); + ov->set_encrypt_sound(true); make_and_verify_dcp (ov); auto ov_key_check = get_key_from_digest ("build/test/dcp_digest_file_test2_ov/" + ov->dcp_name() + ".dcpdig"); @@ -91,7 +92,8 @@ BOOST_AUTO_TEST_CASE (dcp_digest_file_test2) ov_dcp->set_reference_video (true); ov_dcp->set_reference_audio (true); auto vf = new_test_film("dcp_digest_file_test2_vf", { ov_dcp }); - vf->set_encrypted (true); + vf->set_encrypt_picture(true); + vf->set_encrypt_sound(true); make_and_verify_dcp(vf, {dcp::VerificationNote::Code::EXTERNAL_ASSET}, false); auto vf_key_check = get_key_from_digest ("build/test/dcp_digest_file_test2_vf/" + vf->dcp_name() + ".dcpdig"); diff --git a/test/encryption_test.cc b/test/encryption_test.cc index 97359cd44..31e31d53c 100644 --- a/test/encryption_test.cc +++ b/test/encryption_test.cc @@ -27,9 +27,16 @@ #include "test.h" #include <dcp/cpl.h> #include <dcp/dcp.h> +#include <dcp/j2k_transcode.h> +#include <dcp/mono_j2k_picture_asset.h> +#include <dcp/reel.h> +#include <dcp/reel_picture_asset.h> +#include <dcp/reel_sound_asset.h> +#include <dcp/reel_text_asset.h> #include <boost/test/unit_test.hpp> +using std::dynamic_pointer_cast; using std::make_shared; @@ -38,7 +45,9 @@ BOOST_AUTO_TEST_CASE (smpte_dcp_with_subtitles_can_be_decrypted) auto content = content_factory("test/data/15s.srt"); auto film = new_test_film("smpte_dcp_with_subtitles_can_be_decrypted", content); film->set_interop (false); - film->set_encrypted (true); + film->set_encrypt_picture(true); + film->set_encrypt_sound(true); + film->set_encrypt_text(true); make_and_verify_dcp ( film, { @@ -67,3 +76,98 @@ BOOST_AUTO_TEST_CASE (smpte_dcp_with_subtitles_can_be_decrypted) BOOST_CHECK (examiner.kdm_valid()); } + +BOOST_AUTO_TEST_CASE(encrypt_only_picture) +{ + auto picture = content_factory("test/data/flat_red.png")[0]; + auto text = content_factory("test/data/15s.srt")[0]; + auto film = new_test_film("encrypt_only_picture", { picture, text }); + film->set_encrypt_picture(true); + film->set_encrypt_sound(false); + film->set_encrypt_text(false); + /* clairmeta says "Encrypted is not coherent for all reels" */ + make_and_verify_dcp( + film, + { + dcp::VerificationNote::Code::MISSING_CPL_METADATA, + dcp::VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED, + dcp::VerificationNote::Code::PARTIALLY_ENCRYPTED, + dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, + dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME, + }, true, false); + + dcp::DCP dcp(film->dir(film->dcp_name())); + dcp.read(); + BOOST_REQUIRE_EQUAL(dcp.cpls().size(), 1U); + auto cpl = dcp.cpls()[0]; + BOOST_REQUIRE(cpl->file()); + + auto dcp_picture = dynamic_pointer_cast<dcp::MonoJ2KPictureAsset>(cpl->reels()[0]->main_picture()->asset()); + auto reader = dcp_picture->start_read(); + auto frame = reader->get_frame(0); + BOOST_CHECK_THROW(dcp::decompress_j2k(frame->data(), frame->size(), 0), dcp::J2KDecompressionError); +} + + +BOOST_AUTO_TEST_CASE(encrypt_only_sound) +{ + auto picture = content_factory("test/data/flat_red.png")[0]; + auto text = content_factory("test/data/15s.srt")[0]; + auto film = new_test_film("encrypt_only_picture", { picture, text }); + film->set_encrypt_picture(false); + film->set_encrypt_sound(true); + film->set_encrypt_text(false); + /* clairmeta says "Encrypted is not coherent for all reels" */ + make_and_verify_dcp( + film, + { + dcp::VerificationNote::Code::MISSING_CPL_METADATA, + dcp::VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED, + dcp::VerificationNote::Code::PARTIALLY_ENCRYPTED, + dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, + dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME, + }, true, false); + + dcp::DCP dcp(film->dir(film->dcp_name())); + dcp.read(); + BOOST_REQUIRE_EQUAL(dcp.cpls().size(), 1U); + auto cpl = dcp.cpls()[0]; + BOOST_REQUIRE(cpl->file()); + + auto dcp_sound = dynamic_pointer_cast<dcp::SoundAsset>(cpl->reels()[0]->main_sound()->asset()); + auto reader = dcp_sound->start_read(); + auto frame = reader->get_frame(0); + int zeros = 0; + for (int i = 0; i < 1024; ++i) { + zeros += frame->data()[i] == 0; + } + BOOST_CHECK(zeros != 1024); +} + + +BOOST_AUTO_TEST_CASE(encrypt_only_text) +{ + auto picture = content_factory("test/data/flat_red.png")[0]; + auto text = content_factory("test/data/15s.srt")[0]; + auto film = new_test_film("encrypt_only_picture", { picture, text }); + film->set_encrypt_picture(false); + film->set_encrypt_sound(false); + film->set_encrypt_text(true); + make_and_verify_dcp( + film, + { + dcp::VerificationNote::Code::MISSING_CPL_METADATA, + dcp::VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED, + dcp::VerificationNote::Code::PARTIALLY_ENCRYPTED, + }); + + dcp::DCP dcp(film->dir(film->dcp_name())); + dcp.read(); + BOOST_REQUIRE_EQUAL(dcp.cpls().size(), 1U); + auto cpl = dcp.cpls()[0]; + BOOST_REQUIRE(cpl->file()); + + auto dcp_subtitle = dynamic_pointer_cast<dcp::TextAsset>(cpl->reels()[0]->main_subtitle()->asset()); + BOOST_CHECK_THROW(dcp_subtitle->xml_as_string(), dcp::ProgrammingError); +} + diff --git a/test/import_dcp_test.cc b/test/import_dcp_test.cc index 8782e7f73..0201594c7 100644 --- a/test/import_dcp_test.cc +++ b/test/import_dcp_test.cc @@ -58,7 +58,8 @@ BOOST_AUTO_TEST_CASE (import_dcp_test) auto c = make_shared<FFmpegContent>("test/data/test.mp4"); auto A = new_test_film("import_dcp_test", { c }); - A->set_encrypted (true); + A->set_encrypt_picture(true); + A->set_encrypt_sound(true); A->set_dcp_content_type(DCPContentType::from_isdcf_name("TLR")); make_and_verify_dcp (A); diff --git a/test/kdm_cli_test.cc b/test/kdm_cli_test.cc index 4292da17e..4bc3ddf46 100644 --- a/test/kdm_cli_test.cc +++ b/test/kdm_cli_test.cc @@ -276,7 +276,9 @@ BOOST_AUTO_TEST_CASE(kdm_cli_specify_cert) boost::filesystem::remove(kdm_filename, ec); auto film = new_test_film("kdm_cli_specify_cert", content_factory("test/data/flat_red.png")); - film->set_encrypted(true); + film->set_encrypt_picture(true); + film->set_encrypt_sound(true); + film->set_encrypt_text(true); film->set_name("KDMCLI"); film->set_use_isdcf_name(false); make_and_verify_dcp(film); diff --git a/test/kdm_naming_test.cc b/test/kdm_naming_test.cc index 2308e6ac6..efc96b2c2 100644 --- a/test/kdm_naming_test.cc +++ b/test/kdm_naming_test.cc @@ -89,7 +89,8 @@ BOOST_AUTO_TEST_CASE (single_kdm_naming_test) film->set_name ("my_great_film"); film->examine_and_add_content(content_factory("test/data/flat_black.png")); BOOST_REQUIRE (!wait_for_jobs()); - film->set_encrypted (true); + film->set_encrypt_picture(true); + film->set_encrypt_sound(true); make_and_verify_dcp (film); auto cpls = film->cpls (); BOOST_REQUIRE(cpls.size() == 1); @@ -156,7 +157,8 @@ BOOST_AUTO_TEST_CASE(directory_kdm_naming_test) ); film->set_name ("my_great_film"); - film->set_encrypted (true); + film->set_encrypt_picture(true); + film->set_encrypt_sound(true); make_and_verify_dcp (film); auto cpls = film->cpls (); BOOST_REQUIRE(cpls.size() == 1); diff --git a/test/player_test.cc b/test/player_test.cc index ab4560ffa..684cede04 100644 --- a/test/player_test.cc +++ b/test/player_test.cc @@ -463,7 +463,9 @@ BOOST_AUTO_TEST_CASE (encrypted_dcp_with_no_kdm_gives_no_butler_error) auto film = new_test_film("encrypted_dcp_with_no_kdm_gives_no_butler_error", { content }); int constexpr length = 24 * 25; content->video->set_length(length); - film->set_encrypted (true); + film->set_encrypt_picture(true); + film->set_encrypt_sound(true); + film->set_encrypt_text(true); make_and_verify_dcp ( film, { diff --git a/test/recover_test.cc b/test/recover_test.cc index ba06fcde9..73d54b017 100644 --- a/test/recover_test.cc +++ b/test/recover_test.cc @@ -142,7 +142,8 @@ BOOST_AUTO_TEST_CASE (recover_test_2d_encrypted, * boost::unit_test::depends_on( { auto content = make_shared<FFmpegContent>("test/data/count300bd24.m2ts"); auto film = new_test_film("recover_test_2d_encrypted", { content }); - film->set_encrypted (true); + film->set_encrypt_picture(true); + film->set_encrypt_sound(true); film->_key = dcp::Key("eafcb91c9f5472edf01f3a2404c57258"); film->set_video_bit_rate(VideoEncoding::JPEG2000, 100000000); diff --git a/test/remake_id_test.cc b/test/remake_id_test.cc index 0673a4283..579a711a8 100644 --- a/test/remake_id_test.cc +++ b/test/remake_id_test.cc @@ -69,7 +69,8 @@ BOOST_AUTO_TEST_CASE (remake_id_test2) /* Make a DCP */ auto content = content_factory("test/data/flat_red.png"); auto film = new_test_film("remake_id_test2_1", content); - film->set_encrypted (true); + film->set_encrypt_picture(true); + film->set_encrypt_sound(true); make_and_verify_dcp (film); /* Remove and remake it */ diff --git a/test/vf_kdm_test.cc b/test/vf_kdm_test.cc index 3fd8bc327..a5989088b 100644 --- a/test/vf_kdm_test.cc +++ b/test/vf_kdm_test.cc @@ -55,7 +55,8 @@ BOOST_AUTO_TEST_CASE (vf_kdm_test) auto A = new_test_film("vf_kdm_test_ov", { c }); A->set_interop (true); A->set_dcp_content_type(DCPContentType::from_isdcf_name("TLR")); - A->set_encrypted (true); + A->set_encrypt_picture(true); + A->set_encrypt_sound(true); make_and_verify_dcp (A, {dcp::VerificationNote::Code::INVALID_STANDARD}); dcp::DCP A_dcp ("build/test/vf_kdm_test_ov/" + A->dcp_name()); @@ -79,7 +80,8 @@ BOOST_AUTO_TEST_CASE (vf_kdm_test) B->set_interop(true); d->set_reference_video (true); - B->set_encrypted (true); + B->set_encrypt_picture(true); + B->set_encrypt_sound(true); make_and_verify_dcp (B, {dcp::VerificationNote::Code::INVALID_STANDARD, dcp::VerificationNote::Code::EXTERNAL_ASSET}); dcp::DCP B_dcp ("build/test/vf_kdm_test_vf/" + B->dcp_name()); |
