summaryrefslogtreecommitdiff
path: root/libavformat/crypto.c
diff options
context:
space:
mode:
Diffstat (limited to 'libavformat/crypto.c')
-rw-r--r--libavformat/crypto.c139
1 files changed, 125 insertions, 14 deletions
diff --git a/libavformat/crypto.c b/libavformat/crypto.c
index bb4adb688a..9a48f2e6f5 100644
--- a/libavformat/crypto.c
+++ b/libavformat/crypto.c
@@ -2,20 +2,20 @@
* Decryption protocol handler
* Copyright (c) 2011 Martin Storsjo
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * 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.
*
- * Libav is distributed in the hope that it will be useful,
+ * 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 Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -26,7 +26,8 @@
#include "internal.h"
#include "url.h"
-#define MAX_BUFFER_BLOCKS 150
+// encourage reads of 4096 bytes - 1 block is always retained.
+#define MAX_BUFFER_BLOCKS 257
#define BLOCKSIZE 16
typedef struct CryptoContext {
@@ -36,6 +37,8 @@ typedef struct CryptoContext {
outbuffer[BLOCKSIZE*MAX_BUFFER_BLOCKS];
uint8_t *outptr;
int indata, indata_used, outdata;
+ int64_t position; // position in file - used in seek
+ int flags;
int eof;
uint8_t *key;
int keylen;
@@ -91,10 +94,9 @@ static int set_aes_arg(URLContext *h, uint8_t **buf, int *buf_len,
desc, default_buf_len, BLOCKSIZE);
return AVERROR(EINVAL);
}
- *buf = av_malloc(default_buf_len);
+ *buf = av_memdup(default_buf, default_buf_len);
if (!*buf)
return AVERROR(ENOMEM);
- memcpy(*buf, default_buf, default_buf_len);
*buf_len = default_buf_len;
} else if (*buf_len != BLOCKSIZE) {
av_log(h, AV_LOG_ERROR,
@@ -105,11 +107,12 @@ static int set_aes_arg(URLContext *h, uint8_t **buf, int *buf_len,
return 0;
}
-static int crypto_open(URLContext *h, const char *uri, int flags)
+static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary **options)
{
const char *nested_url;
int ret = 0;
CryptoContext *c = h->priv_data;
+ c->flags = flags;
if (!av_strstart(uri, "crypto+", &nested_url) &&
!av_strstart(uri, "crypto:", &nested_url)) {
@@ -137,8 +140,9 @@ static int crypto_open(URLContext *h, const char *uri, int flags)
goto err;
}
- if ((ret = ffurl_open(&c->hd, nested_url, flags,
- &h->interrupt_callback, NULL, h->protocols, h)) < 0) {
+ if ((ret = ffurl_open_whitelist(&c->hd, nested_url, flags,
+ &h->interrupt_callback, options,
+ h->protocol_whitelist, h->protocol_blacklist, h)) < 0) {
av_log(h, AV_LOG_ERROR, "Unable to open resource: %s\n", nested_url);
goto err;
}
@@ -152,6 +156,10 @@ static int crypto_open(URLContext *h, const char *uri, int flags)
ret = av_aes_init(c->aes_decrypt, c->decrypt_key, BLOCKSIZE * 8, 1);
if (ret < 0)
goto err;
+
+ // pass back information about the context we openned
+ if (c->hd->is_streamed)
+ h->is_streamed = c->hd->is_streamed;
}
if (flags & AVIO_FLAG_WRITE) {
@@ -163,10 +171,11 @@ static int crypto_open(URLContext *h, const char *uri, int flags)
ret = av_aes_init(c->aes_encrypt, c->encrypt_key, BLOCKSIZE * 8, 0);
if (ret < 0)
goto err;
+ // for write, we must be streamed
+ // - linear write only for crytpo aes-128-cbc
+ h->is_streamed = 1;
}
- h->is_streamed = 1;
-
err:
return ret;
}
@@ -181,6 +190,7 @@ retry:
memcpy(buf, c->outptr, size);
c->outptr += size;
c->outdata -= size;
+ c->position = c->position + size;
return size;
}
// We avoid using the last block until we've found EOF,
@@ -220,7 +230,107 @@ retry:
goto retry;
}
-static int crypto_write(URLContext *h, const uint8_t *buf, int size)
+static int64_t crypto_seek(URLContext *h, int64_t pos, int whence)
+{
+ CryptoContext *c = h->priv_data;
+ int64_t block;
+ int64_t newpos;
+
+ if (c->flags & AVIO_FLAG_WRITE) {
+ av_log(h, AV_LOG_ERROR,
+ "Crypto: seek not supported for write\r\n");
+ /* seems the most appropriate error to return */
+ return AVERROR(ESPIPE);
+ }
+
+ // reset eof, else we won't read it correctly if we already hit eof.
+ c->eof = 0;
+
+ switch (whence) {
+ case SEEK_SET:
+ break;
+ case SEEK_CUR:
+ pos = pos + c->position;
+ break;
+ case SEEK_END: {
+ int64_t newpos = ffurl_seek( c->hd, pos, AVSEEK_SIZE );
+ if (newpos < 0) {
+ av_log(h, AV_LOG_ERROR,
+ "Crypto: seek_end - can't get file size (pos=%lld)\r\n", (long long int)pos);
+ return newpos;
+ }
+ pos = newpos - pos;
+ }
+ break;
+ case AVSEEK_SIZE: {
+ int64_t newpos = ffurl_seek( c->hd, pos, AVSEEK_SIZE );
+ return newpos;
+ }
+ break;
+ default:
+ av_log(h, AV_LOG_ERROR,
+ "Crypto: no support for seek where 'whence' is %d\r\n", whence);
+ return AVERROR(EINVAL);
+ }
+
+ c->outdata = 0;
+ c->indata = 0;
+ c->indata_used = 0;
+ c->outptr = c->outbuffer;
+
+ // identify the block containing the IV for the
+ // next block we will decrypt
+ block = pos/BLOCKSIZE;
+ if (block == 0) {
+ // restore the iv to the seed one - this is the iv for the FIRST block
+ memcpy( c->decrypt_iv, c->iv, c->ivlen );
+ c->position = 0;
+ } else {
+ // else, go back one block - we will get av_cyrpt to read this block
+ // which it will then store use as the iv.
+ // note that the DECRYPTED result will not be correct,
+ // but will be discarded
+ block--;
+ c->position = (block * BLOCKSIZE);
+ }
+
+ newpos = ffurl_seek( c->hd, c->position, SEEK_SET );
+ if (newpos < 0) {
+ av_log(h, AV_LOG_ERROR,
+ "Crypto: nested protocol no support for seek or seek failed\n");
+ return newpos;
+ }
+
+ // read and discard from here up to required position
+ // (which will set the iv correctly to it).
+ if (pos - c->position) {
+ uint8_t buff[BLOCKSIZE*2]; // maximum size of pos-c->position
+ int len = pos - c->position;
+ int res;
+
+ while (len > 0) {
+ // note: this may not return all the bytes first time
+ res = crypto_read(h, buff, len);
+ if (res < 0)
+ break;
+ len -= res;
+ }
+
+ // if we did not get all the bytes
+ if (len != 0) {
+ char errbuf[100] = "unknown error";
+ av_strerror(res, errbuf, sizeof(errbuf));
+ av_log(h, AV_LOG_ERROR,
+ "Crypto: discard read did not get all the bytes (%d remain) - read returned (%d)-%s\n",
+ len, res, errbuf);
+ return AVERROR(EINVAL);
+ }
+ }
+
+ return c->position;
+}
+
+static int crypto_write(URLContext *h, const unsigned char *buf, int size)
{
CryptoContext *c = h->priv_data;
int total_size, blocks, pad_len, out_size;
@@ -285,7 +395,8 @@ static int crypto_close(URLContext *h)
const URLProtocol ff_crypto_protocol = {
.name = "crypto",
- .url_open = crypto_open,
+ .url_open2 = crypto_open2,
+ .url_seek = crypto_seek,
.url_read = crypto_read,
.url_write = crypto_write,
.url_close = crypto_close,