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 #define MAX_BUFFER_BLOCKS 150
35 uint8_t inbuffer
[BLOCKSIZE
*MAX_BUFFER_BLOCKS
],
36 outbuffer
[BLOCKSIZE
*MAX_BUFFER_BLOCKS
];
38 int indata
, indata_used
, outdata
;
47 #define OFFSET(x) offsetof(CryptoContext, x)
48 #define D AV_OPT_FLAG_DECODING_PARAM
49 static const AVOption options
[] = {
50 {"key", "AES decryption key", OFFSET(key
), AV_OPT_TYPE_BINARY
, .flags
= D
},
51 {"iv", "AES decryption initialization vector", OFFSET(iv
), AV_OPT_TYPE_BINARY
, .flags
= D
},
55 static const AVClass crypto_class
= {
56 .class_name
= "crypto",
57 .item_name
= av_default_item_name
,
59 .version
= LIBAVUTIL_VERSION_INT
,
62 static int crypto_open2(URLContext
*h
, const char *uri
, int flags
, AVDictionary
**options
)
64 const char *nested_url
;
66 CryptoContext
*c
= h
->priv_data
;
68 if (!av_strstart(uri
, "crypto+", &nested_url
) &&
69 !av_strstart(uri
, "crypto:", &nested_url
)) {
70 av_log(h
, AV_LOG_ERROR
, "Unsupported url %s\n", uri
);
71 ret
= AVERROR(EINVAL
);
75 if (c
->keylen
< BLOCKSIZE
|| c
->ivlen
< BLOCKSIZE
) {
76 av_log(h
, AV_LOG_ERROR
, "Key or IV not set\n");
77 ret
= AVERROR(EINVAL
);
80 if (flags
& AVIO_FLAG_WRITE
) {
81 av_log(h
, AV_LOG_ERROR
, "Only decryption is supported currently\n");
82 ret
= AVERROR(ENOSYS
);
85 if ((ret
= ffurl_open(&c
->hd
, nested_url
, AVIO_FLAG_READ
,
86 &h
->interrupt_callback
, options
)) < 0) {
87 av_log(h
, AV_LOG_ERROR
, "Unable to open input\n");
90 c
->aes
= av_aes_alloc();
92 ret
= AVERROR(ENOMEM
);
96 av_aes_init(c
->aes
, c
->key
, 128, 1);
104 static int crypto_read(URLContext
*h
, uint8_t *buf
, int size
)
106 CryptoContext
*c
= h
->priv_data
;
109 if (c
->outdata
> 0) {
110 size
= FFMIN(size
, c
->outdata
);
111 memcpy(buf
, c
->outptr
, size
);
116 // We avoid using the last block until we've found EOF,
117 // since we'll remove PKCS7 padding at the end. So make
118 // sure we've got at least 2 blocks, so we can decrypt
120 while (c
->indata
- c
->indata_used
< 2*BLOCKSIZE
) {
121 int n
= ffurl_read(c
->hd
, c
->inbuffer
+ c
->indata
,
122 sizeof(c
->inbuffer
) - c
->indata
);
129 blocks
= (c
->indata
- c
->indata_used
) / BLOCKSIZE
;
134 av_aes_crypt(c
->aes
, c
->outbuffer
, c
->inbuffer
+ c
->indata_used
, blocks
,
136 c
->outdata
= BLOCKSIZE
* blocks
;
137 c
->outptr
= c
->outbuffer
;
138 c
->indata_used
+= BLOCKSIZE
* blocks
;
139 if (c
->indata_used
>= sizeof(c
->inbuffer
)/2) {
140 memmove(c
->inbuffer
, c
->inbuffer
+ c
->indata_used
,
141 c
->indata
- c
->indata_used
);
142 c
->indata
-= c
->indata_used
;
146 // Remove PKCS7 padding at the end
147 int padding
= c
->outbuffer
[c
->outdata
- 1];
148 c
->outdata
-= padding
;
153 static int crypto_close(URLContext
*h
)
155 CryptoContext
*c
= h
->priv_data
;
162 URLProtocol ff_crypto_protocol
= {
164 .url_open2
= crypto_open2
,
165 .url_read
= crypto_read
,
166 .url_close
= crypto_close
,
167 .priv_data_size
= sizeof(CryptoContext
),
168 .priv_data_class
= &crypto_class
,
169 .flags
= URL_PROTOCOL_FLAG_NESTED_SCHEME
,