]>
Commit | Line | Data |
---|---|---|
552cc11b MW |
1 | /* |
2 | * Copyright (C) 1998-2002 D. Hugh Redelmeier. | |
3 | * Copyright (C) 1999, 2000, 2001 Henry Spencer. | |
908d5717 | 4 | * Copyright (C) 2010 Tobias Brunner |
552cc11b MW |
5 | * Copyright (C) 2005-2008 Martin Willi |
6 | * Copyright (C) 2005 Jan Hutter | |
7 | * Hochschule fuer Technik Rapperswil | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify it | |
10 | * under the terms of the GNU General Public License as published by the | |
11 | * Free Software Foundation; either version 2 of the License, or (at your | |
12 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, but | |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
16 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
17 | * for more details. | |
552cc11b MW |
18 | */ |
19 | ||
20 | #include <gmp.h> | |
21 | ||
22 | #include "gmp_diffie_hellman.h" | |
23 | ||
f05b4272 | 24 | #include <utils/debug.h> |
552cc11b | 25 | |
3e35a6e7 MW |
26 | #ifdef HAVE_MPZ_POWM_SEC |
27 | # undef mpz_powm | |
28 | # define mpz_powm mpz_powm_sec | |
29 | #endif | |
552cc11b | 30 | |
552cc11b MW |
31 | typedef struct private_gmp_diffie_hellman_t private_gmp_diffie_hellman_t; |
32 | ||
33 | /** | |
34 | * Private data of an gmp_diffie_hellman_t object. | |
35 | */ | |
36 | struct private_gmp_diffie_hellman_t { | |
37 | /** | |
38 | * Public gmp_diffie_hellman_t interface. | |
39 | */ | |
40 | gmp_diffie_hellman_t public; | |
7daf5226 | 41 | |
552cc11b MW |
42 | /** |
43 | * Diffie Hellman group number. | |
44 | */ | |
45 | u_int16_t group; | |
7daf5226 MW |
46 | |
47 | /* | |
552cc11b | 48 | * Generator value. |
7daf5226 | 49 | */ |
552cc11b | 50 | mpz_t g; |
7daf5226 | 51 | |
552cc11b MW |
52 | /** |
53 | * My private value. | |
54 | */ | |
55 | mpz_t xa; | |
7daf5226 | 56 | |
552cc11b MW |
57 | /** |
58 | * My public value. | |
59 | */ | |
60 | mpz_t ya; | |
7daf5226 | 61 | |
552cc11b MW |
62 | /** |
63 | * Other public value. | |
7daf5226 | 64 | */ |
552cc11b | 65 | mpz_t yb; |
7daf5226 | 66 | |
552cc11b MW |
67 | /** |
68 | * Shared secret. | |
7daf5226 | 69 | */ |
552cc11b MW |
70 | mpz_t zz; |
71 | ||
72 | /** | |
73 | * Modulus. | |
74 | */ | |
75 | mpz_t p; | |
7daf5226 | 76 | |
552cc11b MW |
77 | /** |
78 | * Modulus length. | |
79 | */ | |
80 | size_t p_len; | |
7daf5226 | 81 | |
552cc11b MW |
82 | /** |
83 | * True if shared secret is computed and stored in my_public_value. | |
84 | */ | |
85 | bool computed; | |
86 | }; | |
87 | ||
876b61e1 MW |
88 | METHOD(diffie_hellman_t, set_other_public_value, void, |
89 | private_gmp_diffie_hellman_t *this, chunk_t value) | |
552cc11b MW |
90 | { |
91 | mpz_t p_min_1; | |
7daf5226 | 92 | |
552cc11b MW |
93 | mpz_init(p_min_1); |
94 | mpz_sub_ui(p_min_1, this->p, 1); | |
7daf5226 | 95 | |
552cc11b | 96 | mpz_import(this->yb, value.len, 1, 1, 1, 0, value.ptr); |
7daf5226 MW |
97 | |
98 | /* check public value: | |
552cc11b MW |
99 | * 1. 0 or 1 is invalid as 0^a = 0 and 1^a = 1 |
100 | * 2. a public value larger or equal the modulus is invalid */ | |
cdaf57ec | 101 | if (mpz_cmp_ui(this->yb, 1) > 0 && |
552cc11b MW |
102 | mpz_cmp(this->yb, p_min_1) < 0) |
103 | { | |
104 | #ifdef EXTENDED_DH_TEST | |
105 | /* 3. test if y ^ q mod p = 1, where q = (p - 1)/2. */ | |
106 | mpz_t q, one; | |
4590260b | 107 | diffie_hellman_params_t *params; |
7daf5226 | 108 | |
552cc11b MW |
109 | mpz_init(q); |
110 | mpz_init(one); | |
4590260b MW |
111 | |
112 | params = diffie_hellman_get_params(this->group); | |
113 | if (!params->subgroup.len) | |
114 | { | |
115 | mpz_fdiv_q_2exp(q, p_min_1, 1); | |
116 | } | |
117 | else | |
118 | { | |
119 | mpz_import(q, params->subgroup.len, 1, 1, 1, 0, params->subgroup.ptr); | |
120 | } | |
552cc11b MW |
121 | mpz_powm(one, this->yb, q, this->p); |
122 | mpz_clear(q); | |
123 | if (mpz_cmp_ui(one, 1) == 0) | |
124 | { | |
125 | mpz_powm(this->zz, this->yb, this->xa, this->p); | |
126 | this->computed = TRUE; | |
127 | } | |
128 | else | |
129 | { | |
8b0e0910 TB |
130 | DBG1(DBG_LIB, "public DH value verification failed:" |
131 | " y ^ q mod p != 1"); | |
552cc11b MW |
132 | } |
133 | mpz_clear(one); | |
134 | #else | |
135 | mpz_powm(this->zz, this->yb, this->xa, this->p); | |
136 | this->computed = TRUE; | |
137 | #endif | |
138 | } | |
139 | else | |
140 | { | |
8b0e0910 TB |
141 | DBG1(DBG_LIB, "public DH value verification failed:" |
142 | " y < 2 || y > p - 1 "); | |
552cc11b MW |
143 | } |
144 | mpz_clear(p_min_1); | |
145 | } | |
146 | ||
876b61e1 MW |
147 | METHOD(diffie_hellman_t, get_my_public_value, void, |
148 | private_gmp_diffie_hellman_t *this,chunk_t *value) | |
552cc11b MW |
149 | { |
150 | value->len = this->p_len; | |
323f9f99 MW |
151 | value->ptr = mpz_export(NULL, NULL, 1, value->len, 1, 0, this->ya); |
152 | if (value->ptr == NULL) | |
153 | { | |
154 | value->len = 0; | |
155 | } | |
552cc11b MW |
156 | } |
157 | ||
876b61e1 MW |
158 | METHOD(diffie_hellman_t, get_shared_secret, status_t, |
159 | private_gmp_diffie_hellman_t *this, chunk_t *secret) | |
552cc11b MW |
160 | { |
161 | if (!this->computed) | |
162 | { | |
163 | return FAILED; | |
164 | } | |
165 | secret->len = this->p_len; | |
73f6886a MW |
166 | secret->ptr = mpz_export(NULL, NULL, 1, secret->len, 1, 0, this->zz); |
167 | if (secret->ptr == NULL) | |
168 | { | |
169 | return FAILED; | |
170 | } | |
552cc11b MW |
171 | return SUCCESS; |
172 | } | |
173 | ||
876b61e1 MW |
174 | METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t, |
175 | private_gmp_diffie_hellman_t *this) | |
552cc11b MW |
176 | { |
177 | return this->group; | |
178 | } | |
179 | ||
876b61e1 MW |
180 | METHOD(diffie_hellman_t, destroy, void, |
181 | private_gmp_diffie_hellman_t *this) | |
552cc11b MW |
182 | { |
183 | mpz_clear(this->p); | |
184 | mpz_clear(this->xa); | |
185 | mpz_clear(this->ya); | |
186 | mpz_clear(this->yb); | |
187 | mpz_clear(this->zz); | |
188 | mpz_clear(this->g); | |
189 | free(this); | |
190 | } | |
191 | ||
08d8b940 MW |
192 | /** |
193 | * Generic internal constructor | |
552cc11b | 194 | */ |
08d8b940 MW |
195 | static gmp_diffie_hellman_t *create_generic(diffie_hellman_group_t group, |
196 | size_t exp_len, chunk_t g, chunk_t p) | |
552cc11b | 197 | { |
b34b93db | 198 | private_gmp_diffie_hellman_t *this; |
552cc11b | 199 | chunk_t random; |
08d8b940 | 200 | rng_t *rng; |
b34b93db | 201 | |
876b61e1 | 202 | INIT(this, |
ba31fe1f MW |
203 | .public = { |
204 | .dh = { | |
205 | .get_shared_secret = _get_shared_secret, | |
206 | .set_other_public_value = _set_other_public_value, | |
207 | .get_my_public_value = _get_my_public_value, | |
208 | .get_dh_group = _get_dh_group, | |
209 | .destroy = _destroy, | |
210 | }, | |
876b61e1 MW |
211 | }, |
212 | .group = group, | |
08d8b940 | 213 | .p_len = p.len, |
876b61e1 | 214 | ); |
7daf5226 | 215 | |
552cc11b MW |
216 | mpz_init(this->p); |
217 | mpz_init(this->yb); | |
218 | mpz_init(this->ya); | |
219 | mpz_init(this->xa); | |
220 | mpz_init(this->zz); | |
221 | mpz_init(this->g); | |
08d8b940 MW |
222 | mpz_import(this->g, g.len, 1, 1, 1, 0, g.ptr); |
223 | mpz_import(this->p, p.len, 1, 1, 1, 0, p.ptr); | |
908d5717 | 224 | |
6a365f07 MW |
225 | rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); |
226 | if (!rng) | |
552cc11b | 227 | { |
8b0e0910 TB |
228 | DBG1(DBG_LIB, "no RNG found for quality %N", rng_quality_names, |
229 | RNG_STRONG); | |
552cc11b MW |
230 | destroy(this); |
231 | return NULL; | |
232 | } | |
5025135f TB |
233 | if (!rng->allocate_bytes(rng, exp_len, &random)) |
234 | { | |
235 | DBG1(DBG_LIB, "failed to allocate DH secret"); | |
236 | rng->destroy(rng); | |
237 | destroy(this); | |
238 | return NULL; | |
239 | } | |
6a365f07 | 240 | rng->destroy(rng); |
f5ab7f5f | 241 | |
08d8b940 | 242 | if (exp_len == this->p_len) |
f5ab7f5f AS |
243 | { |
244 | /* achieve bitsof(p)-1 by setting MSB to 0 */ | |
245 | *random.ptr &= 0x7F; | |
246 | } | |
552cc11b MW |
247 | mpz_import(this->xa, random.len, 1, 1, 1, 0, random.ptr); |
248 | chunk_free(&random); | |
8b0e0910 TB |
249 | DBG2(DBG_LIB, "size of DH secret exponent: %u bits", |
250 | mpz_sizeinbase(this->xa, 2)); | |
f5ab7f5f | 251 | |
552cc11b | 252 | mpz_powm(this->ya, this->g, this->xa, this->p); |
7daf5226 | 253 | |
552cc11b MW |
254 | return &this->public; |
255 | } | |
256 | ||
08d8b940 MW |
257 | /* |
258 | * Described in header. | |
259 | */ | |
260 | gmp_diffie_hellman_t *gmp_diffie_hellman_create(diffie_hellman_group_t group) | |
261 | { | |
262 | diffie_hellman_params_t *params; | |
263 | ||
264 | params = diffie_hellman_get_params(group); | |
265 | if (!params) | |
266 | { | |
267 | return NULL; | |
268 | } | |
269 | return create_generic(group, params->exp_len, | |
270 | params->generator, params->prime); | |
271 | } | |
272 | ||
273 | ||
274 | gmp_diffie_hellman_t *gmp_diffie_hellman_create_custom( | |
275 | diffie_hellman_group_t group, chunk_t g, chunk_t p) | |
276 | { | |
277 | if (group == MODP_CUSTOM) | |
278 | { | |
279 | return create_generic(MODP_CUSTOM, p.len, g, p); | |
280 | } | |
281 | return NULL; | |
282 | } |