Add a hint about certificate validity, moving some things around
authorCarl Hetherington <cth@carlh.net>
Mon, 14 Feb 2022 10:00:52 +0000 (11:00 +0100)
committerCarl Hetherington <cth@carlh.net>
Mon, 14 Feb 2022 10:00:52 +0000 (11:00 +0100)
so that it's easier for the user to re-make the certificates if
they need to.

src/lib/config.cc
src/lib/config.h
src/lib/hints.cc
src/lib/hints.h
src/wx/config_dialog.cc
src/wx/config_dialog.h
src/wx/make_chain_dialog.cc
src/wx/make_chain_dialog.h
test/hints_test.cc

index 37168296611746e713d1d6bb99b8089c127d5109..bab15ecb705768025d00e58c3e2bf7ebd4982761 100644 (file)
@@ -450,25 +450,7 @@ try
                }
        }
 
-       optional<BadReason> bad;
-
-       for (auto const& i: _signer_chain->unordered()) {
-               if (i.has_utf8_strings()) {
-                       bad = BAD_SIGNER_UTF8_STRINGS;
-               }
-               if ((i.not_after().year() - i.not_before().year()) > 15) {
-                       bad = BAD_SIGNER_VALIDITY_TOO_LONG;
-               }
-       }
-
-       if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
-               bad = BAD_SIGNER_INCONSISTENT;
-       }
-
-       if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
-               bad = BAD_DECRYPTION_INCONSISTENT;
-       }
-
+       auto bad = check_certificates ();
        if (bad) {
                auto const remake = Bad(*bad);
                if (remake && *remake) {
@@ -1470,3 +1452,29 @@ Config::add_custom_language (dcp::LanguageTag tag)
        }
 }
 
+
+optional<Config::BadReason>
+Config::check_certificates () const
+{
+       optional<BadReason> bad;
+
+       for (auto const& i: _signer_chain->unordered()) {
+               if (i.has_utf8_strings()) {
+                       bad = BAD_SIGNER_UTF8_STRINGS;
+               }
+               if ((i.not_after().year() - i.not_before().year()) > 15) {
+                       bad = BAD_SIGNER_VALIDITY_TOO_LONG;
+               }
+       }
+
+       if (!_signer_chain->chain_valid() || !_signer_chain->private_key_valid()) {
+               bad = BAD_SIGNER_INCONSISTENT;
+       }
+
+       if (!_decryption_chain->chain_valid() || !_decryption_chain->private_key_valid()) {
+               bad = BAD_DECRYPTION_INCONSISTENT;
+       }
+
+       return bad;
+}
+
index be4e6ecf13814ac01adb54916efe3b9c4d976d1f..e01eab780e8c9ce4dfabd2aee89caa7675d1845e 100644 (file)
@@ -1084,6 +1084,8 @@ public:
        void rename_template (std::string old_name, std::string new_name) const;
        void delete_template (std::string name) const;
 
+       boost::optional<BadReason> check_certificates () const;
+
        static Config* instance ();
        static void drop ();
        static void restore_defaults ();
index 40b51e81791544e70a6bdba7d7466dcfc605d4da..0f1cfece8e2040e8a8a8257a13126b3271e57520 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2016-2021 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2016-2022 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -23,6 +23,7 @@
 #include "audio_content.h"
 #include "audio_processor.h"
 #include "compose.hpp"
+#include "config.h"
 #include "content.h"
 #include "cross.h"
 #include "dcp_content_type.h"
@@ -381,6 +382,7 @@ try
 
        auto content = film->content ();
 
+       check_certificates ();
        check_interop ();
        check_big_font_files ();
        check_few_audio_channels ();
@@ -652,3 +654,31 @@ Hints::check_audio_language ()
        }
 }
 
+
+void
+Hints::check_certificates ()
+{
+       auto bad = Config::instance()->check_certificates();
+       if (!bad) {
+               return;
+       }
+
+       switch (*bad) {
+       case Config::BAD_SIGNER_UTF8_STRINGS:
+               hint(_("The certificate chain that DCP-o-matic uses for signing DCPs and KDMs contains a small error "
+                      "which will prevent DCPs from being validated correctly on some systems.  You are advised to "
+                      "re-create the signing certificate chain by clicking the \"Re-make certificates and key...\" "
+                      "button in the Keys page of Preferences."));
+               break;
+       case Config::BAD_SIGNER_VALIDITY_TOO_LONG:
+               hint(_("The certificate chain that DCP-o-matic uses for signing DCPs and KDMs has a validity period "
+                      "that is too long.  This will cause problems playing back DCPs on some systems. "
+                      "You are advised to re-create the signing certificate chain by clicking the "
+                      "\"Re-make certificates and key...\" button in the Keys page of Preferences."));
+               break;
+       default:
+               /* Some bad situations can't happen here as DCP-o-matic would have refused to start until they are fixed */
+               break;
+       }
+}
+
index 6553593a06e761da7b908ce1949f5dc3fc06fa58..985fa1910d9b10733b88a4f827687bf11de9455a 100644 (file)
@@ -65,6 +65,7 @@ private:
        void closed_caption (PlayerText text, dcpomatic::DCPTimePeriod period);
        void open_subtitle (PlayerText text, dcpomatic::DCPTimePeriod period);
 
