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"
40 using std::stringstream;
45 using boost::shared_ptr;
46 using namespace libdcp;
48 KDM::KDM (boost::filesystem::path kdm, boost::filesystem::path private_key)
49 : xml_kdm (new xml::DCinemaSecurityMessage (kdm))
51 /* Read the private key */
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);
58 RSA* rsa = PEM_read_RSAPrivateKey (private_key_file, 0, 0, 0);
59 fclose (private_key_file);
61 throw FileError ("could not read RSA private key file", private_key);
64 /* Use it to decrypt the keys */
66 list<string> encrypted_keys = xml_kdm->authenticated_private.encrypted_keys;
68 for (list<string>::iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) {
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));
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) {
79 throw MiscError (String::compose ("Could not decrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
82 _keys.push_back (KDMKey (decrypted, decrypted_len));
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 MXFMetadata mxf_metadata, XMLMetadata xml_metadata
94 : xml_kdm (new xml::DCinemaSecurityMessage)
96 xml::AuthenticatedPublic& apu = xml_kdm->authenticated_public;
98 /* AuthenticatedPublic */
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 = mxf_metadata.product_name;
103 apu.issue_date = xml_metadata.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_title_text = cpl->name ();
111 apu.content_keys_not_valid_before = ptime_to_string (not_valid_before);
112 apu.content_keys_not_valid_after = ptime_to_string (not_valid_after);
113 apu.authorized_device_info.device_list_identifier = "urn:uuid:" + make_uuid ();
114 apu.authorized_device_info.device_list_description = recipient_cert->subject ();
115 apu.authorized_device_info.device_list.push_back (recipient_cert->thumbprint ());
117 list<shared_ptr<const Asset> > assets = cpl->assets ();
118 for (list<shared_ptr<const Asset> >::iterator i = assets.begin(); i != assets.end(); ++i) {
119 /* XXX: non-MXF assets? */
120 shared_ptr<const MXFAsset> mxf = boost::dynamic_pointer_cast<const MXFAsset> (*i);
122 apu.key_id_list.push_back (xml::TypedKeyId (mxf->key_type(), "urn:uuid:" + mxf->key_id()));
126 apu.forensic_mark_flag_list.push_back ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable");
127 apu.forensic_mark_flag_list.push_back ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable");
129 /* AuthenticatedPrivate */
131 for (list<shared_ptr<const Asset> >::iterator i = assets.begin(); i != assets.end(); ++i) {
132 /* XXX: non-MXF assets? */
133 shared_ptr<const MXFAsset> mxf = boost::dynamic_pointer_cast<const MXFAsset> (*i);
135 xml_kdm->authenticated_private.encrypted_keys.push_back (
136 KDMKey (signer, cpl->id (), mxf->key_id (), not_valid_before, not_valid_after, mxf->key().get()).base64 ()
143 shared_ptr<xmlpp::Document> doc = xml_kdm->as_xml ();
144 shared_ptr<cxml::Node> root (new cxml::Node (doc->get_root_node ()));
145 xmlpp::Node* signature = root->node_child("Signature")->node();
146 signer->add_signature_value (signature, "ds");
147 xml_kdm->signature = xml::Signature (shared_ptr<cxml::Node> (new cxml::Node (signature)));
151 KDM::as_xml (boost::filesystem::path path) const
153 shared_ptr<xmlpp::Document> doc = xml_kdm->as_xml ();
154 doc->write_to_file_formatted (path.string(), "UTF-8");
157 KDMKey::KDMKey (shared_ptr<const Signer> signer, string cpl_id, string key_id, boost::posix_time::ptime from, boost::posix_time::ptime until, Key key)
160 , _not_valid_before (ptime_to_string (from))
161 , _not_valid_after (ptime_to_string (until))
164 base64_decode (signer->certificates().leaf()->thumbprint (), _signer_thumbprint, 20);
167 KDMKey::KDMKey (uint8_t const * raw, int len)
172 /* [0-15] is structure id (fixed sequence specified by standard) */
174 get (_signer_thumbprint, &raw, 20);
175 _cpl_id = get_uuid (&raw);
176 _key_id = get_uuid (&raw);
177 _not_valid_before = get (&raw, 25);
178 _not_valid_after = get (&raw, 25);
183 /* [0-15] is structure id (fixed sequence specified by standard) */
185 get (_signer_thumbprint, &raw, 20);
186 _cpl_id = get_uuid (&raw);
187 _key_type = get (&raw, 4);
188 _key_id = get_uuid (&raw);
189 _not_valid_before = get (&raw, 25);
190 _not_valid_after = get (&raw, 25);
198 KDMKey::KDMKey (KDMKey const & other)
199 : _cpl_id (other._cpl_id)
200 , _key_type (other._key_type)
201 , _key_id (other._key_id)
202 , _not_valid_before (other._not_valid_before)
203 , _not_valid_after (other._not_valid_after)
206 memcpy (_signer_thumbprint, other._signer_thumbprint, 20);
210 KDMKey::operator= (KDMKey const & other)
212 if (&other == this) {
216 _cpl_id = other._cpl_id;
217 _key_type = other._key_type;
218 _key_id = other._key_id;
219 _not_valid_before = other._not_valid_before;
220 _not_valid_after = other._not_valid_after;
222 memcpy (_signer_thumbprint, other._signer_thumbprint, 20);
228 KDMKey::base64 () const
230 /* XXX: SMPTE only */
234 /* Magic value specified by SMPTE S430-1-2006 */
235 uint8_t structure_id[] = { 0xf1, 0xdc, 0x12, 0x44, 0x60, 0x16, 0x9a, 0x0e, 0x85, 0xbc, 0x30, 0x06, 0x42, 0xf8, 0x66, 0xab };
236 put (&p, structure_id, 16);
237 put (&p, _signer_thumbprint, 20);
238 put_uuid (&p, _cpl_id);
240 put_uuid (&p, _key_id);
241 put (&p, _not_valid_before);
242 put (&p, _not_valid_after);
243 put (&p, _key.value(), ASDCP::KeyLen);
245 /* Lazy overallocation */
246 char string[138 * 2];
247 return Kumu::base64encode (block, 138, string, 138 * 2);
251 KDMKey::get (uint8_t const ** p, int N) const
254 for (int i = 0; i < N; ++i) {
263 KDMKey::get (uint8_t* o, uint8_t const ** p, int N) const
270 KDMKey::get_uuid (unsigned char const ** p) const
274 for (int i = 0; i < 16; ++i) {
275 g << setw(2) << setfill('0') << hex << static_cast<int> (**p);
277 if (i == 3 || i == 5 || i == 7 || i == 9) {
286 KDMKey::put (uint8_t ** d, uint8_t const * s, int N) const
293 KDMKey::put (uint8_t ** d, string s) const
295 memcpy (*d, s.c_str(), s.length());
300 KDMKey::put_uuid (uint8_t ** d, string id) const
302 id.erase (std::remove (id.begin(), id.end(), '-'));
303 for (int i = 0; i < 32; i += 2) {
305 s << id[i] << id[i + 1];