2 * Decryption protocol handler
3 * Copyright (c) 2011 Martin Storsjo
5 * This file is part of FFmpeg.
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.
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.
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
23 #include "libavutil/aes.h"
24 #include "libavutil/avstring.h"
25 #include "libavutil/opt.h"
29 // encourage reads of 4096 bytes - 1 block is always retained.
30 #define MAX_BUFFER_BLOCKS 257
33 typedef struct CryptoContext
{
36 uint8_t inbuffer
[BLOCKSIZE
*MAX_BUFFER_BLOCKS
],
37 outbuffer
[BLOCKSIZE
*MAX_BUFFER_BLOCKS
];
39 int indata
, indata_used
, outdata
;
40 int64_t position
; // position in file - used in seek
55 struct AVAES
*aes_decrypt
;
56 struct AVAES
*aes_encrypt
;
58 uint8_t pad
[BLOCKSIZE
];
63 #define OFFSET(x) offsetof(CryptoContext, x)
64 #define D AV_OPT_FLAG_DECODING_PARAM
65 #define E AV_OPT_FLAG_ENCODING_PARAM
66 static const AVOption options
[] = {
67 {"key", "AES encryption/decryption key", OFFSET(key
), AV_OPT_TYPE_BINARY
, .flags
= D
|E
},
68 {"iv", "AES encryption/decryption initialization vector", OFFSET(iv
), AV_OPT_TYPE_BINARY
, .flags
= D
|E
},
69 {"decryption_key", "AES decryption key", OFFSET(decrypt_key
), AV_OPT_TYPE_BINARY
, .flags
= D
},
70 {"decryption_iv", "AES decryption initialization vector", OFFSET(decrypt_iv
), AV_OPT_TYPE_BINARY
, .flags
= D
},
71 {"encryption_key", "AES encryption key", OFFSET(encrypt_key
), AV_OPT_TYPE_BINARY
, .flags
= E
},
72 {"encryption_iv", "AES encryption initialization vector", OFFSET(encrypt_iv
), AV_OPT_TYPE_BINARY
, .flags
= E
},
76 static const AVClass crypto_class
= {
77 .class_name
= "crypto",
78 .item_name
= av_default_item_name
,
80 .version
= LIBAVUTIL_VERSION_INT
,
83 static int set_aes_arg(CryptoContext
*c
, uint8_t **buf
, int *buf_len
,
84 uint8_t *default_buf
, int default_buf_len
,
88 if (!default_buf_len
) {
89 av_log(c
, AV_LOG_ERROR
, "%s not set\n", desc
);
90 return AVERROR(EINVAL
);
91 } else if (default_buf_len
!= BLOCKSIZE
) {
92 av_log(c
, AV_LOG_ERROR
,
93 "invalid %s size (%d bytes, block size is %d)\n",
94 desc
, default_buf_len
, BLOCKSIZE
);
95 return AVERROR(EINVAL
);
97 *buf
= av_memdup(default_buf
, default_buf_len
);
99 return AVERROR(ENOMEM
);
100 *buf_len
= default_buf_len
;
101 } else if (*buf_len
!= BLOCKSIZE
) {
102 av_log(c
, AV_LOG_ERROR
,
103 "invalid %s size (%d bytes, block size is %d)\n",
104 desc
, *buf_len
, BLOCKSIZE
);
105 return AVERROR(EINVAL
);
110 static int crypto_open2(URLContext
*h
, const char *uri
, int flags
, AVDictionary
**options
)
112 const char *nested_url
;
114 CryptoContext
*c
= h
->priv_data
;
117 if (!av_strstart(uri
, "crypto+", &nested_url
) &&
118 !av_strstart(uri
, "crypto:", &nested_url
)) {
119 av_log(h
, AV_LOG_ERROR
, "Unsupported url %s\n", uri
);
120 ret
= AVERROR(EINVAL
);
126 if (flags
& AVIO_FLAG_READ
) {
127 if ((ret
= set_aes_arg(c
, &c
->decrypt_key
, &c
->decrypt_keylen
,
128 c
->key
, c
->keylen
, "decryption key")) < 0)
130 if ((ret
= set_aes_arg(c
, &c
->decrypt_iv
, &c
->decrypt_ivlen
,
131 c
->iv
, c
->ivlen
, "decryption IV")) < 0)
135 if (flags
& AVIO_FLAG_WRITE
) {
136 if ((ret
= set_aes_arg(c
, &c
->encrypt_key
, &c
->encrypt_keylen
,
137 c
->key
, c
->keylen
, "encryption key")) < 0)
140 if ((ret
= set_aes_arg(c
, &c
->encrypt_iv
, &c
->encrypt_ivlen
,
141 c
->iv
, c
->ivlen
, "encryption IV")) < 0)
145 if ((ret
= ffurl_open_whitelist(&c
->hd
, nested_url
, flags
,
146 &h
->interrupt_callback
, options
,
147 h
->protocol_whitelist
, h
->protocol_blacklist
, h
)) < 0) {
148 av_log(h
, AV_LOG_ERROR
, "Unable to open resource: %s\n", nested_url
);
152 if (flags
& AVIO_FLAG_READ
) {
153 c
->aes_decrypt
= av_aes_alloc();
154 if (!c
->aes_decrypt
) {
155 ret
= AVERROR(ENOMEM
);
158 ret
= av_aes_init(c
->aes_decrypt
, c
->decrypt_key
, BLOCKSIZE
*8, 1);
162 // pass back information about the context we openned
163 if (c
->hd
->is_streamed
)
164 h
->is_streamed
= c
->hd
->is_streamed
;
167 if (flags
& AVIO_FLAG_WRITE
) {
168 c
->aes_encrypt
= av_aes_alloc();
169 if (!c
->aes_encrypt
) {
170 ret
= AVERROR(ENOMEM
);
173 ret
= av_aes_init(c
->aes_encrypt
, c
->encrypt_key
, BLOCKSIZE
*8, 0);
176 // for write, we must be streamed
177 // - linear write only for crytpo aes-128-cbc
187 static int crypto_read(URLContext
*h
, uint8_t *buf
, int size
)
189 CryptoContext
*c
= h
->priv_data
;
192 if (c
->outdata
> 0) {
193 size
= FFMIN(size
, c
->outdata
);
194 memcpy(buf
, c
->outptr
, size
);
197 c
->position
= c
->position
+ size
;
200 // We avoid using the last block until we've found EOF,
201 // since we'll remove PKCS7 padding at the end. So make
202 // sure we've got at least 2 blocks, so we can decrypt
204 while (c
->indata
- c
->indata_used
< 2*BLOCKSIZE
) {
205 int n
= ffurl_read(c
->hd
, c
->inbuffer
+ c
->indata
,
206 sizeof(c
->inbuffer
) - c
->indata
);
213 blocks
= (c
->indata
- c
->indata_used
) / BLOCKSIZE
;
218 av_aes_crypt(c
->aes_decrypt
, c
->outbuffer
, c
->inbuffer
+ c
->indata_used
,
219 blocks
, c
->decrypt_iv
, 1);
220 c
->outdata
= BLOCKSIZE
* blocks
;
221 c
->outptr
= c
->outbuffer
;
222 c
->indata_used
+= BLOCKSIZE
* blocks
;
223 if (c
->indata_used
>= sizeof(c
->inbuffer
)/2) {
224 memmove(c
->inbuffer
, c
->inbuffer
+ c
->indata_used
,
225 c
->indata
- c
->indata_used
);
226 c
->indata
-= c
->indata_used
;
230 // Remove PKCS7 padding at the end
231 int padding
= c
->outbuffer
[c
->outdata
- 1];
232 c
->outdata
-= padding
;
237 static int64_t crypto_seek(URLContext
*h
, int64_t pos
, int whence
)
239 CryptoContext
*c
= h
->priv_data
;
243 if (c
->flags
& AVIO_FLAG_WRITE
) {
244 av_log(h
, AV_LOG_ERROR
,
245 "Crypto: seek not supported for write\r\n");
246 /* seems the most appropriate error to return */
247 return AVERROR(ESPIPE
);
250 // reset eof, else we won't read it correctly if we already hit eof.
257 pos
= pos
+ c
->position
;
260 int64_t newpos
= ffurl_seek( c
->hd
, pos
, AVSEEK_SIZE
);
262 av_log(h
, AV_LOG_ERROR
,
263 "Crypto: seek_end - can't get file size (pos=%lld)\r\n", (long long int)pos
);
270 int64_t newpos
= ffurl_seek( c
->hd
, pos
, AVSEEK_SIZE
);
275 av_log(h
, AV_LOG_ERROR
,
276 "Crypto: no support for seek where 'whence' is %d\r\n", whence
);
277 return AVERROR(EINVAL
);
283 c
->outptr
= c
->outbuffer
;
285 // identify the block containing the IV for the
286 // next block we will decrypt
287 block
= pos
/BLOCKSIZE
;
289 // restore the iv to the seed one - this is the iv for the FIRST block
290 memcpy( c
->decrypt_iv
, c
->iv
, c
->ivlen
);
293 // else, go back one block - we will get av_cyrpt to read this block
294 // which it will then store use as the iv.
295 // note that the DECRYPTED result will not be correct,
296 // but will be discarded
298 c
->position
= (block
* BLOCKSIZE
);
301 newpos
= ffurl_seek( c
->hd
, c
->position
, SEEK_SET
);
303 av_log(h
, AV_LOG_ERROR
,
304 "Crypto: nested protocol no support for seek or seek failed\n");
308 // read and discard from here up to required position
309 // (which will set the iv correctly to it).
310 if (pos
- c
->position
) {
311 uint8_t buff
[BLOCKSIZE
*2]; // maximum size of pos-c->position
312 int len
= pos
- c
->position
;
316 // note: this may not return all the bytes first time
317 res
= crypto_read(h
, buff
, len
);
323 // if we did not get all the bytes
325 char errbuf
[100] = "unknown error";
326 av_strerror(res
, errbuf
, sizeof(errbuf
));
327 av_log(h
, AV_LOG_ERROR
,
328 "Crypto: discard read did not get all the bytes (%d remain) - read returned (%d)-%s\n",
330 return AVERROR(EINVAL
);
337 static int crypto_write(URLContext
*h
, const unsigned char *buf
, int size
)
339 CryptoContext
*c
= h
->priv_data
;
340 int total_size
, blocks
, pad_len
, out_size
;
344 total_size
= size
+ c
->pad_len
;
345 pad_len
= total_size
% BLOCKSIZE
;
346 out_size
= total_size
- pad_len
;
347 blocks
= out_size
/ BLOCKSIZE
;
350 out_buf
= av_malloc(out_size
);
352 return AVERROR(ENOMEM
);
355 memcpy(&c
->pad
[c
->pad_len
], buf
, BLOCKSIZE
- c
->pad_len
);
356 av_aes_crypt(c
->aes_encrypt
, out_buf
, c
->pad
, 1, c
->encrypt_iv
, 0);
360 av_aes_crypt(c
->aes_encrypt
, &out_buf
[c
->pad_len
? BLOCKSIZE
: 0],
361 &buf
[c
->pad_len
? BLOCKSIZE
- c
->pad_len
: 0],
362 blocks
, c
->encrypt_iv
, 0);
364 ret
= ffurl_write(c
->hd
, out_buf
, out_size
);
369 memcpy(c
->pad
, &buf
[size
- pad_len
], pad_len
);
371 memcpy(&c
->pad
[c
->pad_len
], buf
, size
);
373 c
->pad_len
= pad_len
;
378 static int crypto_close(URLContext
*h
)
380 CryptoContext
*c
= h
->priv_data
;
381 uint8_t out_buf
[BLOCKSIZE
];
384 if (c
->aes_encrypt
) {
385 pad
= BLOCKSIZE
- c
->pad_len
;
386 memset(&c
->pad
[c
->pad_len
], pad
, pad
);
387 av_aes_crypt(c
->aes_encrypt
, out_buf
, c
->pad
, 1, c
->encrypt_iv
, 0);
388 if ((ret
= ffurl_write(c
->hd
, out_buf
, BLOCKSIZE
)) < 0)
394 av_freep(&c
->aes_decrypt
);
395 av_freep(&c
->aes_encrypt
);
399 const URLProtocol ff_crypto_protocol
= {
401 .url_open2
= crypto_open2
,
402 .url_seek
= crypto_seek
,
403 .url_read
= crypto_read
,
404 .url_write
= crypto_write
,
405 .url_close
= crypto_close
,
406 .priv_data_size
= sizeof(CryptoContext
),
407 .priv_data_class
= &crypto_class
,
408 .flags
= URL_PROTOCOL_FLAG_NESTED_SCHEME
,