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 KDMDecryptionError (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 bool did_one = false;
240 BOOST_FOREACH(shared_ptr<const ReelAsset> i, cpl->reel_assets ()) {
241 shared_ptr<const ReelMXF> mxf = boost::dynamic_pointer_cast<const ReelMXF> (i);
242 shared_ptr<const ReelAsset> asset = boost::dynamic_pointer_cast<const ReelAsset> (i);
243 if (asset && mxf && mxf->key_id ()) {
244 _keys.push_back (DecryptedKDMKey (mxf->key_type(), mxf->key_id().get(), key, cpl->id ()));
250 throw NotEncryptedError (cpl->id ());
254 /** @param type (MDIK, MDAK etc.)
255 * @param key_id Key ID.
256 * @param key The actual symmetric key.
257 * @param cpl_id ID of CPL that the key is for.
260 DecryptedKDM::add_key (string type, string key_id, Key key, string cpl_id)
262 _keys.push_back (DecryptedKDMKey (type, key_id, key, cpl_id));
266 DecryptedKDM::add_key (DecryptedKDMKey key)
268 _keys.push_back (key);
272 DecryptedKDM::encrypt (shared_ptr<const CertificateChain> signer, Certificate recipient, vector<Certificate> trusted_devices, Formulation formulation) const
274 list<pair<string, string> > key_ids;
276 BOOST_FOREACH (DecryptedKDMKey const & i, _keys) {
277 key_ids.push_back (make_pair (i.type(), i.id ()));
279 /* XXX: SMPTE only */
283 /* Magic value specified by SMPTE S430-1-2006 */
284 uint8_t structure_id[] = { 0xf1, 0xdc, 0x12, 0x44, 0x60, 0x16, 0x9a, 0x0e, 0x85, 0xbc, 0x30, 0x06, 0x42, 0xf8, 0x66, 0xab };
285 put (&p, structure_id, 16);
287 base64_decode (signer->leaf().thumbprint (), p, 20);
290 put_uuid (&p, i.cpl_id ());
292 put_uuid (&p, i.id ());
293 put (&p, _not_valid_before.as_string ());
294 put (&p, _not_valid_after.as_string ());
295 put (&p, i.key().value(), ASDCP::KeyLen);
297 /* Encrypt using the projector's public key */
298 RSA* rsa = recipient.public_key ();
299 unsigned char encrypted[RSA_size(rsa)];
300 int const encrypted_len = RSA_public_encrypt (p - block, block, encrypted, rsa, RSA_PKCS1_OAEP_PADDING);
301 if (encrypted_len == -1) {
302 throw MiscError (String::compose ("Could not encrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
305 /* Lazy overallocation */
306 char out[encrypted_len * 2];
307 Kumu::base64encode (encrypted, encrypted_len, out, encrypted_len * 2);
308 int const N = strlen (out);
310 for (int i = 0; i < N; ++i) {
311 if (i > 0 && (i % 64) == 0) {
317 keys.push_back (lines);
320 string device_list_description = recipient.subject_common_name ();
321 if (device_list_description.find (".") != string::npos) {
322 device_list_description = device_list_description.substr (device_list_description.find (".") + 1);
325 return EncryptedKDM (
329 _keys.front().cpl_id (),