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);
83 "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
84 *d + 0, *d + 1, *d + 2, *d + 3, *d + 4, *d + 5, *d + 6, *d + 7,
85 *d + 8, *d + 9, *d + 10, *d + 11, *d + 12, *d + 13, *d + 14, *d + 15
92 DecryptedKDM::get_uuid (unsigned char ** p)
96 buffer, sizeof(buffer), "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
97 (*p)[0], (*p)[1], (*p)[2], (*p)[3], (*p)[4], (*p)[5], (*p)[6], (*p)[7],
98 (*p)[8], (*p)[9], (*p)[10], (*p)[11], (*p)[12], (*p)[13], (*p)[14], (*p)[15]
106 get (uint8_t ** p, int N)
109 for (int i = 0; i < N; ++i) {
117 DecryptedKDM::DecryptedKDM (EncryptedKDM const & kdm, string private_key)
119 /* Read the private key */
121 BIO* bio = BIO_new_mem_buf (const_cast<char *> (private_key.c_str ()), -1);
123 throw MiscError ("could not create memory BIO");
126 RSA* rsa = PEM_read_bio_RSAPrivateKey (bio, 0, 0, 0);
128 throw FileError ("could not read RSA private key file", private_key, errno);
131 /* Use the private key to decrypt the keys */
133 BOOST_FOREACH (string const & i, kdm.keys ()) {
134 /* Decode the base-64-encoded cipher value from the KDM */
135 unsigned char cipher_value[256];
136 int const cipher_value_len = base64_decode (i, cipher_value, sizeof (cipher_value));
139 unsigned char * decrypted = new unsigned char[RSA_size(rsa)];
140 int const decrypted_len = RSA_private_decrypt (cipher_value_len, cipher_value, decrypted, rsa, RSA_PKCS1_OAEP_PADDING);
141 if (decrypted_len == -1) {
143 throw MiscError (String::compose ("Could not decrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
146 unsigned char* p = decrypted;
147 switch (decrypted_len) {
151 /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
153 /* 16 is is signer thumbprint [20 bytes] */
155 /* 36 is CPL id [16 bytes] */
156 string const cpl_id = get_uuid (&p);
157 /* 52 is key id [16 bytes] */
158 string const key_id = get_uuid (&p);
159 /* 68 is not-valid-before (a string) [25 bytes] */
161 /* 93 is not-valid-after (a string) [25 bytes] */
163 /* 118 is the key [ASDCP::KeyLen bytes] */
164 _keys.push_back (DecryptedKDMKey ("", key_id, Key (p), cpl_id));
170 /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
172 /* 16 is is signer thumbprint [20 bytes] */
174 /* 36 is CPL id [16 bytes] */
175 string const cpl_id = get_uuid (&p);
176 /* 52 is key type [4 bytes] */
177 string const key_type = get (&p, 4);
178 /* 56 is key id [16 bytes] */
179 string const key_id = get_uuid (&p);
180 /* 72 is not-valid-before (a string) [25 bytes] */
182 /* 97 is not-valid-after (a string) [25 bytes] */
184 /* 112 is the key [ASDCP::KeyLen bytes] */
185 _keys.push_back (DecryptedKDMKey (key_type, key_id, Key (p), cpl_id));
198 _annotation_text = kdm.annotation_text ();
199 _content_title_text = kdm.content_title_text ();
200 _issue_date = kdm.issue_date ();
203 DecryptedKDM::DecryptedKDM (
204 LocalTime not_valid_before,
205 LocalTime not_valid_after,
206 string annotation_text,
207 string content_title_text,
210 : _not_valid_before (not_valid_before)
211 , _not_valid_after (not_valid_after)
212 , _annotation_text (annotation_text)
213 , _content_title_text (content_title_text)
214 , _issue_date (issue_date)
219 DecryptedKDM::DecryptedKDM (
220 boost::shared_ptr<const CPL> cpl,
222 LocalTime not_valid_before,
223 LocalTime not_valid_after,
224 string annotation_text,
225 string content_title_text,
228 : _not_valid_before (not_valid_before)
229 , _not_valid_after (not_valid_after)
230 , _annotation_text (annotation_text)
231 , _content_title_text (content_title_text)
232 , _issue_date (issue_date)
234 /* Create DecryptedKDMKey objects for each encryptable asset */
235 BOOST_FOREACH(shared_ptr<const ReelAsset> i, cpl->reel_assets ()) {
236 shared_ptr<const ReelMXF> mxf = boost::dynamic_pointer_cast<const ReelMXF> (i);
237 shared_ptr<const ReelAsset> asset = boost::dynamic_pointer_cast<const ReelAsset> (i);
239 if (!mxf->key_id ()) {
240 throw NotEncryptedError (asset->id ());
242 _keys.push_back (DecryptedKDMKey (mxf->key_type(), mxf->key_id().get(), key, cpl->id ()));
247 /** @param type (MDIK, MDAK etc.)
248 * @param key_id Key ID.
249 * @param key The actual symmetric key.
250 * @param cpl_id ID of CPL that the key is for.
253 DecryptedKDM::add_key (string type, string key_id, Key key, string cpl_id)
255 _keys.push_back (DecryptedKDMKey (type, key_id, key, cpl_id));
259 DecryptedKDM::add_key (DecryptedKDMKey key)
261 _keys.push_back (key);
265 DecryptedKDM::encrypt (shared_ptr<const CertificateChain> signer, Certificate recipient, vector<Certificate> trusted_devices, Formulation formulation) const
267 list<pair<string, string> > key_ids;
269 BOOST_FOREACH (DecryptedKDMKey const & i, _keys) {
270 key_ids.push_back (make_pair (i.type(), i.id ()));
272 /* XXX: SMPTE only */
276 /* Magic value specified by SMPTE S430-1-2006 */
277 uint8_t structure_id[] = { 0xf1, 0xdc, 0x12, 0x44, 0x60, 0x16, 0x9a, 0x0e, 0x85, 0xbc, 0x30, 0x06, 0x42, 0xf8, 0x66, 0xab };
278 put (&p, structure_id, 16);
280 base64_decode (signer->leaf().thumbprint (), p, 20);
283 put_uuid (&p, i.cpl_id ());
285 put_uuid (&p, i.id ());
286 put (&p, _not_valid_before.as_string ());
287 put (&p, _not_valid_after.as_string ());
288 put (&p, i.key().value(), ASDCP::KeyLen);
290 /* Encrypt using the projector's public key */
291 RSA* rsa = recipient.public_key ();
292 unsigned char encrypted[RSA_size(rsa)];
293 int const encrypted_len = RSA_public_encrypt (p - block, block, encrypted, rsa, RSA_PKCS1_OAEP_PADDING);
294 if (encrypted_len == -1) {
295 throw MiscError (String::compose ("Could not encrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
298 /* Lazy overallocation */
299 char out[encrypted_len * 2];
300 Kumu::base64encode (encrypted, encrypted_len, out, encrypted_len * 2);
301 int const N = strlen (out);
303 for (int i = 0; i < N; ++i) {
304 if (i > 0 && (i % 64) == 0) {
310 keys.push_back (lines);
313 string device_list_description = recipient.subject_common_name ();
314 if (device_list_description.find (".") != string::npos) {
315 device_list_description = device_list_description.substr (device_list_description.find (".") + 1);
318 return EncryptedKDM (
322 device_list_description,
323 _keys.front().cpl_id (),