xref: /third_party/ffmpeg/libavformat/rtmpdh.c (revision cabdff1a)
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)
110static 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)
137static 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)
155static 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
223static 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
248static 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
269void 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
280static 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;
315fail:
316    bn_free(bn);
317
318    return ret;
319}
320
321av_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
342fail:
343    ff_dh_free(dh);
344
345    return NULL;
346}
347
348int ff_dh_generate_public_key(FF_DH *dh)
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
374int 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
390int ff_dh_compute_shared_secret_key(FF_DH *dh, const uint8_t *pub_key,
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
418fail:
419    bn_free(pub_key_bn);
420    bn_free(q1);
421
422    return ret;
423}
424