2 Copyright (C) 2013-2017 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
6 libdcp is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 libdcp is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with libdcp. If not, see <http://www.gnu.org/licenses/>.
19 In addition, as a special exception, the copyright holders give
20 permission to link the code of portions of this program with the
21 OpenSSL library under certain conditions as described in each
22 individual source file, and distribute linked combinations
25 You must obey the GNU General Public License in all respects
26 for all of the code used other than OpenSSL. If you modify
27 file(s) with this exception, you may extend this exception to your
28 version of the file(s), but you are not obligated to do so. If you
29 do not wish to do so, delete this exception statement from your
30 version. If you delete this exception statement from all source
31 files in the program, then also delete it here.
34 #include "decrypted_kdm.h"
35 #include "decrypted_kdm_key.h"
36 #include "encrypted_kdm.h"
38 #include "reel_asset.h"
40 #include "exceptions.h"
42 #include "certificate_chain.h"
43 #include "dcp_assert.h"
44 #include "compose.hpp"
45 #include <asdcp/AS_DCP.h>
46 #include <asdcp/KM_util.h>
47 #include <openssl/rsa.h>
48 #include <openssl/pem.h>
49 #include <openssl/err.h>
50 #include <boost/foreach.hpp>
60 using boost::shared_ptr;
61 using boost::optional;
65 put (uint8_t ** d, string s)
67 memcpy (*d, s.c_str(), s.length());
72 put (uint8_t ** d, uint8_t const * s, int N)
79 DecryptedKDM::put_uuid (uint8_t ** d, string id)
81 /* 32 hex digits plus some hyphens */
82 DCP_ASSERT (id.length() == 36);
89 "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
90 *d + 0, *d + 1, *d + 2, *d + 3, *d + 4, *d + 5, *d + 6, *d + 7,
91 *d + 8, *d + 9, *d + 10, *d + 11, *d + 12, *d + 13, *d + 14, *d + 15
98 DecryptedKDM::get_uuid (unsigned char ** p)
102 buffer, sizeof(buffer), "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
103 (*p)[0], (*p)[1], (*p)[2], (*p)[3], (*p)[4], (*p)[5], (*p)[6], (*p)[7],
104 (*p)[8], (*p)[9], (*p)[10], (*p)[11], (*p)[12], (*p)[13], (*p)[14], (*p)[15]
112 get (uint8_t ** p, int N)
115 for (int i = 0; i < N; ++i) {
123 DecryptedKDM::DecryptedKDM (EncryptedKDM const & kdm, string private_key)
125 /* Read the private key */
127 BIO* bio = BIO_new_mem_buf (const_cast<char *> (private_key.c_str ()), -1);
129 throw MiscError ("could not create memory BIO");
132 RSA* rsa = PEM_read_bio_RSAPrivateKey (bio, 0, 0, 0);
134 throw FileError ("could not read RSA private key file", private_key, errno);
137 /* Use the private key to decrypt the keys */
139 BOOST_FOREACH (string const & i, kdm.keys ()) {
140 /* Decode the base-64-encoded cipher value from the KDM */
141 unsigned char cipher_value[256];
142 int const cipher_value_len = base64_decode (i, cipher_value, sizeof (cipher_value));
145 unsigned char * decrypted = new unsigned char[RSA_size(rsa)];
146 int const decrypted_len = RSA_private_decrypt (cipher_value_len, cipher_value, decrypted, rsa, RSA_PKCS1_OAEP_PADDING);
147 if (decrypted_len == -1) {
149 throw KDMDecryptionError (ERR_error_string (ERR_get_error(), 0));
152 unsigned char* p = decrypted;
153 switch (decrypted_len) {
157 /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
159 /* 16 is is signer thumbprint [20 bytes] */
161 /* 36 is CPL id [16 bytes] */
162 string const cpl_id = get_uuid (&p);
163 /* 52 is key id [16 bytes] */
164 string const key_id = get_uuid (&p);
165 /* 68 is not-valid-before (a string) [25 bytes] */
167 /* 93 is not-valid-after (a string) [25 bytes] */
169 /* 118 is the key [ASDCP::KeyLen bytes] */
170 add_key (optional<string>(), key_id, Key (p), cpl_id);
176 /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
178 /* 16 is is signer thumbprint [20 bytes] */
180 /* 36 is CPL id [16 bytes] */
181 string const cpl_id = get_uuid (&p);
182 /* 52 is key type [4 bytes] */
183 string const key_type = get (&p, 4);
184 /* 56 is key id [16 bytes] */
185 string const key_id = get_uuid (&p);
186 /* 72 is not-valid-before (a string) [25 bytes] */
188 /* 97 is not-valid-after (a string) [25 bytes] */
190 /* 112 is the key [ASDCP::KeyLen bytes] */
191 add_key (key_type, key_id, Key (p), cpl_id);
204 _annotation_text = kdm.annotation_text ();
205 _content_title_text = kdm.content_title_text ();
206 _issue_date = kdm.issue_date ();
209 DecryptedKDM::DecryptedKDM (
210 LocalTime not_valid_before,
211 LocalTime not_valid_after,
212 string annotation_text,
213 string content_title_text,
216 : _not_valid_before (not_valid_before)
217 , _not_valid_after (not_valid_after)
218 , _annotation_text (annotation_text)
219 , _content_title_text (content_title_text)
220 , _issue_date (issue_date)
225 DecryptedKDM::DecryptedKDM (
227 map<shared_ptr<const ReelMXF>, Key> keys,
228 LocalTime not_valid_before,
229 LocalTime not_valid_after,
230 string annotation_text,
231 string content_title_text,
234 : _not_valid_before (not_valid_before)
235 , _not_valid_after (not_valid_after)
236 , _annotation_text (annotation_text)
237 , _content_title_text (content_title_text)
238 , _issue_date (issue_date)
240 for (map<shared_ptr<const ReelMXF>, Key>::const_iterator i = keys.begin(); i != keys.end(); ++i) {
241 add_key (i->first->key_type(), i->first->key_id().get(), i->second, cpl_id);
245 DecryptedKDM::DecryptedKDM (
246 shared_ptr<const CPL> cpl,
248 LocalTime not_valid_before,
249 LocalTime not_valid_after,
250 string annotation_text,
251 string content_title_text,
254 : _not_valid_before (not_valid_before)
255 , _not_valid_after (not_valid_after)
256 , _annotation_text (annotation_text)
257 , _content_title_text (content_title_text)
258 , _issue_date (issue_date)
260 /* Create DecryptedKDMKey objects for each encryptable asset */
261 bool did_one = false;
262 BOOST_FOREACH(shared_ptr<const ReelAsset> i, cpl->reel_assets ()) {
263 shared_ptr<const ReelMXF> mxf = boost::dynamic_pointer_cast<const ReelMXF> (i);
264 if (mxf && mxf->key_id ()) {
265 add_key (mxf->key_type(), mxf->key_id().get(), key, cpl->id ());
271 throw NotEncryptedError (cpl->id ());
275 /** @param type (MDIK, MDAK etc.)
276 * @param key_id Key ID.
277 * @param key The actual symmetric key.
278 * @param cpl_id ID of CPL that the key is for.
281 DecryptedKDM::add_key (optional<string> type, string key_id, Key key, string cpl_id)
283 _keys.push_back (DecryptedKDMKey (type, key_id, key, cpl_id));
287 DecryptedKDM::add_key (DecryptedKDMKey key)
289 _keys.push_back (key);
293 DecryptedKDM::encrypt (shared_ptr<const CertificateChain> signer, Certificate recipient, vector<Certificate> trusted_devices, Formulation formulation) const
295 list<pair<string, string> > key_ids;
297 BOOST_FOREACH (DecryptedKDMKey const & i, _keys) {
298 /* We're making SMPTE keys so we must have a type for each one */
299 DCP_ASSERT (i.type());
300 key_ids.push_back (make_pair (i.type().get(), i.id ()));
302 /* XXX: SMPTE only */
306 /* Magic value specified by SMPTE S430-1-2006 */
307 uint8_t structure_id[] = { 0xf1, 0xdc, 0x12, 0x44, 0x60, 0x16, 0x9a, 0x0e, 0x85, 0xbc, 0x30, 0x06, 0x42, 0xf8, 0x66, 0xab };
308 put (&p, structure_id, 16);
310 base64_decode (signer->leaf().thumbprint (), p, 20);
313 put_uuid (&p, i.cpl_id ());
314 put (&p, i.type().get());
315 put_uuid (&p, i.id ());
316 put (&p, _not_valid_before.as_string ());
317 put (&p, _not_valid_after.as_string ());
318 put (&p, i.key().value(), ASDCP::KeyLen);
320 /* Encrypt using the projector's public key */
321 RSA* rsa = recipient.public_key ();
322 unsigned char encrypted[RSA_size(rsa)];
323 int const encrypted_len = RSA_public_encrypt (p - block, block, encrypted, rsa, RSA_PKCS1_OAEP_PADDING);
324 if (encrypted_len == -1) {
325 throw MiscError (String::compose ("Could not encrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
328 /* Lazy overallocation */
329 char out[encrypted_len * 2];
330 Kumu::base64encode (encrypted, encrypted_len, out, encrypted_len * 2);
331 int const N = strlen (out);
333 for (int i = 0; i < N; ++i) {
334 if (i > 0 && (i % 64) == 0) {
340 keys.push_back (lines);
343 string device_list_description = recipient.subject_common_name ();
344 if (device_list_description.find (".") != string::npos) {
345 device_list_description = device_list_description.substr (device_list_description.find (".") + 1);
348 return EncryptedKDM (
352 _keys.front().cpl_id (),