299865da2e09264bfbac8c95720a2f458dbc97df
[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, void,
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 (this->yb)
83 {
84 gcry_mpi_release(this->yb);
85 this->yb = NULL;
86 }
87 err = gcry_mpi_scan(&this->yb, GCRYMPI_FMT_USG, value.ptr, value.len, NULL);
88 if (err)
89 {
90 DBG1(DBG_LIB, "importing mpi yb failed: %s", gpg_strerror(err));
91 return;
92 }
93
94 p_min_1 = gcry_mpi_new(this->p_len * 8);
95 gcry_mpi_sub_ui(p_min_1, this->p, 1);
96
97 /* check public value:
98 * 1. 0 or 1 is invalid as 0^a = 0 and 1^a = 1
99 * 2. a public value larger or equal the modulus is invalid */
100 if (gcry_mpi_cmp_ui(this->yb, 1) > 0 &&
101 gcry_mpi_cmp(this->yb, p_min_1) < 0)
102 {
103 if (!this->zz)
104 {
105 this->zz = gcry_mpi_new(this->p_len * 8);
106 }
107 gcry_mpi_powm(this->zz, this->yb, this->xa, this->p);
108 }
109 else
110 {
111 DBG1(DBG_LIB, "public DH value verification failed:"
112 " y < 2 || y > p - 1 ");
113 }
114 gcry_mpi_release(p_min_1);
115 }
116
117 /**
118 * export a gcry_mpi to an allocated chunk of len bytes
119 */
120 static chunk_t export_mpi(gcry_mpi_t value, size_t len)
121 {
122 chunk_t chunk;
123 size_t written;
124
125 chunk = chunk_alloc(len);
126 gcry_mpi_print(GCRYMPI_FMT_USG, chunk.ptr, chunk.len, &written, value);
127 if (written < len)
128 { /* right-align number of written bytes in chunk */
129 memmove(chunk.ptr + (len - written), chunk.ptr, written);
130 memset(chunk.ptr, 0, len - written);
131 }
132 return chunk;
133 }
134
135 METHOD(diffie_hellman_t, get_my_public_value, void,
136 private_gcrypt_dh_t *this, chunk_t *value)
137 {
138 *value = export_mpi(this->ya, this->p_len);
139 }
140
141 METHOD(diffie_hellman_t, get_shared_secret, status_t,
142 private_gcrypt_dh_t *this, chunk_t *secret)
143 {
144 if (!this->zz)
145 {
146 return FAILED;
147 }
148 *secret = export_mpi(this->zz, this->p_len);
149 return SUCCESS;
150 }
151
152 METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t,
153 private_gcrypt_dh_t *this)
154 {
155 return this->group;
156 }
157
158 METHOD(diffie_hellman_t, destroy, void,
159 private_gcrypt_dh_t *this)
160 {
161 gcry_mpi_release(this->p);
162 gcry_mpi_release(this->xa);
163 gcry_mpi_release(this->ya);
164 gcry_mpi_release(this->g);
165 gcry_mpi_release(this->yb);
166 gcry_mpi_release(this->zz);
167 free(this);
168 }
169
170 /*
171 * Generic internal constructor
172 */
173 gcrypt_dh_t *create_generic(diffie_hellman_group_t group, size_t exp_len,
174 chunk_t g, chunk_t p)
175 {
176 private_gcrypt_dh_t *this;
177 gcry_error_t err;
178 chunk_t random;
179 rng_t *rng;
180
181 INIT(this,
182 .public = {
183 .dh = {
184 .get_shared_secret = _get_shared_secret,
185 .set_other_public_value = _set_other_public_value,
186 .get_my_public_value = _get_my_public_value,
187 .get_dh_group = _get_dh_group,
188 .destroy = _destroy,
189 },
190 },
191 .group = group,
192 .p_len = p.len,
193 );
194 err = gcry_mpi_scan(&this->p, GCRYMPI_FMT_USG, p.ptr, p.len, NULL);
195 if (err)
196 {
197 DBG1(DBG_LIB, "importing mpi modulus failed: %s", gpg_strerror(err));
198 free(this);
199 return NULL;
200 }
201 err = gcry_mpi_scan(&this->g, GCRYMPI_FMT_USG, g.ptr, g.len, NULL);
202 if (err)
203 {
204 DBG1(DBG_LIB, "importing mpi generator failed: %s", gpg_strerror(err));
205 gcry_mpi_release(this->p);
206 free(this);
207 return NULL;
208 }
209
210 rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
211 if (rng && rng->allocate_bytes(rng, exp_len, &random))
212 { /* prefer external randomizer */
213 rng->destroy(rng);
214 err = gcry_mpi_scan(&this->xa, GCRYMPI_FMT_USG,
215 random.ptr, random.len, NULL);
216 chunk_clear(&random);
217 if (err)
218 {
219 DBG1(DBG_LIB, "importing mpi xa failed: %s", gpg_strerror(err));
220 gcry_mpi_release(this->p);
221 gcry_mpi_release(this->g);
222 free(this);
223 return NULL;
224 }
225 }
226 else
227 { /* fallback to gcrypt internal randomizer, shouldn't ever happen */
228 DESTROY_IF(rng);
229 this->xa = gcry_mpi_new(exp_len * 8);
230 gcry_mpi_randomize(this->xa, exp_len * 8, GCRY_STRONG_RANDOM);
231 }
232 if (exp_len == this->p_len)
233 {
234 /* achieve bitsof(p)-1 by setting MSB to 0 */
235 gcry_mpi_clear_bit(this->xa, exp_len * 8 - 1);
236 }
237
238 this->ya = gcry_mpi_new(this->p_len * 8);
239
240 gcry_mpi_powm(this->ya, this->g, this->xa, this->p);
241
242 return &this->public;
243 }
244
245
246 /*
247 * Described in header.
248 */
249 gcrypt_dh_t *gcrypt_dh_create(diffie_hellman_group_t group)
250 {
251
252 diffie_hellman_params_t *params;
253
254 params = diffie_hellman_get_params(group);
255 if (!params)
256 {
257 return NULL;
258 }
259 return create_generic(group, params->exp_len,
260 params->generator, params->prime);
261 }
262
263 /*
264 * Described in header.
265 */
266 gcrypt_dh_t *gcrypt_dh_create_custom(diffie_hellman_group_t group,
267 chunk_t g, chunk_t p)
268 {
269 if (group == MODP_CUSTOM)
270 {
271 return create_generic(group, p.len, g, p);
272 }
273 return NULL;
274 }