On Thu, Dec 11, 2014 at 12:27:37PM -0600, Christian Suloway wrote: > Added HLS encryption with -hls_key_info_file <key_info_file> option. The > first line of key_info_file specifies the key URI for the playlist, and > the second line specifies the path to the file containing the encryption > key. Changes to key_info_file will be reflected in segment encryption > along with an entry in the playlist for the new key URI. > > Also added -hls_flags random_iv option flag to use a random IV for > encryption instead of the segment number. > > Signed-off-by: Christian Suloway <csulo...@globaleagleent.com> > --- > doc/muxers.texi | 11 +++ > libavformat/hlsenc.c | 257 > +++++++++++++++++++++++++++++++++++++++++++++++++-- > 2 files changed, 262 insertions(+), 6 deletions(-) > > diff --git a/doc/muxers.texi b/doc/muxers.texi > index a1264d2..29a5de3 100644 > --- a/doc/muxers.texi > +++ b/doc/muxers.texi > @@ -263,6 +263,13 @@ ffmpeg in.nut -hls_segment_filename 'file%03d.ts' > out.m3u8 > This example will produce the playlist, @file{out.m3u8}, and segment files: > @file{file000.ts}, @file{file001.ts}, @file{file002.ts}, etc. > > +@item hls_key_info_file @var{file} > +Use in the information in @var{file} for segment encryption. The first line > of > +@var{file} specifies the key URI for the playlist. The second line specifies > +the path to the file containing the encryption key as a single packed array > of > +16 octets in binary format. Changes to @var{file} will result in segment > +encryption with the new key and an entry in the playlist for the new key URI. > + > @item hls_flags single_file > If this flag is set, the muxer will store all segments in a single MPEG-TS > file, and will use byte ranges in the playlist. HLS playlists generated with > @@ -277,6 +284,10 @@ Will produce the playlist, @file{out.m3u8}, and a single > segment file, > @item hls_flags delete_segments > Segment files removed from the playlist are deleted after a period of time > equal to the duration of the segment plus the duration of the playlist. > + > +@item hls_flags random_iv > +Segment file encryption will use a random initialization vector (IV) instead > of > +the segment number. > @end table > > @anchor{ico} > diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c > index 79f3a23..5bde70e 100644 > --- a/libavformat/hlsenc.c > +++ b/libavformat/hlsenc.c > @@ -32,17 +32,24 @@ > #include "libavutil/avstring.h" > #include "libavutil/opt.h" > #include "libavutil/log.h" > +#include "libavutil/lfg.h" > +#include "libavutil/random_seed.h" > > #include "avformat.h" > #include "internal.h" > #include "os_support.h" > > +#define BLOCKSIZE 16 > + > typedef struct HLSSegment { > char filename[1024]; > double duration; /* in seconds */ > int64_t pos; > int64_t size; > > + char *key_uri; > + char *iv_string; > + > struct HLSSegment *next; > } HLSSegment; > > @@ -50,6 +57,7 @@ typedef enum HLSFlags { > // Generate a single media file and use byte ranges in the playlist. > HLS_SINGLE_FILE = (1 << 0), > HLS_DELETE_SEGMENTS = (1 << 1), > + HLS_RANDOM_IV = (1 << 2), > } HLSFlags; > > typedef struct HLSContext { > @@ -86,9 +94,23 @@ typedef struct HLSContext { > char *format_options_str; > AVDictionary *format_options; > > + char *key_info_file; > + char *key_file; > + char *key_uri; > + char *key_string; > + char *iv_string; > + AVLFG *lfg; > + > AVIOContext *pb; > } HLSContext; > > +static void hls_free_segment(HLSSegment *en) > +{ > + av_freep(&en->key_uri); > + av_freep(&en->iv_string); > + av_freep(&en); > +} > + > static int hls_delete_old_segments(HLSContext *hls) { > > HLSSegment *segment, *previous_segment = NULL; > @@ -145,7 +167,7 @@ static int hls_delete_old_segments(HLSContext *hls) { > av_free(path); > previous_segment = segment; > segment = previous_segment->next; > - av_free(previous_segment); > + hls_free_segment(previous_segment); > } > > fail: > @@ -154,6 +176,157 @@ fail: > return ret; > } > > +static int hls_encryption_init(AVFormatContext *s) > +{ > + HLSContext *hls = s->priv_data; > + > + if (hls->flags & HLS_RANDOM_IV) { > + hls->lfg = av_malloc(sizeof(AVLFG)); > + if (!hls->lfg) > + return AVERROR(ENOMEM); > + av_lfg_init(hls->lfg, av_get_random_seed()); > + } > + > + return 0; > +} > + > +static int hls_encryption_start(HLSContext *hls) > +{ > + > + int ret = 0, i, j, rotate_iv = 0; > + AVIOContext *pb = NULL; > + AVIOContext *dyn_buf = NULL; > + uint8_t buf[1024], *tmp = NULL, *key = NULL, *iv = NULL; > + char *p, *tstr, *saveptr = NULL, *key_string = NULL; > + unsigned int u; > + > + if ((ret = avio_open(&pb, hls->key_info_file, AVIO_FLAG_READ)) < 0) { > + av_log(hls, AV_LOG_ERROR, "error opening key info file %s\n", > + hls->key_info_file); > + goto fail; > + } > + > + ret = avio_open_dyn_buf(&dyn_buf); > + if (ret < 0) { > + avio_closep(&pb); > + goto fail; > + } > + > + while ((ret = avio_read(pb, buf, sizeof(buf))) > 0) > + avio_write(dyn_buf, buf, ret); > + avio_closep(&pb); > + if (ret != AVERROR_EOF && ret < 0) { > + avio_close_dyn_buf(dyn_buf, &tmp); > + goto fail; > + } > + > + avio_w8(dyn_buf, 0); > + if ((ret = avio_close_dyn_buf(dyn_buf, &tmp)) < 0) > + goto fail; > + > + p = tmp; > + if (!(tstr = av_strtok(p, "\n", &saveptr)) || !*tstr) { > + av_log(hls, AV_LOG_ERROR, "no key URI specified in key info file > %s\n", > + hls->key_info_file); > + ret = AVERROR(EINVAL); > + goto fail; > + } > + p = NULL; > + av_free(hls->key_uri); > + hls->key_uri = av_strdup(tstr); > + if (!hls->key_uri) { > + ret = AVERROR(ENOMEM); > + goto fail; > + } > + if (!(tstr = av_strtok(p, "\n", &saveptr)) || !*tstr) { > + av_log(hls, AV_LOG_ERROR, "no key file specified in key info file > %s\n", > + hls->key_info_file); > + ret = AVERROR(EINVAL); > + goto fail; > + } > + av_free(hls->key_file); > + hls->key_file = av_strdup(tstr); > + if (!hls->key_file) { > + ret = AVERROR(ENOMEM); > + goto fail; > + } > + > + if ((ret = avio_open(&pb, hls->key_file, AVIO_FLAG_READ)) < 0) { > + av_log(hls, AV_LOG_ERROR, "error opening key file %s\n", > + hls->key_file); > + goto fail; > + } > + > + key = av_malloc(BLOCKSIZE); > + if (!key) { > + ret = AVERROR(ENOMEM); > + goto fail; > + } > + ret = avio_read(pb, key, BLOCKSIZE); > + avio_closep(&pb); > + if (ret != BLOCKSIZE) { > + av_log(hls, AV_LOG_ERROR, "error reading key file %s\n", > + hls->key_file); > + if (ret >= 0 || ret == AVERROR_EOF) > + ret = AVERROR(EINVAL); > + goto fail; > + } > + > + key_string = av_mallocz(BLOCKSIZE*2 + 1); > + if (!key_string) { > + ret = AVERROR(ENOMEM); > + goto fail; > + } > + ff_data_to_hex(key_string, key, BLOCKSIZE, 0); > + if (!hls->key_string || strncmp(key_string, hls->key_string, > BLOCKSIZE*2)) { > + av_free(hls->key_string); > + hls->key_string = av_strdup(key_string); > + if (!hls->key_string) { > + ret = AVERROR(ENOMEM); > + goto fail; > + } > + rotate_iv = 1; > + } > + > + if (!(hls->flags & HLS_RANDOM_IV)) { > + iv = av_mallocz(BLOCKSIZE); > + if (!iv) { > + ret = AVERROR(ENOMEM); > + goto fail; > + } > + for (i = 0; i < 8; i++) > + iv[BLOCKSIZE - 1 - i ] = (hls->sequence >> i*8) & 0xff; > + } else if (!hls->iv_string || rotate_iv) { > + iv = av_malloc(BLOCKSIZE); > + if (!iv) { > + ret = AVERROR(ENOMEM); > + goto fail; > + }
> + for (i = 0; i < BLOCKSIZE >> 2; i++) { > + u = av_lfg_get(hls->lfg); > + for (j = 0; j < 4; j++) > + iv[i*4 + j] = (u >> j*8) & 0xff; > + } combining the random seed with a LFG seems a bit odd i would out of principle use something stronger, though i dont know what the exact scenarios are this is intended to prevent, so maybe its fine [...] -- Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB It is dangerous to be right in matters on which the established authorities are wrong. -- Voltaire
signature.asc
Description: Digital signature
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel