Attempt to fix Sony digest validation by indenting the <Signer>
authorCarl Hetherington <cth@carlh.net>
Sun, 20 Jan 2019 20:26:41 +0000 (20:26 +0000)
committerCarl Hetherington <cth@carlh.net>
Thu, 24 Jan 2019 02:03:05 +0000 (02:03 +0000)
and <Signature> before signing.  This is in the belief that, perhaps,
the Sony software "reformats" the XML before checking that the signature
is correct (or something).

src/certificate_chain.cc
src/certificate_chain.h
src/cpl.cc
src/dcp.cc
src/encrypted_kdm.cc
src/pkl.cc
tools/wscript

index 6b4216b5eeadb2a73f2428aaf26a7d2f6d1066e7..ebc3cc7ff215d5e2691a296f73fdaecc6e1dd1bd 100644 (file)
@@ -552,6 +552,33 @@ CertificateChain::root_to_leaf () const
        throw CertificateChainError ("certificate chain is not consistent");
 }
 
+static string
+spaces (int n)
+{
+       string s = "";
+       for (int i = 0; i < n; ++i) {
+               s += " ";
+       }
+       return s;
+}
+
+static void
+indent (xmlpp::Element* element, int initial)
+{
+       xmlpp::Node* last = 0;
+       BOOST_FOREACH (xmlpp::Node * n, element->get_children()) {
+               xmlpp::Element* e = dynamic_cast<xmlpp::Element*>(n);
+               if (e) {
+                       element->add_child_text_before (e, "\n" + spaces(initial + 2));
+                       indent (e, initial + 2);
+                       last = n;
+               }
+       }
+       if (last) {
+               element->add_child_text (last, "\n" + spaces(initial));
+       }
+}
+
 /** Add a &lt;Signer&gt; and &lt;ds:Signature&gt; nodes to an XML node.
  *  @param parent XML node to add to.
  *  @param standard INTEROP or SMPTE.
@@ -561,6 +588,7 @@ CertificateChain::sign (xmlpp::Element* parent, Standard standard) const
 {
        /* <Signer> */
 
+       parent->add_child_text("  ");
        xmlpp::Element* signer = parent->add_child("Signer");
        signer->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "dsig");
        xmlpp::Element* data = signer->add_child("X509Data", "dsig");
@@ -569,11 +597,15 @@ CertificateChain::sign (xmlpp::Element* parent, Standard standard) const
        serial_element->add_child("X509SerialNumber", "dsig")->add_child_text (leaf().serial());
        data->add_child("X509SubjectName", "dsig")->add_child_text (leaf().subject());
 
+       indent (signer, 2);
+
        /* <Signature> */
 
+       parent->add_child_text("\n  ");
        xmlpp::Element* signature = parent->add_child("Signature");
        signature->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "dsig");
        signature->set_namespace ("dsig");
+       parent->add_child_text("\n");
 
        xmlpp::Element* signed_info = signature->add_child ("SignedInfo", "dsig");
        signed_info->add_child("CanonicalizationMethod", "dsig")->set_attribute ("Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315");
@@ -608,7 +640,7 @@ CertificateChain::sign (xmlpp::Element* parent, Standard standard) const
  *  @param ns Namespace to use for the signature XML nodes.
  */
 void
-CertificateChain::add_signature_value (xmlpp::Node* parent, string ns) const
+CertificateChain::add_signature_value (xmlpp::Element* parent, string ns) const
 {
        cxml::Node cp (parent);
        xmlpp::Node* key_info = cp.node_child("KeyInfo")->node ();
@@ -639,6 +671,7 @@ CertificateChain::add_signature_value (xmlpp::Node* parent, string ns) const
                throw runtime_error ("could not read private key");
        }
 
