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.
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>
31 #include "compose.hpp"
32 #include "exceptions.h"
35 #include "mxf_asset.h"
36 #include "xml/kdm_smpte.h"
37 #include "parse/cpl.h"
41 using std::stringstream;
46 using boost::shared_ptr;
47 using namespace libdcp;
49 KDM::KDM (boost::filesystem::path kdm, boost::filesystem::path private_key)
50 : _xml_kdm (new xml::DCinemaSecurityMessage (kdm))
52 /* Read the private key */
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);
59 RSA* rsa = PEM_read_RSAPrivateKey (private_key_file, 0, 0, 0);
60 fclose (private_key_file);
62 throw FileError ("could not read RSA private key file", private_key, errno);
65 /* Use it to decrypt the keys */
67 list<string> encrypted_keys = _xml_kdm->authenticated_private.encrypted_keys;
69 for (list<string>::iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) {
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));
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) {
80 throw MiscError (String::compose ("Could not decrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
83 _keys.push_back (KDMKey (decrypted, decrypted_len));
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.
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
100 : _xml_kdm (new xml::DCinemaSecurityMessage)
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.
108 parse::CPL cpl (cpl_file);
110 xml::AuthenticatedPublic& apu = _xml_kdm->authenticated_public;
112 /* AuthenticatedPublic */
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 ();
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);
135 apu.authorized_device_info.device_list_description = n;
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 ());
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));
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));
153 if ((*i)->asset_list->main_sound) {
154 apu.key_id_list.push_back (xml::TypedKeyId ("MDAK", (*i)->asset_list->main_sound->key_id));
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");
161 /* AuthenticatedPrivate */
163 for (list<shared_ptr<parse::Reel> >::iterator i = cpl.reels.begin(); i != cpl.reels.end(); ++i) {
164 /* XXX: subtitle assets? */
166 if ((*i)->asset_list->main_picture) {
167 if ((*i)->asset_list->main_picture->key_id.empty ()) {
168 throw NotEncryptedError ("MainPicture");
172 signer, cpl.id.substr (9), "MDIK", (*i)->asset_list->main_picture->key_id.substr (9),
173 not_valid_before, not_valid_after, key
176 _keys.push_back (kkey);
177 _xml_kdm->authenticated_private.encrypted_keys.push_back (kkey.encrypted_base64 (recipient_cert));
180 if ((*i)->asset_list->main_stereoscopic_picture) {
181 if ((*i)->asset_list->main_stereoscopic_picture->key_id.empty ()) {
182 throw NotEncryptedError ("MainStereoscopicPicture");
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
190 _keys.push_back (kkey);
191 _xml_kdm->authenticated_private.encrypted_keys.push_back (kkey.encrypted_base64 (recipient_cert));
194 if ((*i)->asset_list->main_sound) {
195 if ((*i)->asset_list->main_sound->key_id.empty ()) {
196 throw NotEncryptedError ("MainSound");
200 signer, cpl.id.substr (9), "MDAK", (*i)->asset_list->main_sound->key_id.substr (9),
201 not_valid_before, not_valid_after, key
204 _keys.push_back (kkey);
205 _xml_kdm->authenticated_private.encrypted_keys.push_back (kkey.encrypted_base64 (recipient_cert));
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)));
218 KDM::KDM (KDM const & other)
219 : _keys (other._keys)
220 , _xml_kdm (new xml::DCinemaSecurityMessage (*other._xml_kdm.get()))
226 KDM::operator= (KDM const & other)
228 if (this == &other) {
233 _xml_kdm.reset (new xml::DCinemaSecurityMessage (*other._xml_kdm.get ()));
239 KDM::as_xml (boost::filesystem::path path) const
241 shared_ptr<xmlpp::Document> doc = _xml_kdm->as_xml ();
242 /* This must *not* be the _formatted version, otherwise the signature
245 doc->write_to_file (path.string(), "UTF-8");
251 shared_ptr<xmlpp::Document> doc = _xml_kdm->as_xml ();
252 /* This must *not* be the _formatted version, otherwise the signature
255 return doc->write_to_string ("UTF-8");
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
262 , _key_type (key_type)
264 , _not_valid_before (ptime_to_string (from))
265 , _not_valid_after (ptime_to_string (until))
268 base64_decode (signer->certificates().leaf()->thumbprint (), _signer_thumbprint, 20);
271 KDMKey::KDMKey (uint8_t const * raw, int len)
276 /* [0-15] is structure id (fixed sequence specified by standard) */
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);
287 /* [0-15] is structure id (fixed sequence specified by standard) */
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);
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)
310 memcpy (_signer_thumbprint, other._signer_thumbprint, 20);
314 KDMKey::operator= (KDMKey const & other)
316 if (&other == this) {
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;
326 memcpy (_signer_thumbprint, other._signer_thumbprint, 20);
332 KDMKey::encrypted_base64 (shared_ptr<const Certificate> recipient_cert) const
334 assert (_key_type.length() == 4);
335 assert (_not_valid_before.length() == 25);
336 assert (_not_valid_after.length() == 25);
338 /* XXX: SMPTE only */
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);
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);
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)));
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);
366 for (int i = 0; i < N; ++i) {
367 if (i > 0 && (i % 64) == 0) {
377 KDMKey::get (uint8_t const ** p, int N) const
380 for (int i = 0; i < N; ++i) {
389 KDMKey::get (uint8_t* o, uint8_t const ** p, int N) const
396 KDMKey::get_uuid (unsigned char const ** p) const
400 for (int i = 0; i < 16; ++i) {
401 g << setw(2) << setfill('0') << hex << static_cast<int> (**p);
403 if (i == 3 || i == 5 || i == 7 || i == 9) {
412 KDMKey::put (uint8_t ** d, uint8_t const * s, int N) const
419 KDMKey::put (uint8_t ** d, string s) const
421 memcpy (*d, s.c_str(), s.length());
426 KDMKey::put_uuid (uint8_t ** d, string id) const
428 id.erase (std::remove (id.begin(), id.end(), '-'));
429 for (int i = 0; i < 32; i += 2) {
431 s << id[i] << id[i + 1];
440 libdcp::operator== (libdcp::KDMKey const & a, libdcp::KDMKey const & b)
442 if (memcmp (a._signer_thumbprint, b._signer_thumbprint, 20) != 0) {
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 &&