]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/libstrongswan/plugins/gcrypt/gcrypt_dh.c
Merge branch 'dh-test-vectors'
[thirdparty/strongswan.git] / src / libstrongswan / plugins / gcrypt / gcrypt_dh.c
1 /*
2 * Copyright (C) 2010 Tobias Brunner
3 * Copyright (C) 2009 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include <gcrypt.h>
18
19 #include "gcrypt_dh.h"
20
21 #include <utils/debug.h>
22
23 typedef struct private_gcrypt_dh_t private_gcrypt_dh_t;
24
25 /**
26 * Private data of an gcrypt_dh_t object.
27 */
28 struct private_gcrypt_dh_t {
29
30 /**
31 * Public gcrypt_dh_t interface
32 */
33 gcrypt_dh_t public;
34
35 /**
36 * Diffie Hellman group number
37 */
38 diffie_hellman_group_t group;
39
40 /*
41 * Generator value
42 */
43 gcry_mpi_t g;
44
45 /**
46 * Own private value
47 */
48 gcry_mpi_t xa;
49
50 /**
51 * Own public value
52 */
53 gcry_mpi_t ya;
54
55 /**
56 * Other public value
57 */
58 gcry_mpi_t yb;
59
60 /**
61 * Shared secret
62 */
63 gcry_mpi_t zz;
64
65 /**
66 * Modulus
67 */
68 gcry_mpi_t p;
69
70 /**
71 * Modulus length.
72 */
73 size_t p_len;
74 };
75
76 METHOD(diffie_hellman_t, set_other_public_value, bool,
77 private_gcrypt_dh_t *this, chunk_t value)
78 {
79 gcry_mpi_t p_min_1;
80 gcry_error_t err;
81
82 if (!diffie_hellman_verify_value(this->group, value))
83 {
84 return FALSE;
85 }
86
87 if (this->yb)
88 {
89 gcry_mpi_release(this->yb);
90 this->yb = NULL;
91 }
92 err = gcry_mpi_scan(&this->yb, GCRYMPI_FMT_USG, value.ptr, value.len, NULL);
93 if (err)
94 {
95 DBG1(DBG_LIB, "importing mpi yb failed: %s", gpg_strerror(err));
96 return FALSE;
97 }
98
99 p_min_1 = gcry_mpi_new(this->p_len * 8);
100 gcry_mpi_sub_ui(p_min_1, this->p, 1);
101
102 /* check public value:
103 * 1. 0 or 1 is invalid as 0^a = 0 and 1^a = 1
104 * 2. a public value larger or equal the modulus is invalid */
105 if (gcry_mpi_cmp_ui(this->yb, 1) > 0 &&
106 gcry_mpi_cmp(this->yb, p_min_1) < 0)
107 {
108 if (!this->zz)
109 {
110 this->zz = gcry_mpi_new(this->p_len * 8);
111 }
112 gcry_mpi_powm(this->zz, this->yb, this->xa, this->p);
113 }
114 else
115 {
116 DBG1(DBG_LIB, "public DH value verification failed:"
117 " y < 2 || y > p - 1 ");
118 }
119 gcry_mpi_release(p_min_1);
120 return this->zz != NULL;
121 }
122
123 /**
124 * export a gcry_mpi to an allocated chunk of len bytes
125 */
126 static chunk_t export_mpi(gcry_mpi_t value, size_t len)
127 {
128 chunk_t chunk;
129 size_t written;
130
131 chunk = chunk_alloc(len);
132 gcry_mpi_print(GCRYMPI_FMT_USG, chunk.ptr, chunk.len, &written, value);
133 if (written < len)
134 { /* right-align number of written bytes in chunk */
135 memmove(chunk.ptr + (len - written), chunk.ptr, written);
136 memset(chunk.ptr, 0, len - written);
137 }
138 return chunk;
139 }
140
141 METHOD(diffie_hellman_t, get_my_public_value, bool,
142 private_gcrypt_dh_t *this, chunk_t *value)
143 {
144 *value = export_mpi(this->ya, this->p_len);
145 return TRUE;
146 }
147
148 METHOD(diffie_hellman_t, set_private_value, bool,
149 private_gcrypt_dh_t *this, chunk_t value)
150 {
151 gcry_error_t err;
152 gcry_mpi_t xa;
153
154 err = gcry_mpi_scan(&xa, GCRYMPI_FMT_USG, value.ptr, value.len, NULL);
155 if (!err)
156 {
157 gcry_mpi_release(this->xa);
158 this->xa = xa;
159 gcry_mpi_powm(this->ya, this->g, this->xa, this->p);
160 gcry_mpi_release(this->zz);
161 this->zz = NULL;
162 }
163 return !err;
164 }
165
166 METHOD(diffie_hellman_t, get_shared_secret, bool,
167 private_gcrypt_dh_t *this, chunk_t *secret)
168 {
169 if (!this->zz)
170 {
171 return FALSE;
172 }
173 *secret = export_mpi(this->zz, this->p_len);
174 return TRUE;
175 }
176
177 METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t,
178 private_gcrypt_dh_t *this)
179 {
180 return this->group;
181 }
182
183 METHOD(diffie_hellman_t, destroy, void,
184 private_gcrypt_dh_t *this)
185 {
186 gcry_mpi_release(this->p);
187 gcry_mpi_release(this->xa);
188 gcry_mpi_release(this->ya);
189 gcry_mpi_release(this->g);
190 gcry_mpi_release(this->yb);
191 gcry_mpi_release(this->zz);
192 free(this);
193 }
194
195 /*
196 * Generic internal constructor
197 */
198 gcrypt_dh_t *create_generic(diffie_hellman_group_t group, size_t exp_len,
199 chunk_t g, chunk_t p)
200 {
201 private_gcrypt_dh_t *this;
202 gcry_error_t err;
203 chunk_t random;
204 rng_t *rng;
205
206 INIT(this,
207 .public = {
208 .dh = {
209 .get_shared_secret = _get_shared_secret,
210 .set_other_public_value = _set_other_public_value,
211 .get_my_public_value = _get_my_public_value,
212 .set_private_value = _set_private_value,
213 .get_dh_group = _get_dh_group,
214 .destroy = _destroy,
215 },
216 },
217 .group = group,
218 .p_len = p.len,
219 );
220 err = gcry_mpi_scan(&this->p, GCRYMPI_FMT_USG, p.ptr, p.len, NULL);
221 if (err)
222 {
223 DBG1(DBG_LIB, "importing mpi modulus failed: %s", gpg_strerror(err));
224 free(this);
225 return NULL;
226 }
227 err = gcry_mpi_scan(&this->g, GCRYMPI_FMT_USG, g.ptr, g.len, NULL);
228 if (err)
229 {
230 DBG1(DBG_LIB, "importing mpi generator failed: %s", gpg_strerror(err));
231 gcry_mpi_release(this->p);
232 free(this);
233 return NULL;
234 }
235
236 rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
237 if (rng && rng->allocate_bytes(rng, exp_len, &random))
238 { /* prefer external randomizer */
239 rng->destroy(rng);
240 err = gcry_mpi_scan(&this->xa, GCRYMPI_FMT_USG,
241 random.ptr, random.len, NULL);
242 chunk_clear(&random);
243 if (err)
244 {
245 DBG1(DBG_LIB, "importing mpi xa failed: %s", gpg_strerror(err));
246 gcry_mpi_release(this->p);
247 gcry_mpi_release(this->g);
248 free(this);
249 return NULL;
250 }
251 }
252 else
253 { /* fallback to gcrypt internal randomizer, shouldn't ever happen */
254 DESTROY_IF(rng);
255 this->xa = gcry_mpi_new(exp_len * 8);
256 gcry_mpi_randomize(this->xa, exp_len * 8, GCRY_STRONG_RANDOM);
257 }
258 if (exp_len == this->p_len)
259 {
260 /* achieve bitsof(p)-1 by setting MSB to 0 */
261 gcry_mpi_clear_bit(this->xa, exp_len * 8 - 1);
262 }
263
264 this->ya = gcry_mpi_new(this->p_len * 8);
265
266 gcry_mpi_powm(this->ya, this->g, this->xa, this->p);
267
268 return &this->public;
269 }
270
271
272 /*
273 * Described in header.
274 */
275 gcrypt_dh_t *gcrypt_dh_create(diffie_hellman_group_t group)
276 {
277
278 diffie_hellman_params_t *params;
279
280 params = diffie_hellman_get_params(group);
281 if (!params)
282 {
283 return NULL;
284 }
285 return create_generic(group, params->exp_len,
286 params->generator, params->prime);
287 }
288
289 /*
290 * Described in header.
291 */
292 gcrypt_dh_t *gcrypt_dh_create_custom(diffie_hellman_group_t group,
293 chunk_t g, chunk_t p)
294 {
295 if (group == MODP_CUSTOM)
296 {
297 return create_generic(group, p.len, g, p);
298 }
299 return NULL;
300 }