More win32 build hacks.
[libdcp.git] / src / util.cc
index c66e63f5104bfb14d272dde96e74bf90af34bf0d..18fa1b173d556b2c856b528a4e8afd576353629a 100644 (file)
 #include <iostream>
 #include <iomanip>
 #include <boost/filesystem.hpp>
+#include <boost/lexical_cast.hpp>
 #include <openssl/sha.h>
+#include <libxml++/nodes/element.h>
+#include <libxml++/document.h>
+#include <xmlsec/xmldsig.h>
+#include <xmlsec/dl.h>
+#include <xmlsec/app.h>
 #include "KM_util.h"
 #include "KM_fileio.h"
 #include "AS_DCP.h"
 #include "exceptions.h"
 #include "types.h"
 #include "argb_frame.h"
+#include "certificates.h"
 #include "gamma_lut.h"
 
 using std::string;
+using std::cout;
 using std::stringstream;
 using std::min;
 using std::max;
+using std::list;
 using boost::shared_ptr;
+using boost::lexical_cast;
 using namespace libdcp;
 
 /** Create a UUID.
@@ -91,7 +101,6 @@ libdcp::make_digest (string filename)
        byte_t byte_buffer[20];
        SHA1_Final (byte_buffer, &sha);
 
-       stringstream s;
        char digest[64];
        return Kumu::base64encode (byte_buffer, 20, digest, 64);
 }
@@ -187,7 +196,7 @@ libdcp::decompress_j2k (uint8_t* data, int64_t size, int reduce)
        if (!image) {
                opj_destroy_decompress (decoder);
                opj_cio_close (cio);
-               boost::throw_exception (DCPReadError ("could not decode JPEG2000 codestream"));
+               boost::throw_exception (DCPReadError ("could not decode JPEG2000 codestream of " + lexical_cast<string> (size) + " bytes."));
        }
 
        opj_cio_close (cio);
@@ -292,6 +301,100 @@ libdcp::empty_or_white_space (string s)
        return true;
 }
 
+void
+libdcp::init ()
+{
+       if (xmlSecInit() < 0) {
+               throw MiscError ("could not initialise xmlsec");
+       }
+}
+
+void
+libdcp::add_signature_value (xmlpp::Element* parent, CertificateChain const & certificates, string const & signer_key, string const & ns)
+{
+       parent->add_child("SignatureValue", ns);
+       
+       xmlpp::Element* key_info = parent->add_child("KeyInfo", ns);
+       list<shared_ptr<Certificate> > c = certificates.leaf_to_root ();
+       for (list<shared_ptr<Certificate> >::iterator i = c.begin(); i != c.end(); ++i) {
+               xmlpp::Element* data = key_info->add_child("X509Data", ns);
+               
+               {
+                       xmlpp::Element* serial = data->add_child("X509IssuerSerial", ns);
+                       serial->add_child("X509IssuerName", ns)->add_child_text((*i)->issuer ());
+                       serial->add_child("X509SerialNumber", ns)->add_child_text((*i)->serial ());
+               }
+               
+               data->add_child("X509Certificate", ns)->add_child_text((*i)->certificate());
+       }
+
+       xmlSecKeysMngrPtr keys_manager = xmlSecKeysMngrCreate();
+       if (!keys_manager) {
+               throw MiscError ("could not create keys manager");
+       }
+       
+       xmlSecDSigCtx signature_context;
+       
+       if (xmlSecDSigCtxInitialize (&signature_context, keys_manager) < 0) {
+               throw MiscError ("could not initialise XMLSEC context");
+       }
+       
+       if (xmlSecDSigCtxSign (&signature_context, parent->cobj()) < 0) {
+               throw MiscError ("could not sign");
+       }
+       
+       xmlSecDSigCtxFinalize (&signature_context);
+       xmlSecKeysMngrDestroy (keys_manager);
+}
+
+
+void
+libdcp::add_signer (xmlpp::Element* parent, CertificateChain const & certificates, string const & ns)
+{
+       xmlpp::Element* signer = parent->add_child("Signer");
+
+       {
+               xmlpp::Element* data = signer->add_child("X509Data", ns);
+               
+               {
+                       xmlpp::Element* serial_element = data->add_child("X509IssuerSerial", ns);
+                       serial_element->add_child("X509IssuerName", ns)->add_child_text (certificates.leaf()->issuer());
+                       serial_element->add_child("X509SerialNumber", ns)->add_child_text (certificates.leaf()->serial());
+               }
+               
+               data->add_child("X509SubjectName", ns)->add_child_text (certificates.leaf()->subject());
+       }
+}
+
+void
+libdcp::sign (xmlpp::Element* parent, CertificateChain const & certificates, string const & signer_key)
+{
+       add_signer (parent, certificates, "dsig");
+
+       xmlpp::Element* signature = parent->add_child("Signature", "dsig");
+       
+       {
+               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");
+               signed_info->add_child("SignatureMethod", "dsig")->set_attribute("Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
+               {
+                       xmlpp::Element* reference = signed_info->add_child("Reference", "dsig");
+                       reference->set_attribute ("URI", "");
+                       {
+                               xmlpp::Element* transforms = reference->add_child("Transforms", "dsig");
+                               transforms->add_child("Transform", "dsig")->set_attribute (
+                                       "Algorithm", "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
+                                       );
+                       }
+                       reference->add_child("DigestMethod", "dsig")->set_attribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
+                       /* This will be filled in by the signing later */
+                       reference->add_child("DigestValue", "dsig");
+               }
+       }
+       
+       add_signature_value (signature, certificates, signer_key, "dsig");
+}
+
 bool libdcp::operator== (libdcp::Size const & a, libdcp::Size const & b)
 {
        return (a.width == b.width && a.height == b.height);
@@ -302,3 +405,30 @@ bool libdcp::operator!= (libdcp::Size const & a, libdcp::Size const & b)
        return !(a == b);
 }
 
+/** The base64 decode routine in KM_util.cpp gives different values to both
+ *  this and the command-line base64 for some inputs.  Not sure why.
+ */
+int
+libdcp::base64_decode (string const & in, unsigned char* out, int out_length)
+{
+       BIO* b64 = BIO_new (BIO_f_base64 ());
+
+       /* This means the input should have no newlines */
+       BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL);
+
+       /* Copy our input string, removing newlines */
+       char in_buffer[in.size() + 1];
+       char* p = in_buffer;
+       for (size_t i = 0; i < in.size(); ++i) {
+               if (in[i] != '\n' && in[i] != '\r') {
+                       *p++ = in[i];
+               }
+       }
+               
+       BIO* bmem = BIO_new_mem_buf (in_buffer, p - in_buffer);
+       bmem = BIO_push (b64, bmem);
+       int const N = BIO_read (bmem, out, out_length);
+       BIO_free_all (bmem);
+
+       return N;
+}