2 Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
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.
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.
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.
20 #include "decrypted_kdm.h"
21 #include "decrypted_kdm_key.h"
22 #include "encrypted_kdm.h"
23 #include "reel_mxf_asset.h"
25 #include "exceptions.h"
31 #include "compose.hpp"
32 #include <openssl/rsa.h>
33 #include <openssl/pem.h>
34 #include <openssl/err.h>
38 using std::stringstream;
43 using boost::shared_ptr;
47 put (uint8_t ** d, string s)
49 memcpy (*d, s.c_str(), s.length());
54 put (uint8_t ** d, uint8_t const * s, int N)
61 put_uuid (uint8_t ** d, string id)
63 id.erase (std::remove (id.begin(), id.end(), '-'));
64 for (int i = 0; i < 32; i += 2) {
66 s << id[i] << id[i + 1];
75 get_uuid (unsigned char ** p)
79 for (int i = 0; i < 16; ++i) {
80 g << setw(2) << setfill('0') << hex << static_cast<int> (**p);
82 if (i == 3 || i == 5 || i == 7 || i == 9) {
91 get (uint8_t ** p, int N)
94 for (int i = 0; i < N; ++i) {
102 DecryptedKDM::DecryptedKDM (EncryptedKDM const & kdm, string private_key)
104 /* Read the private key */
106 BIO* bio = BIO_new_mem_buf (const_cast<char *> (private_key.c_str ()), -1);
108 throw MiscError ("could not create memory BIO");
111 RSA* rsa = PEM_read_bio_RSAPrivateKey (bio, 0, 0, 0);
113 throw FileError ("could not read RSA private key file", private_key, errno);
116 /* Use the private key to decrypt the keys */
118 list<string> const encrypted_keys = kdm.keys ();
119 for (list<string>::const_iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) {
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));
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) {
130 throw MiscError (String::compose ("Could not decrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
133 unsigned char* p = decrypted;
134 switch (decrypted_len) {
138 /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
140 /* 16 is is signer thumbprint [20 bytes] */
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] */
148 /* 93 is not-valid-after (a string) [25 bytes] */
150 /* 118 is the key [ASDCP::KeyLen bytes] */
151 _keys.push_back (DecryptedKDMKey ("", key_id, Key (p), cpl_id));
157 /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
159 /* 16 is is signer thumbprint [20 bytes] */
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] */
169 /* 97 is not-valid-after (a string) [25 bytes] */
171 /* 112 is the key [ASDCP::KeyLen bytes] */
172 _keys.push_back (DecryptedKDMKey (key_type, key_id, Key (p), cpl_id));
186 DecryptedKDM::DecryptedKDM (
187 boost::shared_ptr<const CPL> cpl,
189 LocalTime not_valid_before,
190 LocalTime not_valid_after,
191 string annotation_text,
192 string content_title_text,
195 : _not_valid_before (not_valid_before)
196 , _not_valid_after (not_valid_after)
197 , _annotation_text (annotation_text)
198 , _content_title_text (content_title_text)
199 , _issue_date (issue_date)
201 /* Create DecryptedKDMKey objects for each MXF asset */
202 list<shared_ptr<const ReelAsset> > assets = cpl->reel_assets ();
203 for (list<shared_ptr<const ReelAsset> >::iterator i = assets.begin(); i != assets.end(); ++i) {
204 /* XXX: do non-MXF assets need keys? */
205 shared_ptr<const ReelMXFAsset> mxf = boost::dynamic_pointer_cast<const ReelMXFAsset> (*i);
207 if (mxf->key_id().empty ()) {
208 throw NotEncryptedError (mxf->id());
210 _keys.push_back (DecryptedKDMKey (mxf->key_type(), mxf->key_id(), key, cpl->id ()));
216 DecryptedKDM::encrypt (shared_ptr<const Signer> signer, Certificate recipient, Formulation formulation) const
218 list<pair<string, string> > key_ids;
220 for (list<DecryptedKDMKey>::const_iterator i = _keys.begin(); i != _keys.end(); ++i) {
222 key_ids.push_back (make_pair (i->type(), i->id ()));
224 /* XXX: SMPTE only */
228 /* Magic value specified by SMPTE S430-1-2006 */
229 uint8_t structure_id[] = { 0xf1, 0xdc, 0x12, 0x44, 0x60, 0x16, 0x9a, 0x0e, 0x85, 0xbc, 0x30, 0x06, 0x42, 0xf8, 0x66, 0xab };
230 put (&p, structure_id, 16);
232 base64_decode (signer->certificates().leaf().thumbprint (), p, 20);
235 put_uuid (&p, i->cpl_id ());
236 put (&p, i->type ());
237 put_uuid (&p, i->id ());
238 put (&p, _not_valid_before.as_string ());
239 put (&p, _not_valid_after.as_string ());
240 put (&p, i->key().value(), ASDCP::KeyLen);
242 /* Encrypt using the projector's public key */
243 RSA* rsa = recipient.public_key ();
244 unsigned char encrypted[RSA_size(rsa)];
245 int const encrypted_len = RSA_public_encrypt (p - block, block, encrypted, rsa, RSA_PKCS1_OAEP_PADDING);
246 if (encrypted_len == -1) {
247 throw MiscError (String::compose ("Could not encrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
250 /* Lazy overallocation */
251 char out[encrypted_len * 2];
252 Kumu::base64encode (encrypted, encrypted_len, out, encrypted_len * 2);
253 int const N = strlen (out);
255 for (int i = 0; i < N; ++i) {
256 if (i > 0 && (i % 64) == 0) {
262 keys.push_back (lines.str ());
265 string device_list_description = recipient.common_name ();
266 if (device_list_description.find (".") != string::npos) {
267 device_list_description = device_list_description.substr (device_list_description.find (".") + 1);
270 return EncryptedKDM (
273 device_list_description,
274 _keys.front().cpl_id (),