#include "encrypted_ecinema_kdm.h"
#include "decrypted_ecinema_kdm.h"
+#include "exceptions.h"
#include <dcp/key.h>
+#include <dcp/util.h>
#include <dcp/certificate.h>
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+using std::string;
+using std::runtime_error;
using dcp::Certificate;
-DecryptedECinemaKDM::DecryptedECinemaKDM (dcp::Key content_key)
- : _content_key (content_key)
+DecryptedECinemaKDM::DecryptedECinemaKDM (string id, dcp::Key content_key)
+ : _id (id)
+ , _content_key (content_key)
{
}
+DecryptedECinemaKDM::DecryptedECinemaKDM (EncryptedECinemaKDM kdm, string private_key)
+ : _id (kdm.id())
+{
+ /* Read the private key */
+
+ BIO* bio = BIO_new_mem_buf (const_cast<char *> (private_key.c_str()), -1);
+ if (!bio) {
+ throw runtime_error ("could not create memory BIO");
+ }
+
+ RSA* rsa = PEM_read_bio_RSAPrivateKey (bio, 0, 0, 0);
+ if (!rsa) {
+ throw FileError ("could not read RSA private key file", private_key);
+ }
+
+ uint8_t value[RSA_size(rsa)];
+ int const len = RSA_private_decrypt (kdm.key().size(), kdm.key().data().get(), value, rsa, RSA_PKCS1_OAEP_PADDING);
+ if (len == -1) {
+ throw KDMError (ERR_error_string(ERR_get_error(), 0), "");
+ }
+
+ _content_key = dcp::Key (value, len);
+}
+
EncryptedECinemaKDM
DecryptedECinemaKDM::encrypt (Certificate recipient)
{
- return EncryptedECinemaKDM (_content_key, recipient);
+ return EncryptedECinemaKDM (_id, _content_key, recipient);
}
#endif
class DecryptedECinemaKDM
{
public:
- DecryptedECinemaKDM (dcp::Key content_key);
+ DecryptedECinemaKDM (std::string id, dcp::Key content_key);
+ DecryptedECinemaKDM (EncryptedECinemaKDM kdm, std::string private_key);
EncryptedECinemaKDM encrypt (dcp::Certificate recipient);
+ std::string id () const {
+ return _id;
+ }
+
+ dcp::Key key () const {
+ return _content_key;
+ }
+
private:
+ std::string _id;
/** unenecrypted content key */
dcp::Key _content_key;
};
#include "encrypted_ecinema_kdm.h"
#include <dcp/key.h>
#include <dcp/certificate.h>
+#include <dcp/util.h>
+#include <libcxml/cxml.h>
#include <libxml++/libxml++.h>
#include <openssl/rsa.h>
#include <iostream>
using boost::shared_ptr;
using dcp::Certificate;
-EncryptedECinemaKDM::EncryptedECinemaKDM (dcp::Key content_key, Certificate recipient)
+EncryptedECinemaKDM::EncryptedECinemaKDM (string id, dcp::Key content_key, Certificate recipient)
+ : _id (id)
{
RSA* rsa = recipient.public_key ();
_content_key = dcp::Data (RSA_size(rsa));
int const N = RSA_public_encrypt (content_key.length(), content_key.value(), _content_key.data().get(), rsa, RSA_PKCS1_OAEP_PADDING);
}
+EncryptedECinemaKDM::EncryptedECinemaKDM (string xml)
+{
+ cxml::Document doc ("ECinemaSecurityMessage");
+ doc.read_string (xml);
+ _id = doc.string_child ("Id");
+ _content_key = dcp::Data (256);
+ int const len = dcp::base64_decode (doc.string_child("Key"), _content_key.data().get(), _content_key.size());
+ _content_key.set_size (len);
+}
+
string
EncryptedECinemaKDM::as_xml () const
{
}
xmlpp::Document document;
- xmlpp::Element* root = document.create_root_node ("ECinemaSecurityMesage");
+ xmlpp::Element* root = document.create_root_node ("ECinemaSecurityMessage");
+ root->add_child("Id")->add_child_text(_id);
root->add_child("Key")->add_child_text(lines);
return document.write_to_string ("UTF-8");
}
class EncryptedECinemaKDM
{
public:
+ explicit EncryptedECinemaKDM (std::string xml);
std::string as_xml () const;
+ std::string id () const {
+ return _id;
+ }
+
+ dcp::Data key () const {
+ return _content_key;
+ }
+
private:
friend class DecryptedECinemaKDM;
- EncryptedECinemaKDM (dcp::Key key, dcp::Certificate recipient);
+ EncryptedECinemaKDM (std::string id, dcp::Key key, dcp::Certificate recipient);
+ std::string _id;
/** encrypted content key */
dcp::Data _content_key;
};
/*
- Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2019 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
#include "dcpomatic_log.h"
#include "ffmpeg_subtitle_stream.h"
#include "ffmpeg_audio_stream.h"
+#include "decrypted_ecinema_kdm.h"
#include "digester.h"
#include "compose.hpp"
+#include "config.h"
#include <dcp/raw_convert.h>
extern "C" {
#include <libavcodec/avcodec.h>
*/
av_dict_set (&options, "analyzeduration", raw_convert<string> (5 * 60 * 1000000).c_str(), 0);
av_dict_set (&options, "probesize", raw_convert<string> (5 * 60 * 1000000).c_str(), 0);
- if (_ffmpeg_content->decryption_key()) {
- av_dict_set (&options, "decryption_key", _ffmpeg_content->decryption_key()->c_str(), 0);
+ if (_ffmpeg_content->kdm()) {
+ DecryptedECinemaKDM kdm (_ffmpeg_content->kdm().get(), Config::instance()->decryption_chain()->key().get());
+ av_dict_set (&options, "decryption_key", kdm.key().hex().c_str(), 0);
}
int e = avformat_open_input (&_format_context, 0, 0, &options);
int const FFmpegContentProperty::SUBTITLE_STREAMS = 100;
int const FFmpegContentProperty::SUBTITLE_STREAM = 101;
int const FFmpegContentProperty::FILTERS = 102;
+int const FFmpegContentProperty::KDM = 103;
FFmpegContent::FFmpegContent (boost::filesystem::path p)
: Content (p)
_color_trc = get_optional_enum<AVColorTransferCharacteristic>(node, "ColorTransferCharacteristic");
_colorspace = get_optional_enum<AVColorSpace>(node, "Colorspace");
_bits_per_pixel = node->optional_number_child<int> ("BitsPerPixel");
- _decryption_key = node->optional_string_child ("DecryptionKey");
_encrypted = node->optional_bool_child("Encrypted").get_value_or(false);
}
if (_bits_per_pixel) {
node->add_child("BitsPerPixel")->add_child_text (raw_convert<string> (*_bits_per_pixel));
}
- if (_decryption_key) {
- node->add_child("DecryptionKey")->add_child_text (_decryption_key.get());
- }
if (_encrypted) {
node->add_child("Encypted")->add_child_text ("1");
}
if (examiner->has_video ()) {
set_default_colour_conversion ();
}
+
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+ _id = examiner->id ();
+#endif
}
string
Content::take_settings_from (c);
_filters = fc->_filters;
}
+
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+void
+FFmpegContent::add_kdm (EncryptedECinemaKDM kdm)
+{
+ ChangeSignaller<Content> cc (this, FFmpegContentProperty::KDM);
+ boost::mutex::scoped_lock lm (_mutex);
+ _kdm = kdm;
+
+}
+#endif
#ifndef DCPOMATIC_FFMPEG_CONTENT_H
#define DCPOMATIC_FFMPEG_CONTENT_H
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+#include "encrypted_ecinema_kdm.h"
+#endif
#include "content.h"
#include "audio_stream.h"
/** The chosen subtitle stream, or something about it */
static int const SUBTITLE_STREAM;
static int const FILTERS;
+ static int const KDM;
};
class FFmpegContent : public Content
void signal_subtitle_stream_changed ();
- boost::optional<std::string> decryption_key () const {
- boost::mutex::scoped_lock lm (_mutex);
- return _decryption_key;
- }
+#ifdef DCPOMATIC_VARIANT_SWAROOP
bool encrypted () const {
boost::mutex::scoped_lock lm (_mutex);
return _encrypted;
}
+ void add_kdm (EncryptedECinemaKDM kdm);
+
+ boost::optional<EncryptedECinemaKDM> kdm () const {
+ return _kdm;
+ }
+
+ boost::optional<std::string> id () const {
+ return _id;
+ }
+
+#endif
+
private:
void add_properties (boost::shared_ptr<const Film> film, std::list<UserProperty> &) const;
boost::optional<AVColorTransferCharacteristic> _color_trc;
boost::optional<AVColorSpace> _colorspace;
boost::optional<int> _bits_per_pixel;
- boost::optional<std::string> _decryption_key;
+#ifdef DCPOMATIC_VARIANT_SWAROOP
bool _encrypted;
+ boost::optional<EncryptedECinemaKDM> _kdm;
+ boost::optional<std::string> _id;
+#endif
};
#endif
#include "i18n.h"
-
using std::cout;
using std::string;
using std::vector;
bool
FFmpegDecoder::pass ()
{
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+ if (_ffmpeg_content->encrypted() && !_ffmpeg_content->kdm()) {
+ return true;
+ }
+#endif
+
int r = av_read_frame (_format_context, &_packet);
/* AVERROR_INVALIDDATA can apparently be returned sometimes even when av_read_frame
DCPOMATIC_ASSERT (fabs (*_rotation - 90 * round (*_rotation / 90)) < 2);
}
+
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+ AVDictionaryEntry* e = av_dict_get (_format_context->metadata, SWAROOP_ID_TAG, 0, 0);
+ if (e) {
+ _id = e->value;
+ }
+#endif
}
void
return _rotation;
}
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+ boost::optional<std::string> id () const {
+ return _id;
+ }
+#endif
+
private:
void video_packet (AVCodecContext *);
void audio_packet (AVCodecContext *, boost::shared_ptr<FFmpegAudioStream>);
boost::optional<double> _rotation;
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+ boost::optional<std::string> _id;
+#endif
+
struct SubtitleStart
{
SubtitleStart (std::string id_, bool image_, dcpomatic::ContentTime time_)
#define CLOSED_CAPTION_LINES 3
/** Maximum line length of closed caption viewers */
#define CLOSED_CAPTION_LENGTH 30
+/* We are mis-using episode_id here, as non-iTunes metadata tags are ignored.
+ I tried the use_metadata_tags option but it didn't seem to make any difference.
+*/
+#define SWAROOP_ID_TAG "episode_id"
extern std::string program_name;
extern bool is_batch_converter;
#include "lib/version.h"
#include "lib/decrypted_ecinema_kdm.h"
#include "lib/config.h"
+#include "lib/util.h"
#include <dcp/key.h>
extern "C" {
#include <libavformat/avformat.h>
dcp::Key key (AES_CTR_KEY_SIZE);
AVDictionary* options = 0;
av_dict_set (&options, "encryption_key", key.hex().c_str(), 0);
+ /* XXX: is this OK? */
+ av_dict_set (&options, "encryption_kid", "00000000000000000000000000000000", 0);
+ av_dict_set (&options, "encryption_scheme", "cenc-aes-ctr", 0);
+
+ string id = dcp::make_uuid ();
+ if (av_dict_set(&output_fc->metadata, SWAROOP_ID_TAG, id.c_str(), 0) < 0) {
+ cerr << "Could not write ID to output.\n";
+ exit (EXIT_FAILURE);
+ }
if (avformat_write_header (output_fc, &options) < 0) {
cerr << "Could not write header to output.\n";
avformat_free_context (input_fc);
avformat_free_context (output_fc);
- DecryptedECinemaKDM decrypted_kdm (key);
+ DecryptedECinemaKDM decrypted_kdm (id, key);
EncryptedECinemaKDM encrypted_kdm = decrypted_kdm.encrypt (Config::instance()->decryption_chain()->leaf());
cout << encrypted_kdm.as_xml() << "\n";
}
if (d->ShowModal() == wxID_OK) {
DCPOMATIC_ASSERT (_film);
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+ shared_ptr<FFmpegContent> ffmpeg = boost::dynamic_pointer_cast<FFmpegContent>(_film->content().front());
+ if (ffmpeg) {
+ try {
+ ffmpeg->add_kdm (EncryptedECinemaKDM(dcp::file_to_string(wx_to_std(d->GetPath()), MAX_KDM_SIZE)));
+ } catch (exception& e) {
+ error_dialog (this, wxString::Format(_("Could not load KDM.")), std_to_wx(e.what()));
+ d->Destroy();
+ return;
+ }
+ }
+#endif
shared_ptr<DCPContent> dcp = boost::dynamic_pointer_cast<DCPContent>(_film->content().front());
+#ifndef DCPOMATIC_VARIANT_SWAROOP
DCPOMATIC_ASSERT (dcp);
+#endif
try {
- dcp->add_kdm (dcp::EncryptedKDM (dcp::file_to_string (wx_to_std (d->GetPath ()), MAX_KDM_SIZE)));
- dcp->examine (_film, shared_ptr<Job>());
+ if (dcp) {
+ dcp->add_kdm (dcp::EncryptedKDM(dcp::file_to_string(wx_to_std(d->GetPath()), MAX_KDM_SIZE)));
+ dcp->examine (_film, shared_ptr<Job>());
+ }
} catch (exception& e) {
error_dialog (this, wxString::Format (_("Could not load KDM.")), std_to_wx(e.what()));
d->Destroy ();
/*
- Copyright (C) 2018 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2018-2019 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
#include "lib/cross.h"
#include "lib/scoped_temporary.h"
#include "lib/internet.h"
+#include "lib/ffmpeg_content.h"
#include <dcp/raw_convert.h>
#include <dcp/exceptions.h>
#include <wx/listctrl.h>
return optional<dcp::EncryptedKDM>();
}
+optional<EncryptedECinemaKDM>
+SwaroopControls::get_kdm_from_directory (shared_ptr<FFmpegContent> ffmpeg)
+{
+ using namespace boost::filesystem;
+ optional<path> kdm_dir = Config::instance()->player_kdm_directory();
+ if (!kdm_dir) {
+ return optional<EncryptedECinemaKDM>();
+ }
+ for (directory_iterator i = directory_iterator(*kdm_dir); i != directory_iterator(); ++i) {
+ try {
+ if (file_size(i->path()) < MAX_KDM_SIZE) {
+ EncryptedECinemaKDM kdm (dcp::file_to_string(i->path()));
+ if (kdm.id() == ffmpeg->id().get_value_or("")) {
+ return kdm;
+ }
+ }
+ } catch (std::exception& e) {
+ /* Hey well */
+ }
+ }
+ return optional<EncryptedECinemaKDM>();
+}
void
SwaroopControls::spl_selection_changed ()
{
return;
}
}
+ shared_ptr<FFmpegContent> ffmpeg = dynamic_pointer_cast<FFmpegContent> (i.content);
+ if (ffmpeg && ffmpeg->encrypted()) {
+ optional<EncryptedECinemaKDM> kdm = get_kdm_from_directory (ffmpeg);
+ if (kdm) {
+ try {
+ ffmpeg->add_kdm (*kdm);
+ } catch (KDMError& e) {
+ error_dialog (this, "Could not load KDM.");
+ }
+ } else {
+ error_dialog (this, "This playlist cannot be loaded as a KDM is missing.");
+ _selected_playlist = boost::none;
+ _spl_view->SetItemState (selected, 0, wxLIST_STATE_SELECTED);
+ return;
+ }
+ }
}
_current_spl_view->DeleteAllItems ();
#include "controls.h"
class DCPContent;
+class EncryptedECinemaKDM;
class SwaroopControls : public Controls
{
boost::optional<dcp::EncryptedKDM> get_kdm_from_url (boost::shared_ptr<DCPContent> dcp);
boost::optional<dcp::EncryptedKDM> get_kdm_from_directory (boost::shared_ptr<DCPContent> dcp);
+ boost::optional<EncryptedECinemaKDM> get_kdm_from_directory (boost::shared_ptr<FFmpegContent> ffmpeg);
wxButton* _play_button;
wxButton* _pause_button;