+       void check_certificates ();
        void check_interop ();
        void check_big_font_files ();
        void check_few_audio_channels ();
index c5879d3bbd3d31da6af802ffa631b484b436218b..f91ebe670244d9de702534459cc59bb99cd2dd84 100644 (file)
@@ -536,62 +536,15 @@ CertificateChainEditor::update_certificate_list ()
 void
 CertificateChainEditor::remake_certificates ()
 {
-       auto chain = _get();
-
-       string subject_organization_name;
-       string subject_organizational_unit_name;
-       string root_common_name;
-       string intermediate_common_name;
-       string leaf_common_name;
-
-       auto all = chain->root_to_leaf ();
-
-       if (all.size() >= 1) {
-               /* Have a root */
-               subject_organization_name = chain->root().subject_organization_name ();
-               subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
-               root_common_name = chain->root().subject_common_name ();
-       }
-
-       if (all.size() >= 2) {
-               /* Have a leaf */
-               leaf_common_name = chain->leaf().subject_common_name ();
-       }
-
-       if (all.size() >= 3) {
-               /* Have an intermediate */
-               dcp::CertificateChain::List::iterator i = all.begin ();
-               ++i;
-               intermediate_common_name = i->subject_common_name ();
-       }
-
        if (_nag_alter()) {
                /* Cancel was clicked */
                return;
        }
 
-       auto d = new MakeChainDialog (
-               this,
-               subject_organization_name,
-               subject_organizational_unit_name,
-               root_common_name,
-               intermediate_common_name,
-               leaf_common_name
-               );
+       auto d = new MakeChainDialog (this, _get());
 
        if (d->ShowModal () == wxID_OK) {
-               _set (
-                       make_shared<dcp::CertificateChain> (
-                               openssl_path (),
-                               CERTIFICATE_VALIDITY_PERIOD,
-                               d->organisation (),
-                               d->organisational_unit (),
-                               d->root_common_name (),
-                               d->intermediate_common_name (),
-                               d->leaf_common_name ()
-                               )
-                       );
-
+               _set (d->get());
                update_certificate_list ();
                update_private_key ();
        }
@@ -694,18 +647,18 @@ KeysPage::setup ()
                sizer->Add (m, 0, wxALL | wxEXPAND, _border);
        }
 
-       auto buttons = new wxBoxSizer (wxVERTICAL);
+       auto kdm_buttons = new wxBoxSizer (wxVERTICAL);
 
        auto export_decryption_certificate = new Button (_panel, _("Export KDM decryption leaf certificate..."));
