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