FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 "config.h"
30 #include "rtmpdh.h"
31 #include "libavutil/random_seed.h"
32 
33 #define P1024 \
34  "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
35  "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
36  "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
37  "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
38  "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \
39  "FFFFFFFFFFFFFFFF"
40 
41 #define Q1024 \
42  "7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68" \
43  "948127044533E63A0105DF531D89CD9128A5043CC71A026E" \
44  "F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122" \
45  "F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6" \
46  "F71C35FDAD44CFD2D74F9208BE258FF324943328F67329C0" \
47  "FFFFFFFFFFFFFFFF"
48 
49 #if CONFIG_GMP || CONFIG_GCRYPT
50 #if CONFIG_GMP
51 #define bn_new(bn) \
52  do { \
53  bn = av_malloc(sizeof(*bn)); \
54  if (bn) \
55  mpz_init2(bn, 1); \
56  } while (0)
57 #define bn_free(bn) \
58  do { \
59  mpz_clear(bn); \
60  av_free(bn); \
61  } while (0)
62 #define bn_set_word(bn, w) mpz_set_ui(bn, w)
63 #define bn_cmp(a, b) mpz_cmp(a, b)
64 #define bn_copy(to, from) mpz_set(to, from)
65 #define bn_sub_word(bn, w) mpz_sub_ui(bn, bn, w)
66 #define bn_cmp_1(bn) mpz_cmp_ui(bn, 1)
67 #define bn_num_bytes(bn) (mpz_sizeinbase(bn, 2) + 7) / 8
68 #define bn_bn2bin(bn, buf, len) \
69  do { \
70  memset(buf, 0, len); \
71  if (bn_num_bytes(bn) <= len) \
72  mpz_export(buf, NULL, 1, 1, 0, 0, bn); \
73  } while (0)
74 #define bn_bin2bn(bn, buf, len) \
75  do { \
76  bn_new(bn); \
77  if (bn) \
78  mpz_import(bn, len, 1, 1, 0, 0, buf); \
79  } while (0)
80 #define bn_hex2bn(bn, buf, ret) \
81  do { \
82  bn_new(bn); \
83  if (bn) \
84  ret = (mpz_set_str(bn, buf, 16) == 0); \
85  else \
86  ret = 1; \
87  } while (0)
88 #define bn_modexp(bn, y, q, p) mpz_powm(bn, y, q, p)
89 #define bn_random(bn, num_bits) \
90  do { \
91  int bits = num_bits; \
92  mpz_set_ui(bn, 0); \
93  for (bits = num_bits; bits > 0; bits -= 32) { \
94  mpz_mul_2exp(bn, bn, 32); \
95  mpz_add_ui(bn, bn, av_get_random_seed()); \
96  } \
97  mpz_fdiv_r_2exp(bn, bn, num_bits); \
98  } while (0)
99 #elif CONFIG_GCRYPT
100 #define bn_new(bn) bn = gcry_mpi_new(1)
101 #define bn_free(bn) gcry_mpi_release(bn)
102 #define bn_set_word(bn, w) gcry_mpi_set_ui(bn, w)
103 #define bn_cmp(a, b) gcry_mpi_cmp(a, b)
104 #define bn_copy(to, from) gcry_mpi_set(to, from)
105 #define bn_sub_word(bn, w) gcry_mpi_sub_ui(bn, bn, w)
106 #define bn_cmp_1(bn) gcry_mpi_cmp_ui(bn, 1)
107 #define bn_num_bytes(bn) (gcry_mpi_get_nbits(bn) + 7) / 8
108 #define bn_bn2bin(bn, buf, len) gcry_mpi_print(GCRYMPI_FMT_USG, buf, len, NULL, bn)
109 #define bn_bin2bn(bn, buf, len) gcry_mpi_scan(&bn, GCRYMPI_FMT_USG, buf, len, NULL)
110 #define bn_hex2bn(bn, buf, ret) ret = (gcry_mpi_scan(&bn, GCRYMPI_FMT_HEX, buf, 0, 0) == 0)
111 #define bn_modexp(bn, y, q, p) gcry_mpi_powm(bn, y, q, p)
112 #define bn_random(bn, num_bits) gcry_mpi_randomize(bn, num_bits, GCRY_WEAK_RANDOM)
113 #endif
114 
115 #define MAX_BYTES 18000
116 
117 #define dh_new() av_malloc(sizeof(FF_DH))
118 
119 static FFBigNum dh_generate_key(FF_DH *dh)
120 {
121  int num_bytes;
122 
123  num_bytes = bn_num_bytes(dh->p) - 1;
124  if (num_bytes <= 0 || num_bytes > MAX_BYTES)
125  return NULL;
126 
127  bn_new(dh->priv_key);
128  if (!dh->priv_key)
129  return NULL;
130  bn_random(dh->priv_key, 8 * num_bytes);
131 
132  bn_new(dh->pub_key);
133  if (!dh->pub_key) {
134  bn_free(dh->priv_key);
135  return NULL;
136  }
137 
138  bn_modexp(dh->pub_key, dh->g, dh->priv_key, dh->p);
139 
140  return dh->pub_key;
141 }
142 
143 static int dh_compute_key(FF_DH *dh, FFBigNum pub_key_bn,
144  uint32_t secret_key_len, uint8_t *secret_key)
145 {
146  FFBigNum k;
147 
148  bn_new(k);
149  if (!k)
150  return -1;
151 
152  bn_modexp(k, pub_key_bn, dh->priv_key, dh->p);
153  bn_bn2bin(k, secret_key, secret_key_len);
154  bn_free(k);
155 
156  /* return the length of the shared secret key like DH_compute_key */
157  return secret_key_len;
158 }
159 
160 void ff_dh_free(FF_DH *dh)
161 {
162  if (!dh)
163  return;
164  bn_free(dh->p);
165  bn_free(dh->g);
166  bn_free(dh->pub_key);
167  bn_free(dh->priv_key);
168  av_free(dh);
169 }
170 #elif CONFIG_OPENSSL
171 #define bn_new(bn) bn = BN_new()
172 #define bn_free(bn) BN_free(bn)
173 #define bn_set_word(bn, w) BN_set_word(bn, w)
174 #define bn_cmp(a, b) BN_cmp(a, b)
175 #define bn_copy(to, from) BN_copy(to, from)
176 #define bn_sub_word(bn, w) BN_sub_word(bn, w)
177 #define bn_cmp_1(bn) BN_cmp(bn, BN_value_one())
178 #define bn_num_bytes(bn) BN_num_bytes(bn)
179 #define bn_bn2bin(bn, buf, len) BN_bn2bin(bn, buf)
180 #define bn_bin2bn(bn, buf, len) bn = BN_bin2bn(buf, len, 0)
181 #define bn_hex2bn(bn, buf, ret) ret = BN_hex2bn(&bn, buf)
182 #define bn_modexp(bn, y, q, p) \
183  do { \
184  BN_CTX *ctx = BN_CTX_new(); \
185  if (!ctx) \
186  return AVERROR(ENOMEM); \
187  if (!BN_mod_exp(bn, y, q, p, ctx)) { \
188  BN_CTX_free(ctx); \
189  return AVERROR(EINVAL); \
190  } \
191  BN_CTX_free(ctx); \
192  } while (0)
193 
194 #define dh_new() DH_new()
195 #define dh_generate_key(dh) DH_generate_key(dh)
196 
197 static int dh_compute_key(FF_DH *dh, FFBigNum pub_key_bn,
198  uint32_t secret_key_len, uint8_t *secret_key)
199 {
200  if (secret_key_len < DH_size(dh))
201  return AVERROR(EINVAL);
202  return DH_compute_key(secret_key, pub_key_bn, dh);
203 }
204 
205 void ff_dh_free(FF_DH *dh)
206 {
207  if (!dh)
208  return;
209  DH_free(dh);
210 }
211 #endif
212 
213 static int dh_is_valid_public_key(FFBigNum y, FFBigNum p, FFBigNum q)
214 {
215  FFBigNum bn = NULL;
216  int ret = AVERROR(EINVAL);
217 
218  bn_new(bn);
219  if (!bn)
220  return AVERROR(ENOMEM);
221 
222  /* y must lie in [2, p - 1] */
223  bn_set_word(bn, 1);
224  if (!bn_cmp(y, bn))
225  goto fail;
226 
227  /* bn = p - 2 */
228  bn_copy(bn, p);
229  bn_sub_word(bn, 1);
230  if (!bn_cmp(y, bn))
231  goto fail;
232 
233  /* Verify with Sophie-Germain prime
234  *
235  * This is a nice test to make sure the public key position is calculated
236  * correctly. This test will fail in about 50% of the cases if applied to
237  * random data.
238  */
239  /* y must fulfill y^q mod p = 1 */
240  bn_modexp(bn, y, q, p);
241 
242  if (bn_cmp_1(bn))
243  goto fail;
244 
245  ret = 0;
246 fail:
247  bn_free(bn);
248 
249  return ret;
250 }
251 
252 av_cold FF_DH *ff_dh_init(int key_len)
253 {
254  FF_DH *dh;
255  int ret;
256 
257  if (!(dh = dh_new()))
258  return NULL;
259 
260  bn_new(dh->g);
261  if (!dh->g)
262  goto fail;
263 
264  bn_hex2bn(dh->p, P1024, ret);
265  if (!ret)
266  goto fail;
267 
268  bn_set_word(dh->g, 2);
269  dh->length = key_len;
270 
271  return dh;
272 
273 fail:
274  ff_dh_free(dh);
275 
276  return NULL;
277 }
278 
280 {
281  int ret = 0;
282 
283  while (!ret) {
284  FFBigNum q1 = NULL;
285 
286  if (!dh_generate_key(dh))
287  return AVERROR(EINVAL);
288 
289  bn_hex2bn(q1, Q1024, ret);
290  if (!ret)
291  return AVERROR(ENOMEM);
292 
293  ret = dh_is_valid_public_key(dh->pub_key, dh->p, q1);
294  bn_free(q1);
295 
296  if (!ret) {
297  /* the public key is valid */
298  break;
299  }
300  }
301 
302  return ret;
303 }
304 
305 int ff_dh_write_public_key(FF_DH *dh, uint8_t *pub_key, int pub_key_len)
306 {
307  int len;
308 
309  /* compute the length of the public key */
310  len = bn_num_bytes(dh->pub_key);
311  if (len <= 0 || len > pub_key_len)
312  return AVERROR(EINVAL);
313 
314  /* convert the public key value into big-endian form */
315  memset(pub_key, 0, pub_key_len);
316  bn_bn2bin(dh->pub_key, pub_key + pub_key_len - len, len);
317 
318  return 0;
319 }
320 
321 int ff_dh_compute_shared_secret_key(FF_DH *dh, const uint8_t *pub_key,
322  int pub_key_len, uint8_t *secret_key,
323  int secret_key_len)
324 {
325  FFBigNum q1 = NULL, pub_key_bn = NULL;
326  int ret;
327 
328  /* convert the big-endian form of the public key into a bignum */
329  bn_bin2bn(pub_key_bn, pub_key, pub_key_len);
330  if (!pub_key_bn)
331  return AVERROR(ENOMEM);
332 
333  /* convert the string containing a hexadecimal number into a bignum */
334  bn_hex2bn(q1, Q1024, ret);
335  if (!ret) {
336  ret = AVERROR(ENOMEM);
337  goto fail;
338  }
339 
340  /* when the public key is valid we have to compute the shared secret key */
341  if ((ret = dh_is_valid_public_key(pub_key_bn, dh->p, q1)) < 0) {
342  goto fail;
343  } else if ((ret = dh_compute_key(dh, pub_key_bn, secret_key_len,
344  secret_key)) < 0) {
345  ret = AVERROR(EINVAL);
346  goto fail;
347  }
348 
349 fail:
350  bn_free(pub_key_bn);
351  bn_free(q1);
352 
353  return ret;
354 }
355 
356 #ifdef TEST
357 static int test_random_shared_secret(void)
358 {
359  FF_DH *peer1 = NULL, *peer2 = NULL;
360  int ret;
361  uint8_t pubkey1[128], pubkey2[128];
362  uint8_t sharedkey1[128], sharedkey2[128];
363 
364  peer1 = ff_dh_init(1024);
365  peer2 = ff_dh_init(1024);
366  if (!peer1 || !peer2) {
367  ret = AVERROR(ENOMEM);
368  goto fail;
369  }
370  if ((ret = ff_dh_generate_public_key(peer1)) < 0)
371  goto fail;
372  if ((ret = ff_dh_generate_public_key(peer2)) < 0)
373  goto fail;
374  if ((ret = ff_dh_write_public_key(peer1, pubkey1, sizeof(pubkey1))) < 0)
375  goto fail;
376  if ((ret = ff_dh_write_public_key(peer2, pubkey2, sizeof(pubkey2))) < 0)
377  goto fail;
378  if ((ret = ff_dh_compute_shared_secret_key(peer1, pubkey2, sizeof(pubkey2),
379  sharedkey1, sizeof(sharedkey1))) < 0)
380  goto fail;
381  if ((ret = ff_dh_compute_shared_secret_key(peer2, pubkey1, sizeof(pubkey1),
382  sharedkey2, sizeof(sharedkey2))) < 0)
383  goto fail;
384  if (memcmp(sharedkey1, sharedkey2, sizeof(sharedkey1))) {
385  printf("Mismatched generated shared key\n");
386  ret = AVERROR_INVALIDDATA;
387  } else {
388  printf("Generated shared key ok\n");
389  }
390 fail:
391  ff_dh_free(peer1);
392  ff_dh_free(peer2);
393  return ret;
394 }
395 
396 static const char *private_key =
397  "976C18FCADC255B456564F74F3EEDA59D28AF6B744D743F2357BFD2404797EF896EF1A"
398  "7C1CBEAAA3AB60AF3192D189CFF3F991C9CBBFD78119FCA2181384B94011943B6D6F28"
399  "9E1B708E2D1A0C7771169293F03DA27E561F15F16F0AC9BC858C77A80FA98FD088A232"
400  "19D08BE6F165DE0B02034B18705829FAD0ACB26A5B75EF";
401 static const char *public_key =
402  "F272ECF8362257C5D2C3CC2229CF9C0A03225BC109B1DBC76A68C394F256ACA3EF5F64"
403  "FC270C26382BF315C19E97A76104A716FC998A651E8610A3AE6CF65D8FAE5D3F32EEA0"
404  "0B32CB9609B494116A825D7142D17B88E3D20EDD98743DE29CF37A23A9F6A58B960591"
405  "3157D5965FCB46DDA73A1F08DD897BAE88DFE6FC937CBA";
406 static const uint8_t public_key_bin[] = {
407  0xf2, 0x72, 0xec, 0xf8, 0x36, 0x22, 0x57, 0xc5, 0xd2, 0xc3, 0xcc, 0x22,
408  0x29, 0xcf, 0x9c, 0x0a, 0x03, 0x22, 0x5b, 0xc1, 0x09, 0xb1, 0xdb, 0xc7,
409  0x6a, 0x68, 0xc3, 0x94, 0xf2, 0x56, 0xac, 0xa3, 0xef, 0x5f, 0x64, 0xfc,
410  0x27, 0x0c, 0x26, 0x38, 0x2b, 0xf3, 0x15, 0xc1, 0x9e, 0x97, 0xa7, 0x61,
411  0x04, 0xa7, 0x16, 0xfc, 0x99, 0x8a, 0x65, 0x1e, 0x86, 0x10, 0xa3, 0xae,
412  0x6c, 0xf6, 0x5d, 0x8f, 0xae, 0x5d, 0x3f, 0x32, 0xee, 0xa0, 0x0b, 0x32,
413  0xcb, 0x96, 0x09, 0xb4, 0x94, 0x11, 0x6a, 0x82, 0x5d, 0x71, 0x42, 0xd1,
414  0x7b, 0x88, 0xe3, 0xd2, 0x0e, 0xdd, 0x98, 0x74, 0x3d, 0xe2, 0x9c, 0xf3,
415  0x7a, 0x23, 0xa9, 0xf6, 0xa5, 0x8b, 0x96, 0x05, 0x91, 0x31, 0x57, 0xd5,
416  0x96, 0x5f, 0xcb, 0x46, 0xdd, 0xa7, 0x3a, 0x1f, 0x08, 0xdd, 0x89, 0x7b,
417  0xae, 0x88, 0xdf, 0xe6, 0xfc, 0x93, 0x7c, 0xba
418 };
419 static const uint8_t peer_public_key[] = {
420  0x58, 0x66, 0x05, 0x49, 0x94, 0x23, 0x2b, 0x66, 0x52, 0x13, 0xff, 0x46,
421  0xf2, 0xb3, 0x79, 0xa9, 0xee, 0xae, 0x1a, 0x13, 0xf0, 0x71, 0x52, 0xfb,
422  0x93, 0x4e, 0xee, 0x97, 0x05, 0x73, 0x50, 0x7d, 0xaf, 0x02, 0x07, 0x72,
423  0xac, 0xdc, 0xa3, 0x95, 0x78, 0xee, 0x9a, 0x19, 0x71, 0x7e, 0x99, 0x9f,
424  0x2a, 0xd4, 0xb3, 0xe2, 0x0c, 0x1d, 0x1a, 0x78, 0x4c, 0xde, 0xf1, 0xad,
425  0xb4, 0x60, 0xa8, 0x51, 0xac, 0x71, 0xec, 0x86, 0x70, 0xa2, 0x63, 0x36,
426  0x92, 0x7c, 0xe3, 0x87, 0xee, 0xe4, 0xf1, 0x62, 0x24, 0x74, 0xb4, 0x04,
427  0xfa, 0x5c, 0xdf, 0xba, 0xfa, 0xa3, 0xc2, 0xbb, 0x62, 0x27, 0xd0, 0xf4,
428  0xe4, 0x43, 0xda, 0x8a, 0x88, 0x69, 0x60, 0xe2, 0xdb, 0x75, 0x2a, 0x98,
429  0x9d, 0xb5, 0x50, 0xe3, 0x99, 0xda, 0xe0, 0xa6, 0x14, 0xc9, 0x80, 0x12,
430  0xf9, 0x3c, 0xac, 0x06, 0x02, 0x7a, 0xde, 0x74
431 };
432 static const uint8_t shared_secret[] = {
433  0xb2, 0xeb, 0xcb, 0x71, 0xf3, 0x61, 0xfb, 0x5b, 0x4e, 0x5c, 0x4c, 0xcf,
434  0x5c, 0x08, 0x5f, 0x96, 0x26, 0x77, 0x1d, 0x31, 0xf1, 0xe1, 0xf7, 0x4b,
435  0x92, 0xac, 0x82, 0x2a, 0x88, 0xc7, 0x83, 0xe1, 0xc7, 0xf3, 0xd3, 0x1a,
436  0x7d, 0xc8, 0x31, 0xe3, 0x97, 0xe4, 0xec, 0x31, 0x0e, 0x8f, 0x73, 0x1a,
437  0xe4, 0xf6, 0xd8, 0xc8, 0x94, 0xff, 0xa0, 0x03, 0x84, 0x03, 0x0f, 0xa5,
438  0x30, 0x5d, 0x67, 0xe0, 0x7a, 0x3b, 0x5f, 0xed, 0x4c, 0xf5, 0xbc, 0x18,
439  0xea, 0xd4, 0x77, 0xa9, 0x07, 0xb3, 0x54, 0x0b, 0x02, 0xd9, 0xc6, 0xb8,
440  0x66, 0x5e, 0xec, 0xa4, 0xcd, 0x47, 0xed, 0xc9, 0x38, 0xc6, 0x91, 0x08,
441  0xf3, 0x85, 0x9b, 0x69, 0x16, 0x78, 0x0d, 0xb7, 0x74, 0x51, 0xaa, 0x5b,
442  0x4d, 0x74, 0xe4, 0x29, 0x2e, 0x9e, 0x8e, 0xf7, 0xe5, 0x42, 0x83, 0xb0,
443  0x65, 0xb0, 0xce, 0xc6, 0xb2, 0x8f, 0x5b, 0xb0
444 };
445 
446 static int test_ref_data(void)
447 {
448  FF_DH *dh;
449  int ret = AVERROR(ENOMEM);
450  uint8_t pubkey_test[128];
451  uint8_t sharedkey_test[128];
452 
453  dh = ff_dh_init(1024);
454  if (!dh)
455  goto fail;
456  bn_hex2bn(dh->priv_key, private_key, ret);
457  if (!ret)
458  goto fail;
459  bn_hex2bn(dh->pub_key, public_key, ret);
460  if (!ret)
461  goto fail;
462  if ((ret = ff_dh_write_public_key(dh, pubkey_test, sizeof(pubkey_test))) < 0)
463  goto fail;
464  if (memcmp(pubkey_test, public_key_bin, sizeof(pubkey_test))) {
465  printf("Mismatched generated public key\n");
466  ret = AVERROR_INVALIDDATA;
467  goto fail;
468  } else {
469  printf("Generated public key ok\n");
470  }
471  if ((ret = ff_dh_compute_shared_secret_key(dh, peer_public_key, sizeof(peer_public_key),
472  sharedkey_test, sizeof(sharedkey_test))) < 0)
473  goto fail;
474  if (memcmp(shared_secret, sharedkey_test, sizeof(sharedkey_test))) {
475  printf("Mismatched generated shared key\n");
476  ret = AVERROR_INVALIDDATA;
477  } else {
478  printf("Generated shared key ok\n");
479  }
480 fail:
481  ff_dh_free(dh);
482  return ret;
483 }
484 
485 int main(void)
486 {
487  if (test_random_shared_secret() < 0)
488  return 1;
489  if (test_ref_data() < 0)
490  return 1;
491  return 0;
492 }
493 #endif
uint32_t p[AV_BF_ROUNDS+2]
Definition: blowfish.h:37
#define NULL
Definition: coverity.c:32
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
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:305
static int dh_is_valid_public_key(FFBigNum y, FFBigNum p, FFBigNum q)
Definition: rtmpdh.c:213
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's public value...
Definition: rtmpdh.c:321
#define P1024
Definition: rtmpdh.c:33
static const uint8_t q1[256]
Definition: twofish.c:96
uint8_t
#define av_cold
Definition: attributes.h:74
int ff_dh_generate_public_key(FF_DH *dh)
Generate a public key.
Definition: rtmpdh.c:279
#define AVERROR(e)
Definition: error.h:43
#define fail()
Definition: checkasm.h:57
float y
void ff_dh_free(FF_DH *dh)
Free a Diffie-Hellmann context.
#define Q1024
Definition: rtmpdh.c:41
av_cold FF_DH * ff_dh_init(int key_len)
Initialize a Diffie-Hellmann context.
Definition: rtmpdh.c:252
#define av_free(p)
int len
int main(int argc, char **argv)
Definition: main.c:22