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 #define D AV_OPT_FLAG_DECODING_PARAM
00049 static const AVOption options[] = {
00050 {"key", "AES decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, .flags = D },
00051 {"iv", "AES decryption initialization vector", OFFSET(iv), AV_OPT_TYPE_BINARY, .flags = D },
00052 { NULL }
00053 };
00054
00055 static const AVClass crypto_class = {
00056 .class_name = "crypto",
00057 .item_name = av_default_item_name,
00058 .option = options,
00059 .version = LIBAVUTIL_VERSION_INT,
00060 };
00061
00062 static int crypto_open(URLContext *h, const char *uri, int flags)
00063 {
00064 const char *nested_url;
00065 int ret = 0;
00066 CryptoContext *c = h->priv_data;
00067
00068 if (!av_strstart(uri, "crypto+", &nested_url) &&
00069 !av_strstart(uri, "crypto:", &nested_url)) {
00070 av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);
00071 ret = AVERROR(EINVAL);
00072 goto err;
00073 }
00074
00075 if (c->keylen < BLOCKSIZE || c->ivlen < BLOCKSIZE) {
00076 av_log(h, AV_LOG_ERROR, "Key or IV not set\n");
00077 ret = AVERROR(EINVAL);
00078 goto err;
00079 }
00080 if (flags & AVIO_FLAG_WRITE) {
00081 av_log(h, AV_LOG_ERROR, "Only decryption is supported currently\n");
00082 ret = AVERROR(ENOSYS);
00083 goto err;
00084 }
00085 if ((ret = ffurl_open(&c->hd, nested_url, AVIO_FLAG_READ,
00086 &h->interrupt_callback, NULL)) < 0) {
00087 av_log(h, AV_LOG_ERROR, "Unable to open input\n");
00088 goto err;
00089 }
00090 c->aes = av_mallocz(av_aes_size);
00091 if (!c->aes) {
00092 ret = AVERROR(ENOMEM);
00093 goto err;
00094 }
00095
00096 av_aes_init(c->aes, c->key, 128, 1);
00097
00098 h->is_streamed = 1;
00099
00100 err:
00101 return ret;
00102 }
00103
00104 static int crypto_read(URLContext *h, uint8_t *buf, int size)
00105 {
00106 CryptoContext *c = h->priv_data;
00107 int blocks;
00108 retry:
00109 if (c->outdata > 0) {
00110 size = FFMIN(size, c->outdata);
00111 memcpy(buf, c->outptr, size);
00112 c->outptr += size;
00113 c->outdata -= size;
00114 return size;
00115 }
00116
00117
00118
00119
00120 while (c->indata - c->indata_used < 2*BLOCKSIZE) {
00121 int n = ffurl_read(c->hd, c->inbuffer + c->indata,
00122 sizeof(c->inbuffer) - c->indata);
00123 if (n <= 0) {
00124 c->eof = 1;
00125 break;
00126 }
00127 c->indata += n;
00128 }
00129 blocks = (c->indata - c->indata_used) / BLOCKSIZE;
00130 if (!blocks)
00131 return AVERROR_EOF;
00132 if (!c->eof)
00133 blocks--;
00134 av_aes_crypt(c->aes, c->outbuffer, c->inbuffer + c->indata_used, blocks,
00135 c->iv, 1);
00136 c->outdata = BLOCKSIZE * blocks;
00137 c->outptr = c->outbuffer;
00138 c->indata_used += BLOCKSIZE * blocks;
00139 if (c->indata_used >= sizeof(c->inbuffer)/2) {
00140 memmove(c->inbuffer, c->inbuffer + c->indata_used,
00141 c->indata - c->indata_used);
00142 c->indata -= c->indata_used;
00143 c->indata_used = 0;
00144 }
00145 if (c->eof) {
00146
00147 int padding = c->outbuffer[c->outdata - 1];
00148 c->outdata -= padding;
00149 }
00150 goto retry;
00151 }
00152
00153 static int crypto_close(URLContext *h)
00154 {
00155 CryptoContext *c = h->priv_data;
00156 if (c->hd)
00157 ffurl_close(c->hd);
00158 av_freep(&c->aes);
00159 return 0;
00160 }
00161
00162 URLProtocol ff_crypto_protocol = {
00163 .name = "crypto",
00164 .url_open = crypto_open,
00165 .url_read = crypto_read,
00166 .url_close = crypto_close,
00167 .priv_data_size = sizeof(CryptoContext),
00168 .priv_data_class = &crypto_class,
00169 .flags = URL_PROTOCOL_FLAG_NESTED_SCHEME,
00170 };