2 Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /** @file src/xml/kdm_smpte.h
21 * @brief 1:1ish C++ representations of the XML schema for a SMPTE KDM.
23 * This file contains classes which map pretty-much 1:1 to the elements in a SMPTE KDM
24 * (Key Delivery Message). The `main' KDM class contains a pointer to a DCinemaSecurityMessage
27 * This should probably have been automatically generated from the XSD,
28 * but I think it's too much trouble considering that the XSD does not
32 #ifndef LIBDCP_XML_KDM_SMPTE_H
33 #define LIBDCP_XML_KDM_SMPTE_H
37 #include <boost/optional.hpp>
38 #include <boost/filesystem.hpp>
39 #include <libxml/parser.h>
40 #include <libxml++/libxml++.h>
41 #include <libcxml/cxml.h>
42 #include "../exceptions.h"
51 : document (new xmlpp::Document)
54 boost::shared_ptr<xmlpp::Document> document;
55 std::map<std::string, xmlpp::Attribute *> references;
62 Signer (boost::shared_ptr<const cxml::Node> node)
63 : x509_issuer_name (node->string_child ("X509IssuerName"))
64 , x509_serial_number (node->string_child ("X509SerialNumber"))
69 void as_xml (xmlpp::Element* node) const
71 node->add_child("X509IssuerName", "ds")->add_child_text (x509_issuer_name);
72 node->add_child("X509SerialNumber", "ds")->add_child_text (x509_serial_number);
75 std::string x509_issuer_name;
76 std::string x509_serial_number;
83 Recipient (boost::shared_ptr<const cxml::Node> node)
84 : x509_issuer_serial (node->node_child ("X509IssuerSerial"))
85 , x509_subject_name (node->string_child ("X509SubjectName"))
90 void as_xml (xmlpp::Element* node) const
92 x509_issuer_serial.as_xml (node->add_child ("X509IssuerSerial"));
93 node->add_child("X509SubjectName")->add_child_text (x509_subject_name);
96 Signer x509_issuer_serial;
97 std::string x509_subject_name;
100 class AuthorizedDeviceInfo
103 AuthorizedDeviceInfo () {}
104 AuthorizedDeviceInfo (boost::shared_ptr<const cxml::Node> node)
105 : device_list_identifier (node->string_child ("DeviceListIdentifier"))
106 , device_list_description (node->string_child ("DeviceListDescription"))
108 std::list<boost::shared_ptr<cxml::Node> > ct = node->node_child("DeviceList")->node_children("CertificateThumbprint");
109 for (std::list<boost::shared_ptr<cxml::Node> >::const_iterator i = ct.begin(); i != ct.end(); ++i) {
110 device_list.push_back ((*i)->content ());
116 void as_xml (xmlpp::Element* node) const
118 node->add_child ("DeviceListIdentifier")->add_child_text (device_list_identifier);
119 node->add_child ("DeviceListDescription")->add_child_text (device_list_description);
120 xmlpp::Element* dl = node->add_child ("DeviceList");
121 for (std::list<std::string>::const_iterator i = device_list.begin(); i != device_list.end(); ++i) {
122 dl->add_child("CertificateThumbprint")->add_child_text (*i);
126 std::string device_list_identifier;
127 std::string device_list_description;
128 std::list<std::string> device_list;
136 TypedKeyId (std::string t, std::string i)
141 TypedKeyId (boost::shared_ptr<const cxml::Node> node)
142 : key_type (node->string_child ("KeyType"))
143 , key_id (node->string_child ("KeyId"))
148 void as_xml (xmlpp::Element* node) const
150 node->add_child("KeyType")->add_child_text (key_type);
151 node->add_child("KeyId")->add_child_text (key_id);
154 std::string key_type;
158 class AuthenticatedPublic
161 AuthenticatedPublic () {}
162 AuthenticatedPublic (boost::shared_ptr<const cxml::Node> node)
163 : message_id (node->string_child ("MessageId"))
164 , message_type (node->string_child ("MessageType"))
165 , annotation_text (node->optional_string_child ("AnnotationText"))
166 , issue_date (node->string_child ("IssueDate"))
167 , signer (node->node_child ("Signer"))
169 boost::shared_ptr<const cxml::Node> c = node->node_child ("RequiredExtensions");
170 c = c->node_child ("KDMRequiredExtensions");
171 recipient = Recipient (c->node_child ("Recipient"));
172 composition_playlist_id = c->string_child ("CompositionPlaylistId");
173 content_title_text = c->string_child ("ContentTitleText");
174 content_keys_not_valid_before = c->string_child ("ContentKeysNotValidBefore");
175 content_keys_not_valid_after = c->string_child ("ContentKeysNotValidAfter");
176 authorized_device_info = AuthorizedDeviceInfo (c->node_child ("AuthorizedDeviceInfo"));
178 std::list<boost::shared_ptr<cxml::Node> > kil = c->node_child("KeyIdList")->node_children("TypedKeyId");
179 for (std::list<boost::shared_ptr<cxml::Node> >::iterator i = kil.begin(); i != kil.end(); ++i) {
180 key_id_list.push_back (TypedKeyId (*i));
183 boost::shared_ptr<cxml::Node> fmfl = c->optional_node_child("ForensicMarkFlagList");
185 std::list<boost::shared_ptr<cxml::Node> > fmf = fmfl->node_children("ForensicMarkFlag");
186 for (std::list<boost::shared_ptr<cxml::Node> >::iterator i = fmf.begin(); i != fmf.end(); ++i) {
187 forensic_mark_flag_list.push_back ((*i)->content ());
191 node->ignore_child ("NonCriticalExtensions");
195 void as_xml (Writer& writer, xmlpp::Element* node) const
197 writer.references["ID_AuthenticatedPublic"] = node->set_attribute ("Id", "ID_AuthenticatedPublic");
199 node->add_child("MessageId")->add_child_text (message_id);
200 node->add_child("MessageType")->add_child_text (message_type);
201 if (annotation_text) {
202 node->add_child("AnnotationText")->add_child_text (annotation_text.get ());
204 node->add_child("IssueDate")->add_child_text (issue_date);
205 signer.as_xml (node->add_child("Signer"));
207 xmlpp::Element* kdm_required_extensions = node->add_child("RequiredExtensions")->add_child("KDMRequiredExtensions");
208 kdm_required_extensions->set_attribute ("xmlns", "http://www.smpte-ra.org/schemas/430-1/2006/KDM");
209 recipient.as_xml (kdm_required_extensions->add_child ("Recipient"));
211 kdm_required_extensions->add_child("CompositionPlaylistId")->add_child_text (composition_playlist_id);
212 kdm_required_extensions->add_child("ContentTitleText")->add_child_text (content_title_text);
213 kdm_required_extensions->add_child("ContentKeysNotValidBefore")->add_child_text (content_keys_not_valid_before);
214 kdm_required_extensions->add_child("ContentKeysNotValidAfter")->add_child_text (content_keys_not_valid_after);
215 authorized_device_info.as_xml (kdm_required_extensions->add_child("AuthorizedDeviceInfo"));
217 xmlpp::Element* kil = kdm_required_extensions->add_child("KeyIdList");
218 for (std::list<TypedKeyId>::const_iterator i = key_id_list.begin(); i != key_id_list.end(); ++i) {
219 i->as_xml (kil->add_child ("TypedKeyId"));
222 xmlpp::Element* fmfl = kdm_required_extensions->add_child ("ForensicMarkFlagList");
223 for (std::list<std::string>::const_iterator i = forensic_mark_flag_list.begin(); i != forensic_mark_flag_list.end(); ++i) {
224 fmfl->add_child("ForensicMarkFlag")->add_child_text (*i);
227 node->add_child ("NonCriticalExtensions");
230 std::string message_id;
231 std::string message_type;
232 boost::optional<std::string> annotation_text;
233 std::string issue_date;
236 std::string composition_playlist_id;
237 std::string content_title_text;
238 std::string content_keys_not_valid_before;
239 std::string content_keys_not_valid_after;
240 AuthorizedDeviceInfo authorized_device_info;
241 std::list<TypedKeyId> key_id_list;
242 std::list<std::string> forensic_mark_flag_list;
245 class AuthenticatedPrivate
248 AuthenticatedPrivate () {}
250 AuthenticatedPrivate (boost::shared_ptr<const cxml::Node> node)
252 std::list<boost::shared_ptr<cxml::Node> > ek = node->node_children ("EncryptedKey");
253 for (std::list<boost::shared_ptr<cxml::Node> >::const_iterator i = ek.begin(); i != ek.end(); ++i) {
254 encrypted_keys.push_back ((*i)->node_child("CipherData")->string_child("CipherValue"));
260 void as_xml (Writer& writer, xmlpp::Element* node) const
262 writer.references["ID_AuthenticatedPrivate"] = node->set_attribute ("Id", "ID_AuthenticatedPrivate");
264 for (std::list<std::string>::const_iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) {
265 xmlpp::Element* encrypted_key = node->add_child ("EncryptedKey", "enc");
266 xmlpp::Element* encryption_method = encrypted_key->add_child ("EncryptionMethod", "enc");
267 encryption_method->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p");
268 xmlpp::Element* digest_method = encryption_method->add_child ("DigestMethod", "ds");
269 digest_method->set_attribute ("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
270 xmlpp::Element* cipher_data = encrypted_key->add_child ("CipherData", "enc");
271 cipher_data->add_child("CipherValue", "enc")->add_child_text (*i);
275 std::list<std::string> encrypted_keys;
282 X509Data (boost::shared_ptr<const cxml::Node> node)
283 : x509_issuer_serial (Signer (node->node_child ("X509IssuerSerial")))
284 , x509_certificate (node->string_child ("X509Certificate"))
289 void as_xml (xmlpp::Element* node) const
291 x509_issuer_serial.as_xml (node->add_child ("X509IssuerSerial", "ds"));
292 node->add_child("X509Certificate", "ds")->add_child_text (x509_certificate);
295 Signer x509_issuer_serial;
296 std::string x509_certificate;
303 Reference (std::string u)
307 Reference (boost::shared_ptr<const cxml::Node> node)
308 : uri (node->string_attribute ("URI"))
309 , digest_value (node->string_child ("DigestValue"))
311 node->ignore_child ("DigestMethod");
315 void as_xml (Writer& writer, xmlpp::Element* node) const
317 xmlpp::Element* reference = node->add_child ("Reference", "ds");
318 reference->set_attribute ("URI", uri);
319 reference->add_child("DigestMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256");
320 reference->add_child("DigestValue", "ds")->add_child_text (digest_value);
323 xmlAddID (0, writer.document->cobj(), (const xmlChar *) uri.substr(1).c_str(), writer.references[uri.substr(1)]->cobj ());
328 std::string digest_value;
335 : authenticated_public ("#ID_AuthenticatedPublic")
336 , authenticated_private ("#ID_AuthenticatedPrivate")
339 Signature (boost::shared_ptr<const cxml::Node> node)
341 std::list<boost::shared_ptr<cxml::Node> > refs = node->node_child("SignedInfo")->node_children ("Reference");
342 for (std::list<boost::shared_ptr<cxml::Node> >::const_iterator i = refs.begin(); i != refs.end(); ++i) {
343 if ((*i)->string_attribute("URI") == "#ID_AuthenticatedPublic") {
344 authenticated_public = Reference (*i);
345 } else if ((*i)->string_attribute("URI") == "#ID_AuthenticatedPrivate") {
346 authenticated_private = Reference (*i);
348 throw XMLError ("unrecognised reference URI");
352 std::list<boost::shared_ptr<cxml::Node> > data = node->node_child("KeyInfo")->node_children ("X509Data");
353 for (std::list<boost::shared_ptr<cxml::Node> >::const_iterator i = data.begin(); i != data.end(); ++i) {
354 key_info.push_back (X509Data (*i));
357 signature_value = node->string_child ("SignatureValue");
362 void as_xml (Writer& writer, xmlpp::Element* node) const
364 xmlpp::Element* si = node->add_child ("SignedInfo", "ds");
365 si->add_child ("CanonicalizationMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments");
366 si->add_child ("SignatureMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
368 authenticated_public.as_xml (writer, si);
369 authenticated_private.as_xml (writer, si);
371 node->add_child("SignatureValue", "ds")->add_child_text (signature_value);
373 xmlpp::Element* ki = node->add_child ("KeyInfo", "ds");
374 for (std::list<X509Data>::const_iterator i = key_info.begin(); i != key_info.end(); ++i) {
375 i->as_xml (ki->add_child ("X509Data", "ds"));
379 Reference authenticated_public;
380 Reference authenticated_private;
381 std::string signature_value;
382 std::list<X509Data> key_info;
385 class DCinemaSecurityMessage
388 DCinemaSecurityMessage () {}
389 DCinemaSecurityMessage (boost::filesystem::path file)
391 cxml::Document f ("DCinemaSecurityMessage");
392 f.read_file (file.string ());
394 authenticated_public = AuthenticatedPublic (f.node_child ("AuthenticatedPublic"));
395 authenticated_private = AuthenticatedPrivate (f.node_child ("AuthenticatedPrivate"));
396 signature = Signature (f.node_child ("Signature"));
401 boost::shared_ptr<xmlpp::Document> as_xml () const
405 xmlpp::Element* root = writer.document->create_root_node ("DCinemaSecurityMessage", "http://www.smpte-ra.org/schemas/430-3/2006/ETM");
406 root->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "ds");
407 root->set_namespace_declaration ("http://www.w3.org/2001/04/xmlenc#", "enc");
409 authenticated_public.as_xml (writer, root->add_child ("AuthenticatedPublic"));
410 authenticated_private.as_xml (writer, root->add_child ("AuthenticatedPrivate"));
411 signature.as_xml (writer, root->add_child ("Signature", "ds"));
413 return writer.document;
416 AuthenticatedPublic authenticated_public;
417 AuthenticatedPrivate authenticated_private;