4dc5663f170809b3396a8c1ebb02dda39c5724a2
[strongswan.git] / src / libstrongswan / plugins / openssl / openssl_diffie_hellman.c
1 /*
2 * Copyright (C) 2008-2010 Tobias Brunner
3 * Copyright (C) 2008 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 <openssl/dh.h>
18
19 #include "openssl_diffie_hellman.h"
20
21 #include <utils/debug.h>
22
23 typedef struct private_openssl_diffie_hellman_t private_openssl_diffie_hellman_t;
24
25 /**
26 * Private data of an openssl_diffie_hellman_t object.
27 */
28 struct private_openssl_diffie_hellman_t {
29 /**
30 * Public openssl_diffie_hellman_t interface.
31 */
32 openssl_diffie_hellman_t public;
33
34 /**
35 * Diffie Hellman group number.
36 */
37 u_int16_t group;
38
39 /**
40 * Diffie Hellman object
41 */
42 DH *dh;
43
44 /**
45 * Other public value
46 */
47 BIGNUM *pub_key;
48
49 /**
50 * Shared secret
51 */
52 chunk_t shared_secret;
53
54 /**
55 * True if shared secret is computed
56 */
57 bool computed;
58 };
59
60 METHOD(diffie_hellman_t, get_my_public_value, void,
61 private_openssl_diffie_hellman_t *this, chunk_t *value)
62 {
63 *value = chunk_alloc(DH_size(this->dh));
64 memset(value->ptr, 0, value->len);
65 BN_bn2bin(this->dh->pub_key,
66 value->ptr + value->len - BN_num_bytes(this->dh->pub_key));
67 }
68
69 METHOD(diffie_hellman_t, get_shared_secret, status_t,
70 private_openssl_diffie_hellman_t *this, chunk_t *secret)
71 {
72 if (!this->computed)
73 {
74 return FAILED;
75 }
76 /* shared secret should requires a len according the DH group */
77 *secret = chunk_alloc(DH_size(this->dh));
78 memset(secret->ptr, 0, secret->len);
79 memcpy(secret->ptr + secret->len - this->shared_secret.len,
80 this->shared_secret.ptr, this->shared_secret.len);
81 return SUCCESS;
82 }
83
84
85 METHOD(diffie_hellman_t, set_other_public_value, void,
86 private_openssl_diffie_hellman_t *this, chunk_t value)
87 {
88 int len;
89
90 BN_bin2bn(value.ptr, value.len, this->pub_key);
91 chunk_clear(&this->shared_secret);
92 this->shared_secret.ptr = malloc(DH_size(this->dh));
93 memset(this->shared_secret.ptr, 0xFF, this->shared_secret.len);
94 len = DH_compute_key(this->shared_secret.ptr, this->pub_key, this->dh);
95 if (len < 0)
96 {
97 DBG1(DBG_LIB, "DH shared secret computation failed");
98 return;
99 }
100 this->shared_secret.len = len;
101 this->computed = TRUE;
102 }
103
104 METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t,
105 private_openssl_diffie_hellman_t *this)
106 {
107 return this->group;
108 }
109
110 /**
111 * Lookup the modulus in modulo table
112 */
113 static status_t set_modulus(private_openssl_diffie_hellman_t *this)
114 {
115 diffie_hellman_params_t *params = diffie_hellman_get_params(this->group);
116 if (!params)
117 {
118 return NOT_FOUND;
119 }
120 this->dh->p = BN_bin2bn(params->prime.ptr, params->prime.len, NULL);
121 this->dh->g = BN_bin2bn(params->generator.ptr, params->generator.len, NULL);
122 if (params->exp_len != params->prime.len)
123 {
124 this->dh->length = params->exp_len * 8;
125 }
126 return SUCCESS;
127 }
128
129 METHOD(diffie_hellman_t, destroy, void,
130 private_openssl_diffie_hellman_t *this)
131 {
132 BN_clear_free(this->pub_key);
133 DH_free(this->dh);
134 chunk_clear(&this->shared_secret);
135 free(this);
136 }
137
138 /*
139 * Described in header.
140 */
141 openssl_diffie_hellman_t *openssl_diffie_hellman_create(
142 diffie_hellman_group_t group, chunk_t g, chunk_t p)
143 {
144 private_openssl_diffie_hellman_t *this;
145
146 INIT(this,
147 .public = {
148 .dh = {
149 .get_shared_secret = _get_shared_secret,
150 .set_other_public_value = _set_other_public_value,
151 .get_my_public_value = _get_my_public_value,
152 .get_dh_group = _get_dh_group,
153 .destroy = _destroy,
154 },
155 },
156 );
157
158 this->dh = DH_new();
159 if (!this->dh)
160 {
161 free(this);
162 return NULL;
163 }
164
165 this->group = group;
166 this->computed = FALSE;
167 this->pub_key = BN_new();
168 this->shared_secret = chunk_empty;
169
170 if (group == MODP_CUSTOM)
171 {
172 this->dh->p = BN_bin2bn(p.ptr, p.len, NULL);
173 this->dh->g = BN_bin2bn(g.ptr, g.len, NULL);
174 }
175 else
176 {
177 /* find a modulus according to group */
178 if (set_modulus(this) != SUCCESS)
179 {
180 destroy(this);
181 return NULL;
182 }
183 }
184
185 /* generate my public and private values */
186 if (!DH_generate_key(this->dh))
187 {
188 destroy(this);
189 return NULL;
190 }
191 DBG2(DBG_LIB, "size of DH secret exponent: %d bits",
192 BN_num_bits(this->dh->priv_key));
193
194 return &this->public;
195 }