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
22 #include "libavutil/aes.h"
23 #include "libavutil/avstring.h"
24 #include "libavutil/mem.h"
25 #include "libavutil/opt.h"
28 // encourage reads of 4096 bytes - 1 block is always retained.
29 #define MAX_BUFFER_BLOCKS 257
32 typedef struct CryptoContext
{
35 uint8_t inbuffer
[BLOCKSIZE
*MAX_BUFFER_BLOCKS
],
36 outbuffer
[BLOCKSIZE
*MAX_BUFFER_BLOCKS
];
38 int indata
, indata_used
, outdata
;
39 int64_t position
; // position in file - used in seek
54 struct AVAES
*aes_decrypt
;
55 struct AVAES
*aes_encrypt
;
57 unsigned int write_buf_size
;
58 uint8_t pad
[BLOCKSIZE
];
62 #define OFFSET(x) offsetof(CryptoContext, x)
63 #define D AV_OPT_FLAG_DECODING_PARAM
64 #define E AV_OPT_FLAG_ENCODING_PARAM
65 static const AVOption crypto_options
[] = {
66 {"key", "AES encryption/decryption key", OFFSET(key
), AV_OPT_TYPE_BINARY
, .flags
= D
|E
},
67 {"iv", "AES encryption/decryption initialization vector", OFFSET(iv
), AV_OPT_TYPE_BINARY
, .flags
= D
|E
},
68 {"decryption_key", "AES decryption key", OFFSET(decrypt_key
), AV_OPT_TYPE_BINARY
, .flags
= D
},
69 {"decryption_iv", "AES decryption initialization vector", OFFSET(decrypt_iv
), AV_OPT_TYPE_BINARY
, .flags
= D
},
70 {"encryption_key", "AES encryption key", OFFSET(encrypt_key
), AV_OPT_TYPE_BINARY
, .flags
= E
},
71 {"encryption_iv", "AES encryption initialization vector", OFFSET(encrypt_iv
), AV_OPT_TYPE_BINARY
, .flags
= E
},
75 static const AVClass crypto_class
= {
76 .class_name
= "crypto",
77 .item_name
= av_default_item_name
,
78 .option
= crypto_options
,
79 .version
= LIBAVUTIL_VERSION_INT
,
82 static int set_aes_arg(URLContext
*h
, uint8_t **buf
, int *buf_len
,
83 uint8_t *default_buf
, int default_buf_len
,
87 if (!default_buf_len
) {
88 av_log(h
, AV_LOG_ERROR
, "%s not set\n", desc
);
89 return AVERROR(EINVAL
);
90 } else if (default_buf_len
!= BLOCKSIZE
) {
91 av_log(h
, AV_LOG_ERROR
,
92 "invalid %s size (%d bytes, block size is %d)\n",
93 desc
, default_buf_len
, BLOCKSIZE
);
94 return AVERROR(EINVAL
);
96 *buf
= av_memdup(default_buf
, default_buf_len
);
98 return AVERROR(ENOMEM
);
99 *buf_len
= default_buf_len
;
100 } else if (*buf_len
!= BLOCKSIZE
) {
101 av_log(h
, AV_LOG_ERROR
,
102 "invalid %s size (%d bytes, block size is %d)\n",
103 desc
, *buf_len
, BLOCKSIZE
);
104 return AVERROR(EINVAL
);
109 static int crypto_open2(URLContext
*h
, const char *uri
, int flags
, AVDictionary
**options
)
111 const char *nested_url
;
113 CryptoContext
*c
= h
->priv_data
;
116 if (!av_strstart(uri
, "crypto+", &nested_url
) &&
117 !av_strstart(uri
, "crypto:", &nested_url
)) {
118 av_log(h
, AV_LOG_ERROR
, "Unsupported url %s\n", uri
);
119 ret
= AVERROR(EINVAL
);
123 if (flags
& AVIO_FLAG_READ
) {
124 if ((ret
= set_aes_arg(h
, &c
->decrypt_key
, &c
->decrypt_keylen
,
125 c
->key
, c
->keylen
, "decryption key")) < 0)
127 if ((ret
= set_aes_arg(h
, &c
->decrypt_iv
, &c
->decrypt_ivlen
,
128 c
->iv
, c
->ivlen
, "decryption IV")) < 0)
132 if (flags
& AVIO_FLAG_WRITE
) {
133 if ((ret
= set_aes_arg(h
, &c
->encrypt_key
, &c
->encrypt_keylen
,
134 c
->key
, c
->keylen
, "encryption key")) < 0)
137 if ((ret
= set_aes_arg(h
, &c
->encrypt_iv
, &c
->encrypt_ivlen
,
138 c
->iv
, c
->ivlen
, "encryption IV")) < 0)
142 if ((ret
= ffurl_open_whitelist(&c
->hd
, nested_url
, flags
,
143 &h
->interrupt_callback
, options
,
144 h
->protocol_whitelist
, h
->protocol_blacklist
, h
)) < 0) {
145 av_log(h
, AV_LOG_ERROR
, "Unable to open resource: %s\n", nested_url
);
149 if (flags
& AVIO_FLAG_READ
) {
150 c
->aes_decrypt
= av_aes_alloc();
151 if (!c
->aes_decrypt
) {
152 ret
= AVERROR(ENOMEM
);
155 ret
= av_aes_init(c
->aes_decrypt
, c
->decrypt_key
, BLOCKSIZE
* 8, 1);
159 // pass back information about the context we opened
160 if (c
->hd
->is_streamed
)
161 h
->is_streamed
= c
->hd
->is_streamed
;
164 if (flags
& AVIO_FLAG_WRITE
) {
165 c
->aes_encrypt
= av_aes_alloc();
166 if (!c
->aes_encrypt
) {
167 ret
= AVERROR(ENOMEM
);
170 ret
= av_aes_init(c
->aes_encrypt
, c
->encrypt_key
, BLOCKSIZE
* 8, 0);
173 // for write, we must be streamed
174 // - linear write only for crypto aes-128-cbc
182 static int crypto_read(URLContext
*h
, uint8_t *buf
, int size
)
184 CryptoContext
*c
= h
->priv_data
;
187 if (c
->outdata
> 0) {
188 size
= FFMIN(size
, c
->outdata
);
189 memcpy(buf
, c
->outptr
, size
);
192 c
->position
= c
->position
+ size
;
195 // We avoid using the last block until we've found EOF,
196 // since we'll remove PKCS7 padding at the end. So make
197 // sure we've got at least 2 blocks, so we can decrypt
199 while (c
->indata
- c
->indata_used
< 2*BLOCKSIZE
) {
200 int n
= ffurl_read(c
->hd
, c
->inbuffer
+ c
->indata
,
201 sizeof(c
->inbuffer
) - c
->indata
);
208 blocks
= (c
->indata
- c
->indata_used
) / BLOCKSIZE
;
213 av_aes_crypt(c
->aes_decrypt
, c
->outbuffer
, c
->inbuffer
+ c
->indata_used
,
214 blocks
, c
->decrypt_iv
, 1);
215 c
->outdata
= BLOCKSIZE
* blocks
;
216 c
->outptr
= c
->outbuffer
;
217 c
->indata_used
+= BLOCKSIZE
* blocks
;
218 if (c
->indata_used
>= sizeof(c
->inbuffer
)/2) {
219 memmove(c
->inbuffer
, c
->inbuffer
+ c
->indata_used
,
220 c
->indata
- c
->indata_used
);
221 c
->indata
-= c
->indata_used
;
225 // Remove PKCS7 padding at the end
226 int padding
= c
->outbuffer
[c
->outdata
- 1];
227 c
->outdata
-= padding
;
232 static int64_t crypto_seek(URLContext
*h
, int64_t pos
, int whence
)
234 CryptoContext
*c
= h
->priv_data
;
238 if (c
->flags
& AVIO_FLAG_WRITE
) {
239 av_log(h
, AV_LOG_ERROR
,
240 "Crypto: seek not supported for write\r\n");
241 /* seems the most appropriate error to return */
242 return AVERROR(ESPIPE
);
245 // reset eof, else we won't read it correctly if we already hit eof.
252 pos
= pos
+ c
->position
;
255 newpos
= ffurl_seek( c
->hd
, pos
, AVSEEK_SIZE
);
257 av_log(h
, AV_LOG_ERROR
,
258 "Crypto: seek_end - can't get file size (pos=%"PRId64
")\r\n", pos
);
264 return ffurl_seek( c
->hd
, pos
, AVSEEK_SIZE
);
266 av_log(h
, AV_LOG_ERROR
,
267 "Crypto: no support for seek where 'whence' is %d\r\n", whence
);
268 return AVERROR(EINVAL
);
274 c
->outptr
= c
->outbuffer
;
276 // identify the block containing the IV for the
277 // next block we will decrypt
278 block
= pos
/BLOCKSIZE
;
280 // restore the iv to the seed one - this is the iv for the FIRST block
281 memcpy( c
->decrypt_iv
, c
->iv
, c
->ivlen
);
284 // else, go back one block - we will get av_cyrpt to read this block
285 // which it will then store use as the iv.
286 // note that the DECRYPTED result will not be correct,
287 // but will be discarded
289 c
->position
= (block
* BLOCKSIZE
);
292 newpos
= ffurl_seek( c
->hd
, c
->position
, SEEK_SET
);
294 av_log(h
, AV_LOG_ERROR
,
295 "Crypto: nested protocol no support for seek or seek failed\n");
299 // read and discard from here up to required position
300 // (which will set the iv correctly to it).
301 if (pos
- c
->position
) {
302 uint8_t buff
[BLOCKSIZE
*2]; // maximum size of pos-c->position
303 int len
= pos
- c
->position
;
307 // note: this may not return all the bytes first time
308 res
= crypto_read(h
, buff
, len
);
314 // if we did not get all the bytes
316 av_log(h
, AV_LOG_ERROR
,
317 "Crypto: discard read did not get all the bytes (%d remain) - read returned (%d)-%s\n",
318 len
, res
, av_err2str(res
));
319 return AVERROR(EINVAL
);
326 static int crypto_write(URLContext
*h
, const unsigned char *buf
, int size
)
328 CryptoContext
*c
= h
->priv_data
;
329 int total_size
, blocks
, pad_len
, out_size
;
332 total_size
= size
+ c
->pad_len
;
333 pad_len
= total_size
% BLOCKSIZE
;
334 out_size
= total_size
- pad_len
;
335 blocks
= out_size
/ BLOCKSIZE
;
338 av_fast_malloc(&c
->write_buf
, &c
->write_buf_size
, out_size
);
341 return AVERROR(ENOMEM
);
344 memcpy(&c
->pad
[c
->pad_len
], buf
, BLOCKSIZE
- c
->pad_len
);
345 av_aes_crypt(c
->aes_encrypt
, c
->write_buf
, c
->pad
, 1, c
->encrypt_iv
, 0);
349 av_aes_crypt(c
->aes_encrypt
,
350 &c
->write_buf
[c
->pad_len
? BLOCKSIZE
: 0],
351 &buf
[c
->pad_len
? BLOCKSIZE
- c
->pad_len
: 0],
352 blocks
, c
->encrypt_iv
, 0);
354 ret
= ffurl_write(c
->hd
, c
->write_buf
, out_size
);
358 memcpy(c
->pad
, &buf
[size
- pad_len
], pad_len
);
360 memcpy(&c
->pad
[c
->pad_len
], buf
, size
);
362 c
->pad_len
= pad_len
;
367 static int crypto_close(URLContext
*h
)
369 CryptoContext
*c
= h
->priv_data
;
372 if (c
->aes_encrypt
) {
373 uint8_t out_buf
[BLOCKSIZE
];
374 int pad
= BLOCKSIZE
- c
->pad_len
;
376 memset(&c
->pad
[c
->pad_len
], pad
, pad
);
377 av_aes_crypt(c
->aes_encrypt
, out_buf
, c
->pad
, 1, c
->encrypt_iv
, 0);
378 ret
= ffurl_write(c
->hd
, out_buf
, BLOCKSIZE
);
381 ffurl_closep(&c
->hd
);
382 av_freep(&c
->aes_decrypt
);
383 av_freep(&c
->aes_encrypt
);
384 av_freep(&c
->write_buf
);
388 const URLProtocol ff_crypto_protocol
= {
390 .url_open2
= crypto_open2
,
391 .url_seek
= crypto_seek
,
392 .url_read
= crypto_read
,
393 .url_write
= crypto_write
,
394 .url_close
= crypto_close
,
395 .priv_data_size
= sizeof(CryptoContext
),
396 .priv_data_class
= &crypto_class
,
397 .flags
= URL_PROTOCOL_FLAG_NESTED_SCHEME
,