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