Add comment.
[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                 }
262         }
263
264         string key_type;
265         string key_id;
266 };
267
268 class KeyIdList
269 {
270 public:
271         KeyIdList () {}
272
273         explicit KeyIdList (shared_ptr<const cxml::Node> node)
274         {
275                 list<shared_ptr<cxml::Node> > typed_key_id_nodes = node->node_children ("TypedKeyId");
276                 for (list<shared_ptr<cxml::Node> >::const_iterator i = typed_key_id_nodes.begin(); i != typed_key_id_nodes.end(); ++i) {
277                         typed_key_id.push_back (TypedKeyId (*i));
278                 }
279         }
280
281         void as_xml (xmlpp::Element* node) const
282         {
283                 for (list<TypedKeyId>::const_iterator i = typed_key_id.begin(); i != typed_key_id.end(); ++i) {
284                         i->as_xml (node->add_child("TypedKeyId"));
285                 }
286         }
287
288         list<TypedKeyId> typed_key_id;
289 };
290
291 class AuthorizedDeviceInfo
292 {
293 public:
294         AuthorizedDeviceInfo () {}
295
296         explicit AuthorizedDeviceInfo (shared_ptr<const cxml::Node> node)
297                 : device_list_identifier (remove_urn_uuid (node->string_child ("DeviceListIdentifier")))
298                 , device_list_description (node->optional_string_child ("DeviceListDescription"))
299         {
300                 BOOST_FOREACH (cxml::ConstNodePtr i, node->node_child("DeviceList")->node_children("CertificateThumbprint")) {
301                         certificate_thumbprints.push_back (i->content ());
302                 }
303         }
304
305         void as_xml (xmlpp::Element* node) const
306         {
307                 node->add_child ("DeviceListIdentifier")->add_child_text ("urn:uuid:" + device_list_identifier);
308                 if (device_list_description) {
309                         node->add_child ("DeviceListDescription")->add_child_text (device_list_description.get());
310                 }
311                 xmlpp::Element* device_list = node->add_child ("DeviceList");
312                 BOOST_FOREACH (string i, certificate_thumbprints) {
313                         device_list->add_child("CertificateThumbprint")->add_child_text (i);
314                 }
315         }
316
317         /** DeviceListIdentifier without the urn:uuid: prefix */
318         string device_list_identifier;
319         boost::optional<string> device_list_description;
320         std::list<string> certificate_thumbprints;
321 };
322
323 class X509IssuerSerial
324 {
325 public:
326         X509IssuerSerial () {}
327
328         explicit X509IssuerSerial (shared_ptr<const cxml::Node> node)
329                 : x509_issuer_name (node->string_child ("X509IssuerName"))
330                 , x509_serial_number (node->string_child ("X509SerialNumber"))
331         {
332
333         }
334
335         void as_xml (xmlpp::Element* node) const
336         {
337                 node->add_child("X509IssuerName", "ds")->add_child_text (x509_issuer_name);
338                 node->add_child("X509SerialNumber", "ds")->add_child_text (x509_serial_number);
339         }
340
341         string x509_issuer_name;
342         string x509_serial_number;
343 };
344
345 class Recipient
346 {
347 public:
348         Recipient () {}
349
350         explicit Recipient (shared_ptr<const cxml::Node> node)
351                 : x509_issuer_serial (node->node_child ("X509IssuerSerial"))
352                 , x509_subject_name (node->string_child ("X509SubjectName"))
353         {
354
355         }
356
357         void as_xml (xmlpp::Element* node) const
358         {
359                 x509_issuer_serial.as_xml (node->add_child ("X509IssuerSerial"));
360                 node->add_child("X509SubjectName")->add_child_text (x509_subject_name);
361         }
362
363         X509IssuerSerial x509_issuer_serial;
364         string x509_subject_name;
365 };
366
367 class KDMRequiredExtensions
368 {
369 public:
370         KDMRequiredExtensions () {}
371
372         explicit KDMRequiredExtensions (shared_ptr<const cxml::Node> node)
373                 : recipient (node->node_child ("Recipient"))
374                 , composition_playlist_id (remove_urn_uuid (node->string_child ("CompositionPlaylistId")))
375                 , content_title_text (node->string_child ("ContentTitleText"))
376                 , not_valid_before (node->string_child ("ContentKeysNotValidBefore"))
377                 , not_valid_after (node->string_child ("ContentKeysNotValidAfter"))
378                 , authorized_device_info (node->node_child ("AuthorizedDeviceInfo"))
379                 , key_id_list (node->node_child ("KeyIdList"))
380         {
381
382         }
383
384         void as_xml (xmlpp::Element* node) const
385         {
386                 node->set_attribute ("xmlns", "http://www.smpte-ra.org/schemas/430-1/2006/KDM");
387
388                 recipient.as_xml (node->add_child ("Recipient"));
389                 node->add_child("CompositionPlaylistId")->add_child_text ("urn:uuid:" + composition_playlist_id);
390                 node->add_child("ContentTitleText")->add_child_text (content_title_text);
391                 if (content_authenticator) {
392                         node->add_child("ContentAuthenticator")->add_child_text (content_authenticator.get ());
393                 }
394                 node->add_child("ContentKeysNotValidBefore")->add_child_text (not_valid_before.as_string ());
395                 node->add_child("ContentKeysNotValidAfter")->add_child_text (not_valid_after.as_string ());
396                 if (authorized_device_info) {
397                         authorized_device_info->as_xml (node->add_child ("AuthorizedDeviceInfo"));
398                 }
399                 key_id_list.as_xml (node->add_child ("KeyIdList"));
400
401                 xmlpp::Element* forensic_mark_flag_list = node->add_child ("ForensicMarkFlagList");
402                 forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable");
403                 forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable");
404         }
405
406         Recipient recipient;
407         string composition_playlist_id;
408         boost::optional<string> content_authenticator;
409         string content_title_text;
410         LocalTime not_valid_before;
411         LocalTime not_valid_after;
412         boost::optional<AuthorizedDeviceInfo> authorized_device_info;
413         KeyIdList key_id_list;
414 };
415
416 class RequiredExtensions
417 {
418 public:
419         RequiredExtensions () {}
420
421         explicit RequiredExtensions (shared_ptr<const cxml::Node> node)
422                 : kdm_required_extensions (node->node_child ("KDMRequiredExtensions"))
423         {
424
425         }
426
427         void as_xml (xmlpp::Element* node) const
428         {
429                 kdm_required_extensions.as_xml (node->add_child ("KDMRequiredExtensions"));
430         }
431
432         KDMRequiredExtensions kdm_required_extensions;
433 };
434
435 class AuthenticatedPublic
436 {
437 public:
438         AuthenticatedPublic ()
439                 : message_id (make_uuid ())
440                   /* XXX: hack for Dolby to see if there must be a not-empty annotation text */
441                 , annotation_text ("none")
442                 , issue_date (LocalTime().as_string ())
443         {}
444
445         explicit AuthenticatedPublic (shared_ptr<const cxml::Node> node)
446                 : message_id (remove_urn_uuid (node->string_child ("MessageId")))
447                 , annotation_text (node->optional_string_child ("AnnotationText"))
448                 , issue_date (node->string_child ("IssueDate"))
449                 , signer (node->node_child ("Signer"))
450                 , required_extensions (node->node_child ("RequiredExtensions"))
451         {
452
453         }
454
455         void as_xml (xmlpp::Element* node, map<string, xmlpp::Attribute *>& references) const
456         {
457                 references["ID_AuthenticatedPublic"] = node->set_attribute ("Id", "ID_AuthenticatedPublic");
458
459                 node->add_child("MessageId")->add_child_text ("urn:uuid:" + message_id);
460                 node->add_child("MessageType")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#kdm-key-type");
461                 if (annotation_text) {
462                         node->add_child("AnnotationText")->add_child_text (annotation_text.get ());
463                 }
464                 node->add_child("IssueDate")->add_child_text (issue_date);
465
466                 signer.as_xml (node->add_child ("Signer"));
467                 required_extensions.as_xml (node->add_child ("RequiredExtensions"));
468
469                 node->add_child ("NonCriticalExtensions");
470         }
471
472         string message_id;
473         optional<string> annotation_text;
474         string issue_date;
475         Signer signer;
476         RequiredExtensions required_extensions;
477 };
478
479 /** Class to describe our data.  We use a class hierarchy as it's a bit nicer
480  *  for XML data than a flat description.
481  */
482 class EncryptedKDMData
483 {
484 public:
485         EncryptedKDMData ()
486         {
487
488         }
489
490         explicit EncryptedKDMData (shared_ptr<const cxml::Node> node)
491                 : authenticated_public (node->node_child ("AuthenticatedPublic"))
492                 , authenticated_private (node->node_child ("AuthenticatedPrivate"))
493                 , signature (node->node_child ("Signature"))
494         {
495
496         }
497
498         shared_ptr<xmlpp::Document> as_xml () const
499         {
500                 shared_ptr<xmlpp::Document> document (new xmlpp::Document ());
501                 xmlpp::Element* root = document->create_root_node ("DCinemaSecurityMessage", "http://www.smpte-ra.org/schemas/430-3/2006/ETM");
502                 root->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "ds");
503                 root->set_namespace_declaration ("http://www.w3.org/2001/04/xmlenc#", "enc");
504                 map<string, xmlpp::Attribute *> references;
505                 authenticated_public.as_xml (root->add_child ("AuthenticatedPublic"), references);
506                 authenticated_private.as_xml (root->add_child ("AuthenticatedPrivate"), references);
507                 signature.as_xml (root->add_child ("Signature", "ds"));
508
509                 for (map<string, xmlpp::Attribute*>::const_iterator i = references.begin(); i != references.end(); ++i) {
510                         xmlAddID (0, document->cobj(), (const xmlChar *) i->first.c_str(), i->second->cobj ());
511                 }
512
513                 return document;
514         }
515
516         AuthenticatedPublic authenticated_public;
517         AuthenticatedPrivate authenticated_private;
518         Signature signature;
519 };
520
521 }
522 }
523
524 EncryptedKDM::EncryptedKDM (string s)
525 {
526         shared_ptr<cxml::Document> doc (new cxml::Document ("DCinemaSecurityMessage"));
527         doc->read_string (s);
528         _data = new data::EncryptedKDMData (doc);
529 }
530
531 EncryptedKDM::EncryptedKDM (
532         shared_ptr<const CertificateChain> signer,
533         Certificate recipient,
534         vector<Certificate> trusted_devices,
535         string cpl_id,
536         string content_title_text,
537         optional<string> annotation_text,
538         LocalTime not_valid_before,
539         LocalTime not_valid_after,
540         Formulation formulation,
541         list<pair<string, string> > key_ids,
542         list<string> keys
543         )
544         : _data (new data::EncryptedKDMData)
545 {
546         /* Fill our XML-ish description in with the juicy bits that the caller has given */
547
548         /* Our ideas about the KDM types are:
549          *
550          * Type                      Trusted-device thumb  ContentAuthenticator
551          * MODIFIED_TRANSITIONAL_1   assume-trust          No
552          * DCI_ANY                   assume-trust          Yes
553          * DCI_SPECIFIC              as specified          Yes
554          */
555
556         data::AuthenticatedPublic& aup = _data->authenticated_public;
557         aup.signer.x509_issuer_name = signer->leaf().issuer ();
558         aup.signer.x509_serial_number = signer->leaf().serial ();
559         aup.annotation_text = annotation_text;
560
561         data::KDMRequiredExtensions& kre = _data->authenticated_public.required_extensions.kdm_required_extensions;
562         kre.recipient.x509_issuer_serial.x509_issuer_name = recipient.issuer ();
563         kre.recipient.x509_issuer_serial.x509_serial_number = recipient.serial ();
564         kre.recipient.x509_subject_name = recipient.subject ();
565         kre.composition_playlist_id = cpl_id;
566         if (formulation == DCI_ANY || formulation == DCI_SPECIFIC) {
567                 kre.content_authenticator = signer->leaf().thumbprint ();
568         }
569         kre.content_title_text = content_title_text;
570         kre.not_valid_before = not_valid_before;
571         kre.not_valid_after = not_valid_after;
572
573         if (formulation != MODIFIED_TRANSITIONAL_TEST) {
574                 kre.authorized_device_info = data::AuthorizedDeviceInfo ();
575                 kre.authorized_device_info->device_list_identifier = make_uuid ();
576                 string n = recipient.subject_common_name ();
577                 if (n.find (".") != string::npos) {
578                         n = n.substr (n.find (".") + 1);
579                 }
580                 kre.authorized_device_info->device_list_description = n;
581
582                 if (formulation == MODIFIED_TRANSITIONAL_1 || formulation == DCI_ANY) {
583                         /* Use the "assume trust" thumbprint */
584                         kre.authorized_device_info->certificate_thumbprints.push_back ("2jmj7l5rSw0yVb/vlWAYkK/YBwk=");
585                 } else if (formulation == DCI_SPECIFIC) {
586                         /* As I read the standard we should use the recipient
587                            /and/ other trusted device thumbprints here.  MJD
588                            reports that this doesn't work with his setup;
589                            a working KDM does not include the recipient's
590                            thumbprint (recipient.thumbprint()).
591                            Waimea uses only the trusted devices here, too.
592                         */
593                         BOOST_FOREACH (Certificate const & i, trusted_devices) {
594                                 kre.authorized_device_info->certificate_thumbprints.push_back (i.thumbprint ());
595                         }
596                 }
597         }
598
599         for (list<pair<string, string> >::const_iterator i = key_ids.begin(); i != key_ids.end(); ++i) {
600                 kre.key_id_list.typed_key_id.push_back (data::TypedKeyId (i->first, i->second));
601         }
602
603         _data->authenticated_private.encrypted_key = keys;
604
605         /* Read the XML so far and sign it */
606         shared_ptr<xmlpp::Document> doc = _data->as_xml ();
607         xmlpp::Node::NodeList children = doc->get_root_node()->get_children ();
608         for (xmlpp::Node::NodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
609                 if ((*i)->get_name() == "Signature") {
610                         signer->add_signature_value (*i, "ds");
611                 }
612         }
613
614         /* Read the bits that add_signature_value did back into our variables */
615         shared_ptr<cxml::Node> signed_doc (new cxml::Node (doc->get_root_node ()));
616         _data->signature = data::Signature (signed_doc->node_child ("Signature"));
617 }
618
619 EncryptedKDM::EncryptedKDM (EncryptedKDM const & other)
620         : _data (new data::EncryptedKDMData (*other._data))
621 {
622
623 }
624
625 EncryptedKDM &
626 EncryptedKDM::operator= (EncryptedKDM const & other)
627 {
628         if (this == &other) {
629                 return *this;
630         }
631
632         delete _data;
633         _data = new data::EncryptedKDMData (*other._data);
634         return *this;
635 }
636
637 EncryptedKDM::~EncryptedKDM ()
638 {
639         delete _data;
640 }
641
642 void
643 EncryptedKDM::as_xml (boost::filesystem::path path) const
644 {
645         FILE* f = fopen_boost (path, "w");
646         string const x = as_xml ();
647         fwrite (x.c_str(), 1, x.length(), f);
648         fclose (f);
649 }
650
651 string
652 EncryptedKDM::as_xml () const
653 {
654         return _data->as_xml()->write_to_string ("UTF-8");
655 }
656
657 list<string>
658 EncryptedKDM::keys () const
659 {
660         return _data->authenticated_private.encrypted_key;
661 }
662
663 optional<string>
664 EncryptedKDM::annotation_text () const
665 {
666         return _data->authenticated_public.annotation_text;
667 }
668
669 string
670 EncryptedKDM::content_title_text () const
671 {
672         return _data->authenticated_public.required_extensions.kdm_required_extensions.content_title_text;
673 }
674
675 string
676 EncryptedKDM::cpl_id () const
677 {
678         return _data->authenticated_public.required_extensions.kdm_required_extensions.composition_playlist_id;
679 }
680
681 string
682 EncryptedKDM::issue_date () const
683 {
684         return _data->authenticated_public.issue_date;
685 }
686
687 LocalTime
688 EncryptedKDM::not_valid_before () const
689 {
690         return _data->authenticated_public.required_extensions.kdm_required_extensions.not_valid_before;
691 }
692
693 LocalTime
694 EncryptedKDM::not_valid_after () const
695 {
696         return _data->authenticated_public.required_extensions.kdm_required_extensions.not_valid_after;
697 }
698
699 string
700 EncryptedKDM::recipient_x509_subject_name () const
701 {
702         return _data->authenticated_public.required_extensions.kdm_required_extensions.recipient.x509_subject_name;
703 }
704
705 bool
706 dcp::operator== (EncryptedKDM const & a, EncryptedKDM const & b)
707 {
708         /* Not exactly efficient... */
709         return a.as_xml() == b.as_xml();
710 }