00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "avformat.h"
00023 #include "libavutil/aes.h"
00024 #include "libavutil/avstring.h"
00025 #include "libavutil/opt.h"
00026 #include "internal.h"
00027 #include "url.h"
00028
00029 #define MAX_BUFFER_BLOCKS 150
00030 #define BLOCKSIZE 16
00031
00032 typedef struct {
00033 const AVClass *class;
00034 URLContext *hd;
00035 uint8_t inbuffer [BLOCKSIZE*MAX_BUFFER_BLOCKS],
00036 outbuffer[BLOCKSIZE*MAX_BUFFER_BLOCKS];
00037 uint8_t *outptr;
00038 int indata, indata_used, outdata;
00039 int eof;
00040 uint8_t *key;
00041 int keylen;
00042 uint8_t *iv;
00043 int ivlen;
00044 struct AVAES *aes;
00045 } CryptoContext;
00046
00047 #define OFFSET(x) offsetof(CryptoContext, x)
00048 static const AVOption options[] = {
00049 {"key", "AES decryption key", OFFSET(key), FF_OPT_TYPE_BINARY },
00050 {"iv", "AES decryption initialization vector", OFFSET(iv), FF_OPT_TYPE_BINARY },
00051 { NULL }
00052 };
00053
00054 static const AVClass crypto_class = {
00055 .class_name = "crypto",
00056 .item_name = av_default_item_name,
00057 .option = options,
00058 .version = LIBAVUTIL_VERSION_INT,
00059 };
00060
00061 static int crypto_open(URLContext *h, const char *uri, int flags)
00062 {
00063 const char *nested_url;
00064 int ret;
00065 CryptoContext *c = h->priv_data;
00066
00067 if (!av_strstart(uri, "crypto+", &nested_url) &&
00068 !av_strstart(uri, "crypto:", &nested_url)) {
00069 av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);
00070 ret = AVERROR(EINVAL);
00071 goto err;
00072 }
00073
00074 if (c->keylen < BLOCKSIZE || c->ivlen < BLOCKSIZE) {
00075 av_log(h, AV_LOG_ERROR, "Key or IV not set\n");
00076 ret = AVERROR(EINVAL);
00077 goto err;
00078 }
00079 if (flags & AVIO_FLAG_WRITE) {
00080 av_log(h, AV_LOG_ERROR, "Only decryption is supported currently\n");
00081 ret = AVERROR(ENOSYS);
00082 goto err;
00083 }
00084 if ((ret = ffurl_open(&c->hd, nested_url, AVIO_FLAG_READ)) < 0) {
00085 av_log(h, AV_LOG_ERROR, "Unable to open input\n");
00086 goto err;
00087 }
00088 c->aes = av_mallocz(av_aes_size);
00089 if (!c->aes) {
00090 ret = AVERROR(ENOMEM);
00091 goto err;
00092 }
00093
00094 av_aes_init(c->aes, c->key, 128, 1);
00095
00096 h->is_streamed = 1;
00097
00098 return 0;
00099 err:
00100 av_freep(&c->key);
00101 av_freep(&c->iv);
00102 return ret;
00103 }
00104
00105 static int crypto_read(URLContext *h, uint8_t *buf, int size)
00106 {
00107 CryptoContext *c = h->priv_data;
00108 int blocks;
00109 retry:
00110 if (c->outdata > 0) {
00111 size = FFMIN(size, c->outdata);
00112 memcpy(buf, c->outptr, size);
00113 c->outptr += size;
00114 c->outdata -= size;
00115 return size;
00116 }
00117
00118
00119
00120
00121 while (c->indata - c->indata_used < 2*BLOCKSIZE) {
00122 int n = ffurl_read(c->hd, c->inbuffer + c->indata,
00123 sizeof(c->inbuffer) - c->indata);
00124 if (n <= 0) {
00125 c->eof = 1;
00126 break;
00127 }
00128 c->indata += n;
00129 }
00130 blocks = (c->indata - c->indata_used) / BLOCKSIZE;
00131 if (!blocks)
00132 return AVERROR_EOF;
00133 if (!c->eof)
00134 blocks--;
00135 av_aes_crypt(c->aes, c->outbuffer, c->inbuffer + c->indata_used, blocks,
00136 c->iv, 1);
00137 c->outdata = BLOCKSIZE * blocks;
00138 c->outptr = c->outbuffer;
00139 c->indata_used += BLOCKSIZE * blocks;
00140 if (c->indata_used >= sizeof(c->inbuffer)/2) {
00141 memmove(c->inbuffer, c->inbuffer + c->indata_used,
00142 c->indata - c->indata_used);
00143 c->indata -= c->indata_used;
00144 c->indata_used = 0;
00145 }
00146 if (c->eof) {
00147
00148 int padding = c->outbuffer[c->outdata - 1];
00149 c->outdata -= padding;
00150 }
00151 goto retry;
00152 }
00153
00154 static int crypto_close(URLContext *h)
00155 {
00156 CryptoContext *c = h->priv_data;
00157 if (c->hd)
00158 ffurl_close(c->hd);
00159 av_freep(&c->aes);
00160 av_freep(&c->key);
00161 av_freep(&c->iv);
00162 return 0;
00163 }
00164
00165 URLProtocol ff_crypto_protocol = {
00166 .name = "crypto",
00167 .url_open = crypto_open,
00168 .url_read = crypto_read,
00169 .url_close = crypto_close,
00170 .priv_data_size = sizeof(CryptoContext),
00171 .priv_data_class = &crypto_class,
00172 .flags = URL_PROTOCOL_FLAG_NESTED_SCHEME,
00173 };