Coverage Report

Created: 2026-01-17 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensc/openpace/src/eac_dh.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2010-2012 Frank Morgner and Dominik Oepen
3
 *
4
 * This file is part of OpenPACE.
5
 *
6
 * OpenPACE is free software: you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License as published by the Free
8
 * Software Foundation, either version 3 of the License, or (at your option)
9
 * any later version.
10
 *
11
 * OpenPACE is distributed in the hope that it will be useful, but WITHOUT ANY
12
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
14
 * details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with
17
 * OpenPACE.  If not, see <http://www.gnu.org/licenses/>.
18
 *
19
 * Additional permission under GNU GPL version 3 section 7
20
 *
21
 * If you modify this Program, or any covered work, by linking or combining it
22
 * with OpenSSL (or a modified version of that library), containing
23
 * parts covered by the terms of OpenSSL's license, the licensors of
24
 * this Program grant you additional permission to convey the resulting work.
25
 * Corresponding Source for a non-source form of such a combination shall include
26
 * the source code for the parts of OpenSSL used as well as that of the
27
 * covered work.
28
 *
29
 * If you modify this Program, or any covered work, by linking or combining it
30
 * with OpenSC (or a modified version of that library), containing
31
 * parts covered by the terms of OpenSC's license, the licensors of
32
 * this Program grant you additional permission to convey the resulting work. 
33
 * Corresponding Source for a non-source form of such a combination shall include
34
 * the source code for the parts of OpenSC used as well as that of the
35
 * covered work.
36
 */
37
38
/**
39
 * @file eac_dh.c
40
 * @brief Diffie Hellman helper functions
41
 *
42
 * @author Frank Morgner <frankmorgner@gmail.com>
43
 * @author Dominik Oepen <oepen@informatik.hu-berlin.de>
44
 */
45
46
#ifdef HAVE_CONFIG_H
47
#include "config.h"
48
#endif
49
50
#include "eac_dh.h"
51
#include "eac_err.h"
52
#include "misc.h"
53
#include "ssl_compat.h"
54
#include <eac/eac.h>
55
#include <openssl/bn.h>
56
#include <openssl/evp.h>
57
58
/**
59
 * @brief Public key validation method described in RFC 2631.
60
 *
61
 * Verify that DH->pub_key lies within the interval [2,p-1]. If it does not,
62
 * the key is invalid.
63
 * If DH->q exists, compute y^q mod p. If the result == 1, the key is valid.
64
 * Otherwise the key is invalid.
65
 *
66
 * @param[in] dh DH object to use
67
 * @param[in] ctx BN_CTX object
68
 * @param[out] ret Can contain these flags as result:
69
 * DH_CHECK_PUBKEY_TOO_SMALL (smaller than 2)
70
 * DH_CHECK_PUBKEY_TOO_LARGE (bigger than p-1)
71
 * DH_CHECK_PUBKEY_INVALID (y^q mod p != 1)
72
 *
73
 * @return 1 on success or 0 if an error occurred
74
 */
