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