libdcp
decrypted_kdm.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2013-2021 Carl Hetherington <cth@carlh.net>
3 
4  This file is part of libdcp.
5 
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.
10 
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.
15 
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/>.
18 
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
23  including the two.
24 
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.
32 */
33 
34 
40 #include "certificate_chain.h"
41 #include "compose.hpp"
42 #include "cpl.h"
43 #include "dcp_assert.h"
44 #include "decrypted_kdm.h"
45 #include "decrypted_kdm_key.h"
46 #include "encrypted_kdm.h"
47 #include "exceptions.h"
48 #include "reel_asset.h"
49 #include "reel_file_asset.h"
50 #include "util.h"
51 #include <asdcp/AS_DCP.h>
52 #include <asdcp/KM_util.h>
53 #include <openssl/rsa.h>
54 #include <openssl/pem.h>
55 #include <openssl/err.h>
56 
57 
58 using std::list;
59 using std::vector;
60 using std::string;
61 using std::setw;
62 using std::setfill;
63 using std::hex;
64 using std::pair;
65 using std::map;
66 using std::shared_ptr;
67 using boost::optional;
68 using namespace dcp;
69 
70 
71 /* Magic value specified by SMPTE S430-1-2006 */
72 static uint8_t smpte_structure_id[] = { 0xf1, 0xdc, 0x12, 0x44, 0x60, 0x16, 0x9a, 0x0e, 0x85, 0xbc, 0x30, 0x06, 0x42, 0xf8, 0x66, 0xab };
73 
74 
75 static void
76 put (uint8_t ** d, string s)
77 {
78  memcpy (*d, s.c_str(), s.length());
79  (*d) += s.length();
80 }
81 
82 
83 static void
84 put (uint8_t ** d, uint8_t const * s, int N)
85 {
86  memcpy (*d, s, N);
87  (*d) += N;
88 }
89 
90 
91 void
92 DecryptedKDM::put_uuid (uint8_t ** d, string id)
93 {
94  /* 32 hex digits plus some hyphens */
95  DCP_ASSERT (id.length() == 36);
96 #ifdef LIBDCP_WINDOWS
97  __mingw_sscanf (
98 #else
99  sscanf (
100 #endif
101  id.c_str(),
102  "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
103  *d + 0, *d + 1, *d + 2, *d + 3, *d + 4, *d + 5, *d + 6, *d + 7,
104  *d + 8, *d + 9, *d + 10, *d + 11, *d + 12, *d + 13, *d + 14, *d + 15
105  );
106 
107  *d += 16;
108 }
109 
110 
111 string
112 DecryptedKDM::get_uuid (unsigned char ** p)
113 {
114  char buffer[37];
115 #ifdef LIBDCP_WINDOWS
116  __mingw_snprintf (
117 #else
118  snprintf (
119 #endif
120  buffer, sizeof(buffer), "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
121  (*p)[0], (*p)[1], (*p)[2], (*p)[3], (*p)[4], (*p)[5], (*p)[6], (*p)[7],
122  (*p)[8], (*p)[9], (*p)[10], (*p)[11], (*p)[12], (*p)[13], (*p)[14], (*p)[15]
123  );
124 
125  *p += 16;
126  return buffer;
127 }
128 
129 
130 static string
131 get (uint8_t ** p, int N)
132 {
133  string g;
134  for (int i = 0; i < N; ++i) {
135  g += **p;
136  (*p)++;
137  }
138 
139  return g;
140 }
141 
142 
143 DecryptedKDM::DecryptedKDM (EncryptedKDM const & kdm, string private_key)
144 {
145  /* Read the private key */
146 
147  auto bio = BIO_new_mem_buf (const_cast<char *>(private_key.c_str()), -1);
148  if (!bio) {
149  throw MiscError ("could not create memory BIO");
150  }
151 
152  auto rsa = PEM_read_bio_RSAPrivateKey (bio, 0, 0, 0);
153  if (!rsa) {
154  throw FileError ("could not read RSA private key file", private_key, errno);
155  }
156 
157  /* Use the private key to decrypt the keys */
158 
159  for (auto const& i: kdm.keys()) {
160  /* Decode the base-64-encoded cipher value from the KDM */
161  unsigned char cipher_value[256];
162  int const cipher_value_len = base64_decode (i, cipher_value, sizeof (cipher_value));
163 
164  /* Decrypt it */
165  auto decrypted = new unsigned char[RSA_size(rsa)];
166  int const decrypted_len = RSA_private_decrypt (cipher_value_len, cipher_value, decrypted, rsa, RSA_PKCS1_OAEP_PADDING);
167  if (decrypted_len == -1) {
168  delete[] decrypted;
169 #if OPENSSL_VERSION_NUMBER > 0x10100000L
170  throw KDMDecryptionError (ERR_error_string (ERR_get_error(), 0), cipher_value_len, RSA_bits(rsa));
171 #else
172  throw KDMDecryptionError (ERR_error_string (ERR_get_error(), 0), cipher_value_len, rsa->n->dmax);
173 #endif
174  }
175 
176  unsigned char* p = decrypted;
177  switch (decrypted_len) {
178  case 134:
179  {
180  /* Inter-op */
181  /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
182  p += 16;
183  /* 16 is is signer thumbprint [20 bytes] */
184  p += 20;
185  /* 36 is CPL id [16 bytes] */
186  string const cpl_id = get_uuid (&p);
187  /* 52 is key id [16 bytes] */
188  string const key_id = get_uuid (&p);
189  /* 68 is not-valid-before (a string) [25 bytes] */
190  p += 25;
191  /* 93 is not-valid-after (a string) [25 bytes] */
192  p += 25;
193  /* 118 is the key [ASDCP::KeyLen bytes] */
194  add_key (optional<string>(), key_id, Key(p), cpl_id, Standard::INTEROP);
195  break;
196  }
197  case 138:
198  {
199  /* SMPTE */
200  /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
201  DCP_ASSERT (memcmp (p, smpte_structure_id, 16) == 0);
202  p += 16;
203  /* 16 is is signer thumbprint [20 bytes] */
204  p += 20;
205  /* 36 is CPL id [16 bytes] */
206  string const cpl_id = get_uuid (&p);
207  /* 52 is key type [4 bytes] */
208  string const key_type = get (&p, 4);
209  /* 56 is key id [16 bytes] */
210  string const key_id = get_uuid (&p);
211  /* 72 is not-valid-before (a string) [25 bytes] */
212  p += 25;
213  /* 97 is not-valid-after (a string) [25 bytes] */
214  p += 25;
215  /* 112 is the key [ASDCP::KeyLen bytes] */
216  add_key (key_type, key_id, Key(p), cpl_id, Standard::SMPTE);
217  break;
218  }
219  default:
220  DCP_ASSERT (false);
221  }
222 
223  delete[] decrypted;
224  }
225 
226  RSA_free (rsa);
227  BIO_free (bio);
228 
229  _annotation_text = kdm.annotation_text ();
230  _content_title_text = kdm.content_title_text ();
231  _issue_date = kdm.issue_date ();
232 }
233 
234 
236  LocalTime not_valid_before,
237  LocalTime not_valid_after,
238  string annotation_text,
239  string content_title_text,
240  string issue_date
241  )
242  : _not_valid_before (not_valid_before)
243  , _not_valid_after (not_valid_after)
244  , _annotation_text (annotation_text)
245  , _content_title_text (content_title_text)
246  , _issue_date (issue_date)
247 {
248 
249 }
250 
251 
253  string cpl_id,
254  map<shared_ptr<const ReelFileAsset>, Key> keys,
255  LocalTime not_valid_before,
256  LocalTime not_valid_after,
257  string annotation_text,
258  string content_title_text,
259  string issue_date
260  )
261  : _not_valid_before (not_valid_before)
262  , _not_valid_after (not_valid_after)
263  , _annotation_text (annotation_text)
264  , _content_title_text (content_title_text)
265  , _issue_date (issue_date)
266 {
267  for (auto const& i: keys) {
268  add_key (i.first->key_type(), i.first->key_id().get(), i.second, cpl_id, Standard::SMPTE);
269  }
270 }
271 
272 
274  shared_ptr<const CPL> cpl,
275  Key key,
276  LocalTime not_valid_before,
277  LocalTime not_valid_after,
278  string annotation_text,
279  string content_title_text,
280  string issue_date
281  )
282  : _not_valid_before (not_valid_before)
283  , _not_valid_after (not_valid_after)
284  , _annotation_text (annotation_text)
285  , _content_title_text (content_title_text)
286  , _issue_date (issue_date)
287 {
288  /* Create DecryptedKDMKey objects for each encryptable asset */
289  bool did_one = false;
290  for (auto i: cpl->reel_file_assets()) {
291  if (i->encryptable()) {
292  add_key (i->key_type().get(), i->key_id().get(), key, cpl->id(), Standard::SMPTE);
293  did_one = true;
294  }
295  }
296 
297  if (!did_one) {
298  throw NotEncryptedError (cpl->id ());
299  }
300 }
301 
302 
303 void
304 DecryptedKDM::add_key (optional<string> type, string key_id, Key key, string cpl_id, Standard standard)
305 {
306  _keys.push_back (DecryptedKDMKey (type, key_id, key, cpl_id, standard));
307 }
308 
309 
310 void
312 {
313  _keys.push_back (key);
314 }
315 
316 
319  shared_ptr<const CertificateChain> signer,
320  Certificate recipient,
321  vector<string> trusted_devices,
322  Formulation formulation,
323  bool disable_forensic_marking_picture,
324  optional<int> disable_forensic_marking_audio
325  ) const
326 {
327  DCP_ASSERT (!_keys.empty ());
328 
329  for (auto i: signer->leaf_to_root()) {
330  if (day_greater_than_or_equal(i.not_before(), _not_valid_before)) {
331  throw BadKDMDateError (true);
332  } else if (day_less_than_or_equal(i.not_after(), _not_valid_after)) {
333  throw BadKDMDateError (false);
334  }
335  }
336 
337  vector<pair<string, string>> key_ids;
338  vector<string> keys;
339  for (auto const& i: _keys) {
340  /* We're making SMPTE keys so we must have a type for each one */
341  DCP_ASSERT (i.type());
342  key_ids.push_back (make_pair (i.type().get(), i.id ()));
343 
344  /* XXX: SMPTE only */
345  uint8_t block[138];
346  uint8_t* p = block;
347 
348  put (&p, smpte_structure_id, 16);
349 
350  base64_decode (signer->leaf().thumbprint (), p, 20);
351  p += 20;
352 
353  put_uuid (&p, i.cpl_id ());
354  put (&p, i.type().get());
355  put_uuid (&p, i.id ());
356  put (&p, _not_valid_before.as_string ());
357  put (&p, _not_valid_after.as_string ());
358  put (&p, i.key().value(), ASDCP::KeyLen);
359 
360  /* Encrypt using the projector's public key */
361  RSA* rsa = recipient.public_key ();
362  unsigned char encrypted[RSA_size(rsa)];
363  int const encrypted_len = RSA_public_encrypt (p - block, block, encrypted, rsa, RSA_PKCS1_OAEP_PADDING);
364  if (encrypted_len == -1) {
365  throw MiscError (String::compose ("Could not encrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
366  }
367 
368  /* Lazy overallocation */
369  char out[encrypted_len * 2];
370  Kumu::base64encode (encrypted, encrypted_len, out, encrypted_len * 2);
371  int const N = strlen (out);
372  string lines;
373  for (int i = 0; i < N; ++i) {
374  if (i > 0 && (i % 64) == 0) {
375  lines += "\n";
376  }
377  lines += out[i];
378  }
379 
380  keys.push_back (lines);
381  }
382 
383  string device_list_description = recipient.subject_common_name ();
384  if (device_list_description.find (".") != string::npos) {
385  device_list_description = device_list_description.substr (device_list_description.find (".") + 1);
386  }
387 
388  return EncryptedKDM (
389  signer,
390  recipient,
391  trusted_devices,
392  _keys.front().cpl_id (),
393  _content_title_text,
394  _annotation_text,
395  _not_valid_before,
396  _not_valid_after,
397  formulation,
398  disable_forensic_marking_picture,
399  disable_forensic_marking_audio,
400  key_ids,
401  keys
402  );
403 }
CertificateChain class.
A wrapper for an X509 certificate.
Definition: certificate.h:66
RSA * public_key() const
Definition: certificate.cc:422
An un- or de-crypted key from a KDM.
DecryptedKDM(EncryptedKDM const &kdm, std::string private_key)
void add_key(boost::optional< std::string > type, std::string key_id, Key key, std::string cpl_id, Standard standard)
std::vector< DecryptedKDMKey > keys() const
EncryptedKDM encrypt(std::shared_ptr< const CertificateChain > signer, Certificate recipient, std::vector< std::string > trusted_devices, Formulation formulation, bool disable_forensic_marking_picture, boost::optional< int > disable_forensic_marking_audio) const
An encrypted KDM.
Definition: encrypted_kdm.h:76
std::vector< std::string > keys() const
An exception related to a file.
Definition: exceptions.h:56
A key for decrypting/encrypting assets.
Definition: key.h:59
A representation of a local time (down to the second), including its offset from GMT (equivalent to x...
Definition: local_time.h:64
std::string as_string(bool with_millisecond=false) const
Definition: local_time.cc:186
A miscellaneous exception.
Definition: exceptions.h:94
An error raised when creating a DecryptedKDM object for assets that are not encrypted.
Definition: exceptions.h:197
CPL class.
DCP_ASSERT macro.
DecryptedKDM class.
DecryptedKDMKey class.
EncryptedKDM class.
Exceptions thrown by libdcp.
Namespace for everything in libdcp.
Definition: array_data.h:50
int base64_decode(std::string const &in, unsigned char *out, int out_length)
Definition: util.cc:206
bool day_less_than_or_equal(LocalTime a, LocalTime b)
Definition: util.cc:383
Formulation
Definition: types.h:287
bool day_greater_than_or_equal(LocalTime a, LocalTime b)
Definition: util.cc:398
ReelAsset class.
ReelFileAsset class.
Utility methods and classes.