aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Hailes <btsimonh@googlemail.com>2016-08-30 09:40:14 +0100
committerMichael Niedermayer <michael@niedermayer.cc>2016-08-30 17:14:28 +0200
commita13a81a0dc98a35293f3f2603db9b01bf76b6a05 (patch)
tree3ada567d7e14db4edcd475b94a215bae90ffef11
parenta2fcacc880ca79b3f3636cf3e78d8c9164067224 (diff)
downloadffmpeg-a13a81a0dc98a35293f3f2603db9b01bf76b6a05.tar.gz
avformat/crypto: add seeking support on read
cyrpto allows reading of data which has been aes-128-cbc encrypted given a key and an iv. But it did not handle filetypes which require seeking... e.g. it failed on an encrypted .mp4 file. example: take 25.mp4 created with: ffmpeg -f lavfi -i sine=frequency=1000:beep_factor=2:r=48000:duration=720.0 -f lavfi -i testsrc=duration=720.0:rate=25 -vcodec libx264 -cmp 22 -timecode 10:00:00:00 -r 25 -y out\25.mp4 encrypt with: openssl enc -aes-128-cbc -K 12345678901234567890123456789012 -iv 12345678901234567890123456789012 -in 25.mp4 -out 25.enc then to transcode in ffmpeg: ffmpeg -key 12345678901234567890123456789012 -iv 12345678901234567890123456789012 -i crypto:25.enc -vcodec mpeg4 -r 25 -y 25dec.mp4 prior to this modification, the transcode would fail. Note also: crypto previously maked both reads and writes as streamed, which caused the whole file to be read before the transcode started. Now, for read only, if the underlying layer is not marked as streamed, then crypto is not. This should enable efficient reading of encrypted containers which require seeking. Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
-rw-r--r--libavformat/crypto.c116
1 files changed, 114 insertions, 2 deletions
diff --git a/libavformat/crypto.c b/libavformat/crypto.c
index 31a438c94c..346c7061b4 100644
--- a/libavformat/crypto.c
+++ b/libavformat/crypto.c
@@ -37,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;
@@ -110,6 +112,7 @@ static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary
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)) {
@@ -118,6 +121,8 @@ static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary
goto err;
}
+ c->position = 0;
+
if (flags & AVIO_FLAG_READ) {
if ((ret = set_aes_arg(c, &c->decrypt_key, &c->decrypt_keylen,
c->key, c->keylen, "decryption key")) < 0)
@@ -153,6 +158,10 @@ static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary
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) {
@@ -164,12 +173,13 @@ static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary
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;
}
c->pad_len = 0;
- h->is_streamed = 1;
-
err:
return ret;
}
@@ -184,6 +194,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,
@@ -223,6 +234,106 @@ retry:
goto retry;
}
+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;
@@ -288,6 +399,7 @@ static int crypto_close(URLContext *h)
const URLProtocol ff_crypto_protocol = {
.name = "crypto",
.url_open2 = crypto_open2,
+ .url_seek = crypto_seek,
.url_read = crypto_read,
.url_write = crypto_write,
.url_close = crypto_close,