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