diff --git a/player/src/common/defines.h b/player/src/common/defines.h index 5be6087..fd56e80 100644 --- a/player/src/common/defines.h +++ b/player/src/common/defines.h @@ -81,11 +81,14 @@ //! Folder for certificates #define CERTIFICATES_FOLDER "KnownCerts" +//! Folder for PKCS12 keys +#define KEYSTORE_FOLDER "KeyStore" + //! Binary format filter -#define BINARY_FORMAT QObject::tr("Binary format (*.der)") +#define CERT_FORMAT_EXTENSIONS QObject::tr("Binary format (*.der;*.cer;*.crt;*.pem)") //! Binary filter. -#define BINARY_FILTER "*.der" +#define CERT_FILE_EXTENSIONS "*.der;*.cer;*.crt;*.pfx;*.pem" //! Binary extention #define BINARY_EXT ".der" diff --git a/player/src/common/segmentInfo.cpp b/player/src/common/segmentInfo.cpp index 2f267cb..a6712bc 100644 --- a/player/src/common/segmentInfo.cpp +++ b/player/src/common/segmentInfo.cpp @@ -26,6 +26,7 @@ ************************************************************************************/ #include "segmentInfo.h" +#include "certificateStorage.h" SegmentInfo::SegmentInfo(QString file_name /*= QString()*/) : m_segment_number(0) @@ -57,6 +58,11 @@ void SegmentInfo::read(MediaHeaderBox*box) if (m_videoTimescale == 0) m_videoTimescale = box->getTimeScale(); } +void SegmentInfo::read(ProtectionSystemSpecificHeaderBox* box) +{ + m_key = CertificateStorage(KEYSTORE_FOLDER).decryptKey(box); +} + void SegmentInfo::readAfIdentificationBox(AFIdentificationBox *box) { m_duration = box->getDuration() / m_videoTimescale; diff --git a/player/src/common/segmentInfo.h b/player/src/common/segmentInfo.h index e5ed98d..bdefe89 100644 --- a/player/src/common/segmentInfo.h +++ b/player/src/common/segmentInfo.h @@ -46,6 +46,7 @@ #include "correctstarttimebox.hpp" #include "helpers/optional.hpp" #include "templateTableBoxes.hpp" +#include "protectionSystemSpecificHeaderBox.hpp" /** * Class that descibes one MP4 file. @@ -72,6 +73,7 @@ class SegmentInfo void read(CompositionOffsetBox* box); void read(TrackRunBox* box); void read(MediaHeaderBox* box); + void read(ProtectionSystemSpecificHeaderBox* box); //! Returns if a fragment is a Surveillance file. bool isSurveillanceFragment() const; @@ -154,6 +156,7 @@ class SegmentInfo public: uint32_t m_currentParserTrackId; ///< track id currently beingparsed uint32_t m_defaultSampleDuration; ///< default sample duration of last tfhd read in order to pass to trun + HexArray m_key; ///< optional key for decrypting }; typedef QList SegmentList; diff --git a/player/src/parser/boxFactory.cpp b/player/src/parser/boxFactory.cpp index d6fb08f..6da7dda 100644 --- a/player/src/parser/boxFactory.cpp +++ b/player/src/parser/boxFactory.cpp @@ -57,6 +57,7 @@ #include "trackHeaderBox.hpp" #include "trackRunBox.hpp" #include "correctstarttimebox.hpp" +#include "protectionSystemSpecificHeaderBox.hpp" BoxFactory::BoxFactory() { @@ -134,6 +135,7 @@ BoxFactory::BoxFactory() registerCreator(); registerCreator(); registerCreator(); + registerCreator(); } BoxFactory & BoxFactory::instance() diff --git a/player/src/parser/certificateStorage.cpp b/player/src/parser/certificateStorage.cpp index 1d11719..bf2cd60 100644 --- a/player/src/parser/certificateStorage.cpp +++ b/player/src/parser/certificateStorage.cpp @@ -28,11 +28,21 @@ #include "certificateStorage.h" #include +#include +#include #include "defines.h" -CertificateStorage::CertificateStorage() +#include +#include +#include +#include +#include +#include + +CertificateStorage::CertificateStorage(const char *folder) { + m_folder = folder; update(); } @@ -45,9 +55,10 @@ QString CertificateStorage::getCertificateFolder() { QString certificates_folder; #ifdef _WIN32 - certificates_folder = QDir::homePath() + WINP_APP_DATA_ROAMING + COMPANY_NAME + "/" + PRODUCT_NAME + "/" + CERTIFICATES_FOLDER; -#else - certificates_folder = QDir::homePath() + "/." + PRODUCT_NAME + "/" + CERTIFICATES_FOLDER; + certificates_folder = QDir::homePath() + WINP_APP_DATA_ROAMING + COMPANY_NAME + "/" + PRODUCT_NAME + "/" + m_folder; +#endif //WIN32 +#ifdef UNIX + certificates_folder = QDir::homePath() + "/." + PRODUCT_NAME + "/" + m_folder; #endif //UNIX //create it if needed @@ -61,7 +72,8 @@ void CertificateStorage::update() { m_files.clear(); QDir certificate_dir(getCertificateFolder()); - m_files = certificate_dir.entryInfoList(QStringList(BINARY_FILTER), QDir::Files | QDir::Readable, QDir::Name); + auto ext = QString(CERT_FILE_EXTENSIONS).split(';'); + m_files = certificate_dir.entryInfoList(ext, QDir::Files | QDir::Readable, QDir::Name); } void CertificateStorage::removeCertificate(int index) @@ -93,3 +105,150 @@ bool CertificateStorage::isCertificateKnown(const QByteArray& binary_certificate } return false; } + +HexArray CertificateStorage::decryptKey(ProtectionSystemSpecificHeaderBox* box) +{ + HexArray thumbPrint = box->getCertThumbprint(); + HexArray resp; + // + // Lookup cached hashes + // Note: will fail when a file is replaced and encrypted by different key + // + std::map files; + auto hexthumb = QByteArray(thumbPrint).toHex().toStdString().c_str(); + auto hashes = getCertificateFolder() + "/hashes.txt"; + std::string fname; + if (FILE* fd = fopen(hashes.toStdString().c_str(), "r")) { + char line[1024] = {}; + while (fgets(line, sizeof(line), fd)) { + auto del = strchr(line, ' '); + size_t hlen = del - line, len = strlen(line); + if (len > 0 && line[len - 1] == '\n') line[len - 1] = 0; // remove trailing newline + files[del + 1] = true; + if (hlen < sizeof(line) && hlen == thumbPrint.size() * 2 && !memcmp(line, hexthumb, hlen)) { + fname = del + 1; + } + } + fclose(fd); + } + + EVP_PKEY* pkey = 0; + PKCS12* p12 = 0; + if (fname.empty()) { // update hashes if not found + // + // If unkown cert hash search for a new pkcs12 file + // + if (FILE* fd = fopen(hashes.toStdString().c_str(), "a+")) { + for (auto cIter = m_files.constBegin(); cIter != m_files.constEnd(); ++cIter) + { + if (files.find(cIter->fileName().toStdString()) == files.end()) { + if (auto p12_file = fopen(cIter->absoluteFilePath().toStdString().c_str(), "rb")) + { + if (p12) PKCS12_free(p12); + p12 = d2i_PKCS12_fp(p12_file, 0); + fclose(p12_file); + + bool ok{}; + QString text = QInputDialog::getText(0, "Password for " + cIter->fileName(), + "Password:", QLineEdit::Normal, "", &ok); + + EVP_PKEY* pk = 0; + X509* cert = 0; + if (!PKCS12_parse(p12, text.toStdString().c_str(), &pk, &cert, 0)) { + char buf[256]; + fprintf(stdout, "Error parsing PKCS#12 file: %s\n", ERR_error_string(ERR_get_error(), buf)); + } + else { + if (cert) { + const EVP_MD* md = EVP_sha256(); + uint8_t sig[64] = {}; + uint32_t siglen = sizeof(sig); + if (X509_digest(cert, md, sig, &siglen)) { + const char* thumb = QByteArray((char*)sig, siglen).toHex().toStdString().c_str(); + fprintf(fd, "%s %s\n", thumb, cIter->fileName().toStdString().c_str()); + if (siglen == thumbPrint.size() && !memcmp(thumb, thumbPrint.data(), siglen)) { + fname = cIter->fileName().toStdString(); + pkey = pk; // keep key for decryption + pk = 0; + } + } + X509_free(cert); + } + if (pk) EVP_PKEY_free(pk); + } + fclose(p12_file); + } + } + } + fclose(fd); + } + } + + if (pkey == 0) { + auto pfx = (getCertificateFolder() + QString("/") + QString(fname.c_str())).toStdString(); + if (auto p12_file = fopen(pfx.c_str(), "rb")) + { + auto p12 = d2i_PKCS12_fp(p12_file, 0); + + bool ok{}; + QString text = QInputDialog::getText(0, "Password for " + QString(fname.c_str()), + "Password:", QLineEdit::Normal, "", &ok); + X509* cert = 0; + if (!PKCS12_parse(p12, text.toStdString().c_str(), &pkey, &cert, 0) || pkey == 0) { + QMessageBox::warning(0, "Decryption", "Error accessing private key.\n Check password."); + return resp; + } + fclose(p12_file); + } + } + if (pkey == 0) { + QMessageBox::warning(0, "Decryption", QString("Error accessing key ") + QString(fname.c_str())); + return resp; + } + + auto encryptedKey = box->getEncryptedKey(); + switch (box->getEncryptionVersion()) { + case 1: { + char buf[256]; + auto ctx = EVP_PKEY_CTX_new(pkey, NULL); + EVP_PKEY_decrypt_init(ctx); + EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING); + if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, EVP_sha256()) <= 0) + fprintf(stdout, "Error parsing PKCS#12 file: %s\n", ERR_error_string(ERR_get_error(), buf)); + + uint8_t buffer[1024] = {}; + size_t bufsiz = sizeof(buffer); + EVP_PKEY_decrypt(ctx, NULL, &bufsiz, encryptedKey.data(), encryptedKey.size()); + + if (bufsiz <= sizeof(buffer) && EVP_PKEY_decrypt(ctx, buffer, &bufsiz, encryptedKey.data(), encryptedKey.size())) { + resp = HexArray(buffer, bufsiz); + } + else { + fprintf(stdout, "Error parsing PKCS#12 file: %s\n", ERR_error_string(ERR_get_error(), buf)); + } + if (p12) PKCS12_free(p12); + EVP_PKEY_CTX_free(ctx); + if (pkey) EVP_PKEY_free(pkey); + break; + } + case 2: { + OSSL_HPKE_SUITE suite = { box->getHPKE_KEM(), box->getHPKE_KDF(), box->getHPKE_AEAD() }; + OSSL_HPKE_CTX* ctx = OSSL_HPKE_CTX_new(OSSL_HPKE_MODE_BASE, suite, OSSL_HPKE_ROLE_RECEIVER, NULL, NULL); + + uint8_t buffer[256] = {}; + size_t bufsiz = sizeof(buffer); + if (ctx) { + OSSL_HPKE_decap(ctx, box->getSharedSecret().data(), box->getSharedSecret().size(), pkey, box->getInfo().data(), box->getInfo().size()); + if (OSSL_HPKE_open(ctx, buffer, &bufsiz, NULL, 0, encryptedKey.data(), encryptedKey.size())) { + resp = HexArray(buffer, bufsiz); + } + } + OSSL_HPKE_CTX_free(ctx); + break; + } + default: + QMessageBox::warning(0, "Decryption", QString("Encryption version not supported")); + break; + } + return resp; +} diff --git a/player/src/parser/certificateStorage.h b/player/src/parser/certificateStorage.h index 5663996..54bbae4 100644 --- a/player/src/parser/certificateStorage.h +++ b/player/src/parser/certificateStorage.h @@ -29,17 +29,19 @@ #define CERTIFICATESTORAGE_H #include +#include "helpers/hexarray.hpp" +#include "ProtectionSystemSpecificHeaderBox.hpp" //! Certificate storage. class CertificateStorage { public: - CertificateStorage(); + CertificateStorage(const char *folder); ~CertificateStorage(); //! Get folders with certificate. - static QString getCertificateFolder(); + QString getCertificateFolder(); //! Update certificate storage. void update(); @@ -53,9 +55,13 @@ class CertificateStorage //! Check if certificate known. bool isCertificateKnown(const QByteArray& binary_certificate); + //! Try to load pkcs12 file and decrypt the key + HexArray decryptKey(ProtectionSystemSpecificHeaderBox* box); + private: //! List of files QFileInfoList m_files; + QString m_folder; }; #endif // CERTIFICATESTORAGE_H diff --git a/player/src/parser/consistencyChecker.cpp b/player/src/parser/consistencyChecker.cpp index 2007d3e..ed16a64 100644 --- a/player/src/parser/consistencyChecker.cpp +++ b/player/src/parser/consistencyChecker.cpp @@ -55,6 +55,7 @@ #include "trackHeaderBox.hpp" #include "trackRunBox.hpp" #include "correctstarttimebox.hpp" +#include "protectionSystemSpecificHeaderBox.hpp" ConsistencyChecker::ConsistencyChecker() : m_current_box(nullptr) @@ -89,7 +90,8 @@ ConsistencyChecker::ConsistencyChecker() this->zeroOrOneCheck(); this->zeroOrOneCheck(); this->zeroOrOneCheck(); - }); + this->zeroOrOneCheck(); + }); registerChecker( [this] () { this->exactlyOneCheck(); diff --git a/player/src/parser/helpers/hexarray.hpp b/player/src/parser/helpers/hexarray.hpp new file mode 100644 index 0000000..0012bdf --- /dev/null +++ b/player/src/parser/helpers/hexarray.hpp @@ -0,0 +1,42 @@ +/************************************************************************************ +* Copyright (c) 2013 ONVIF. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of ONVIF nor the names of its contributors may be +* used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL ONVIF BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +************************************************************************************/ + +#ifndef HELPERS_HEXARRAY_H +#define HELPERS_HEXARRAY_H + +#include +//! Union describing uint24_t data type. +class HexArray : public std::vector +{ +public: + HexArray() {} + HexArray(uint8_t *data, size_t size) : std::vector(data, data + size) {} + +}; + + +#endif // HELPERS_UINT24_H diff --git a/player/src/parser/helpers/property.hpp b/player/src/parser/helpers/property.hpp index 010ec70..8e55795 100644 --- a/player/src/parser/helpers/property.hpp +++ b/player/src/parser/helpers/property.hpp @@ -47,6 +47,7 @@ #include "endian.hpp" #include "uint24.hpp" #include "optional.hpp" +#include "hexarray.hpp" //! Helper class, allowing to build trees of string values. Used for exporting box properties in human readable format. Uses curiosly recurring template pattern. class Property CC_CXX11_FINAL @@ -114,6 +115,12 @@ class Property CC_CXX11_FINAL m_string = (QString)value; } + //! Converts HexArray to strings. + inline void convert(HexArray value) + { + m_string = QByteArray((char*)value.data(), value.size()).toHex(); + } + //! Converts DateTime values to strings. inline void convert(QDateTime value) { diff --git a/player/src/parser/protectionSystemSpecificHeaderBox.hpp b/player/src/parser/protectionSystemSpecificHeaderBox.hpp new file mode 100644 index 0000000..c902916 --- /dev/null +++ b/player/src/parser/protectionSystemSpecificHeaderBox.hpp @@ -0,0 +1,168 @@ +/************************************************************************************ +* Copyright (c) 2013 ONVIF. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of ONVIF nor the names of its contributors may be +* used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL ONVIF BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +************************************************************************************/ + +#ifndef PSSH_BOX_H +#define PSSH_BOX_H + +#include "basic/fullBox.hpp" +#include +#include + +typedef uint8_t KidType[16]; + +//! Class describing Surveillance Export box. +/*! + * \brief Declared in ISO 23001-7 and Onvif Recording Encryption. \n + * Type: 'pssh' \n + * Container: moov, file level \n + * Mandatory: No \n + * Quantity: Zero or one + */ +class ProtectionSystemSpecificHeaderBox + : public FullBox +{ +public: + typedef FullBox FullBoxType; + +public: + explicit ProtectionSystemSpecificHeaderBox(ChildrenMixin * parent = nullptr) + : FullBoxType(parent) + {} + +public: + //! Reads the box from the input stream. + virtual void initialize(LimitedStreamReader &stream) + { + FullBoxType::initialize(stream); + readContents(stream); + } + +public: + QUuid getSystemID() { return std::get<2>(FullBoxType::m_data); } + uint32_t getKID_count() { return std::get<3>(FullBoxType::m_data); } + HexArray getKID() { return HexArray(m_kid, sizeof(m_kid)); } + uint32_t getDataSize() { return m_dataSize; } + uint32_t getEntries() { return m_entries; } + uint16_t getCertThumbprintAlg() { return m_thumbAlg; } + HexArray getCertThumbprint() { return HexArray(m_thumb, m_thumbSize); } + uint16_t getEncryptionVersion() { return m_encVersion; } + HexArray getEncryptedKey() { return HexArray(m_encKey, m_encDataSize); } + uint16_t getHPKE_KEM() { return m_hpke[0]; } + uint16_t getHPKE_KDF() { return m_hpke[1]; } + uint16_t getHPKE_AEAD() { return m_hpke[2]; } + uint16_t getSharedSecretSize() { + switch (m_hpke[0]) { + case OSSL_HPKE_KEM_ID_P256: return 65; + case OSSL_HPKE_KEM_ID_P384: return 97; + case OSSL_HPKE_KEM_ID_P521: return 209; + case OSSL_HPKE_KEM_ID_X25519: return 32; + default: return 0; + } + } + HexArray getSharedSecret() { return HexArray(m_sharedSecret, getSharedSecretSize()); } + std::vector getInfo() { return m_info; } + +public: + BOX_INFO("pssh", "Protection System Specific Header Box") + +protected: + //! Registers properties for a box type. + virtual void registerProperties() override + { + FullBoxType::registerProperties(); + BOX_PROPERTY(SystemID); + BOX_PROPERTY(KID_count); + BOX_PROPERTY(KID); + BOX_PROPERTY(DataSize); + BOX_PROPERTY(Entries); + BOX_PROPERTY(CertThumbprintAlg); + BOX_PROPERTY(CertThumbprint); + BOX_PROPERTY(EncryptionVersion); + BOX_PROPERTY(EncryptedKey); + BOX_PROPERTY(HPKE_KEM); + BOX_PROPERTY(HPKE_KDF); + BOX_PROPERTY(HPKE_AEAD); + BOX_PROPERTY(SharedSecret); + } + +private: + //! Reads the lists of video and audio entries from the stream. + void readContents(LimitedStreamReader &stream) + { +// int version = getVersion(); + stream.read(m_kid, 16); + stream.read(m_dataSize); + stream.read(m_entries); + stream.read(m_thumbAlg); + stream.read(m_thumbSize); + stream.read(m_thumb, m_thumbSize); + stream.read(m_encVersion); + if (m_encVersion == 1) { + stream.read(m_encDataSize); + if (m_encDataSize <= sizeof(m_encKey)) { + stream.read(m_encKey, m_encDataSize); + } + } + else if (m_encVersion == 2) { + stream.read(m_hpke[0]); + stream.read(m_hpke[1]); + stream.read(m_hpke[2]); + int sharedSecretSize = getSharedSecretSize(); + if (sharedSecretSize <= sizeof(m_sharedSecret)) { + stream.read(m_sharedSecret, sharedSecretSize); + } + stream.read(m_encDataSize); + if (m_encDataSize <= sizeof(m_encKey)) { + stream.read(m_encKey, m_encDataSize); + } + if (stream.getCurrentOffset() < stream.getFinalOffset()) { + uint16_t size; + stream.read(size); + if (size > 0) { + m_info.resize(size); + stream.read(m_info.data(), size); + } + } + } + } + +private: + uint8_t m_kid[16]; + uint32_t m_dataSize; + uint32_t m_entries; + uint16_t m_thumbAlg; + uint32_t m_thumbSize; + uint8_t m_thumb[128]; + uint8_t m_encVersion{}; + uint16_t m_encDataSize{}; + uint8_t m_encKey[1024]; + uint16_t m_hpke[3]{}; + uint8_t m_sharedSecret[128]{}; + std::vector m_info; +}; + +#endif // SURVIELANCE_EXPORT_BOX_H diff --git a/player/src/parser/segmentExtractor.cpp b/player/src/parser/segmentExtractor.cpp index a8a7c86..8fd5e82 100644 --- a/player/src/parser/segmentExtractor.cpp +++ b/player/src/parser/segmentExtractor.cpp @@ -115,5 +115,8 @@ void SegmentExtractor::onBoxCreated(Box *box) case 'mdhd': m_segments.back().read(dynamic_cast(box)); break; + case 'pssh': + m_segments.back().read(dynamic_cast(box)); + break; } } diff --git a/player/src/parserUI/certificateStorageDialog.cpp b/player/src/parserUI/certificateStorageDialog.cpp index 7e1ad9c..948f328 100644 --- a/player/src/parserUI/certificateStorageDialog.cpp +++ b/player/src/parserUI/certificateStorageDialog.cpp @@ -35,11 +35,14 @@ #include "defines.h" -CertificateStorageDialog::CertificateStorageDialog(QWidget* parent) : +CertificateStorageDialog::CertificateStorageDialog(QWidget* parent, const char* name, const char* extensions, const char *folder) : QDialog(parent), - m_ui(new Ui::CertificateStorageDialog) + m_ui(new Ui::CertificateStorageDialog), + m_storage(folder) { m_ui->setupUi(this); + setWindowTitle(name); + m_extensions = extensions; Qt::WindowFlags flags = this->windowFlags(); flags &= ~(Qt::WindowContextHelpButtonHint); @@ -80,13 +83,13 @@ void CertificateStorageDialog::onAdd() QSettings settings(QDir::homePath() + "/." + PRODUCT_NAME + "/" + CONFIG_FILE_NAME, QSettings::IniFormat); #endif //UNIX - QString file_name = QFileDialog::getOpenFileName(this, tr("Add certificate file"), settings.value("lastOpenedCertificateFolder", QDir::homePath()).toString(), BINARY_FORMAT); + QString file_name = QFileDialog::getOpenFileName(this, tr("Add certificate file"), settings.value("lastOpenedCertificateFolder", QDir::homePath()).toString(), m_extensions); if(file_name.isEmpty()) return; settings.setValue("lastOpenedCertificateFolder", QFileInfo(file_name).absolutePath()); - if(QFile::copy(file_name, CertificateStorage::getCertificateFolder() + "/" + QFileInfo(file_name).fileName())) + if(QFile::copy(file_name, m_storage.getCertificateFolder() + "/" + QFileInfo(file_name).fileName())) fillFilesList(); } diff --git a/player/src/parserUI/certificateStorageDialog.h b/player/src/parserUI/certificateStorageDialog.h index 37335a9..dfeaa1f 100644 --- a/player/src/parserUI/certificateStorageDialog.h +++ b/player/src/parserUI/certificateStorageDialog.h @@ -43,7 +43,7 @@ class CertificateStorageDialog : public QDialog Q_OBJECT public: - explicit CertificateStorageDialog(QWidget* parent = 0); + explicit CertificateStorageDialog(QWidget* parent, const char *name, const char *extensions, const char *folder); ~CertificateStorageDialog(); @@ -63,6 +63,7 @@ private slots: Ui::CertificateStorageDialog* m_ui; //! Certificate storage. CertificateStorage m_storage; + QString m_extensions; }; #endif // CERTIFICATESTORAGEDIALOG_H diff --git a/player/src/parserUI/certificateStorageDialog.ui b/player/src/parserUI/certificateStorageDialog.ui index 183c6a2..142ff99 100644 --- a/player/src/parserUI/certificateStorageDialog.ui +++ b/player/src/parserUI/certificateStorageDialog.ui @@ -1,7 +1,7 @@ CertificateStorageDialog - + 0 diff --git a/player/src/parserUI/verifyerdialog.cpp b/player/src/parserUI/verifyerdialog.cpp index ac31ddb..971f65c 100644 --- a/player/src/parserUI/verifyerdialog.cpp +++ b/player/src/parserUI/verifyerdialog.cpp @@ -218,7 +218,7 @@ void VerifyerDialog::onSignatureItemClicked(QTreeWidgetItem * item, int column) void VerifyerDialog::onCertificateActionClicked(void) { - QString filter = BINARY_FORMAT; + QString filter = CERT_FORMAT_EXTENSIONS; QString dir; #ifdef _WIN32 dir = QDir::homePath() + WINP_APP_DATA_ROAMING + COMPANY_NAME + "/" + PRODUCT_NAME + "/" + CERTIFICATES_FOLDER + "/"; @@ -232,7 +232,7 @@ void VerifyerDialog::onCertificateActionClicked(void) save_file_dialog.setDirectory(dir); save_file_dialog.setOptions(QFileDialog::DontUseNativeDialog); save_file_dialog.setWindowTitle(tr("Save certificate to file...")); - save_file_dialog.setNameFilter(QString(BINARY_FORMAT) + QString(";;") + QString(BASE64_FORMAT)); + save_file_dialog.setNameFilter(QString(CERT_FORMAT_EXTENSIONS) + QString(";;") + QString(BASE64_FORMAT)); save_file_dialog.selectFile(m_verifier->getCertificateIssuer()); QString fileName; if(save_file_dialog.exec() == QDialog::Accepted && @@ -247,7 +247,7 @@ void VerifyerDialog::onCertificateActionClicked(void) QFileInfo fi(fileName); if ( (fi.suffix()).isEmpty() ) // add selected filter { - if(filter == BINARY_FORMAT) + if(filter == CERT_FORMAT_EXTENSIONS) fileName.append(BINARY_EXT); else fileName.append(BASE64_EXT); @@ -371,7 +371,7 @@ void VerifyerDialog::onOperationCompleted(VerificationStatus result) ver_struct.m_tree_item->setTextAlignment(2, Qt::AlignVCenter | Qt::AlignHCenter); // certificate - bool certificate_known = CertificateStorage().isCertificateKnown(m_verifier->getBinaryCertificate()); + bool certificate_known = CertificateStorage(CERTIFICATES_FOLDER).isCertificateKnown(m_verifier->getBinaryCertificate()); ver_struct.m_tree_item->setText(3, certificate_known ? tr("Known") : tr("Unknown")); ver_struct.m_tree_item->setBackground(3, certificate_known ? QColor(green) : QColor(yellow)); ver_struct.m_tree_item->setTextAlignment(3, Qt::AlignVCenter | Qt::AlignHCenter); diff --git a/player/src/player/controller.cpp b/player/src/player/controller.cpp index 72852fe..0ea5ff3 100644 --- a/player/src/player/controller.cpp +++ b/player/src/player/controller.cpp @@ -55,7 +55,8 @@ Controller::Controller(Engine& engine, QObject::connect(&m_player_widget, SIGNAL(changeAudioStream(int)), this, SLOT(onAudioStreamIndexChanged(int))); QObject::connect(&m_player_widget, SIGNAL(showFileStructure()), this, SLOT(showFileStructure())); QObject::connect(&m_player_widget, SIGNAL(verifyFileSignature()), this, SLOT(verifyFileSignature())); - QObject::connect(&m_player_widget, SIGNAL(openCertificateStorage()), this, SLOT(openCertificateStorage())); + QObject::connect(&m_player_widget, SIGNAL(openTrustedCertificate()), this, SLOT(openTrustedCertificate())); + QObject::connect(&m_player_widget, SIGNAL(openKeyStore()), this, SLOT(openKeyStore())); QObject::connect(&m_player_widget, SIGNAL(exit()), this, SLOT(exit())); QObject::connect(&m_player_widget, SIGNAL(showLocalTimeChanged(bool)), this, SLOT(onshowLocalTimeChanged(bool))); @@ -167,9 +168,15 @@ void Controller::verifyFileSignature() m_verifyer_dialog.show(); } -void Controller::openCertificateStorage() +void Controller::openTrustedCertificate() { - CertificateStorageDialog certificate_storage_dialog(&m_player_widget); + CertificateStorageDialog certificate_storage_dialog(&m_player_widget, "Trusted Certificate Authorities", "Certificates (*.der;*.cer;*.crt)", CERTIFICATES_FOLDER); + certificate_storage_dialog.exec(); +} + +void Controller::openKeyStore() +{ + CertificateStorageDialog certificate_storage_dialog(&m_player_widget, "Decryption Keys", "PKCS12 (*.pfx)", KEYSTORE_FOLDER); certificate_storage_dialog.exec(); } diff --git a/player/src/player/controller.h b/player/src/player/controller.h index c44e1f8..a302514 100644 --- a/player/src/player/controller.h +++ b/player/src/player/controller.h @@ -64,8 +64,11 @@ private slots: void verifyFileSignature(); //! This slot will be called when we want to work with certificates. - void openCertificateStorage(); + void openTrustedCertificate(); + //! Open key storage. + void openKeyStore(); + //! This slot will be called when new Exit selected in menu. void exit(); diff --git a/player/src/player/engine.cpp b/player/src/player/engine.cpp index 06c8b8a..2e04b36 100644 --- a/player/src/player/engine.cpp +++ b/player/src/player/engine.cpp @@ -249,9 +249,9 @@ bool Engine::initDecoders(const QString& file_name, SegmentInfo *segment) { bool res = true; - res = res && m_video_decoder.open(file_name); - res = res && m_audio_decoder.open(file_name); - res = res && m_metadata_decoder.open(file_name); + res = res && m_video_decoder.open(file_name, segment); + res = res && m_audio_decoder.open(file_name, segment); + res = res && m_metadata_decoder.open(file_name, segment); res = res && m_video_decoder.getStreamsCount(); m_video_decoder.m_context.m_segment = segment; m_metadata_decoder.m_context.m_segment = segment; diff --git a/player/src/player/streamReader.cpp b/player/src/player/streamReader.cpp index df1d743..2bf47ff 100644 --- a/player/src/player/streamReader.cpp +++ b/player/src/player/streamReader.cpp @@ -41,10 +41,10 @@ StreamReader::~StreamReader() clear(); } -bool StreamReader::open(const QString& file_name, const QSet& valid_streams) +bool StreamReader::open(const QString& file_name, const SegmentInfo* segment, const QSet& valid_streams) { clear(); - if(!init(file_name, valid_streams)) + if(!init(file_name, segment, valid_streams)) { clear(); return false; @@ -99,13 +99,17 @@ bool StreamReader::seek(int timestamp_ms) return true; } -bool StreamReader::init(const QString& file_name, const QSet& valid_streams) +bool StreamReader::init(const QString& file_name, const SegmentInfo* info, const QSet& valid_streams) { if(file_name.isEmpty() || !QFile::exists(file_name)) return false; - if(avformat_open_input(&m_format_context, file_name.toUtf8().data(), 0, 0) != 0) + AVDictionary* format_opts = NULL; + if (info->m_key.size()) { + av_dict_set(&format_opts, "decryption_key", QByteArray(info->m_key).toHex().toStdString().c_str(), 0); + } + if (avformat_open_input(&m_format_context, file_name.toUtf8().data(), 0, &format_opts) != 0) return false; if(avformat_find_stream_info(m_format_context, 0) < 0) diff --git a/player/src/player/streamReader.h b/player/src/player/streamReader.h index cd80ae3..a0894af 100644 --- a/player/src/player/streamReader.h +++ b/player/src/player/streamReader.h @@ -29,6 +29,7 @@ #define MAINCONTEXT_H #include "crosscompilation_cxx11.h" +#include "segmentInfo.h" #include "ffmpeg.h" @@ -50,7 +51,7 @@ class StreamReader ~StreamReader(); //! Init MainContext with file. - bool open(const QString& file_name, const QSet& valid_streams = QSet()); + bool open(const QString& file_name, const SegmentInfo *info, const QSet& valid_streams = QSet()); //! Clear MainContext; void clear(); @@ -79,7 +80,7 @@ class StreamReader private: //! Init with file. - bool init(const QString& file_name, const QSet& valid_streams = QSet()); + bool init(const QString& file_name, const SegmentInfo *segment, const QSet& valid_streams = QSet()); protected: //! Structure that describes one stream in video file. diff --git a/player/src/playerUI/playerWidget.cpp b/player/src/playerUI/playerWidget.cpp index 8eb9d21..1d1d58c 100644 --- a/player/src/playerUI/playerWidget.cpp +++ b/player/src/playerUI/playerWidget.cpp @@ -53,7 +53,8 @@ PlayerWidget::PlayerWidget(QWidget* parent) : QObject::connect(m_ui->actionOpen, SIGNAL(triggered()), this, SLOT(onOpenFile())); QObject::connect(m_ui->actionFile_structure, SIGNAL(triggered()), this, SIGNAL(showFileStructure())); QObject::connect(m_ui->actionFile_signature, SIGNAL(triggered()), this, SIGNAL(verifyFileSignature())); - QObject::connect(m_ui->actionCertificate_storage, SIGNAL(triggered()), this, SIGNAL(openCertificateStorage())); + QObject::connect(m_ui->actionCertificate_storage, SIGNAL(triggered()), this, SIGNAL(openTrustedCertificate())); + QObject::connect(m_ui->actionKey_storage, SIGNAL(triggered()), this, SIGNAL(openKeyStore())); QObject::connect(m_ui->actionExit, SIGNAL(triggered()), this, SIGNAL(exit())); QObject::connect(m_ui->actionLocalTime, SIGNAL(triggered()), this, SLOT(showLocalTime())); #ifdef MEMORY_INFO diff --git a/player/src/playerUI/playerWidget.h b/player/src/playerUI/playerWidget.h index 500bfa6..d488295 100644 --- a/player/src/playerUI/playerWidget.h +++ b/player/src/playerUI/playerWidget.h @@ -75,7 +75,10 @@ class PlayerWidget : public QMainWindow, public PlayerWidgetInterface void verifyFileSignature(); //! Open certificate storage. - void openCertificateStorage(); + void openTrustedCertificate(); + + //! Open key storage. + void openKeyStore(); //! Change video stream. void changeVideoStream(int index); diff --git a/player/src/playerUI/playerWidget.ui b/player/src/playerUI/playerWidget.ui index fadc883..c4af331 100644 --- a/player/src/playerUI/playerWidget.ui +++ b/player/src/playerUI/playerWidget.ui @@ -165,6 +165,7 @@ + @@ -226,10 +227,15 @@ - Certificate storage + Trusted CAs - + + + Decryption keys + + + true