Libav
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 Libav.
8  *
9  * Libav 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  * Libav 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 Libav; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  */
23 
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 #define P1024 \
42  "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
43  "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
44  "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
45  "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
46  "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \
47  "FFFFFFFFFFFFFFFF"
48 
49 #define Q1024 \
50  "7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68" \
51  "948127044533E63A0105DF531D89CD9128A5043CC71A026E" \
52  "F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122" \
53  "F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6" \
54  "F71C35FDAD44CFD2D74F9208BE258FF324943328F67329C0" \
55  "FFFFFFFFFFFFFFFF"
56 
57 #if CONFIG_GMP
58 #define bn_new(bn) \
59  do { \
60  bn = av_malloc(sizeof(*bn)); \
61  if (bn) \
62  mpz_init2(bn, 1); \
63  } while (0)
64 #define bn_free(bn) \
65  do { \
66  mpz_clear(bn); \
67  av_free(bn); \
68  } while (0)
69 #define bn_set_word(bn, w) mpz_set_ui(bn, w)
70 #define bn_cmp(a, b) mpz_cmp(a, b)
71 #define bn_copy(to, from) mpz_set(to, from)
72 #define bn_sub_word(bn, w) mpz_sub_ui(bn, bn, w)
73 #define bn_cmp_1(bn) mpz_cmp_ui(bn, 1)
74 #define bn_num_bytes(bn) (mpz_sizeinbase(bn, 2) + 7) / 8
75 #define bn_bn2bin(bn, buf, len) \
76  do { \
77  memset(buf, 0, len); \
78  if (bn_num_bytes(bn) <= len) \
79  mpz_export(buf, NULL, 1, 1, 0, 0, bn); \
80  } while (0)
81 #define bn_bin2bn(bn, buf, len) \
82  do { \
83  bn_new(bn); \
84  if (bn) \
85  mpz_import(bn, len, 1, 1, 0, 0, buf); \
86  } while (0)
87 #define bn_hex2bn(bn, buf, ret) \
88  do { \
89  bn_new(bn); \
90  if (bn) \
91  ret = (mpz_set_str(bn, buf, 16) == 0); \
92  else \
93  ret = 1; \
94  } while (0)
95 #define bn_random(bn, num_bits) \
96  do { \
97  int bits = num_bits; \
98  mpz_set_ui(bn, 0); \
99  for (bits = num_bits; bits > 0; bits -= 32) { \
100  mpz_mul_2exp(bn, bn, 32); \
101  mpz_add_ui(bn, bn, av_get_random_seed()); \
102  } \
103  mpz_fdiv_r_2exp(bn, bn, num_bits); \
104  } while (0)
105 static int bn_modexp(FFBigNum bn, FFBigNum y, FFBigNum q, FFBigNum p)
106 {
107  mpz_powm(bn, y, q, p);
108  return 0;
109 }
110 #elif CONFIG_GCRYPT
111 #define bn_new(bn) bn = gcry_mpi_new(1)
112 #define bn_free(bn) gcry_mpi_release(bn)
113 #define bn_set_word(bn, w) gcry_mpi_set_ui(bn, w)
114 #define bn_cmp(a, b) gcry_mpi_cmp(a, b)
115 #define bn_copy(to, from) gcry_mpi_set(to, from)
116 #define bn_sub_word(bn, w) gcry_mpi_sub_ui(bn, bn, w)
117 #define bn_cmp_1(bn) gcry_mpi_cmp_ui(bn, 1)
118 #define bn_num_bytes(bn) (gcry_mpi_get_nbits(bn) + 7) / 8
119 #define bn_bn2bin(bn, buf, len) gcry_mpi_print(GCRYMPI_FMT_USG, buf, len, NULL, bn)
120 #define bn_bin2bn(bn, buf, len) gcry_mpi_scan(&bn, GCRYMPI_FMT_USG, buf, len, NULL)
121 #define bn_hex2bn(bn, buf, ret) ret = (gcry_mpi_scan(&bn, GCRYMPI_FMT_HEX, buf, 0, 0) == 0)
122 #define bn_random(bn, num_bits) gcry_mpi_randomize(bn, num_bits, GCRY_WEAK_RANDOM)
123 static int bn_modexp(FFBigNum bn, FFBigNum y, FFBigNum q, FFBigNum p)
124 {
125  gcry_mpi_powm(bn, y, q, p);
126  return 0;
127 }
128 #elif CONFIG_OPENSSL
129 #define bn_new(bn) bn = BN_new()
130 #define bn_free(bn) BN_free(bn)
131 #define bn_set_word(bn, w) BN_set_word(bn, w)
132 #define bn_cmp(a, b) BN_cmp(a, b)
133 #define bn_copy(to, from) BN_copy(to, from)
134 #define bn_sub_word(bn, w) BN_sub_word(bn, w)
135 #define bn_cmp_1(bn) BN_cmp(bn, BN_value_one())
136 #define bn_num_bytes(bn) BN_num_bytes(bn)
137 #define bn_bn2bin(bn, buf, len) BN_bn2bin(bn, buf)
138 #define bn_bin2bn(bn, buf, len) bn = BN_bin2bn(buf, len, 0)
139 #define bn_hex2bn(bn, buf, ret) ret = BN_hex2bn(&bn, buf)
140 #define bn_random(bn, num_bits) BN_rand(bn, num_bits, 0, 0)
141 static int bn_modexp(FFBigNum bn, FFBigNum y, FFBigNum q, FFBigNum p)
142 {
143  BN_CTX *ctx = BN_CTX_new();
144  if (!ctx)
145  return AVERROR(ENOMEM);
146  if (!BN_mod_exp(bn, y, q, p, ctx)) {
147  BN_CTX_free(ctx);
148  return AVERROR(EINVAL);
149  }
150  BN_CTX_free(ctx);
151  return 0;
152 }
153 #endif
154 
155 #define MAX_BYTES 18000
156 
157 #define dh_new() av_mallocz(sizeof(FF_DH))
158 
159 static FFBigNum dh_generate_key(FF_DH *dh)
160 {
161  int num_bytes;
162 
163  num_bytes = bn_num_bytes(dh->p) - 1;
164  if (num_bytes <= 0 || num_bytes > MAX_BYTES)
165  return NULL;
166 
167  bn_new(dh->priv_key);
168  if (!dh->priv_key)
169  return NULL;
170  bn_random(dh->priv_key, 8 * num_bytes);
171 
172  bn_new(dh->pub_key);
173  if (!dh->pub_key) {
174  bn_free(dh->priv_key);
175  return NULL;
176  }
177 
178  if (bn_modexp(dh->pub_key, dh->g, dh->priv_key, dh->p) < 0)
179  return NULL;
180 
181  return dh->pub_key;
182 }
183 
184 static int dh_compute_key(FF_DH *dh, FFBigNum pub_key_bn,
185  uint32_t secret_key_len, uint8_t *secret_key)
186 {
187  FFBigNum k;
188  int ret;
189 
190  bn_new(k);
191  if (!k)
192  return -1;
193 
194  if ((ret = bn_modexp(k, pub_key_bn, dh->priv_key, dh->p)) < 0) {
195  bn_free(k);
196  return ret;
197  }
198  bn_bn2bin(k, secret_key, secret_key_len);
199  bn_free(k);
200 
201  /* return the length of the shared secret key like DH_compute_key */
202  return secret_key_len;
203 }
204 
205 void ff_dh_free(FF_DH *dh)
206 {
207  if (!dh)
208  return;
209  bn_free(dh->p);
210  bn_free(dh->g);
211  bn_free(dh->pub_key);
212  bn_free(dh->priv_key);
213  av_free(dh);
214 }
215 
216 static int dh_is_valid_public_key(FFBigNum y, FFBigNum p, FFBigNum q)
217 {
218  FFBigNum bn = NULL;
219  int ret = AVERROR(EINVAL);
220 
221  bn_new(bn);
222  if (!bn)
223  return AVERROR(ENOMEM);
224 
225  /* y must lie in [2, p - 1] */
226  bn_set_word(bn, 1);
227  if (!bn_cmp(y, bn))
228  goto fail;
229 
230  /* bn = p - 2 */
231  bn_copy(bn, p);
232  bn_sub_word(bn, 1);
233  if (!bn_cmp(y, bn))
234  goto fail;
235 
236  /* Verify with Sophie-Germain prime
237  *
238  * This is a nice test to make sure the public key position is calculated
239  * correctly. This test will fail in about 50% of the cases if applied to
240  * random data.
241  */
242  /* y must fulfill y^q mod p = 1 */
243  if ((ret = bn_modexp(bn, y, q, p)) < 0)
244  goto fail;
245 
246  ret = AVERROR(EINVAL);
247  if (bn_cmp_1(bn))
248  goto fail;
249 
250  ret = 0;
251 fail:
252  bn_free(bn);
253 
254  return ret;
255 }
256 
257 av_cold FF_DH *ff_dh_init(int key_len)
258 {
259  FF_DH *dh;
260  int ret;
261 
262  if (!(dh = dh_new()))
263  return NULL;
264 
265  bn_new(dh->g);
266  if (!dh->g)
267  goto fail;
268 
269  bn_hex2bn(dh->p, P1024, ret);
270  if (!ret)
271  goto fail;
272 
273  bn_set_word(dh->g, 2);
274  dh->length = key_len;
275 
276  return dh;
277 
278 fail:
279  ff_dh_free(dh);
280 
281  return NULL;
282 }
283 
285 {
286  int ret = 0;
287 
288  while (!ret) {
289  FFBigNum q1 = NULL;
290 
291  if (!dh_generate_key(dh))
292  return AVERROR(EINVAL);
293 
294  bn_hex2bn(q1, Q1024, ret);
295  if (!ret)
296  return AVERROR(ENOMEM);
297 
298  ret = dh_is_valid_public_key(dh->pub_key, dh->p, q1);
299  bn_free(q1);
300 
301  if (!ret) {
302  /* the public key is valid */
303  break;
304  }
305  }
306 
307  return ret;
308 }
309 
310 int ff_dh_write_public_key(FF_DH *dh, uint8_t *pub_key, int pub_key_len)
311 {
312  int len;
313 
314  /* compute the length of the public key */
315  len = bn_num_bytes(dh->pub_key);
316  if (len <= 0 || len > pub_key_len)
317  return AVERROR(EINVAL);
318 
319  /* convert the public key value into big-endian form */
320  memset(pub_key, 0, pub_key_len);
321  bn_bn2bin(dh->pub_key, pub_key + pub_key_len - len, len);
322 
323  return 0;
324 }
325 
327  int pub_key_len, uint8_t *secret_key,
328  int secret_key_len)
329 {
330  FFBigNum q1 = NULL, pub_key_bn = NULL;
331  int ret;
332 
333  /* convert the big-endian form of the public key into a bignum */
334  bn_bin2bn(pub_key_bn, pub_key, pub_key_len);
335  if (!pub_key_bn)
336  return AVERROR(ENOMEM);
337 
338  /* convert the string containing a hexadecimal number into a bignum */
339  bn_hex2bn(q1, Q1024, ret);
340  if (!ret) {
341  ret = AVERROR(ENOMEM);
342  goto fail;
343  }
344 
345  /* when the public key is valid we have to compute the shared secret key */
346  if ((ret = dh_is_valid_public_key(pub_key_bn, dh->p, q1)) < 0) {
347  goto fail;
348  } else if ((ret = dh_compute_key(dh, pub_key_bn, secret_key_len,
349  secret_key)) < 0) {
350  ret = AVERROR(EINVAL);
351  goto fail;
352  }
353 
354 fail:
355  bn_free(pub_key_bn);
356  bn_free(q1);
357 
358  return ret;
359 }
uint32_t p[AV_BF_ROUNDS+2]
Definition: blowfish.h:37
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:310
Definition: rtmpdh.h:45
static int dh_is_valid_public_key(FFBigNum y, FFBigNum p, FFBigNum q)
Definition: rtmpdh.c:216
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:326
void ff_dh_free(FF_DH *dh)
Free a Diffie-Hellmann context.
Definition: rtmpdh.c:205
#define P1024
Definition: rtmpdh.c:41
FFBigNum priv_key
Definition: rtmpdh.h:49
Macro definitions for various function/variable attributes.
uint8_t
#define av_cold
Definition: attributes.h:66
int ff_dh_generate_public_key(FF_DH *dh)
Generate a public key.
Definition: rtmpdh.c:284
void av_free(void *ptr)
Free a memory block which has been allocated with av_malloc(z)() or av_realloc(). ...
Definition: mem.c:190
error code definitions
FFBigNum p
Definition: rtmpdh.h:46
#define AVERROR(e)
Definition: error.h:43
#define fail()
Definition: checkasm.h:80
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:184
FFBigNum g
Definition: rtmpdh.h:47
NULL
Definition: eval.c:55
#define dh_new()
Definition: rtmpdh.c:157
#define Q1024
Definition: rtmpdh.c:49
#define MAX_BYTES
Definition: rtmpdh.c:155
long length
Definition: rtmpdh.h:50
av_cold FF_DH * ff_dh_init(int key_len)
Initialize a Diffie-Hellmann context.
Definition: rtmpdh.c:257
FFBigNum pub_key
Definition: rtmpdh.h:48
int len
static FFBigNum dh_generate_key(FF_DH *dh)
Definition: rtmpdh.c:159