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