From: Luis Scheurenbrand via ffmpeg-devel <ffmpeg-devel@ffmpeg.org> To: ffmpeg-devel@ffmpeg.org Cc: Luis Scheurenbrand <luis@scheurenbrand.me> Subject: [FFmpeg-devel] [PATCH] avformat/hls: Re-use crypto URLContext Date: Fri, 29 Sep 2023 18:50:27 +0200 Message-ID: <20230929165027.204216-1-luis@scheurenbrand.me> (raw) Reset crypto state and keep nested http context alive. Previously, an unencrypted file, followed by an encrypted file would result in a corrupted stream, as the stream would not be closed and an encrypted file would be forwarded to the demuxer. fixes: https://trac.ffmpeg.org/ticket/10599 fixes: https://trac.ffmpeg.org/ticket/8490 Signed-off-by: Luis Scheurenbrand <luis@scheurenbrand.me> --- libavformat/crypto.c | 55 ++++++++++++++++++++++++++++++++++++++++++++ libavformat/crypto.h | 38 ++++++++++++++++++++++++++++++ libavformat/hls.c | 32 ++++++++++++++++++++++---- 3 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 libavformat/crypto.h diff --git a/libavformat/crypto.c b/libavformat/crypto.c index 75b00020bc..543300c7c0 100644 --- a/libavformat/crypto.c +++ b/libavformat/crypto.c @@ -20,8 +20,10 @@ */ #include "libavutil/aes.h" +#include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/opt.h" +#include "crypto.h" #include "url.h" // encourage reads of 4096 bytes - 1 block is always retained. @@ -105,6 +107,59 @@ static int set_aes_arg(URLContext *h, uint8_t **buf, int *buf_len, return 0; } +URLContext *ff_crypto_get_inner(URLContext *h) +{ + CryptoContext *c = h->priv_data; + return c->hd; +} + +int ff_crypto_reset(URLContext *h) +{ + int ret = 0; + CryptoContext *c = h->priv_data; + int flags = c->flags; + + if (flags & AVIO_FLAG_READ) { + av_assert0(c->decrypt_key); + av_assert0(c->decrypt_iv); + av_assert0(c->aes_decrypt); + + memcpy(c->decrypt_key, c->key, c->keylen); + memcpy(c->decrypt_iv, c->iv, c->ivlen); + + ret = av_aes_init(c->aes_decrypt, c->decrypt_key, BLOCKSIZE * 8, 1); + if (ret < 0) + goto err; + } + + if (flags & AVIO_FLAG_WRITE) { + av_assert0(c->encrypt_key); + av_assert0(c->encrypt_iv); + av_assert0(c->aes_encrypt); + + memcpy(c->encrypt_key, c->key, c->keylen); + memcpy(c->encrypt_iv, c->iv, c->ivlen); + + ret = av_aes_init(c->aes_encrypt, c->encrypt_key, BLOCKSIZE * 8, 0); + if (ret < 0) + goto err; + } + + c->indata = c->indata_used = c->outdata = 0; + c->position = 0; + c->eof = 0; + c->outdata = 0; + c->outptr = 0; + + av_assert0(c->outbuffer); + av_assert0(c->inbuffer); + memset(c->outbuffer, 0, sizeof(c->outbuffer)); + memset(c->inbuffer, 0, sizeof(c->inbuffer)); + +err: + return ret; +} + static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary **options) { const char *nested_url; diff --git a/libavformat/crypto.h b/libavformat/crypto.h new file mode 100644 index 0000000000..7e6d7f0279 --- /dev/null +++ b/libavformat/crypto.h @@ -0,0 +1,38 @@ +/* + * HTTP definitions + * Copyright (c) 2010 Josh Allmann + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_CRYPTO_H +#define AVFORMAT_CRYPTO_H + +#include "url.h" + +URLContext *ff_crypto_get_inner(URLContext *h); + +/** + * Resets the crypto context. + * + * @param h pointer to the resource + * @return a negative value if an error condition occurred, 0 + * otherwise + */ +int ff_crypto_reset(URLContext *h); + +#endif /* AVFORMAT_CRYPTO_H */ diff --git a/libavformat/hls.c b/libavformat/hls.c index 332a5a15bb..7ad0ec9aec 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -44,6 +44,7 @@ #include "avio_internal.h" #include "id3v2.h" #include "url.h" +#include "crypto.h" #include "hls_sample_encryption.h" @@ -620,17 +621,38 @@ static int ensure_playlist(HLSContext *c, struct playlist **pls, const char *url return 0; } +/* compares two segments to ensure the encryption is the same and + * we can reuse the crypto and/or http context. */ +static int crypto_matches(const struct segment *lhs, const struct segment *rhs) { + if (!lhs || !rhs) + return 0; + if (lhs->key_type != rhs->key_type) + return 0; + if (lhs->key_type == KEY_NONE) + return 1; + + return + strcmp(lhs->key, rhs->key) == 0 && + memcmp(lhs->iv, rhs->iv, sizeof(lhs->iv)) == 0; +} + static int open_url_keepalive(AVFormatContext *s, AVIOContext **pb, const char *url, AVDictionary **options) { #if !CONFIG_HTTP_PROTOCOL return AVERROR_PROTOCOL_NOT_FOUND; #else - int ret; + int ret=0; URLContext *uc = ffio_geturlcontext(*pb); av_assert0(uc); (*pb)->eof_reached = 0; - ret = ff_http_do_new_request2(uc, url, options); + + if (av_strstart(url, "crypto+", &url)) { + ret = ff_crypto_reset(uc); + uc = ff_crypto_get_inner(uc); + } + + if (ret == 0) ret = ff_http_do_new_request2(uc, url, options); if (ret < 0) { ff_format_io_close(s, pb); } @@ -1592,7 +1614,7 @@ reload: seg = next_segment(v); if (c->http_multiple == 1 && !v->input_next_requested && - seg && seg->key_type == KEY_NONE && av_strstart(seg->url, "http", NULL)) { + seg && av_strstart(seg->url, "http", NULL)) { ret = open_input(c, v, seg, &v->input_next); if (ret < 0) { if (ff_check_interrupt(c->interrupt_callback)) @@ -1624,8 +1646,8 @@ reload: return ret; } - if (c->http_persistent && - seg->key_type == KEY_NONE && av_strstart(seg->url, "http", NULL)) { + if (c->http_persistent && crypto_matches(seg, next_segment(v)) && + av_strstart(seg->url, "http", NULL)) { v->input_read_done = 1; } else { ff_format_io_close(v->parent, &v->input); -- 2.42.0 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
next reply other threads:[~2023-09-29 16:51 UTC|newest] Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top 2023-09-29 16:50 Luis Scheurenbrand via ffmpeg-devel [this message] -- strict thread matches above, loose matches on Subject: below -- 2023-09-29 15:10 Luis Scheurenbrand via ffmpeg-devel 2023-09-29 16:25 ` Michael Niedermayer
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20230929165027.204216-1-luis@scheurenbrand.me \ --to=ffmpeg-devel@ffmpeg.org \ --cc=luis@scheurenbrand.me \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git