blob: 8eb088237b72aba763bb5322a2e9d415b84f3721
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 | #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) \ |
112 | do { \ |
113 | if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) { \ |
114 | if (!gcry_check_version("1.5.4")) \ |
115 | return AVERROR(EINVAL); \ |
116 | gcry_control(GCRYCTL_DISABLE_SECMEM, 0); \ |
117 | gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); \ |
118 | } \ |
119 | bn = gcry_mpi_new(1); \ |
120 | } while (0) |
121 | #define bn_free(bn) gcry_mpi_release(bn) |
122 | #define bn_set_word(bn, w) gcry_mpi_set_ui(bn, w) |
123 | #define bn_cmp(a, b) gcry_mpi_cmp(a, b) |
124 | #define bn_copy(to, from) gcry_mpi_set(to, from) |
125 | #define bn_sub_word(bn, w) gcry_mpi_sub_ui(bn, bn, w) |
126 | #define bn_cmp_1(bn) gcry_mpi_cmp_ui(bn, 1) |
127 | #define bn_num_bytes(bn) (gcry_mpi_get_nbits(bn) + 7) / 8 |
128 | #define bn_bn2bin(bn, buf, len) gcry_mpi_print(GCRYMPI_FMT_USG, buf, len, NULL, bn) |
129 | #define bn_bin2bn(bn, buf, len) gcry_mpi_scan(&bn, GCRYMPI_FMT_USG, buf, len, NULL) |
130 | #define bn_hex2bn(bn, buf, ret) ret = (gcry_mpi_scan(&bn, GCRYMPI_FMT_HEX, buf, 0, 0) == 0) |
131 | #define bn_random(bn, num_bits) gcry_mpi_randomize(bn, num_bits, GCRY_WEAK_RANDOM) |
132 | static int bn_modexp(FFBigNum bn, FFBigNum y, FFBigNum q, FFBigNum p) |
133 | { |
134 | gcry_mpi_powm(bn, y, q, p); |
135 | return 0; |
136 | } |
137 | #elif CONFIG_OPENSSL |
138 | #define bn_new(bn) bn = BN_new() |
139 | #define bn_free(bn) BN_free(bn) |
140 | #define bn_set_word(bn, w) BN_set_word(bn, w) |
141 | #define bn_cmp(a, b) BN_cmp(a, b) |
142 | #define bn_copy(to, from) BN_copy(to, from) |
143 | #define bn_sub_word(bn, w) BN_sub_word(bn, w) |
144 | #define bn_cmp_1(bn) BN_cmp(bn, BN_value_one()) |
145 | #define bn_num_bytes(bn) BN_num_bytes(bn) |
146 | #define bn_bn2bin(bn, buf, len) BN_bn2bin(bn, buf) |
147 | #define bn_bin2bn(bn, buf, len) bn = BN_bin2bn(buf, len, 0) |
148 | #define bn_hex2bn(bn, buf, ret) ret = BN_hex2bn(&bn, buf) |
149 | #define bn_random(bn, num_bits) BN_rand(bn, num_bits, 0, 0) |
150 | static int bn_modexp(FFBigNum bn, FFBigNum y, FFBigNum q, FFBigNum p) |
151 | { |
152 | BN_CTX *ctx = BN_CTX_new(); |
153 | if (!ctx) |
154 | return AVERROR(ENOMEM); |
155 | if (!BN_mod_exp(bn, y, q, p, ctx)) { |
156 | BN_CTX_free(ctx); |
157 | return AVERROR(EINVAL); |
158 | } |
159 | BN_CTX_free(ctx); |
160 | return 0; |
161 | } |
162 | #endif |
163 | |
164 | #define MAX_BYTES 18000 |
165 | |
166 | #define dh_new() av_mallocz(sizeof(FF_DH)) |
167 | |
168 | static FFBigNum dh_generate_key(FF_DH *dh) |
169 | { |
170 | int num_bytes; |
171 | |
172 | num_bytes = bn_num_bytes(dh->p) - 1; |
173 | if (num_bytes <= 0 || num_bytes > MAX_BYTES) |
174 | return NULL; |
175 | |
176 | bn_new(dh->priv_key); |
177 | if (!dh->priv_key) |
178 | return NULL; |
179 | bn_random(dh->priv_key, 8 * num_bytes); |
180 | |
181 | bn_new(dh->pub_key); |
182 | if (!dh->pub_key) { |
183 | bn_free(dh->priv_key); |
184 | return NULL; |
185 | } |
186 | |
187 | if (bn_modexp(dh->pub_key, dh->g, dh->priv_key, dh->p) < 0) |
188 | return NULL; |
189 | |
190 | return dh->pub_key; |
191 | } |
192 | |
193 | static int dh_compute_key(FF_DH *dh, FFBigNum pub_key_bn, |
194 | uint32_t secret_key_len, uint8_t *secret_key) |
195 | { |
196 | FFBigNum k; |
197 | int ret; |
198 | |
199 | bn_new(k); |
200 | if (!k) |
201 | return -1; |
202 | |
203 | if ((ret = bn_modexp(k, pub_key_bn, dh->priv_key, dh->p)) < 0) { |
204 | bn_free(k); |
205 | return ret; |
206 | } |
207 | bn_bn2bin(k, secret_key, secret_key_len); |
208 | bn_free(k); |
209 | |
210 | /* return the length of the shared secret key like DH_compute_key */ |
211 | return secret_key_len; |
212 | } |
213 | |
214 | void ff_dh_free(FF_DH *dh) |
215 | { |
216 | if (!dh) |
217 | return; |
218 | bn_free(dh->p); |
219 | bn_free(dh->g); |
220 | bn_free(dh->pub_key); |
221 | bn_free(dh->priv_key); |
222 | av_free(dh); |
223 | } |
224 | |
225 | static int dh_is_valid_public_key(FFBigNum y, FFBigNum p, FFBigNum q) |
226 | { |
227 | FFBigNum bn = NULL; |
228 | int ret = AVERROR(EINVAL); |
229 | |
230 | bn_new(bn); |
231 | if (!bn) |
232 | return AVERROR(ENOMEM); |
233 | |
234 | /* y must lie in [2, p - 1] */ |
235 | bn_set_word(bn, 1); |
236 | if (!bn_cmp(y, bn)) |
237 | goto fail; |
238 | |
239 | /* bn = p - 2 */ |
240 | bn_copy(bn, p); |
241 | bn_sub_word(bn, 1); |
242 | if (!bn_cmp(y, bn)) |
243 | goto fail; |
244 | |
245 | /* Verify with Sophie-Germain prime |
246 | * |
247 | * This is a nice test to make sure the public key position is calculated |
248 | * correctly. This test will fail in about 50% of the cases if applied to |
249 | * random data. |
250 | */ |
251 | /* y must fulfill y^q mod p = 1 */ |
252 | if ((ret = bn_modexp(bn, y, q, p)) < 0) |
253 | goto fail; |
254 | |
255 | ret = AVERROR(EINVAL); |
256 | if (bn_cmp_1(bn)) |
257 | goto fail; |
258 | |
259 | ret = 0; |
260 | fail: |
261 | bn_free(bn); |
262 | |
263 | return ret; |
264 | } |
265 | |
266 | av_cold FF_DH *ff_dh_init(int key_len) |
267 | { |
268 | FF_DH *dh; |
269 | int ret; |
270 | |
271 | if (!(dh = dh_new())) |
272 | return NULL; |
273 | |
274 | bn_new(dh->g); |
275 | if (!dh->g) |
276 | goto fail; |
277 | |
278 | bn_hex2bn(dh->p, P1024, ret); |
279 | if (!ret) |
280 | goto fail; |
281 | |
282 | bn_set_word(dh->g, 2); |
283 | dh->length = key_len; |
284 | |
285 | return dh; |
286 | |
287 | fail: |
288 | ff_dh_free(dh); |
289 | |
290 | return NULL; |
291 | } |
292 | |
293 | int ff_dh_generate_public_key(FF_DH *dh) |
294 | { |
295 | int ret = 0; |
296 | |
297 | while (!ret) { |
298 | FFBigNum q1 = NULL; |
299 | |
300 | if (!dh_generate_key(dh)) |
301 | return AVERROR(EINVAL); |
302 | |
303 | bn_hex2bn(q1, Q1024, ret); |
304 | if (!ret) |
305 | return AVERROR(ENOMEM); |
306 | |
307 | ret = dh_is_valid_public_key(dh->pub_key, dh->p, q1); |
308 | bn_free(q1); |
309 | |
310 | if (!ret) { |
311 | /* the public key is valid */ |
312 | break; |
313 | } |
314 | } |
315 | |
316 | return ret; |
317 | } |
318 | |
319 | int ff_dh_write_public_key(FF_DH *dh, uint8_t *pub_key, int pub_key_len) |
320 | { |
321 | int len; |
322 | |
323 | /* compute the length of the public key */ |
324 | len = bn_num_bytes(dh->pub_key); |
325 | if (len <= 0 || len > pub_key_len) |
326 | return AVERROR(EINVAL); |
327 | |
328 | /* convert the public key value into big-endian form */ |
329 | memset(pub_key, 0, pub_key_len); |
330 | bn_bn2bin(dh->pub_key, pub_key + pub_key_len - len, len); |
331 | |
332 | return 0; |
333 | } |
334 | |
335 | int ff_dh_compute_shared_secret_key(FF_DH *dh, const uint8_t *pub_key, |
336 | int pub_key_len, uint8_t *secret_key, |
337 | int secret_key_len) |
338 | { |
339 | FFBigNum q1 = NULL, pub_key_bn = NULL; |
340 | int ret; |
341 | |
342 | /* convert the big-endian form of the public key into a bignum */ |
343 | bn_bin2bn(pub_key_bn, pub_key, pub_key_len); |
344 | if (!pub_key_bn) |
345 | return AVERROR(ENOMEM); |
346 | |
347 | /* convert the string containing a hexadecimal number into a bignum */ |
348 | bn_hex2bn(q1, Q1024, ret); |
349 | if (!ret) { |
350 | ret = AVERROR(ENOMEM); |
351 | goto fail; |
352 | } |
353 | |
354 | /* when the public key is valid we have to compute the shared secret key */ |
355 | if ((ret = dh_is_valid_public_key(pub_key_bn, dh->p, q1)) < 0) { |
356 | goto fail; |
357 | } else if ((ret = dh_compute_key(dh, pub_key_bn, secret_key_len, |
358 | secret_key)) < 0) { |
359 | ret = AVERROR(EINVAL); |
360 | goto fail; |
361 | } |
362 | |
363 | fail: |
364 | bn_free(pub_key_bn); |
365 | bn_free(q1); |
366 | |
367 | return ret; |
368 | } |
369 |