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  gcry_control(GCRYCTL_DISABLE_SECMEM, 0); \
121  gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); \
122  } \
123  } \
124  if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) \
125  bn = gcry_mpi_new(1); \
126  else \
127  bn = NULL; \
128  } while (0)
129 #define bn_free(bn) gcry_mpi_release(bn)
130 #define bn_set_word(bn, w) gcry_mpi_set_ui(bn, w)
131 #define bn_cmp(a, b) gcry_mpi_cmp(a, b)
132 #define bn_copy(to, from) gcry_mpi_set(to, from)
133 #define bn_sub_word(bn, w) gcry_mpi_sub_ui(bn, bn, w)
134 #define bn_cmp_1(bn) gcry_mpi_cmp_ui(bn, 1)
135 #define bn_num_bytes(bn) (gcry_mpi_get_nbits(bn) + 7) / 8
136 #define bn_bn2bin(bn, buf, len) gcry_mpi_print(GCRYMPI_FMT_USG, buf, len, NULL, bn)
137 #define bn_bin2bn(bn, buf, len) gcry_mpi_scan(&bn, GCRYMPI_FMT_USG, buf, len, NULL)
138 #define bn_hex2bn(bn, buf, ret) ret = (gcry_mpi_scan(&bn, GCRYMPI_FMT_HEX, buf, 0, 0) == 0)
139 #define bn_random(bn, num_bits) gcry_mpi_randomize(bn, num_bits, GCRY_WEAK_RANDOM)
140 static int bn_modexp(FFBigNum bn, FFBigNum y, FFBigNum q, FFBigNum p)
141 {
142  gcry_mpi_powm(bn, y, q, p);
143  return 0;
144 }
145 #elif CONFIG_OPENSSL
146 #define bn_new(bn) bn = BN_new()
147 #define bn_free(bn) BN_free(bn)
148 #define bn_set_word(bn, w) BN_set_word(bn, w)
149 #define bn_cmp(a, b) BN_cmp(a, b)
150 #define bn_copy(to, from) BN_copy(to, from)
151 #define bn_sub_word(bn, w) BN_sub_word(bn, w)
152 #define bn_cmp_1(bn) BN_cmp(bn, BN_value_one())
153 #define bn_num_bytes(bn) BN_num_bytes(bn)
154 #define bn_bn2bin(bn, buf, len) BN_bn2bin(bn, buf)
155 #define bn_bin2bn(bn, buf, len) bn = BN_bin2bn(buf, len, 0)
156 #define bn_hex2bn(bn, buf, ret) ret = BN_hex2bn(&bn, buf)
157 #define bn_random(bn, num_bits) BN_rand(bn, num_bits, 0, 0)
158 static int bn_modexp(FFBigNum bn, FFBigNum y, FFBigNum q, FFBigNum p)
159 {
160  BN_CTX *ctx = BN_CTX_new();
161  if (!ctx)
162  return AVERROR(ENOMEM);
163  if (!BN_mod_exp(bn, y, q, p, ctx)) {
164  BN_CTX_free(ctx);
165  return AVERROR(EINVAL);
166  }
167  BN_CTX_free(ctx);
168  return 0;
169 }
170 #elif CONFIG_MBEDTLS
171 #define bn_new(bn) \
172  do { \
173  bn = av_malloc(sizeof(*bn)); \
174  if (bn) \
175  mbedtls_mpi_init(bn); \
176  } while (0)
177 #define bn_free(bn) \
178  do { \
179  mbedtls_mpi_free(bn); \
180  av_free(bn); \
181  } while (0)
182 #define bn_set_word(bn, w) mbedtls_mpi_lset(bn, w)
183 #define bn_cmp(a, b) mbedtls_mpi_cmp_mpi(a, b)
184 #define bn_copy(to, from) mbedtls_mpi_copy(to, from)
185 #define bn_sub_word(bn, w) mbedtls_mpi_sub_int(bn, bn, w)
186 #define bn_cmp_1(bn) mbedtls_mpi_cmp_int(bn, 1)
187 #define bn_num_bytes(bn) (mbedtls_mpi_bitlen(bn) + 7) / 8
188 #define bn_bn2bin(bn, buf, len) mbedtls_mpi_write_binary(bn, buf, len)
189 #define bn_bin2bn(bn, buf, len) \
190  do { \
191  bn_new(bn); \
192  if (bn) \
193  mbedtls_mpi_read_binary(bn, buf, len); \
194  } while (0)
195 #define bn_hex2bn(bn, buf, ret) \
196  do { \
197  bn_new(bn); \
198  if (bn) \
199  ret = (mbedtls_mpi_read_string(bn, 16, buf) == 0); \
200  else \
201  ret = 1; \
202  } while (0)
203 #define bn_random(bn, num_bits) \
204  do { \
205  mbedtls_entropy_context entropy_ctx; \
206  mbedtls_ctr_drbg_context ctr_drbg_ctx; \
207  \
208  mbedtls_entropy_init(&entropy_ctx); \
209  mbedtls_ctr_drbg_init(&ctr_drbg_ctx); \
210  mbedtls_ctr_drbg_seed(&ctr_drbg_ctx, \
211  mbedtls_entropy_func, \
212  &entropy_ctx, \
213  NULL, 0); \
214  mbedtls_mpi_fill_random(bn, (num_bits + 7) / 8, mbedtls_ctr_drbg_random, &ctr_drbg_ctx); \
215  mbedtls_ctr_drbg_free(&ctr_drbg_ctx); \
216  mbedtls_entropy_free(&entropy_ctx); \
217  } while (0)
218 #define bn_modexp(bn, y, q, p) mbedtls_mpi_exp_mod(bn, y, q, p, 0)
219 
220 #endif
221 
222 #define MAX_BYTES 18000
223 
224 #define dh_new() av_mallocz(sizeof(FF_DH))
225 
226 static FFBigNum dh_generate_key(FF_DH *dh)
227 {
228  int num_bytes;
229 
230  num_bytes = bn_num_bytes(dh->p) - 1;
231  if (num_bytes <= 0 || num_bytes > MAX_BYTES)
232  return NULL;
233 
234  bn_new(dh->priv_key);
235  if (!dh->priv_key)
236  return NULL;
237  bn_random(dh->priv_key, 8 * num_bytes);
238 
239  bn_new(dh->pub_key);
240  if (!dh->pub_key) {
241  bn_free(dh->priv_key);
242  return NULL;
243  }
244 
245  if (bn_modexp(dh->pub_key, dh->g, dh->priv_key, dh->p) < 0)
246  return NULL;
247 
248  return dh->pub_key;
249 }
250 
251 static int dh_compute_key(FF_DH *dh, FFBigNum pub_key_bn,
252  uint32_t secret_key_len, uint8_t *secret_key)
253 {
254  FFBigNum k;
255  int ret;
256 
257  bn_new(k);
258  if (!k)
259  return -1;
260 
261  if ((ret = bn_modexp(k, pub_key_bn, dh->priv_key, dh->p)) < 0) {
262  bn_free(k);
263  return ret;
264  }
265  bn_bn2bin(k, secret_key, secret_key_len);
266  bn_free(k);
267 
268  /* return the length of the shared secret key like DH_compute_key */
269  return secret_key_len;
270 }
271 
272 void ff_dh_free(FF_DH *dh)
273 {
274  if (!dh)
275  return;
276  bn_free(dh->p);
277  bn_free(dh->g);
278  bn_free(dh->pub_key);
279  bn_free(dh->priv_key);
280  av_free(dh);
281 }
282 
283 static int dh_is_valid_public_key(FFBigNum y, FFBigNum p, FFBigNum q)
284 {
285  FFBigNum bn = NULL;
286  int ret = AVERROR(EINVAL);
287 
288  bn_new(bn);
289  if (!bn)
290  return AVERROR(ENOMEM);
291 
292  /* y must lie in [2, p - 1] */
293  bn_set_word(bn, 1);
294  if (!bn_cmp(y, bn))
295  goto fail;
296 
297  /* bn = p - 2 */
298  bn_copy(bn, p);
299  bn_sub_word(bn, 1);
300  if (!bn_cmp(y, bn))
301  goto fail;
302 
303  /* Verify with Sophie-Germain prime
304  *
305  * This is a nice test to make sure the public key position is calculated
306  * correctly. This test will fail in about 50% of the cases if applied to
307  * random data.
308  */
309  /* y must fulfill y^q mod p = 1 */
310  if ((ret = bn_modexp(bn, y, q, p)) < 0)
311  goto fail;
312 
313  ret = AVERROR(EINVAL);
314  if (bn_cmp_1(bn))
315  goto fail;
316 
317  ret = 0;
318 fail:
319  bn_free(bn);
320 
321  return ret;
322 }
323 
324 av_cold FF_DH *ff_dh_init(int key_len)
325 {
326  FF_DH *dh;
327  int ret;
328 
329  if (!(dh = dh_new()))
330  return NULL;
331 
332  bn_new(dh->g);
333  if (!dh->g)
334  goto fail;
335 
336  bn_hex2bn(dh->p, P1024, ret);
337  if (!ret)
338  goto fail;
339 
340  bn_set_word(dh->g, 2);
341  dh->length = key_len;
342 
343  return dh;
344 
345 fail:
346  ff_dh_free(dh);
347 
348  return NULL;
349 }
350 
352 {
353  int ret = 0;
354 
355  while (!ret) {
356  FFBigNum q1 = NULL;
357 
358  if (!dh_generate_key(dh))
359  return AVERROR(EINVAL);
360 
361  bn_hex2bn(q1, Q1024, ret);
362  if (!ret)
363  return AVERROR(ENOMEM);
364 
365  ret = dh_is_valid_public_key(dh->pub_key, dh->p, q1);
366  bn_free(q1);
367 
368  if (!ret) {
369  /* the public key is valid */
370  break;
371  }
372  }
373 
374  return ret;
375 }
376 
377 int ff_dh_write_public_key(FF_DH *dh, uint8_t *pub_key, int pub_key_len)
378 {
379  int len;
380 
381  /* compute the length of the public key */
382  len = bn_num_bytes(dh->pub_key);
383  if (len <= 0 || len > pub_key_len)
384  return AVERROR(EINVAL);
385 
386  /* convert the public key value into big-endian form */
387  memset(pub_key, 0, pub_key_len);
388  bn_bn2bin(dh->pub_key, pub_key + pub_key_len - len, len);
389 
390  return 0;
391 }
392 
393 int ff_dh_compute_shared_secret_key(FF_DH *dh, const uint8_t *pub_key,
394  int pub_key_len, uint8_t *secret_key,
395  int secret_key_len)
396 {
397  FFBigNum q1 = NULL, pub_key_bn = NULL;
398  int ret;
399 
400  /* convert the big-endian form of the public key into a bignum */
401  bn_bin2bn(pub_key_bn, pub_key, pub_key_len);
402  if (!pub_key_bn)
403  return AVERROR(ENOMEM);
404 
405  /* convert the string containing a hexadecimal number into a bignum */
406  bn_hex2bn(q1, Q1024, ret);
407  if (!ret) {
408  ret = AVERROR(ENOMEM);
409  goto fail;
410  }
411 
412  /* when the public key is valid we have to compute the shared secret key */
413  if ((ret = dh_is_valid_public_key(pub_key_bn, dh->p, q1)) < 0) {
414  goto fail;
415  } else if ((ret = dh_compute_key(dh, pub_key_bn, secret_key_len,
416  secret_key)) < 0) {
417  ret = AVERROR(EINVAL);
418  goto fail;
419  }
420 
421 fail:
422  bn_free(pub_key_bn);
423  bn_free(q1);
424 
425  return ret;
426 }
q1
static const uint8_t q1[256]
Definition: twofish.c:100
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
ff_dh_compute_shared_secret_key
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:393
AVBlowfish::p
uint32_t p[AV_BF_ROUNDS+2]
Definition: blowfish.h:36
dh_generate_key
static FFBigNum dh_generate_key(FF_DH *dh)
Definition: rtmpdh.c:226
FF_DH::length
long length
Definition: rtmpdh.h:55
fail
#define fail()
Definition: checkasm.h:193
av_cold
#define av_cold
Definition: attributes.h:90
ctx
AVFormatContext * ctx
Definition: movenc.c:49
P1024
#define P1024
Definition: rtmpdh.c:46
dh_is_valid_public_key
static int dh_is_valid_public_key(FFBigNum y, FFBigNum p, FFBigNum q)
Definition: rtmpdh.c:283
NULL
#define NULL
Definition: coverity.c:32
FF_DH::pub_key
FFBigNum pub_key
Definition: rtmpdh.h:53
error.h
dh_new
#define dh_new()
Definition: rtmpdh.c:224
FF_DH::priv_key
FFBigNum priv_key
Definition: rtmpdh.h:54
MAX_BYTES
#define MAX_BYTES
Definition: rtmpdh.c:222
FF_DH::g
FFBigNum g
Definition: rtmpdh.h:52
FF_DH::p
FFBigNum p
Definition: rtmpdh.h:51
attributes.h
dh_compute_key
static int dh_compute_key(FF_DH *dh, FFBigNum pub_key_bn, uint32_t secret_key_len, uint8_t *secret_key)
Definition: rtmpdh.c:251
len
int len
Definition: vorbis_enc_data.h:426
ret
ret
Definition: filter_design.txt:187
ff_dh_write_public_key
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:377
FF_DH
Definition: rtmpdh.h:50
rtmpdh.h
random_seed.h
ff_dh_free
void ff_dh_free(FF_DH *dh)
Free a Diffie-Hellmann context.
Definition: rtmpdh.c:272
mem.h
ff_dh_generate_public_key
int ff_dh_generate_public_key(FF_DH *dh)
Generate a public key.
Definition: rtmpdh.c:351
av_free
#define av_free(p)
Definition: tableprint_vlc.h:33
ff_dh_init
av_cold FF_DH * ff_dh_init(int key_len)
Initialize a Diffie-Hellmann context.
Definition: rtmpdh.c:324
Q1024
#define Q1024
Definition: rtmpdh.c:54