No-op: whitespace.
[libdcp.git] / src / kdm.cc
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 #include <iomanip>
21 #include <algorithm>
22 #include <boost/algorithm/string.hpp>
23 #include <openssl/rsa.h>
24 #include <openssl/pem.h>
25 #include <openssl/err.h>
26 #include <libcxml/cxml.h>
27 #include "AS_DCP.h"
28 #include "KM_util.h"
29 #include "util.h"
30 #include "kdm.h"
31 #include "compose.hpp"
32 #include "exceptions.h"
33 #include "signer.h"
34 #include "cpl.h"
35 #include "mxf_asset.h"
36 #include "xml/kdm_smpte.h"
37 #include "parse/cpl.h"
38
39 using std::list;
40 using std::string;
41 using std::stringstream;
42 using std::hex;
43 using std::setw;
44 using std::setfill;
45 using std::cout;
46 using boost::shared_ptr;
47 using namespace libdcp;
48
49 KDM::KDM (boost::filesystem::path kdm, boost::filesystem::path private_key)
50         : _xml_kdm (new xml::DCinemaSecurityMessage (kdm))
51 {
52         /* Read the private key */
53
54         FILE* private_key_file = fopen_boost (private_key, "r");
55         if (!private_key_file) {
56                 throw FileError ("could not find RSA private key file", private_key, errno);
57         }
58
59         RSA* rsa = PEM_read_RSAPrivateKey (private_key_file, 0, 0, 0);
60         fclose (private_key_file);
61         if (!rsa) {
62                 throw FileError ("could not read RSA private key file", private_key, errno);
63         }
64
65         /* Use it to decrypt the keys */
66
67         list<string> encrypted_keys = _xml_kdm->authenticated_private.encrypted_keys;
68
69         for (list<string>::iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) {
70
71                 /* Decode the base-64-encoded cipher value from the KDM */
72                 unsigned char cipher_value[256];
73                 int const cipher_value_len = base64_decode (*i, cipher_value, sizeof (cipher_value));
74
75                 /* Decrypt it */
76                 unsigned char* decrypted = new unsigned char[RSA_size(rsa)];
77                 int const decrypted_len = RSA_private_decrypt (cipher_value_len, cipher_value, decrypted, rsa, RSA_PKCS1_OAEP_PADDING);
78                 if (decrypted_len == -1) {
79                         delete[] decrypted;
80                         throw MiscError (String::compose ("Could not decrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
81                 }
82
83                 _keys.push_back (KDMKey (decrypted, decrypted_len));
84                 delete[] decrypted;
85         }
86
87         RSA_free (rsa);
88 }
89
90 /** @param not_valid_before KDM not-valid-before time in local time.
91  *  @param not_valid_after KDM not-valid-after time in local time.
92  */
93 KDM::KDM (
94         boost::filesystem::path cpl_file,
95         shared_ptr<const Signer> signer,
96         shared_ptr<const Certificate> recipient_cert, Key key,
97         boost::posix_time::ptime not_valid_before, boost::posix_time::ptime not_valid_after,
98         string annotation_text, string issue_date, KDM::Formulation formulation
99         )
100         : _xml_kdm (new xml::DCinemaSecurityMessage)
101 {
102         /* This is all a bit of a hack, and should hopefully be nicer in libdcp1.
103            We load in the CPL file using our parser here, and extract everything
104            we need.  This is much better than needing the whole DCP and going through
105            the dance of setting the MXF's keys and so on.
106         */
107
108         parse::CPL cpl (cpl_file);
109
110         xml::AuthenticatedPublic& apu = _xml_kdm->authenticated_public;
111
112         /* AuthenticatedPublic */
113
114         apu.message_id = "urn:uuid:" + make_uuid ();
115         apu.message_type = "http://www.smpte-ra.org/430-1/2006/KDM#kdm-key-type";
116         apu.annotation_text = annotation_text;
117         apu.issue_date = issue_date;
118         apu.signer.x509_issuer_name = signer->certificates().leaf()->issuer ();
119         apu.signer.x509_serial_number = signer->certificates().leaf()->serial ();
120         apu.recipient.x509_issuer_serial.x509_issuer_name = recipient_cert->issuer ();
121         apu.recipient.x509_issuer_serial.x509_serial_number = recipient_cert->serial ();
122         apu.recipient.x509_subject_name = recipient_cert->subject ();
123         apu.composition_playlist_id = cpl.id;
124         if (formulation == DCI_ANY || formulation == DCI_SPECIFIC) {
125                 apu.content_authenticator = signer->certificates().leaf()->thumbprint ();
126         }
127         apu.content_title_text = cpl.annotation_text;
128         apu.content_keys_not_valid_before = ptime_to_string (not_valid_before);
129         apu.content_keys_not_valid_after = ptime_to_string (not_valid_after);
130         apu.authorized_device_info.device_list_identifier = "urn:uuid:" + make_uuid ();
131         string n = recipient_cert->common_name ();
132         if (n.find (".") != string::npos) {
133                 n = n.substr (n.find (".") + 1);
134         }
135         apu.authorized_device_info.device_list_description = n;
136
137         if (formulation == MODIFIED_TRANSITIONAL_1 || formulation == DCI_ANY) {
138                 /* Use the "assume trust" thumbprint */
139                 apu.authorized_device_info.device_list.push_back ("2jmj7l5rSw0yVb/vlWAYkK/YBwk=");
140         } else if (formulation == DCI_SPECIFIC) {
141                 /* Use the recipient thumbprint */
142                 apu.authorized_device_info.device_list.push_back (recipient_cert->thumbprint ());
143         }
144
145         for (list<shared_ptr<parse::Reel> >::const_iterator i = cpl.reels.begin(); i != cpl.reels.end(); ++i) {
146                 /* XXX: subtitle assets? */
147                 if ((*i)->asset_list->main_picture) {
148                         apu.key_id_list.push_back (xml::TypedKeyId ("MDIK", (*i)->asset_list->main_picture->key_id));
149                 }
150                 if ((*i)->asset_list->main_stereoscopic_picture) {
151                         apu.key_id_list.push_back (xml::TypedKeyId ("MDIK", (*i)->asset_list->main_stereoscopic_picture->key_id));
152                 }
153                 if ((*i)->asset_list->main_sound) {
154                         apu.key_id_list.push_back (xml::TypedKeyId ("MDAK", (*i)->asset_list->main_sound->key_id));
155                 }
156         }
157
158         apu.forensic_mark_flag_list.push_back ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable");
159         apu.forensic_mark_flag_list.push_back ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable");
160
161         /* AuthenticatedPrivate */
162
163         for (list<shared_ptr<parse::Reel> >::iterator i = cpl.reels.begin(); i != cpl.reels.end(); ++i) {
164                 /* XXX: subtitle assets? */
165
166                 if ((*i)->asset_list->main_picture) {
167                         if ((*i)->asset_list->main_picture->key_id.empty ()) {
168                                 throw NotEncryptedError ("MainPicture");
169                         }
170
171                         KDMKey kkey (
172                                 signer, cpl.id.substr (9), "MDIK", (*i)->asset_list->main_picture->key_id.substr (9),
173                                 not_valid_before, not_valid_after, key
174                                 );
175
176                         _keys.push_back (kkey);
177                         _xml_kdm->authenticated_private.encrypted_keys.push_back (kkey.encrypted_base64 (recipient_cert));
178                 }
179
180                 if ((*i)->asset_list->main_stereoscopic_picture) {
181                         if ((*i)->asset_list->main_stereoscopic_picture->key_id.empty ()) {
182                                 throw NotEncryptedError ("MainStereoscopicPicture");
183                         }
184
185                         KDMKey kkey (
186                                 signer, cpl.id.substr (9), "MDIK", (*i)->asset_list->main_stereoscopic_picture->key_id.substr (9),
187                                 not_valid_before, not_valid_after, key
188                                 );
189
190                         _keys.push_back (kkey);
191                         _xml_kdm->authenticated_private.encrypted_keys.push_back (kkey.encrypted_base64 (recipient_cert));
192                 }
193
194                 if ((*i)->asset_list->main_sound) {
195                         if ((*i)->asset_list->main_sound->key_id.empty ()) {
196                                 throw NotEncryptedError ("MainSound");
197                         }
198
199                         KDMKey kkey (
200                                 signer, cpl.id.substr (9), "MDAK", (*i)->asset_list->main_sound->key_id.substr (9),
201                                 not_valid_before, not_valid_after, key
202                                 );
203
204                         _keys.push_back (kkey);
205                         _xml_kdm->authenticated_private.encrypted_keys.push_back (kkey.encrypted_base64 (recipient_cert));
206                 }
207         }
208
209         /* Signature */
210
211         shared_ptr<xmlpp::Document> doc = _xml_kdm->as_xml ();
212         shared_ptr<cxml::Node> root (new cxml::Node (doc->get_root_node ()));
213         xmlpp::Node* signature = root->node_child("Signature")->node();
214         signer->add_signature_value (signature, "ds");
215         _xml_kdm->signature = xml::Signature (shared_ptr<cxml::Node> (new cxml::Node (signature)));
216 }
217
218 KDM::KDM (KDM const & other)
219         : _keys (other._keys)
220         , _xml_kdm (new xml::DCinemaSecurityMessage (*other._xml_kdm.get()))
221 {
222
223 }
224
225 KDM &
226 KDM::operator= (KDM const & other)
227 {
228         if (this == &other) {
229                 return *this;
230         }
231
232         _keys = other._keys;
233         _xml_kdm.reset (new xml::DCinemaSecurityMessage (*other._xml_kdm.get ()));
234
235         return *this;
236 }
237
238 void
239 KDM::as_xml (boost::filesystem::path path) const
240 {
241         shared_ptr<xmlpp::Document> doc = _xml_kdm->as_xml ();
242         /* This must *not* be the _formatted version, otherwise the signature
243            will be wrong.
244         */
245         doc->write_to_file (path.string(), "UTF-8");
246 }
247
248 string
249 KDM::as_xml () const
250 {
251         shared_ptr<xmlpp::Document> doc = _xml_kdm->as_xml ();
252         /* This must *not* be the _formatted version, otherwise the signature
253            will be wrong.
254         */
255         return doc->write_to_string ("UTF-8");
256 }
257
258 KDMKey::KDMKey (
259         shared_ptr<const Signer> signer, string cpl_id, string key_type, string key_id, boost::posix_time::ptime from, boost::posix_time::ptime until, Key key
260         )
261         : _cpl_id (cpl_id)
262         , _key_type (key_type)
263         , _key_id (key_id)
264         , _not_valid_before (ptime_to_string (from))
265         , _not_valid_after (ptime_to_string (until))
266         , _key (key)
267 {
268         base64_decode (signer->certificates().leaf()->thumbprint (), _signer_thumbprint, 20);
269 }
270
271 KDMKey::KDMKey (uint8_t const * raw, int len)
272 {
273         switch (len) {
274         case 134:
275                 /* interop */
276                 /* [0-15] is structure id (fixed sequence specified by standard) */
277                 raw += 16;
278                 get (_signer_thumbprint, &raw, 20);
279                 _cpl_id = get_uuid (&raw);
280                 _key_id = get_uuid (&raw);
281                 _not_valid_before = get (&raw, 25);
282                 _not_valid_after = get (&raw, 25);
283                 _key = Key (raw);
284                 break;
285         case 138:
286                 /* SMPTE */
287                 /* [0-15] is structure id (fixed sequence specified by standard) */
288                 raw += 16;
289                 get (_signer_thumbprint, &raw, 20);
290                 _cpl_id = get_uuid (&raw);
291                 _key_type = get (&raw, 4);
292                 _key_id = get_uuid (&raw);
293                 _not_valid_before = get (&raw, 25);
294                 _not_valid_after = get (&raw, 25);
295                 _key = Key (raw);
296                 break;
297         default:
298                 assert (false);
299         }
300 }
301
302 KDMKey::KDMKey (KDMKey const & other)
303         : _cpl_id (other._cpl_id)
304         , _key_type (other._key_type)
305         , _key_id (other._key_id)
306         , _not_valid_before (other._not_valid_before)
307         , _not_valid_after (other._not_valid_after)
308         , _key (other._key)
309 {
310         memcpy (_signer_thumbprint, other._signer_thumbprint, 20);
311 }
312
313 KDMKey &
314 KDMKey::operator= (KDMKey const & other)
315 {
316         if (&other == this) {
317                 return *this;
318         }
319
320         _cpl_id = other._cpl_id;
321         _key_type = other._key_type;
322         _key_id = other._key_id;
323         _not_valid_before = other._not_valid_before;
324         _not_valid_after = other._not_valid_after;
325         _key = other._key;
326         memcpy (_signer_thumbprint, other._signer_thumbprint, 20);
327
328         return *this;
329 }
330
331 string
332 KDMKey::encrypted_base64 (shared_ptr<const Certificate> recipient_cert) const
333 {
334         assert (_key_type.length() == 4);
335         assert (_not_valid_before.length() == 25);
336         assert (_not_valid_after.length() == 25);
337
338         /* XXX: SMPTE only */
339         uint8_t block[138];
340         uint8_t* p = block;
341
342         /* Magic value specified by SMPTE S430-1-2006 */
343         uint8_t structure_id[] = { 0xf1, 0xdc, 0x12, 0x44, 0x60, 0x16, 0x9a, 0x0e, 0x85, 0xbc, 0x30, 0x06, 0x42, 0xf8, 0x66, 0xab };
344         put (&p, structure_id, 16);
345         put (&p, _signer_thumbprint, 20);
346         put_uuid (&p, _cpl_id);
347         put (&p, _key_type);
348         put_uuid (&p, _key_id);
349         put (&p, _not_valid_before);
350         put (&p, _not_valid_after);
351         put (&p, _key.value(), ASDCP::KeyLen);
352
353         /* Encrypt using the projector's public key */
354         RSA* rsa = recipient_cert->public_key ();
355         unsigned char encrypted[RSA_size(rsa)];
356         int const encrypted_len = RSA_public_encrypt (p - block, block, encrypted, rsa, RSA_PKCS1_OAEP_PADDING);
357         if (encrypted_len == -1) {
358                 throw MiscError (String::compose ("Could not encrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
359         }
360
361         /* Lazy overallocation */
362         char out[encrypted_len * 2];
363         Kumu::base64encode (encrypted, encrypted_len, out, encrypted_len * 2);
364         int const N = strlen (out);
365         stringstream lines;
366         for (int i = 0; i < N; ++i) {
367                 if (i > 0 && (i % 64) == 0) {
368                         lines << "\n";
369                 }
370                 lines << out[i];
371         }
372
373         return lines.str ();
374 }
375
376 string
377 KDMKey::get (uint8_t const ** p, int N) const
378 {
379         string g;
380         for (int i = 0; i < N; ++i) {
381                 g += **p;
382                 (*p)++;
383         }
384
385         return g;
386 }
387
388 void
389 KDMKey::get (uint8_t* o, uint8_t const ** p, int N) const
390 {
391         memcpy (o, *p, N);
392         *p += N;
393 }
394
395 string
396 KDMKey::get_uuid (unsigned char const ** p) const
397 {
398         stringstream g;
399
400         for (int i = 0; i < 16; ++i) {
401                 g << setw(2) << setfill('0') << hex << static_cast<int> (**p);
402                 (*p)++;
403                 if (i == 3 || i == 5 || i == 7 || i == 9) {
404                         g << '-';
405                 }
406         }
407
408         return g.str ();
409 }
410
411 void
412 KDMKey::put (uint8_t ** d, uint8_t const * s, int N) const
413 {
414         memcpy (*d, s, N);
415         (*d) += N;
416 }
417
418 void
419 KDMKey::put (uint8_t ** d, string s) const
420 {
421         memcpy (*d, s.c_str(), s.length());
422         (*d) += s.length();
423 }
424
425 void
426 KDMKey::put_uuid (uint8_t ** d, string id) const
427 {
428         id.erase (std::remove (id.begin(), id.end(), '-'));
429         for (int i = 0; i < 32; i += 2) {
430                 stringstream s;
431                 s << id[i] << id[i + 1];
432                 int h;
433                 s >> hex >> h;
434                 **d = h;
435                 (*d)++;
436         }
437 }
438
439 bool
440 libdcp::operator== (libdcp::KDMKey const & a, libdcp::KDMKey const & b)
441 {
442         if (memcmp (a._signer_thumbprint, b._signer_thumbprint, 20) != 0) {
443                 return false;
444         }
445
446         return (
447                 a._cpl_id == b._cpl_id &&
448                 a._key_type == b._key_type &&
449                 a._key_id == b._key_id &&
450                 a._not_valid_before == b._not_valid_before &&
451                 a._not_valid_after == b._not_valid_after &&
452                 a._key == b._key
453                 );
454 }