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 "avformat.h"
23 #include "libavutil/aes.h"
24 #include "libavutil/avstring.h"
25 #include "libavutil/opt.h"
26 #include "internal.h"
27 #include "url.h"
28 
29 // encourage reads of 4096 bytes - 1 block is always retained.
30 #define MAX_BUFFER_BLOCKS 257
31 #define BLOCKSIZE 16
32 
33 typedef struct CryptoContext {
34  const AVClass *class;
40  int64_t position; // position in file - used in seek
41  int flags;
42  int eof;
44  int keylen;
46  int ivlen;
55  struct AVAES *aes_decrypt;
56  struct AVAES *aes_encrypt;
58  unsigned int write_buf_size;
60  int pad_len;
62 
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 },
73  { NULL }
74 };
75 
76 static const AVClass crypto_class = {
77  .class_name = "crypto",
78  .item_name = av_default_item_name,
79  .option = options,
80  .version = LIBAVUTIL_VERSION_INT,
81 };
82 
83 static int set_aes_arg(URLContext *h, uint8_t **buf, int *buf_len,
84  uint8_t *default_buf, int default_buf_len,
85  const char *desc)
86 {
87  if (!*buf_len) {
88  if (!default_buf_len) {
89  av_log(h, AV_LOG_ERROR, "%s not set\n", desc);
90  return AVERROR(EINVAL);
91  } else if (default_buf_len != BLOCKSIZE) {
93  "invalid %s size (%d bytes, block size is %d)\n",
94  desc, default_buf_len, BLOCKSIZE);
95  return AVERROR(EINVAL);
96  }
97  *buf = av_memdup(default_buf, default_buf_len);
98  if (!*buf)
99  return AVERROR(ENOMEM);
100  *buf_len = default_buf_len;
101  } else if (*buf_len != BLOCKSIZE) {
102  av_log(h, AV_LOG_ERROR,
103  "invalid %s size (%d bytes, block size is %d)\n",
104  desc, *buf_len, BLOCKSIZE);
105  return AVERROR(EINVAL);
106  }
107  return 0;
108 }
109 
110 static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary **options)
111 {
112  const char *nested_url;
113  int ret = 0;
114  CryptoContext *c = h->priv_data;
115  c->flags = flags;
116 
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);
121  goto err;
122  }
123 
124  if (flags & AVIO_FLAG_READ) {
125  if ((ret = set_aes_arg(h, &c->decrypt_key, &c->decrypt_keylen,
126  c->key, c->keylen, "decryption key")) < 0)
127  goto err;
128  if ((ret = set_aes_arg(h, &c->decrypt_iv, &c->decrypt_ivlen,
129  c->iv, c->ivlen, "decryption IV")) < 0)
130  goto err;
131  }
132 
133  if (flags & AVIO_FLAG_WRITE) {
134  if ((ret = set_aes_arg(h, &c->encrypt_key, &c->encrypt_keylen,
135  c->key, c->keylen, "encryption key")) < 0)
136  if (ret < 0)
137  goto err;
138  if ((ret = set_aes_arg(h, &c->encrypt_iv, &c->encrypt_ivlen,
139  c->iv, c->ivlen, "encryption IV")) < 0)
140  goto err;
141  }
142 
143  if ((ret = ffurl_open_whitelist(&c->hd, nested_url, flags,
144  &h->interrupt_callback, options,
145  h->protocol_whitelist, h->protocol_blacklist, h)) < 0) {
146  av_log(h, AV_LOG_ERROR, "Unable to open resource: %s\n", nested_url);
147  goto err;
148  }
149 
150  if (flags & AVIO_FLAG_READ) {
151  c->aes_decrypt = av_aes_alloc();
152  if (!c->aes_decrypt) {
153  ret = AVERROR(ENOMEM);
154  goto err;
155  }
156  ret = av_aes_init(c->aes_decrypt, c->decrypt_key, BLOCKSIZE * 8, 1);
157  if (ret < 0)
158  goto err;
159 
160  // pass back information about the context we openned
161  if (c->hd->is_streamed)
162  h->is_streamed = c->hd->is_streamed;
163  }
164 
165  if (flags & AVIO_FLAG_WRITE) {
166  c->aes_encrypt = av_aes_alloc();
167  if (!c->aes_encrypt) {
168  ret = AVERROR(ENOMEM);
169  goto err;
170  }
171  ret = av_aes_init(c->aes_encrypt, c->encrypt_key, BLOCKSIZE * 8, 0);
172  if (ret < 0)
173  goto err;
174  // for write, we must be streamed
175  // - linear write only for crytpo aes-128-cbc
176  h->is_streamed = 1;
177  }
178 
179 err:
180  return ret;
181 }
182 
183 static int crypto_read(URLContext *h, uint8_t *buf, int size)
184 {
185  CryptoContext *c = h->priv_data;
186  int blocks;
187 retry:
188  if (c->outdata > 0) {
189  size = FFMIN(size, c->outdata);
190  memcpy(buf, c->outptr, size);
191  c->outptr += size;
192  c->outdata -= size;
193  c->position = c->position + size;
194  return size;
195  }
196  // We avoid using the last block until we've found EOF,
197  // since we'll remove PKCS7 padding at the end. So make
198  // sure we've got at least 2 blocks, so we can decrypt
199  // at least one.
200  while (c->indata - c->indata_used < 2*BLOCKSIZE) {
201  int n = ffurl_read(c->hd, c->inbuffer + c->indata,
202  sizeof(c->inbuffer) - c->indata);
203  if (n <= 0) {
204  c->eof = 1;
205  break;
206  }
207  c->indata += n;
208  }
209  blocks = (c->indata - c->indata_used) / BLOCKSIZE;
210  if (!blocks)
211  return AVERROR_EOF;
212  if (!c->eof)
213  blocks--;
215  blocks, c->decrypt_iv, 1);
216  c->outdata = BLOCKSIZE * blocks;
217  c->outptr = c->outbuffer;
218  c->indata_used += BLOCKSIZE * blocks;
219  if (c->indata_used >= sizeof(c->inbuffer)/2) {
220  memmove(c->inbuffer, c->inbuffer + c->indata_used,
221  c->indata - c->indata_used);
222  c->indata -= c->indata_used;
223  c->indata_used = 0;
224  }
225  if (c->eof) {
226  // Remove PKCS7 padding at the end
227  int padding = c->outbuffer[c->outdata - 1];
228  c->outdata -= padding;
229  }
230  goto retry;
231 }
232 
233 static int64_t crypto_seek(URLContext *h, int64_t pos, int whence)
234 {
235  CryptoContext *c = h->priv_data;
236  int64_t block;
237  int64_t newpos;
238 
239  if (c->flags & AVIO_FLAG_WRITE) {
240  av_log(h, AV_LOG_ERROR,
241  "Crypto: seek not supported for write\r\n");
242  /* seems the most appropriate error to return */
243  return AVERROR(ESPIPE);
244  }
245 
246  // reset eof, else we won't read it correctly if we already hit eof.
247  c->eof = 0;
248 
249  switch (whence) {
250  case SEEK_SET:
251  break;
252  case SEEK_CUR:
253  pos = pos + c->position;
254  break;
255  case SEEK_END: {
256  int64_t newpos = ffurl_seek( c->hd, pos, AVSEEK_SIZE );
257  if (newpos < 0) {
258  av_log(h, AV_LOG_ERROR,
259  "Crypto: seek_end - can't get file size (pos=%lld)\r\n", (long long int)pos);
260  return newpos;
261  }
262  pos = newpos - pos;
263  }
264  break;
265  case AVSEEK_SIZE: {
266  int64_t newpos = ffurl_seek( c->hd, pos, AVSEEK_SIZE );
267  return newpos;
268  }
269  break;
270  default:
271  av_log(h, AV_LOG_ERROR,
272  "Crypto: no support for seek where 'whence' is %d\r\n", whence);
273  return AVERROR(EINVAL);
274  }
275 
276  c->outdata = 0;
277  c->indata = 0;
278  c->indata_used = 0;
279  c->outptr = c->outbuffer;
280 
281  // identify the block containing the IV for the
282  // next block we will decrypt
283  block = pos/BLOCKSIZE;
284  if (block == 0) {
285  // restore the iv to the seed one - this is the iv for the FIRST block
286  memcpy( c->decrypt_iv, c->iv, c->ivlen );
287  c->position = 0;
288  } else {
289  // else, go back one block - we will get av_cyrpt to read this block
290  // which it will then store use as the iv.
291  // note that the DECRYPTED result will not be correct,
292  // but will be discarded
293  block--;
294  c->position = (block * BLOCKSIZE);
295  }
296 
297  newpos = ffurl_seek( c->hd, c->position, SEEK_SET );
298  if (newpos < 0) {
299  av_log(h, AV_LOG_ERROR,
300  "Crypto: nested protocol no support for seek or seek failed\n");
301  return newpos;
302  }
303 
304  // read and discard from here up to required position
305  // (which will set the iv correctly to it).
306  if (pos - c->position) {
307  uint8_t buff[BLOCKSIZE*2]; // maximum size of pos-c->position
308  int len = pos - c->position;
309  int res;
310 
311  while (len > 0) {
312  // note: this may not return all the bytes first time
313  res = crypto_read(h, buff, len);
314  if (res < 0)
315  break;
316  len -= res;
317  }
318 
319  // if we did not get all the bytes
320  if (len != 0) {
321  char errbuf[100] = "unknown error";
322  av_strerror(res, errbuf, sizeof(errbuf));
323  av_log(h, AV_LOG_ERROR,
324  "Crypto: discard read did not get all the bytes (%d remain) - read returned (%d)-%s\n",
325  len, res, errbuf);
326  return AVERROR(EINVAL);
327  }
328  }
329 
330  return c->position;
331 }
332 
333 static int crypto_write(URLContext *h, const unsigned char *buf, int size)
334 {
335  CryptoContext *c = h->priv_data;
336  int total_size, blocks, pad_len, out_size;
337  int ret = 0;
338 
339  total_size = size + c->pad_len;
340  pad_len = total_size % BLOCKSIZE;
341  out_size = total_size - pad_len;
342  blocks = out_size / BLOCKSIZE;
343 
344  if (out_size) {
345  av_fast_malloc(&c->write_buf, &c->write_buf_size, out_size);
346 
347  if (!c->write_buf)
348  return AVERROR(ENOMEM);
349 
350  if (c->pad_len) {
351  memcpy(&c->pad[c->pad_len], buf, BLOCKSIZE - c->pad_len);
352  av_aes_crypt(c->aes_encrypt, c->write_buf, c->pad, 1, c->encrypt_iv, 0);
353  blocks--;
354  }
355 
357  &c->write_buf[c->pad_len ? BLOCKSIZE : 0],
358  &buf[c->pad_len ? BLOCKSIZE - c->pad_len : 0],
359  blocks, c->encrypt_iv, 0);
360 
361  ret = ffurl_write(c->hd, c->write_buf, out_size);
362  if (ret < 0)
363  return ret;
364 
365  memcpy(c->pad, &buf[size - pad_len], pad_len);
366  } else
367  memcpy(&c->pad[c->pad_len], buf, size);
368 
369  c->pad_len = pad_len;
370 
371  return size;
372 }
373 
375 {
376  CryptoContext *c = h->priv_data;
377  int ret = 0;
378 
379  if (c->aes_encrypt) {
380  uint8_t out_buf[BLOCKSIZE];
381  int pad = BLOCKSIZE - c->pad_len;
382 
383  memset(&c->pad[c->pad_len], pad, pad);
384  av_aes_crypt(c->aes_encrypt, out_buf, c->pad, 1, c->encrypt_iv, 0);
385  ret = ffurl_write(c->hd, out_buf, BLOCKSIZE);
386  }
387 
388  if (c->hd)
389  ffurl_close(c->hd);
390  av_freep(&c->aes_decrypt);
391  av_freep(&c->aes_encrypt);
392  av_freep(&c->write_buf);
393  return ret;
394 }
395 
397  .name = "crypto",
398  .url_open2 = crypto_open2,
399  .url_seek = crypto_seek,
400  .url_read = crypto_read,
401  .url_write = crypto_write,
402  .url_close = crypto_close,
403  .priv_data_size = sizeof(CryptoContext),
404  .priv_data_class = &crypto_class,
406 };
static int crypto_close(URLContext *h)
Definition: crypto.c:374
#define NULL
Definition: coverity.c:32
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:307
uint8_t * iv
Definition: crypto.c:45
AVOption.
Definition: opt.h:246
int ivlen
Definition: crypto.c:46
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
int ffurl_write(URLContext *h, const unsigned char *buf, int size)
Write size bytes from buf to the resource accessed by h.
Definition: avio.c:421
const char * desc
Definition: nvenc.c:68
int is_streamed
true if streamed (no seek possible), default = false
Definition: url.h:45
int decrypt_ivlen
Definition: crypto.c:50
int64_t position
Definition: crypto.c:40
AVIOInterruptCB interrupt_callback
Definition: url.h:47
static int crypto_read(URLContext *h, uint8_t *buf, int size)
Definition: crypto.c:183
#define AVIO_FLAG_READ
read-only
Definition: avio.h:654
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:191
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:163
#define AVIO_FLAG_WRITE
write-only
Definition: avio.h:655
int decrypt_keylen
Definition: crypto.c:48
int out_size
Definition: movenc.c:55
int encrypt_ivlen
Definition: crypto.c:54
static const AVOption options[]
Definition: crypto.c:66
unsigned int write_buf_size
Definition: crypto.c:58
uint8_t inbuffer[BLOCKSIZE *MAX_BUFFER_BLOCKS]
Definition: crypto.c:36
uint8_t * outptr
Definition: crypto.c:38
uint8_t * encrypt_key
Definition: crypto.c:51
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:83
#define E
Definition: crypto.c:65
uint8_t outbuffer[BLOCKSIZE *MAX_BUFFER_BLOCKS]
Definition: crypto.c:36
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:72
int keylen
Definition: crypto.c:44
The exact code depends on how similar the blocks are and how related they are to the block
uint8_t
AVOptions.
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
#define AVERROR_EOF
End of file.
Definition: error.h:55
ptrdiff_t size
Definition: opengl_enc.c:100
#define av_log(a,...)
static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary **options)
Definition: crypto.c:110
#define MAX_BUFFER_BLOCKS
Definition: crypto.c:30
#define BLOCKSIZE
Definition: crypto.c:31
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
struct AVAES * aes_decrypt
Definition: crypto.c:55
uint8_t * decrypt_key
Definition: crypto.c:47
int indata_used
Definition: crypto.c:39
const char * protocol_whitelist
Definition: url.h:49
uint8_t pad[BLOCKSIZE]
Definition: crypto.c:59
#define URL_PROTOCOL_FLAG_NESTED_SCHEME
Definition: url.h:33
int pad_len
Definition: crypto.c:60
int outdata
Definition: crypto.c:39
struct AVAES * av_aes_alloc(void)
Allocate an AVAES context.
Definition: aes.c:31
struct AVAES * aes_encrypt
Definition: crypto.c:56
uint8_t * encrypt_iv
Definition: crypto.c:53
void * av_memdup(const void *p, size_t size)
Duplicate a buffer with av_malloc().
Definition: mem.c:283
#define D
Definition: crypto.c:64
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:500
uint8_t * key
Definition: crypto.c:43
#define FFMIN(a, b)
Definition: common.h:96
static const AVClass crypto_class
Definition: crypto.c:76
URLContext * hd
Definition: crypto.c:35
uint8_t * write_buf
Definition: crypto.c:57
int n
Definition: avisynth_c.h:760
int indata
Definition: crypto.c:39
offset must point to a pointer immediately followed by an int for the length
Definition: opt.h:229
int av_aes_init(AVAES *a, const uint8_t *key, int key_bits, int decrypt)
Initialize an AVAES context.
Definition: aes.c:195
const char * protocol_blacklist
Definition: url.h:50
const URLProtocol ff_crypto_protocol
Definition: crypto.c:396
static int64_t crypto_seek(URLContext *h, int64_t pos, int whence)
Definition: crypto.c:233
uint8_t * decrypt_iv
Definition: crypto.c:49
void * buf
Definition: avisynth_c.h:766
Definition: url.h:38
Describe the class of an AVClass context structure.
Definition: log.h:67
void * priv_data
Definition: url.h:41
static int crypto_write(URLContext *h, const unsigned char *buf, int size)
Definition: crypto.c:333
const char * name
Definition: url.h:55
int ffurl_close(URLContext *h)
Definition: avio.c:467
int av_strerror(int errnum, char *errbuf, size_t errbuf_size)
Put a description of the AVERROR code errnum in errbuf.
Definition: error.c:105
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:34
Main libavformat public API header.
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: avio.c:434
#define AVSEEK_SIZE
ORing this as the "whence" parameter to a seek function causes it to return the filesize without seek...
Definition: avio.h:531
int len
#define OFFSET(x)
Definition: crypto.c:63
int flags
Definition: crypto.c:41
#define av_freep(p)
unbuffered private I/O API
int encrypt_keylen
Definition: crypto.c:52
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
int ffurl_read(URLContext *h, unsigned char *buf, int size)
Read up to size bytes from the resource accessed by h, and store the read bytes in buf...
Definition: avio.c:407