diff options
| author | Carl Hetherington <cth@carlh.net> | 2014-03-19 21:46:01 +0000 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2014-03-19 21:46:01 +0000 |
| commit | 7702e5d643440e75369078863b34f8a574ee4143 (patch) | |
| tree | e01edc51718fd6c475b449748ff7fa6e5c526bc9 /src/decrypted_kdm.cc | |
| parent | c91aa27e13703874c944fed763b5b039ceae71d2 (diff) | |
Considerable re-work of KDM class to express the difference between encrypted and unencrypted KDMs.
Diffstat (limited to 'src/decrypted_kdm.cc')
| -rw-r--r-- | src/decrypted_kdm.cc | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/src/decrypted_kdm.cc b/src/decrypted_kdm.cc new file mode 100644 index 00000000..7e9e146d --- /dev/null +++ b/src/decrypted_kdm.cc @@ -0,0 +1,276 @@ +/* + Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "decrypted_kdm.h" +#include "decrypted_kdm_key.h" +#include "encrypted_kdm.h" +#include "util.h" +#include "exceptions.h" +#include "cpl.h" +#include "mxf.h" +#include "signer.h" +#include "AS_DCP.h" +#include "KM_util.h" +#include "compose.hpp" +#include <openssl/rsa.h> +#include <openssl/pem.h> +#include <openssl/err.h> + +using std::list; +using std::string; +using std::stringstream; +using std::setw; +using std::setfill; +using std::hex; +using std::pair; +using boost::shared_ptr; +using namespace dcp; + +static void +put (uint8_t ** d, string s) +{ + memcpy (*d, s.c_str(), s.length()); + (*d) += s.length(); +} + +static void +put (uint8_t ** d, uint8_t const * s, int N) +{ + memcpy (*d, s, N); + (*d) += N; +} + +static void +put_uuid (uint8_t ** d, string id) +{ + id.erase (std::remove (id.begin(), id.end(), '-')); + for (int i = 0; i < 32; i += 2) { + stringstream s; + s << id[i] << id[i + 1]; + int h; + s >> hex >> h; + **d = h; + (*d)++; + } +} + +static string +get_uuid (unsigned char ** p) +{ + stringstream g; + + for (int i = 0; i < 16; ++i) { + g << setw(2) << setfill('0') << hex << static_cast<int> (**p); + (*p)++; + if (i == 3 || i == 5 || i == 7 || i == 9) { + g << '-'; + } + } + + return g.str (); +} + +static string +get (uint8_t ** p, int N) +{ + string g; + for (int i = 0; i < N; ++i) { + g += **p; + (*p)++; + } + + return g; +} + +DecryptedKDM::DecryptedKDM (EncryptedKDM const & kdm, boost::filesystem::path private_key) +{ + /* Read the private key */ + + FILE* private_key_file = fopen_boost (private_key, "r"); + if (!private_key_file) { + throw FileError ("could not find RSA private key file", private_key, errno); + } + + RSA* rsa = PEM_read_RSAPrivateKey (private_key_file, 0, 0, 0); + fclose (private_key_file); + if (!rsa) { + throw FileError ("could not read RSA private key file", private_key, errno); + } + + /* Use the private key to decrypt the keys */ + + list<string> const encrypted_keys = kdm.keys (); + for (list<string>::const_iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) { + + /* Decode the base-64-encoded cipher value from the KDM */ + unsigned char cipher_value[256]; + int const cipher_value_len = base64_decode (*i, cipher_value, sizeof (cipher_value)); + + /* Decrypt it */ + unsigned char * decrypted = new unsigned char[RSA_size(rsa)]; + int const decrypted_len = RSA_private_decrypt (cipher_value_len, cipher_value, decrypted, rsa, RSA_PKCS1_OAEP_PADDING); + if (decrypted_len == -1) { + delete[] decrypted; + throw MiscError (String::compose ("Could not decrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0))); + } + + unsigned char* p = decrypted; + switch (decrypted_len) { + case 134: + { + /* Inter-op */ + /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */ + p += 16; + /* 16 is is signer thumbprint [20 bytes] */ + p += 20; + /* 36 is CPL id [16 bytes] */ + string const cpl_id = get_uuid (&p); + /* 52 is key id [16 bytes] */ + string const key_id = get_uuid (&p); + /* 68 is not-valid-before (a string) [25 bytes] */ + p += 25; + /* 93 is not-valid-after (a string) [25 bytes] */ + p += 25; + /* 118 is the key [ASDCP::KeyLen bytes] */ + _keys.push_back (DecryptedKDMKey ("", key_id, Key (p), cpl_id)); + break; + } + case 138: + { + /* SMPTE */ + /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */ + p += 16; + /* 16 is is signer thumbprint [20 bytes] */ + p += 20; + /* 36 is CPL id [16 bytes] */ + string const cpl_id = get_uuid (&p); + /* 52 is key type [4 bytes] */ + string const key_type = get (&p, 4); + /* 56 is key id [16 bytes] */ + string const key_id = get_uuid (&p); + /* 72 is not-valid-before (a string) [25 bytes] */ + p += 25; + /* 97 is not-valid-after (a string) [25 bytes] */ + p += 25; + /* 112 is the key [ASDCP::KeyLen bytes] */ + _keys.push_back (DecryptedKDMKey (key_type, key_id, Key (p), cpl_id)); + break; + } + default: + assert (false); + } + + delete[] decrypted; + } + + RSA_free (rsa); +} + +DecryptedKDM::DecryptedKDM ( + boost::shared_ptr<const CPL> cpl, + LocalTime not_valid_before, + LocalTime not_valid_after, + string annotation_text, + string content_title_text, + string issue_date + ) + : _not_valid_before (not_valid_before) + , _not_valid_after (not_valid_after) + , _annotation_text (annotation_text) + , _content_title_text (content_title_text) + , _issue_date (issue_date) +{ + /* Create DecryptedKDMKey objects for each MXF asset */ + list<shared_ptr<const Content> > content = cpl->content (); + for (list<shared_ptr<const Content> >::iterator i = content.begin(); i != content.end(); ++i) { + /* XXX: do non-MXF assets need keys? */ + shared_ptr<const MXF> mxf = boost::dynamic_pointer_cast<const MXF> (*i); + if (mxf) { + _keys.push_back (DecryptedKDMKey (mxf->key_type(), mxf->key_id(), mxf->key().get (), cpl->id ())); + } + } +} + +EncryptedKDM +DecryptedKDM::encrypt (shared_ptr<const Signer> signer, shared_ptr<const Certificate> recipient) const +{ + list<pair<string, string> > key_ids; + list<string> keys; + for (list<DecryptedKDMKey>::const_iterator i = _keys.begin(); i != _keys.end(); ++i) { + + key_ids.push_back (make_pair (i->type(), i->id ())); + + /* XXX: SMPTE only */ + uint8_t block[138]; + uint8_t* p = block; + + /* Magic value specified by SMPTE S430-1-2006 */ + uint8_t structure_id[] = { 0xf1, 0xdc, 0x12, 0x44, 0x60, 0x16, 0x9a, 0x0e, 0x85, 0xbc, 0x30, 0x06, 0x42, 0xf8, 0x66, 0xab }; + put (&p, structure_id, 16); + + base64_decode (signer->certificates().leaf()->thumbprint (), p, 20); + p += 20; + + put_uuid (&p, i->cpl_id ()); + put (&p, i->type ()); + put_uuid (&p, i->id ()); + put (&p, _not_valid_before.as_string ()); + put (&p, _not_valid_after.as_string ()); + put (&p, i->key().value(), ASDCP::KeyLen); + + /* Encrypt using the projector's public key */ + RSA* rsa = recipient->public_key (); + unsigned char encrypted[RSA_size(rsa)]; + int const encrypted_len = RSA_public_encrypt (p - block, block, encrypted, rsa, RSA_PKCS1_OAEP_PADDING); + if (encrypted_len == -1) { + throw MiscError (String::compose ("Could not encrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0))); + } + + /* Lazy overallocation */ + char out[encrypted_len * 2]; + Kumu::base64encode (encrypted, encrypted_len, out, encrypted_len * 2); + int const N = strlen (out); + stringstream lines; + for (int i = 0; i < N; ++i) { + if (i > 0 && (i % 64) == 0) { + lines << "\n"; + } + lines << out[i]; + } + + keys.push_back (lines.str ()); + } + + string device_list_description = recipient->common_name (); + if (device_list_description.find (".") != string::npos) { + device_list_description = device_list_description.substr (device_list_description.find (".") + 1); + } + + return EncryptedKDM ( + signer, + recipient, + device_list_description, + _keys.front().cpl_id (), + _content_title_text, + _not_valid_before, + _not_valid_after, + key_ids, + keys + ); +} |
