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