From 026796fa7f148554ec857f9bfb199fa936e5b64c Mon Sep 17 00:00:00 2001 From: HansBusch <47081128+HansBusch@users.noreply.github.com> Date: Fri, 15 Aug 2025 19:36:00 +0200 Subject: [PATCH 1/9] Add support for PKCS12 key store. --- player/src/common/defines.h | 7 +++++-- player/src/parser/certificateStorage.cpp | 10 ++++++---- player/src/parser/certificateStorage.h | 5 +++-- player/src/parserUI/certificateStorageDialog.cpp | 11 +++++++---- player/src/parserUI/certificateStorageDialog.h | 3 ++- player/src/parserUI/certificateStorageDialog.ui | 2 +- player/src/parserUI/verifyerdialog.cpp | 8 ++++---- player/src/player/controller.cpp | 13 ++++++++++--- player/src/player/controller.h | 5 ++++- player/src/player/streamReader.cpp | 5 ++++- player/src/playerUI/playerWidget.cpp | 3 ++- player/src/playerUI/playerWidget.h | 5 ++++- player/src/playerUI/playerWidget.ui | 10 ++++++++-- 13 files changed, 60 insertions(+), 27 deletions(-) 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/parser/certificateStorage.cpp b/player/src/parser/certificateStorage.cpp index b31465e..b6526ae 100644 --- a/player/src/parser/certificateStorage.cpp +++ b/player/src/parser/certificateStorage.cpp @@ -31,8 +31,9 @@ #include "defines.h" -CertificateStorage::CertificateStorage() +CertificateStorage::CertificateStorage(const char *folder) { + m_folder = folder; update(); } @@ -45,10 +46,10 @@ QString CertificateStorage::getCertificateFolder() { QString certificates_folder; #ifdef WIN32 - certificates_folder = QDir::homePath() + WINP_APP_DATA_ROAMING + COMPANY_NAME + "/" + 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 + "/" + CERTIFICATES_FOLDER; + certificates_folder = QDir::homePath() + "/." + PRODUCT_NAME + "/" + m_folder; #endif //UNIX //create it if needed @@ -62,7 +63,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) diff --git a/player/src/parser/certificateStorage.h b/player/src/parser/certificateStorage.h index 5663996..6452db4 100644 --- a/player/src/parser/certificateStorage.h +++ b/player/src/parser/certificateStorage.h @@ -34,12 +34,12 @@ class CertificateStorage { public: - CertificateStorage(); + CertificateStorage(const char *folder); ~CertificateStorage(); //! Get folders with certificate. - static QString getCertificateFolder(); + QString getCertificateFolder(); //! Update certificate storage. void update(); @@ -56,6 +56,7 @@ class CertificateStorage private: //! List of files QFileInfoList m_files; + QString m_folder; }; #endif // CERTIFICATESTORAGE_H diff --git a/player/src/parserUI/certificateStorageDialog.cpp b/player/src/parserUI/certificateStorageDialog.cpp index e3da1e8..297e27f 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 9c537d6..906ddcf 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 + "/"; @@ -233,7 +233,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 && @@ -248,7 +248,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); @@ -372,7 +372,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/streamReader.cpp b/player/src/player/streamReader.cpp index df1d743..579fcbf 100644 --- a/player/src/player/streamReader.cpp +++ b/player/src/player/streamReader.cpp @@ -105,7 +105,10 @@ bool StreamReader::init(const QString& file_name, const QSet& valid_streams !QFile::exists(file_name)) return false; - if(avformat_open_input(&m_format_context, file_name.toUtf8().data(), 0, 0) != 0) + AVDictionary* format_opts = NULL; + av_dict_set(&format_opts, "decryption_key", "76a6c65c5ea762046bd749a2e632ccbb", 0); + if (avformat_open_input(&m_format_context, file_name.toUtf8().data(), 0, &format_opts) != 0) +// if(avformat_open_input(&m_format_context, file_name.toUtf8().data(), 0, 0) != 0) return false; if(avformat_find_stream_info(m_format_context, 0) < 0) diff --git a/player/src/playerUI/playerWidget.cpp b/player/src/playerUI/playerWidget.cpp index bc45dad..32bdf4a 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 From 5eb0518a9ab8673fe542ac437707c141860cf249 Mon Sep 17 00:00:00 2001 From: HansBusch <47081128+HansBusch@users.noreply.github.com> Date: Tue, 19 Aug 2025 08:25:34 +0200 Subject: [PATCH 2/9] Add prototyping stage RSA pssh box to parser. --- player/src/parser/boxFactory.cpp | 2 + player/src/parser/consistencyChecker.cpp | 4 +- player/src/parser/helpers/hexarray.hpp | 41 ++++++ player/src/parser/helpers/property.hpp | 7 + .../protectionSystemSpecificHeaderBox.hpp | 124 ++++++++++++++++++ 5 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 player/src/parser/helpers/hexarray.hpp create mode 100644 player/src/parser/protectionSystemSpecificHeaderBox.hpp 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/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..4547ed6 --- /dev/null +++ b/player/src/parser/helpers/hexarray.hpp @@ -0,0 +1,41 @@ +/************************************************************************************ +* 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 QByteArray +{ +public: + HexArray(uint8_t *data, size_t size) : QByteArray((char*)data, size) {} + +}; + + +#endif // HELPERS_UINT24_H diff --git a/player/src/parser/helpers/property.hpp b/player/src/parser/helpers/property.hpp index 010ec70..ff3f32b 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 = value.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..acf2bc5 --- /dev/null +++ b/player/src/parser/protectionSystemSpecificHeaderBox.hpp @@ -0,0 +1,124 @@ +/************************************************************************************ +* 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" + +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 getCertThumbprintAlgorithm() { 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); } + +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(CertThumbprintAlgorithm); + BOX_PROPERTY(CertThumbprint); + BOX_PROPERTY(EncryptionVersion); + BOX_PROPERTY(EncryptedKey); + } + +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); + } + } + } + +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]; + uint16_t m_encVersion; + uint32_t m_encDataSize; + uint8_t m_encKey[1024]; +}; + +#endif // SURVIELANCE_EXPORT_BOX_H From 99839706cde11c610db8f9a4fa59e5c50f8d8857 Mon Sep 17 00:00:00 2001 From: HansBusch <47081128+HansBusch@users.noreply.github.com> Date: Tue, 19 Aug 2025 13:38:56 +0200 Subject: [PATCH 3/9] Extract symmetric key from pssh and key store. --- player/src/common/segmentInfo.cpp | 6 ++ player/src/common/segmentInfo.h | 3 + player/src/parser/certificateStorage.cpp | 114 +++++++++++++++++++++++ player/src/parser/certificateStorage.h | 4 + player/src/parser/helpers/hexarray.hpp | 7 +- player/src/parser/helpers/property.hpp | 2 +- player/src/parser/segmentExtractor.cpp | 3 + 7 files changed, 135 insertions(+), 4 deletions(-) diff --git a/player/src/common/segmentInfo.cpp b/player/src/common/segmentInfo.cpp index 2f267cb..a96b1e9 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->getCertThumbprint(), box->getEncryptedKey()); +} + 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/certificateStorage.cpp b/player/src/parser/certificateStorage.cpp index b6526ae..543a374 100644 --- a/player/src/parser/certificateStorage.cpp +++ b/player/src/parser/certificateStorage.cpp @@ -28,9 +28,16 @@ #include "certificateStorage.h" #include +#include #include "defines.h" +#include +#include +#include +#include +#include + CertificateStorage::CertificateStorage(const char *folder) { m_folder = folder; @@ -96,3 +103,110 @@ bool CertificateStorage::isCertificateKnown(const QByteArray& binary_certificate } return false; } + +HexArray CertificateStorage::decryptKey(const HexArray& thumbPrint, const HexArray& encryptedKey) +{ + 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; + 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")) + { + auto 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); + PKCS12_free(p12); + } + 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); + fclose(p12_file); + + bool ok{}; + QString text = QInputDialog::getText(0, "Password for " + QString(fname.c_str()), + "Password:", QLineEdit::Normal, "", &ok); + + if (!PKCS12_parse(p12, text.toStdString().c_str(), &pkey, 0, 0)) { + char buf[256]; + fprintf(stdout, "Error parsing PKCS#12 file: %s\n", ERR_error_string(ERR_get_error(), buf)); + return resp; + } + PKCS12_free(p12); + } + } + auto ctx = EVP_PKEY_CTX_new(pkey, NULL); + EVP_PKEY_decrypt_init(ctx); + EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING); + uint8_t buffer[32]; + size_t bufsiz = sizeof(buffer); + if (EVP_PKEY_decrypt(ctx, buffer, &bufsiz, encryptedKey.data(), encryptedKey.size())) { + resp = HexArray(buffer, bufsiz); + } + + return resp; +} diff --git a/player/src/parser/certificateStorage.h b/player/src/parser/certificateStorage.h index 6452db4..a0c7755 100644 --- a/player/src/parser/certificateStorage.h +++ b/player/src/parser/certificateStorage.h @@ -29,6 +29,7 @@ #define CERTIFICATESTORAGE_H #include +#include "helpers/hexarray.hpp" //! Certificate storage. class CertificateStorage @@ -53,6 +54,9 @@ class CertificateStorage //! Check if certificate known. bool isCertificateKnown(const QByteArray& binary_certificate); + //! Try to load pkcs12 file and decrypt the key + HexArray decryptKey(const HexArray &thumbPrint, const HexArray &encryptedKey); + private: //! List of files QFileInfoList m_files; diff --git a/player/src/parser/helpers/hexarray.hpp b/player/src/parser/helpers/hexarray.hpp index 4547ed6..0012bdf 100644 --- a/player/src/parser/helpers/hexarray.hpp +++ b/player/src/parser/helpers/hexarray.hpp @@ -28,12 +28,13 @@ #ifndef HELPERS_HEXARRAY_H #define HELPERS_HEXARRAY_H -#include +#include //! Union describing uint24_t data type. -class HexArray : public QByteArray +class HexArray : public std::vector { public: - HexArray(uint8_t *data, size_t size) : QByteArray((char*)data, size) {} + HexArray() {} + HexArray(uint8_t *data, size_t size) : std::vector(data, data + size) {} }; diff --git a/player/src/parser/helpers/property.hpp b/player/src/parser/helpers/property.hpp index ff3f32b..8e55795 100644 --- a/player/src/parser/helpers/property.hpp +++ b/player/src/parser/helpers/property.hpp @@ -118,7 +118,7 @@ class Property CC_CXX11_FINAL //! Converts HexArray to strings. inline void convert(HexArray value) { - m_string = value.toHex(); + m_string = QByteArray((char*)value.data(), value.size()).toHex(); } //! Converts DateTime values to strings. 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; } } From 98e0d0a8c95fceb3df25ff96c6eafa8c47bc6dfd Mon Sep 17 00:00:00 2001 From: HansBusch <47081128+HansBusch@users.noreply.github.com> Date: Tue, 19 Aug 2025 15:27:26 +0200 Subject: [PATCH 4/9] Increase decrypt buffer size to please openssl. --- player/src/parser/certificateStorage.cpp | 29 +++++++++++++++++------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/player/src/parser/certificateStorage.cpp b/player/src/parser/certificateStorage.cpp index 543a374..3598577 100644 --- a/player/src/parser/certificateStorage.cpp +++ b/player/src/parser/certificateStorage.cpp @@ -130,6 +130,7 @@ HexArray CertificateStorage::decryptKey(const HexArray& thumbPrint, const HexArr } 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 @@ -140,7 +141,8 @@ HexArray CertificateStorage::decryptKey(const HexArray& thumbPrint, const HexArr if (files.find(cIter->fileName().toStdString()) == files.end()) { if (auto p12_file = fopen(cIter->absoluteFilePath().toStdString().c_str(), "rb")) { - auto p12 = d2i_PKCS12_fp(p12_file, 0); + if (p12) PKCS12_free(p12); + p12 = d2i_PKCS12_fp(p12_file, 0); fclose(p12_file); bool ok{}; @@ -170,7 +172,6 @@ HexArray CertificateStorage::decryptKey(const HexArray& thumbPrint, const HexArr X509_free(cert); } if (pk) EVP_PKEY_free(pk); - PKCS12_free(p12); } fclose(p12_file); } @@ -185,28 +186,40 @@ HexArray CertificateStorage::decryptKey(const HexArray& thumbPrint, const HexArr if (auto p12_file = fopen(pfx.c_str(), "rb")) { auto p12 = d2i_PKCS12_fp(p12_file, 0); - fclose(p12_file); bool ok{}; QString text = QInputDialog::getText(0, "Password for " + QString(fname.c_str()), "Password:", QLineEdit::Normal, "", &ok); - - if (!PKCS12_parse(p12, text.toStdString().c_str(), &pkey, 0, 0)) { + X509* cert = 0; + if (!PKCS12_parse(p12, text.toStdString().c_str(), &pkey, &cert, 0) || pkey == 0) { char buf[256]; fprintf(stdout, "Error parsing PKCS#12 file: %s\n", ERR_error_string(ERR_get_error(), buf)); return resp; } - PKCS12_free(p12); + fclose(p12_file); } } + + 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); - uint8_t buffer[32]; + 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); - if (EVP_PKEY_decrypt(ctx, buffer, &bufsiz, encryptedKey.data(), encryptedKey.size())) { + 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); return resp; } From fd2222391b3c05126f03949ffd1c04c565bbe669 Mon Sep 17 00:00:00 2001 From: HansBusch <47081128+HansBusch@users.noreply.github.com> Date: Tue, 19 Aug 2025 17:59:13 +0200 Subject: [PATCH 5/9] Set decryption key to ffmpeg reader. --- player/src/player/engine.cpp | 6 +++--- player/src/player/streamReader.cpp | 11 ++++++----- player/src/player/streamReader.h | 5 +++-- 3 files changed, 12 insertions(+), 10 deletions(-) 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 579fcbf..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,16 +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; AVDictionary* format_opts = NULL; - av_dict_set(&format_opts, "decryption_key", "76a6c65c5ea762046bd749a2e632ccbb", 0); + 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) -// if(avformat_open_input(&m_format_context, file_name.toUtf8().data(), 0, 0) != 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. From afce693016f08e00c9771a920851dc4d458f7f06 Mon Sep 17 00:00:00 2001 From: HansBusch <47081128+HansBusch@users.noreply.github.com> Date: Thu, 21 Aug 2025 13:26:53 +0200 Subject: [PATCH 6/9] Add support HPKE. --- player/src/common/segmentInfo.cpp | 2 +- player/src/parser/certificateStorage.cpp | 73 +++++++++++++------ player/src/parser/certificateStorage.h | 3 +- .../protectionSystemSpecificHeaderBox.hpp | 45 ++++++++++-- 4 files changed, 94 insertions(+), 29 deletions(-) diff --git a/player/src/common/segmentInfo.cpp b/player/src/common/segmentInfo.cpp index a96b1e9..a6712bc 100644 --- a/player/src/common/segmentInfo.cpp +++ b/player/src/common/segmentInfo.cpp @@ -60,7 +60,7 @@ void SegmentInfo::read(MediaHeaderBox*box) void SegmentInfo::read(ProtectionSystemSpecificHeaderBox* box) { - m_key = CertificateStorage(KEYSTORE_FOLDER).decryptKey(box->getCertThumbprint(), box->getEncryptedKey()); + m_key = CertificateStorage(KEYSTORE_FOLDER).decryptKey(box); } void SegmentInfo::readAfIdentificationBox(AFIdentificationBox *box) diff --git a/player/src/parser/certificateStorage.cpp b/player/src/parser/certificateStorage.cpp index 3598577..6ecdf62 100644 --- a/player/src/parser/certificateStorage.cpp +++ b/player/src/parser/certificateStorage.cpp @@ -29,9 +29,11 @@ #include #include +#include #include "defines.h" +#include #include #include #include @@ -104,8 +106,9 @@ bool CertificateStorage::isCertificateKnown(const QByteArray& binary_certificate return false; } -HexArray CertificateStorage::decryptKey(const HexArray& thumbPrint, const HexArray& encryptedKey) +HexArray CertificateStorage::decryptKey(ProtectionSystemSpecificHeaderBox* box) { + HexArray thumbPrint = box->getCertThumbprint(); HexArray resp; // // Lookup cached hashes @@ -192,34 +195,60 @@ HexArray CertificateStorage::decryptKey(const HexArray& thumbPrint, const HexArr "Password:", QLineEdit::Normal, "", &ok); X509* cert = 0; if (!PKCS12_parse(p12, text.toStdString().c_str(), &pkey, &cert, 0) || pkey == 0) { - char buf[256]; - fprintf(stdout, "Error parsing PKCS#12 file: %s\n", ERR_error_string(ERR_get_error(), buf)); + 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; + } - 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); + 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; } - else { - fprintf(stdout, "Error parsing PKCS#12 file: %s\n", ERR_error_string(ERR_get_error(), buf)); + 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, NULL, 0); + 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; } - if (p12) PKCS12_free(p12); - EVP_PKEY_CTX_free(ctx); - if (pkey) EVP_PKEY_free(pkey); - return resp; } diff --git a/player/src/parser/certificateStorage.h b/player/src/parser/certificateStorage.h index a0c7755..54bbae4 100644 --- a/player/src/parser/certificateStorage.h +++ b/player/src/parser/certificateStorage.h @@ -30,6 +30,7 @@ #include #include "helpers/hexarray.hpp" +#include "ProtectionSystemSpecificHeaderBox.hpp" //! Certificate storage. class CertificateStorage @@ -55,7 +56,7 @@ class CertificateStorage bool isCertificateKnown(const QByteArray& binary_certificate); //! Try to load pkcs12 file and decrypt the key - HexArray decryptKey(const HexArray &thumbPrint, const HexArray &encryptedKey); + HexArray decryptKey(ProtectionSystemSpecificHeaderBox* box); private: //! List of files diff --git a/player/src/parser/protectionSystemSpecificHeaderBox.hpp b/player/src/parser/protectionSystemSpecificHeaderBox.hpp index acf2bc5..449a69c 100644 --- a/player/src/parser/protectionSystemSpecificHeaderBox.hpp +++ b/player/src/parser/protectionSystemSpecificHeaderBox.hpp @@ -29,6 +29,7 @@ #define PSSH_BOX_H #include "basic/fullBox.hpp" +#include typedef uint8_t KidType[16]; @@ -65,10 +66,23 @@ class ProtectionSystemSpecificHeaderBox HexArray getKID() { return HexArray(m_kid, sizeof(m_kid)); } uint32_t getDataSize() { return m_dataSize; } uint32_t getEntries() { return m_entries; } - uint16_t getCertThumbprintAlgorithm() { return m_thumbAlg; } + 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()); } public: BOX_INFO("pssh", "Protection System Specific Header Box") @@ -83,10 +97,14 @@ class ProtectionSystemSpecificHeaderBox BOX_PROPERTY(KID); BOX_PROPERTY(DataSize); BOX_PROPERTY(Entries); - BOX_PROPERTY(CertThumbprintAlgorithm); + 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: @@ -107,6 +125,21 @@ class ProtectionSystemSpecificHeaderBox 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); + } + uint16_t u16; + stream.read(u16); + m_encDataSize = u16; + if (m_encDataSize <= sizeof(m_encKey)) { + stream.read(m_encKey, m_encDataSize); + } + } } private: @@ -116,9 +149,11 @@ class ProtectionSystemSpecificHeaderBox uint16_t m_thumbAlg; uint32_t m_thumbSize; uint8_t m_thumb[128]; - uint16_t m_encVersion; - uint32_t m_encDataSize; + uint8_t m_encVersion{}; + uint32_t m_encDataSize{}; uint8_t m_encKey[1024]; -}; + uint16_t m_hpke[3]{}; + uint8_t m_sharedSecret[128]{}; +}; #endif // SURVIELANCE_EXPORT_BOX_H From f3d8b04c2f642b7a38a09d61f8e2a40c559273b8 Mon Sep 17 00:00:00 2001 From: HansBusch <47081128+HansBusch@users.noreply.github.com> Date: Tue, 26 Aug 2025 16:17:55 +0200 Subject: [PATCH 7/9] Unify encrypted key size according to spec update. --- player/src/parser/protectionSystemSpecificHeaderBox.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/player/src/parser/protectionSystemSpecificHeaderBox.hpp b/player/src/parser/protectionSystemSpecificHeaderBox.hpp index 449a69c..1d50bc6 100644 --- a/player/src/parser/protectionSystemSpecificHeaderBox.hpp +++ b/player/src/parser/protectionSystemSpecificHeaderBox.hpp @@ -133,9 +133,7 @@ class ProtectionSystemSpecificHeaderBox if (sharedSecretSize <= sizeof(m_sharedSecret)) { stream.read(m_sharedSecret, sharedSecretSize); } - uint16_t u16; - stream.read(u16); - m_encDataSize = u16; + stream.read(m_encDataSize); if (m_encDataSize <= sizeof(m_encKey)) { stream.read(m_encKey, m_encDataSize); } @@ -150,7 +148,7 @@ class ProtectionSystemSpecificHeaderBox uint32_t m_thumbSize; uint8_t m_thumb[128]; uint8_t m_encVersion{}; - uint32_t m_encDataSize{}; + uint16_t m_encDataSize{}; uint8_t m_encKey[1024]; uint16_t m_hpke[3]{}; uint8_t m_sharedSecret[128]{}; From ba3baed8303e07c746f649eeb247ec96ab787831 Mon Sep 17 00:00:00 2001 From: HansBusch <47081128+HansBusch@users.noreply.github.com> Date: Tue, 3 Mar 2026 06:24:21 +0100 Subject: [PATCH 8/9] Update to ffmpeg 8.0.1 and include CMAKE folder. --- player/CMake/Default.cmake | 65 +++++++++ player/CMake/DefaultCXX.cmake | 12 ++ player/CMake/DefaultFortran.cmake | 12 ++ player/CMake/Utils.cmake | 234 ++++++++++++++++++++++++++++++ player/conanfile.txt | 2 +- 5 files changed, 324 insertions(+), 1 deletion(-) create mode 100644 player/CMake/Default.cmake create mode 100644 player/CMake/DefaultCXX.cmake create mode 100644 player/CMake/DefaultFortran.cmake create mode 100644 player/CMake/Utils.cmake diff --git a/player/CMake/Default.cmake b/player/CMake/Default.cmake new file mode 100644 index 0000000..70bfa90 --- /dev/null +++ b/player/CMake/Default.cmake @@ -0,0 +1,65 @@ +################################################################################ +# Command for variable_watch. This command issues error message, if a variable +# is changed. If variable PROPERTY_READER_GUARD_DISABLED is TRUE nothing happens +# variable_watch( property_reader_guard) +################################################################################ +function(property_reader_guard VARIABLE ACCESS VALUE CURRENT_LIST_FILE STACK) + if("${PROPERTY_READER_GUARD_DISABLED}") + return() + endif() + + if("${ACCESS}" STREQUAL "MODIFIED_ACCESS") + message(FATAL_ERROR + " Variable ${VARIABLE} is not supposed to be changed.\n" + " It is used only for reading target property ${VARIABLE}.\n" + " Use\n" + " set_target_properties(\"\" PROPERTIES \"${VARIABLE}\" \"\")\n" + " or\n" + " set_target_properties(\"\" PROPERTIES \"${VARIABLE}_\" \"\")\n" + " instead.\n") + endif() +endfunction() + +################################################################################ +# Create variable with generator expression that expands to value of +# target property _. If property is empty or not set then property +# is used instead. Variable has watcher property_reader_guard that +# doesn't allow to edit it. +# create_property_reader() +# Input: +# name - Name of watched property and output variable +################################################################################ +function(create_property_reader NAME) + set(PROPERTY_READER_GUARD_DISABLED TRUE) + set(CONFIG_VALUE "$>>>") + set(IS_CONFIG_VALUE_EMPTY "$") + set(GENERAL_VALUE "$>") + set("${NAME}" "$" PARENT_SCOPE) + variable_watch("${NAME}" property_reader_guard) +endfunction() + +################################################################################ +# Set property $_${PROPS_CONFIG_U} of ${PROPS_TARGET} to +# set_config_specific_property( ) +# Input: +# name - Prefix of property name +# value - New value +################################################################################ +function(set_config_specific_property NAME VALUE) + set_target_properties("${PROPS_TARGET}" PROPERTIES "${NAME}_${PROPS_CONFIG_U}" "${VALUE}") +endfunction() + +################################################################################ + +create_property_reader("TARGET_NAME") +create_property_reader("OUTPUT_DIRECTORY") + +set_config_specific_property("TARGET_NAME" "${PROPS_TARGET}") +set_config_specific_property("OUTPUT_NAME" "${TARGET_NAME}") +set_config_specific_property("ARCHIVE_OUTPUT_NAME" "${TARGET_NAME}") +set_config_specific_property("LIBRARY_OUTPUT_NAME" "${TARGET_NAME}") +set_config_specific_property("RUNTIME_OUTPUT_NAME" "${TARGET_NAME}") + +set_config_specific_property("ARCHIVE_OUTPUT_DIRECTORY" "${OUTPUT_DIRECTORY}") +set_config_specific_property("LIBRARY_OUTPUT_DIRECTORY" "${OUTPUT_DIRECTORY}") +set_config_specific_property("RUNTIME_OUTPUT_DIRECTORY" "${OUTPUT_DIRECTORY}") \ No newline at end of file diff --git a/player/CMake/DefaultCXX.cmake b/player/CMake/DefaultCXX.cmake new file mode 100644 index 0000000..eff0e56 --- /dev/null +++ b/player/CMake/DefaultCXX.cmake @@ -0,0 +1,12 @@ +include("${CMAKE_CURRENT_LIST_DIR}/Default.cmake") + +set_config_specific_property("OUTPUT_DIRECTORY" "${CMAKE_SOURCE_DIR}$<$>:/${CMAKE_VS_PLATFORM_NAME}>/${PROPS_CONFIG}") + +if(MSVC) + create_property_reader("DEFAULT_CXX_EXCEPTION_HANDLING") + create_property_reader("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT") + + set_target_properties("${PROPS_TARGET}" PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") + set_config_specific_property("DEFAULT_CXX_EXCEPTION_HANDLING" "/EHsc") + set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Zi") +endif() diff --git a/player/CMake/DefaultFortran.cmake b/player/CMake/DefaultFortran.cmake new file mode 100644 index 0000000..3c16740 --- /dev/null +++ b/player/CMake/DefaultFortran.cmake @@ -0,0 +1,12 @@ +include("${CMAKE_CURRENT_LIST_DIR}/Default.cmake") + +set_config_specific_property("OUTPUT_DIRECTORY" "${CMAKE_CURRENT_SOURCE_DIR}$<$>:/${CMAKE_VS_PLATFORM_NAME}>/${PROPS_CONFIG}") + +get_target_property(${PROPS_TARGET}_BINARY_DIR ${PROPS_TARGET} BINARY_DIR) +set(DEFAULT_FORTRAN_MODULES_DIR "${${PROPS_TARGET}_BINARY_DIR}/${PROPS_TARGET}.Modules.dir") +set_target_properties(${PROPS_TARGET} PROPERTIES Fortran_MODULE_DIRECTORY ${DEFAULT_FORTRAN_MODULES_DIR}) + +if(${CMAKE_GENERATOR} MATCHES "Visual Studio") + # Hack for visual studio generator (https://gitlab.kitware.com/cmake/cmake/issues/19552) + add_custom_command(TARGET ${PROPS_TARGET} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory $/${CMAKE_CFG_INTDIR}) +endif() \ No newline at end of file diff --git a/player/CMake/Utils.cmake b/player/CMake/Utils.cmake new file mode 100644 index 0000000..9e2f961 --- /dev/null +++ b/player/CMake/Utils.cmake @@ -0,0 +1,234 @@ +# utils file for projects came from visual studio solution with cmake-converter. + +################################################################################ +# Wrap each token of the command with condition +################################################################################ +cmake_policy(PUSH) +cmake_policy(SET CMP0054 NEW) +macro(prepare_commands) + unset(TOKEN_ROLE) + unset(COMMANDS) + foreach(TOKEN ${ARG_COMMANDS}) + if("${TOKEN}" STREQUAL "COMMAND") + set(TOKEN_ROLE "KEYWORD") + elseif("${TOKEN_ROLE}" STREQUAL "KEYWORD") + set(TOKEN_ROLE "CONDITION") + elseif("${TOKEN_ROLE}" STREQUAL "CONDITION") + set(TOKEN_ROLE "COMMAND") + elseif("${TOKEN_ROLE}" STREQUAL "COMMAND") + set(TOKEN_ROLE "ARG") + endif() + + if("${TOKEN_ROLE}" STREQUAL "KEYWORD") + list(APPEND COMMANDS "${TOKEN}") + elseif("${TOKEN_ROLE}" STREQUAL "CONDITION") + set(CONDITION ${TOKEN}) + elseif("${TOKEN_ROLE}" STREQUAL "COMMAND") + list(APPEND COMMANDS "$<$:${DUMMY}>$<${CONDITION}:${TOKEN}>") + elseif("${TOKEN_ROLE}" STREQUAL "ARG") + list(APPEND COMMANDS "$<${CONDITION}:${TOKEN}>") + endif() + endforeach() +endmacro() +cmake_policy(POP) + +################################################################################ +# Transform all the tokens to absolute paths +################################################################################ +macro(prepare_output) + unset(OUTPUT) + foreach(TOKEN ${ARG_OUTPUT}) + if(IS_ABSOLUTE ${TOKEN}) + list(APPEND OUTPUT "${TOKEN}") + else() + list(APPEND OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/${TOKEN}") + endif() + endforeach() +endmacro() + +################################################################################ +# Parse add_custom_command_if args. +# +# Input: +# PRE_BUILD - Pre build event option +# PRE_LINK - Pre link event option +# POST_BUILD - Post build event option +# TARGET - Target +# OUTPUT - List of output files +# DEPENDS - List of files on which the command depends +# COMMANDS - List of commands(COMMAND condition1 commannd1 args1 COMMAND +# condition2 commannd2 args2 ...) +# Output: +# OUTPUT - Output files +# DEPENDS - Files on which the command depends +# COMMENT - Comment +# PRE_BUILD - TRUE/FALSE +# PRE_LINK - TRUE/FALSE +# POST_BUILD - TRUE/FALSE +# TARGET - Target name +# COMMANDS - Prepared commands(every token is wrapped in CONDITION) +# NAME - Unique name for custom target +# STEP - PRE_BUILD/PRE_LINK/POST_BUILD +################################################################################ +function(add_custom_command_if_parse_arguments) + cmake_parse_arguments("ARG" "PRE_BUILD;PRE_LINK;POST_BUILD" "TARGET;COMMENT" "DEPENDS;OUTPUT;COMMANDS" ${ARGN}) + + if(WIN32) + set(DUMMY "cd.") + elseif(UNIX) + set(DUMMY "true") + endif() + + prepare_commands() + prepare_output() + + set(DEPENDS "${ARG_DEPENDS}") + set(COMMENT "${ARG_COMMENT}") + set(PRE_BUILD "${ARG_PRE_BUILD}") + set(PRE_LINK "${ARG_PRE_LINK}") + set(POST_BUILD "${ARG_POST_BUILD}") + set(TARGET "${ARG_TARGET}") + if(PRE_BUILD) + set(STEP "PRE_BUILD") + elseif(PRE_LINK) + set(STEP "PRE_LINK") + elseif(POST_BUILD) + set(STEP "POST_BUILD") + endif() + set(NAME "${TARGET}_${STEP}") + + set(OUTPUT "${OUTPUT}" PARENT_SCOPE) + set(DEPENDS "${DEPENDS}" PARENT_SCOPE) + set(COMMENT "${COMMENT}" PARENT_SCOPE) + set(PRE_BUILD "${PRE_BUILD}" PARENT_SCOPE) + set(PRE_LINK "${PRE_LINK}" PARENT_SCOPE) + set(POST_BUILD "${POST_BUILD}" PARENT_SCOPE) + set(TARGET "${TARGET}" PARENT_SCOPE) + set(COMMANDS "${COMMANDS}" PARENT_SCOPE) + set(STEP "${STEP}" PARENT_SCOPE) + set(NAME "${NAME}" PARENT_SCOPE) +endfunction() + +################################################################################ +# Add conditional custom command +# +# Generating Files +# The first signature is for adding a custom command to produce an output: +# add_custom_command_if( +# +# +# +# [COMMAND condition command2 [args2...]] +# [DEPENDS [depends...]] +# [COMMENT comment] +# +# Build Events +# add_custom_command_if( +# +# +# +# [COMMAND condition command2 [args2...]] +# [COMMENT comment] +# +# Input: +# output - Output files the command is expected to produce +# condition - Generator expression for wrapping the command +# command - Command-line(s) to execute at build time. +# args - Command`s args +# depends - Files on which the command depends +# comment - Display the given message before the commands are executed at +# build time. +# PRE_BUILD - Run before any other rules are executed within the target +# PRE_LINK - Run after sources have been compiled but before linking the +# binary +# POST_BUILD - Run after all other rules within the target have been +# executed +################################################################################ +function(add_custom_command_if) + add_custom_command_if_parse_arguments(${ARGN}) + + if(OUTPUT AND TARGET) + message(FATAL_ERROR "Wrong syntax. A TARGET and OUTPUT can not both be specified.") + endif() + + if(OUTPUT) + add_custom_command(OUTPUT ${OUTPUT} + ${COMMANDS} + DEPENDS ${DEPENDS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT ${COMMENT}) + elseif(TARGET) + if(PRE_BUILD AND NOT ${CMAKE_GENERATOR} MATCHES "Visual Studio") + add_custom_target( + ${NAME} + ${COMMANDS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT ${COMMENT}) + add_dependencies(${TARGET} ${NAME}) + else() + add_custom_command( + TARGET ${TARGET} + ${STEP} + ${COMMANDS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT ${COMMENT}) + endif() + else() + message(FATAL_ERROR "Wrong syntax. A TARGET or OUTPUT must be specified.") + endif() +endfunction() + +################################################################################ +# Use props file for a target and configs +# use_props( ) +# Inside there are following variables: +# PROPS_TARGET - +# PROPS_CONFIG - One of +# PROPS_CONFIG_U - Uppercase PROPS_CONFIG +# Input: +# target - Target to apply props file +# configs - Build configurations to apply props file +# props_file - CMake script +################################################################################ +macro(use_props TARGET CONFIGS PROPS_FILE) + set(PROPS_TARGET "${TARGET}") + foreach(PROPS_CONFIG ${CONFIGS}) + string(TOUPPER "${PROPS_CONFIG}" PROPS_CONFIG_U) + + get_filename_component(ABSOLUTE_PROPS_FILE "${PROPS_FILE}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_LIST_DIR}") + if(EXISTS "${ABSOLUTE_PROPS_FILE}") + include("${ABSOLUTE_PROPS_FILE}") + else() + message(WARNING "Corresponding cmake file from props \"${ABSOLUTE_PROPS_FILE}\" doesn't exist") + endif() + endforeach() +endmacro() + +################################################################################ +# Add compile options to source file +# source_file_compile_options( [compile_options...]) +# Input: +# source_file - Source file +# compile_options - Options to add to COMPILE_FLAGS property +################################################################################ +function(source_file_compile_options SOURCE_FILE) + if("${ARGC}" LESS_EQUAL "1") + return() + endif() + + get_source_file_property(COMPILE_OPTIONS "${SOURCE_FILE}" COMPILE_OPTIONS) + + if(COMPILE_OPTIONS) + list(APPEND COMPILE_OPTIONS ${ARGN}) + else() + set(COMPILE_OPTIONS "${ARGN}") + endif() + + set_source_files_properties("${SOURCE_FILE}" PROPERTIES COMPILE_OPTIONS "${COMPILE_OPTIONS}") +endfunction() + +################################################################################ +# Default properties of visual studio projects +################################################################################ +set(DEFAULT_CXX_PROPS "${CMAKE_CURRENT_LIST_DIR}/DefaultCXX.cmake") +set(DEFAULT_Fortran_PROPS "${CMAKE_CURRENT_LIST_DIR}/DefaultFortran.cmake") diff --git a/player/conanfile.txt b/player/conanfile.txt index 31c7d12..f48f33f 100644 --- a/player/conanfile.txt +++ b/player/conanfile.txt @@ -1,6 +1,6 @@ [requires] openssl/3.5.1 -ffmpeg/7.1.1 +ffmpeg/8.0.1 pugixml/1.15 [generators] From 90b342c5dc18e17c95f6c48bb7e8d579dc65e18f Mon Sep 17 00:00:00 2001 From: HansBusch <47081128+HansBusch@users.noreply.github.com> Date: Tue, 3 Mar 2026 06:25:22 +0100 Subject: [PATCH 9/9] Add info field support. --- player/src/parser/certificateStorage.cpp | 2 +- .../src/parser/protectionSystemSpecificHeaderBox.hpp | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/player/src/parser/certificateStorage.cpp b/player/src/parser/certificateStorage.cpp index 6ecdf62..472dc80 100644 --- a/player/src/parser/certificateStorage.cpp +++ b/player/src/parser/certificateStorage.cpp @@ -238,7 +238,7 @@ HexArray CertificateStorage::decryptKey(ProtectionSystemSpecificHeaderBox* box) uint8_t buffer[256] = {}; size_t bufsiz = sizeof(buffer); if (ctx) { - OSSL_HPKE_decap(ctx, box->getSharedSecret().data(), box->getSharedSecret().size(), pkey, NULL, 0); + 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); } diff --git a/player/src/parser/protectionSystemSpecificHeaderBox.hpp b/player/src/parser/protectionSystemSpecificHeaderBox.hpp index 1d50bc6..c902916 100644 --- a/player/src/parser/protectionSystemSpecificHeaderBox.hpp +++ b/player/src/parser/protectionSystemSpecificHeaderBox.hpp @@ -30,6 +30,7 @@ #include "basic/fullBox.hpp" #include +#include typedef uint8_t KidType[16]; @@ -83,6 +84,7 @@ class ProtectionSystemSpecificHeaderBox } } HexArray getSharedSecret() { return HexArray(m_sharedSecret, getSharedSecretSize()); } + std::vector getInfo() { return m_info; } public: BOX_INFO("pssh", "Protection System Specific Header Box") @@ -137,6 +139,14 @@ class ProtectionSystemSpecificHeaderBox 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); + } + } } } @@ -152,6 +162,7 @@ class ProtectionSystemSpecificHeaderBox uint8_t m_encKey[1024]; uint16_t m_hpke[3]{}; uint8_t m_sharedSecret[128]{}; + std::vector m_info; }; #endif // SURVIELANCE_EXPORT_BOX_H