2 Copyright (C) 2013-2017 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
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.
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.
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/>.
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
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.
34 #include "encrypted_kdm.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/date_time/posix_time/posix_time.hpp>
43 #include <boost/foreach.hpp>
50 using boost::shared_ptr;
51 using boost::optional;
56 /** Namespace for classes used to hold our data; they are internal to this .cc file */
64 explicit Signer (shared_ptr<const cxml::Node> node)
65 : x509_issuer_name (node->string_child ("X509IssuerName"))
66 , x509_serial_number (node->string_child ("X509SerialNumber"))
71 void as_xml (xmlpp::Element* node) const
73 node->add_child("X509IssuerName", "ds")->add_child_text (x509_issuer_name);
74 node->add_child("X509SerialNumber", "ds")->add_child_text (x509_serial_number);
77 string x509_issuer_name;
78 string x509_serial_number;
86 explicit X509Data (boost::shared_ptr<const cxml::Node> node)
87 : x509_issuer_serial (Signer (node->node_child ("X509IssuerSerial")))
88 , x509_certificate (node->string_child ("X509Certificate"))
93 void as_xml (xmlpp::Element* node) const
95 x509_issuer_serial.as_xml (node->add_child ("X509IssuerSerial", "ds"));
96 node->add_child("X509Certificate", "ds")->add_child_text (x509_certificate);
99 Signer x509_issuer_serial;
100 std::string x509_certificate;
108 explicit Reference (string u)
112 explicit Reference (shared_ptr<const cxml::Node> node)
113 : uri (node->string_attribute ("URI"))
114 , digest_value (node->string_child ("DigestValue"))
119 void as_xml (xmlpp::Element* node) const
121 node->set_attribute ("URI", uri);
122 node->add_child("DigestMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256");
123 node->add_child("DigestValue", "ds")->add_child_text (digest_value);
134 : authenticated_public ("#ID_AuthenticatedPublic")
135 , authenticated_private ("#ID_AuthenticatedPrivate")
138 explicit SignedInfo (shared_ptr<const cxml::Node> node)
140 list<shared_ptr<cxml::Node> > references = node->node_children ("Reference");
141 for (list<shared_ptr<cxml::Node> >::const_iterator i = references.begin(); i != references.end(); ++i) {
142 if ((*i)->string_attribute ("URI") == "#ID_AuthenticatedPublic") {
143 authenticated_public = Reference (*i);
144 } else if ((*i)->string_attribute ("URI") == "#ID_AuthenticatedPrivate") {
145 authenticated_private = Reference (*i);
148 /* XXX: do something if we don't recognise the node */
152 void as_xml (xmlpp::Element* node) const
154 node->add_child ("CanonicalizationMethod", "ds")->set_attribute (
155 "Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
158 node->add_child ("SignatureMethod", "ds")->set_attribute (
159 "Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
162 authenticated_public.as_xml (node->add_child ("Reference", "ds"));
163 authenticated_private.as_xml (node->add_child ("Reference", "ds"));
167 Reference authenticated_public;
168 Reference authenticated_private;
176 explicit Signature (shared_ptr<const cxml::Node> node)
177 : signed_info (node->node_child ("SignedInfo"))
178 , signature_value (node->string_child ("SignatureValue"))
180 list<shared_ptr<cxml::Node> > x509_data_nodes = node->node_child("KeyInfo")->node_children ("X509Data");
181 for (list<shared_ptr<cxml::Node> >::const_iterator i = x509_data_nodes.begin(); i != x509_data_nodes.end(); ++i) {
182 x509_data.push_back (X509Data (*i));
186 void as_xml (xmlpp::Node* node) const
188 signed_info.as_xml (node->add_child ("SignedInfo", "ds"));
189 node->add_child("SignatureValue", "ds")->add_child_text (signature_value);
191 xmlpp::Element* key_info_node = node->add_child ("KeyInfo", "ds");
192 for (std::list<X509Data>::const_iterator i = x509_data.begin(); i != x509_data.end(); ++i) {
193 i->as_xml (key_info_node->add_child ("X509Data", "ds"));
197 SignedInfo signed_info;
198 string signature_value;
199 list<X509Data> x509_data;
202 class AuthenticatedPrivate
205 AuthenticatedPrivate () {}
207 explicit AuthenticatedPrivate (shared_ptr<const cxml::Node> node)
209 list<shared_ptr<cxml::Node> > encrypted_key_nodes = node->node_children ("EncryptedKey");
210 for (list<shared_ptr<cxml::Node> >::const_iterator i = encrypted_key_nodes.begin(); i != encrypted_key_nodes.end(); ++i) {
211 encrypted_key.push_back ((*i)->node_child("CipherData")->string_child ("CipherValue"));
215 void as_xml (xmlpp::Element* node, map<string, xmlpp::Attribute *>& references) const
217 references["ID_AuthenticatedPrivate"] = node->set_attribute ("Id", "ID_AuthenticatedPrivate");
219 for (list<string>::const_iterator i = encrypted_key.begin(); i != encrypted_key.end(); ++i) {
220 xmlpp::Element* encrypted_key = node->add_child ("EncryptedKey", "enc");
221 /* XXX: hack for testing with Dolby */
222 encrypted_key->set_namespace_declaration ("http://www.w3.org/2001/04/xmlenc#", "enc");
223 xmlpp::Element* encryption_method = encrypted_key->add_child ("EncryptionMethod", "enc");
224 encryption_method->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p");
225 xmlpp::Element* digest_method = encryption_method->add_child ("DigestMethod", "ds");
226 /* XXX: hack for testing with Dolby */
227 digest_method->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "ds");
228 digest_method->set_attribute ("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
229 xmlpp::Element* cipher_data = encrypted_key->add_child ("CipherData", "enc");
230 cipher_data->add_child("CipherValue", "enc")->add_child_text (*i);
234 list<string> encrypted_key;
242 explicit TypedKeyId (shared_ptr<const cxml::Node> node)
243 : key_type (node->string_child ("KeyType"))
244 , key_id (remove_urn_uuid (node->string_child ("KeyId")))
249 TypedKeyId (string type, string id)
254 void as_xml (xmlpp::Element* node) const
256 xmlpp::Element* type = node->add_child("KeyType");
257 type->add_child_text (key_type);
258 node->add_child("KeyId")->add_child_text ("urn:uuid:" + key_id);
259 /* XXX: this feels like a bit of a hack */
260 if (key_type == "MDEK") {
261 type->set_attribute ("scope", "http://www.dolby.com/cp850/2012/KDM#kdm-key-type");
263 type->set_attribute ("scope", "http://www.smpte-ra.org/430-1/2006/KDM#kdm-key-type");
276 explicit KeyIdList (shared_ptr<const cxml::Node> node)
278 list<shared_ptr<cxml::Node> > typed_key_id_nodes = node->node_children ("TypedKeyId");
279 for (list<shared_ptr<cxml::Node> >::const_iterator i = typed_key_id_nodes.begin(); i != typed_key_id_nodes.end(); ++i) {
280 typed_key_id.push_back (TypedKeyId (*i));
284 void as_xml (xmlpp::Element* node) const
286 for (list<TypedKeyId>::const_iterator i = typed_key_id.begin(); i != typed_key_id.end(); ++i) {
287 i->as_xml (node->add_child("TypedKeyId"));
291 list<TypedKeyId> typed_key_id;
294 class AuthorizedDeviceInfo
297 AuthorizedDeviceInfo () {}
299 explicit AuthorizedDeviceInfo (shared_ptr<const cxml::Node> node)
300 : device_list_identifier (remove_urn_uuid (node->string_child ("DeviceListIdentifier")))
301 , device_list_description (node->optional_string_child ("DeviceListDescription"))
303 BOOST_FOREACH (cxml::ConstNodePtr i, node->node_child("DeviceList")->node_children("CertificateThumbprint")) {
304 certificate_thumbprints.push_back (i->content ());
308 void as_xml (xmlpp::Element* node) const
310 node->add_child ("DeviceListIdentifier")->add_child_text ("urn:uuid:" + device_list_identifier);
311 if (device_list_description) {
312 node->add_child ("DeviceListDescription")->add_child_text (device_list_description.get());
314 xmlpp::Element* device_list = node->add_child ("DeviceList");
315 BOOST_FOREACH (string i, certificate_thumbprints) {
316 device_list->add_child("CertificateThumbprint")->add_child_text (i);
320 /** DeviceListIdentifier without the urn:uuid: prefix */
321 string device_list_identifier;
322 boost::optional<string> device_list_description;
323 std::list<string> certificate_thumbprints;
326 class X509IssuerSerial
329 X509IssuerSerial () {}
331 explicit X509IssuerSerial (shared_ptr<const cxml::Node> node)
332 : x509_issuer_name (node->string_child ("X509IssuerName"))
333 , x509_serial_number (node->string_child ("X509SerialNumber"))
338 void as_xml (xmlpp::Element* node) const
340 node->add_child("X509IssuerName", "ds")->add_child_text (x509_issuer_name);
341 node->add_child("X509SerialNumber", "ds")->add_child_text (x509_serial_number);
344 string x509_issuer_name;
345 string x509_serial_number;
353 explicit Recipient (shared_ptr<const cxml::Node> node)
354 : x509_issuer_serial (node->node_child ("X509IssuerSerial"))
355 , x509_subject_name (node->string_child ("X509SubjectName"))
360 void as_xml (xmlpp::Element* node) const
362 x509_issuer_serial.as_xml (node->add_child ("X509IssuerSerial"));
363 node->add_child("X509SubjectName")->add_child_text (x509_subject_name);
366 X509IssuerSerial x509_issuer_serial;
367 string x509_subject_name;
370 class KDMRequiredExtensions
373 KDMRequiredExtensions () {}
375 explicit KDMRequiredExtensions (shared_ptr<const cxml::Node> node)
376 : recipient (node->node_child ("Recipient"))
377 , composition_playlist_id (remove_urn_uuid (node->string_child ("CompositionPlaylistId")))
378 , content_title_text (node->string_child ("ContentTitleText"))
379 , not_valid_before (node->string_child ("ContentKeysNotValidBefore"))
380 , not_valid_after (node->string_child ("ContentKeysNotValidAfter"))
381 , authorized_device_info (node->node_child ("AuthorizedDeviceInfo"))
382 , key_id_list (node->node_child ("KeyIdList"))
387 void as_xml (xmlpp::Element* node) const
389 node->set_attribute ("xmlns", "http://www.smpte-ra.org/schemas/430-1/2006/KDM");
391 recipient.as_xml (node->add_child ("Recipient"));
392 node->add_child("CompositionPlaylistId")->add_child_text ("urn:uuid:" + composition_playlist_id);
393 node->add_child("ContentTitleText")->add_child_text (content_title_text);
394 if (content_authenticator) {
395 node->add_child("ContentAuthenticator")->add_child_text (content_authenticator.get ());
397 node->add_child("ContentKeysNotValidBefore")->add_child_text (not_valid_before.as_string ());
398 node->add_child("ContentKeysNotValidAfter")->add_child_text (not_valid_after.as_string ());
399 if (authorized_device_info) {
400 authorized_device_info->as_xml (node->add_child ("AuthorizedDeviceInfo"));
402 key_id_list.as_xml (node->add_child ("KeyIdList"));
404 xmlpp::Element* forensic_mark_flag_list = node->add_child ("ForensicMarkFlagList");
405 forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable");
406 forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable");
410 string composition_playlist_id;
411 boost::optional<string> content_authenticator;
412 string content_title_text;
413 LocalTime not_valid_before;
414 LocalTime not_valid_after;
415 boost::optional<AuthorizedDeviceInfo> authorized_device_info;
416 KeyIdList key_id_list;
419 class RequiredExtensions
422 RequiredExtensions () {}
424 explicit RequiredExtensions (shared_ptr<const cxml::Node> node)
425 : kdm_required_extensions (node->node_child ("KDMRequiredExtensions"))
430 void as_xml (xmlpp::Element* node) const
432 kdm_required_extensions.as_xml (node->add_child ("KDMRequiredExtensions"));
435 KDMRequiredExtensions kdm_required_extensions;
438 class AuthenticatedPublic
441 AuthenticatedPublic ()
442 : message_id (make_uuid ())
443 /* XXX: hack for Dolby to see if there must be a not-empty annotation text */
444 , annotation_text ("none")
445 , issue_date (LocalTime().as_string ())
448 explicit AuthenticatedPublic (shared_ptr<const cxml::Node> node)
449 : message_id (remove_urn_uuid (node->string_child ("MessageId")))
450 , annotation_text (node->optional_string_child ("AnnotationText"))
451 , issue_date (node->string_child ("IssueDate"))
452 , signer (node->node_child ("Signer"))
453 , required_extensions (node->node_child ("RequiredExtensions"))
458 void as_xml (xmlpp::Element* node, map<string, xmlpp::Attribute *>& references) const
460 references["ID_AuthenticatedPublic"] = node->set_attribute ("Id", "ID_AuthenticatedPublic");
462 node->add_child("MessageId")->add_child_text ("urn:uuid:" + message_id);
463 node->add_child("MessageType")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#kdm-key-type");
464 if (annotation_text) {
465 node->add_child("AnnotationText")->add_child_text (annotation_text.get ());
467 node->add_child("IssueDate")->add_child_text (issue_date);
469 signer.as_xml (node->add_child ("Signer"));
470 required_extensions.as_xml (node->add_child ("RequiredExtensions"));
472 node->add_child ("NonCriticalExtensions");
476 optional<string> annotation_text;
479 RequiredExtensions required_extensions;
482 /** Class to describe our data. We use a class hierarchy as it's a bit nicer
483 * for XML data than a flat description.
485 class EncryptedKDMData
493 explicit EncryptedKDMData (shared_ptr<const cxml::Node> node)
494 : authenticated_public (node->node_child ("AuthenticatedPublic"))
495 , authenticated_private (node->node_child ("AuthenticatedPrivate"))
496 , signature (node->node_child ("Signature"))
501 shared_ptr<xmlpp::Document> as_xml () const
503 shared_ptr<xmlpp::Document> document (new xmlpp::Document ());
504 xmlpp::Element* root = document->create_root_node ("DCinemaSecurityMessage", "http://www.smpte-ra.org/schemas/430-3/2006/ETM");
505 root->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "ds");
506 root->set_namespace_declaration ("http://www.w3.org/2001/04/xmlenc#", "enc");
507 map<string, xmlpp::Attribute *> references;
508 authenticated_public.as_xml (root->add_child ("AuthenticatedPublic"), references);
509 authenticated_private.as_xml (root->add_child ("AuthenticatedPrivate"), references);
510 signature.as_xml (root->add_child ("Signature", "ds"));
512 for (map<string, xmlpp::Attribute*>::const_iterator i = references.begin(); i != references.end(); ++i) {
513 xmlAddID (0, document->cobj(), (const xmlChar *) i->first.c_str(), i->second->cobj ());
519 AuthenticatedPublic authenticated_public;
520 AuthenticatedPrivate authenticated_private;
527 EncryptedKDM::EncryptedKDM (string s)
530 shared_ptr<cxml::Document> doc (new cxml::Document ("DCinemaSecurityMessage"));
531 doc->read_string (s);
532 _data = new data::EncryptedKDMData (doc);
533 } catch (xmlpp::parse_error& e) {
534 throw KDMFormatError (e.what ());
538 EncryptedKDM::EncryptedKDM (
539 shared_ptr<const CertificateChain> signer,
540 Certificate recipient,
541 vector<Certificate> trusted_devices,
543 string content_title_text,
544 optional<string> annotation_text,
545 LocalTime not_valid_before,
546 LocalTime not_valid_after,
547 Formulation formulation,
548 list<pair<string, string> > key_ids,
551 : _data (new data::EncryptedKDMData)
553 /* Fill our XML-ish description in with the juicy bits that the caller has given */
555 /* Our ideas about the KDM types are:
557 * Type Trusted-device thumb ContentAuthenticator
558 * MODIFIED_TRANSITIONAL_1 assume-trust No
559 * DCI_ANY assume-trust Yes
560 * DCI_SPECIFIC as specified Yes
563 data::AuthenticatedPublic& aup = _data->authenticated_public;
564 aup.signer.x509_issuer_name = signer->leaf().issuer ();
565 aup.signer.x509_serial_number = signer->leaf().serial ();
566 aup.annotation_text = annotation_text;
568 data::KDMRequiredExtensions& kre = _data->authenticated_public.required_extensions.kdm_required_extensions;
569 kre.recipient.x509_issuer_serial.x509_issuer_name = recipient.issuer ();
570 kre.recipient.x509_issuer_serial.x509_serial_number = recipient.serial ();
571 kre.recipient.x509_subject_name = recipient.subject ();
572 kre.composition_playlist_id = cpl_id;
573 if (formulation == DCI_ANY || formulation == DCI_SPECIFIC) {
574 kre.content_authenticator = signer->leaf().thumbprint ();
576 kre.content_title_text = content_title_text;
577 kre.not_valid_before = not_valid_before;
578 kre.not_valid_after = not_valid_after;
580 if (formulation != MODIFIED_TRANSITIONAL_TEST) {
581 kre.authorized_device_info = data::AuthorizedDeviceInfo ();
582 kre.authorized_device_info->device_list_identifier = make_uuid ();
583 string n = recipient.subject_common_name ();
584 if (n.find (".") != string::npos) {
585 n = n.substr (n.find (".") + 1);
587 kre.authorized_device_info->device_list_description = n;
589 if (formulation == MODIFIED_TRANSITIONAL_1 || formulation == DCI_ANY) {
590 /* Use the "assume trust" thumbprint */
591 kre.authorized_device_info->certificate_thumbprints.push_back ("2jmj7l5rSw0yVb/vlWAYkK/YBwk=");
592 } else if (formulation == DCI_SPECIFIC) {
593 /* As I read the standard we should use the recipient
594 /and/ other trusted device thumbprints here. MJD
595 reports that this doesn't work with his setup;
596 a working KDM does not include the recipient's
597 thumbprint (recipient.thumbprint()).
598 Waimea uses only the trusted devices here, too.
600 BOOST_FOREACH (Certificate const & i, trusted_devices) {
601 kre.authorized_device_info->certificate_thumbprints.push_back (i.thumbprint ());
606 for (list<pair<string, string> >::const_iterator i = key_ids.begin(); i != key_ids.end(); ++i) {
607 kre.key_id_list.typed_key_id.push_back (data::TypedKeyId (i->first, i->second));
610 _data->authenticated_private.encrypted_key = keys;
612 /* Read the XML so far and sign it */
613 shared_ptr<xmlpp::Document> doc = _data->as_xml ();
614 xmlpp::Node::NodeList children = doc->get_root_node()->get_children ();
615 for (xmlpp::Node::NodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
616 if ((*i)->get_name() == "Signature") {
617 signer->add_signature_value (*i, "ds");
621 /* Read the bits that add_signature_value did back into our variables */
622 shared_ptr<cxml::Node> signed_doc (new cxml::Node (doc->get_root_node ()));
623 _data->signature = data::Signature (signed_doc->node_child ("Signature"));
626 EncryptedKDM::EncryptedKDM (EncryptedKDM const & other)
627 : _data (new data::EncryptedKDMData (*other._data))
633 EncryptedKDM::operator= (EncryptedKDM const & other)
635 if (this == &other) {
640 _data = new data::EncryptedKDMData (*other._data);
644 EncryptedKDM::~EncryptedKDM ()
650 EncryptedKDM::as_xml (boost::filesystem::path path) const
652 FILE* f = fopen_boost (path, "w");
653 string const x = as_xml ();
654 fwrite (x.c_str(), 1, x.length(), f);
659 EncryptedKDM::as_xml () const
661 return _data->as_xml()->write_to_string ("UTF-8");
665 EncryptedKDM::keys () const
667 return _data->authenticated_private.encrypted_key;
671 EncryptedKDM::annotation_text () const
673 return _data->authenticated_public.annotation_text;
677 EncryptedKDM::content_title_text () const
679 return _data->authenticated_public.required_extensions.kdm_required_extensions.content_title_text;
683 EncryptedKDM::cpl_id () const
685 return _data->authenticated_public.required_extensions.kdm_required_extensions.composition_playlist_id;
689 EncryptedKDM::issue_date () const
691 return _data->authenticated_public.issue_date;
695 EncryptedKDM::not_valid_before () const
697 return _data->authenticated_public.required_extensions.kdm_required_extensions.not_valid_before;
701 EncryptedKDM::not_valid_after () const
703 return _data->authenticated_public.required_extensions.kdm_required_extensions.not_valid_after;
707 EncryptedKDM::recipient_x509_subject_name () const
709 return _data->authenticated_public.required_extensions.kdm_required_extensions.recipient.x509_subject_name;
713 dcp::operator== (EncryptedKDM const & a, EncryptedKDM const & b)
715 /* Not exactly efficient... */
716 return a.as_xml() == b.as_xml();