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
;
52 struct AVAES
*aes_decrypt
;
53 struct AVAES
*aes_encrypt
;
55 uint8_t pad
[BLOCKSIZE
];
60 #define OFFSET(x) offsetof(CryptoContext, x)
61 #define D AV_OPT_FLAG_DECODING_PARAM
62 #define E AV_OPT_FLAG_ENCODING_PARAM
63 static const AVOption options
[] = {
64 {"key", "AES encryption/decryption key", OFFSET(key
), AV_OPT_TYPE_BINARY
, .flags
= D
|E
},
65 {"iv", "AES encryption/decryption initialization vector", OFFSET(iv
), AV_OPT_TYPE_BINARY
, .flags
= D
|E
},
66 {"decryption_key", "AES decryption key", OFFSET(decrypt_key
), AV_OPT_TYPE_BINARY
, .flags
= D
},
67 {"decryption_iv", "AES decryption initialization vector", OFFSET(decrypt_iv
), AV_OPT_TYPE_BINARY
, .flags
= D
},
68 {"encryption_key", "AES encryption key", OFFSET(encrypt_key
), AV_OPT_TYPE_BINARY
, .flags
= E
},
69 {"encryption_iv", "AES encryption initialization vector", OFFSET(encrypt_iv
), AV_OPT_TYPE_BINARY
, .flags
= E
},
73 static const AVClass crypto_class
= {
74 .class_name
= "crypto",
75 .item_name
= av_default_item_name
,
77 .version
= LIBAVUTIL_VERSION_INT
,
80 static int set_aes_arg(CryptoContext
*c
, uint8_t **buf
, int *buf_len
,
81 uint8_t *default_buf
, int default_buf_len
,
85 if (!default_buf_len
) {
86 av_log(c
, AV_LOG_ERROR
, "%s not set\n", desc
);
87 return AVERROR(EINVAL
);
88 } else if (default_buf_len
!= BLOCKSIZE
) {
89 av_log(c
, AV_LOG_ERROR
,
90 "invalid %s size (%d bytes, block size is %d)\n",
91 desc
, default_buf_len
, BLOCKSIZE
);
92 return AVERROR(EINVAL
);
94 *buf
= av_memdup(default_buf
, default_buf_len
);
96 return AVERROR(ENOMEM
);
97 *buf_len
= default_buf_len
;
98 } else if (*buf_len
!= BLOCKSIZE
) {
99 av_log(c
, AV_LOG_ERROR
,
100 "invalid %s size (%d bytes, block size is %d)\n",
101 desc
, *buf_len
, BLOCKSIZE
);
102 return AVERROR(EINVAL
);
107 static int crypto_open2(URLContext
*h
, const char *uri
, int flags
, AVDictionary
**options
)
109 const char *nested_url
;
111 CryptoContext
*c
= h
->priv_data
;
113 if (!av_strstart(uri
, "crypto+", &nested_url
) &&
114 !av_strstart(uri
, "crypto:", &nested_url
)) {
115 av_log(h
, AV_LOG_ERROR
, "Unsupported url %s\n", uri
);
116 ret
= AVERROR(EINVAL
);
120 if (flags
& AVIO_FLAG_READ
) {
121 if ((ret
= set_aes_arg(c
, &c
->decrypt_key
, &c
->decrypt_keylen
,
122 c
->key
, c
->keylen
, "decryption key")) < 0)
124 if ((ret
= set_aes_arg(c
, &c
->decrypt_iv
, &c
->decrypt_ivlen
,
125 c
->iv
, c
->ivlen
, "decryption IV")) < 0)
129 if (flags
& AVIO_FLAG_WRITE
) {
130 if ((ret
= set_aes_arg(c
, &c
->encrypt_key
, &c
->encrypt_keylen
,
131 c
->key
, c
->keylen
, "encryption key")) < 0)
134 if ((ret
= set_aes_arg(c
, &c
->encrypt_iv
, &c
->encrypt_ivlen
,
135 c
->iv
, c
->ivlen
, "encryption IV")) < 0)
139 if ((ret
= ffurl_open(&c
->hd
, nested_url
, flags
,
140 &h
->interrupt_callback
, options
)) < 0) {
141 av_log(h
, AV_LOG_ERROR
, "Unable to open resource: %s\n", nested_url
);
145 if (flags
& AVIO_FLAG_READ
) {
146 c
->aes_decrypt
= av_aes_alloc();
147 if (!c
->aes_decrypt
) {
148 ret
= AVERROR(ENOMEM
);
151 ret
= av_aes_init(c
->aes_decrypt
, c
->decrypt_key
, BLOCKSIZE
*8, 1);
156 if (flags
& AVIO_FLAG_WRITE
) {
157 c
->aes_encrypt
= av_aes_alloc();
158 if (!c
->aes_encrypt
) {
159 ret
= AVERROR(ENOMEM
);
162 ret
= av_aes_init(c
->aes_encrypt
, c
->encrypt_key
, BLOCKSIZE
*8, 0);
175 static int crypto_read(URLContext
*h
, uint8_t *buf
, int size
)
177 CryptoContext
*c
= h
->priv_data
;
180 if (c
->outdata
> 0) {
181 size
= FFMIN(size
, c
->outdata
);
182 memcpy(buf
, c
->outptr
, size
);
187 // We avoid using the last block until we've found EOF,
188 // since we'll remove PKCS7 padding at the end. So make
189 // sure we've got at least 2 blocks, so we can decrypt
191 while (c
->indata
- c
->indata_used
< 2*BLOCKSIZE
) {
192 int n
= ffurl_read(c
->hd
, c
->inbuffer
+ c
->indata
,
193 sizeof(c
->inbuffer
) - c
->indata
);
200 blocks
= (c
->indata
- c
->indata_used
) / BLOCKSIZE
;
205 av_aes_crypt(c
->aes_decrypt
, c
->outbuffer
, c
->inbuffer
+ c
->indata_used
,
206 blocks
, c
->decrypt_iv
, 1);
207 c
->outdata
= BLOCKSIZE
* blocks
;
208 c
->outptr
= c
->outbuffer
;
209 c
->indata_used
+= BLOCKSIZE
* blocks
;
210 if (c
->indata_used
>= sizeof(c
->inbuffer
)/2) {
211 memmove(c
->inbuffer
, c
->inbuffer
+ c
->indata_used
,
212 c
->indata
- c
->indata_used
);
213 c
->indata
-= c
->indata_used
;
217 // Remove PKCS7 padding at the end
218 int padding
= c
->outbuffer
[c
->outdata
- 1];
219 c
->outdata
-= padding
;
224 static int crypto_write(URLContext
*h
, const unsigned char *buf
, int size
)
226 CryptoContext
*c
= h
->priv_data
;
227 int total_size
, blocks
, pad_len
, out_size
;
231 total_size
= size
+ c
->pad_len
;
232 pad_len
= total_size
% BLOCKSIZE
;
233 out_size
= total_size
- pad_len
;
234 blocks
= out_size
/ BLOCKSIZE
;
237 out_buf
= av_malloc(out_size
);
239 return AVERROR(ENOMEM
);
242 memcpy(&c
->pad
[c
->pad_len
], buf
, BLOCKSIZE
- c
->pad_len
);
243 av_aes_crypt(c
->aes_encrypt
, out_buf
, c
->pad
, 1, c
->encrypt_iv
, 0);
247 av_aes_crypt(c
->aes_encrypt
, &out_buf
[c
->pad_len
? BLOCKSIZE
: 0],
248 &buf
[c
->pad_len
? BLOCKSIZE
- c
->pad_len
: 0],
249 blocks
, c
->encrypt_iv
, 0);
251 ret
= ffurl_write(c
->hd
, out_buf
, out_size
);
256 memcpy(c
->pad
, &buf
[size
- pad_len
], pad_len
);
258 memcpy(&c
->pad
[c
->pad_len
], buf
, size
);
260 c
->pad_len
= pad_len
;
265 static int crypto_close(URLContext
*h
)
267 CryptoContext
*c
= h
->priv_data
;
268 uint8_t out_buf
[BLOCKSIZE
];
271 if (c
->aes_encrypt
) {
272 pad
= BLOCKSIZE
- c
->pad_len
;
273 memset(&c
->pad
[c
->pad_len
], pad
, pad
);
274 av_aes_crypt(c
->aes_encrypt
, out_buf
, c
->pad
, 1, c
->encrypt_iv
, 0);
275 if ((ret
= ffurl_write(c
->hd
, out_buf
, BLOCKSIZE
)) < 0)
281 av_freep(&c
->aes_decrypt
);
282 av_freep(&c
->aes_encrypt
);
286 URLProtocol ff_crypto_protocol
= {
288 .url_open2
= crypto_open2
,
289 .url_read
= crypto_read
,
290 .url_write
= crypto_write
,
291 .url_close
= crypto_close
,
292 .priv_data_size
= sizeof(CryptoContext
),
293 .priv_data_class
= &crypto_class
,
294 .flags
= URL_PROTOCOL_FLAG_NESTED_SCHEME
,