Move everything into the header.
[libdcp.git] / src / xml / kdm_smpte.h
1 /*
2     Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
3
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.
8
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.
13
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.
17
18 */
19
20 #ifndef LIBDCP_XML_KDM_SMPTE_H
21 #define LIBDCP_XML_KDM_SMPTE_H
22
23 #include <string>
24 #include <list>
25 #include <boost/optional.hpp>
26 #include <boost/filesystem.hpp>
27 #include <libxml/parser.h>
28
29 namespace libdcp {
30 namespace xml {
31
32 class Writer
33 {
34 public:
35         Writer ()
36                 : document (new xmlpp::Document)
37         {}
38         
39         boost::shared_ptr<xmlpp::Document> document;
40         std::map<std::string, xmlpp::Attribute *> references;
41 };
42
43 class Signer
44 {
45 public:
46         Signer () {}
47         Signer (boost::shared_ptr<const cxml::Node> node)
48                 : x509_issuer_name (node->string_child ("X509IssuerName"))
49                 , x509_serial_number (node->string_child ("X509SerialNumber"))
50         {
51                 node->done ();
52         }
53
54         void as_xml (xmlpp::Element* node) const
55         {
56                 node->add_child("X509IssuerName", "ds")->add_child_text (x509_issuer_name);
57                 node->add_child("X509SerialNumber", "ds")->add_child_text (x509_serial_number);
58         }
59         
60         std::string x509_issuer_name;
61         std::string x509_serial_number;
62 };
63
64 class Recipient
65 {
66 public:
67         Recipient () {}
68         Recipient (boost::shared_ptr<const cxml::Node> node)
69                 : x509_issuer_serial (node->node_child ("X509IssuerSerial"))
70                 , x509_subject_name (node->string_child ("X509SubjectName"))
71         {
72                 node->done ();
73         }
74
75         void as_xml (xmlpp::Element* node) const
76         {
77                 x509_issuer_serial.as_xml (node->add_child ("X509IssuerSerial"));
78                 node->add_child("X509SubjectName")->add_child_text (x509_subject_name);
79         }
80         
81         Signer x509_issuer_serial;
82         std::string x509_subject_name;
83 };
84
85 class AuthorizedDeviceInfo
86 {
87 public:
88         AuthorizedDeviceInfo () {}
89         AuthorizedDeviceInfo (boost::shared_ptr<const cxml::Node> node)
90                 : device_list_identifier (node->string_child ("DeviceListIdentifier"))
91                 , device_list_description (node->string_child ("DeviceListDescription"))
92         {
93                 std::list<boost::shared_ptr<cxml::Node> > ct = node->node_child("DeviceList")->node_children("CertificateThumbprint");
94                 for (std::list<boost::shared_ptr<cxml::Node> >::const_iterator i = ct.begin(); i != ct.end(); ++i) {
95                         device_list.push_back ((*i)->content ());
96                 }
97
98                 node->done ();
99         }
100         
101         void as_xml (xmlpp::Element* node) const
102         {
103                 node->add_child ("DeviceListIdentifier")->add_child_text (device_list_identifier);
104                 node->add_child ("DeviceListDescription")->add_child_text (device_list_description);
105                 xmlpp::Element* dl = node->add_child ("DeviceList");
106                 for (std::list<std::string>::const_iterator i = device_list.begin(); i != device_list.end(); ++i) {
107                         dl->add_child("CertificateThumbprint")->add_child_text (*i);
108                 }
109         }
110         
111         std::string device_list_identifier;
112         std::string device_list_description;
113         std::list<std::string> device_list;
114 };
115
116 class TypedKeyId
117 {
118 public:
119         TypedKeyId () {}
120
121         TypedKeyId (std::string t, std::string i)
122                 : key_type (t)
123                 , key_id (i)
124         {}
125         
126         TypedKeyId (boost::shared_ptr<const cxml::Node> node)
127                 : key_type (node->string_child ("KeyType"))
128                 , key_id (node->string_child ("KeyId"))
129         {
130                 node->done ();
131         }
132         
133         void as_xml (xmlpp::Element* node) const
134         {
135                 node->add_child("KeyType")->add_child_text (key_type);
136                 node->add_child("KeyId")->add_child_text (key_id);
137         }
138
139         std::string key_type;
140         std::string key_id;
141 };
142
143 class AuthenticatedPublic
144 {
145 public:
146         AuthenticatedPublic () {}
147         AuthenticatedPublic (boost::shared_ptr<const cxml::Node> node)
148                 : message_id (node->string_child ("MessageId"))
149                 , message_type (node->string_child ("MessageType"))
150                 , annotation_text (node->optional_string_child ("AnnotationText"))
151                 , issue_date (node->string_child ("IssueDate"))
152                 , signer (node->node_child ("Signer"))
153         {
154                 boost::shared_ptr<const cxml::Node> c = node->node_child ("RequiredExtensions");
155                 c = c->node_child ("KDMRequiredExtensions");
156                 recipient = Recipient (c->node_child ("Recipient"));
157                 composition_playlist_id = c->string_child ("CompositionPlaylistId");
158                 content_title_text = c->string_child ("ContentTitleText");
159                 content_keys_not_valid_before = c->string_child ("ContentKeysNotValidBefore");
160                 content_keys_not_valid_after = c->string_child ("ContentKeysNotValidAfter");
161                 authorized_device_info = AuthorizedDeviceInfo (c->node_child ("AuthorizedDeviceInfo"));
162                 
163                 std::list<boost::shared_ptr<cxml::Node> > kil = c->node_child("KeyIdList")->node_children("TypedKeyId");
164                 for (std::list<boost::shared_ptr<cxml::Node> >::iterator i = kil.begin(); i != kil.end(); ++i) {
165                         key_id_list.push_back (TypedKeyId (*i));
166                 }
167                 
168                 boost::shared_ptr<cxml::Node> fmfl = c->optional_node_child("ForensicMarkFlagList");
169                 if (fmfl) {
170                         std::list<boost::shared_ptr<cxml::Node> > fmf = fmfl->node_children("ForensicMarkFlag");
171                         for (std::list<boost::shared_ptr<cxml::Node> >::iterator i = fmf.begin(); i != fmf.end(); ++i) {
172                                 forensic_mark_flag_list.push_back ((*i)->content ());
173                         }
174                 }
175                 
176                 node->ignore_child ("NonCriticalExtensions");
177                 node->done ();
178         }
179
180         void as_xml (Writer& writer, xmlpp::Element* node) const
181         {
182                 writer.references["ID_AuthenticatedPublic"] = node->set_attribute ("Id", "ID_AuthenticatedPublic");
183                 
184                 node->add_child("MessageId")->add_child_text (message_id);
185                 node->add_child("MessageType")->add_child_text (message_type);
186                 if (annotation_text) {
187                         node->add_child("AnnotationText")->add_child_text (annotation_text.get ());
188                 }
189                 node->add_child("IssueDate")->add_child_text (issue_date);
190                 signer.as_xml (node->add_child("Signer"));
191                 
192                 xmlpp::Element* kdm_required_extensions = node->add_child("RequiredExtensions")->add_child("KDMRequiredExtensions");
193                 kdm_required_extensions->set_attribute ("xmlns", "http://www.smpte-ra.org/schemas/430-1/2006/KDM");
194                 recipient.as_xml (kdm_required_extensions->add_child ("Recipient"));
195                 
196                 kdm_required_extensions->add_child("CompositionPlaylistId")->add_child_text (composition_playlist_id);
197                 kdm_required_extensions->add_child("ContentTitleText")->add_child_text (content_title_text);
198                 kdm_required_extensions->add_child("ContentKeysNotValidBefore")->add_child_text (content_keys_not_valid_before);
199                 kdm_required_extensions->add_child("ContentKeysNotValidAfter")->add_child_text (content_keys_not_valid_after);
200                 authorized_device_info.as_xml (kdm_required_extensions->add_child("AuthorizedDeviceInfo"));
201                 
202                 xmlpp::Element* kil = kdm_required_extensions->add_child("KeyIdList");
203                 for (std::list<TypedKeyId>::const_iterator i = key_id_list.begin(); i != key_id_list.end(); ++i) {
204                         i->as_xml (kil->add_child ("TypedKeyId"));
205                 }
206                 
207                 xmlpp::Element* fmfl = kdm_required_extensions->add_child ("ForensicMarkFlagList");
208                 for (std::list<std::string>::const_iterator i = forensic_mark_flag_list.begin(); i != forensic_mark_flag_list.end(); ++i) {
209                         fmfl->add_child("ForensicMarkFlag")->add_child_text (*i);
210                 }
211                 
212                 node->add_child ("NonCriticalExtensions");
213         }
214         
215         std::string message_id;
216         std::string message_type;
217         boost::optional<std::string> annotation_text;
218         std::string issue_date;
219         Signer signer;
220         Recipient recipient;
221         std::string composition_playlist_id;
222         std::string content_title_text;
223         std::string content_keys_not_valid_before;
224         std::string content_keys_not_valid_after;
225         AuthorizedDeviceInfo authorized_device_info;
226         std::list<TypedKeyId> key_id_list;
227         std::list<std::string> forensic_mark_flag_list;
228 };
229
230 class AuthenticatedPrivate
231 {
232 public:
233         AuthenticatedPrivate () {}
234         
235         AuthenticatedPrivate (boost::shared_ptr<const cxml::Node> node)
236         {
237                 std::list<boost::shared_ptr<cxml::Node> > ek = node->node_children ("EncryptedKey");
238                 for (std::list<boost::shared_ptr<cxml::Node> >::const_iterator i = ek.begin(); i != ek.end(); ++i) {
239                         encrypted_keys.push_back ((*i)->node_child("CipherData")->string_child("CipherValue"));
240                 }
241                 
242                 node->done ();
243         }
244         
245         void as_xml (Writer& writer, xmlpp::Element* node) const
246         {
247                 writer.references["ID_AuthenticatedPrivate"] = node->set_attribute ("Id", "ID_AuthenticatedPrivate");
248                 
249                 for (std::list<std::string>::const_iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) {
250                         xmlpp::Element* encrypted_key = node->add_child ("EncryptedKey", "enc");
251                         xmlpp::Element* encryption_method = encrypted_key->add_child ("EncryptionMethod", "enc");
252                         encryption_method->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p");
253                         xmlpp::Element* digest_method = encryption_method->add_child ("DigestMethod", "ds");
254                         digest_method->set_attribute ("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
255                         xmlpp::Element* cipher_data = encrypted_key->add_child ("CipherData", "enc");
256                         cipher_data->add_child("CipherValue", "enc")->add_child_text (*i);
257                 }
258         }       
259         
260         std::list<std::string> encrypted_keys;
261 };
262
263 class X509Data
264 {
265 public:
266         X509Data () {}
267         X509Data (boost::shared_ptr<const cxml::Node> node)
268                 : x509_issuer_serial (Signer (node->node_child ("X509IssuerSerial")))
269                 , x509_certificate (node->string_child ("X509Certificate"))
270         {
271                 node->done ();
272         }
273
274         void as_xml (xmlpp::Element* node) const
275         {
276                 x509_issuer_serial.as_xml (node->add_child ("X509IssuerSerial", "ds"));
277                 node->add_child("X509Certificate", "ds")->add_child_text (x509_certificate);
278         }
279         
280         Signer x509_issuer_serial;
281         std::string x509_certificate;
282 };
283
284 class Reference
285 {
286 public:
287         Reference () {}
288         Reference (std::string u)
289                 : uri (u)
290         {}
291                   
292         Reference (boost::shared_ptr<const cxml::Node> node)
293                 : uri (node->string_attribute ("URI"))
294                 , digest_value (node->string_child ("DigestValue"))
295         {
296                 node->ignore_child ("DigestMethod");
297                 node->done ();
298         }
299
300         void as_xml (Writer& writer, xmlpp::Element* node) const
301         {
302                 xmlpp::Element* reference = node->add_child ("Reference", "ds");
303                 reference->set_attribute ("URI", uri);
304                 reference->add_child("DigestMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256");
305                 reference->add_child("DigestValue", "ds")->add_child_text (digest_value);
306                 
307                 if (!uri.empty ()) {
308                         xmlAddID (0, writer.document->cobj(), (const xmlChar *) uri.substr(1).c_str(), writer.references[uri.substr(1)]->cobj ());
309                 }
310         }
311         
312         std::string uri;
313         std::string digest_value;
314 };
315
316 class Signature
317 {
318 public:
319         Signature ()
320                 : authenticated_public ("#ID_AuthenticatedPublic")
321                 , authenticated_private ("#ID_AuthenticatedPrivate")
322         {}
323         
324         Signature (boost::shared_ptr<const cxml::Node> node)
325         {
326                 std::list<boost::shared_ptr<cxml::Node> > refs = node->node_child("SignedInfo")->node_children ("Reference");
327                 for (std::list<boost::shared_ptr<cxml::Node> >::const_iterator i = refs.begin(); i != refs.end(); ++i) {
328                         if ((*i)->string_attribute("URI") == "#ID_AuthenticatedPublic") {
329                                 authenticated_public = Reference (*i);
330                         } else if ((*i)->string_attribute("URI") == "#ID_AuthenticatedPrivate") {
331                                 authenticated_private = Reference (*i);
332                         } else {
333                                 throw XMLError ("unrecognised reference URI");
334                         }
335                 }
336                 
337                 std::list<boost::shared_ptr<cxml::Node> > data = node->node_child("KeyInfo")->node_children ("X509Data");
338                 for (std::list<boost::shared_ptr<cxml::Node> >::const_iterator i = data.begin(); i != data.end(); ++i) {
339                         key_info.push_back (X509Data (*i));
340                 }
341
342                 signature_value = node->string_child ("SignatureValue");
343                 
344                 node->done ();
345         }
346         
347         void as_xml (Writer& writer, xmlpp::Element* node) const
348         {
349                 xmlpp::Element* si = node->add_child ("SignedInfo", "ds");
350                 si->add_child ("CanonicalizationMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments");
351                 si->add_child ("SignatureMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
352                 
353                 authenticated_public.as_xml (writer, si);
354                 authenticated_private.as_xml (writer, si);
355                 
356                 node->add_child("SignatureValue", "ds")->add_child_text (signature_value);
357                 
358                 xmlpp::Element* ki = node->add_child ("KeyInfo", "ds");
359                 for (std::list<X509Data>::const_iterator i = key_info.begin(); i != key_info.end(); ++i) {
360                         i->as_xml (ki->add_child ("X509Data", "ds"));
361                 }
362         }
363
364         Reference authenticated_public;
365         Reference authenticated_private;
366         std::string signature_value;
367         std::list<X509Data> key_info;
368 };
369
370 class DCinemaSecurityMessage
371 {
372 public:
373         DCinemaSecurityMessage () {}
374         DCinemaSecurityMessage (boost::filesystem::path file)
375         {
376                 cxml::Document f ("DCinemaSecurityMessage");
377                 f.read_file (file.string ());
378                 
379                 authenticated_public = AuthenticatedPublic (f.node_child ("AuthenticatedPublic"));
380                 authenticated_private = AuthenticatedPrivate (f.node_child ("AuthenticatedPrivate"));
381                 signature = Signature (f.node_child ("Signature"));
382                 
383                 f.done ();
384         }
385         
386         boost::shared_ptr<xmlpp::Document> as_xml () const
387         {
388                 Writer writer;
389                 
390                 xmlpp::Element* root = writer.document->create_root_node ("DCinemaSecurityMessage", "http://www.smpte-ra.org/schemas/430-3/2006/ETM");
391                 root->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "ds");
392                 root->set_namespace_declaration ("http://www.w3.org/2001/04/xmlenc#", "enc");
393                 
394                 authenticated_public.as_xml (writer, root->add_child ("AuthenticatedPublic"));
395                 authenticated_private.as_xml (writer, root->add_child ("AuthenticatedPrivate"));
396                 signature.as_xml (writer, root->add_child ("Signature", "ds"));
397                 
398                 return writer.document;
399         }
400
401         AuthenticatedPublic authenticated_public;
402         AuthenticatedPrivate authenticated_private;
403         Signature signature;
404 };
405
406 }
407 }
408
409 #endif
410