vlc | branch: master | Francois Cartegnie <[email protected]> | Wed Apr 17 18:46:55 2019 +0200| [f47c77ac17a129a0aef5d8e59c9051d456c60c54] | committer: Francois Cartegnie
demux: adaptive: refactor HLS encryption into adaptive > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=f47c77ac17a129a0aef5d8e59c9051d456c60c54 --- modules/demux/Makefile.am | 2 + .../demux/adaptive/encryption/CommonEncryption.cpp | 125 +++++++++++++++++++++ .../demux/adaptive/encryption/CommonEncryption.hpp | 60 ++++++++++ modules/demux/hls/playlist/HLSSegment.cpp | 86 ++++---------- modules/demux/hls/playlist/HLSSegment.hpp | 28 +---- modules/demux/hls/playlist/Parser.cpp | 8 +- 6 files changed, 217 insertions(+), 92 deletions(-) diff --git a/modules/demux/Makefile.am b/modules/demux/Makefile.am index 85d4b3fba9..a3e1bd6fee 100644 --- a/modules/demux/Makefile.am +++ b/modules/demux/Makefile.am @@ -319,6 +319,8 @@ libadaptive_plugin_la_SOURCES = \ demux/adaptive/playlist/Url.cpp \ demux/adaptive/playlist/Url.hpp \ demux/adaptive/playlist/Templates.hpp \ + demux/adaptive/encryption/CommonEncryption.cpp \ + demux/adaptive/encryption/CommonEncryption.hpp \ demux/adaptive/logic/AbstractAdaptationLogic.cpp \ demux/adaptive/logic/AbstractAdaptationLogic.h \ demux/adaptive/logic/AlwaysBestAdaptationLogic.cpp \ diff --git a/modules/demux/adaptive/encryption/CommonEncryption.cpp b/modules/demux/adaptive/encryption/CommonEncryption.cpp new file mode 100644 index 0000000000..31709dea70 --- /dev/null +++ b/modules/demux/adaptive/encryption/CommonEncryption.cpp @@ -0,0 +1,125 @@ +/***************************************************************************** + * CommonEncryption.cpp + ***************************************************************************** + * Copyright (C) 2015-2019 VLC authors and VideoLAN + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "CommonEncryption.hpp" + +#include <vlc_common.h> + +#ifdef HAVE_GCRYPT + #include <gcrypt.h> + #include <vlc_gcrypt.h> +#endif + +using namespace adaptive::encryption; + + +CommonEncryption::CommonEncryption() +{ + method = CommonEncryption::Method::NONE; +} + +CommonEncryptionSession::CommonEncryptionSession() +{ + ctx = NULL; +} + +CommonEncryptionSession::~CommonEncryptionSession() +{ + close(); +} + +bool CommonEncryptionSession::start(const CommonEncryption &enc) +{ + if(ctx) + close(); + encryption = enc; +#ifdef HAVE_GCRYPT + if(encryption.method == CommonEncryption::Method::AES_128) + { + vlc_gcrypt_init(); + gcry_cipher_hd_t handle; + if( gcry_cipher_open(&handle, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0) || + encryption.key.size() != 16 || + gcry_cipher_setkey(handle, &encryption.key[0], 16) || + gcry_cipher_setiv(handle, &encryption.iv[0], 16) ) + { + gcry_cipher_close(handle); + ctx = NULL; + return false; + } + ctx = handle; + } +#endif + return true; +} + +void CommonEncryptionSession::close() +{ + gcry_cipher_hd_t handle = reinterpret_cast<gcry_cipher_hd_t>(ctx); +#ifdef HAVE_GCRYPT + if(ctx) + gcry_cipher_close(handle); + ctx = NULL; +#endif +} + +size_t CommonEncryptionSession::decrypt(void *inputdata, size_t inputbytes, bool last) +{ + gcry_cipher_hd_t handle = reinterpret_cast<gcry_cipher_hd_t>(ctx); +#ifndef HAVE_GCRYPT + (void)data; + (void)bytes; + (void)last; +#else + if(encryption.method == CommonEncryption::Method::AES_128 && ctx) + { + if ((inputbytes % 16) != 0 || inputbytes < 16 || + gcry_cipher_decrypt(handle, inputdata, inputbytes, NULL, 0)) + { + inputbytes = 0; + } + else if(last) + { + /* last bytes */ + /* remove the PKCS#7 padding from the buffer */ + const uint8_t pad = reinterpret_cast<uint8_t *>(inputdata)[inputbytes - 1]; + for(uint8_t i=0; i<pad && i<=16; i++) + { + if(reinterpret_cast<uint8_t *>(inputdata)[inputbytes - i - 1] != pad) + break; + + if(i==pad) + inputbytes -= pad; + } + } + } + else +#endif + if(encryption.method != CommonEncryption::Method::NONE) + { + inputbytes = 0; + } + + return inputbytes; +} diff --git a/modules/demux/adaptive/encryption/CommonEncryption.hpp b/modules/demux/adaptive/encryption/CommonEncryption.hpp new file mode 100644 index 0000000000..25d3984bf3 --- /dev/null +++ b/modules/demux/adaptive/encryption/CommonEncryption.hpp @@ -0,0 +1,60 @@ +/***************************************************************************** + * CommonEncryption.hpp + ***************************************************************************** + * Copyright (C) 2015-2019 VLC authors and VideoLAN + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ +#ifndef COMMONENCRYPTION_H +#define COMMONENCRYPTION_H + +#include <vector> + +namespace adaptive +{ + namespace encryption + { + class CommonEncryption + { + public: + CommonEncryption(); + enum Method + { + NONE, + AES_128, + AES_SAMPLE, + } method; + std::vector<unsigned char> key; + std::vector<unsigned char> iv; + }; + + class CommonEncryptionSession + { + public: + CommonEncryptionSession(); + ~CommonEncryptionSession(); + + bool start(const CommonEncryption &); + void close(); + size_t decrypt(void *, size_t, bool); + + private: + CommonEncryption encryption; + void *ctx; + }; + } +} + +#endif diff --git a/modules/demux/hls/playlist/HLSSegment.cpp b/modules/demux/hls/playlist/HLSSegment.cpp index c43f6a58ad..a3f26256e1 100644 --- a/modules/demux/hls/playlist/HLSSegment.cpp +++ b/modules/demux/hls/playlist/HLSSegment.cpp @@ -24,36 +24,22 @@ #include "HLSSegment.hpp" #include "../adaptive/playlist/SegmentChunk.hpp" #include "../adaptive/playlist/BaseRepresentation.h" +#include "../adaptive/encryption/CommonEncryption.hpp" #include <vlc_common.h> #include <vlc_block.h> -#ifdef HAVE_GCRYPT - #include <vlc_gcrypt.h> -#endif using namespace hls::playlist; -SegmentEncryption::SegmentEncryption() -{ - method = SegmentEncryption::NONE; -} - HLSSegment::HLSSegment( ICanonicalUrl *parent, uint64_t seq ) : Segment( parent ) { setSequenceNumber(seq); utcTime = 0; -#ifdef HAVE_GCRYPT - ctx = NULL; -#endif } HLSSegment::~HLSSegment() { -#ifdef HAVE_GCRYPT - if(ctx) - gcry_cipher_close(ctx); -#endif } void HLSSegment::onChunkDownload(block_t **pp_block, SegmentChunk *chunk, BaseRepresentation *) @@ -63,67 +49,37 @@ void HLSSegment::onChunkDownload(block_t **pp_block, SegmentChunk *chunk, BaseRe #ifndef HAVE_GCRYPT (void)chunk; #else - if(encryption.method == SegmentEncryption::AES_128) + if(encryption.method == CommonEncryption::Method::AES_128) { - block_t *p_block = *pp_block; - /* first bytes */ - if(!ctx && chunk->getBytesRead() == p_block->i_buffer) + if (encryption.iv.size() != 16) { - vlc_gcrypt_init(); - if (encryption.iv.size() != 16) - { - encryption.iv.clear(); - encryption.iv.resize(16); - encryption.iv[15] = (getSequenceNumber() - Segment::SEQUENCE_FIRST) & 0xff; - encryption.iv[14] = ((getSequenceNumber() - Segment::SEQUENCE_FIRST) >> 8)& 0xff; - encryption.iv[13] = ((getSequenceNumber() - Segment::SEQUENCE_FIRST) >> 16)& 0xff; - encryption.iv[12] = ((getSequenceNumber() - Segment::SEQUENCE_FIRST) >> 24)& 0xff; - } - - if( gcry_cipher_open(&ctx, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0) || - encryption.key.size() != 16 || - gcry_cipher_setkey(ctx, &encryption.key[0], 16) || - gcry_cipher_setiv(ctx, &encryption.iv[0], 16) ) - { - gcry_cipher_close(ctx); - ctx = NULL; - } + encryption.iv.clear(); + encryption.iv.resize(16); + encryption.iv[15] = (getSequenceNumber() - Segment::SEQUENCE_FIRST) & 0xff; + encryption.iv[14] = ((getSequenceNumber() - Segment::SEQUENCE_FIRST) >> 8)& 0xff; + encryption.iv[13] = ((getSequenceNumber() - Segment::SEQUENCE_FIRST) >> 16)& 0xff; + encryption.iv[12] = ((getSequenceNumber() - Segment::SEQUENCE_FIRST) >> 24)& 0xff; } - if(ctx) + block_t *p_block = *pp_block; + /* first bytes */ + if(chunk->getBytesRead() == p_block->i_buffer) { - if ((p_block->i_buffer % 16) != 0 || p_block->i_buffer < 16 || - gcry_cipher_decrypt(ctx, p_block->p_buffer, p_block->i_buffer, NULL, 0)) + if(!encryptSession.start(encryption)) { p_block->i_buffer = 0; - gcry_cipher_close(ctx); - ctx = NULL; - } - else - { - /* last bytes */ - if(chunk->isEmpty()) - { - /* remove the PKCS#7 padding from the buffer */ - const uint8_t pad = p_block->p_buffer[p_block->i_buffer - 1]; - for(uint8_t i=0; i<pad && i<=16; i++) - { - if(p_block->p_buffer[p_block->i_buffer - i - 1] != pad) - break; - - if(i==pad) - p_block->i_buffer -= pad; - } - - gcry_cipher_close(ctx); - ctx = NULL; - } + return; } } + + bool b_last = chunk->isEmpty(); + p_block->i_buffer = encryptSession.decrypt(p_block->p_buffer, p_block->i_buffer, b_last); + if(b_last) + encryptSession.close(); } else #endif - if(encryption.method != SegmentEncryption::NONE) + if(encryption.method != CommonEncryption::Method::NONE) { p_block->i_buffer = 0; } @@ -134,7 +90,7 @@ vlc_tick_t HLSSegment::getUTCTime() const return utcTime; } -void HLSSegment::setEncryption(SegmentEncryption &enc) +void HLSSegment::setEncryption(CommonEncryption &enc) { encryption = enc; } diff --git a/modules/demux/hls/playlist/HLSSegment.hpp b/modules/demux/hls/playlist/HLSSegment.hpp index b96a558aef..22cee023c3 100644 --- a/modules/demux/hls/playlist/HLSSegment.hpp +++ b/modules/demux/hls/playlist/HLSSegment.hpp @@ -21,30 +21,14 @@ #define HLSSEGMENT_HPP #include "../adaptive/playlist/Segment.h" -#include <vector> -#ifdef HAVE_GCRYPT - #include <gcrypt.h> -#endif +#include "../adaptive/encryption/CommonEncryption.hpp" namespace hls { namespace playlist { using namespace adaptive::playlist; - - class SegmentEncryption - { - public: - SegmentEncryption(); - enum - { - NONE, - AES_128, - AES_SAMPLE, - } method; - std::vector<uint8_t> key; - std::vector<uint8_t> iv; - }; + using namespace adaptive::encryption; class HLSSegment : public Segment { @@ -53,7 +37,7 @@ namespace hls public: HLSSegment( ICanonicalUrl *parent, uint64_t sequence ); virtual ~HLSSegment(); - void setEncryption(SegmentEncryption &); + void setEncryption(CommonEncryption &); vlc_tick_t getUTCTime() const; virtual int compare(ISegment *) const; /* reimpl */ @@ -61,10 +45,8 @@ namespace hls vlc_tick_t utcTime; virtual void onChunkDownload(block_t **, SegmentChunk *, BaseRepresentation *); /* reimpl */ - SegmentEncryption encryption; -#ifdef HAVE_GCRYPT - gcry_cipher_hd_t ctx; -#endif + CommonEncryption encryption; + CommonEncryptionSession encryptSession; }; } } diff --git a/modules/demux/hls/playlist/Parser.cpp b/modules/demux/hls/playlist/Parser.cpp index db4d72bbef..d722a0792c 100644 --- a/modules/demux/hls/playlist/Parser.cpp +++ b/modules/demux/hls/playlist/Parser.cpp @@ -204,7 +204,7 @@ void M3U8Parser::parseSegments(vlc_object_t *, Representation *rep, const std::l bool discontinuity = false; std::size_t prevbyterangeoffset = 0; const SingleValueTag *ctx_byterange = NULL; - SegmentEncryption encryption; + CommonEncryption encryption; const ValuesListTag *ctx_extinf = NULL; std::list<Tag *>::const_iterator it; @@ -282,7 +282,7 @@ void M3U8Parser::parseSegments(vlc_object_t *, Representation *rep, const std::l discontinuity = false; } - if(encryption.method != SegmentEncryption::NONE) + if(encryption.method != CommonEncryption::Method::NONE) segment->setEncryption(encryption); } break; @@ -312,7 +312,7 @@ void M3U8Parser::parseSegments(vlc_object_t *, Representation *rep, const std::l keytag->getAttributeByName("METHOD")->value == "AES-128" && keytag->getAttributeByName("URI") ) { - encryption.method = SegmentEncryption::AES_128; + encryption.method = CommonEncryption::Method::AES_128; encryption.key.clear(); Url keyurl(keytag->getAttributeByName("URI")->quotedString()); @@ -333,7 +333,7 @@ void M3U8Parser::parseSegments(vlc_object_t *, Representation *rep, const std::l else { /* unsupported or invalid */ - encryption.method = SegmentEncryption::NONE; + encryption.method = CommonEncryption::Method::NONE; encryption.key.clear(); encryption.iv.clear(); } _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