-       buttons->Add (export_decryption_certificate, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
+       kdm_buttons->Add (export_decryption_certificate, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
        auto export_settings = new Button (_panel, _("Export all KDM decryption settings..."));
-       buttons->Add (export_settings, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
+       kdm_buttons->Add (export_settings, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
        auto import_settings = new Button (_panel, _("Import all KDM decryption settings..."));
-       buttons->Add (import_settings, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
+       kdm_buttons->Add (import_settings, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
        auto decryption_advanced = new Button (_panel, _("Advanced..."));
-       buttons->Add (decryption_advanced, 0);
+       kdm_buttons->Add (decryption_advanced, 0);
 
-       sizer->Add (buttons, 0, wxLEFT, _border);
+       sizer->Add (kdm_buttons, 0, wxLEFT, _border);
 
        export_decryption_certificate->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_certificate, this));
        export_settings->Bind (wxEVT_BUTTON, bind (&KeysPage::export_decryption_chain_and_key, this));
@@ -718,11 +671,31 @@ KeysPage::setup ()
                sizer->Add (m, 0, wxALL | wxEXPAND, _border);
        }
 
+       auto signing_buttons = new wxBoxSizer (wxVERTICAL);
+
        auto signing_advanced = new Button (_panel, _("Advanced..."));
-       sizer->Add (signing_advanced, 0, wxLEFT | wxBOTTOM, _border);
+       signing_buttons->Add (signing_advanced, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
+       auto remake_signing = new Button (_panel, _("Re-make certificates and key..."));
+       signing_buttons->Add (remake_signing, 0, wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
+
+       sizer->Add (signing_buttons, 0, wxLEFT, _border);
+
        signing_advanced->Bind (wxEVT_BUTTON, bind (&KeysPage::signing_advanced, this));
+       remake_signing->Bind (wxEVT_BUTTON, bind(&KeysPage::remake_signing, this));
 }
 
+
+void
+KeysPage::remake_signing ()
+{
+       auto d = new MakeChainDialog (_panel, Config::instance()->signer_chain());
+
+       if (d->ShowModal () == wxID_OK) {
+               Config::instance()->set_signer_chain(d->get());
+       }
+}
+
+
 void
 KeysPage::decryption_advanced ()
 {
index 9924909273a0b62c84f662adebb3f1fc0fd5f592..b7f3a269f9ac442e1cbe887cf30234da24c18daf 100644 (file)
@@ -176,6 +176,7 @@ private:
        void signing_advanced ();
        void export_decryption_chain_and_key ();
        void import_decryption_chain_and_key ();
+       void remake_signing ();
 };
 
 
index d681c00e876f866e30caf59863854c5c1c6f4b3c..4255fb307881dcba3a98930b87db7f377f8793dd 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2014-2018 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2014-2022 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 
 */
 
+
 #include "make_chain_dialog.h"
 #include "static_text.h"
+#include "lib/cross.h"
+#include "lib/util.h"
+#include <dcp/certificate_chain.h>
 #include <boost/algorithm/string.hpp>
 
+
+using std::make_shared;
+using std::shared_ptr;
 using std::string;
 
+
 MakeChainDialog::MakeChainDialog (
        wxWindow* parent,
-       string organisation,
-       string organisational_unit_name,
-       string root_common_name,
-       string intermediate_common_name,
-       string leaf_common_name
+       shared_ptr<const dcp::CertificateChain> chain
        )
        : TableDialog (parent, _("Make certificate chain"), 2, 1, true)
 {
+       string subject_organization_name;
+       string subject_organizational_unit_name;
+       string root_common_name;
+       string intermediate_common_name;
+       string leaf_common_name;
+
+       auto all = chain->root_to_leaf ();
+
+       if (all.size() >= 1) {
+               /* Have a root */
+               subject_organization_name = chain->root().subject_organization_name ();
+               subject_organizational_unit_name = chain->root().subject_organizational_unit_name ();
+               root_common_name = chain->root().subject_common_name ();
+       }
+
+       if (all.size() >= 2) {
+               /* Have a leaf */
+               leaf_common_name = chain->leaf().subject_common_name ();
+       }
+
+       if (all.size() >= 3) {
+               /* Have an intermediate */
+               dcp::CertificateChain::List::iterator i = all.begin ();
+               ++i;
+               intermediate_common_name = i->subject_common_name ();
+       }
+
        wxTextValidator validator (wxFILTER_EXCLUDE_CHAR_LIST);
        validator.SetCharExcludes (wxT ("/"));
 
@@ -50,14 +81,14 @@ MakeChainDialog::MakeChainDialog (
        }
 
        add (_("Organisation"), true);
-       add (_organisation = new wxTextCtrl (this, wxID_ANY, std_to_wx (organisation), wxDefaultPosition, wxSize (480, -1), 0, validator));
+       add (_organisation = new wxTextCtrl (this, wxID_ANY, std_to_wx(subject_organization_name), wxDefaultPosition, wxSize (480, -1), 0, validator));
        add (_("Organisational unit"), true);
-       add (_organisational_unit = new wxTextCtrl (this, wxID_ANY, std_to_wx (organisational_unit_name), wxDefaultPosition, wxDefaultSize, 0, validator));
+       add (_organisational_unit = new wxTextCtrl (this, wxID_ANY, std_to_wx(subject_organizational_unit_name), wxDefaultPosition, wxDefaultSize, 0, validator));
 
        add (_("Root common name"), true);
 
        {
-               wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+               auto s = new wxBoxSizer (wxHORIZONTAL);
                s->Add (new StaticText (this, wxT (".")), 0, wxALIGN_CENTER_VERTICAL);
                s->Add (_root_common_name = new wxTextCtrl (
                                this, wxID_ANY, std_to_wx (root_common_name), wxDefaultPosition, wxDefaultSize, 0, validator), 1, wxALIGN_CENTER_VERTICAL
@@ -68,7 +99,7 @@ MakeChainDialog::MakeChainDialog (
        add (_("Intermediate common name"), true);
 
        {
-               wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+               auto s = new wxBoxSizer (wxHORIZONTAL);
                s->Add (new StaticText (this, wxT (".")), 0, wxALIGN_CENTER_VERTICAL);
                s->Add (_intermediate_common_name = new wxTextCtrl (
                                this, wxID_ANY, std_to_wx (intermediate_common_name), wxDefaultPosition, wxDefaultSize, 0, validator), 1, wxALIGN_CENTER_VERTICAL
@@ -79,7 +110,7 @@ MakeChainDialog::MakeChainDialog (
        add (_("Leaf common name"), true);
 
        {
-               wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+               auto s = new wxBoxSizer (wxHORIZONTAL);
                s->Add (new StaticText (this, wxT ("CS.")), 0, wxALIGN_CENTER_VERTICAL);
                s->Add (_leaf_common_name = new wxTextCtrl (
                                this, wxID_ANY, std_to_wx (leaf_common_name), wxDefaultPosition, wxDefaultSize, 0, validator), 1, wxALIGN_CENTER_VERTICAL
@@ -91,3 +122,18 @@ MakeChainDialog::MakeChainDialog (
 
        _organisation->SetFocus ();
 }
+
+
+shared_ptr<dcp::CertificateChain>
+MakeChainDialog::get () const
+{
+       return make_shared<dcp::CertificateChain>(
+               openssl_path(),
+               CERTIFICATE_VALIDITY_PERIOD,
+               wx_to_std(_organisation->GetValue()),
+               wx_to_std(_organisational_unit->GetValue()),
+               "." + wx_to_std(_root_common_name->GetValue()),
+               "." + wx_to_std(_intermediate_common_name->GetValue()),
+               "CS." + wx_to_std(_leaf_common_name->GetValue())
+               );
+}
index 5ad62430e25f2f1efbd51931ee095d98dd2a7de6..018db99a202c2e2ce7d42ef3c0db60c870ea789b 100644 (file)
 class MakeChainDialog : public TableDialog
 {
 public:
-       MakeChainDialog (
-               wxWindow* parent,
-               std::string organisation,
-               std::string organisational_unit_name,
-               std::string root_common_name,
-               std::string intermediate_common_name,
-               std::string leaf_common_name
-               );
-
-       std::string organisation () const {
-               return wx_to_std (_organisation->GetValue ());
-       }
-
-       std::string organisational_unit () const {
-               return wx_to_std (_organisational_unit->GetValue ());
-       }
-
-       std::string root_common_name () const {
-               return "." + wx_to_std (_root_common_name->GetValue ());
-       }
-
-       std::string intermediate_common_name () const {
-               return "." + wx_to_std (_intermediate_common_name->GetValue ());
-       }
-
-       std::string leaf_common_name () const {
-               return "CS." + wx_to_std (_leaf_common_name->GetValue ());
-       }
+       MakeChainDialog (wxWindow* parent, std::shared_ptr<const dcp::CertificateChain> chain);
 
+       std::shared_ptr<dcp::CertificateChain> get () const;
 
 private:
        wxTextCtrl* _organisation;
index c228cd07af4c6b38089c259dd9242a0b0785b20f..51374b274d74546faa5dc18e7f39f8ebda510c53 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2020-2021 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2020-2022 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -20,6 +20,7 @@
 
 
 #include "lib/audio_content.h"
+#include "lib/config.h"
 #include "lib/content.h"
 #include "lib/content_factory.h"
 #include "lib/cross.h"
@@ -254,6 +255,24 @@ BOOST_AUTO_TEST_CASE (hints_audio_with_no_language)
                "Some of your content has audio but you have not set the audio language.  It is advisable to set the audio language "
                "in the \"DCP\" tab unless your audio has no spoken parts."
                );
+}
+
+
+BOOST_AUTO_TEST_CASE (hints_certificate_validity)
+{
+       ConfigRestorer cr;
+
+       Config::instance()->set_signer_chain(make_shared<dcp::CertificateChain>(openssl_path(), 40 * 365));
 
+       auto film = new_test_film2 ("hints_certificate_validity");
+       auto hints = get_hints (film);
+       BOOST_REQUIRE_EQUAL (hints.size(), 1U);
+       BOOST_CHECK_EQUAL (
+               hints[0],
+               "The certificate chain that DCP-o-matic uses for signing DCPs and KDMs has a validity period "
+               "that is too long.  This will cause problems playing back DCPs on some systems. "
+               "You are advised to re-create the signing certificate chain by clicking the "
+               "\"Re-make certificates and key...\" button in the Keys page of Preferences."
+               );
 }