Use a different ID for the XML inside a SMPTE subtitle MXF than the
[libdcp.git] / src / encrypted_kdm.cc
1 /*
2     Copyright (C) 2013-2017 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
6     libdcp is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     libdcp is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with libdcp.  If not, see <http://www.gnu.org/licenses/>.
18
19     In addition, as a special exception, the copyright holders give
20     permission to link the code of portions of this program with the
21     OpenSSL library under certain conditions as described in each
22     individual source file, and distribute linked combinations
23     including the two.
24
25     You must obey the GNU General Public License in all respects
26     for all of the code used other than OpenSSL.  If you modify
27     file(s) with this exception, you may extend this exception to your
28     version of the file(s), but you are not obligated to do so.  If you
29     do not wish to do so, delete this exception statement from your
30     version.  If you delete this exception statement from all source
31     files in the program, then also delete it here.
32 */
33
34 #include "encrypted_kdm.h"
35 #include "util.h"
36 #include "certificate_chain.h"
37 #include "exceptions.h"
38 #include <libcxml/cxml.h>
39 #include <libxml++/document.h>
40 #include <libxml++/nodes/element.h>
41 #include <libxml/parser.h>
42 #include <boost/date_time/posix_time/posix_time.hpp>
43 #include <boost/foreach.hpp>
44
45 using std::list;
46 using std::vector;
47 using std::string;
48 using std::map;
49 using std::pair;
50 using boost::shared_ptr;
51 using boost::optional;
52 using namespace dcp;
53
54 namespace dcp {
55
56 /** Namespace for classes used to hold our data; they are internal to this .cc file */
57 namespace data {
58
59 class Signer
60 {
61 public:
62         Signer () {}
63
64         explicit Signer (shared_ptr<const cxml::Node> node)
65                 : x509_issuer_name (node->string_child ("X509IssuerName"))
66                 , x509_serial_number (node->string_child ("X509SerialNumber"))
67         {
68
69         }
70
71         void as_xml (xmlpp::Element* node) const
72         {
73                 node->add_child("X509IssuerName", "ds")->add_child_text (x509_issuer_name);
74                 node->add_child("X509SerialNumber", "ds")->add_child_text (x509_serial_number);
75         }
76
77         string x509_issuer_name;
78         string x509_serial_number;
79 };
80
81 class X509Data
82 {
83 public:
84         X509Data () {}
85
86         explicit X509Data (boost::shared_ptr<const cxml::Node> node)
87                 : x509_issuer_serial (Signer (node->node_child ("X509IssuerSerial")))
88                 , x509_certificate (node->string_child ("X509Certificate"))
89         {
90                 node->done ();
91         }
92
93         void as_xml (xmlpp::Element* node) const
94         {
95                 x509_issuer_serial.as_xml (node->add_child ("X509IssuerSerial", "ds"));
96                 node->add_child("X509Certificate", "ds")->add_child_text (x509_certificate);
97         }
98
99         Signer x509_issuer_serial;
100         std::string x509_certificate;
101 };
102
103 class Reference
104 {
105 public:
106         Reference () {}
107
108         explicit Reference (string u)
109                 : uri (u)
110         {}
111
112         explicit Reference (shared_ptr<const cxml::Node> node)
113                 : uri (node->string_attribute ("URI"))
114                 , digest_value (node->string_child ("DigestValue"))
115         {
116
117         }
118
119         void as_xml (xmlpp::Element* node) const
120         {
121                 node->set_attribute ("URI", uri);
122                 node->add_child("DigestMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256");
123                 node->add_child("DigestValue", "ds")->add_child_text (digest_value);
124         }
125
126         string uri;
127         string digest_value;
128 };
129
130 class SignedInfo
131 {
132 public:
133         SignedInfo ()
134                 : authenticated_public ("#ID_AuthenticatedPublic")
135                 , authenticated_private ("#ID_AuthenticatedPrivate")
136         {}
137
138         explicit SignedInfo (shared_ptr<const cxml::Node> node)
139         {
140                 list<shared_ptr<cxml::Node> > references = node->node_children ("Reference");
141                 for (list<shared_ptr<cxml::Node> >::const_iterator i = references.begin(); i != references.end(); ++i) {
142                         if ((*i)->string_attribute ("URI") == "#ID_AuthenticatedPublic") {
143                                 authenticated_public = Reference (*i);
144                         } else if ((*i)->string_attribute ("URI") == "#ID_AuthenticatedPrivate") {
145                                 authenticated_private = Reference (*i);
146                         }
147
148                         /* XXX: do something if we don't recognise the node */
149                 }
150         }
151
152         void as_xml (xmlpp::Element* node) const
153         {
154                 node->add_child ("CanonicalizationMethod", "ds")->set_attribute (
155                         "Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
156                         );
157
158                 node->add_child ("SignatureMethod", "ds")->set_attribute (
159                         "Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
160                         );
161
162                 authenticated_public.as_xml (node->add_child ("Reference", "ds"));
163                 authenticated_private.as_xml (node->add_child ("Reference", "ds"));
164         }
165
166 private:
167         Reference authenticated_public;
168         Reference authenticated_private;
169 };
170
171 class Signature
172 {
173 public:
174         Signature () {}
175
176         explicit Signature (shared_ptr<const cxml::Node> node)
177                 : signed_info (node->node_child ("SignedInfo"))
178                 , signature_value (node->string_child ("SignatureValue"))
179         {
180                 list<shared_ptr<cxml::Node> > x509_data_nodes = node->node_child("KeyInfo")->node_children ("X509Data");
181                 for (list<shared_ptr<cxml::Node> >::const_iterator i = x509_data_nodes.begin(); i != x509_data_nodes.end(); ++i) {
182                         x509_data.push_back (X509Data (*i));
183                 }
184         }
185
186         void as_xml (xmlpp::Node* node) const
187         {
188                 signed_info.as_xml (node->add_child ("SignedInfo", "ds"));
189                 node->add_child("SignatureValue", "ds")->add_child_text (signature_value);
190
191                 xmlpp::Element* key_info_node = node->add_child ("KeyInfo", "ds");
192                 for (std::list<X509Data>::const_iterator i = x509_data.begin(); i != x509_data.end(); ++i) {
193                         i->as_xml (key_info_node->add_child ("X509Data", "ds"));
194                 }
195         }
196
197         SignedInfo signed_info;
198         string signature_value;
199         list<X509Data> x509_data;
200 };
201
202 class AuthenticatedPrivate
203 {
204 public:
205         AuthenticatedPrivate () {}
206
207         explicit AuthenticatedPrivate (shared_ptr<const cxml::Node> node)
208         {
209                 list<shared_ptr<cxml::Node> > encrypted_key_nodes = node->node_children ("EncryptedKey");
210                 for (list<shared_ptr<cxml::Node> >::const_iterator i = encrypted_key_nodes.begin(); i != encrypted_key_nodes.end(); ++i) {
211                         encrypted_key.push_back ((*i)->node_child("CipherData")->string_child ("CipherValue"));
212                 }
213         }
214
215         void as_xml (xmlpp::Element* node, map<string, xmlpp::Attribute *>& references) const
216         {
217                 references["ID_AuthenticatedPrivate"] = node->set_attribute ("Id", "ID_AuthenticatedPrivate");
218
219                 for (list<string>::const_iterator i = encrypted_key.begin(); i != encrypted_key.end(); ++i) {
220                         xmlpp::Element* encrypted_key = node->add_child ("EncryptedKey", "enc");
221                         /* XXX: hack for testing with Dolby */
222                         encrypted_key->set_namespace_declaration ("http://www.w3.org/2001/04/xmlenc#", "enc");
223                         xmlpp::Element* encryption_method = encrypted_key->add_child ("EncryptionMethod", "enc");
224                         encryption_method->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p");
225                         xmlpp::Element* digest_method = encryption_method->add_child ("DigestMethod", "ds");
226                         /* XXX: hack for testing with Dolby */
227                         digest_method->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "ds");
228                         digest_method->set_attribute ("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
229                         xmlpp::Element* cipher_data = encrypted_key->add_child ("CipherData", "enc");
230                         cipher_data->add_child("CipherValue", "enc")->add_child_text (*i);
231                 }
232         }
233
234         list<string> encrypted_key;
235 };
236
237 class TypedKeyId
238 {
239 public:
240         TypedKeyId () {}
241
242         explicit TypedKeyId (shared_ptr<const cxml::Node> node)
243                 : key_type (node->string_child ("KeyType"))
244                 , key_id (remove_urn_uuid (node->string_child ("KeyId")))
245         {
246
247         }
248
249         TypedKeyId (string type, string id)
250                 : key_type (type)
251                 , key_id (id)
252         {}
253
254         void as_xml (xmlpp::Element* node) const
255         {
256                 xmlpp::Element* type = node->add_child("KeyType");
257                 type->add_child_text (key_type);
258                 node->add_child("KeyId")->add_child_text ("urn:uuid:" + key_id);
259                 /* XXX: this feels like a bit of a hack */
260                 if (key_type == "MDEK") {
261                         type->set_attribute ("scope", "http://www.dolby.com/cp850/2012/KDM#kdm-key-type");
262                 } else {
263                         type->set_attribute ("scope", "http://www.smpte-ra.org/430-1/2006/KDM#kdm-key-type");
264                 }
265         }
266
267         string key_type;
268         string key_id;
269 };
270
271 class KeyIdList
272 {
273 public:
274         KeyIdList () {}
275
276         explicit KeyIdList (shared_ptr<const cxml::Node> node)
277         {
278                 list<shared_ptr<cxml::Node> > typed_key_id_nodes = node->node_children ("TypedKeyId");
279                 for (list<shared_ptr<cxml::Node> >::const_iterator i = typed_key_id_nodes.begin(); i != typed_key_id_nodes.end(); ++i) {
280                         typed_key_id.push_back (TypedKeyId (*i));
281                 }
282         }
283
284         void as_xml (xmlpp::Element* node) const
285         {
286                 for (list<TypedKeyId>::const_iterator i = typed_key_id.begin(); i != typed_key_id.end(); ++i) {
287                         i->as_xml (node->add_child("TypedKeyId"));
288                 }
289         }
290
291         list<TypedKeyId> typed_key_id;
292 };
293
294 class AuthorizedDeviceInfo
295 {
296 public:
297         AuthorizedDeviceInfo () {}
298
299         explicit AuthorizedDeviceInfo (shared_ptr<const cxml::Node> node)
300                 : device_list_identifier (remove_urn_uuid (node->string_child ("DeviceListIdentifier")))
301                 , device_list_description (node->optional_string_child ("DeviceListDescription"))
302         {
303                 BOOST_FOREACH (cxml::ConstNodePtr i, node->node_child("DeviceList")->node_children("CertificateThumbprint")) {
304                         certificate_thumbprints.push_back (i->content ());
305                 }
306         }
307
308         void as_xml (xmlpp::Element* node) const
309         {
310                 node->add_child ("DeviceListIdentifier")->add_child_text ("urn:uuid:" + device_list_identifier);
311                 if (device_list_description) {
312                         node->add_child ("DeviceListDescription")->add_child_text (device_list_description.get());
313                 }
314                 xmlpp::Element* device_list = node->add_child ("DeviceList");
315                 BOOST_FOREACH (string i, certificate_thumbprints) {
316                         device_list->add_child("CertificateThumbprint")->add_child_text (i);
317                 }
318         }
319
320         /** DeviceListIdentifier without the urn:uuid: prefix */
321         string device_list_identifier;
322         boost::optional<string> device_list_description;
323         std::list<string> certificate_thumbprints;
324 };
325
326 class X509IssuerSerial
327 {
328 public:
329         X509IssuerSerial () {}
330
331         explicit X509IssuerSerial (shared_ptr<const cxml::Node> node)
332                 : x509_issuer_name (node->string_child ("X509IssuerName"))
333                 , x509_serial_number (node->string_child ("X509SerialNumber"))
334         {
335
336         }
337
338         void as_xml (xmlpp::Element* node) const
339         {
340                 node->add_child("X509IssuerName", "ds")->add_child_text (x509_issuer_name);
341                 node->add_child("X509SerialNumber", "ds")->add_child_text (x509_serial_number);
342         }
343
344         string x509_issuer_name;
345         string x509_serial_number;
346 };
347
348 class Recipient
349 {
350 public:
351         Recipient () {}
352
353         explicit Recipient (shared_ptr<const cxml::Node> node)
354                 : x509_issuer_serial (node->node_child ("X509IssuerSerial"))
355                 , x509_subject_name (node->string_child ("X509SubjectName"))
356         {
357
358         }
359
360         void as_xml (xmlpp::Element* node) const
361         {
362                 x509_issuer_serial.as_xml (node->add_child ("X509IssuerSerial"));
363                 node->add_child("X509SubjectName")->add_child_text (x509_subject_name);
364         }
365
366         X509IssuerSerial x509_issuer_serial;
367         string x509_subject_name;
368 };
369
370 class KDMRequiredExtensions
371 {
372 public:
373         KDMRequiredExtensions () {}
374
375         explicit KDMRequiredExtensions (shared_ptr<const cxml::Node> node)
376                 : recipient (node->node_child ("Recipient"))
377                 , composition_playlist_id (remove_urn_uuid (node->string_child ("CompositionPlaylistId")))
378                 , content_title_text (node->string_child ("ContentTitleText"))
379                 , not_valid_before (node->string_child ("ContentKeysNotValidBefore"))
380                 , not_valid_after (node->string_child ("ContentKeysNotValidAfter"))
381                 , authorized_device_info (node->node_child ("AuthorizedDeviceInfo"))
382                 , key_id_list (node->node_child ("KeyIdList"))
383         {
384
385         }
386
387         void as_xml (xmlpp::Element* node) const
388         {
389                 node->set_attribute ("xmlns", "http://www.smpte-ra.org/schemas/430-1/2006/KDM");
390
391                 recipient.as_xml (node->add_child ("Recipient"));
392                 node->add_child("CompositionPlaylistId")->add_child_text ("urn:uuid:" + composition_playlist_id);
393                 node->add_child("ContentTitleText")->add_child_text (content_title_text);
394                 if (content_authenticator) {
395                         node->add_child("ContentAuthenticator")->add_child_text (content_authenticator.get ());
396                 }
397                 node->add_child("ContentKeysNotValidBefore")->add_child_text (not_valid_before.as_string ());
398                 node->add_child("ContentKeysNotValidAfter")->add_child_text (not_valid_after.as_string ());
399                 if (authorized_device_info) {
400                         authorized_device_info->as_xml (node->add_child ("AuthorizedDeviceInfo"));
401                 }
402                 key_id_list.as_xml (node->add_child ("KeyIdList"));
403
404                 xmlpp::Element* forensic_mark_flag_list = node->add_child ("ForensicMarkFlagList");
405                 forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable");
406                 forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable");
407         }
408
409         Recipient recipient;
410         string composition_playlist_id;
411         boost::optional<string> content_authenticator;
412         string content_title_text;
413         LocalTime not_valid_before;
414         LocalTime not_valid_after;
415         boost::optional<AuthorizedDeviceInfo> authorized_device_info;
416         KeyIdList key_id_list;
417 };
418
419 class RequiredExtensions
420 {
421 public:
422         RequiredExtensions () {}
423
424         explicit RequiredExtensions (shared_ptr<const cxml::Node> node)
425                 : kdm_required_extensions (node->node_child ("KDMRequiredExtensions"))
426         {
427
428         }
429
430         void as_xml (xmlpp::Element* node) const
431         {
432                 kdm_required_extensions.as_xml (node->add_child ("KDMRequiredExtensions"));
433         }
434
435         KDMRequiredExtensions kdm_required_extensions;
436 };
437
438 class AuthenticatedPublic
439 {
440 public:
441         AuthenticatedPublic ()
442                 : message_id (make_uuid ())
443                   /* XXX: hack for Dolby to see if there must be a not-empty annotation text */
444                 , annotation_text ("none")
445                 , issue_date (LocalTime().as_string ())
446         {}
447
448         explicit AuthenticatedPublic (shared_ptr<const cxml::Node> node)
449                 : message_id (remove_urn_uuid (node->string_child ("MessageId")))
450                 , annotation_text (node->optional_string_child ("AnnotationText"))
451                 , issue_date (node->string_child ("IssueDate"))
452                 , signer (node->node_child ("Signer"))
453                 , required_extensions (node->node_child ("RequiredExtensions"))
454         {
455
456         }
457
458         void as_xml (xmlpp::Element* node, map<string, xmlpp::Attribute *>& references) const
459         {
460                 references["ID_AuthenticatedPublic"] = node->set_attribute ("Id", "ID_AuthenticatedPublic");
461
462                 node->add_child("MessageId")->add_child_text ("urn:uuid:" + message_id);
463                 node->add_child("MessageType")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#kdm-key-type");
464                 if (annotation_text) {
465                         node->add_child("AnnotationText")->add_child_text (annotation_text.get ());
466                 }
467                 node->add_child("IssueDate")->add_child_text (issue_date);
468
469                 signer.as_xml (node->add_child ("Signer"));
470                 required_extensions.as_xml (node->add_child ("RequiredExtensions"));
471
472                 node->add_child ("NonCriticalExtensions");
473         }
474
475         string message_id;
476         optional<string> annotation_text;
477         string issue_date;
478         Signer signer;
479         RequiredExtensions required_extensions;
480 };
481
482 /** Class to describe our data.  We use a class hierarchy as it's a bit nicer
483  *  for XML data than a flat description.
484  */
485 class EncryptedKDMData
486 {
487 public:
488         EncryptedKDMData ()
489         {
490
491         }
492
493         explicit EncryptedKDMData (shared_ptr<const cxml::Node> node)
494                 : authenticated_public (node->node_child ("AuthenticatedPublic"))
495                 , authenticated_private (node->node_child ("AuthenticatedPrivate"))
496                 , signature (node->node_child ("Signature"))
497         {
498
499         }
500
501         shared_ptr<xmlpp::Document> as_xml () const
502         {
503                 shared_ptr<xmlpp::Document> document (new xmlpp::Document ());
504                 xmlpp::Element* root = document->create_root_node ("DCinemaSecurityMessage", "http://www.smpte-ra.org/schemas/430-3/2006/ETM");
505                 root->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "ds");
506                 root->set_namespace_declaration ("http://www.w3.org/2001/04/xmlenc#", "enc");
507                 map<string, xmlpp::Attribute *> references;
508                 authenticated_public.as_xml (root->add_child ("AuthenticatedPublic"), references);
509                 authenticated_private.as_xml (root->add_child ("AuthenticatedPrivate"), references);
510                 signature.as_xml (root->add_child ("Signature", "ds"));
511
512                 for (map<string, xmlpp::Attribute*>::const_iterator i = references.begin(); i != references.end(); ++i) {
513                         xmlAddID (0, document->cobj(), (const xmlChar *) i->first.c_str(), i->second->cobj ());
514                 }
515
516                 return document;
517         }
518
519         AuthenticatedPublic authenticated_public;
520         AuthenticatedPrivate authenticated_private;
521         Signature signature;
522 };
523
524 }
525 }
526
527 EncryptedKDM::EncryptedKDM (string s)
528 {
529         try {
530                 shared_ptr<cxml::Document> doc (new cxml::Document ("DCinemaSecurityMessage"));
531                 doc->read_string (s);
532                 _data = new data::EncryptedKDMData (doc);
533         } catch (xmlpp::parse_error& e) {
534                 throw KDMFormatError (e.what ());
535         }
536 }
537
538 EncryptedKDM::EncryptedKDM (
539         shared_ptr<const CertificateChain> signer,
540         Certificate recipient,
541         vector<Certificate> trusted_devices,
542         string cpl_id,
543         string content_title_text,
544         optional<string> annotation_text,
545         LocalTime not_valid_before,
546         LocalTime not_valid_after,
547         Formulation formulation,
548         list<pair<string, string> > key_ids,
549         list<string> keys
550         )
551         : _data (new data::EncryptedKDMData)
552 {
553         /* Fill our XML-ish description in with the juicy bits that the caller has given */
554
555         /* Our ideas about the KDM types are:
556          *
557          * Type                      Trusted-device thumb  ContentAuthenticator
558          * MODIFIED_TRANSITIONAL_1   assume-trust          No
559          * DCI_ANY                   assume-trust          Yes
560          * DCI_SPECIFIC              as specified          Yes
561          */
562
563         data::AuthenticatedPublic& aup = _data->authenticated_public;
564         aup.signer.x509_issuer_name = signer->leaf().issuer ();
565         aup.signer.x509_serial_number = signer->leaf().serial ();
566         aup.annotation_text = annotation_text;
567
568         data::KDMRequiredExtensions& kre = _data->authenticated_public.required_extensions.kdm_required_extensions;
569         kre.recipient.x509_issuer_serial.x509_issuer_name = recipient.issuer ();
570         kre.recipient.x509_issuer_serial.x509_serial_number = recipient.serial ();
571         kre.recipient.x509_subject_name = recipient.subject ();
572         kre.composition_playlist_id = cpl_id;
573         if (formulation == DCI_ANY || formulation == DCI_SPECIFIC) {
574                 kre.content_authenticator = signer->leaf().thumbprint ();
575         }
576         kre.content_title_text = content_title_text;
577         kre.not_valid_before = not_valid_before;
578         kre.not_valid_after = not_valid_after;
579
580         if (formulation != MODIFIED_TRANSITIONAL_TEST) {
581                 kre.authorized_device_info = data::AuthorizedDeviceInfo ();
582                 kre.authorized_device_info->device_list_identifier = make_uuid ();
583                 string n = recipient.subject_common_name ();
584                 if (n.find (".") != string::npos) {
585                         n = n.substr (n.find (".") + 1);
586                 }
587                 kre.authorized_device_info->device_list_description = n;
588
589                 if (formulation == MODIFIED_TRANSITIONAL_1 || formulation == DCI_ANY) {
590                         /* Use the "assume trust" thumbprint */
591                         kre.authorized_device_info->certificate_thumbprints.push_back ("2jmj7l5rSw0yVb/vlWAYkK/YBwk=");
592                 } else if (formulation == DCI_SPECIFIC) {
593                         /* As I read the standard we should use the recipient
594                            /and/ other trusted device thumbprints here.  MJD
595                            reports that this doesn't work with his setup;
596                            a working KDM does not include the recipient's
597                            thumbprint (recipient.thumbprint()).
598                            Waimea uses only the trusted devices here, too.
599                         */
600                         BOOST_FOREACH (Certificate const & i, trusted_devices) {
601                                 kre.authorized_device_info->certificate_thumbprints.push_back (i.thumbprint ());
602                         }
603                 }
604         }
605
606         for (list<pair<string, string> >::const_iterator i = key_ids.begin(); i != key_ids.end(); ++i) {
607                 kre.key_id_list.typed_key_id.push_back (data::TypedKeyId (i->first, i->second));
608         }
609
610         _data->authenticated_private.encrypted_key = keys;
611
612         /* Read the XML so far and sign it */
613         shared_ptr<xmlpp::Document> doc = _data->as_xml ();
614         xmlpp::Node::NodeList children = doc->get_root_node()->get_children ();
615         for (xmlpp::Node::NodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
616                 if ((*i)->get_name() == "Signature") {
617                         signer->add_signature_value (*i, "ds");
618                 }
619         }
620
621         /* Read the bits that add_signature_value did back into our variables */
622         shared_ptr<cxml::Node> signed_doc (new cxml::Node (doc->get_root_node ()));
623         _data->signature = data::Signature (signed_doc->node_child ("Signature"));
624 }
625
626 EncryptedKDM::EncryptedKDM (EncryptedKDM const & other)
627         : _data (new data::EncryptedKDMData (*other._data))
628 {
629
630 }
631
632 EncryptedKDM &
633 EncryptedKDM::operator= (EncryptedKDM const & other)
634 {
635         if (this == &other) {
636                 return *this;
637         }
638
639         delete _data;
640         _data = new data::EncryptedKDMData (*other._data);
641         return *this;
642 }
643
644 EncryptedKDM::~EncryptedKDM ()
645 {
646         delete _data;
647 }
648
649 void
650 EncryptedKDM::as_xml (boost::filesystem::path path) const
651 {
652         FILE* f = fopen_boost (path, "w");
653         string const x = as_xml ();
654         fwrite (x.c_str(), 1, x.length(), f);
655         fclose (f);
656 }
657
658 string
659 EncryptedKDM::as_xml () const
660 {
661         return _data->as_xml()->write_to_string ("UTF-8");
662 }
663
664 list<string>
665 EncryptedKDM::keys () const
666 {
667         return _data->authenticated_private.encrypted_key;
668 }
669
670 optional<string>
671 EncryptedKDM::annotation_text () const
672 {
673         return _data->authenticated_public.annotation_text;
674 }
675
676 string
677 EncryptedKDM::content_title_text () const
678 {
679         return _data->authenticated_public.required_extensions.kdm_required_extensions.content_title_text;
680 }
681
682 string
683 EncryptedKDM::cpl_id () const
684 {
685         return _data->authenticated_public.required_extensions.kdm_required_extensions.composition_playlist_id;
686 }
687
688 string
689 EncryptedKDM::issue_date () const
690 {
691         return _data->authenticated_public.issue_date;
692 }
693
694 LocalTime
695 EncryptedKDM::not_valid_before () const
696 {
697         return _data->authenticated_public.required_extensions.kdm_required_extensions.not_valid_before;
698 }
699
700 LocalTime
701 EncryptedKDM::not_valid_after () const
702 {
703         return _data->authenticated_public.required_extensions.kdm_required_extensions.not_valid_after;
704 }
705
706 string
707 EncryptedKDM::recipient_x509_subject_name () const
708 {
709         return _data->authenticated_public.required_extensions.kdm_required_extensions.recipient.x509_subject_name;
710 }
711
712 bool
713 dcp::operator== (EncryptedKDM const & a, EncryptedKDM const & b)
714 {
715         /* Not exactly efficient... */
716         return a.as_xml() == b.as_xml();
717 }