Attempt to fix Sony digest validation by indenting the <Signer>
[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 /** @param trusted_devices Trusted device thumbprints */
580 EncryptedKDM::EncryptedKDM (
581         shared_ptr<const CertificateChain> signer,
582         Certificate recipient,
583         vector<string> trusted_devices,
584         string cpl_id,
585         string content_title_text,
586         optional<string> annotation_text,
587         LocalTime not_valid_before,
588         LocalTime not_valid_after,
589         Formulation formulation,
590         bool disable_forensic_marking_picture,
591         optional<int> disable_forensic_marking_audio,
592         list<pair<string, string> > key_ids,
593         list<string> keys
594         )
595         : _data (new data::EncryptedKDMData)
596 {
597         /* Fill our XML-ish description in with the juicy bits that the caller has given */
598
599         /* Our ideas, based on http://isdcf.com/papers/ISDCF-Doc5-kdm-certs.pdf, about the KDM types are:
600          *
601          * Type                               Trusted-device thumb  ContentAuthenticator
602          * MODIFIED_TRANSITIONAL_1            assume-trust          No
603          * MULTIPLE_MODIFIED_TRANSITIONAL_1   as specified          No
604          * DCI_ANY                            assume-trust          Yes
605          * DCI_SPECIFIC                       as specified          Yes
606          */
607
608         data::AuthenticatedPublic& aup = _data->authenticated_public;
609         aup.signer.x509_issuer_name = signer->leaf().issuer ();
610         aup.signer.x509_serial_number = signer->leaf().serial ();
611         aup.annotation_text = annotation_text;
612
613         data::KDMRequiredExtensions& kre = _data->authenticated_public.required_extensions.kdm_required_extensions;
614         kre.recipient.x509_issuer_serial.x509_issuer_name = recipient.issuer ();
615         kre.recipient.x509_issuer_serial.x509_serial_number = recipient.serial ();
616         kre.recipient.x509_subject_name = recipient.subject ();
617         kre.composition_playlist_id = cpl_id;
618         if (formulation == DCI_ANY || formulation == DCI_SPECIFIC) {
619                 kre.content_authenticator = signer->leaf().thumbprint ();
620         }
621         kre.content_title_text = content_title_text;
622         kre.not_valid_before = not_valid_before;
623         kre.not_valid_after = not_valid_after;
624         kre.disable_forensic_marking_picture = disable_forensic_marking_picture;
625         kre.disable_forensic_marking_audio = disable_forensic_marking_audio;
626
627         if (formulation != MODIFIED_TRANSITIONAL_TEST) {
628                 kre.authorized_device_info = data::AuthorizedDeviceInfo ();
629                 kre.authorized_device_info->device_list_identifier = make_uuid ();
630                 string n = recipient.subject_common_name ();
631                 if (n.find (".") != string::npos) {
632                         n = n.substr (n.find (".") + 1);
633                 }
634                 kre.authorized_device_info->device_list_description = n;
635
636                 if (formulation == MODIFIED_TRANSITIONAL_1 || formulation == DCI_ANY) {
637                         /* Use the "assume trust" thumbprint */
638                         kre.authorized_device_info->certificate_thumbprints.push_back ("2jmj7l5rSw0yVb/vlWAYkK/YBwk=");
639                 } else if (formulation == MULTIPLE_MODIFIED_TRANSITIONAL_1 || formulation == DCI_SPECIFIC) {
640                         if (trusted_devices.empty ()) {
641                                 /* Fall back on the "assume trust" thumbprint so we
642                                    can generate "modified-transitional-1" KDMs
643                                    together with "multiple-modified-transitional-1"
644                                    KDMs in one go, and similarly for "dci-any" etc.
645                                 */
646                                 kre.authorized_device_info->certificate_thumbprints.push_back ("2jmj7l5rSw0yVb/vlWAYkK/YBwk=");
647                         } else {
648                                 /* As I read the standard we should use the
649                                    recipient /and/ other trusted device thumbprints
650                                    here. MJD reports that this doesn't work with
651                                    his setup; a working KDM does not include the
652                                    recipient's thumbprint (recipient.thumbprint()).
653                                    Waimea uses only the trusted devices here, too.
654                                 */
655                                 BOOST_FOREACH (string i, trusted_devices) {
656                                         kre.authorized_device_info->certificate_thumbprints.push_back (i);
657                                 }
658                         }
659                 }
660         }
661
662         for (list<pair<string, string> >::const_iterator i = key_ids.begin(); i != key_ids.end(); ++i) {
663                 kre.key_id_list.typed_key_id.push_back (data::TypedKeyId (i->first, i->second));
664         }
665
666         _data->authenticated_private.encrypted_key = keys;
667
668         /* Read the XML so far and sign it */
669         shared_ptr<xmlpp::Document> doc = _data->as_xml ();
670         xmlpp::Node::NodeList children = doc->get_root_node()->get_children ();
671         for (xmlpp::Node::NodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
672                 if ((*i)->get_name() == "Signature") {
673                         signer->add_signature_value (dynamic_cast<xmlpp::Element*>(*i), "ds");
674                 }
675         }
676
677         /* Read the bits that add_signature_value did back into our variables */
678         shared_ptr<cxml::Node> signed_doc (new cxml::Node (doc->get_root_node ()));
679         _data->signature = data::Signature (signed_doc->node_child ("Signature"));
680 }
681
682 EncryptedKDM::EncryptedKDM (EncryptedKDM const & other)
683         : _data (new data::EncryptedKDMData (*other._data))
684 {
685
686 }
687
688 EncryptedKDM &
689 EncryptedKDM::operator= (EncryptedKDM const & other)
690 {
691         if (this == &other) {
692                 return *this;
693         }
694
695         delete _data;
696         _data = new data::EncryptedKDMData (*other._data);
697         return *this;
698 }
699
700 EncryptedKDM::~EncryptedKDM ()
701 {
702         delete _data;
703 }
704
705 void
706 EncryptedKDM::as_xml (boost::filesystem::path path) const
707 {
708         FILE* f = fopen_boost (path, "w");
709         string const x = as_xml ();
710         fwrite (x.c_str(), 1, x.length(), f);
711         fclose (f);
712 }
713
714 string
715 EncryptedKDM::as_xml () const
716 {
717         return _data->as_xml()->write_to_string ("UTF-8");
718 }
719
720 list<string>
721 EncryptedKDM::keys () const
722 {
723         return _data->authenticated_private.encrypted_key;
724 }
725
726 string
727 EncryptedKDM::id () const
728 {
729         return _data->authenticated_public.message_id;
730 }
731
732 optional<string>
733 EncryptedKDM::annotation_text () const
734 {
735         return _data->authenticated_public.annotation_text;
736 }
737
738 string
739 EncryptedKDM::content_title_text () const
740 {
741         return _data->authenticated_public.required_extensions.kdm_required_extensions.content_title_text;
742 }
743
744 string
745 EncryptedKDM::cpl_id () const
746 {
747         return _data->authenticated_public.required_extensions.kdm_required_extensions.composition_playlist_id;
748 }
749
750 string
751 EncryptedKDM::issue_date () const
752 {
753         return _data->authenticated_public.issue_date;
754 }
755
756 LocalTime
757 EncryptedKDM::not_valid_before () const
758 {
759         return _data->authenticated_public.required_extensions.kdm_required_extensions.not_valid_before;
760 }
761
762 LocalTime
763 EncryptedKDM::not_valid_after () const
764 {
765         return _data->authenticated_public.required_extensions.kdm_required_extensions.not_valid_after;
766 }
767
768 string
769 EncryptedKDM::recipient_x509_subject_name () const
770 {
771         return _data->authenticated_public.required_extensions.kdm_required_extensions.recipient.x509_subject_name;
772 }
773
774 bool
775 dcp::operator== (EncryptedKDM const & a, EncryptedKDM const & b)
776 {
777         /* Not exactly efficient... */
778         return a.as_xml() == b.as_xml();
779 }