FFmpeg
crypto.c
Go to the documentation of this file.
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 <inttypes.h>
23 #include <string.h>
24 
25 #include "libavutil/aes.h"
26 #include "libavutil/avstring.h"
27 #include "libavutil/error.h"
28 #include "libavutil/mem.h"
29 #include "libavutil/opt.h"
30 #include "url.h"
31 
32 // encourage reads of 4096 bytes - 1 block is always retained.
33 #define MAX_BUFFER_BLOCKS 257
34 #define BLOCKSIZE 16
35 
36 typedef struct CryptoContext {
37  const AVClass *class;
41  uint8_t *outptr;
43  int64_t position; // position in file - used in seek
44  int flags;
45  int eof;
46  uint8_t *key;
47  int keylen;
48  uint8_t *iv;
49  int ivlen;
50  uint8_t *decrypt_key;
52  uint8_t *decrypt_iv;
54  uint8_t *encrypt_key;
56  uint8_t *encrypt_iv;
58  struct AVAES *aes_decrypt;
59  struct AVAES *aes_encrypt;
60  uint8_t *write_buf;
61  unsigned int write_buf_size;
62  uint8_t pad[BLOCKSIZE];
63  int pad_len;
65 
66 #define OFFSET(x) offsetof(CryptoContext, x)
67 #define D AV_OPT_FLAG_DECODING_PARAM
68 #define E AV_OPT_FLAG_ENCODING_PARAM
69 static const AVOption crypto_options[] = {
70  {"key", "AES encryption/decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, .flags = D|E },
71  {"iv", "AES encryption/decryption initialization vector", OFFSET(iv), AV_OPT_TYPE_BINARY, .flags = D|E },
72  {"decryption_key", "AES decryption key", OFFSET(decrypt_key), AV_OPT_TYPE_BINARY, .flags = D },
73  {"decryption_iv", "AES decryption initialization vector", OFFSET(decrypt_iv), AV_OPT_TYPE_BINARY, .flags = D },
74  {"encryption_key", "AES encryption key", OFFSET(encrypt_key), AV_OPT_TYPE_BINARY, .flags = E },
75  {"encryption_iv", "AES encryption initialization vector", OFFSET(encrypt_iv), AV_OPT_TYPE_BINARY, .flags = E },
76  { NULL }
77 };
78 
79 static const AVClass crypto_class = {
80  .class_name = "crypto",
81  .item_name = av_default_item_name,
82  .option = crypto_options,
83  .version = LIBAVUTIL_VERSION_INT,
84 };
85 
86 static int set_aes_arg(URLContext *h, uint8_t **buf, int *buf_len,
87  uint8_t *default_buf, int default_buf_len,
88  const char *desc)
89 {
90  if (!*buf_len) {
91  if (!default_buf_len) {
92  av_log(h, AV_LOG_ERROR, "%s not set\n", desc);
93  return AVERROR(EINVAL);
94  } else if (default_buf_len != BLOCKSIZE) {
96  "invalid %s size (%d bytes, block size is %d)\n",
97  desc, default_buf_len, BLOCKSIZE);
98  return AVERROR(EINVAL);
99  }
100  *buf = av_memdup(default_buf, default_buf_len);
101  if (!*buf)
102  return AVERROR(ENOMEM);
103  *buf_len = default_buf_len;
104  } else if (*buf_len != BLOCKSIZE) {
106  "invalid %s size (%d bytes, block size is %d)\n",
107  desc, *buf_len, BLOCKSIZE);
108  return AVERROR(EINVAL);
109  }
110  return 0;
111 }
112 
113 static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary **options)
114 {
115  const char *nested_url;
116  int ret = 0;
117  CryptoContext *c = h->priv_data;
118  c->flags = flags;
119 
120  if (!av_strstart(uri, "crypto+", &nested_url) &&
121  !av_strstart(uri, "crypto:", &nested_url)) {
122  av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);
123  ret = AVERROR(EINVAL);
124  goto err;
125  }
126 
127  if (flags & AVIO_FLAG_READ) {
128  if ((ret = set_aes_arg(h, &c->decrypt_key, &c->decrypt_keylen,
129  c->key, c->keylen, "decryption key")) < 0)
130  goto err;
131  if ((ret = set_aes_arg(h, &c->decrypt_iv, &c->decrypt_ivlen,
132  c->iv, c->ivlen, "decryption IV")) < 0)
133  goto err;
134  }
135 
136  if (flags & AVIO_FLAG_WRITE) {
137  if ((ret = set_aes_arg(h, &c->encrypt_key, &c->encrypt_keylen,
138  c->key, c->keylen, "encryption key")) < 0)
139  if (ret < 0)
140  goto err;
141  if ((ret = set_aes_arg(h, &c->encrypt_iv, &c->encrypt_ivlen,
142  c->iv, c->ivlen, "encryption IV")) < 0)
143  goto err;
144  }
145 
146  if ((ret = ffurl_open_whitelist(&c->hd, nested_url, flags,
147  &h->interrupt_callback, options,
148  h->protocol_whitelist, h->protocol_blacklist, h)) < 0) {
149  av_log(h, AV_LOG_ERROR, "Unable to open resource: %s\n", nested_url);
150  goto err;
151  }
152 
153  if (flags & AVIO_FLAG_READ) {
154  c->aes_decrypt = av_aes_alloc();
155  if (!c->aes_decrypt) {
156  ret = AVERROR(ENOMEM);
157  goto err;
158  }
159  ret = av_aes_init(c->aes_decrypt, c->decrypt_key, BLOCKSIZE * 8, 1);
160  if (ret < 0)
161  goto err;
162 
163  // pass back information about the context we opened
164  if (c->hd->is_streamed)
165  h->is_streamed = c->hd->is_streamed;
166  }
167 
168  if (flags & AVIO_FLAG_WRITE) {
169  c->aes_encrypt = av_aes_alloc();
170  if (!c->aes_encrypt) {
171  ret = AVERROR(ENOMEM);
172  goto err;
173  }
174  ret = av_aes_init(c->aes_encrypt, c->encrypt_key, BLOCKSIZE * 8, 0);
175  if (ret < 0)
176  goto err;
177  // for write, we must be streamed
178  // - linear write only for crypto aes-128-cbc
179  h->is_streamed = 1;
180  }
181 
182 err:
183  return ret;
184 }
185 
186 static int crypto_read(URLContext *h, uint8_t *buf, int size)
187 {
188  CryptoContext *c = h->priv_data;
189  int blocks;
190 retry:
191  if (c->outdata > 0) {
192  size = FFMIN(size, c->outdata);
193  memcpy(buf, c->outptr, size);
194  c->outptr += size;
195  c->outdata -= size;
196  c->position = c->position + size;
197  return size;
198  }
199  // We avoid using the last block until we've found EOF,
200  // since we'll remove PKCS7 padding at the end. So make
201  // sure we've got at least 2 blocks, so we can decrypt
202  // at least one.
203  while (c->indata - c->indata_used < 2*BLOCKSIZE) {
204  int n = ffurl_read(c->hd, c->inbuffer + c->indata,
205  sizeof(c->inbuffer) - c->indata);
206  if (n <= 0) {
207  c->eof = 1;
208  break;
209  }
210  c->indata += n;
211  }
212  blocks = (c->indata - c->indata_used) / BLOCKSIZE;
213  if (!blocks)
214  return AVERROR_EOF;
215  if (!c->eof)
216  blocks--;
217  av_aes_crypt(c->aes_decrypt, c->outbuffer, c->inbuffer + c->indata_used,
218  blocks, c->decrypt_iv, 1);
219  c->outdata = BLOCKSIZE * blocks;
220  c->outptr = c->outbuffer;
221  c->indata_used += BLOCKSIZE * blocks;
222  if (c->indata_used >= sizeof(c->inbuffer)/2) {
223  memmove(c->inbuffer, c->inbuffer + c->indata_used,
224  c->indata - c->indata_used);
225  c->indata -= c->indata_used;
226  c->indata_used = 0;
227  }
228  if (c->eof) {
229  // Remove PKCS7 padding at the end
230  int padding = c->outbuffer[c->outdata - 1];
231  c->outdata -= padding;
232  }
233  goto retry;
234 }
235 
236 static int64_t crypto_seek(URLContext *h, int64_t pos, int whence)
237 {
238  CryptoContext *c = h->priv_data;
239  int64_t block;
240  int64_t newpos;
241 
242  if (c->flags & AVIO_FLAG_WRITE) {
244  "Crypto: seek not supported for write\r\n");
245  /* seems the most appropriate error to return */
246  return AVERROR(ESPIPE);
247  }
248 
249  // reset eof, else we won't read it correctly if we already hit eof.
250  c->eof = 0;
251 
252  switch (whence) {
253  case SEEK_SET:
254  break;
255  case SEEK_CUR:
256  pos = pos + c->position;
257  break;
258  case SEEK_END:
259  newpos = ffurl_seek( c->hd, pos, AVSEEK_SIZE );
260  if (newpos < 0) {
262  "Crypto: seek_end - can't get file size (pos=%"PRId64")\r\n", pos);
263  return newpos;
264  }
265  pos = newpos - pos;
266  break;
267  case AVSEEK_SIZE:
268  return ffurl_seek( c->hd, pos, AVSEEK_SIZE );
269  default:
271  "Crypto: no support for seek where 'whence' is %d\r\n", whence);
272  return AVERROR(EINVAL);
273  }
274 
275  c->outdata = 0;
276  c->indata = 0;
277  c->indata_used = 0;
278  c->outptr = c->outbuffer;
279 
280  // identify the block containing the IV for the
281  // next block we will decrypt
282  block = pos/BLOCKSIZE;
283  if (block == 0) {
284  // restore the iv to the seed one - this is the iv for the FIRST block
285  memcpy( c->decrypt_iv, c->iv, c->ivlen );
286  c->position = 0;
287  } else {
288  // else, go back one block - we will get av_cyrpt to read this block
289  // which it will then store use as the iv.
290  // note that the DECRYPTED result will not be correct,
291  // but will be discarded
292  block--;
293  c->position = (block * BLOCKSIZE);
294  }
295 
296  newpos = ffurl_seek( c->hd, c->position, SEEK_SET );
297  if (newpos < 0) {
299  "Crypto: nested protocol no support for seek or seek failed\n");
300  return newpos;
301  }
302 
303  // read and discard from here up to required position
304  // (which will set the iv correctly to it).
305  if (pos - c->position) {
306  uint8_t buff[BLOCKSIZE*2]; // maximum size of pos-c->position
307  int len = pos - c->position;
308  int res;
309 
310  while (len > 0) {
311  // note: this may not return all the bytes first time
312  res = crypto_read(h, buff, len);
313  if (res < 0)
314  break;
315  len -= res;
316  }
317 
318  // if we did not get all the bytes
319  if (len != 0) {
321  "Crypto: discard read did not get all the bytes (%d remain) - read returned (%d)-%s\n",
322  len, res, av_err2str(res));
323  return AVERROR(EINVAL);
324  }
325  }
326 
327  return c->position;
328 }
329 
330 static int crypto_write(URLContext *h, const unsigned char *buf, int size)
331 {
332  CryptoContext *c = h->priv_data;
333  int total_size, blocks, pad_len, out_size;
334  int ret = 0;
335 
336  total_size = size + c->pad_len;
337  pad_len = total_size % BLOCKSIZE;
338  out_size = total_size - pad_len;
339  blocks = out_size / BLOCKSIZE;
340 
341  if (out_size) {
342  av_fast_malloc(&c->write_buf, &c->write_buf_size, out_size);
343 
344  if (!c->write_buf)
345  return AVERROR(ENOMEM);
346 
347  if (c->pad_len) {
348  memcpy(&c->pad[c->pad_len], buf, BLOCKSIZE - c->pad_len);
349  av_aes_crypt(c->aes_encrypt, c->write_buf, c->pad, 1, c->encrypt_iv, 0);
350  blocks--;
351  }
352 
353  av_aes_crypt(c->aes_encrypt,
354  &c->write_buf[c->pad_len ? BLOCKSIZE : 0],
355  &buf[c->pad_len ? BLOCKSIZE - c->pad_len : 0],
356  blocks, c->encrypt_iv, 0);
357 
358  ret = ffurl_write(c->hd, c->write_buf, out_size);
359  if (ret < 0)
360  return ret;
361 
362  memcpy(c->pad, &buf[size - pad_len], pad_len);
363  } else
364  memcpy(&c->pad[c->pad_len], buf, size);
365 
366  c->pad_len = pad_len;
367 
368  return size;
369 }
370 
372 {
373  CryptoContext *c = h->priv_data;
374  int ret = 0;
375 
376  if (c->aes_encrypt) {
377  uint8_t out_buf[BLOCKSIZE];
378  int pad = BLOCKSIZE - c->pad_len;
379 
380  memset(&c->pad[c->pad_len], pad, pad);
381  av_aes_crypt(c->aes_encrypt, out_buf, c->pad, 1, c->encrypt_iv, 0);
382  ret = ffurl_write(c->hd, out_buf, BLOCKSIZE);
383  }
384 
385  ffurl_closep(&c->hd);
386  av_freep(&c->aes_decrypt);
387  av_freep(&c->aes_encrypt);
388  av_freep(&c->write_buf);
389  return ret;
390 }
391 
393  .name = "crypto",
394  .url_open2 = crypto_open2,
395  .url_seek = crypto_seek,
396  .url_read = crypto_read,
397  .url_write = crypto_write,
398  .url_close = crypto_close,
399  .priv_data_size = sizeof(CryptoContext),
400  .priv_data_class = &crypto_class,
402 };
flags
const SwsFlags flags[]
Definition: swscale.c:85
ffurl_seek
static int64_t ffurl_seek(URLContext *h, int64_t pos, int whence)
Change the position that will be used by the next read/write operation on the resource accessed by h.
Definition: url.h:222
av_aes_init
int av_aes_init(AVAES *a, const uint8_t *key, int key_bits, int decrypt)
Initialize an AVAES context.
Definition: aes.c:231
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
opt.h
crypto_write
static int crypto_write(URLContext *h, const unsigned char *buf, int size)
Definition: crypto.c:330
AVERROR_EOF
#define AVERROR_EOF
End of file.
Definition: error.h:57
int64_t
long long int64_t
Definition: coverity.c:34
ffurl_write
static int ffurl_write(URLContext *h, const uint8_t *buf, int size)
Write size bytes from buf to the resource accessed by h.
Definition: url.h:202
out_size
static int out_size
Definition: movenc.c:56
CryptoContext::encrypt_ivlen
int encrypt_ivlen
Definition: crypto.c:57
CryptoContext::indata
int indata
Definition: crypto.c:42
AVOption
AVOption.
Definition: opt.h:428
AVSEEK_SIZE
#define AVSEEK_SIZE
Passing this as the "whence" parameter to a seek function causes it to return the filesize without se...
Definition: avio.h:468
CryptoContext::position
int64_t position
Definition: crypto.c:43
CryptoContext::eof
int eof
Definition: crypto.c:45
AVDictionary
Definition: dict.c:32
OFFSET
#define OFFSET(x)
Definition: crypto.c:66
URLProtocol
Definition: url.h:51
CryptoContext::flags
int flags
Definition: crypto.c:44
av_memdup
void * av_memdup(const void *p, size_t size)
Duplicate a buffer with av_malloc().
Definition: mem.c:304
CryptoContext::indata_used
int indata_used
Definition: crypto.c:42
AV_OPT_TYPE_BINARY
@ AV_OPT_TYPE_BINARY
Underlying C type is a uint8_t* that is either NULL or points to an array allocated with the av_mallo...
Definition: opt.h:285
CryptoContext::encrypt_key
uint8_t * encrypt_key
Definition: crypto.c:54
crypto_class
static const AVClass crypto_class
Definition: crypto.c:79
CryptoContext::aes_decrypt
struct AVAES * aes_decrypt
Definition: crypto.c:58
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
ffurl_open_whitelist
int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options, const char *whitelist, const char *blacklist, URLContext *parent)
Create an URLContext for accessing to the resource indicated by url, and open it.
Definition: avio.c:368
CryptoContext::decrypt_iv
uint8_t * decrypt_iv
Definition: crypto.c:52
CryptoContext::encrypt_iv
uint8_t * encrypt_iv
Definition: crypto.c:56
AVIO_FLAG_WRITE
#define AVIO_FLAG_WRITE
write-only
Definition: avio.h:618
CryptoContext
Definition: crypto.c:36
key
const char * key
Definition: hwcontext_opencl.c:189
set_aes_arg
static int set_aes_arg(URLContext *h, uint8_t **buf, int *buf_len, uint8_t *default_buf, int default_buf_len, const char *desc)
Definition: crypto.c:86
CryptoContext::key
uint8_t * key
Definition: crypto.c:46
CryptoContext::hd
URLContext * hd
Definition: crypto.c:38
LIBAVUTIL_VERSION_INT
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
CryptoContext::decrypt_keylen
int decrypt_keylen
Definition: crypto.c:51
aes.h
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:76
NULL
#define NULL
Definition: coverity.c:32
CryptoContext
@ CryptoContext
Definition: mxf.h:42
av_default_item_name
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:242
CryptoContext::aes_encrypt
struct AVAES * aes_encrypt
Definition: crypto.c:59
av_aes_crypt
void av_aes_crypt(AVAES *a, uint8_t *dst, const uint8_t *src, int count, uint8_t *iv, int decrypt)
Encrypt or decrypt a buffer using a previously initialized context.
Definition: aes.c:171
av_aes_alloc
struct AVAES * av_aes_alloc(void)
Allocate an AVAES context.
Definition: aes.c:37
options
Definition: swscale.c:50
CryptoContext::outptr
uint8_t * outptr
Definition: crypto.c:41
CryptoContext::encrypt_keylen
int encrypt_keylen
Definition: crypto.c:55
c
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C it is not safe even if the output of undefined operations is unused The unsafety may seem nit picking but Optimizing compilers have in fact optimized code on the assumption that no undefined Behavior occurs Optimizing code based on wrong assumptions can and has in some cases lead to effects beyond the output of computations The signed integer overflow problem in speed critical code Code which is highly optimized and works with signed integers sometimes has the problem that often the output of the computation does not c
Definition: undefined.txt:32
error.h
CryptoContext::decrypt_key
uint8_t * decrypt_key
Definition: crypto.c:50
av_err2str
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition: error.h:122
URL_PROTOCOL_FLAG_NESTED_SCHEME
#define URL_PROTOCOL_FLAG_NESTED_SCHEME
Definition: url.h:32
size
int size
Definition: twinvq_data.h:10344
crypto_open2
static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary **options)
Definition: crypto.c:113
URLProtocol::name
const char * name
Definition: url.h:52
CryptoContext::iv
uint8_t * iv
Definition: crypto.c:48
av_strstart
int av_strstart(const char *str, const char *pfx, const char **ptr)
Return non-zero if pfx is a prefix of str.
Definition: avstring.c:36
CryptoContext::inbuffer
uint8_t inbuffer[BLOCKSIZE *MAX_BUFFER_BLOCKS]
Definition: crypto.c:39
CryptoContext::keylen
int keylen
Definition: crypto.c:47
crypto_close
static int crypto_close(URLContext *h)
Definition: crypto.c:371
crypto_options
static const AVOption crypto_options[]
Definition: crypto.c:69
URLContext
Definition: url.h:35
D
#define D
Definition: crypto.c:67
FFMIN
#define FFMIN(a, b)
Definition: macros.h:49
url.h
CryptoContext::ivlen
int ivlen
Definition: crypto.c:49
len
int len
Definition: vorbis_enc_data.h:426
CryptoContext::outdata
int outdata
Definition: crypto.c:42
ffurl_closep
int ffurl_closep(URLContext **hh)
Close the resource accessed by the URLContext h, and free the memory used by it.
Definition: avio.c:594
ret
ret
Definition: filter_design.txt:187
AVClass::class_name
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
Definition: log.h:81
AVAES
Definition: aes_internal.h:34
pos
unsigned int pos
Definition: spdifenc.c:414
CryptoContext::decrypt_ivlen
int decrypt_ivlen
Definition: crypto.c:53
CryptoContext::write_buf_size
unsigned int write_buf_size
Definition: crypto.c:61
CryptoContext::outbuffer
uint8_t outbuffer[BLOCKSIZE *MAX_BUFFER_BLOCKS]
Definition: crypto.c:40
E
#define E
Definition: crypto.c:68
crypto_read
static int crypto_read(URLContext *h, uint8_t *buf, int size)
Definition: crypto.c:186
AVIO_FLAG_READ
#define AVIO_FLAG_READ
read-only
Definition: avio.h:617
desc
const char * desc
Definition: libsvtav1.c:83
mem.h
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
av_fast_malloc
void av_fast_malloc(void *ptr, unsigned int *size, size_t min_size)
Allocate a buffer, reusing the given one if large enough.
Definition: mem.c:557
ff_crypto_protocol
const URLProtocol ff_crypto_protocol
Definition: crypto.c:392
block
The exact code depends on how similar the blocks are and how related they are to the block
Definition: filter_design.txt:207
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
crypto_seek
static int64_t crypto_seek(URLContext *h, int64_t pos, int whence)
Definition: crypto.c:236
h
h
Definition: vp9dsp_template.c:2070
avstring.h
CryptoContext::pad
uint8_t pad[BLOCKSIZE]
Definition: crypto.c:62
CryptoContext::pad_len
int pad_len
Definition: crypto.c:63
BLOCKSIZE
#define BLOCKSIZE
Definition: crypto.c:34
MAX_BUFFER_BLOCKS
#define MAX_BUFFER_BLOCKS
Definition: crypto.c:33
ffurl_read
static int ffurl_read(URLContext *h, uint8_t *buf, int size)
Read up to size bytes from the resource accessed by h, and store the read bytes in buf.
Definition: url.h:181
CryptoContext::write_buf
uint8_t * write_buf
Definition: crypto.c:60