FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/crypto.c
Date: 2023-09-24 13:02:57
Exec Total Coverage
Lines: 0 185 0.0%
Functions: 0 6 0.0%
Branches: 0 93 0.0%

Line Branch Exec Source
1 /*
2 * Decryption protocol handler
3 * Copyright (c) 2011 Martin Storsjo
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include "libavutil/aes.h"
23 #include "libavutil/avstring.h"
24 #include "libavutil/opt.h"
25 #include "url.h"
26
27 // encourage reads of 4096 bytes - 1 block is always retained.
28 #define MAX_BUFFER_BLOCKS 257
29 #define BLOCKSIZE 16
30
31 typedef struct CryptoContext {
32 const AVClass *class;
33 URLContext *hd;
34 uint8_t inbuffer [BLOCKSIZE*MAX_BUFFER_BLOCKS],
35 outbuffer[BLOCKSIZE*MAX_BUFFER_BLOCKS];
36 uint8_t *outptr;
37 int indata, indata_used, outdata;
38 int64_t position; // position in file - used in seek
39 int flags;
40 int eof;
41 uint8_t *key;
42 int keylen;
43 uint8_t *iv;
44 int ivlen;
45 uint8_t *decrypt_key;
46 int decrypt_keylen;
47 uint8_t *decrypt_iv;
48 int decrypt_ivlen;
49 uint8_t *encrypt_key;
50 int encrypt_keylen;
51 uint8_t *encrypt_iv;
52 int encrypt_ivlen;
53 struct AVAES *aes_decrypt;
54 struct AVAES *aes_encrypt;
55 uint8_t *write_buf;
56 unsigned int write_buf_size;
57 uint8_t pad[BLOCKSIZE];
58 int pad_len;
59 } CryptoContext;
60
61 #define OFFSET(x) offsetof(CryptoContext, x)
62 #define D AV_OPT_FLAG_DECODING_PARAM
63 #define E AV_OPT_FLAG_ENCODING_PARAM
64 static const AVOption options[] = {
65 {"key", "AES encryption/decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, .flags = D|E },
66 {"iv", "AES encryption/decryption initialization vector", OFFSET(iv), AV_OPT_TYPE_BINARY, .flags = D|E },
67 {"decryption_key", "AES decryption key", OFFSET(decrypt_key), AV_OPT_TYPE_BINARY, .flags = D },
68 {"decryption_iv", "AES decryption initialization vector", OFFSET(decrypt_iv), AV_OPT_TYPE_BINARY, .flags = D },
69 {"encryption_key", "AES encryption key", OFFSET(encrypt_key), AV_OPT_TYPE_BINARY, .flags = E },
70 {"encryption_iv", "AES encryption initialization vector", OFFSET(encrypt_iv), AV_OPT_TYPE_BINARY, .flags = E },
71 { NULL }
72 };
73
74 static const AVClass crypto_class = {
75 .class_name = "crypto",
76 .item_name = av_default_item_name,
77 .option = options,
78 .version = LIBAVUTIL_VERSION_INT,
79 };
80
81 static int set_aes_arg(URLContext *h, uint8_t **buf, int *buf_len,
82 uint8_t *default_buf, int default_buf_len,
83 const char *desc)
84 {
85 if (!*buf_len) {
86 if (!default_buf_len) {
87 av_log(h, AV_LOG_ERROR, "%s not set\n", desc);
88 return AVERROR(EINVAL);
89 } else if (default_buf_len != BLOCKSIZE) {
90 av_log(h, AV_LOG_ERROR,
91 "invalid %s size (%d bytes, block size is %d)\n",
92 desc, default_buf_len, BLOCKSIZE);
93 return AVERROR(EINVAL);
94 }
95 *buf = av_memdup(default_buf, default_buf_len);
96 if (!*buf)
97 return AVERROR(ENOMEM);
98 *buf_len = default_buf_len;
99 } else if (*buf_len != BLOCKSIZE) {
100 av_log(h, AV_LOG_ERROR,
101 "invalid %s size (%d bytes, block size is %d)\n",
102 desc, *buf_len, BLOCKSIZE);
103 return AVERROR(EINVAL);
104 }
105 return 0;
106 }
107
108 static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary **options)
109 {
110 const char *nested_url;
111 int ret = 0;
112 CryptoContext *c = h->priv_data;
113 c->flags = flags;
114
115 if (!av_strstart(uri, "crypto+", &nested_url) &&
116 !av_strstart(uri, "crypto:", &nested_url)) {
117 av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);
118 ret = AVERROR(EINVAL);
119 goto err;
120 }
121
122 if (flags & AVIO_FLAG_READ) {
123 if ((ret = set_aes_arg(h, &c->decrypt_key, &c->decrypt_keylen,
124 c->key, c->keylen, "decryption key")) < 0)
125 goto err;
126 if ((ret = set_aes_arg(h, &c->decrypt_iv, &c->decrypt_ivlen,
127 c->iv, c->ivlen, "decryption IV")) < 0)
128 goto err;
129 }
130
131 if (flags & AVIO_FLAG_WRITE) {
132 if ((ret = set_aes_arg(h, &c->encrypt_key, &c->encrypt_keylen,
133 c->key, c->keylen, "encryption key")) < 0)
134 if (ret < 0)
135 goto err;
136 if ((ret = set_aes_arg(h, &c->encrypt_iv, &c->encrypt_ivlen,
137 c->iv, c->ivlen, "encryption IV")) < 0)
138 goto err;
139 }
140
141 if ((ret = ffurl_open_whitelist(&c->hd, nested_url, flags,
142 &h->interrupt_callback, options,
143 h->protocol_whitelist, h->protocol_blacklist, h)) < 0) {
144 av_log(h, AV_LOG_ERROR, "Unable to open resource: %s\n", nested_url);
145 goto err;
146 }
147
148 if (flags & AVIO_FLAG_READ) {
149 c->aes_decrypt = av_aes_alloc();
150 if (!c->aes_decrypt) {
151 ret = AVERROR(ENOMEM);
152 goto err;
153 }
154 ret = av_aes_init(c->aes_decrypt, c->decrypt_key, BLOCKSIZE * 8, 1);
155 if (ret < 0)
156 goto err;
157
158 // pass back information about the context we openned
159 if (c->hd->is_streamed)
160 h->is_streamed = c->hd->is_streamed;
161 }
162
163 if (flags & AVIO_FLAG_WRITE) {
164 c->aes_encrypt = av_aes_alloc();
165 if (!c->aes_encrypt) {
166 ret = AVERROR(ENOMEM);
167 goto err;
168 }
169 ret = av_aes_init(c->aes_encrypt, c->encrypt_key, BLOCKSIZE * 8, 0);
170 if (ret < 0)
171 goto err;
172 // for write, we must be streamed
173 // - linear write only for crytpo aes-128-cbc
174 h->is_streamed = 1;
175 }
176
177 err:
178 return ret;
179 }
180
181 static int crypto_read(URLContext *h, uint8_t *buf, int size)
182 {
183 CryptoContext *c = h->priv_data;
184 int blocks;
185 retry:
186 if (c->outdata > 0) {
187 size = FFMIN(size, c->outdata);
188 memcpy(buf, c->outptr, size);
189 c->outptr += size;
190 c->outdata -= size;
191 c->position = c->position + size;
192 return size;
193 }
194 // We avoid using the last block until we've found EOF,
195 // since we'll remove PKCS7 padding at the end. So make
196 // sure we've got at least 2 blocks, so we can decrypt
197 // at least one.
198 while (c->indata - c->indata_used < 2*BLOCKSIZE) {
199 int n = ffurl_read(c->hd, c->inbuffer + c->indata,
200 sizeof(c->inbuffer) - c->indata);
201 if (n <= 0) {
202 c->eof = 1;
203 break;
204 }
205 c->indata += n;
206 }
207 blocks = (c->indata - c->indata_used) / BLOCKSIZE;
208 if (!blocks)
209 return AVERROR_EOF;
210 if (!c->eof)
211 blocks--;
212 av_aes_crypt(c->aes_decrypt, c->outbuffer, c->inbuffer + c->indata_used,
213 blocks, c->decrypt_iv, 1);
214 c->outdata = BLOCKSIZE * blocks;
215 c->outptr = c->outbuffer;
216 c->indata_used += BLOCKSIZE * blocks;
217 if (c->indata_used >= sizeof(c->inbuffer)/2) {
218 memmove(c->inbuffer, c->inbuffer + c->indata_used,
219 c->indata - c->indata_used);
220 c->indata -= c->indata_used;
221 c->indata_used = 0;
222 }
223 if (c->eof) {
224 // Remove PKCS7 padding at the end
225 int padding = c->outbuffer[c->outdata - 1];
226 c->outdata -= padding;
227 }
228 goto retry;
229 }
230
231 static int64_t crypto_seek(URLContext *h, int64_t pos, int whence)
232 {
233 CryptoContext *c = h->priv_data;
234 int64_t block;
235 int64_t newpos;
236
237 if (c->flags & AVIO_FLAG_WRITE) {
238 av_log(h, AV_LOG_ERROR,
239 "Crypto: seek not supported for write\r\n");
240 /* seems the most appropriate error to return */
241 return AVERROR(ESPIPE);
242 }
243
244 // reset eof, else we won't read it correctly if we already hit eof.
245 c->eof = 0;
246
247 switch (whence) {
248 case SEEK_SET:
249 break;
250 case SEEK_CUR:
251 pos = pos + c->position;
252 break;
253 case SEEK_END:
254 newpos = ffurl_seek( c->hd, pos, AVSEEK_SIZE );
255 if (newpos < 0) {
256 av_log(h, AV_LOG_ERROR,
257 "Crypto: seek_end - can't get file size (pos=%lld)\r\n", (long long int)pos);
258 return newpos;
259 }
260 pos = newpos - pos;
261 break;
262 case AVSEEK_SIZE:
263 return ffurl_seek( c->hd, pos, AVSEEK_SIZE );
264 default:
265 av_log(h, AV_LOG_ERROR,
266 "Crypto: no support for seek where 'whence' is %d\r\n", whence);
267 return AVERROR(EINVAL);
268 }
269
270 c->outdata = 0;
271 c->indata = 0;
272 c->indata_used = 0;
273 c->outptr = c->outbuffer;
274
275 // identify the block containing the IV for the
276 // next block we will decrypt
277 block = pos/BLOCKSIZE;
278 if (block == 0) {
279 // restore the iv to the seed one - this is the iv for the FIRST block
280 memcpy( c->decrypt_iv, c->iv, c->ivlen );
281 c->position = 0;
282 } else {
283 // else, go back one block - we will get av_cyrpt to read this block
284 // which it will then store use as the iv.
285 // note that the DECRYPTED result will not be correct,
286 // but will be discarded
287 block--;
288 c->position = (block * BLOCKSIZE);
289 }
290
291 newpos = ffurl_seek( c->hd, c->position, SEEK_SET );
292 if (newpos < 0) {
293 av_log(h, AV_LOG_ERROR,
294 "Crypto: nested protocol no support for seek or seek failed\n");
295 return newpos;
296 }
297
298 // read and discard from here up to required position
299 // (which will set the iv correctly to it).
300 if (pos - c->position) {
301 uint8_t buff[BLOCKSIZE*2]; // maximum size of pos-c->position
302 int len = pos - c->position;
303 int res;
304
305 while (len > 0) {
306 // note: this may not return all the bytes first time
307 res = crypto_read(h, buff, len);
308 if (res < 0)
309 break;
310 len -= res;
311 }
312
313 // if we did not get all the bytes
314 if (len != 0) {
315 char errbuf[100] = "unknown error";
316 av_strerror(res, errbuf, sizeof(errbuf));
317 av_log(h, AV_LOG_ERROR,
318 "Crypto: discard read did not get all the bytes (%d remain) - read returned (%d)-%s\n",
319 len, res, errbuf);
320 return AVERROR(EINVAL);
321 }
322 }
323
324 return c->position;
325 }
326
327 static int crypto_write(URLContext *h, const unsigned char *buf, int size)
328 {
329 CryptoContext *c = h->priv_data;
330 int total_size, blocks, pad_len, out_size;
331 int ret = 0;
332
333 total_size = size + c->pad_len;
334 pad_len = total_size % BLOCKSIZE;
335 out_size = total_size - pad_len;
336 blocks = out_size / BLOCKSIZE;
337
338 if (out_size) {
339 av_fast_malloc(&c->write_buf, &c->write_buf_size, out_size);
340
341 if (!c->write_buf)
342 return AVERROR(ENOMEM);
343
344 if (c->pad_len) {
345 memcpy(&c->pad[c->pad_len], buf, BLOCKSIZE - c->pad_len);
346 av_aes_crypt(c->aes_encrypt, c->write_buf, c->pad, 1, c->encrypt_iv, 0);
347 blocks--;
348 }
349
350 av_aes_crypt(c->aes_encrypt,
351 &c->write_buf[c->pad_len ? BLOCKSIZE : 0],
352 &buf[c->pad_len ? BLOCKSIZE - c->pad_len : 0],
353 blocks, c->encrypt_iv, 0);
354
355 ret = ffurl_write(c->hd, c->write_buf, out_size);
356 if (ret < 0)
357 return ret;
358
359 memcpy(c->pad, &buf[size - pad_len], pad_len);
360 } else
361 memcpy(&c->pad[c->pad_len], buf, size);
362
363 c->pad_len = pad_len;
364
365 return size;
366 }
367
368 static int crypto_close(URLContext *h)
369 {
370 CryptoContext *c = h->priv_data;
371 int ret = 0;
372
373 if (c->aes_encrypt) {
374 uint8_t out_buf[BLOCKSIZE];
375 int pad = BLOCKSIZE - c->pad_len;
376
377 memset(&c->pad[c->pad_len], pad, pad);
378 av_aes_crypt(c->aes_encrypt, out_buf, c->pad, 1, c->encrypt_iv, 0);
379 ret = ffurl_write(c->hd, out_buf, BLOCKSIZE);
380 }
381
382 ffurl_closep(&c->hd);
383 av_freep(&c->aes_decrypt);
384 av_freep(&c->aes_encrypt);
385 av_freep(&c->write_buf);
386 return ret;
387 }
388
389 const URLProtocol ff_crypto_protocol = {
390 .name = "crypto",
391 .url_open2 = crypto_open2,
392 .url_seek = crypto_seek,
393 .url_read = crypto_read,
394 .url_write = crypto_write,
395 .url_close = crypto_close,
396 .priv_data_size = sizeof(CryptoContext),
397 .priv_data_class = &crypto_class,
398 .flags = URL_PROTOCOL_FLAG_NESTED_SCHEME,
399 };
400