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