Skip to content
Snippets Groups Projects
Commit f47c77ac authored by François Cartegnie's avatar François Cartegnie :fingers_crossed:
Browse files

demux: adaptive: refactor HLS encryption into adaptive

parent dfe4aca1
No related branches found
No related tags found
No related merge requests found
......@@ -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 \
......
/*****************************************************************************
* 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;
}
/*****************************************************************************
* 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
......@@ -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;
}
......
......@@ -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;
};
}
}
......
......@@ -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();
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment