Line data Source code
1 : /*
2 : * gnome-keyring
3 : *
4 : * Copyright (C) 2008 Stefan Walter
5 : *
6 : * This program is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU Lesser General Public License as
8 : * published by the Free Software Foundation; either version 2.1 of
9 : * the License, or (at your option) any later version.
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
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : * Lesser General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU Lesser General Public
17 : * License along with this program; if not, see
18 : * <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include "config.h"
22 :
23 : #include "gkm-attributes.h"
24 : #include "gkm-crypto.h"
25 : #include "gkm-dh-mechanism.h"
26 : #include "gkm-dh-private-key.h"
27 : #include "gkm-session.h"
28 : #include "gkm-transaction.h"
29 :
30 : #include "egg/egg-dh.h"
31 : #include "egg/egg-libgcrypt.h"
32 : #include "egg/egg-secure-memory.h"
33 :
34 1 : EGG_SECURE_DECLARE (dh_mechanism);
35 :
36 : static GkmObject*
37 2 : create_dh_object (GkmSession *session, GkmTransaction *transaction, CK_OBJECT_CLASS klass,
38 : CK_ATTRIBUTE_PTR value, CK_ATTRIBUTE_PTR prime, CK_ATTRIBUTE_PTR base,
39 : CK_ATTRIBUTE_PTR id, CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs)
40 : {
41 2 : CK_KEY_TYPE type = CKK_DH;
42 : CK_ATTRIBUTE attr;
43 : GkmObject *object;
44 : GArray *array;
45 :
46 2 : array = g_array_new (FALSE, TRUE, sizeof (CK_ATTRIBUTE));
47 :
48 : /* Setup the value */
49 2 : g_array_append_val (array, *value);
50 :
51 : /* Add in the DH params */
52 2 : g_array_append_val (array, *prime);
53 2 : g_array_append_val (array, *base);
54 :
55 : /* Setup the class */
56 2 : attr.type = CKA_CLASS;
57 2 : attr.pValue = &klass;
58 2 : attr.ulValueLen = sizeof (klass);
59 2 : g_array_append_val (array, attr);
60 :
61 : /* Setup the key type */
62 2 : attr.type = CKA_KEY_TYPE;
63 2 : attr.pValue = &type;
64 2 : attr.ulValueLen = sizeof (type);
65 2 : g_array_append_val (array, attr);
66 :
67 : /* All the other values */
68 2 : g_array_append_vals (array, attrs, n_attrs);
69 :
70 : /* Add in the identifier last */
71 2 : g_array_append_val (array, *id);
72 :
73 : /* Create the public key object */
74 2 : object = gkm_session_create_object_for_attributes (session, transaction,
75 2 : (CK_ATTRIBUTE_PTR)array->data, array->len);
76 :
77 2 : g_array_free (array, TRUE);
78 2 : return object;
79 : }
80 :
81 : CK_RV
82 1 : gkm_dh_mechanism_generate (GkmSession *session, CK_ATTRIBUTE_PTR pub_atts,
83 : CK_ULONG n_pub_atts, CK_ATTRIBUTE_PTR priv_atts,
84 : CK_ULONG n_priv_atts, GkmObject **pub_key,
85 : GkmObject **priv_key)
86 : {
87 1 : gcry_mpi_t prime = NULL;
88 1 : gcry_mpi_t base = NULL;
89 1 : gcry_mpi_t pub = NULL;
90 1 : gcry_mpi_t priv = NULL;
91 : gcry_error_t gcry;
92 : CK_ATTRIBUTE value, id;
93 : CK_ATTRIBUTE_PTR aprime;
94 : CK_ATTRIBUTE_PTR abase;
95 : GkmTransaction *transaction;
96 : gboolean ret;
97 : gsize length;
98 : gulong bits;
99 : CK_RV rv;
100 :
101 1 : g_return_val_if_fail (GKM_IS_SESSION (session), CKR_GENERAL_ERROR);
102 1 : g_return_val_if_fail (pub_key, CKR_GENERAL_ERROR);
103 1 : g_return_val_if_fail (priv_key, CKR_GENERAL_ERROR);
104 :
105 1 : *priv_key = NULL;
106 1 : *pub_key = NULL;
107 :
108 1 : aprime = gkm_attributes_find (pub_atts, n_pub_atts, CKA_PRIME);
109 1 : abase = gkm_attributes_find (pub_atts, n_pub_atts, CKA_BASE);
110 1 : if (!aprime || !abase)
111 0 : return CKR_TEMPLATE_INCOMPLETE;
112 :
113 1 : rv = gkm_attribute_get_mpi (aprime, &prime);
114 1 : if (rv != CKR_OK)
115 0 : return rv;
116 :
117 1 : rv = gkm_attribute_get_mpi (abase, &base);
118 1 : if (rv != CKR_OK) {
119 0 : gcry_mpi_release (prime);
120 0 : return rv;
121 : }
122 :
123 1 : if (!gkm_attributes_find_ulong (priv_atts, n_priv_atts, CKA_VALUE_BITS, &bits))
124 1 : bits = gcry_mpi_get_nbits (prime);
125 1 : gkm_attributes_consume (priv_atts, n_priv_atts, CKA_VALUE_BITS, G_MAXULONG);
126 :
127 : /* The private key must be less than or equal to prime */
128 1 : if (bits > gcry_mpi_get_nbits (prime)) {
129 0 : gcry_mpi_release (prime);
130 0 : gcry_mpi_release (base);
131 0 : return CKR_TEMPLATE_INCONSISTENT;
132 : }
133 :
134 1 : ret = egg_dh_gen_pair (prime, base, bits, &pub, &priv);
135 :
136 1 : gcry_mpi_release (prime);
137 1 : gcry_mpi_release (base);
138 :
139 1 : if (ret == FALSE)
140 0 : return CKR_FUNCTION_FAILED;
141 :
142 : /* Write the public key out to raw data */
143 1 : value.type = CKA_VALUE;
144 1 : gcry = gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &length, pub);
145 1 : g_return_val_if_fail (gcry == 0, CKR_GENERAL_ERROR);
146 1 : value.pValue = g_malloc (length);
147 1 : gcry = gcry_mpi_print (GCRYMPI_FMT_USG, value.pValue, length, &length, pub);
148 1 : g_return_val_if_fail (gcry == 0, CKR_GENERAL_ERROR);
149 1 : value.ulValueLen = length;
150 :
151 : /* Create an identifier */
152 1 : id.type = CKA_ID;
153 1 : if (value.ulValueLen < 16) {
154 0 : id.ulValueLen = value.ulValueLen;
155 0 : id.pValue = g_memdup (value.pValue, value.ulValueLen);
156 : } else {
157 1 : id.ulValueLen = 16;
158 1 : id.pValue = g_memdup ((guchar*)value.pValue + (value.ulValueLen - 16), id.ulValueLen);
159 : }
160 :
161 1 : transaction = gkm_transaction_new ();
162 :
163 1 : *pub_key = create_dh_object (session, transaction, CKO_PUBLIC_KEY, &value,
164 : aprime, abase, &id, pub_atts, n_pub_atts);
165 1 : g_free (value.pValue);
166 :
167 1 : if (!gkm_transaction_get_failed (transaction)) {
168 :
169 : /* Write the private key out to raw data */
170 1 : value.type = CKA_VALUE;
171 1 : gcry = gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &length, priv);
172 1 : g_return_val_if_fail (gcry == 0, CKR_GENERAL_ERROR);
173 1 : value.pValue = egg_secure_alloc (length);
174 1 : gcry = gcry_mpi_print (GCRYMPI_FMT_USG, value.pValue, length, &length, priv);
175 1 : g_return_val_if_fail (gcry == 0, CKR_GENERAL_ERROR);
176 1 : value.ulValueLen = length;
177 :
178 1 : *priv_key = create_dh_object (session, transaction, CKO_PRIVATE_KEY, &value,
179 : aprime, abase, &id, priv_atts, n_priv_atts);
180 1 : egg_secure_clear (value.pValue, value.ulValueLen);
181 1 : egg_secure_free (value.pValue);
182 : }
183 :
184 1 : g_free (id.pValue);
185 :
186 1 : gkm_transaction_complete (transaction);
187 1 : if (gkm_transaction_get_failed (transaction)) {
188 0 : if (*pub_key)
189 0 : g_object_unref (*pub_key);
190 0 : if (*priv_key)
191 0 : g_object_unref (*priv_key);
192 0 : *priv_key = *pub_key = NULL;
193 : }
194 :
195 1 : rv = gkm_transaction_get_result (transaction);
196 1 : g_object_unref (transaction);
197 :
198 1 : gkm_attributes_consume (pub_atts, n_pub_atts, CKA_PRIME, CKA_BASE, G_MAXULONG);
199 :
200 1 : return rv;
201 : }
202 :
203 : CK_RV
204 1 : gkm_dh_mechanism_derive (GkmSession *session, CK_MECHANISM_PTR mech, GkmObject *base,
205 : CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, GkmObject **derived)
206 : {
207 1 : gcry_mpi_t peer = NULL;
208 : gcry_mpi_t prime;
209 : gcry_mpi_t priv;
210 : gcry_error_t gcry;
211 : CK_ATTRIBUTE attr;
212 : GArray *array;
213 1 : gsize n_actual = 0;
214 1 : CK_ULONG n_value = 0;
215 : guchar *value;
216 : GkmTransaction *transaction;
217 : CK_KEY_TYPE type;
218 :
219 1 : g_return_val_if_fail (GKM_IS_DH_PRIVATE_KEY (base), CKR_GENERAL_ERROR);
220 :
221 1 : if (mech->ulParameterLen && mech->pParameter) {
222 1 : gcry = gcry_mpi_scan (&peer, GCRYMPI_FMT_USG, mech->pParameter,
223 : mech->ulParameterLen, NULL);
224 1 : if (gcry != 0)
225 0 : peer = NULL;
226 : }
227 :
228 1 : if (peer == NULL)
229 0 : return CKR_MECHANISM_PARAM_INVALID;
230 :
231 1 : prime = gkm_dh_key_get_prime (GKM_DH_KEY (base));
232 1 : priv = gkm_dh_private_key_get_value (GKM_DH_PRIVATE_KEY (base));
233 :
234 : /* What length should we truncate to? */
235 1 : if (!gkm_attributes_find_ulong (attrs, n_attrs, CKA_VALUE_LEN, &n_value)) {
236 1 : if (gkm_attributes_find_ulong (attrs, n_attrs, CKA_KEY_TYPE, &type))
237 1 : n_value = gkm_crypto_secret_key_length (type);
238 : }
239 :
240 : /* Default to full length of the DH prime */
241 1 : if (n_value == 0)
242 1 : n_value = (gcry_mpi_get_nbits (prime) + 7) / 8;
243 :
244 1 : value = egg_dh_gen_secret (peer, priv, prime, &n_actual);
245 1 : gcry_mpi_release (peer);
246 :
247 1 : if (value == NULL)
248 0 : return CKR_FUNCTION_FAILED;
249 :
250 : /* Now setup the attributes with our new value */
251 1 : array = g_array_new (FALSE, FALSE, sizeof (CK_ATTRIBUTE));
252 :
253 : /* Prepend the value */
254 1 : attr.type = CKA_VALUE;
255 1 : attr.ulValueLen = n_value;
256 :
257 : /* Is it too long, move to the front and truncate */
258 1 : if (n_actual > n_value) {
259 0 : attr.pValue = value + (n_actual - n_value);
260 :
261 : /* If it's too short, expand with zeros */
262 1 : } else if (n_actual < n_value) {
263 0 : value = egg_secure_realloc (value, n_value);
264 0 : memmove (value + (n_value - n_actual), value, n_actual);
265 0 : memset (value, 0, (n_value - n_actual));
266 0 : attr.pValue = value;
267 :
268 : /* It's just right */
269 : } else {
270 1 : attr.pValue = value;
271 : }
272 :
273 1 : g_array_append_val (array, attr);
274 :
275 : /* Add the remainder of the attributes */
276 1 : g_array_append_vals (array, attrs, n_attrs);
277 :
278 1 : transaction = gkm_transaction_new ();
279 :
280 : /* Now create an object with these attributes */
281 2 : *derived = gkm_session_create_object_for_attributes (session, transaction,
282 1 : (CK_ATTRIBUTE_PTR)array->data, array->len);
283 :
284 1 : egg_secure_free (value);
285 1 : g_array_free (array, TRUE);
286 :
287 1 : return gkm_transaction_complete_and_unref (transaction);
288 : }
|