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 "pkcs11/pkcs11.h"
24 :
25 : #include "gkm-attributes.h"
26 : #include "gkm-credential.h"
27 : #define DEBUG_FLAG GKM_DEBUG_OBJECT
28 : #include "gkm-debug.h"
29 : #include "gkm-factory.h"
30 : #include "gkm-private-xsa-key.h"
31 : #include "gkm-data-der.h"
32 : #include "gkm-session.h"
33 : #include "gkm-transaction.h"
34 : #include "gkm-util.h"
35 :
36 : struct _GkmPrivateXsaKeyPrivate {
37 : GkmSexp *sexp;
38 : };
39 :
40 111 : G_DEFINE_TYPE_WITH_PRIVATE (GkmPrivateXsaKey, gkm_private_xsa_key, GKM_TYPE_SEXP_KEY);
41 :
42 : /* -----------------------------------------------------------------------------
43 : * INTERNAL
44 : */
45 :
46 : /* Can't be defined in gkm_attributes, because it does not know anything about
47 : * DER encoding nor OIDs
48 : */
49 : gboolean
50 1 : gkm_attributes_find_ecc_oid (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, GQuark *value)
51 : {
52 : GBytes *bytes;
53 : CK_ATTRIBUTE_PTR attr;
54 : GQuark oid;
55 :
56 1 : g_return_val_if_fail (attrs || !n_attrs, FALSE);
57 :
58 1 : attr = gkm_attributes_find (attrs, n_attrs, CKA_EC_PARAMS);
59 1 : if (attr == NULL)
60 0 : return FALSE;
61 :
62 1 : bytes = g_bytes_new (attr->pValue, attr->ulValueLen);
63 1 : g_return_val_if_fail (bytes != NULL, FALSE);
64 :
65 1 : oid = gkm_data_der_oid_from_ec_params (bytes);
66 1 : g_return_val_if_fail (oid != 0, FALSE);
67 1 : *value = oid;
68 :
69 1 : g_bytes_unref (bytes);
70 1 : return TRUE;
71 : }
72 :
73 : gboolean
74 1 : gkm_attributes_find_ecc_q (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs,
75 : CK_ATTRIBUTE_TYPE type, GBytes **value)
76 : {
77 : GBytes *data;
78 : gboolean rv;
79 :
80 1 : rv = gkm_attributes_find_bytes (attrs, n_attrs, type, &data);
81 1 : g_return_val_if_fail (rv, FALSE);
82 :
83 1 : rv = gkm_data_der_decode_ecdsa_q (data, value);
84 :
85 1 : return rv;
86 : }
87 :
88 : static CK_RV
89 1 : create_rsa_private (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, gcry_sexp_t *skey)
90 : {
91 : gcry_error_t gcry;
92 1 : gcry_mpi_t n = NULL;
93 1 : gcry_mpi_t e = NULL;
94 1 : gcry_mpi_t d = NULL;
95 1 : gcry_mpi_t p = NULL;
96 1 : gcry_mpi_t q = NULL;
97 1 : gcry_mpi_t u = NULL;
98 : CK_RV ret;
99 :
100 2 : if (!gkm_attributes_find_mpi (attrs, n_attrs, CKA_MODULUS, &n) ||
101 2 : !gkm_attributes_find_mpi (attrs, n_attrs, CKA_PUBLIC_EXPONENT, &e) ||
102 2 : !gkm_attributes_find_mpi (attrs, n_attrs, CKA_PRIVATE_EXPONENT, &d) ||
103 2 : !gkm_attributes_find_mpi (attrs, n_attrs, CKA_PRIME_1, &p) ||
104 1 : !gkm_attributes_find_mpi (attrs, n_attrs, CKA_PRIME_2, &q)) {
105 0 : ret = CKR_TEMPLATE_INCOMPLETE;
106 0 : goto done;
107 : }
108 :
109 : /* Fix up the incoming key so gcrypt likes it */
110 1 : if (gcry_mpi_cmp (p, q) > 0)
111 1 : gcry_mpi_swap (p, q);
112 :
113 : /* Compute U. */
114 1 : u = gcry_mpi_snew (gcry_mpi_get_nbits (n));
115 1 : gcry_mpi_invm (u, p, q);
116 :
117 1 : gcry = gcry_sexp_build (skey, NULL,
118 : "(private-key (rsa (n %m) (e %m) (d %m) (p %m) (q %m) (u %m)))",
119 : n, e, d, p, q, u);
120 :
121 1 : if (gcry != 0) {
122 0 : g_message ("couldn't create RSA key from passed attributes: %s", gcry_strerror (gcry));
123 0 : ret = CKR_FUNCTION_FAILED;
124 0 : goto done;
125 : }
126 :
127 1 : gkm_attributes_consume (attrs, n_attrs, CKA_MODULUS, CKA_PUBLIC_EXPONENT,
128 : CKA_PRIVATE_EXPONENT, CKA_PRIME_1, CKA_PRIME_2,
129 : CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT, G_MAXULONG);
130 1 : ret = CKR_OK;
131 :
132 1 : done:
133 1 : gcry_mpi_release (n);
134 1 : gcry_mpi_release (e);
135 1 : gcry_mpi_release (d);
136 1 : gcry_mpi_release (p);
137 1 : gcry_mpi_release (q);
138 1 : gcry_mpi_release (u);
139 1 : return ret;
140 : }
141 :
142 : static CK_RV
143 0 : create_dsa_private (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, gcry_sexp_t *skey)
144 : {
145 : gcry_error_t gcry;
146 0 : gcry_mpi_t p = NULL;
147 0 : gcry_mpi_t q = NULL;
148 0 : gcry_mpi_t g = NULL;
149 0 : gcry_mpi_t y = NULL;
150 0 : gcry_mpi_t value = NULL;
151 : CK_RV ret;
152 :
153 0 : if (!gkm_attributes_find_mpi (attrs, n_attrs, CKA_PRIME, &p) ||
154 0 : !gkm_attributes_find_mpi (attrs, n_attrs, CKA_SUBPRIME, &q) ||
155 0 : !gkm_attributes_find_mpi (attrs, n_attrs, CKA_BASE, &g) ||
156 0 : !gkm_attributes_find_mpi (attrs, n_attrs, CKA_VALUE, &value)) {
157 0 : ret = CKR_TEMPLATE_INCOMPLETE;
158 0 : goto done;
159 : }
160 :
161 : /* Calculate the public part from the private */
162 0 : y = gcry_mpi_snew (gcry_mpi_get_nbits (value));
163 0 : g_return_val_if_fail (y, CKR_GENERAL_ERROR);
164 0 : gcry_mpi_powm (y, g, value, p);
165 :
166 0 : gcry = gcry_sexp_build (skey, NULL,
167 : "(private-key (dsa (p %m) (q %m) (g %m) (y %m) (x %m)))",
168 : p, q, g, y, value);
169 :
170 0 : if (gcry != 0) {
171 0 : g_message ("couldn't create DSA key from passed attributes: %s", gcry_strerror (gcry));
172 0 : ret = CKR_FUNCTION_FAILED;
173 0 : goto done;
174 : }
175 :
176 0 : gkm_attributes_consume (attrs, n_attrs, CKA_PRIME, CKA_SUBPRIME,
177 : CKA_BASE, CKA_VALUE, G_MAXULONG);
178 0 : ret = CKR_OK;
179 :
180 0 : done:
181 0 : gcry_mpi_release (p);
182 0 : gcry_mpi_release (q);
183 0 : gcry_mpi_release (g);
184 0 : gcry_mpi_release (y);
185 0 : gcry_mpi_release (value);
186 0 : return ret;
187 : }
188 :
189 : static CK_RV
190 0 : create_ecdsa_private (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, gcry_sexp_t *skey)
191 : {
192 : gcry_error_t gcry;
193 0 : gcry_mpi_t d = NULL;
194 : const gchar *curve_name, *q_data;
195 0 : GBytes *q = NULL;
196 : gsize q_size;
197 : GQuark oid;
198 : CK_RV ret;
199 :
200 0 : if (!gkm_attributes_find_ecc_oid (attrs, n_attrs, &oid) ||
201 0 : !gkm_attributes_find_ecc_q (attrs, n_attrs, CKA_EC_POINT, &q) ||
202 0 : !gkm_attributes_find_mpi (attrs, n_attrs, CKA_VALUE, &d)) {
203 0 : ret = CKR_TEMPLATE_INCOMPLETE;
204 0 : goto done;
205 : }
206 :
207 0 : curve_name = gkm_data_der_oid_to_curve (oid);
208 0 : if (curve_name == NULL) {
209 0 : ret = CKR_FUNCTION_FAILED;
210 0 : goto done;
211 : }
212 :
213 0 : q_data = g_bytes_get_data (q, &q_size);
214 :
215 0 : gcry = gcry_sexp_build (skey, NULL,
216 : "(private-key (ecdsa (curve %s) (q %b) (d %m)))",
217 : curve_name, q_size, q_data, d);
218 :
219 0 : if (gcry != 0) {
220 0 : g_message ("couldn't create ECDSA key from passed attributes: %s", gcry_strerror (gcry));
221 0 : ret = CKR_FUNCTION_FAILED;
222 0 : goto done;
223 : }
224 :
225 0 : gkm_attributes_consume (attrs, n_attrs, CKA_EC_PARAMS,
226 : CKA_EC_POINT, CKA_VALUE, G_MAXULONG);
227 0 : ret = CKR_OK;
228 :
229 0 : done:
230 0 : g_bytes_unref (q);
231 0 : gcry_mpi_release (d);
232 0 : return ret;
233 : }
234 :
235 : static GkmObject*
236 0 : factory_create_private_xsa_key (GkmSession *session, GkmTransaction *transaction,
237 : CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs)
238 : {
239 : GkmPrivateXsaKey *key;
240 : GkmSexp *sexp;
241 :
242 0 : g_return_val_if_fail (GKM_IS_TRANSACTION (transaction), NULL);
243 0 : g_return_val_if_fail (attrs || !n_attrs, NULL);
244 :
245 0 : sexp = gkm_private_xsa_key_create_sexp (session, transaction, attrs, n_attrs);
246 0 : if (sexp == NULL)
247 0 : return NULL;
248 :
249 0 : key = g_object_new (GKM_TYPE_PRIVATE_XSA_KEY, "base-sexp", sexp,
250 : "module", gkm_session_get_module (session),
251 : "manager", gkm_manager_for_template (attrs, n_attrs, session),
252 : NULL);
253 0 : key->pv->sexp = sexp;
254 :
255 0 : gkm_session_complete_object_creation (session, transaction, GKM_OBJECT (key),
256 : TRUE, attrs, n_attrs);
257 0 : return GKM_OBJECT (key);
258 : }
259 :
260 : static gboolean
261 0 : acquire_from_credential (GkmCredential *cred, GkmObject *object, gpointer user_data)
262 : {
263 0 : GkmSexp **result = user_data;
264 :
265 0 : g_assert (result);
266 0 : g_assert (!*result);
267 :
268 : /* The sexp we stored on the credential */
269 0 : *result = gkm_credential_pop_data (cred, GKM_BOXED_SEXP);
270 0 : if (*result != NULL)
271 0 : return TRUE;
272 :
273 0 : return FALSE;
274 : }
275 :
276 : static gboolean
277 0 : have_from_credential (GkmCredential *cred, GkmObject *object, gpointer unused)
278 : {
279 : /* The sexp we stored on the credential */
280 0 : return gkm_credential_peek_data (cred, GKM_BOXED_SEXP) ? TRUE : FALSE;
281 : }
282 :
283 : /* -----------------------------------------------------------------------------
284 : * PRIVATE_XSA_KEY
285 : */
286 :
287 : static CK_RV
288 39 : gkm_private_xsa_key_real_get_attribute (GkmObject *base, GkmSession *session, CK_ATTRIBUTE* attr)
289 : {
290 39 : GkmPrivateXsaKey *self = GKM_PRIVATE_XSA_KEY (base);
291 : gboolean have;
292 : gint algorithm;
293 :
294 39 : switch (attr->type) {
295 5 : case CKA_CLASS:
296 5 : return gkm_attribute_set_ulong (attr, CKO_PRIVATE_KEY);
297 :
298 5 : case CKA_PRIVATE:
299 5 : return gkm_attribute_set_bool (attr, TRUE);
300 :
301 1 : case CKA_SENSITIVE:
302 1 : return gkm_attribute_set_bool (attr, TRUE);
303 :
304 1 : case CKA_DECRYPT:
305 1 : algorithm = gkm_sexp_key_get_algorithm (GKM_SEXP_KEY (self));
306 1 : return gkm_attribute_set_bool (attr, algorithm == GCRY_PK_RSA);
307 :
308 4 : case CKA_SIGN:
309 4 : return gkm_attribute_set_bool (attr, TRUE);
310 :
311 3 : case CKA_SIGN_RECOVER:
312 3 : return gkm_attribute_set_bool (attr, FALSE);
313 :
314 0 : case CKA_UNWRAP:
315 0 : return gkm_attribute_set_bool (attr, FALSE);
316 :
317 0 : case CKA_EXTRACTABLE:
318 0 : return gkm_attribute_set_bool (attr, FALSE);
319 :
320 0 : case CKA_ALWAYS_SENSITIVE:
321 0 : return gkm_attribute_set_bool (attr, FALSE);
322 :
323 0 : case CKA_NEVER_EXTRACTABLE:
324 0 : return gkm_attribute_set_bool (attr, FALSE);
325 :
326 0 : case CKA_WRAP_WITH_TRUSTED:
327 0 : return gkm_attribute_set_bool (attr, FALSE);
328 :
329 0 : case CKA_UNWRAP_TEMPLATE:
330 0 : gkm_debug ("CKR_ATTRIBUTE_TYPE_INVALID: no CKA_UNWRAP_TEMPLATE on key");
331 0 : return CKR_ATTRIBUTE_TYPE_INVALID;
332 :
333 0 : case CKA_ALWAYS_AUTHENTICATE:
334 0 : have = self->pv->sexp ? TRUE : FALSE;
335 0 : if (!have && session)
336 0 : have = gkm_credential_for_each (session, base, have_from_credential, NULL);
337 0 : return gkm_attribute_set_bool (attr, !have);
338 :
339 1 : case CKA_MODULUS:
340 1 : return gkm_sexp_key_set_part (GKM_SEXP_KEY (self), GCRY_PK_RSA, "n", attr);
341 :
342 1 : case CKA_PUBLIC_EXPONENT:
343 1 : return gkm_sexp_key_set_part (GKM_SEXP_KEY (self), GCRY_PK_RSA, "e", attr);
344 :
345 : /* RSA private key parts */
346 1 : case CKA_PRIVATE_EXPONENT:
347 : case CKA_PRIME_1:
348 : case CKA_PRIME_2:
349 : case CKA_EXPONENT_1:
350 : case CKA_EXPONENT_2:
351 : case CKA_COEFFICIENT:
352 1 : return CKR_ATTRIBUTE_SENSITIVE;
353 :
354 1 : case CKA_PRIME:
355 1 : return gkm_sexp_key_set_part (GKM_SEXP_KEY (self), GCRY_PK_DSA, "p", attr);
356 :
357 1 : case CKA_SUBPRIME:
358 1 : return gkm_sexp_key_set_part (GKM_SEXP_KEY (self), GCRY_PK_DSA, "q", attr);
359 :
360 1 : case CKA_BASE:
361 1 : return gkm_sexp_key_set_part (GKM_SEXP_KEY (self), GCRY_PK_DSA, "g", attr);
362 :
363 1 : case CKA_EC_POINT:
364 1 : return gkm_sexp_key_set_ec_q (GKM_SEXP_KEY (self), GCRY_PK_ECC, attr);
365 :
366 1 : case CKA_EC_PARAMS:
367 1 : return gkm_sexp_key_set_ec_params (GKM_SEXP_KEY (self), GCRY_PK_ECC, attr);
368 :
369 : /* (EC)DSA private parts */
370 2 : case CKA_VALUE:
371 2 : return CKR_ATTRIBUTE_SENSITIVE;
372 : };
373 :
374 10 : return GKM_OBJECT_CLASS (gkm_private_xsa_key_parent_class)->get_attribute (base, session, attr);
375 : }
376 :
377 : static GkmSexp*
378 0 : gkm_private_xsa_key_real_acquire_crypto_sexp (GkmSexpKey *base, GkmSession *session)
379 : {
380 0 : GkmPrivateXsaKey *self = GKM_PRIVATE_XSA_KEY (base);
381 0 : GkmSexp *sexp = NULL;
382 :
383 : /* We have an unlocked private key here */
384 0 : if (self->pv->sexp)
385 0 : sexp = gkm_sexp_ref (self->pv->sexp);
386 :
387 : /* Find an credential, with an unlocked copy */
388 : else
389 0 : gkm_credential_for_each (session, GKM_OBJECT (self),
390 : acquire_from_credential, &sexp);
391 :
392 0 : return sexp;
393 : }
394 :
395 : static void
396 15 : gkm_private_xsa_key_init (GkmPrivateXsaKey *self)
397 : {
398 15 : self->pv = gkm_private_xsa_key_get_instance_private (self);
399 15 : }
400 :
401 : static void
402 16 : gkm_private_xsa_key_dispose (GObject *obj)
403 : {
404 16 : GkmPrivateXsaKey *self = GKM_PRIVATE_XSA_KEY (obj);
405 :
406 16 : if (self->pv->sexp)
407 3 : gkm_sexp_unref (self->pv->sexp);
408 16 : self->pv->sexp = NULL;
409 :
410 16 : G_OBJECT_CLASS (gkm_private_xsa_key_parent_class)->dispose (obj);
411 16 : }
412 :
413 : static void
414 15 : gkm_private_xsa_key_finalize (GObject *obj)
415 : {
416 15 : GkmPrivateXsaKey *self = GKM_PRIVATE_XSA_KEY (obj);
417 :
418 15 : g_assert (self->pv->sexp == NULL);
419 :
420 15 : G_OBJECT_CLASS (gkm_private_xsa_key_parent_class)->finalize (obj);
421 15 : }
422 :
423 : static void
424 4 : gkm_private_xsa_key_class_init (GkmPrivateXsaKeyClass *klass)
425 : {
426 4 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
427 4 : GkmObjectClass *gkm_class = GKM_OBJECT_CLASS (klass);
428 4 : GkmSexpKeyClass *key_class = GKM_SEXP_KEY_CLASS (klass);
429 :
430 4 : gobject_class->dispose = gkm_private_xsa_key_dispose;
431 4 : gobject_class->finalize = gkm_private_xsa_key_finalize;
432 :
433 4 : gkm_class->get_attribute = gkm_private_xsa_key_real_get_attribute;
434 :
435 4 : key_class->acquire_crypto_sexp = gkm_private_xsa_key_real_acquire_crypto_sexp;
436 4 : }
437 :
438 : /* -----------------------------------------------------------------------------
439 : * PUBLIC
440 : */
441 :
442 : void
443 3 : gkm_private_xsa_key_set_unlocked_private (GkmPrivateXsaKey *self, GkmSexp *sexp)
444 : {
445 3 : g_return_if_fail (GKM_IS_PRIVATE_XSA_KEY (self));
446 3 : g_return_if_fail (sexp);
447 :
448 3 : if (sexp)
449 3 : gkm_sexp_ref (sexp);
450 3 : if (self->pv->sexp)
451 0 : gkm_sexp_unref (self->pv->sexp);
452 3 : self->pv->sexp = sexp;
453 : }
454 :
455 : void
456 3 : gkm_private_xsa_key_set_locked_private (GkmPrivateXsaKey *self, GkmCredential *cred,
457 : GkmSexp *sexp)
458 : {
459 3 : g_return_if_fail (GKM_IS_PRIVATE_XSA_KEY (self));
460 3 : g_return_if_fail (GKM_IS_CREDENTIAL (cred));
461 3 : g_return_if_fail (gkm_credential_get_object (cred) == GKM_OBJECT (self));
462 3 : gkm_credential_set_data (cred, GKM_BOXED_SEXP, sexp);
463 : }
464 :
465 : GkmSexp*
466 1 : gkm_private_xsa_key_create_sexp (GkmSession *session, GkmTransaction *transaction,
467 : CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs)
468 : {
469 : CK_KEY_TYPE type;
470 : gcry_sexp_t sexp;
471 : CK_RV ret;
472 :
473 1 : g_return_val_if_fail (GKM_IS_TRANSACTION (transaction), NULL);
474 1 : g_return_val_if_fail (attrs || !n_attrs, NULL);
475 :
476 1 : if (!gkm_attributes_find_ulong (attrs, n_attrs, CKA_KEY_TYPE, &type)) {
477 0 : gkm_transaction_fail (transaction, CKR_TEMPLATE_INCOMPLETE);
478 0 : return NULL;
479 : }
480 :
481 1 : gkm_attributes_consume (attrs, n_attrs, CKA_KEY_TYPE, CKA_CLASS, G_MAXULONG);
482 :
483 1 : switch (type) {
484 1 : case CKK_RSA:
485 1 : ret = create_rsa_private (attrs, n_attrs, &sexp);
486 1 : break;
487 0 : case CKK_DSA:
488 0 : ret = create_dsa_private (attrs, n_attrs, &sexp);
489 0 : break;
490 0 : case CKK_EC:
491 0 : ret = create_ecdsa_private (attrs, n_attrs, &sexp);
492 0 : break;
493 0 : default:
494 0 : ret = CKR_ATTRIBUTE_VALUE_INVALID;
495 0 : break;
496 : };
497 :
498 1 : if (ret != CKR_OK) {
499 0 : gkm_transaction_fail (transaction, ret);
500 0 : return NULL;
501 : }
502 :
503 1 : g_return_val_if_fail (sexp, NULL);
504 1 : return gkm_sexp_new (sexp);
505 : }
506 :
507 : GkmFactory*
508 287 : gkm_private_xsa_key_get_factory (void)
509 : {
510 : static CK_OBJECT_CLASS klass = CKO_PRIVATE_KEY;
511 :
512 : static CK_ATTRIBUTE attributes[] = {
513 : { CKA_CLASS, &klass, sizeof (klass) }
514 : };
515 :
516 : static GkmFactory factory = {
517 : attributes,
518 : G_N_ELEMENTS (attributes),
519 : factory_create_private_xsa_key
520 : };
521 :
522 287 : return &factory;
523 : }
|