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  bool first = true;
160 
161  for (auto const& i: kdm.keys()) {
162  /* Decode the base-64-encoded cipher value from the KDM */
163  unsigned char cipher_value[256];
164  int const cipher_value_len = base64_decode (i, cipher_value, sizeof (cipher_value));
165 
166  /* Decrypt it */
167  auto decrypted = new unsigned char[RSA_size(rsa)];
168  int const decrypted_len = RSA_private_decrypt (cipher_value_len, cipher_value, decrypted, rsa, RSA_PKCS1_OAEP_PADDING);
169  if (decrypted_len == -1) {
170  delete[] decrypted;
171 #if OPENSSL_VERSION_NUMBER > 0x10100000L
172  throw KDMDecryptionError (ERR_error_string (ERR_get_error(), 0), cipher_value_len, RSA_bits(rsa));
173 #else
174  throw KDMDecryptionError (ERR_error_string (ERR_get_error(), 0), cipher_value_len, rsa->n->dmax);
175 #endif
176  }
177 
178  dcp::LocalTime not_valid_before;
179  dcp::LocalTime not_valid_after;
180 
181  unsigned char* p = decrypted;
182  switch (decrypted_len) {
183  case 134:
184  {
185  /* Inter-op */
186  /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
187  p += 16;
188  /* 16 is is signer thumbprint [20 bytes] */
189  p += 20;
190  /* 36 is CPL id [16 bytes] */
191  string const cpl_id = get_uuid (&p);
192  /* 52 is key id [16 bytes] */
193  string const key_id = get_uuid (&p);
194  /* 68 is not-valid-before (a string) [25 bytes] */
195  not_valid_before = dcp::LocalTime(std::string(reinterpret_cast<char*>(p), 25));
196  p += 25;
197  /* 93 is not-valid-after (a string) [25 bytes] */
198  not_valid_after = dcp::LocalTime(std::string(reinterpret_cast<char*>(p), 25));
199  p += 25;
200  /* 118 is the key [ASDCP::KeyLen bytes] */
201  add_key (optional<string>(), key_id, Key(p), cpl_id, Standard::INTEROP);
202  break;
203  }
204  case 138:
205  {
206  /* SMPTE */
207  /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
208  DCP_ASSERT (memcmp (p, smpte_structure_id, 16) == 0);
209  p += 16;
210  /* 16 is is signer thumbprint [20 bytes] */
211  p += 20;
212  /* 36 is CPL id [16 bytes] */
213  string const cpl_id = get_uuid (&p);
214  /* 52 is key type [4 bytes] */
215  string const key_type = get (&p, 4);
216  /* 56 is key id [16 bytes] */
217  string const key_id = get_uuid (&p);
218  /* 72 is not-valid-before (a string) [25 bytes] */
219  not_valid_before = dcp::LocalTime(std::string(reinterpret_cast<char*>(p), 25));
220  p += 25;
221  /* 97 is not-valid-after (a string) [25 bytes] */
222  not_valid_after = dcp::LocalTime(std::string(reinterpret_cast<char*>(p), 25));
223  p += 25;
224  /* 112 is the key [ASDCP::KeyLen bytes] */
225  add_key (key_type, key_id, Key(p), cpl_id, Standard::SMPTE);
226  break;
227  }
228  default:
229  DCP_ASSERT (false);
230  }
231 
232  delete[] decrypted;
233 
234  if (first) {
235  _not_valid_before = not_valid_before;
236  _not_valid_after = not_valid_after;
237  first = false;
238  } else {
239  if (not_valid_before != _not_valid_before || not_valid_after != _not_valid_after) {
241  }
242  }
243  }
244 
245  RSA_free (rsa);
246  BIO_free (bio);
247 
248  _annotation_text = kdm.annotation_text ();
249  _content_title_text = kdm.content_title_text ();
250  _issue_date = kdm.issue_date ();
251 }
252 
253 
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 
268 }
269 
270 
272  string cpl_id,
273  map<shared_ptr<const ReelFileAsset>, Key> keys,
274  LocalTime not_valid_before,
275  LocalTime not_valid_after,
276  string annotation_text,
277  string content_title_text,
278  string issue_date
279  )
280  : _not_valid_before (not_valid_before)
281  , _not_valid_after (not_valid_after)
282  , _annotation_text (annotation_text)
283  , _content_title_text (content_title_text)
284  , _issue_date (issue_date)
285 {
286  for (auto const& i: keys) {
287  add_key (i.first->key_type(), i.first->key_id().get(), i.second, cpl_id, Standard::SMPTE);
288  }
289 }
290 
291 
293  shared_ptr<const CPL> cpl,
294  Key key,
295  LocalTime not_valid_before,
296  LocalTime not_valid_after,
297  string annotation_text,
298  string content_title_text,
299  string issue_date
300  )
301  : _not_valid_before (not_valid_before)
302  , _not_valid_after (not_valid_after)
303  , _annotation_text (annotation_text)
304  , _content_title_text (content_title_text)
305  , _issue_date (issue_date)
306 {
307  /* Create DecryptedKDMKey objects for each encryptable asset */
308  bool did_one = false;
309  for (auto i: cpl->reel_file_assets()) {
310  if (i->encryptable()) {
311  add_key (i->key_type().get(), i->key_id().get(), key, cpl->id(), Standard::SMPTE);
312  did_one = true;
313  }
314  }
315 
316  if (!did_one) {
317  throw NotEncryptedError (cpl->id ());
318  }
319 }
320 
321 
322 void
323 DecryptedKDM::add_key (optional<string> type, string key_id, Key key, string cpl_id, Standard standard)
324 {
325  _keys.push_back (DecryptedKDMKey (type, key_id, key, cpl_id, standard));
326 }
327 
328 
329 void
331 {
332  _keys.push_back (key);
333 }
334 
335 
338  shared_ptr<const CertificateChain> signer,
339  Certificate recipient,
340  vector<string> trusted_devices,
341  Formulation formulation,
342  bool disable_forensic_marking_picture,
343  optional<int> disable_forensic_marking_audio
344  ) const
345 {
346  DCP_ASSERT (!_keys.empty ());
347 
348  for (auto i: signer->leaf_to_root()) {
349  if (day_greater_than_or_equal(i.not_before(), _not_valid_before)) {
350  throw BadKDMDateError (true);
351  } else if (day_less_than_or_equal(i.not_after(), _not_valid_after)) {
352  throw BadKDMDateError (false);
353  }
354  }
355 
356  vector<pair<string, string>> key_ids;
357  vector<string> keys;
358  for (auto const& i: _keys) {
359  /* We're making SMPTE keys so we must have a type for each one */
360  DCP_ASSERT (i.type());
361  key_ids.push_back (make_pair (i.type().get(), i.id ()));
362 
363  /* XXX: SMPTE only */
364  uint8_t block[138];
365  uint8_t* p = block;
366 
367  put (&p, smpte_structure_id, 16);
368 
369  base64_decode (signer->leaf().thumbprint (), p, 20);
370  p += 20;
371 
372  put_uuid (&p, i.cpl_id ());
373  put (&p, i.type().get());
374  put_uuid (&p, i.id ());
375  put (&p, _not_valid_before.as_string ());
376  put (&p, _not_valid_after.as_string ());
377  put (&p, i.key().value(), ASDCP::KeyLen);
378 
379  /* Encrypt using the projector's public key */
380  RSA* rsa = recipient.public_key ();
381  unsigned char encrypted[RSA_size(rsa)];
382  int const encrypted_len = RSA_public_encrypt (p - block, block, encrypted, rsa, RSA_PKCS1_OAEP_PADDING);
383  if (encrypted_len == -1) {
384  throw MiscError (String::compose ("Could not encrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
385  }
386 
387  /* Lazy overallocation */
388  char out[encrypted_len * 2];
389  Kumu::base64encode (encrypted, encrypted_len, out, encrypted_len * 2);
390  int const N = strlen (out);
391  string lines;
392  for (int i = 0; i < N; ++i) {
393  if (i > 0 && (i % 64) == 0) {
394  lines += "\n";
395  }
396  lines += out[i];
397  }
398 
399  keys.push_back (lines);
400  }
401 
402  return EncryptedKDM (
403  signer,
404  recipient,
405  trusted_devices,
406  _keys.front().cpl_id (),
407  _content_title_text,
408  _annotation_text,
409  _not_valid_before,
410  _not_valid_after,
411  formulation,
412  disable_forensic_marking_picture,
413  disable_forensic_marking_audio,
414  key_ids,
415  keys
416  );
417 }
CertificateChain class.
A wrapper for an X509 certificate.
Definition: certificate.h:66
RSA * public_key() const
Definition: certificate.cc:458
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:68
std::string as_string(bool with_millisecond=false, bool with_timezone=true) const
Definition: local_time.cc:188
A miscellaneous exception.
Definition: exceptions.h:94
An error raised when creating a DecryptedKDM object for assets that are not encrypted.
Definition: exceptions.h:224
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:195
bool day_less_than_or_equal(LocalTime a, LocalTime b)
Definition: util.cc:357
bool day_greater_than_or_equal(LocalTime a, LocalTime b)
Definition: util.cc:372
ReelAsset class.
ReelFileAsset class.
Utility methods and classes.