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