gmp: Support setting Diffie-Hellman private values
[strongswan.git] / src / libstrongswan / plugins / gmp / gmp_diffie_hellman.c
1 /*
2 * Copyright (C) 1998-2002 D. Hugh Redelmeier.
3 * Copyright (C) 1999, 2000, 2001 Henry Spencer.
4 * Copyright (C) 2010 Tobias Brunner
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.
18 */
19
20 #include <gmp.h>
21
22 #include "gmp_diffie_hellman.h"
23
24 #include <utils/debug.h>
25
26 #ifdef HAVE_MPZ_POWM_SEC
27 # undef mpz_powm
28 # define mpz_powm mpz_powm_sec
29 #endif
30
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;
41
42 /**
43 * Diffie Hellman group number.
44 */
45 diffie_hellman_group_t group;
46
47 /*
48 * Generator value.
49 */
50 mpz_t g;
51
52 /**
53 * My private value.
54 */
55 mpz_t xa;
56
57 /**
58 * My public value.
59 */
60 mpz_t ya;
61
62 /**
63 * Other public value.
64 */
65 mpz_t yb;
66
67 /**
68 * Shared secret.
69 */
70 mpz_t zz;
71
72 /**
73 * Modulus.
74 */
75 mpz_t p;
76
77 /**
78 * Modulus length.
79 */
80 size_t p_len;
81
82 /**
83 * True if shared secret is computed and stored in my_public_value.
84 */
85 bool computed;
86 };
87
88 METHOD(diffie_hellman_t, set_other_public_value, bool,
89 private_gmp_diffie_hellman_t *this, chunk_t value)
90 {
91 mpz_t p_min_1;
92
93 if (!diffie_hellman_verify_value(this->group, value))
94 {
95 return FALSE;
96 }
97
98 mpz_init(p_min_1);
99 mpz_sub_ui(p_min_1, this->p, 1);
100
101 mpz_import(this->yb, value.len, 1, 1, 1, 0, value.ptr);
102
103 /* check public value:
104 * 1. 0 or 1 is invalid as 0^a = 0 and 1^a = 1
105 * 2. a public value larger or equal the modulus is invalid */
106 if (mpz_cmp_ui(this->yb, 1) > 0 &&
107 mpz_cmp(this->yb, p_min_1) < 0)
108 {
109 #ifdef EXTENDED_DH_TEST
110 /* 3. test if y ^ q mod p = 1, where q = (p - 1)/2. */
111 mpz_t q, one;
112 diffie_hellman_params_t *params;
113
114 mpz_init(q);
115 mpz_init(one);
116
117 params = diffie_hellman_get_params(this->group);
118 if (!params->subgroup.len)
119 {
120 mpz_fdiv_q_2exp(q, p_min_1, 1);
121 }
122 else
123 {
124 mpz_import(q, params->subgroup.len, 1, 1, 1, 0, params->subgroup.ptr);
125 }
126 mpz_powm(one, this->yb, q, this->p);
127 mpz_clear(q);
128 if (mpz_cmp_ui(one, 1) == 0)
129 {
130 mpz_powm(this->zz, this->yb, this->xa, this->p);
131 this->computed = TRUE;
132 }
133 else
134 {
135 DBG1(DBG_LIB, "public DH value verification failed:"
136 " y ^ q mod p != 1");
137 }
138 mpz_clear(one);
139 #else
140 mpz_powm(this->zz, this->yb, this->xa, this->p);
141 this->computed = TRUE;
142 #endif
143 }
144 else
145 {
146 DBG1(DBG_LIB, "public DH value verification failed:"
147 " y < 2 || y > p - 1 ");
148 }
149 mpz_clear(p_min_1);
150 return this->computed;
151 }
152
153 METHOD(diffie_hellman_t, get_my_public_value, bool,
154 private_gmp_diffie_hellman_t *this,chunk_t *value)
155 {
156 value->len = this->p_len;
157 value->ptr = mpz_export(NULL, NULL, 1, value->len, 1, 0, this->ya);
158 if (value->ptr == NULL)
159 {
160 value->len = 0;
161 }
162 return TRUE;
163 }
164
165 METHOD(diffie_hellman_t, set_private_value, bool,
166 private_gmp_diffie_hellman_t *this, chunk_t value)
167 {
168 mpz_import(this->xa, value.len, 1, 1, 1, 0, value.ptr);
169 mpz_powm(this->ya, this->g, this->xa, this->p);
170 this->computed = FALSE;
171 return TRUE;
172 }
173
174 METHOD(diffie_hellman_t, get_shared_secret, bool,
175 private_gmp_diffie_hellman_t *this, chunk_t *secret)
176 {
177 if (!this->computed)
178 {
179 return FALSE;
180 }
181 secret->len = this->p_len;
182 secret->ptr = mpz_export(NULL, NULL, 1, secret->len, 1, 0, this->zz);
183 if (secret->ptr == NULL)
184 {
185 return FALSE;
186 }
187 return TRUE;
188 }
189
190 METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t,
191 private_gmp_diffie_hellman_t *this)
192 {
193 return this->group;
194 }
195
196 METHOD(diffie_hellman_t, destroy, void,
197 private_gmp_diffie_hellman_t *this)
198 {
199 mpz_clear(this->p);
200 mpz_clear(this->xa);
201 mpz_clear(this->ya);
202 mpz_clear(this->yb);
203 mpz_clear(this->zz);
204 mpz_clear(this->g);
205 free(this);
206 }
207
208 /**
209 * Generic internal constructor
210 */
211 static gmp_diffie_hellman_t *create_generic(diffie_hellman_group_t group,
212 size_t exp_len, chunk_t g, chunk_t p)
213 {
214 private_gmp_diffie_hellman_t *this;
215 chunk_t random;
216 rng_t *rng;
217
218 INIT(this,
219 .public = {
220 .dh = {
221 .get_shared_secret = _get_shared_secret,
222 .set_other_public_value = _set_other_public_value,
223 .get_my_public_value = _get_my_public_value,
224 .set_private_value = _set_private_value,
225 .get_dh_group = _get_dh_group,
226 .destroy = _destroy,
227 },
228 },
229 .group = group,
230 .p_len = p.len,
231 );
232
233 mpz_init(this->p);
234 mpz_init(this->yb);
235 mpz_init(this->ya);
236 mpz_init(this->xa);
237 mpz_init(this->zz);
238 mpz_init(this->g);
239 mpz_import(this->g, g.len, 1, 1, 1, 0, g.ptr);
240 mpz_import(this->p, p.len, 1, 1, 1, 0, p.ptr);
241
242 rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
243 if (!rng)
244 {
245 DBG1(DBG_LIB, "no RNG found for quality %N", rng_quality_names,
246 RNG_STRONG);
247 destroy(this);
248 return NULL;
249 }
250 if (!rng->allocate_bytes(rng, exp_len, &random))
251 {
252 DBG1(DBG_LIB, "failed to allocate DH secret");
253 rng->destroy(rng);
254 destroy(this);
255 return NULL;
256 }
257 rng->destroy(rng);
258
259 if (exp_len == this->p_len)
260 {
261 /* achieve bitsof(p)-1 by setting MSB to 0 */
262 *random.ptr &= 0x7F;
263 }
264 mpz_import(this->xa, random.len, 1, 1, 1, 0, random.ptr);
265 chunk_clear(&random);
266 DBG2(DBG_LIB, "size of DH secret exponent: %u bits",
267 mpz_sizeinbase(this->xa, 2));
268
269 mpz_powm(this->ya, this->g, this->xa, this->p);
270
271 return &this->public;
272 }
273
274 /*
275 * Described in header.
276 */
277 gmp_diffie_hellman_t *gmp_diffie_hellman_create(diffie_hellman_group_t group)
278 {
279 diffie_hellman_params_t *params;
280
281 params = diffie_hellman_get_params(group);
282 if (!params)
283 {
284 return NULL;
285 }
286 return create_generic(group, params->exp_len,
287 params->generator, params->prime);
288 }
289
290
291 gmp_diffie_hellman_t *gmp_diffie_hellman_create_custom(
292 diffie_hellman_group_t group, chunk_t g, chunk_t p)
293 {
294 if (group == MODP_CUSTOM)
295 {
296 return create_generic(MODP_CUSTOM, p.len, g, p);
297 }
298 return NULL;
299 }