75
static int
76
DH_check_pub_key_rfc(const DH *dh, BN_CTX *ctx, int *ret);
77
0
#define DH_CHECK_PUBKEY_INVALID        0x04
78
79
int
80
init_dh(DH ** dh, int standardizedDomainParameters)
81
0
{
82
0
    int i;
83
0
    DH *tmp = NULL;
84
85
0
    check(dh, "Invalid arguments");
86
87
0
    if (!*dh) {
88
0
        switch (standardizedDomainParameters) {
89
0
           case 0:
90
0
              tmp = DH_get_1024_160();
91
0
              break;
92
0
           case 1:
93
0
              tmp = DH_get_2048_224();
94
0
              break;
95
0
           case 2:
96
0
              tmp = DH_get_2048_256();
97
0
              break;
98
0
           default:
99
0
              log_err("Invalid arguments");
100
0
              goto err;
101
0
        }
102
0
        if (!tmp)
103
0
            goto err;
104
0
    } else {
105
        /*Note: this could be something not matching standardizedDomainParameters */
106
0
        tmp = *dh;
107
0
    }
108
109
0
    if (!DH_check(tmp, &i))
110
0
        goto err;
111
112
    /* RFC 5114 parameters do not use safe primes and OpenSSL does not know
113
     * how to deal with generator other then 2 or 5. Therefore we have to
114
     * ignore some of the checks */
115
0
    i &= ~DH_CHECK_P_NOT_SAFE_PRIME;
116
0
    i &= ~DH_UNABLE_TO_CHECK_GENERATOR;
117
118
0
    check(!i, "Bad DH key");
119
120
0
    *dh = tmp;
121
122
0
    return 1;
123
124
0
err:
125
0
    if (tmp && !*dh) {
126
0
        DH_free(tmp);
127
0
    }
128
129
0
    return 0;
130
0
}
131
132
static int
133
DH_check_pub_key_rfc(const DH *dh, BN_CTX *ctx, int *ret)
134
0
{
135
0
    BIGNUM *bn = NULL;
136
0
    int ok = 0;
137
0
    const BIGNUM *pub_key, *p, *q, *g;
138
139
0
    check((dh && ret), "Invalid arguments");
140
141
0
    BN_CTX_start(ctx);
142
143
0
    DH_get0_key(dh, &pub_key, NULL);
144
0
    DH_get0_pqg(dh, &p, &q, &g);
145
146
    /* Verify that y lies within the interval [2,p-1]. */
147
0
    if (!DH_check_pub_key(dh, pub_key, ret))
148
0
        goto err;
149
150
    /* If the DH is conform to RFC 2631 it should have a non-NULL q.
151
     * Others (like the DHs generated from OpenSSL) might have a problem with
152
     * this check. */
153
0
    if (q) {
154
        /* Compute y^q mod p. If the result == 1, the key is valid. */
155
0
        bn = BN_CTX_get(ctx);
156
0
        if (!bn || !BN_mod_exp(bn, pub_key, q, p, ctx))
157
0
            goto err;
158
0
        if (!BN_is_one(bn))
159
0
            *ret |= DH_CHECK_PUBKEY_INVALID;
160
0
    }
161
0
    ok = 1;
162
163
0
err:
164
0
    BN_CTX_end(ctx);
165
0
    return ok;
166
0
}
167
168
169
BIGNUM *
170
DH_get_q(const DH *dh, BN_CTX *ctx)
171
0
{
172
0
    BIGNUM *q_new = NULL, *bn = NULL;
173
0
    int i;
174
0
    const BIGNUM *p, *q;
175
176
0
    check(dh, "Invalid arguments");
177
178
0
    DH_get0_pqg(dh, &p, &q, NULL);
179
0
    if (!q) {
180
0
        q_new = BN_new();
181
0
        bn = BN_dup(p);
182
183
        /* DH primes should be strong, based on a Sophie Germain prime q
184
         * p=(2*q)+1 or (p-1)/2=q */
185
0
        if (!q_new || !bn ||
186
0
                !BN_sub_word(bn, 1) ||
187
0
                !BN_rshift1(q_new, bn)) {
188
0
            goto err;
189
0
        }
190
0
    } else {
191
0
        q_new = BN_dup(q);
192
0
    }
193
194
    /* q should always be prime */
195
0
    i = BN_is_prime_ex(q_new, BN_prime_checks, ctx, NULL);
196
0
    if (i <= 0) {
197
0
       if (i == 0)
198
0
          log_err("Unable to get Sophie Germain prime");
199
0
       goto err;
200
0
    }
201
202
0
    return q_new;
203
204
0
err:
205
0
    if (bn)
206
0
        BN_clear_free(bn);
207
0
    if (q_new)
208
0
        BN_clear_free(q_new);
209
210
0
    return NULL;
211
0
}
212
213
BIGNUM *
214
DH_get_order(const DH *dh, BN_CTX *ctx)
215
0
{
216
0
    BIGNUM *order = NULL, *bn = NULL;
217
0
    const BIGNUM *p, *g;
218
219
0
    check(dh && ctx, "Invalid argument");
220
221
0
    BN_CTX_start(ctx);
222
223
0
    DH_get0_pqg(dh, &p, NULL, &g);
224
225
    /* suppose the order of g is q-1 */
226
0
    order = DH_get_q(dh, ctx);
227
0
    bn = BN_CTX_get(ctx);
228
0
    if (!bn || !order || !BN_sub_word(order, 1)
229
0
          || !BN_mod_exp(bn, g, order, p, ctx))
230
0
        goto err;
231
232
0
    if (BN_cmp(bn, BN_value_one()) != 0) {
233
        /* if bn != 1, then q-1 is not the order of g, but p-1 should be */
234
0
        if (!BN_sub(order, p, BN_value_one()) ||
235
0
              !BN_mod_exp(bn, g, order, p, ctx))
236
0
           goto err;
237
0
        check(BN_cmp(bn, BN_value_one()) == 0, "Unable to get order");
238
0
    }
239
240
0
    BN_CTX_end(ctx);
241
242
0
    return order;
243
244
0
err:
245
0
    if (order)
246
0
        BN_clear_free(order);
247
0
    BN_CTX_end(ctx);
248
249
0
    return NULL;
250
0
}
251
252
BUF_MEM *
253
dh_generate_key(EVP_PKEY *key, BN_CTX *bn_ctx)
254
0
{
255
0
    int suc;
256
0
    DH *dh = NULL;
257
0
    BUF_MEM *ret = NULL;
258
0
    const BIGNUM *pub_key;
259
260
261
0
    check(key, "Invalid arguments");
262
263
0
    dh = EVP_PKEY_get1_DH(key);
264
0
    if (!dh)
265
0
        goto err;
266
267
0
    if (!DH_generate_key(dh) || !DH_check_pub_key_rfc(dh, bn_ctx, &suc))
268
0
        goto err;
269
270
0
    if (suc)
271
0
        goto err;
272
273
0
    DH_get0_key(dh, &pub_key, NULL);
274
275
0
    ret = BN_bn2buf(pub_key);
276
277
0
err:
278
0
    if (dh)
279
0
        DH_free(dh);
280
0
    return ret;
281
0
}
282
283
BUF_MEM *
284
dh_compute_key(EVP_PKEY *key, const BUF_MEM * in, BN_CTX *bn_ctx)
285
0
{
286
0
    BUF_MEM * out = NULL;
287
0
    BIGNUM * bn = NULL;
288
0
    DH *dh = NULL;
289
290
0
    check(key && in, "Invalid arguments");
291
292
0
    dh = EVP_PKEY_get1_DH(key);
293
0
    if (!dh)
294
0
        return NULL;
295
296
    /* decode public key */
297
0
    bn = BN_bin2bn((unsigned char *) in->data, in->length, bn);
298
0
    if (!bn)
299
0
        goto err;
300
301
0
    out = BUF_MEM_create(DH_size(dh));
302
0
    if (!out)
303
0
        goto err;
304
305
0
    out->length = DH_compute_key((unsigned char *) out->data, bn, dh);
306
0
    if ((int) out->length < 0)
307
0
        goto err;
308
309
0
    BN_clear_free(bn);
310
0
    DH_free(dh);
311
312
0
    return out;
313
314
0
err:
315
0
    if (out)
316
0
        BUF_MEM_free(out);
317
0
    if (bn)
318
0
        BN_clear_free(bn);
319
0
    if (dh)
320
0
        DH_free(dh);
321
322
0
    return NULL;
323
0
}
324
325
DH *
326
DHparams_dup_with_q(DH *dh)
327
0
{
328
0
    const BIGNUM *p, *q, *g;
329
330
0
    DH *dup = DHparams_dup(dh);
331
0
    if (dup) {
332
0
        DH_get0_pqg(dh, &p, &q, &g);
333
0
        DH_set0_pqg(dup, BN_dup(p), BN_dup(q), BN_dup(g));
334
0
    }
335
336
0
    return dup;
337
0
}