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