summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/asset.cc1
-rw-r--r--src/certificates.cc2
-rw-r--r--src/certificates.h4
-rw-r--r--src/dcp.cc238
-rw-r--r--src/dcp.h13
-rw-r--r--src/mxf_asset.cc9
-rw-r--r--src/mxf_asset.h3
-rw-r--r--src/picture_asset.cc6
-rw-r--r--src/picture_asset.h3
-rw-r--r--src/sound_asset.cc6
-rw-r--r--src/sound_asset.h2
-rw-r--r--src/util.cc110
-rw-r--r--src/util.h11
13 files changed, 311 insertions, 97 deletions
diff --git a/src/asset.cc b/src/asset.cc
index ac8f8b8e..62fdddf6 100644
--- a/src/asset.cc
+++ b/src/asset.cc
@@ -91,3 +91,4 @@ Asset::digest () const
return _digest;
}
+
diff --git a/src/certificates.cc b/src/certificates.cc
index ba0ebaae..ac7e20f7 100644
--- a/src/certificates.cc
+++ b/src/certificates.cc
@@ -4,6 +4,7 @@
#include <openssl/x509.h>
#include <openssl/ssl.h>
#include <openssl/asn1.h>
+#include <libxml++/nodes/element.h>
#include "certificates.h"
#include "exceptions.h"
@@ -104,7 +105,6 @@ Certificate::serial () const
return st;
}
-
/** @param filename Text file of PEM-format certificates,
* in the order:
diff --git a/src/certificates.h b/src/certificates.h
index a1a409ac..1c342acb 100644
--- a/src/certificates.h
+++ b/src/certificates.h
@@ -9,6 +9,10 @@
class certificates;
+namespace xmlpp {
+ class Element;
+}
+
namespace libdcp {
class Certificate : public boost::noncopyable
diff --git a/src/dcp.cc b/src/dcp.cc
index 2ece0eac..a282c33e 100644
--- a/src/dcp.cc
+++ b/src/dcp.cc
@@ -471,7 +471,7 @@ CPL::write_xml (bool encrypted, CertificateChain const & certificates, string co
}
if (encrypted) {
- DCP::sign (cpl, certificates, signer_key);
+ sign (cpl, certificates, signer_key);
}
doc.write_to_file_formatted (p.string(), "UTF-8");
@@ -481,98 +481,6 @@ CPL::write_xml (bool encrypted, CertificateChain const & certificates, string co
}
void
-DCP::sign (xmlpp::Element* parent, CertificateChain const & certificates, string const & signer_key)
-{
- xmlpp::Element* signer = parent->add_child("Signer");
-
- {
- xmlpp::Element* data = signer->add_child("X509Data", "dsig");
- {
- xmlpp::Element* serial = data->add_child("X509IssuerSerial", "dsig");
- serial->add_child("X509IssuerName", "dsig")->add_child_text (
- Certificate::name_for_xml (certificates.leaf()->issuer())
- );
- serial->add_child("X509SerialNumber", "dsig")->add_child_text (
- certificates.leaf()->serial()
- );
- }
- data->add_child("X509SubjectName", "dsig")->add_child_text (
- Certificate::name_for_xml (certificates.leaf()->subject())
- );
- }
-
- 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");
- }
- }
-
- signature->add_child("SignatureValue", "dsig");
-
- xmlpp::Element* key_info = signature->add_child("KeyInfo", "dsig");
- 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", "dsig");
-
- {
- xmlpp::Element* serial = data->add_child("X509IssuerSerial", "dsig");
- serial->add_child("X509IssuerName", "dsig")->add_child_text(
- Certificate::name_for_xml ((*i)->issuer())
- );
- serial->add_child("X509SerialNumber", "dsig")->add_child_text((*i)->serial());
- }
-
- data->add_child("X509Certificate", "dsig")->add_child_text((*i)->certificate());
- }
-
- xmlSecKeysMngrPtr keys_manager = xmlSecKeysMngrCreate();
- if (!keys_manager) {
- throw MiscError ("could not create keys manager");
- }
- if (xmlSecCryptoAppDefaultKeysMngrInit (keys_manager) < 0) {
- throw MiscError ("could not initialise keys manager");
- }
-
- xmlSecKeyPtr const key = xmlSecCryptoAppKeyLoad (signer_key.c_str(), xmlSecKeyDataFormatPem, 0, 0, 0);
- if (key == 0) {
- throw MiscError ("could not load signer key");
- }
-
- if (xmlSecCryptoAppDefaultKeysMngrAdoptKey (keys_manager, key) < 0) {
- xmlSecKeyDestroy (key);
- throw MiscError ("could not use signer key");
- }
-
- xmlSecDSigCtx signature_context;
-
- if (xmlSecDSigCtxInitialize (&signature_context, keys_manager) < 0) {
- throw MiscError ("could not initialise XMLSEC context");
- }
-
- if (xmlSecDSigCtxSign (&signature_context, signature->cobj()) < 0) {
- throw MiscError ("could not sign CPL");
- }
-
- xmlSecDSigCtxFinalize (&signature_context);
- xmlSecKeysMngrDestroy (keys_manager);
-}
-
-void
CPL::write_to_pkl (xmlpp::Element* p) const
{
xmlpp::Element* asset = p->add_child("Asset");
@@ -660,3 +568,147 @@ CPL::equals (CPL const & other, EqualityOptions opt, list<string>& notes) const
return true;
}
+
+shared_ptr<xmlpp::Document>
+CPL::make_kdm (CertificateChain const & certificates, string const & signer_key, shared_ptr<const Certificate> recipient_cert) const
+{
+ shared_ptr<xmlpp::Document> doc (new xmlpp::Document);
+ xmlpp::Element* root = doc->create_root_node ("DCinemaSecurityMessage");
+ root->set_namespace_declaration ("http://www.smpte-ra.org/schemas/430-3/2006/ETM", "");
+ root->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "ds");
+ root->set_namespace_declaration ("http://www.w3.org/2001/04/xmlenc#", "enc");
+
+ {
+ xmlpp::Element* authenticated_public = root->add_child("AuthenticatedPublic");
+ authenticated_public->set_attribute("Id", "ID_AuthenticatedPublic");
+ xmlAddID (0, doc->cobj(), (const xmlChar *) "ID_AuthenticatedPublic", authenticated_public->get_attribute("Id")->cobj());
+
+ authenticated_public->add_child("MessageId")->add_child_text("urn:uuid:" + make_uuid());
+ authenticated_public->add_child("MessageType")->add_child_text("http://www.smpte-ra.org/430-1/2006/KDM#kdm-key-type");
+ authenticated_public->add_child("AnnotationText")->add_child_text(Metadata::instance()->product_name);
+ authenticated_public->add_child("IssueDate")->add_child_text(Metadata::instance()->issue_date);
+
+ {
+ xmlpp::Element* signer = authenticated_public->add_child("Signer");
+ signer->add_child("X509IssuerName", "ds")->add_child_text (
+ Certificate::name_for_xml (recipient_cert->issuer())
+ );
+ signer->add_child("X509SerialNumber", "ds")->add_child_text (
+ recipient_cert->serial()
+ );
+ }
+
+ {
+ xmlpp::Element* required_extensions = authenticated_public->add_child("RequiredExtensions");
+
+ {
+ xmlpp::Element* kdm_required_extensions = required_extensions->add_child("KDMRequiredExtensions");
+ kdm_required_extensions->set_namespace_declaration ("http://www.smpte-ra.org/schemas/430-1/2006/KDM");
+ {
+ xmlpp::Element* recipient = kdm_required_extensions->add_child("Recipient");
+ {
+ xmlpp::Element* serial_element = recipient->add_child("X509IssuerSerial");
+ serial_element->add_child("X509IssuerName", "ds")->add_child_text (
+ Certificate::name_for_xml (recipient_cert->issuer())
+ );
+ serial_element->add_child("X509SerialNumber", "ds")->add_child_text (
+ recipient_cert->serial()
+ );
+ }
+
+ recipient->add_child("X509SubjectName")->add_child_text (Certificate::name_for_xml (recipient_cert->subject()));
+ }
+
+ kdm_required_extensions->add_child("CompositionPlaylistId")->add_child_text("XXX");
+ kdm_required_extensions->add_child("ContentTitleText")->add_child_text("XXX");
+ kdm_required_extensions->add_child("ContentAuthenticator")->add_child_text("XXX");
+ kdm_required_extensions->add_child("ContentKeysNotValidBefore")->add_child_text("XXX");
+ kdm_required_extensions->add_child("ContentKeysNotValidAfter")->add_child_text("XXX");
+
+ {
+ xmlpp::Element* authorized_device_info = kdm_required_extensions->add_child("AuthorizedDeviceInfo");
+ authorized_device_info->add_child("DeviceListIdentifier")->add_child_text("urn:uuid:" + make_uuid());
+ authorized_device_info->add_child("DeviceListDescription")->add_child_text(recipient_cert->subject());
+ {
+ xmlpp::Element* device_list = authorized_device_info->add_child("DeviceList");
+ device_list->add_child("CertificateThumbprint")->add_child_text("XXX");
+ }
+ }
+
+ {
+ xmlpp::Element* key_id_list = kdm_required_extensions->add_child("KeyIdList");
+ list<shared_ptr<const Asset> > a = assets();
+ for (list<shared_ptr<const Asset> >::iterator i = a.begin(); i != a.end(); ++i) {
+ /* XXX: non-MXF assets? */
+ shared_ptr<const MXFAsset> mxf = boost::dynamic_pointer_cast<const MXFAsset> (*i);
+ if (mxf) {
+ mxf->add_typed_key_id (key_id_list);
+ }
+ }
+ }
+
+ {
+ xmlpp::Element* forensic_mark_flag_list = kdm_required_extensions->add_child("ForensicMarkFlagList");
+ forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text (
+ "http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable"
+ );
+ forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text (
+ "http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable"
+ );
+ }
+ }
+ }
+
+ authenticated_public->add_child("NonCriticalExtensions");
+ }
+
+ {
+ xmlpp::Element* authenticated_private = root->add_child("AuthenticatedPrivate");
+ authenticated_private->set_attribute ("Id", "ID_AuthenticatedPrivate");
+ xmlAddID (0, doc->cobj(), (const xmlChar *) "ID_AuthenticatedPrivate", authenticated_private->get_attribute("Id")->cobj());
+ {
+ xmlpp::Element* encrypted_key = authenticated_private->add_child ("EncryptedKey", "enc");
+ {
+ xmlpp::Element* encryption_method = encrypted_key->add_child ("EncryptionMethod", "enc");
+ encryption_method->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p");
+ encryption_method->add_child("DigestMethod", "ds")->set_attribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
+ }
+
+ xmlpp::Element* cipher_data = authenticated_private->add_child ("CipherData", "enc");
+ cipher_data->add_child("CipherValue", "enc")->add_child_text("XXX");
+ }
+ }
+
+ /* XXX: x2 one for each mxf? */
+
+ {
+ xmlpp::Element* signature = root->add_child("Signature", "ds");
+
+ {
+ xmlpp::Element* signed_info = signature->add_child("SignedInfo", "ds");
+ signed_info->add_child("CanonicalizationMethod", "ds")->set_attribute(
+ "Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
+ );
+ signed_info->add_child("SignatureMethod", "ds")->set_attribute(
+ "Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
+ );
+ {
+ xmlpp::Element* reference = signed_info->add_child("Reference", "ds");
+ reference->set_attribute("URI", "#ID_AuthenticatedPublic");
+ reference->add_child("DigestMethod", "ds")->set_attribute("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256");
+ reference->add_child("DigestValue", "ds");
+ }
+
+ {
+ xmlpp::Element* reference = signed_info->add_child("Reference", "ds");
+ reference->set_attribute("URI", "#ID_AuthenticatedPrivate");
+ reference->add_child("DigestMethod", "ds")->set_attribute("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256");
+ reference->add_child("DigestValue", "ds");
+ }
+ }
+
+ add_signature_value (signature, certificates, signer_key, "ds");
+ }
+
+ return doc;
+}
diff --git a/src/dcp.h b/src/dcp.h
index 0ee4a8fc..96b28090 100644
--- a/src/dcp.h
+++ b/src/dcp.h
@@ -32,6 +32,7 @@
#include "certificates.h"
namespace xmlpp {
+ class Document;
class Element;
}
@@ -89,6 +90,8 @@ public:
void write_xml (bool, CertificateChain const &, std::string const &) const;
void write_to_assetmap (std::ostream& s) const;
void write_to_pkl (xmlpp::Element* p) const;
+
+ boost::shared_ptr<xmlpp::Document> make_kdm (CertificateChain const &, std::string const &, boost::shared_ptr<const Certificate>) const;
private:
std::string _directory;
@@ -159,17 +162,23 @@ public:
_certificates = c;
}
+ CertificateChain certificates () const {
+ return _certificates;
+ }
+
void set_signer_key (std::string const & s) {
_signer_key = s;
}
+ std::string signer_key () const {
+ return _signer_key;
+ }
+
/** Emitted with a parameter between 0 and 1 to indicate progress
* for long jobs.
*/
boost::signals2::signal<void (float)> Progress;
- static void sign (xmlpp::Element* parent, CertificateChain const & certificates, std::string const & signer_key);
-
private:
/** Write the PKL file.
diff --git a/src/mxf_asset.cc b/src/mxf_asset.cc
index d229331b..6ba42d75 100644
--- a/src/mxf_asset.cc
+++ b/src/mxf_asset.cc
@@ -24,6 +24,7 @@
#include <iostream>
#include <fstream>
#include <boost/filesystem.hpp>
+#include <libxml++/nodes/element.h>
#include "AS_DCP.h"
#include "KM_prng.h"
#include "KM_util.h"
@@ -128,3 +129,11 @@ MXFAsset::length () const
{
return _length;
}
+
+void
+MXFAsset::add_typed_key_id (xmlpp::Element* parent) const
+{
+ xmlpp::Element* typed_key_id = parent->add_child("TypedKeyId");
+ typed_key_id->add_child("KeyType")->add_child_text(key_type ());
+ typed_key_id->add_child("KeyId")->add_child_text("urn:uuid:" + _key_id);
+}
diff --git a/src/mxf_asset.h b/src/mxf_asset.h
index e45ab2d7..f68edc61 100644
--- a/src/mxf_asset.h
+++ b/src/mxf_asset.h
@@ -51,8 +51,11 @@ public:
virtual bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, std::list<std::string>& notes) const;
int length () const;
+ void add_typed_key_id (xmlpp::Element *) const;
protected:
+ virtual std::string key_type () const = 0;
+
/** Fill in a ADSCP::WriteInfo struct.
* @param w struct to fill in.
*/
diff --git a/src/picture_asset.cc b/src/picture_asset.cc
index 295afc15..f783bb39 100644
--- a/src/picture_asset.cc
+++ b/src/picture_asset.cc
@@ -391,3 +391,9 @@ StereoPictureAsset::get_frame (int n) const
{
return shared_ptr<const StereoPictureFrame> (new StereoPictureFrame (path().string(), n + _entry_point));
}
+
+string
+PictureAsset::key_type () const
+{
+ return "MDIK";
+}
diff --git a/src/picture_asset.h b/src/picture_asset.h
index 15764dab..96bf5659 100644
--- a/src/picture_asset.h
+++ b/src/picture_asset.h
@@ -64,6 +64,9 @@ protected:
int _width;
/** picture height in pixels */
int _height;
+
+private:
+ std::string key_type () const;
};
/** A 2D (monoscopic) picture asset */
diff --git a/src/sound_asset.cc b/src/sound_asset.cc
index b71d218f..77267cbd 100644
--- a/src/sound_asset.cc
+++ b/src/sound_asset.cc
@@ -285,3 +285,9 @@ SoundAsset::get_frame (int n) const
{
return shared_ptr<const SoundFrame> (new SoundFrame (path().string(), n + _entry_point));
}
+
+string
+SoundAsset::key_type () const
+{
+ return "MDAK";
+}
diff --git a/src/sound_asset.h b/src/sound_asset.h
index 0a6c8f4e..67115606 100644
--- a/src/sound_asset.h
+++ b/src/sound_asset.h
@@ -104,6 +104,8 @@ public:
}
private:
+ std::string key_type () const;
+
void construct (boost::function<std::string (Channel)> get_path);
std::string path_from_channel (Channel channel, std::vector<std::string> const & files);
diff --git a/src/util.cc b/src/util.cc
index 323fb1e4..ea6f6c0d 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -27,6 +27,8 @@
#include <iomanip>
#include <boost/filesystem.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>
@@ -38,12 +40,14 @@
#include "types.h"
#include "argb_frame.h"
#include "lut.h"
+#include "certificates.h"
using std::string;
using std::cout;
using std::stringstream;
using std::min;
using std::max;
+using std::list;
using boost::shared_ptr;
using namespace libdcp;
@@ -293,3 +297,109 @@ libdcp::init ()
throw MiscError ("could not initialise xmlsec-crypto");
}
}
+
+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(
+ Certificate::name_for_xml ((*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");
+ }
+ if (xmlSecCryptoAppDefaultKeysMngrInit (keys_manager) < 0) {
+ throw MiscError ("could not initialise keys manager");
+ }
+
+ xmlSecKeyPtr const key = xmlSecCryptoAppKeyLoad (signer_key.c_str(), xmlSecKeyDataFormatPem, 0, 0, 0);
+ if (key == 0) {
+ throw MiscError ("could not load signer key");
+ }
+
+ if (xmlSecCryptoAppDefaultKeysMngrAdoptKey (keys_manager, key) < 0) {
+ xmlSecKeyDestroy (key);
+ throw MiscError ("could not use signer key");
+ }
+
+ 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 (
+ Certificate::name_for_xml (certificates.leaf()->issuer())
+ );
+ serial_element->add_child("X509SerialNumber", ns)->add_child_text (
+ certificates.leaf()->serial()
+ );
+ }
+
+ data->add_child("X509SubjectName", ns)->add_child_text (Certificate::name_for_xml (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");
+}
+
diff --git a/src/util.h b/src/util.h
index 6426eb8d..3b60e0c7 100644
--- a/src/util.h
+++ b/src/util.h
@@ -27,9 +27,14 @@
#include <openjpeg.h>
#include "types.h"
+namespace xmlpp {
+ class Element;
+}
+
namespace libdcp {
-class ARGBFrame;
+class ARGBFrame;
+class CertificateChain;
/** Create a UUID.
* @return UUID.
@@ -55,4 +60,8 @@ extern boost::shared_ptr<ARGBFrame> xyz_to_rgb (opj_image_t* xyz_frame);
extern void init ();
+extern void sign (xmlpp::Element* parent, CertificateChain const & certificates, std::string const & signer_key);
+extern void add_signature_value (xmlpp::Element* parent, CertificateChain const & certificates, std::string const & signer_key, std::string const & ns);
+extern void add_signer (xmlpp::Element* parent, CertificateChain const & certificates, std::string const & ns);
+
}