From 52596a0b046f699a0f36a283aced88f83eb56588 Mon Sep 17 00:00:00 2001 From: Harlan Date: Sun, 25 Mar 2018 12:05:52 +0800 Subject: [PATCH 1/9] add HLS encryption feature --- trunk/src/app/srs_app_config.cpp | 100 +++++++++++++++++++- trunk/src/app/srs_app_config.hpp | 20 ++++ trunk/src/app/srs_app_hls.cpp | 121 ++++++++++++++++++++++-- trunk/src/app/srs_app_hls.hpp | 31 +++++- trunk/src/kernel/srs_kernel_ts.cpp | 59 ++++++++++++ trunk/src/kernel/srs_kernel_ts.hpp | 33 +++++++ trunk/src/kernel/srs_kernel_utility.cpp | 21 ++++ trunk/src/kernel/srs_kernel_utility.hpp | 4 + 8 files changed, 378 insertions(+), 11 deletions(-) diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 566c508a4..64ab0ac06 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -2606,6 +2606,16 @@ srs_error_t SrsConfig::vhost_to_json(SrsConfDirective* vhost, SrsJsonObject* obj hls->set("hls_nb_notify", sdir->dumps_arg0_to_integer()); } else if (sdir->name == "hls_wait_keyframe") { hls->set("hls_wait_keyframe", sdir->dumps_arg0_to_boolean()); + } else if (sdir->name == "hls_keys") { + hls->set("hls_keys", sdir->dumps_arg0_to_boolean()); + } else if (sdir->name == "hls_fragments_per_key") { + hls->set("hls_fragments_per_key", sdir->dumps_arg0_to_number()); + } else if (sdir->name == "hls_key_file") { + hls->set("hls_key_file", sdir->dumps_arg0_to_str()); + } else if (sdir->name == "hls_key_file_path") { + hls->set("hls_key_file_path", sdir->dumps_arg0_to_str()); + } else if (sdir->name == "hls_key_url") { + hls->set("hls_key_url", sdir->dumps_arg0_to_str()); } } } @@ -3773,7 +3783,8 @@ srs_error_t SrsConfig::check_normal_config() if (m != "enabled" && m != "hls_entry_prefix" && m != "hls_path" && m != "hls_fragment" && m != "hls_window" && m != "hls_on_error" && m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" && m != "hls_aof_ratio" && m != "hls_acodec" && m != "hls_vcodec" && m != "hls_m3u8_file" && m != "hls_ts_file" && m != "hls_ts_floor" && m != "hls_cleanup" && m != "hls_nb_notify" - && m != "hls_wait_keyframe" && m != "hls_dispose") { + && m != "hls_wait_keyframe" && m != "hls_dispose" && m != "hls_keys" && m != "hls_fragments_per_key" && m != "hls_key_file" + && m != "hls_key_file_path" && m != "hls_key_url") { return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.hls.%s of %s", m.c_str(), vhost->arg0().c_str()); } @@ -6278,6 +6289,93 @@ bool SrsConfig::get_hls_wait_keyframe(string vhost) return SRS_CONF_PERFER_TRUE(conf->arg0()); } +bool SrsConfig::get_hls_keys(string vhost) +{ + static bool DEFAULT = false; + + SrsConfDirective* conf = get_hls(vhost); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("hls_keys"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return SRS_CONF_PERFER_TRUE(conf->arg0()); +} + +int SrsConfig::get_hls_fragments_per_key(string vhost) +{ + static int DEFAULT = 0; + + SrsConfDirective* conf = get_hls(vhost); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("hls_fragments_per_key"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return ::atoi(conf->arg0().c_str()); +} + +string SrsConfig::get_hls_key_file(string vhost) +{ + static string DEFAULT = "[app]/[stream].key"; + + SrsConfDirective* conf = get_hls(vhost); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("hls_key_file"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return conf->arg0(); +} + +string SrsConfig::get_hls_key_file_path(std::string vhost) +{ + //put the key in ts path defaultly. + static string DEFAULT = get_hls_path(vhost); + + SrsConfDirective* conf = get_hls(vhost); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("hls_key_file_path"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return conf->arg0(); +} + +string SrsConfig::get_hls_key_url(std::string vhost) +{ + //put the key in ts path defaultly. + static string DEFAULT = get_hls_path(vhost); + + SrsConfDirective* conf = get_hls(vhost); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("hls_key_url"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return conf->arg0(); +} + SrsConfDirective *SrsConfig::get_hds(const string &vhost) { SrsConfDirective* conf = get_vhost(vhost); diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 14dc8a7fe..ece63a34d 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -1236,6 +1236,26 @@ public: * whether reap the ts when got keyframe. */ virtual bool get_hls_wait_keyframe(std::string vhost); + /** + * encrypt ts or not + */ + virtual bool get_hls_keys(std::string vhost); + /** + * how many fragments can one key encrypted. + */ + virtual int get_hls_fragments_per_key(std::string vhost); + /** + * get the HLS key file path template. + */ + virtual std::string get_hls_key_file(std::string vhost); + /** + * get the HLS key file store path. + */ + virtual std::string get_hls_key_file_path(std::string vhost); + /** + * get the HLS key file url which will be put in m3u8 + */ + virtual std::string get_hls_key_url(std::string vhost); /** * get the size of bytes to read from cdn network, for the on_hls_notify callback, * that is, to read max bytes of the bytes from the callback, or timeout or error. diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index f6dacc659..75779f102 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -52,6 +52,8 @@ using namespace std; #include #include +#include + // drop the segment when duration of ts too small. #define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100 @@ -60,13 +62,28 @@ using namespace std; // reset the piece id when deviation overflow this. #define SRS_JUMP_WHEN_PIECE_DEVIATION 20 -SrsHlsSegment::SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc) +SrsHlsSegment::SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc,bool needenc) { sequence_no = 0; - writer = new SrsFileWriter(); + + if(needenc) + { + writer = new SrsEncFileWriter(); + } + else + { + writer = new SrsFileWriter(); + } + tscw = new SrsTsContextWriter(writer, c, ac, vc); } +void SrsHlsSegment::SrsSetEncCfg(unsigned char* keyval,unsigned char *ivval) +{ + memcpy(iv,ivval,16); + dynamic_cast(writer)->SetEncCfg(keyval,ivval); +} + SrsHlsSegment::~SrsHlsSegment() { srs_freep(tscw); @@ -193,6 +210,8 @@ SrsHlsMuxer::SrsHlsMuxer() max_td = 0; _sequence_no = 0; current = NULL; + hls_keys = false; + hls_fragments_per_key = 10; async = new SrsAsyncCallWorker(); context = new SrsTsContext(); segments = new SrsFragmentWindow(); @@ -265,8 +284,9 @@ srs_error_t SrsHlsMuxer::initialize() srs_error_t SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, string path, string m3u8_file, string ts_file, double fragment, double window, - bool ts_floor, double aof_ratio, bool cleanup, bool wait_keyframe -) { + bool ts_floor, double aof_ratio, bool cleanup, bool wait_keyframe, bool keys, + int fragments_per_key, string key_file ,string key_file_path, string key_url) + { srs_error_t err = srs_success; srs_freep(req); @@ -284,7 +304,13 @@ srs_error_t SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, accept_floor_ts = 0; hls_window = window; deviation_ts = 0; - + + hls_keys = keys; + hls_fragments_per_key = fragments_per_key; + hls_key_file = key_file; + hls_key_file_path = key_file_path; + hls_key_url = key_url; + // generate the m3u8 dir and path. m3u8_url = srs_path_build_stream(m3u8_file, req->vhost, req->app, req->stream); m3u8 = path + "/" + m3u8_url; @@ -297,7 +323,18 @@ srs_error_t SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, if ((err = srs_create_dir_recursively(m3u8_dir)) != srs_success) { return srs_error_wrap(err, "create dir"); } - + + if(hls_keys && (hls_path != hls_key_file_path) ) + { + string key_file = hls_key_file; + key_file = srs_path_build_stream(key_file, req->vhost, req->app, req->stream); + + string key_dir = srs_path_dirname(hls_key_file_path + "/" + key_file); + if ((err = srs_create_dir_recursively(key_dir)) != srs_success) { + return srs_error_wrap(err, "create dir"); + } + } + return err; } @@ -342,8 +379,53 @@ srs_error_t SrsHlsMuxer::segment_open() } // new segment. - current = new SrsHlsSegment(context, default_acodec, default_vcodec); + current = new SrsHlsSegment(context, default_acodec, default_vcodec, hls_keys); current->sequence_no = _sequence_no++; + + if(hls_keys){ + + if(current->sequence_no % hls_fragments_per_key == 0) + { + string key_file = hls_key_file; + key_file = srs_path_build_stream(key_file, req->vhost, req->app, req->stream); + + if (true) { + std::stringstream ss; + ss << current->sequence_no; + key_file = srs_string_replace(key_file, "[seq]", ss.str()); + } + + string key_full_path = hls_key_file_path + "/" + key_file; + + if (RAND_bytes(key, 16) < 0) { + srs_error_wrap(err, "rand key failed."); + } + + if (RAND_bytes(iv, 16) < 0) { + srs_error_wrap(err, "rand iv failed."); + } + + int flags = O_CREAT|O_WRONLY|O_TRUNC; + mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH; + int fd; + + if ((fd = ::open(key_full_path.c_str(), flags, mode)) < 0) { + return srs_error_new(ERROR_SYSTEM_FILE_OPENE, "open file %s failed", key_full_path.c_str()); + } + ssize_t nwrite; + if ((nwrite = ::write(fd, key, 16)) != 16) { + return srs_error_new(ERROR_SYSTEM_FILE_WRITE, "write to file %s failed", key_full_path.c_str()); + } + + if (::close(fd) < 0) { + srs_warn("close file %s failed",key_full_path.c_str()); + } + + } + current->SrsSetEncCfg(key,iv); + + } + // generate filename. std::string ts_file = hls_ts_file; @@ -681,6 +763,22 @@ srs_error_t SrsHlsMuxer::_refresh_m3u8(string m3u8_file) // #EXT-X-DISCONTINUITY\n ss << "#EXT-X-DISCONTINUITY" << SRS_CONSTS_LF; } + + if(hls_keys && (segment->sequence_no%hls_fragments_per_key == 0)) + { + string filename = req->stream+"-"+srs_int2str(segment->sequence_no)+".key"; + char hexiv[33]; + ff_data_to_hex(hexiv,segment->iv,16,0); + hexiv[32] = '\0'; + string key_path; + //if key_url is not set,only use the file name + if(hls_key_url == hls_key_file_path){ + key_path = filename; + }else{ + key_path = hls_key_url+"/"+filename; + } + ss << "#EXT-X-KEY:METHOD=AES-128,URI=" << "\""<< key_path <<"\",IV=0x"<get_hls_ts_floor(vhost); // the seconds to dispose the hls. int hls_dispose = _srs_config->get_hls_dispose(vhost); + + bool hls_keys = _srs_config->get_hls_keys(vhost); + int hls_fragments_per_key = _srs_config->get_hls_fragments_per_key(vhost); + string hls_key_file = _srs_config->get_hls_key_file(vhost); + string hls_key_file_path = _srs_config->get_hls_key_file_path(vhost); + string hls_key_url = _srs_config->get_hls_key_url(vhost); // TODO: FIXME: support load exists m3u8, to continue publish stream. // for the HLS donot requires the EXT-X-MEDIA-SEQUENCE be monotonically increase. // open muxer if ((err = muxer->update_config(req, entry_prefix, path, m3u8_file, ts_file, hls_fragment, - hls_window, ts_floor, hls_aof_ratio, cleanup, wait_keyframe)) != srs_success ) { + hls_window, ts_floor, hls_aof_ratio, cleanup, wait_keyframe,hls_keys,hls_fragments_per_key, + hls_key_file, hls_key_file_path, hls_key_url)) != srs_success ) { return srs_error_wrap(err, "hls: update config"); } diff --git a/trunk/src/app/srs_app_hls.hpp b/trunk/src/app/srs_app_hls.hpp index e108b0bd1..a790b53eb 100644 --- a/trunk/src/app/srs_app_hls.hpp +++ b/trunk/src/app/srs_app_hls.hpp @@ -67,9 +67,19 @@ public: SrsFileWriter* writer; // The TS context writer to write TS to file. SrsTsContextWriter* tscw; + // Will be saved in m3u8 file. + unsigned char iv[16]; + // The full key path. + std::string keypath; + public: - SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc); + SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc,bool needenc); virtual ~SrsHlsSegment(); +public: + + void SrsSetEncCfg(unsigned char* keyval,unsigned char * ivval); + + }; /** @@ -146,6 +156,22 @@ private: // used to detect the dup or jmp or ts. int64_t accept_floor_ts; int64_t previous_floor_ts; + +private: + //encrypted or not + bool hls_keys; + int hls_fragments_per_key; + //key file name + std::string hls_key_file; + //key file path + std::string hls_key_file_path; + //key file url + std::string hls_key_url; + + unsigned char key[16]; + unsigned char iv[16]; + + private: int _sequence_no; int max_td; @@ -182,7 +208,8 @@ public: virtual srs_error_t update_config(SrsRequest* r, std::string entry_prefix, std::string path, std::string m3u8_file, std::string ts_file, double fragment, double window, bool ts_floor, double aof_ratio, - bool cleanup, bool wait_keyframe); + bool cleanup, bool wait_keyframe , bool keys, int fragments_per_key, + std::string key_file , std::string key_file_path,std::string key_url); /** * open a new segment(a new ts file) */ diff --git a/trunk/src/kernel/srs_kernel_ts.cpp b/trunk/src/kernel/srs_kernel_ts.cpp index 108567662..7f2c5334f 100644 --- a/trunk/src/kernel/srs_kernel_ts.cpp +++ b/trunk/src/kernel/srs_kernel_ts.cpp @@ -2607,6 +2607,65 @@ SrsVideoCodecId SrsTsContextWriter::video_codec() return vcodec; } +srs_error_t SrsEncFileWriter::write(void* buf, size_t count, ssize_t* pnwrite) +{ + + srs_assert(count == SRS_TS_PACKET_SIZE); + srs_error_t err = srs_success; + + if(buflength != HLS_AES_ENCRYPT_BLOCK_LENGTH) + { + memcpy(tmpbuf+buflength,(char*)buf,SRS_TS_PACKET_SIZE); + buflength += SRS_TS_PACKET_SIZE; + } + if(buflength == HLS_AES_ENCRYPT_BLOCK_LENGTH) + { + unsigned char encryptedbuf[HLS_AES_ENCRYPT_BLOCK_LENGTH]; + memset(encryptedbuf,0,HLS_AES_ENCRYPT_BLOCK_LENGTH); + AES_cbc_encrypt((unsigned char *)tmpbuf, (unsigned char *)encryptedbuf, HLS_AES_ENCRYPT_BLOCK_LENGTH, &key, iv, AES_ENCRYPT); + buflength = 0; + memset(tmpbuf,0,HLS_AES_ENCRYPT_BLOCK_LENGTH); + return SrsFileWriter::write(encryptedbuf,HLS_AES_ENCRYPT_BLOCK_LENGTH,pnwrite); + } + else + { + return err; + } + +}; + +srs_error_t SrsEncFileWriter::SetEncCfg(unsigned char* keyval,unsigned char *ivval) +{ + + srs_error_t err = srs_success; + + if (AES_set_encrypt_key(keyval, 16*8, &key)) + { + return srs_error_new(ERROR_SYSTEM_FILE_WRITE, "set aes key failed"); + } + + memcpy(iv,ivval,16); + return err; +} + +void SrsEncFileWriter::close() +{ + if(buflength > 0) + { + int addBytes = 16 - buflength % 16; + memset(tmpbuf + buflength, addBytes, addBytes); + unsigned char encryptedbuf[buflength+addBytes]; + memset(encryptedbuf,0,buflength+addBytes); + AES_cbc_encrypt((unsigned char *)tmpbuf, (unsigned char *)encryptedbuf, buflength+addBytes, &key, iv, AES_ENCRYPT); + SrsFileWriter::write(encryptedbuf,buflength+addBytes,NULL); + + buflength = 0; + memset(tmpbuf,0,HLS_AES_ENCRYPT_BLOCK_LENGTH); + } + SrsFileWriter::close(); +} + + SrsTsMessageCache::SrsTsMessageCache() { audio = NULL; diff --git a/trunk/src/kernel/srs_kernel_ts.hpp b/trunk/src/kernel/srs_kernel_ts.hpp index 36bf01285..d3f131e59 100644 --- a/trunk/src/kernel/srs_kernel_ts.hpp +++ b/trunk/src/kernel/srs_kernel_ts.hpp @@ -31,8 +31,11 @@ #include #include #include +#include #include +#include + class SrsBuffer; class SrsTsMessageCache; @@ -1571,6 +1574,36 @@ public: virtual SrsVideoCodecId video_codec(); }; +/* +* Used for HLS Encryption +*/ + +#define HLS_AES_ENCRYPT_BLOCK_LENGTH 188*4 + +static char tmpbuf[HLS_AES_ENCRYPT_BLOCK_LENGTH] = {0}; +static int buflength = 0; + + +class SrsEncFileWriter: public SrsFileWriter +{ +public: + SrsEncFileWriter() + { + memset(iv,0,16); + } + virtual ~SrsEncFileWriter(){} + + virtual srs_error_t write(void* buf, size_t count, ssize_t* pnwrite); + + srs_error_t SetEncCfg(unsigned char* key,unsigned char *iv); + + virtual void close(); + +private: + AES_KEY key; + unsigned char iv[16]; +}; + /** * TS messages cache, to group frames to TS message, * for example, we may write multiple AAC RAW frames to a TS message. diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp index 1e4142ee5..2e69069f2 100644 --- a/trunk/src/kernel/srs_kernel_utility.cpp +++ b/trunk/src/kernel/srs_kernel_utility.cpp @@ -1050,6 +1050,27 @@ int srs_do_create_dir_recursively(string dir) } return c; } + + char *ff_data_to_hex(char *buff, const uint8_t *src, int s, int lowercase) + { + int i; + static const char hex_table_uc[16] = { '0', '1', '2', '3', + '4', '5', '6', '7', + '8', '9', 'A', 'B', + 'C', 'D', 'E', 'F' }; + static const char hex_table_lc[16] = { '0', '1', '2', '3', + '4', '5', '6', '7', + '8', '9', 'a', 'b', + 'c', 'd', 'e', 'f' }; + const char *hex_table = lowercase ? hex_table_lc : hex_table_uc; + + for (i = 0; i < s; i++) { + buff[i * 2] = hex_table[src[i] >> 4]; + buff[i * 2 + 1] = hex_table[src[i] & 0xF]; + } + + return buff; + } int ff_hex_to_data(uint8_t* data, const char* p) { diff --git a/trunk/src/kernel/srs_kernel_utility.hpp b/trunk/src/kernel/srs_kernel_utility.hpp index b0c4665dc..0403d1d4b 100644 --- a/trunk/src/kernel/srs_kernel_utility.hpp +++ b/trunk/src/kernel/srs_kernel_utility.hpp @@ -181,6 +181,10 @@ extern char* srs_av_base64_encode(char* out, int out_size, const uint8_t* in, in * output hex to data={0x13, 0x90, 0x56, 0xe5, 0xa0} */ extern int ff_hex_to_data(uint8_t* data, const char* p); +/** + * convert data string to hex. + */ +extern char *ff_data_to_hex(char *buff, const uint8_t *src, int s, int lowercase); /** * generate the c0 chunk header for msg. From 43ebd5958dbf65f2fdac98f8b01812d67ae3b0a2 Mon Sep 17 00:00:00 2001 From: Harlan Date: Tue, 3 Apr 2018 22:00:32 +0800 Subject: [PATCH 2/9] fix bug that only one stream can be encrypted.. --- trunk/src/app/srs_app_hls.cpp | 25 ++++++++++++++----------- trunk/src/app/srs_app_hls.hpp | 4 +++- trunk/src/kernel/srs_kernel_ts.hpp | 13 +++++++++---- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index 75779f102..24af1e14b 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -62,18 +62,11 @@ using namespace std; // reset the piece id when deviation overflow this. #define SRS_JUMP_WHEN_PIECE_DEVIATION 20 -SrsHlsSegment::SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc,bool needenc) +SrsHlsSegment::SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc, SrsFileWriter *srswriter) { sequence_no = 0; - if(needenc) - { - writer = new SrsEncFileWriter(); - } - else - { - writer = new SrsFileWriter(); - } + writer = srswriter; tscw = new SrsTsContextWriter(writer, c, ac, vc); } @@ -87,7 +80,7 @@ void SrsHlsSegment::SrsSetEncCfg(unsigned char* keyval,unsigned char *ivval) SrsHlsSegment::~SrsHlsSegment() { srs_freep(tscw); - srs_freep(writer); + //srs_freep(writer); } SrsDvrAsyncCallOnHls::SrsDvrAsyncCallOnHls(int c, SrsRequest* r, string p, string t, string m, string mu, int s, double d) @@ -223,6 +216,7 @@ SrsHlsMuxer::~SrsHlsMuxer() srs_freep(req); srs_freep(async); srs_freep(context); + srs_freep(writer); } void SrsHlsMuxer::dispose() @@ -335,6 +329,15 @@ srs_error_t SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, } } + if(hls_keys) + { + writer = new SrsEncFileWriter(); + } + else + { + writer = new SrsFileWriter(); + } + return err; } @@ -379,7 +382,7 @@ srs_error_t SrsHlsMuxer::segment_open() } // new segment. - current = new SrsHlsSegment(context, default_acodec, default_vcodec, hls_keys); + current = new SrsHlsSegment(context, default_acodec, default_vcodec,writer); current->sequence_no = _sequence_no++; if(hls_keys){ diff --git a/trunk/src/app/srs_app_hls.hpp b/trunk/src/app/srs_app_hls.hpp index a790b53eb..433ec513e 100644 --- a/trunk/src/app/srs_app_hls.hpp +++ b/trunk/src/app/srs_app_hls.hpp @@ -73,7 +73,7 @@ public: std::string keypath; public: - SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc,bool needenc); + SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc, SrsFileWriter *srswriter); virtual ~SrsHlsSegment(); public: @@ -170,6 +170,8 @@ private: unsigned char key[16]; unsigned char iv[16]; + + SrsFileWriter *writer; private: diff --git a/trunk/src/kernel/srs_kernel_ts.hpp b/trunk/src/kernel/srs_kernel_ts.hpp index d3f131e59..733efe4a1 100644 --- a/trunk/src/kernel/srs_kernel_ts.hpp +++ b/trunk/src/kernel/srs_kernel_ts.hpp @@ -1578,18 +1578,17 @@ public: * Used for HLS Encryption */ + #define HLS_AES_ENCRYPT_BLOCK_LENGTH 188*4 -static char tmpbuf[HLS_AES_ENCRYPT_BLOCK_LENGTH] = {0}; -static int buflength = 0; - - class SrsEncFileWriter: public SrsFileWriter { public: SrsEncFileWriter() { memset(iv,0,16); + memset(tmpbuf,0,HLS_AES_ENCRYPT_BLOCK_LENGTH); + buflength = 0; } virtual ~SrsEncFileWriter(){} @@ -1602,6 +1601,12 @@ public: private: AES_KEY key; unsigned char iv[16]; + +private: + + char tmpbuf[HLS_AES_ENCRYPT_BLOCK_LENGTH]; + int buflength; + }; /** From 45a57acbb452a8b2d071175c47b25b2df57bfb14 Mon Sep 17 00:00:00 2001 From: Harlan Date: Fri, 6 Apr 2018 10:05:13 +0800 Subject: [PATCH 3/9] fix bug --- trunk/src/kernel/srs_kernel_ts.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/trunk/src/kernel/srs_kernel_ts.hpp b/trunk/src/kernel/srs_kernel_ts.hpp index 733efe4a1..5fc16a7cc 100644 --- a/trunk/src/kernel/srs_kernel_ts.hpp +++ b/trunk/src/kernel/srs_kernel_ts.hpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include From 90f70d4cd3ae1cf44b376fc8b2384bab7e1649a4 Mon Sep 17 00:00:00 2001 From: Harlan Date: Fri, 6 Apr 2018 10:57:46 +0800 Subject: [PATCH 4/9] CI test --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index ed5eaf8ab..e0927d6a8 100644 --- a/circle.yml +++ b/circle.yml @@ -17,6 +17,6 @@ dependencies: test: override: - - (cd trunk && ./configure --without-ssl --without-valgrind && make) + - (cd trunk && ./configure --with-ssl=openssl --without-valgrind && make) - (cd trunk && ./objs/srs_utest) From e9cb8210fc4e6d8151f22a6775128799069970c2 Mon Sep 17 00:00:00 2001 From: Harlan Date: Tue, 15 May 2018 11:20:52 +0800 Subject: [PATCH 5/9] fix default value error --- trunk/src/app/srs_app_config.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 64ab0ac06..cf53bac18 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -6308,7 +6308,7 @@ bool SrsConfig::get_hls_keys(string vhost) int SrsConfig::get_hls_fragments_per_key(string vhost) { - static int DEFAULT = 0; + static int DEFAULT = 10; SrsConfDirective* conf = get_hls(vhost); if (!conf) { @@ -6325,7 +6325,7 @@ int SrsConfig::get_hls_fragments_per_key(string vhost) string SrsConfig::get_hls_key_file(string vhost) { - static string DEFAULT = "[app]/[stream].key"; + static string DEFAULT = "[app]/[stream]-[seq].key"; SrsConfDirective* conf = get_hls(vhost); if (!conf) { From ac6b37de4b10d105bb78ab5cd0d8fa176ec0e7eb Mon Sep 17 00:00:00 2001 From: Harlan Date: Sun, 12 Aug 2018 14:10:18 +0800 Subject: [PATCH 6/9] rewrite ff_data_to_hex --- trunk/src/app/srs_app_hls.cpp | 2 +- trunk/src/kernel/srs_kernel_utility.cpp | 29 ++++++++++--------------- trunk/src/kernel/srs_kernel_utility.hpp | 2 +- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index 24af1e14b..b403439bd 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -771,7 +771,7 @@ srs_error_t SrsHlsMuxer::_refresh_m3u8(string m3u8_file) { string filename = req->stream+"-"+srs_int2str(segment->sequence_no)+".key"; char hexiv[33]; - ff_data_to_hex(hexiv,segment->iv,16,0); + srs_data_to_hex(hexiv,segment->iv,16); hexiv[32] = '\0'; string key_path; //if key_url is not set,only use the file name diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp index 2e69069f2..2f421cce8 100644 --- a/trunk/src/kernel/srs_kernel_utility.cpp +++ b/trunk/src/kernel/srs_kernel_utility.cpp @@ -1051,25 +1051,18 @@ int srs_do_create_dir_recursively(string dir) return c; } - char *ff_data_to_hex(char *buff, const uint8_t *src, int s, int lowercase) + char *srs_data_to_hex(char *des,const u_int8_t *src,int len) { - int i; - static const char hex_table_uc[16] = { '0', '1', '2', '3', - '4', '5', '6', '7', - '8', '9', 'A', 'B', - 'C', 'D', 'E', 'F' }; - static const char hex_table_lc[16] = { '0', '1', '2', '3', - '4', '5', '6', '7', - '8', '9', 'a', 'b', - 'c', 'd', 'e', 'f' }; - const char *hex_table = lowercase ? hex_table_lc : hex_table_uc; - - for (i = 0; i < s; i++) { - buff[i * 2] = hex_table[src[i] >> 4]; - buff[i * 2 + 1] = hex_table[src[i] & 0xF]; - } - - return buff; + if(src == NULL || len == 0 || des == NULL){ + return NULL; + } + const char *hex_table = "0123456789ABCDEF"; + for (int i=0; i> 4]; + des[i * 2 + 1] = hex_table[src[i] & 0x0F]; + } + + return des; } int ff_hex_to_data(uint8_t* data, const char* p) diff --git a/trunk/src/kernel/srs_kernel_utility.hpp b/trunk/src/kernel/srs_kernel_utility.hpp index 0403d1d4b..6ead50e40 100644 --- a/trunk/src/kernel/srs_kernel_utility.hpp +++ b/trunk/src/kernel/srs_kernel_utility.hpp @@ -184,7 +184,7 @@ extern int ff_hex_to_data(uint8_t* data, const char* p); /** * convert data string to hex. */ -extern char *ff_data_to_hex(char *buff, const uint8_t *src, int s, int lowercase); +extern char *srs_data_to_hex(char *buff, const uint8_t *src, int s); /** * generate the c0 chunk header for msg. From bf12a452745e30e3d2e18785b8caa19f417ffeae Mon Sep 17 00:00:00 2001 From: Harlan Date: Sun, 12 Aug 2018 14:18:05 +0800 Subject: [PATCH 7/9] add configuration instructions for hls encryption --- trunk/conf/full.conf | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 5d1925fd8..e517a3b06 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -1100,6 +1100,26 @@ vhost hls.srs.com { # default: on hls_wait_keyframe on; + # whether using AES encryption. + # default: off + hls_keys on; + # the number of clear ts which one key can encrypt. + # default: 10 + hls_fragments_per_key 10; + # the hls key file name. + # we supports some variables to generate the filename. + # [vhost], the vhost of stream. + # [app], the app of stream. + # [stream], the stream name of stream. + # [seq], the sequence number of key corresponding to the ts. + hls_key_file [app]/[stream]-[seq].key; + # the key output path. + # the key file is configed by hls_path/hls_key_file, the default is: + # ./objs/nginx/html/[app]/[stream]-[seq].key + hls_key_file_path ./objs/nginx/html; + # the key root URL, use this can support https. + hls_key_url https://localhost:8080; + # on_hls, never config in here, should config in http_hooks. # for the hls http callback, @see http_hooks.on_hls of vhost hooks.callback.srs.com # @read https://github.com/ossrs/srs/wiki/v2_CN_DeliveryHLS#http-callback From ee068cf1207bf72fde4395598aa78eb1218b9bc0 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 25 Aug 2018 21:15:26 +0800 Subject: [PATCH 8/9] For #1109, Support without ssl for HLS key. --- circle.yml | 2 +- trunk/configure | 10 +- trunk/src/app/srs_app_hls.cpp | 143 ++++++++++++++++------------- trunk/src/app/srs_app_hls.hpp | 27 ++---- trunk/src/kernel/srs_kernel_ts.cpp | 122 +++++++++++++++--------- trunk/src/kernel/srs_kernel_ts.hpp | 41 +++------ 6 files changed, 188 insertions(+), 157 deletions(-) diff --git a/circle.yml b/circle.yml index e0927d6a8..ed5eaf8ab 100644 --- a/circle.yml +++ b/circle.yml @@ -17,6 +17,6 @@ dependencies: test: override: - - (cd trunk && ./configure --with-ssl=openssl --without-valgrind && make) + - (cd trunk && ./configure --without-ssl --without-valgrind && make) - (cd trunk && ./objs/srs_utest) diff --git a/trunk/configure b/trunk/configure index 2a55d2946..7cdc239cd 100755 --- a/trunk/configure +++ b/trunk/configure @@ -154,7 +154,7 @@ CORE_OBJS="${MODULE_OBJS[@]}" #Kernel, depends on core, provides error/log/config, nothing about stream information. MODULE_ID="KERNEL" MODULE_DEPENDS=("CORE") -ModuleLibIncs=(${SRS_OBJS_DIR}) +ModuleLibIncs=(${SRS_OBJS_DIR} ${LibSSLRoot}) MODULE_FILES=("srs_kernel_error" "srs_kernel_log" "srs_kernel_buffer" "srs_kernel_utility" "srs_kernel_flv" "srs_kernel_codec" "srs_kernel_io" "srs_kernel_consts" "srs_kernel_aac" "srs_kernel_mp3" "srs_kernel_ts" @@ -177,7 +177,7 @@ PROTOCOL_OBJS="${MODULE_OBJS[@]}" if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then MODULE_ID="SERVICE" MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL") - ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR}) + ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibSSLRoot}) MODULE_FILES=("srs_service_log" "srs_service_st" "srs_service_http_client" "srs_service_http_conn" "srs_service_rtmp_conn" "srs_service_utility" "srs_service_conn") @@ -190,7 +190,7 @@ fi if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then MODULE_ID="APP" MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE") - ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR}) + ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibSSLRoot}) MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_source" "srs_app_refer" "srs_app_hls" "srs_app_forward" "srs_app_encoder" "srs_app_http_stream" "srs_app_thread" "srs_app_bandwidth" "srs_app_st" "srs_app_log" "srs_app_config" @@ -225,7 +225,7 @@ LIBS_OBJS="${MODULE_OBJS[@]}" if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then MODULE_ID="SERVER" MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE" "APP") - ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot}) + ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) MODULE_FILES=("srs_main_server") SERVER_INCS="src/main"; MODULE_DIR=${SERVER_INCS} . auto/modules.sh SERVER_OBJS="${MODULE_OBJS[@]}" @@ -235,7 +235,7 @@ fi if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then MODULE_ID="MAIN" MODULE_DEPENDS=("CORE" "KERNEL" "PROTOCOL" "SERVICE") - ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot}) + ModuleLibIncs=(${LibSTRoot} ${SRS_OBJS_DIR} ${LibGperfRoot} ${LibSSLRoot}) MODULE_FILES=() DEFINES="" # add each modules for main diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index 96fe385e3..c036fb693 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -52,7 +52,9 @@ using namespace std; #include #include +#ifdef SRS_AUTO_SSL #include +#endif // drop the segment when duration of ts too small. #define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100 @@ -62,25 +64,26 @@ using namespace std; // reset the piece id when deviation overflow this. #define SRS_JUMP_WHEN_PIECE_DEVIATION 20 -SrsHlsSegment::SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc, SrsFileWriter *srswriter) +SrsHlsSegment::SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc, SrsFileWriter* w) { sequence_no = 0; - - writer = srswriter; - + writer = w; tscw = new SrsTsContextWriter(writer, c, ac, vc); } -void SrsHlsSegment::SrsSetEncCfg(unsigned char* keyval,unsigned char *ivval) -{ - memcpy(iv,ivval,16); - dynamic_cast(writer)->SetEncCfg(keyval,ivval); -} - SrsHlsSegment::~SrsHlsSegment() { srs_freep(tscw); - //srs_freep(writer); +} + +void SrsHlsSegment::config_cipher(unsigned char* key,unsigned char* iv) +{ + memcpy(this->iv, iv,16); + +#ifdef SRS_AUTO_SSL + SrsEncFileWriter* fw = (SrsEncFileWriter*)writer; + fw->config_cipher(key, iv); +#endif } SrsDvrAsyncCallOnHls::SrsDvrAsyncCallOnHls(int c, SrsRequest* r, string p, string t, string m, string mu, int s, double d) @@ -208,6 +211,9 @@ SrsHlsMuxer::SrsHlsMuxer() async = new SrsAsyncCallWorker(); context = new SrsTsContext(); segments = new SrsFragmentWindow(); + + memset(key, 0, 16); + memset(iv, 0, 16); } SrsHlsMuxer::~SrsHlsMuxer() @@ -280,7 +286,7 @@ srs_error_t SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, string path, string m3u8_file, string ts_file, double fragment, double window, bool ts_floor, double aof_ratio, bool cleanup, bool wait_keyframe, bool keys, int fragments_per_key, string key_file ,string key_file_path, string key_url) - { +{ srs_error_t err = srs_success; srs_freep(req); @@ -318,8 +324,7 @@ srs_error_t SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, return srs_error_wrap(err, "create dir"); } - if(hls_keys && (hls_path != hls_key_file_path) ) - { + if (hls_keys && (hls_path != hls_key_file_path)) { string key_file = hls_key_file; key_file = srs_path_build_stream(key_file, req->vhost, req->app, req->stream); @@ -329,12 +334,13 @@ srs_error_t SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, } } - if(hls_keys) - { + if(hls_keys) { +#ifdef SRS_AUTO_SSL writer = new SrsEncFileWriter(); - } - else - { +#else + writer = new SrsFileWriter(); +#endif + } else { writer = new SrsFileWriter(); } @@ -382,54 +388,13 @@ srs_error_t SrsHlsMuxer::segment_open() } // new segment. - current = new SrsHlsSegment(context, default_acodec, default_vcodec,writer); + current = new SrsHlsSegment(context, default_acodec, default_vcodec, writer); current->sequence_no = _sequence_no++; - if(hls_keys){ - - if(current->sequence_no % hls_fragments_per_key == 0) - { - string key_file = hls_key_file; - key_file = srs_path_build_stream(key_file, req->vhost, req->app, req->stream); - - if (true) { - std::stringstream ss; - ss << current->sequence_no; - key_file = srs_string_replace(key_file, "[seq]", ss.str()); - } - - string key_full_path = hls_key_file_path + "/" + key_file; - - if (RAND_bytes(key, 16) < 0) { - srs_error_wrap(err, "rand key failed."); - } - - if (RAND_bytes(iv, 16) < 0) { - srs_error_wrap(err, "rand iv failed."); - } - - int flags = O_CREAT|O_WRONLY|O_TRUNC; - mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH; - int fd; - - if ((fd = ::open(key_full_path.c_str(), flags, mode)) < 0) { - return srs_error_new(ERROR_SYSTEM_FILE_OPENE, "open file %s failed", key_full_path.c_str()); - } - ssize_t nwrite; - if ((nwrite = ::write(fd, key, 16)) != 16) { - return srs_error_new(ERROR_SYSTEM_FILE_WRITE, "write to file %s failed", key_full_path.c_str()); - } - - if (::close(fd) < 0) { - srs_warn("close file %s failed",key_full_path.c_str()); - } - - } - current->SrsSetEncCfg(key,iv); - + if ((err = write_hls_key()) != srs_success) { + return srs_error_wrap(err, "write hls key"); } - // generate filename. std::string ts_file = hls_ts_file; ts_file = srs_path_build_stream(ts_file, req->vhost, req->app, req->stream); @@ -688,6 +653,58 @@ srs_error_t SrsHlsMuxer::segment_close() return err; } +srs_error_t SrsHlsMuxer::write_hls_key() +{ + srs_error_t err = srs_success; + +#ifndef SRS_AUTO_SSL + if (hls_keys) { + srs_warn("SSL is disabled, ignore HLS key"); + } +#endif + +#ifdef SRS_AUTO_SSL + if (hls_keys && current->sequence_no % hls_fragments_per_key == 0) { + string key_file = hls_key_file; + key_file = srs_path_build_stream(key_file, req->vhost, req->app, req->stream); + + if (true) { + std::stringstream ss; + ss << current->sequence_no; + key_file = srs_string_replace(key_file, "[seq]", ss.str()); + } + + string key_full_path = hls_key_file_path + "/" + key_file; + + if (RAND_bytes(key, 16) < 0) { + srs_error_wrap(err, "rand key failed."); + } + + if (RAND_bytes(iv, 16) < 0) { + srs_error_wrap(err, "rand iv failed."); + } + + SrsFileWriter fw; + + if ((err = fw.open(key_full_path)) != srs_success) { + return srs_error_wrap(err, "open file %s", key_full_path.c_str()); + } + + if ((err = fw.write(key, 16, NULL)) != srs_success) { + return srs_error_wrap(err, "write key"); + } + + fw.close(); + } + + if (hls_keys) { + current->config_cipher(key, iv); + } +#endif + + return err; +} + srs_error_t SrsHlsMuxer::refresh_m3u8() { srs_error_t err = srs_success; diff --git a/trunk/src/app/srs_app_hls.hpp b/trunk/src/app/srs_app_hls.hpp index 433ec513e..edb3008a7 100644 --- a/trunk/src/app/srs_app_hls.hpp +++ b/trunk/src/app/srs_app_hls.hpp @@ -71,15 +71,11 @@ public: unsigned char iv[16]; // The full key path. std::string keypath; - public: - SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc, SrsFileWriter *srswriter); + SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc, SrsFileWriter* w); virtual ~SrsHlsSegment(); public: - - void SrsSetEncCfg(unsigned char* keyval,unsigned char * ivval); - - + void config_cipher(unsigned char* key,unsigned char* iv); }; /** @@ -156,24 +152,20 @@ private: // used to detect the dup or jmp or ts. int64_t accept_floor_ts; int64_t previous_floor_ts; - private: - //encrypted or not + // encrypted or not bool hls_keys; int hls_fragments_per_key; - //key file name + // key file name std::string hls_key_file; - //key file path + // key file path std::string hls_key_file_path; - //key file url + // key file url std::string hls_key_url; - + // key and iv. unsigned char key[16]; unsigned char iv[16]; - SrsFileWriter *writer; - - private: int _sequence_no; int max_td; @@ -210,8 +202,8 @@ public: virtual srs_error_t update_config(SrsRequest* r, std::string entry_prefix, std::string path, std::string m3u8_file, std::string ts_file, double fragment, double window, bool ts_floor, double aof_ratio, - bool cleanup, bool wait_keyframe , bool keys, int fragments_per_key, - std::string key_file , std::string key_file_path,std::string key_url); + bool cleanup, bool wait_keyframe, bool keys, int fragments_per_key, + std::string key_file, std::string key_file_path, std::string key_url); /** * open a new segment(a new ts file) */ @@ -244,6 +236,7 @@ public: */ virtual srs_error_t segment_close(); private: + virtual srs_error_t write_hls_key(); virtual srs_error_t refresh_m3u8(); virtual srs_error_t _refresh_m3u8(std::string m3u8_file); }; diff --git a/trunk/src/kernel/srs_kernel_ts.cpp b/trunk/src/kernel/srs_kernel_ts.cpp index 0a054bcec..354ee825b 100644 --- a/trunk/src/kernel/srs_kernel_ts.cpp +++ b/trunk/src/kernel/srs_kernel_ts.cpp @@ -34,15 +34,21 @@ #include using namespace std; +#ifdef SRS_AUTO_SSL +#include +#include +#endif + #include #include -#include #include #include #include #include #include +#define HLS_AES_ENCRYPT_BLOCK_LENGTH SRS_TS_PACKET_SIZE * 4 + // in ms, for HLS aac sync time. #define SRS_CONF_DEFAULT_AAC_SYNC 100 @@ -2610,64 +2616,94 @@ SrsVideoCodecId SrsTsContextWriter::video_codec() return vcodec; } -srs_error_t SrsEncFileWriter::write(void* buf, size_t count, ssize_t* pnwrite) +#ifdef SRS_AUTO_SSL +SrsEncFileWriter::SrsEncFileWriter() { + memset(iv,0,16); + + buf = new char[HLS_AES_ENCRYPT_BLOCK_LENGTH]; + memset(buf, 0, HLS_AES_ENCRYPT_BLOCK_LENGTH); + + nb_buf = 0; + key = (unsigned char*)new AES_KEY(); +} + +SrsEncFileWriter::~SrsEncFileWriter() +{ + srs_freepa(buf); + + AES_KEY* k = (AES_KEY*)key; + srs_freep(k); +} + +srs_error_t SrsEncFileWriter::write(void* data, size_t count, ssize_t* pnwrite) +{ + srs_error_t err = srs_success; srs_assert(count == SRS_TS_PACKET_SIZE); - srs_error_t err = srs_success; - if(buflength != HLS_AES_ENCRYPT_BLOCK_LENGTH) - { - memcpy(tmpbuf+buflength,(char*)buf,SRS_TS_PACKET_SIZE); - buflength += SRS_TS_PACKET_SIZE; + if (nb_buf < HLS_AES_ENCRYPT_BLOCK_LENGTH) { + memcpy(buf + nb_buf, (char*)data, SRS_TS_PACKET_SIZE); + nb_buf += SRS_TS_PACKET_SIZE; } - if(buflength == HLS_AES_ENCRYPT_BLOCK_LENGTH) - { - unsigned char encryptedbuf[HLS_AES_ENCRYPT_BLOCK_LENGTH]; - memset(encryptedbuf,0,HLS_AES_ENCRYPT_BLOCK_LENGTH); - AES_cbc_encrypt((unsigned char *)tmpbuf, (unsigned char *)encryptedbuf, HLS_AES_ENCRYPT_BLOCK_LENGTH, &key, iv, AES_ENCRYPT); - buflength = 0; - memset(tmpbuf,0,HLS_AES_ENCRYPT_BLOCK_LENGTH); - return SrsFileWriter::write(encryptedbuf,HLS_AES_ENCRYPT_BLOCK_LENGTH,pnwrite); - } - else - { - return err; - } - -}; - -srs_error_t SrsEncFileWriter::SetEncCfg(unsigned char* keyval,unsigned char *ivval) -{ - srs_error_t err = srs_success; - - if (AES_set_encrypt_key(keyval, 16*8, &key)) - { - return srs_error_new(ERROR_SYSTEM_FILE_WRITE, "set aes key failed"); - } + if (nb_buf == HLS_AES_ENCRYPT_BLOCK_LENGTH) { + nb_buf = 0; + + char* cipher = new char[HLS_AES_ENCRYPT_BLOCK_LENGTH]; + SrsAutoFreeA(char, cipher); + + AES_KEY* k = (AES_KEY*)key; + AES_cbc_encrypt((unsigned char *)buf, (unsigned char *)cipher, HLS_AES_ENCRYPT_BLOCK_LENGTH, k, iv, AES_ENCRYPT); + + if ((err = SrsFileWriter::write(cipher, HLS_AES_ENCRYPT_BLOCK_LENGTH, pnwrite)) != srs_success) { + return srs_error_wrap(err, "write cipher"); + } + } + + return err; +} - memcpy(iv,ivval,16); +srs_error_t SrsEncFileWriter::config_cipher(unsigned char* key, unsigned char* iv) +{ + srs_error_t err = srs_success; + + memcpy(this->iv, iv, 16); + + AES_KEY* k = (AES_KEY*)this->key; + if (AES_set_encrypt_key(key, 16 * 8, k)) { + return srs_error_new(ERROR_SYSTEM_FILE_WRITE, "set aes key failed"); + } + return err; } void SrsEncFileWriter::close() { - if(buflength > 0) - { - int addBytes = 16 - buflength % 16; - memset(tmpbuf + buflength, addBytes, addBytes); - unsigned char encryptedbuf[buflength+addBytes]; - memset(encryptedbuf,0,buflength+addBytes); - AES_cbc_encrypt((unsigned char *)tmpbuf, (unsigned char *)encryptedbuf, buflength+addBytes, &key, iv, AES_ENCRYPT); - SrsFileWriter::write(encryptedbuf,buflength+addBytes,NULL); + if(nb_buf > 0) { + int nb_padding = 16 - (nb_buf % 16); + if (nb_padding > 0) { + memset(buf + nb_buf, nb_padding, nb_padding); + } + + char* cipher = new char[nb_buf + nb_padding]; + SrsAutoFreeA(char, cipher); + + AES_KEY* k = (AES_KEY*)key; + AES_cbc_encrypt((unsigned char *)buf, (unsigned char *)cipher, nb_buf + nb_padding, k, iv, AES_ENCRYPT); + + srs_error_t err = srs_success; + if ((err = SrsFileWriter::write(cipher, nb_buf + nb_padding, NULL)) != srs_success) { + srs_warn("ignore err %s", srs_error_desc(err).c_str()); + srs_error_reset(err); + } - buflength = 0; - memset(tmpbuf,0,HLS_AES_ENCRYPT_BLOCK_LENGTH); + nb_buf = 0; } + SrsFileWriter::close(); } - +#endif SrsTsMessageCache::SrsTsMessageCache() { diff --git a/trunk/src/kernel/srs_kernel_ts.hpp b/trunk/src/kernel/srs_kernel_ts.hpp index 5fc16a7cc..6d1830826 100644 --- a/trunk/src/kernel/srs_kernel_ts.hpp +++ b/trunk/src/kernel/srs_kernel_ts.hpp @@ -31,13 +31,10 @@ #include #include #include -#include -#include #include #include - class SrsBuffer; class SrsTsMessageCache; class SrsTsContextWriter; @@ -1575,40 +1572,28 @@ public: virtual SrsVideoCodecId video_codec(); }; +#ifdef SRS_AUTO_SSL /* -* Used for HLS Encryption -*/ - - -#define HLS_AES_ENCRYPT_BLOCK_LENGTH 188*4 - + * Used for HLS Encryption + */ class SrsEncFileWriter: public SrsFileWriter { public: - SrsEncFileWriter() - { - memset(iv,0,16); - memset(tmpbuf,0,HLS_AES_ENCRYPT_BLOCK_LENGTH); - buflength = 0; - } - virtual ~SrsEncFileWriter(){} - - virtual srs_error_t write(void* buf, size_t count, ssize_t* pnwrite); - - srs_error_t SetEncCfg(unsigned char* key,unsigned char *iv); - + SrsEncFileWriter(); + virtual ~SrsEncFileWriter(); +public: + virtual srs_error_t write(void* data, size_t count, ssize_t* pnwrite); virtual void close(); - +public: + srs_error_t config_cipher(unsigned char* key, unsigned char* iv); private: - AES_KEY key; + unsigned char* key; unsigned char iv[16]; - private: - - char tmpbuf[HLS_AES_ENCRYPT_BLOCK_LENGTH]; - int buflength; - + char* buf; + int nb_buf; }; +#endif /** * TS messages cache, to group frames to TS message, From 5945fb5a244eabb2431953eba381efea8c8448d5 Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 25 Aug 2018 21:17:33 +0800 Subject: [PATCH 9/9] Fix #1093, Support HLS encryption. 3.0.42 --- README.md | 2 ++ trunk/src/core/srs_core.hpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 928013579..95b9582c6 100755 --- a/README.md +++ b/README.md @@ -184,6 +184,7 @@ Please select according to languages: ### V3 changes +* v3.0, 2018-08-25, Fix [#1093][bug #1093], Support HLS encryption. 3.0.42 * v3.0, 2018-08-25, Fix [#1051][bug #1051], Drop ts when republishing stream. 3.0.41 * v3.0, 2018-08-12, For [#1202][bug #1202], Support edge/forward to Aliyun CDN. 3.0.40 * v3.0, 2018-08-11, For [#910][bug #910], Support HTTP FLV with HTTP callback. 3.0.39 @@ -1468,6 +1469,7 @@ Winlin [bug #727]: https://github.com/ossrs/srs/issues/727 [bug #1087]: https://github.com/ossrs/srs/issues/1087 [bug #1051]: https://github.com/ossrs/srs/issues/1051 +[bug #1093]: https://github.com/ossrs/srs/issues/1093 [bug #xxxxxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxxxxx [exo #828]: https://github.com/google/ExoPlayer/pull/828 diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index c83aad456..fa4f69e38 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -27,7 +27,7 @@ // current release version #define VERSION_MAJOR 3 #define VERSION_MINOR 0 -#define VERSION_REVISION 41 +#define VERSION_REVISION 42 // generated by configure, only macros. #include