FFmpeg
rtmpdh.c
Go to the documentation of this file.
1 /*
2  * RTMP Diffie-Hellmann utilities
3  * Copyright (c) 2009 Andrej Stepanchuk
4  * Copyright (c) 2009-2010 Howard Chu
5  * Copyright (c) 2012 Samuel Pitoiset
6  *
7  * This file is part of FFmpeg.
8  *
9  * FFmpeg is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * FFmpeg is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with FFmpeg; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  */
23 
24 /**
25  * @file
26  * RTMP Diffie-Hellmann utilities
27  */
28 
29 #include <stdint.h>
30 #include <string.h>
31 
32 #include "config.h"
33 
34 #include "libavutil/attributes.h"
35 #include "libavutil/error.h"
36 #include "libavutil/mem.h"
37 #include "libavutil/random_seed.h"
38 
39 #include "rtmpdh.h"
40 
41 #if CONFIG_MBEDTLS
42 #include <mbedtls/ctr_drbg.h>
43 #include <mbedtls/entropy.h>
44 #endif
45 
46 #define P1024 \
47  "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
48  "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
49  "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
50  "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
51  "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \
52  "FFFFFFFFFFFFFFFF"
53 
54 #define Q1024 \
55  "7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68" \
56  "948127044533E63A0105DF531D89CD9128A5043CC71A026E" \
57  "F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122" \
58  "F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6" \
59  "F71C35FDAD44CFD2D74F9208BE258FF324943328F67329C0" \
60  "FFFFFFFFFFFFFFFF"
61 
62 #if CONFIG_GMP
63 #define bn_new(bn) \
64  do { \
65  bn = av_malloc(sizeof(*bn)); \
66  if (bn) \
67  mpz_init2(bn, 1); \
68  } while (0)
69 #define bn_free(bn) \
70  do { \
71  mpz_clear(bn); \
72  av_free(bn); \
73  } while (0)
74 #define bn_set_word(bn, w) mpz_set_ui(bn, w)
75 #define bn_cmp(a, b) mpz_cmp(a, b)
76 #define bn_copy(to, from) mpz_set(to, from)
77 #define bn_sub_word(bn, w) mpz_sub_ui(bn, bn, w)
78 #define bn_cmp_1(bn) mpz_cmp_ui(bn, 1)
79 #define bn_num_bytes(bn) (mpz_sizeinbase(bn, 2) + 7) / 8
80 #define bn_bn2bin(bn, buf, len) \
81  do { \
82  memset(buf, 0, len); \
83  if (bn_num_bytes(bn) <= len) \
84  mpz_export(buf, NULL, 1, 1, 0, 0, bn); \
85  } while (0)
86 #define bn_bin2bn(bn, buf, len) \
87  do { \
88  bn_new(bn); \
89  if (bn) \
90  mpz_import(bn, len, 1, 1, 0, 0, buf); \
91  } while (0)
92 #define bn_hex2bn(bn, buf, ret) \
93  do { \
94  bn_new(bn); \
95  if (bn) \
96  ret = (mpz_set_str(bn, buf, 16) == 0); \
97  else \
98  ret = 1; \
99  } while (0)
100 #define bn_random(bn, num_bits) \
101  do { \
102  int bits = num_bits; \
103  mpz_set_ui(bn, 0); \
104  for (bits = num_bits; bits > 0; bits -= 32) { \
105  mpz_mul_2exp(bn, bn, 32); \
106  mpz_add_ui(bn, bn, av_get_random_seed()); \
107  } \
108  mpz_fdiv_r_2exp(bn, bn, num_bits); \
109  } while (0)
110 static int bn_modexp(FFBigNum bn, FFBigNum y, FFBigNum q, FFBigNum p)
111 {
112  mpz_powm(bn, y, q, p);
113  return 0;
114 }
115 #elif CONFIG_GCRYPT
116 #define bn_new(bn) \
117  do { \
118  if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) { \
119  if (!gcry_check_version("1.5.4")) \
120  return AVERROR(EINVAL); \
121  gcry_control(GCRYCTL_DISABLE_SECMEM, 0); \
122  gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); \
123  } \
124  bn = gcry_mpi_new(1); \
125  } while (0)
126 #define bn_free(bn) gcry_mpi_release(bn)
127 #define bn_set_word(bn, w) gcry_mpi_set_ui(bn, w)
128 #define bn_cmp(a, b) gcry_mpi_cmp(a, b)
129 #define bn_copy(to, from) gcry_mpi_set(to, from)
130 #define bn_sub_word(bn, w) gcry_mpi_sub_ui(bn, bn, w)
131 #define bn_cmp_1(bn) gcry_mpi_cmp_ui(bn, 1)
132 #define bn_num_bytes(bn) (gcry_mpi_get_nbits(bn) + 7) / 8
133 #define bn_bn2bin(bn, buf, len) gcry_mpi_print(GCRYMPI_FMT_USG, buf, len, NULL, bn)
134 #define bn_bin2bn(bn, buf, len) gcry_mpi_scan(&bn, GCRYMPI_FMT_USG, buf, len, NULL)
135 #define bn_hex2bn(bn, buf, ret) ret = (gcry_mpi_scan(&bn, GCRYMPI_FMT_HEX, buf, 0, 0) == 0)
136 #define bn_random(bn, num_bits) gcry_mpi_randomize(bn, num_bits, GCRY_WEAK_RANDOM)
137 static int bn_modexp(FFBigNum bn, FFBigNum y, FFBigNum q, FFBigNum p)
138 {
139  gcry_mpi_powm(bn, y, q, p);
140  return 0;
141 }
142 #elif CONFIG_OPENSSL
143 #define bn_new(bn) bn = BN_new()
144 #define bn_free(bn) BN_free(bn)
145 #define bn_set_word(bn, w) BN_set_word(bn, w)
146 #define bn_cmp(a, b) BN_cmp(a, b)
147 #define bn_copy(to, from) BN_copy(to, from)
148 #define bn_sub_word(bn, w) BN_sub_word(bn, w)
149 #define bn_cmp_1(bn) BN_cmp(bn, BN_value_one())
150 #define bn_num_bytes(bn) BN_num_bytes(bn)
151 #define bn_bn2bin(bn, buf, len) BN_bn2bin(bn, buf)
152 #define bn_bin2bn(bn, buf, len) bn = BN_bin2bn(buf, len, 0)
153 #define bn_hex2bn(bn, buf, ret) ret = BN_hex2bn(&bn, buf)
154 #define bn_random(bn, num_bits) BN_rand(bn, num_bits, 0, 0)
155 static int bn_modexp(FFBigNum bn, FFBigNum y, FFBigNum q, FFBigNum p)
156 {
157  BN_CTX *ctx = BN_CTX_new();
158  if (!ctx)
159  return AVERROR(ENOMEM);
160  if (!BN_mod_exp(bn, y, q, p, ctx)) {
161  BN_CTX_free(ctx);
162  return AVERROR(EINVAL);
163  }
164  BN_CTX_free(ctx);
165  return 0;
166 }
167 #elif CONFIG_MBEDTLS
168 #define bn_new(bn) \
169  do { \
170  bn = av_malloc(sizeof(*bn)); \
171  if (bn) \
172  mbedtls_mpi_init(bn); \
173  } while (0)
174 #define bn_free(bn) \
175  do { \
176  mbedtls_mpi_free(bn); \
177  av_free(bn); \
178  } while (0)
179 #define bn_set_word(bn, w) mbedtls_mpi_lset(bn, w)
180 #define bn_cmp(a, b) mbedtls_mpi_cmp_mpi(a, b)
181 #define bn_copy(to, from) mbedtls_mpi_copy(to, from)
182 #define bn_sub_word(bn, w) mbedtls_mpi_sub_int(bn, bn, w)
183 #define bn_cmp_1(bn) mbedtls_mpi_cmp_int(bn, 1)
184 #define bn_num_bytes(bn) (mbedtls_mpi_bitlen(bn) + 7) / 8
185 #define bn_bn2bin(bn, buf, len) mbedtls_mpi_write_binary(bn, buf, len)
186 #define bn_bin2bn(bn, buf, len) \
187  do { \
188  bn_new(bn); \
189  if (bn) \
190  mbedtls_mpi_read_binary(bn, buf, len); \
191  } while (0)
192 #define bn_hex2bn(bn, buf, ret) \
193  do { \
194  bn_new(bn); \
195  if (bn) \
196  ret = (mbedtls_mpi_read_string(bn, 16, buf) == 0); \
197  else \
198  ret = 1; \
199  } while (0)
200 #define bn_random(bn, num_bits) \
201  do { \
202  mbedtls_entropy_context entropy_ctx; \
203  mbedtls_ctr_drbg_context ctr_drbg_ctx; \
204  \
205  mbedtls_entropy_init(&entropy_ctx); \
206  mbedtls_ctr_drbg_init(&ctr_drbg_ctx); \
207  mbedtls_ctr_drbg_seed(&ctr_drbg_ctx, \
208  mbedtls_entropy_func, \
209  &entropy_ctx, \
210  NULL, 0); \
211  mbedtls_mpi_fill_random(bn, (num_bits + 7) / 8, mbedtls_ctr_drbg_random, &ctr_drbg_ctx); \
212  mbedtls_ctr_drbg_free(&ctr_drbg_ctx); \
213  mbedtls_entropy_free(&entropy_ctx); \
214  } while (0)
215 #define bn_modexp(bn, y, q, p) mbedtls_mpi_exp_mod(bn, y, q, p, 0)
216 
217 #endif
218 
219 #define MAX_BYTES 18000
220 
221 #define dh_new() av_mallocz(sizeof(FF_DH))
222 
223 static FFBigNum dh_generate_key(FF_DH *dh)
224 {
225  int num_bytes;
226 
227  num_bytes = bn_num_bytes(dh->p) - 1;
228  if (num_bytes <= 0 || num_bytes > MAX_BYTES)
229  return NULL;
230 
231  bn_new(dh->priv_key);
232  if (!dh->priv_key)
233  return NULL;
234  bn_random(dh->priv_key, 8 * num_bytes);
235 
236  bn_new(dh->pub_key);
237  if (!dh->pub_key) {
238  bn_free(dh->priv_key);
239  return NULL;
240  }
241 
242  if (bn_modexp(dh->pub_key, dh->g, dh->priv_key, dh->p) < 0)
243  return NULL;
244 
245  return dh->pub_key;
246 }
247 
248 static int dh_compute_key(FF_DH *dh, FFBigNum pub_key_bn,
249  uint32_t secret_key_len, uint8_t *secret_key)
250 {
251  FFBigNum k;
252  int ret;
253 
254  bn_new(k);
255  if (!k)
256  return -1;
257 
258  if ((ret = bn_modexp(k, pub_key_bn, dh->priv_key, dh->p)) < 0) {
259  bn_free(k);
260  return ret;
261  }
262  bn_bn2bin(k, secret_key, secret_key_len);
263  bn_free(k);
264 
265  /* return the length of the shared secret key like DH_compute_key */
266  return secret_key_len;
267 }
268 
269 void ff_dh_free(FF_DH *dh)
270 {
271  if (!dh)
272  return;
273  bn_free(dh->p);
274  bn_free(dh->g);
275  bn_free(dh->pub_key);
276  bn_free(dh->priv_key);
277  av_free(dh);
278 }
279 
280 static int dh_is_valid_public_key(FFBigNum y, FFBigNum p, FFBigNum q)
281 {
282  FFBigNum bn = NULL;
283  int ret = AVERROR(EINVAL);
284 
285  bn_new(bn);
286  if (!bn)
287  return AVERROR(ENOMEM);
288 
289  /* y must lie in [2, p - 1] */
290  bn_set_word(bn, 1);
291  if (!bn_cmp(y, bn))
292  goto fail;
293 
294  /* bn = p - 2 */
295  bn_copy(bn, p);
296  bn_sub_word(bn, 1);
297  if (!bn_cmp(y, bn))
298  goto fail;
299 
300  /* Verify with Sophie-Germain prime
301  *
302  * This is a nice test to make sure the public key position is calculated
303  * correctly. This test will fail in about 50% of the cases if applied to
304  * random data.
305  */
306  /* y must fulfill y^q mod p = 1 */
307  if ((ret = bn_modexp(bn, y, q, p)) < 0)
308  goto fail;
309 
310  ret = AVERROR(EINVAL);
311  if (bn_cmp_1(bn))
312  goto fail;
313 
314  ret = 0;
315 fail:
316  bn_free(bn);
317 
318  return ret;
319 }
320 
321 av_cold FF_DH *ff_dh_init(int key_len)
322 {
323  FF_DH *dh;
324  int ret;
325 
326  if (!(dh = dh_new()))
327  return NULL;
328 
329  bn_new(dh->g);
330  if (!dh->g)
331  goto fail;
332 
333  bn_hex2bn(dh->p, P1024, ret);
334  if (!ret)
335  goto fail;
336 
337  bn_set_word(dh->g, 2);
338  dh->length = key_len;
339 
340  return dh;
341 
342 fail:
343  ff_dh_free(dh);
344 
345  return NULL;
346 }
347 
349 {
350  int ret = 0;
351 
352  while (!ret) {
353  FFBigNum q1 = NULL;
354 
355  if (!dh_generate_key(dh))
356  return AVERROR(EINVAL);
357 
358  bn_hex2bn(q1, Q1024, ret);
359  if (!ret)
360  return AVERROR(ENOMEM);
361 
362  ret = dh_is_valid_public_key(dh->pub_key, dh->p, q1);
363  bn_free(q1);
364 
365  if (!ret) {
366  /* the public key is valid */
367  break;
368  }
369  }
370 
371  return ret;
372 }
373 
374 int ff_dh_write_public_key(FF_DH *dh, uint8_t *pub_key, int pub_key_len)
375 {
376  int len;
377 
378  /* compute the length of the public key */
379  len = bn_num_bytes(dh->pub_key);
380  if (len <= 0 || len > pub_key_len)
381  return AVERROR(EINVAL);
382 
383  /* convert the public key value into big-endian form */
384  memset(pub_key, 0, pub_key_len);
385  bn_bn2bin(dh->pub_key, pub_key + pub_key_len - len, len);
386 
387  return 0;
388 }
389 
391  int pub_key_len, uint8_t *secret_key,
392  int secret_key_len)
393 {
394  FFBigNum q1 = NULL, pub_key_bn = NULL;
395  int ret;
396 
397  /* convert the big-endian form of the public key into a bignum */
398  bn_bin2bn(pub_key_bn, pub_key, pub_key_len);
399  if (!pub_key_bn)
400  return AVERROR(ENOMEM);
401 
402  /* convert the string containing a hexadecimal number into a bignum */
403  bn_hex2bn(q1, Q1024, ret);
404  if (!ret) {
405  ret = AVERROR(ENOMEM);
406  goto fail;
407  }
408 
409  /* when the public key is valid we have to compute the shared secret key */
410  if ((ret = dh_is_valid_public_key(pub_key_bn, dh->p, q1)) < 0) {
411  goto fail;
412  } else if ((ret = dh_compute_key(dh, pub_key_bn, secret_key_len,
413  secret_key)) < 0) {
414  ret = AVERROR(EINVAL);
415  goto fail;
416  }
417 
418 fail:
419  bn_free(pub_key_bn);
420  bn_free(q1);
421 
422  return ret;
423 }
uint32_t p[AV_BF_ROUNDS+2]
Definition: blowfish.h:36
#define NULL
Definition: coverity.c:32
int ff_dh_write_public_key(FF_DH *dh, uint8_t *pub_key, int pub_key_len)
Write the public key into the given buffer.
Definition: rtmpdh.c:374
Definition: rtmpdh.h:50
static int dh_is_valid_public_key(FFBigNum y, FFBigNum p, FFBigNum q)
Definition: rtmpdh.c:280
Memory handling functions.
int ff_dh_compute_shared_secret_key(FF_DH *dh, const uint8_t *pub_key, int pub_key_len, uint8_t *secret_key, int secret_key_len)
Compute the shared secret key from the private FF_DH value and the other party&#39;s public value...
Definition: rtmpdh.c:390
void ff_dh_free(FF_DH *dh)
Free a Diffie-Hellmann context.
Definition: rtmpdh.c:269
#define P1024
Definition: rtmpdh.c:46
FFBigNum priv_key
Definition: rtmpdh.h:54
static const uint8_t q1[256]
Definition: twofish.c:96
Macro definitions for various function/variable attributes.
uint8_t
#define av_cold
Definition: attributes.h:82
int ff_dh_generate_public_key(FF_DH *dh)
Generate a public key.
Definition: rtmpdh.c:348
error code definitions
FFBigNum p
Definition: rtmpdh.h:51
#define fail()
Definition: checkasm.h:121
AVFormatContext * ctx
Definition: movenc.c:48
static int dh_compute_key(FF_DH *dh, FFBigNum pub_key_bn, uint32_t secret_key_len, uint8_t *secret_key)
Definition: rtmpdh.c:248
FFBigNum g
Definition: rtmpdh.h:52
#define dh_new()
Definition: rtmpdh.c:221
#define Q1024
Definition: rtmpdh.c:54
#define MAX_BYTES
Definition: rtmpdh.c:219
long length
Definition: rtmpdh.h:55
av_cold FF_DH * ff_dh_init(int key_len)
Initialize a Diffie-Hellmann context.
Definition: rtmpdh.c:321
FFBigNum pub_key
Definition: rtmpdh.h:53
#define av_free(p)
int len
static FFBigNum dh_generate_key(FF_DH *dh)
Definition: rtmpdh.c:223
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