/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 | } |