+       indent (parent, 2);
        int const r = xmlSecDSigCtxSign (signature_context, parent->cobj ());
        if (r < 0) {
                throw MiscError (String::compose ("could not sign (%1)", r));
index 365632fe138b21a6bf4f5c4d4281563034024325..8b54604d2f6d38e4a2a549231e6e8368ecb35b63 100644 (file)
@@ -103,7 +103,7 @@ public:
        bool private_key_valid () const;
 
        void sign (xmlpp::Element* parent, Standard standard) const;
-       void add_signature_value (xmlpp::Node* parent, std::string ns) const;
+       void add_signature_value (xmlpp::Element* parent, std::string ns) const;
 
        boost::optional<std::string> key () const {
                return _key;
index 726d69b722ef6f35f5f92d0491d3349a81450a08..4a594b5a477a305d65a2337ceadbe28f814b3f16 100644 (file)
@@ -140,6 +140,10 @@ CPL::write_xml (boost::filesystem::path file, Standard standard, shared_ptr<cons
                root = doc.create_root_node ("CompositionPlaylist", cpl_smpte_ns);
        }
 
+       if (signer) {
+               root->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "dsig");
+       }
+
        root->add_child("Id")->add_child_text ("urn:uuid:" + _id);
        root->add_child("AnnotationText")->add_child_text (_metadata.annotation_text);
        root->add_child("IssueDate")->add_child_text (_metadata.issue_date);
@@ -164,8 +168,7 @@ CPL::write_xml (boost::filesystem::path file, Standard standard, shared_ptr<cons
                signer->sign (root, standard);
        }
 
-       /* This must not be the _formatted version otherwise signature digests will be wrong */
-       doc.write_to_file (file.string (), "UTF-8");
+       doc.write_to_file_formatted (file.string(), "UTF-8");
 
        set_file (file);
 }
index 8bf2f02fe97811d255361c28ffa5cc67008babf4..5d908530edc43561f5e8d1231a1f4837fbd6c4b4 100644 (file)
@@ -348,7 +348,7 @@ DCP::write_volindex (Standard standard) const
        }
 
        root->add_child("Index")->add_child_text ("1");
-       doc.write_to_file (p.string (), "UTF-8");
+       doc.write_to_file_formatted (p.string (), "UTF-8");
 }
 
 void
@@ -417,8 +417,7 @@ DCP::write_assetmap (Standard standard, string pkl_uuid, boost::filesystem::path
                i->write_to_assetmap (asset_list, _directory);
        }
 
-       /* This must not be the _formatted version otherwise signature digests will be wrong */
-       doc.write_to_file (p.string (), "UTF-8");
+       doc.write_to_file_formatted (p.string (), "UTF-8");
 }
 
 /** Write all the XML files for this DCP.
index 23052f8a2c438bd358acb3e0b8f63628a68c5b30..9d581451ff0dba97153dacbb33f8f65ba6dcbccd 100644 (file)
@@ -670,7 +670,7 @@ EncryptedKDM::EncryptedKDM (
        xmlpp::Node::NodeList children = doc->get_root_node()->get_children ();
        for (xmlpp::Node::NodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
                if ((*i)->get_name() == "Signature") {
-                       signer->add_signature_value (*i, "ds");
+                       signer->add_signature_value (dynamic_cast<xmlpp::Element*>(*i), "ds");
                }
        }
 
index 43c7d0922376f270cf08eba60e585f0e4cd4932a..9910b1906f8241a4232ca1b26e40f46a2d6362ba 100644 (file)
@@ -113,7 +113,7 @@ PKL::write (boost::filesystem::path file, shared_ptr<const CertificateChain> sig
                signer->sign (pkl, _standard);
        }
 
-       doc.write_to_file (file.string(), "UTF-8");
+       doc.write_to_file_formatted (file.string(), "UTF-8");
 }
 
 optional<string>
index 73c58f3020c458f2d1ab1178108f2cef8f98af6b..98eb0df03945d5e143d17f8c7f2f9e28a1058040 100644 (file)
@@ -44,7 +44,7 @@ def build(bld):
     obj.source = 'dcpinfo.cc common.cc'
     obj.target = 'dcpinfo'
 
-    for f in ['dumpsub', 'decryptmxf', 'kdm', 'thumb', 'recover', 'verify']:
+    for f in ['dumpsub', 'decryptmxf', 'kdm', 'thumb', 'recover', 'verify', 'sign']:
         obj = bld(features='cxx cxxprogram')
         obj.use = ['libdcp%s' % bld.env.API_VERSION]
         obj.uselib = 'OPENJPEG CXML OPENMP ASDCPLIB_CTH BOOST_FILESYSTEM LIBXML++ XMLSEC1 OPENSSL'