Add some KDM metadata accessors.
[libdcp.git] / src / decrypted_kdm.cc
1 /*
2     Copyright (C) 2013-2015 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include "decrypted_kdm.h"
21 #include "decrypted_kdm_key.h"
22 #include "encrypted_kdm.h"
23 #include "reel_mxf.h"
24 #include "reel_asset.h"
25 #include "util.h"
26 #include "exceptions.h"
27 #include "cpl.h"
28 #include "certificate_chain.h"
29 #include "dcp_assert.h"
30 #include "AS_DCP.h"
31 #include "KM_util.h"
32 #include "compose.hpp"
33 #include <openssl/rsa.h>
34 #include <openssl/pem.h>
35 #include <openssl/err.h>
36 #include <boost/foreach.hpp>
37
38 using std::list;
39 using std::string;
40 using std::stringstream;
41 using std::setw;
42 using std::setfill;
43 using std::hex;
44 using std::pair;
45 using boost::shared_ptr;
46 using namespace dcp;
47
48 static void
49 put (uint8_t ** d, string s)
50 {
51         memcpy (*d, s.c_str(), s.length());
52         (*d) += s.length();
53 }
54
55 static void
56 put (uint8_t ** d, uint8_t const * s, int N)
57 {
58         memcpy (*d, s, N);
59         (*d) += N;
60 }
61
62 static void
63 put_uuid (uint8_t ** d, string id)
64 {
65         id.erase (std::remove (id.begin(), id.end(), '-'));
66         for (int i = 0; i < 32; i += 2) {
67                 stringstream s;
68                 s << id[i] << id[i + 1];
69                 int h;
70                 s >> hex >> h;
71                 **d = h;
72                 (*d)++;
73         }
74 }
75
76 static string
77 get_uuid (unsigned char ** p)
78 {
79         stringstream g;
80
81         for (int i = 0; i < 16; ++i) {
82                 g << setw(2) << setfill('0') << hex << static_cast<int> (**p);
83                 (*p)++;
84                 if (i == 3 || i == 5 || i == 7 || i == 9) {
85                         g << '-';
86                 }
87         }
88
89         return g.str ();
90 }
91
92 static string
93 get (uint8_t ** p, int N)
94 {
95         string g;
96         for (int i = 0; i < N; ++i) {
97                 g += **p;
98                 (*p)++;
99         }
100
101         return g;
102 }
103
104 DecryptedKDM::DecryptedKDM (EncryptedKDM const & kdm, string private_key)
105 {
106         /* Read the private key */
107
108         BIO* bio = BIO_new_mem_buf (const_cast<char *> (private_key.c_str ()), -1);
109         if (!bio) {
110                 throw MiscError ("could not create memory BIO");
111         }
112
113         RSA* rsa = PEM_read_bio_RSAPrivateKey (bio, 0, 0, 0);
114         if (!rsa) {
115                 throw FileError ("could not read RSA private key file", private_key, errno);
116         }
117
118         /* Use the private key to decrypt the keys */
119
120         BOOST_FOREACH (string const & i, kdm.keys ()) {
121                 /* Decode the base-64-encoded cipher value from the KDM */
122                 unsigned char cipher_value[256];
123                 int const cipher_value_len = base64_decode (i, cipher_value, sizeof (cipher_value));
124
125                 /* Decrypt it */
126                 unsigned char * decrypted = new unsigned char[RSA_size(rsa)];
127                 int const decrypted_len = RSA_private_decrypt (cipher_value_len, cipher_value, decrypted, rsa, RSA_PKCS1_OAEP_PADDING);
128                 if (decrypted_len == -1) {
129                         delete[] decrypted;
130                         throw MiscError (String::compose ("Could not decrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
131                 }
132
133                 unsigned char* p = decrypted;
134                 switch (decrypted_len) {
135                 case 134:
136                 {
137                         /* Inter-op */
138                         /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
139                         p += 16;
140                         /* 16 is is signer thumbprint [20 bytes] */
141                         p += 20;
142                         /* 36 is CPL id [16 bytes] */
143                         string const cpl_id = get_uuid (&p);
144                         /* 52 is key id [16 bytes] */
145                         string const key_id = get_uuid (&p);
146                         /* 68 is not-valid-before (a string) [25 bytes] */
147                         p += 25;
148                         /* 93 is not-valid-after (a string) [25 bytes] */
149                         p += 25;
150                         /* 118 is the key [ASDCP::KeyLen bytes] */
151                         _keys.push_back (DecryptedKDMKey ("", key_id, Key (p), cpl_id));
152                         break;
153                 }
154                 case 138:
155                 {
156                         /* SMPTE */
157                         /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
158                         p += 16;
159                         /* 16 is is signer thumbprint [20 bytes] */
160                         p += 20;
161                         /* 36 is CPL id [16 bytes] */
162                         string const cpl_id = get_uuid (&p);
163                         /* 52 is key type [4 bytes] */
164                         string const key_type = get (&p, 4);
165                         /* 56 is key id [16 bytes] */
166                         string const key_id = get_uuid (&p);
167                         /* 72 is not-valid-before (a string) [25 bytes] */
168                         p += 25;
169                         /* 97 is not-valid-after (a string) [25 bytes] */
170                         p += 25;
171                         /* 112 is the key [ASDCP::KeyLen bytes] */
172                         _keys.push_back (DecryptedKDMKey (key_type, key_id, Key (p), cpl_id));
173                         break;
174                 }
175                 default:
176                         DCP_ASSERT (false);
177                 }
178
179                 delete[] decrypted;
180         }
181
182         RSA_free (rsa);
183         BIO_free (bio);
184
185         _annotation_text = kdm.annotation_text ();
186         _content_title_text = kdm.content_title_text ();
187         _issue_date = kdm.issue_date ();
188 }
189
190 DecryptedKDM::DecryptedKDM (
191         boost::shared_ptr<const CPL> cpl,
192         Key key,
193         LocalTime not_valid_before,
194         LocalTime not_valid_after,
195         string annotation_text,
196         string content_title_text,
197         string issue_date
198         )
199         : _not_valid_before (not_valid_before)
200         , _not_valid_after (not_valid_after)
201         , _annotation_text (annotation_text)
202         , _content_title_text (content_title_text)
203         , _issue_date (issue_date)
204 {
205         /* Create DecryptedKDMKey objects for each encryptable asset */
206         BOOST_FOREACH(shared_ptr<const ReelAsset> i, cpl->reel_assets ()) {
207                 shared_ptr<const ReelMXF> mxf = boost::dynamic_pointer_cast<const ReelMXF> (i);
208                 shared_ptr<const ReelAsset> asset = boost::dynamic_pointer_cast<const ReelAsset> (i);
209                 if (asset && mxf) {
210                         if (!mxf->key_id ()) {
211                                 throw NotEncryptedError (asset->id ());
212                         }
213                         _keys.push_back (DecryptedKDMKey (mxf->key_type(), mxf->key_id().get(), key, cpl->id ()));
214                 }
215         }
216 }
217
218 EncryptedKDM
219 DecryptedKDM::encrypt (shared_ptr<const CertificateChain> signer, Certificate recipient, Formulation formulation) const
220 {
221         list<pair<string, string> > key_ids;
222         list<string> keys;
223         BOOST_FOREACH (DecryptedKDMKey const & i, _keys) {
224                 key_ids.push_back (make_pair (i.type(), i.id ()));
225
226                 /* XXX: SMPTE only */
227                 uint8_t block[138];
228                 uint8_t* p = block;
229
230                 /* Magic value specified by SMPTE S430-1-2006 */
231                 uint8_t structure_id[] = { 0xf1, 0xdc, 0x12, 0x44, 0x60, 0x16, 0x9a, 0x0e, 0x85, 0xbc, 0x30, 0x06, 0x42, 0xf8, 0x66, 0xab };
232                 put (&p, structure_id, 16);
233
234                 base64_decode (signer->leaf().thumbprint (), p, 20);
235                 p += 20;
236
237                 put_uuid (&p, i.cpl_id ());
238                 put (&p, i.type ());
239                 put_uuid (&p, i.id ());
240                 put (&p, _not_valid_before.as_string ());
241                 put (&p, _not_valid_after.as_string ());
242                 put (&p, i.key().value(), ASDCP::KeyLen);
243
244                 /* Encrypt using the projector's public key */
245                 RSA* rsa = recipient.public_key ();
246                 unsigned char encrypted[RSA_size(rsa)];
247                 int const encrypted_len = RSA_public_encrypt (p - block, block, encrypted, rsa, RSA_PKCS1_OAEP_PADDING);
248                 if (encrypted_len == -1) {
249                         throw MiscError (String::compose ("Could not encrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
250                 }
251
252                 /* Lazy overallocation */
253                 char out[encrypted_len * 2];
254                 Kumu::base64encode (encrypted, encrypted_len, out, encrypted_len * 2);
255                 int const N = strlen (out);
256                 stringstream lines;
257                 for (int i = 0; i < N; ++i) {
258                         if (i > 0 && (i % 64) == 0) {
259                                 lines << "\n";
260                         }
261                         lines << out[i];
262                 }
263
264                 keys.push_back (lines.str ());
265         }
266
267         string device_list_description = recipient.subject_common_name ();
268         if (device_list_description.find (".") != string::npos) {
269                 device_list_description = device_list_description.substr (device_list_description.find (".") + 1);
270         }
271
272         return EncryptedKDM (
273                 signer,
274                 recipient,
275                 device_list_description,
276                 _keys.front().cpl_id (),
277                 _content_title_text,
278                 _not_valid_before,
279                 _not_valid_after,
280                 formulation,
281                 key_ids,
282                 keys
283                 );
284 }