Libav 0.7.1
|
00001 /* 00002 * Decryption protocol handler 00003 * Copyright (c) 2011 Martin Storsjo 00004 * 00005 * This file is part of Libav. 00006 * 00007 * Libav is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * Libav is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with Libav; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 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 // We avoid using the last block until we've found EOF, 00118 // since we'll remove PKCS7 padding at the end. So make 00119 // sure we've got at least 2 blocks, so we can decrypt 00120 // at least one. 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 // Remove PKCS7 padding at the end 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 };