Store DH generator in a chunk, hide non-public data in a private struct
[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(DBG_LIB, "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(DBG_LIB, "public DH value verification failed:"
114 " y < 2 || y > p - 1 ");
115 }
116 gcry_mpi_release(p_min_1);
117 }
118
119 /**
120 * export a gcry_mpi to an allocated chunk of len bytes
121 */
122 static chunk_t export_mpi(gcry_mpi_t value, size_t len)
123 {
124 chunk_t chunk;
125 size_t written;
126
127 chunk = chunk_alloc(len);
128 gcry_mpi_print(GCRYMPI_FMT_USG, chunk.ptr, chunk.len, &written, value);
129 if (written < len)
130 { /* right-align number of written bytes in chunk */
131 memmove(chunk.ptr + (len - written), chunk.ptr, written);
132 memset(chunk.ptr, 0, len - written);
133 }
134 return chunk;
135 }
136
137 /**
138 * Implementation of gcrypt_dh_t.get_my_public_value.
139 */
140 static void get_my_public_value(private_gcrypt_dh_t *this, chunk_t *value)
141 {
142 *value = export_mpi(this->ya, this->p_len);
143 }
144
145 /**
146 * Implementation of gcrypt_dh_t.get_shared_secret.
147 */
148 static status_t get_shared_secret(private_gcrypt_dh_t *this, chunk_t *secret)
149 {
150 if (!this->zz)
151 {
152 return FAILED;
153 }
154 *secret = export_mpi(this->zz, this->p_len);
155 return SUCCESS;
156 }
157
158 /**
159 * Implementation of gcrypt_dh_t.get_dh_group.
160 */
161 static diffie_hellman_group_t get_dh_group(private_gcrypt_dh_t *this)
162 {
163 return this->group;
164 }
165
166 /**
167 * Implementation of gcrypt_dh_t.destroy.
168 */
169 static void destroy(private_gcrypt_dh_t *this)
170 {
171 gcry_mpi_release(this->p);
172 gcry_mpi_release(this->xa);
173 gcry_mpi_release(this->ya);
174 gcry_mpi_release(this->g);
175 gcry_mpi_release(this->yb);
176 gcry_mpi_release(this->zz);
177 free(this);
178 }
179
180 /*
181 * Described in header.
182 */
183 gcrypt_dh_t *gcrypt_dh_create(diffie_hellman_group_t group)
184 {
185 private_gcrypt_dh_t *this;
186 diffie_hellman_params_t *params;
187 gcry_error_t err;
188 chunk_t random;
189 rng_t *rng;
190
191 params = diffie_hellman_get_params(group);
192 if (!params)
193 {
194 return NULL;
195 }
196
197 this = malloc_thing(private_gcrypt_dh_t);
198
199 this->public.dh.get_shared_secret = (status_t (*)(diffie_hellman_t *, chunk_t *)) get_shared_secret;
200 this->public.dh.set_other_public_value = (void (*)(diffie_hellman_t *, chunk_t )) set_other_public_value;
201 this->public.dh.get_my_public_value = (void (*)(diffie_hellman_t *, chunk_t *)) get_my_public_value;
202 this->public.dh.get_dh_group = (diffie_hellman_group_t (*)(diffie_hellman_t *)) get_dh_group;
203 this->public.dh.destroy = (void (*)(diffie_hellman_t *)) destroy;
204
205 this->group = group;
206 this->p_len = params->prime.len;
207 err = gcry_mpi_scan(&this->p, GCRYMPI_FMT_USG,
208 params->prime.ptr, params->prime.len, NULL);
209 if (err)
210 {
211 DBG1(DBG_LIB, "importing mpi modulus failed: %s", gpg_strerror(err));
212 free(this);
213 return NULL;
214 }
215 err = gcry_mpi_scan(&this->g, GCRYMPI_FMT_USG,
216 params->generator.ptr, params->generator.len, NULL);
217 if (err)
218 {
219 DBG1(DBG_LIB, "importing mpi generator failed: %s", gpg_strerror(err));
220 gcry_mpi_release(this->p);
221 free(this);
222 return NULL;
223 }
224
225 rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
226 if (rng)
227 { /* prefer external randomizer */
228 rng->allocate_bytes(rng, params->exp_len, &random);
229 rng->destroy(rng);
230 err = gcry_mpi_scan(&this->xa, GCRYMPI_FMT_USG,
231 random.ptr, random.len, NULL);
232 chunk_clear(&random);
233 if (err)
234 {
235 DBG1(DBG_LIB, "importing mpi xa failed: %s", gpg_strerror(err));
236 gcry_mpi_release(this->p);
237 gcry_mpi_release(this->g);
238 free(this);
239 return NULL;
240 }
241 }
242 else
243 { /* fallback to gcrypt internal randomizer, shouldn't ever happen */
244 this->xa = gcry_mpi_new(params->exp_len * 8);
245 gcry_mpi_randomize(this->xa, params->exp_len * 8, GCRY_STRONG_RANDOM);
246 }
247 if (params->exp_len == this->p_len)
248 {
249 /* achieve bitsof(p)-1 by setting MSB to 0 */
250 gcry_mpi_clear_bit(this->xa, params->exp_len * 8 - 1);
251 }
252
253 this->ya = gcry_mpi_new(this->p_len * 8);
254 this->yb = NULL;
255 this->zz = NULL;
256
257 gcry_mpi_powm(this->ya, this->g, this->xa, this->p);
258
259 return &this->public;
260 }
261