Added support for DH groups 22, 23 and 24, patch contributed by Joy Latten
[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 <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 u_int16_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 /**
89 * Implementation of gmp_diffie_hellman_t.set_other_public_value.
90 */
91 static void set_other_public_value(private_gmp_diffie_hellman_t *this, chunk_t value)
92 {
93 mpz_t p_min_1;
94
95 mpz_init(p_min_1);
96 mpz_sub_ui(p_min_1, this->p, 1);
97
98 mpz_import(this->yb, value.len, 1, 1, 1, 0, value.ptr);
99
100 /* check public value:
101 * 1. 0 or 1 is invalid as 0^a = 0 and 1^a = 1
102 * 2. a public value larger or equal the modulus is invalid */
103 if (mpz_cmp_ui(this->yb, 1) > 0 &&
104 mpz_cmp(this->yb, p_min_1) < 0)
105 {
106 #ifdef EXTENDED_DH_TEST
107 /* 3. test if y ^ q mod p = 1, where q = (p - 1)/2. */
108 mpz_t q, one;
109 diffie_hellman_params_t *params;
110
111 mpz_init(q);
112 mpz_init(one);
113
114 params = diffie_hellman_get_params(this->group);
115 if (!params->subgroup.len)
116 {
117 mpz_fdiv_q_2exp(q, p_min_1, 1);
118 }
119 else
120 {
121 mpz_import(q, params->subgroup.len, 1, 1, 1, 0, params->subgroup.ptr);
122 }
123 mpz_powm(one, this->yb, q, this->p);
124 mpz_clear(q);
125 if (mpz_cmp_ui(one, 1) == 0)
126 {
127 mpz_powm(this->zz, this->yb, this->xa, this->p);
128 this->computed = TRUE;
129 }
130 else
131 {
132 DBG1(DBG_LIB, "public DH value verification failed:"
133 " y ^ q mod p != 1");
134 }
135 mpz_clear(one);
136 #else
137 mpz_powm(this->zz, this->yb, this->xa, this->p);
138 this->computed = TRUE;
139 #endif
140 }
141 else
142 {
143 DBG1(DBG_LIB, "public DH value verification failed:"
144 " y < 2 || y > p - 1 ");
145 }
146 mpz_clear(p_min_1);
147 }
148
149 /**
150 * Implementation of gmp_diffie_hellman_t.get_my_public_value.
151 */
152 static void get_my_public_value(private_gmp_diffie_hellman_t *this,chunk_t *value)
153 {
154 value->len = this->p_len;
155 value->ptr = mpz_export(NULL, NULL, 1, value->len, 1, 0, this->ya);
156 if (value->ptr == NULL)
157 {
158 value->len = 0;
159 }
160 }
161
162 /**
163 * Implementation of gmp_diffie_hellman_t.get_shared_secret.
164 */
165 static status_t get_shared_secret(private_gmp_diffie_hellman_t *this, chunk_t *secret)
166 {
167 if (!this->computed)
168 {
169 return FAILED;
170 }
171 secret->len = this->p_len;
172 secret->ptr = mpz_export(NULL, NULL, 1, secret->len, 1, 0, this->zz);
173 if (secret->ptr == NULL)
174 {
175 return FAILED;
176 }
177 return SUCCESS;
178 }
179
180 /**
181 * Implementation of gmp_diffie_hellman_t.get_dh_group.
182 */
183 static diffie_hellman_group_t get_dh_group(private_gmp_diffie_hellman_t *this)
184 {
185 return this->group;
186 }
187
188 /**
189 * Implementation of gmp_diffie_hellman_t.destroy.
190 */
191 static void destroy(private_gmp_diffie_hellman_t *this)
192 {
193 mpz_clear(this->p);
194 mpz_clear(this->xa);
195 mpz_clear(this->ya);
196 mpz_clear(this->yb);
197 mpz_clear(this->zz);
198 mpz_clear(this->g);
199 free(this);
200 }
201
202 /*
203 * Described in header.
204 */
205 gmp_diffie_hellman_t *gmp_diffie_hellman_create(diffie_hellman_group_t group)
206 {
207 private_gmp_diffie_hellman_t *this;
208 diffie_hellman_params_t *params;
209 rng_t *rng;
210 chunk_t random;
211
212 params = diffie_hellman_get_params(group);
213 if (!params)
214 {
215 return NULL;
216 }
217
218 this = malloc_thing(private_gmp_diffie_hellman_t);
219
220 /* public functions */
221 this->public.dh.get_shared_secret = (status_t (*)(diffie_hellman_t *, chunk_t *)) get_shared_secret;
222 this->public.dh.set_other_public_value = (void (*)(diffie_hellman_t *, chunk_t )) set_other_public_value;
223 this->public.dh.get_my_public_value = (void (*)(diffie_hellman_t *, chunk_t *)) get_my_public_value;
224 this->public.dh.get_dh_group = (diffie_hellman_group_t (*)(diffie_hellman_t *)) get_dh_group;
225 this->public.dh.destroy = (void (*)(diffie_hellman_t *)) destroy;
226
227 /* private variables */
228 this->group = group;
229 mpz_init(this->p);
230 mpz_init(this->yb);
231 mpz_init(this->ya);
232 mpz_init(this->xa);
233 mpz_init(this->zz);
234 mpz_init(this->g);
235
236 this->computed = FALSE;
237 this->p_len = params->prime.len;
238 mpz_import(this->p, params->prime.len, 1, 1, 1, 0, params->prime.ptr);
239 mpz_import(this->g, params->generator.len, 1, 1, 1, 0, params->generator.ptr);
240
241 rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
242 if (!rng)
243 {
244 DBG1(DBG_LIB, "no RNG found for quality %N", rng_quality_names,
245 RNG_STRONG);
246 destroy(this);
247 return NULL;
248 }
249
250 rng->allocate_bytes(rng, params->exp_len, &random);
251 rng->destroy(rng);
252
253 if (params->exp_len == this->p_len)
254 {
255 /* achieve bitsof(p)-1 by setting MSB to 0 */
256 *random.ptr &= 0x7F;
257 }
258 mpz_import(this->xa, random.len, 1, 1, 1, 0, random.ptr);
259 chunk_free(&random);
260 DBG2(DBG_LIB, "size of DH secret exponent: %u bits",
261 mpz_sizeinbase(this->xa, 2));
262
263 mpz_powm(this->ya, this->g, this->xa, this->p);
264
265 return &this->public;
266 }
267