2 Copyright (C) 2013-2016 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>
59 using boost::shared_ptr;
63 put (uint8_t ** d, string s)
65 memcpy (*d, s.c_str(), s.length());
70 put (uint8_t ** d, uint8_t const * s, int N)
77 DecryptedKDM::put_uuid (uint8_t ** d, string id)
79 /* 32 hex digits plus some hyphens */
80 DCP_ASSERT (id.length() == 36);
87 "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
88 *d + 0, *d + 1, *d + 2, *d + 3, *d + 4, *d + 5, *d + 6, *d + 7,
89 *d + 8, *d + 9, *d + 10, *d + 11, *d + 12, *d + 13, *d + 14, *d + 15
96 DecryptedKDM::get_uuid (unsigned char ** p)
100 buffer, sizeof(buffer), "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
101 (*p)[0], (*p)[1], (*p)[2], (*p)[3], (*p)[4], (*p)[5], (*p)[6], (*p)[7],
102 (*p)[8], (*p)[9], (*p)[10], (*p)[11], (*p)[12], (*p)[13], (*p)[14], (*p)[15]
110 get (uint8_t ** p, int N)
113 for (int i = 0; i < N; ++i) {
121 DecryptedKDM::DecryptedKDM (EncryptedKDM const & kdm, string private_key)
123 /* Read the private key */
125 BIO* bio = BIO_new_mem_buf (const_cast<char *> (private_key.c_str ()), -1);
127 throw MiscError ("could not create memory BIO");
130 RSA* rsa = PEM_read_bio_RSAPrivateKey (bio, 0, 0, 0);
132 throw FileError ("could not read RSA private key file", private_key, errno);
135 /* Use the private key to decrypt the keys */
137 BOOST_FOREACH (string const & i, kdm.keys ()) {
138 /* Decode the base-64-encoded cipher value from the KDM */
139 unsigned char cipher_value[256];
140 int const cipher_value_len = base64_decode (i, cipher_value, sizeof (cipher_value));
143 unsigned char * decrypted = new unsigned char[RSA_size(rsa)];
144 int const decrypted_len = RSA_private_decrypt (cipher_value_len, cipher_value, decrypted, rsa, RSA_PKCS1_OAEP_PADDING);
145 if (decrypted_len == -1) {
147 throw MiscError (String::compose ("Could not decrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
150 unsigned char* p = decrypted;
151 switch (decrypted_len) {
155 /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
157 /* 16 is is signer thumbprint [20 bytes] */
159 /* 36 is CPL id [16 bytes] */
160 string const cpl_id = get_uuid (&p);
161 /* 52 is key id [16 bytes] */
162 string const key_id = get_uuid (&p);
163 /* 68 is not-valid-before (a string) [25 bytes] */
165 /* 93 is not-valid-after (a string) [25 bytes] */
167 /* 118 is the key [ASDCP::KeyLen bytes] */
168 _keys.push_back (DecryptedKDMKey ("", key_id, Key (p), cpl_id));
174 /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
176 /* 16 is is signer thumbprint [20 bytes] */
178 /* 36 is CPL id [16 bytes] */
179 string const cpl_id = get_uuid (&p);
180 /* 52 is key type [4 bytes] */
181 string const key_type = get (&p, 4);
182 /* 56 is key id [16 bytes] */
183 string const key_id = get_uuid (&p);
184 /* 72 is not-valid-before (a string) [25 bytes] */
186 /* 97 is not-valid-after (a string) [25 bytes] */
188 /* 112 is the key [ASDCP::KeyLen bytes] */
189 _keys.push_back (DecryptedKDMKey (key_type, key_id, Key (p), cpl_id));
202 _annotation_text = kdm.annotation_text ();
203 _content_title_text = kdm.content_title_text ();
204 _issue_date = kdm.issue_date ();
207 DecryptedKDM::DecryptedKDM (
208 LocalTime not_valid_before,
209 LocalTime not_valid_after,
210 string annotation_text,
211 string content_title_text,
214 : _not_valid_before (not_valid_before)
215 , _not_valid_after (not_valid_after)
216 , _annotation_text (annotation_text)
217 , _content_title_text (content_title_text)
218 , _issue_date (issue_date)
223 DecryptedKDM::DecryptedKDM (
224 boost::shared_ptr<const CPL> cpl,
226 LocalTime not_valid_before,
227 LocalTime not_valid_after,
228 string annotation_text,
229 string content_title_text,
232 : _not_valid_before (not_valid_before)
233 , _not_valid_after (not_valid_after)
234 , _annotation_text (annotation_text)
235 , _content_title_text (content_title_text)
236 , _issue_date (issue_date)
238 /* Create DecryptedKDMKey objects for each encryptable asset */
239 BOOST_FOREACH(shared_ptr<const ReelAsset> i, cpl->reel_assets ()) {
240 shared_ptr<const ReelMXF> mxf = boost::dynamic_pointer_cast<const ReelMXF> (i);
241 shared_ptr<const ReelAsset> asset = boost::dynamic_pointer_cast<const ReelAsset> (i);
243 if (!mxf->key_id ()) {
244 throw NotEncryptedError (asset->id ());
246 _keys.push_back (DecryptedKDMKey (mxf->key_type(), mxf->key_id().get(), key, cpl->id ()));
251 /** @param type (MDIK, MDAK etc.)
252 * @param key_id Key ID.
253 * @param key The actual symmetric key.
254 * @param cpl_id ID of CPL that the key is for.
257 DecryptedKDM::add_key (string type, string key_id, Key key, string cpl_id)
259 _keys.push_back (DecryptedKDMKey (type, key_id, key, cpl_id));
263 DecryptedKDM::add_key (DecryptedKDMKey key)
265 _keys.push_back (key);
269 DecryptedKDM::encrypt (shared_ptr<const CertificateChain> signer, Certificate recipient, vector<Certificate> trusted_devices, Formulation formulation) const
271 list<pair<string, string> > key_ids;
273 BOOST_FOREACH (DecryptedKDMKey const & i, _keys) {
274 key_ids.push_back (make_pair (i.type(), i.id ()));
276 /* XXX: SMPTE only */
280 /* Magic value specified by SMPTE S430-1-2006 */
281 uint8_t structure_id[] = { 0xf1, 0xdc, 0x12, 0x44, 0x60, 0x16, 0x9a, 0x0e, 0x85, 0xbc, 0x30, 0x06, 0x42, 0xf8, 0x66, 0xab };
282 put (&p, structure_id, 16);
284 base64_decode (signer->leaf().thumbprint (), p, 20);
287 put_uuid (&p, i.cpl_id ());
289 put_uuid (&p, i.id ());
290 put (&p, _not_valid_before.as_string ());
291 put (&p, _not_valid_after.as_string ());
292 put (&p, i.key().value(), ASDCP::KeyLen);
294 /* Encrypt using the projector's public key */
295 RSA* rsa = recipient.public_key ();
296 unsigned char encrypted[RSA_size(rsa)];
297 int const encrypted_len = RSA_public_encrypt (p - block, block, encrypted, rsa, RSA_PKCS1_OAEP_PADDING);
298 if (encrypted_len == -1) {
299 throw MiscError (String::compose ("Could not encrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
302 /* Lazy overallocation */
303 char out[encrypted_len * 2];
304 Kumu::base64encode (encrypted, encrypted_len, out, encrypted_len * 2);
305 int const N = strlen (out);
307 for (int i = 0; i < N; ++i) {
308 if (i > 0 && (i % 64) == 0) {
314 keys.push_back (lines);
317 string device_list_description = recipient.subject_common_name ();
318 if (device_list_description.find (".") != string::npos) {
319 device_list_description = device_list_description.substr (device_list_description.find (".") + 1);
322 return EncryptedKDM (
326 device_list_description,
327 _keys.front().cpl_id (),