1 | /* |
2 | * OpenConnect (SSL + DTLS) VPN client |
3 | * |
4 | * Copyright © 2012 Free Software Foundation. |
5 | * Copyright © 2008-2012 Intel Corporation. |
6 | * |
7 | * Author: David Woodhouse <dwmw2@infradead.org> |
8 | * Author: Nikos Mavrogiannopoulos |
9 | * |
10 | * GnuTLS is free software; you can redistribute it and/or |
11 | * modify it under the terms of the GNU Lesser General Public License |
12 | * as published by the Free Software Foundation; either version 2.1 of |
13 | * the License, or (at your option) any later version. |
14 | * |
15 | * This library is distributed in the hope that it will be useful, but |
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 | * Lesser General Public License for more details. |
19 | * |
20 | * You should have received a copy of the GNU Lesser General Public License |
21 | * along with this program. If not, see <http://www.gnu.org/licenses/> |
22 | * |
23 | */ |
24 | |
25 | /* |
26 | * TPM code based on client-tpm.c from |
27 | * Carolin Latze <latze@angry-red-pla.net> and Tobias Soder |
28 | */ |
29 | |
30 | #include <gnutls/gnutls.h> |
31 | #include <gnutls/abstract.h> |
32 | #include <gnutls/tpm.h> |
33 | #include <gnutls_int.h> |
34 | |
35 | #ifdef HAVE_TROUSERS |
36 | |
37 | #include <gnutls_errors.h> |
38 | #include <pkcs11_int.h> |
39 | #include <x509/common.h> |
40 | #include <x509_b64.h> |
41 | #include <random.h> |
42 | #include <pin.h> |
43 | #include <c-ctype.h> |
44 | |
45 | #include <trousers/tss.h> |
46 | #include <trousers/trousers.h> |
47 | |
48 | struct tpm_ctx_st |
49 | { |
50 | TSS_HCONTEXT tpm_ctx; |
51 | TSS_HKEY tpm_key; |
52 | TSS_HPOLICY tpm_key_policy; |
53 | TSS_HKEY srk; |
54 | TSS_HPOLICY srk_policy; |
55 | }; |
56 | |
57 | struct tpm_key_list_st |
58 | { |
59 | UINT32 size; |
60 | TSS_KM_KEYINFO2 * ki; |
61 | TSS_HCONTEXT tpm_ctx; |
62 | }; |
63 | |
64 | static void tpm_close_session(struct tpm_ctx_st *s); |
65 | static int import_tpm_key (gnutls_privkey_t pkey, |
66 | const gnutls_datum_t * fdata, |
67 | gnutls_tpmkey_fmt_t format, |
68 | TSS_UUID *uuid, |
69 | TSS_FLAG storage_type, |
70 | const char *srk_password, |
71 | const char *key_password); |
72 | static int encode_tpmkey_url(char** url, const TSS_UUID* uuid, TSS_FLAG storage); |
73 | |
74 | /* TPM URL format: (draft-mavrogiannopoulos-tpmuri-01) |
75 | * |
76 | * tpmkey:file=/path/to/file |
77 | * tpmkey:uuid=7f468c16-cb7f-11e1-824d-b3a4f4b20343;storage=user |
78 | * tpmkey:uuid=7f468c16-cb7f-11e1-824d-b3a4f4b20343;storage=system |
79 | * |
80 | */ |
81 | |
82 | |
83 | static int tss_err_pwd(TSS_RESULT err, int pwd_error) |
84 | { |
85 | _gnutls_debug_log("TPM (%s) error: %s (%x)\n" , Trspi_Error_Layer(err), Trspi_Error_String(err), (unsigned int)Trspi_Error_Code(err)); |
86 | |
87 | switch(ERROR_LAYER(err)) |
88 | { |
89 | case TSS_LAYER_TPM: |
90 | switch(ERROR_CODE(err)) |
91 | { |
92 | case TPM_E_AUTHFAIL: |
93 | return pwd_error; |
94 | case TPM_E_NOSRK: |
95 | return GNUTLS_E_TPM_UNINITIALIZED; |
96 | default: |
97 | return gnutls_assert_val(GNUTLS_E_TPM_ERROR); |
98 | } |
99 | case TSS_LAYER_TCS: |
100 | case TSS_LAYER_TSP: |
101 | switch(ERROR_CODE(err)) |
102 | { |
103 | case TSS_E_COMM_FAILURE: |
104 | case TSS_E_NO_CONNECTION: |
105 | case TSS_E_CONNECTION_FAILED: |
106 | case TSS_E_CONNECTION_BROKEN: |
107 | return GNUTLS_E_TPM_SESSION_ERROR; |
108 | case TSS_E_PS_KEY_NOTFOUND: |
109 | return GNUTLS_E_TPM_KEY_NOT_FOUND; |
110 | default: |
111 | return gnutls_assert_val(GNUTLS_E_TPM_ERROR); |
112 | } |
113 | default: |
114 | return gnutls_assert_val(GNUTLS_E_TPM_ERROR); |
115 | } |
116 | } |
117 | |
118 | #define tss_err(x) tss_err_pwd(x, GNUTLS_E_TPM_SRK_PASSWORD_ERROR) |
119 | #define tss_err_key(x) tss_err_pwd(x, GNUTLS_E_TPM_KEY_PASSWORD_ERROR) |
120 | |
121 | static void |
122 | tpm_deinit_fn (gnutls_privkey_t key, void *_s) |
123 | { |
124 | struct tpm_ctx_st *s = _s; |
125 | |
126 | Tspi_Context_CloseObject (s->tpm_ctx, s->tpm_key_policy); |
127 | Tspi_Context_CloseObject (s->tpm_ctx, s->tpm_key); |
128 | |
129 | tpm_close_session(s); |
130 | gnutls_free (s); |
131 | } |
132 | |
133 | static int |
134 | tpm_sign_fn (gnutls_privkey_t key, void *_s, |
135 | const gnutls_datum_t * data, gnutls_datum_t * sig) |
136 | { |
137 | struct tpm_ctx_st *s = _s; |
138 | TSS_HHASH hash; |
139 | int err; |
140 | |
141 | _gnutls_debug_log ("TPM sign function called for %u bytes.\n" , |
142 | data->size); |
143 | |
144 | err = |
145 | Tspi_Context_CreateObject (s->tpm_ctx, |
146 | TSS_OBJECT_TYPE_HASH, TSS_HASH_OTHER, |
147 | &hash); |
148 | if (err) |
149 | { |
150 | gnutls_assert (); |
151 | _gnutls_debug_log ("Failed to create TPM hash object: %s\n" , |
152 | Trspi_Error_String (err)); |
153 | return GNUTLS_E_PK_SIGN_FAILED; |
154 | } |
155 | err = Tspi_Hash_SetHashValue (hash, data->size, data->data); |
156 | if (err) |
157 | { |
158 | gnutls_assert (); |
159 | _gnutls_debug_log ("Failed to set value in TPM hash object: %s\n" , |
160 | Trspi_Error_String (err)); |
161 | Tspi_Context_CloseObject (s->tpm_ctx, hash); |
162 | return GNUTLS_E_PK_SIGN_FAILED; |
163 | } |
164 | err = Tspi_Hash_Sign (hash, s->tpm_key, &sig->size, &sig->data); |
165 | Tspi_Context_CloseObject (s->tpm_ctx, hash); |
166 | if (err) |
167 | { |
168 | if (s->tpm_key_policy || err != TPM_E_AUTHFAIL) |
169 | _gnutls_debug_log ("TPM hash signature failed: %s\n" , |
170 | Trspi_Error_String (err)); |
171 | if (err == TPM_E_AUTHFAIL) |
172 | return GNUTLS_E_TPM_KEY_PASSWORD_ERROR; |
173 | else |
174 | return GNUTLS_E_PK_SIGN_FAILED; |
175 | } |
176 | return 0; |
177 | } |
178 | |
179 | static const unsigned char nullpass[20]; |
180 | static const gnutls_datum_t nulldata = {(void*)nullpass, 20}; |
181 | const TSS_UUID srk_uuid = TSS_UUID_SRK; |
182 | |
183 | static int tpm_pin(struct pin_info_st* pin_info, const TSS_UUID* uuid, TSS_FLAG storage, |
184 | char* pin, unsigned int pin_size, unsigned int attempts) |
185 | { |
186 | unsigned int flags = 0; |
187 | const char* label; |
188 | char* url = NULL; |
189 | int ret; |
190 | |
191 | if (attempts > 0) |
192 | flags |= GNUTLS_PIN_WRONG; |
193 | |
194 | if (uuid) |
195 | { |
196 | if (memcmp(uuid, &srk_uuid, sizeof(TSS_UUID)) == 0) |
197 | { |
198 | label = "SRK" ; |
199 | |
200 | ret = encode_tpmkey_url(&url, uuid, storage); |
201 | if (ret < 0) |
202 | return gnutls_assert_val(ret); |
203 | } |
204 | else |
205 | { |
206 | label = "TPM" ; |
207 | |
208 | ret = encode_tpmkey_url(&url, uuid, storage); |
209 | if (ret < 0) |
210 | return gnutls_assert_val(ret); |
211 | } |
212 | } |
213 | else |
214 | label = "unknown" ; |
215 | |
216 | if (pin_info && pin_info->cb) |
217 | ret = pin_info->cb(pin_info->data, attempts, url, label, flags, pin, pin_size); |
218 | else if (_gnutls_pin_func) |
219 | ret = _gnutls_pin_func(_gnutls_pin_data, attempts, url, label, flags, pin, pin_size); |
220 | else |
221 | ret = gnutls_assert_val(GNUTLS_E_TPM_KEY_PASSWORD_ERROR); /* doesn't really matter */ |
222 | |
223 | if (ret < 0) |
224 | { |
225 | gnutls_assert(); |
226 | goto cleanup; |
227 | } |
228 | |
229 | ret = 0; |
230 | cleanup: |
231 | gnutls_free(url); |
232 | return ret; |
233 | } |
234 | |
235 | |
236 | static TSS_RESULT myTspi_Policy_SetSecret(TSS_HPOLICY hPolicy, |
237 | UINT32 ulSecretLength, BYTE* rgbSecret) |
238 | { |
239 | if (rgbSecret == NULL) |
240 | { |
241 | /* Well known NULL key */ |
242 | return Tspi_Policy_SetSecret (hPolicy, |
243 | TSS_SECRET_MODE_SHA1, |
244 | sizeof (nullpass), (BYTE *) nullpass); |
245 | } |
246 | else /* key is given */ |
247 | { |
248 | return Tspi_Policy_SetSecret (hPolicy, TSS_SECRET_MODE_PLAIN, |
249 | ulSecretLength, rgbSecret); |
250 | } |
251 | } |
252 | |
253 | #define SAFE_LEN(x) (x==NULL?0:strlen(x)) |
254 | |
255 | static int tpm_open_session(struct tpm_ctx_st *s, const char* srk_password) |
256 | { |
257 | int err, ret; |
258 | |
259 | err = Tspi_Context_Create (&s->tpm_ctx); |
260 | if (err) |
261 | { |
262 | gnutls_assert (); |
263 | return tss_err(err); |
264 | } |
265 | |
266 | err = Tspi_Context_Connect (s->tpm_ctx, NULL); |
267 | if (err) |
268 | { |
269 | gnutls_assert (); |
270 | ret = tss_err(err); |
271 | goto out_tspi_ctx; |
272 | } |
273 | |
274 | err = |
275 | Tspi_Context_LoadKeyByUUID (s->tpm_ctx, TSS_PS_TYPE_SYSTEM, |
276 | srk_uuid, &s->srk); |
277 | if (err) |
278 | { |
279 | gnutls_assert (); |
280 | ret = tss_err(err); |
281 | goto out_tspi_ctx; |
282 | } |
283 | |
284 | err = Tspi_GetPolicyObject (s->srk, TSS_POLICY_USAGE, &s->srk_policy); |
285 | if (err) |
286 | { |
287 | gnutls_assert (); |
288 | ret = tss_err(err); |
289 | goto out_srk; |
290 | } |
291 | |
292 | err = myTspi_Policy_SetSecret (s->srk_policy, |
293 | SAFE_LEN (srk_password), (BYTE *) srk_password); |
294 | if (err) |
295 | { |
296 | gnutls_assert (); |
297 | ret = tss_err(err); |
298 | goto out_srkpol; |
299 | } |
300 | |
301 | return 0; |
302 | |
303 | out_srkpol: |
304 | Tspi_Context_CloseObject (s->tpm_ctx, s->srk_policy); |
305 | s->srk_policy = 0; |
306 | out_srk: |
307 | Tspi_Context_CloseObject (s->tpm_ctx, s->srk); |
308 | s->srk = 0; |
309 | out_tspi_ctx: |
310 | Tspi_Context_Close (s->tpm_ctx); |
311 | s->tpm_ctx = 0; |
312 | return ret; |
313 | |
314 | } |
315 | |
316 | static void tpm_close_session(struct tpm_ctx_st *s) |
317 | { |
318 | Tspi_Context_CloseObject (s->tpm_ctx, s->srk_policy); |
319 | s->srk_policy = 0; |
320 | Tspi_Context_CloseObject (s->tpm_ctx, s->srk); |
321 | s->srk = 0; |
322 | Tspi_Context_Close (s->tpm_ctx); |
323 | s->tpm_ctx = 0; |
324 | } |
325 | |
326 | static int |
327 | import_tpm_key_cb (gnutls_privkey_t pkey, const gnutls_datum_t * fdata, |
328 | gnutls_tpmkey_fmt_t format, TSS_UUID *uuid, |
329 | TSS_FLAG storage, const char *srk_password, |
330 | const char *key_password) |
331 | { |
332 | unsigned int attempts = 0; |
333 | char pin1[GNUTLS_PKCS11_MAX_PIN_LEN]; |
334 | char pin2[GNUTLS_PKCS11_MAX_PIN_LEN]; |
335 | int ret, ret2; |
336 | |
337 | do |
338 | { |
339 | ret = import_tpm_key(pkey, fdata, format, uuid, storage, srk_password, key_password); |
340 | |
341 | if (attempts > 3) |
342 | break; |
343 | |
344 | if (ret == GNUTLS_E_TPM_SRK_PASSWORD_ERROR) |
345 | { |
346 | ret2 = tpm_pin(&pkey->pin, &srk_uuid, storage, pin1, sizeof(pin1), attempts++); |
347 | if (ret2 < 0) |
348 | { |
349 | gnutls_assert(); |
350 | return GNUTLS_E_TPM_SRK_PASSWORD_ERROR; |
351 | } |
352 | srk_password = pin1; |
353 | } |
354 | |
355 | if (ret == GNUTLS_E_TPM_KEY_PASSWORD_ERROR) |
356 | { |
357 | ret2 = tpm_pin(&pkey->pin, uuid, storage, pin2, sizeof(pin2), attempts++); |
358 | if (ret2 < 0) |
359 | { |
360 | gnutls_assert(); |
361 | return GNUTLS_E_TPM_KEY_PASSWORD_ERROR; |
362 | } |
363 | key_password = pin2; |
364 | } |
365 | } |
366 | while(ret == GNUTLS_E_TPM_KEY_PASSWORD_ERROR || ret == GNUTLS_E_TPM_SRK_PASSWORD_ERROR); |
367 | |
368 | if (ret < 0) |
369 | gnutls_assert(); |
370 | return ret; |
371 | } |
372 | |
373 | static int load_key(TSS_HCONTEXT tpm_ctx, TSS_HKEY srk, |
374 | const gnutls_datum_t * fdata, gnutls_tpmkey_fmt_t format, |
375 | TSS_HKEY* tpm_key) |
376 | { |
377 | int ret, err; |
378 | gnutls_datum_t asn1 = { NULL, 0 }; |
379 | |
380 | if (format == GNUTLS_TPMKEY_FMT_CTK_PEM) |
381 | { |
382 | gnutls_datum_t td; |
383 | |
384 | ret = gnutls_pem_base64_decode_alloc ("TSS KEY BLOB" , fdata, &asn1); |
385 | if (ret) |
386 | { |
387 | gnutls_assert (); |
388 | _gnutls_debug_log ("Error decoding TSS key blob: %s\n" , |
389 | gnutls_strerror (ret)); |
390 | return ret; |
391 | } |
392 | |
393 | ret = _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING, asn1.data, asn1.size, &td); |
394 | if (ret < 0) |
395 | { |
396 | gnutls_assert(); |
397 | goto cleanup; |
398 | } |
399 | gnutls_free(asn1.data); |
400 | asn1.data = td.data; |
401 | asn1.size = td.size; |
402 | } |
403 | else /* DER */ |
404 | { |
405 | UINT32 tint2; |
406 | UINT32 type; |
407 | |
408 | asn1.size = fdata->size; |
409 | asn1.data = gnutls_malloc(asn1.size); |
410 | if (asn1.data == NULL) |
411 | { |
412 | gnutls_assert(); |
413 | return GNUTLS_E_MEMORY_ERROR; |
414 | } |
415 | |
416 | tint2 = asn1.size; |
417 | err = Tspi_DecodeBER_TssBlob(fdata->size, fdata->data, &type, |
418 | &tint2, asn1.data); |
419 | if (err != 0) |
420 | { |
421 | gnutls_assert(); |
422 | ret = tss_err(err); |
423 | goto cleanup; |
424 | } |
425 | |
426 | asn1.size = tint2; |
427 | } |
428 | |
429 | /* ... we get it here instead. */ |
430 | err = Tspi_Context_LoadKeyByBlob (tpm_ctx, srk, |
431 | asn1.size, asn1.data, tpm_key); |
432 | if (err != 0) |
433 | { |
434 | gnutls_assert (); |
435 | ret = tss_err(err); |
436 | goto cleanup; |
437 | } |
438 | |
439 | ret = 0; |
440 | |
441 | cleanup: |
442 | gnutls_free (asn1.data); |
443 | |
444 | return ret; |
445 | } |
446 | |
447 | |
448 | static int |
449 | import_tpm_key (gnutls_privkey_t pkey, |
450 | const gnutls_datum_t * fdata, |
451 | gnutls_tpmkey_fmt_t format, |
452 | TSS_UUID *uuid, |
453 | TSS_FLAG storage, |
454 | const char *srk_password, |
455 | const char *key_password) |
456 | { |
457 | int err, ret; |
458 | struct tpm_ctx_st *s; |
459 | gnutls_datum_t tmp_sig; |
460 | |
461 | s = gnutls_malloc (sizeof (*s)); |
462 | if (s == NULL) |
463 | { |
464 | gnutls_assert (); |
465 | return GNUTLS_E_MEMORY_ERROR; |
466 | } |
467 | |
468 | ret = tpm_open_session(s, srk_password); |
469 | if (ret < 0) |
470 | { |
471 | gnutls_assert(); |
472 | goto out_ctx; |
473 | } |
474 | |
475 | if (fdata != NULL) |
476 | { |
477 | ret = load_key(s->tpm_ctx, s->srk, fdata, format, &s->tpm_key); |
478 | if (ret < 0) |
479 | { |
480 | gnutls_assert(); |
481 | goto out_session; |
482 | } |
483 | } |
484 | else if (uuid) |
485 | { |
486 | err = |
487 | Tspi_Context_LoadKeyByUUID (s->tpm_ctx, storage, |
488 | *uuid, &s->tpm_key); |
489 | |
490 | if (err) |
491 | { |
492 | gnutls_assert (); |
493 | ret = tss_err(err); |
494 | goto out_session; |
495 | } |
496 | } |
497 | else |
498 | { |
499 | gnutls_assert(); |
500 | ret = GNUTLS_E_INVALID_REQUEST; |
501 | goto out_session; |
502 | } |
503 | |
504 | ret = |
505 | gnutls_privkey_import_ext2 (pkey, GNUTLS_PK_RSA, s, |
506 | tpm_sign_fn, NULL, tpm_deinit_fn, 0); |
507 | if (ret < 0) |
508 | { |
509 | gnutls_assert (); |
510 | goto out_session; |
511 | } |
512 | |
513 | ret = |
514 | gnutls_privkey_sign_data (pkey, GNUTLS_DIG_SHA1, 0, &nulldata, &tmp_sig); |
515 | if (ret == GNUTLS_E_TPM_KEY_PASSWORD_ERROR) |
516 | { |
517 | if (!s->tpm_key_policy) |
518 | { |
519 | err = Tspi_Context_CreateObject (s->tpm_ctx, |
520 | TSS_OBJECT_TYPE_POLICY, |
521 | TSS_POLICY_USAGE, |
522 | &s->tpm_key_policy); |
523 | if (err) |
524 | { |
525 | gnutls_assert (); |
526 | ret = tss_err(err); |
527 | goto out_key; |
528 | } |
529 | |
530 | err = Tspi_Policy_AssignToObject (s->tpm_key_policy, s->tpm_key); |
531 | if (err) |
532 | { |
533 | gnutls_assert (); |
534 | ret = tss_err(err); |
535 | goto out_key_policy; |
536 | } |
537 | } |
538 | |
539 | err = myTspi_Policy_SetSecret (s->tpm_key_policy, |
540 | SAFE_LEN(key_password), (void *) key_password); |
541 | |
542 | if (err) |
543 | { |
544 | gnutls_assert (); |
545 | ret = tss_err_key(err); |
546 | goto out_key_policy; |
547 | } |
548 | } |
549 | else if (ret < 0) |
550 | { |
551 | gnutls_assert (); |
552 | goto out_session; |
553 | } |
554 | |
555 | return 0; |
556 | out_key_policy: |
557 | Tspi_Context_CloseObject (s->tpm_ctx, s->tpm_key_policy); |
558 | s->tpm_key_policy = 0; |
559 | out_key: |
560 | Tspi_Context_CloseObject (s->tpm_ctx, s->tpm_key); |
561 | s->tpm_key = 0; |
562 | out_session: |
563 | tpm_close_session(s); |
564 | out_ctx: |
565 | gnutls_free (s); |
566 | return ret; |
567 | } |
568 | |
569 | /** |
570 | * gnutls_privkey_import_tpm_raw: |
571 | * @pkey: The private key |
572 | * @fdata: The TPM key to be imported |
573 | * @format: The format of the private key |
574 | * @srk_password: The password for the SRK key (optional) |
575 | * @key_password: A password for the key (optional) |
576 | * @flags: should be zero |
577 | * |
578 | * This function will import the given private key to the abstract |
579 | * #gnutls_privkey_t structure. |
580 | * |
581 | * With respect to passwords the same as in gnutls_privkey_import_tpm_url() apply. |
582 | * |
583 | * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a |
584 | * negative error value. |
585 | * |
586 | * Since: 3.1.0 |
587 | * |
588 | **/ |
589 | int |
590 | gnutls_privkey_import_tpm_raw (gnutls_privkey_t pkey, |
591 | const gnutls_datum_t * fdata, |
592 | gnutls_tpmkey_fmt_t format, |
593 | const char *srk_password, |
594 | const char *key_password, |
595 | unsigned int flags) |
596 | { |
597 | if (flags & GNUTLS_PRIVKEY_DISABLE_CALLBACKS) |
598 | return import_tpm_key(pkey, fdata, format, NULL, 0, srk_password, key_password); |
599 | else |
600 | return import_tpm_key_cb(pkey, fdata, format, NULL, 0, srk_password, key_password); |
601 | } |
602 | |
603 | struct tpmkey_url_st |
604 | { |
605 | char* filename; |
606 | TSS_UUID uuid; |
607 | TSS_FLAG storage; |
608 | unsigned int uuid_set; |
609 | }; |
610 | |
611 | static void clear_tpmkey_url(struct tpmkey_url_st *s) |
612 | { |
613 | gnutls_free(s->filename); |
614 | memset(s, 0, sizeof(*s)); |
615 | } |
616 | |
617 | static int |
618 | unescape_string (char *output, const char *input, size_t * size, |
619 | char terminator) |
620 | { |
621 | gnutls_buffer_st str; |
622 | int ret = 0; |
623 | char *p; |
624 | int len; |
625 | |
626 | _gnutls_buffer_init (&str); |
627 | |
628 | /* find terminator */ |
629 | p = strchr (input, terminator); |
630 | if (p != NULL) |
631 | len = p - input; |
632 | else |
633 | len = strlen (input); |
634 | |
635 | ret = _gnutls_buffer_append_data (&str, input, len); |
636 | if (ret < 0) |
637 | { |
638 | gnutls_assert (); |
639 | return ret; |
640 | } |
641 | |
642 | ret = _gnutls_buffer_unescape (&str); |
643 | if (ret < 0) |
644 | { |
645 | gnutls_assert (); |
646 | return ret; |
647 | } |
648 | |
649 | ret = _gnutls_buffer_append_data (&str, "" , 1); |
650 | if (ret < 0) |
651 | { |
652 | gnutls_assert (); |
653 | return ret; |
654 | } |
655 | |
656 | _gnutls_buffer_pop_data (&str, output, size); |
657 | |
658 | _gnutls_buffer_clear (&str); |
659 | |
660 | return ret; |
661 | } |
662 | |
663 | #define UUID_SIZE 16 |
664 | |
665 | static int randomize_uuid(TSS_UUID* uuid) |
666 | { |
667 | uint8_t raw_uuid[16]; |
668 | int ret; |
669 | |
670 | ret = _gnutls_rnd (GNUTLS_RND_NONCE, raw_uuid, sizeof(raw_uuid)); |
671 | if (ret < 0) |
672 | return gnutls_assert_val(ret); |
673 | |
674 | /* mark it as random uuid */ |
675 | raw_uuid[6] &= 0x0f; |
676 | raw_uuid[6] |= 0x40; |
677 | raw_uuid[8] &= 0x0f; |
678 | raw_uuid[8] |= 0x80; |
679 | |
680 | memcpy(&uuid->ulTimeLow, raw_uuid, 4); |
681 | memcpy(&uuid->usTimeMid, &raw_uuid[4], 2); |
682 | memcpy(&uuid->usTimeHigh, &raw_uuid[6], 2); |
683 | uuid->bClockSeqHigh = raw_uuid[8]; |
684 | uuid->bClockSeqLow = raw_uuid[9]; |
685 | memcpy(&uuid->rgbNode, &raw_uuid[10], 6); |
686 | |
687 | return 0; |
688 | } |
689 | |
690 | static int encode_tpmkey_url(char** url, const TSS_UUID* uuid, TSS_FLAG storage) |
691 | { |
692 | size_t size = (UUID_SIZE*2+4)*2+32; |
693 | uint8_t u1[UUID_SIZE]; |
694 | gnutls_buffer_st buf; |
695 | gnutls_datum_t dret; |
696 | int ret; |
697 | |
698 | *url = gnutls_malloc(size); |
699 | if (*url == NULL) |
700 | return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); |
701 | |
702 | _gnutls_buffer_init(&buf); |
703 | |
704 | memcpy(u1, &uuid->ulTimeLow, 4); |
705 | memcpy(&u1[4], &uuid->usTimeMid, 2); |
706 | memcpy(&u1[6], &uuid->usTimeHigh, 2); |
707 | u1[8] = uuid->bClockSeqHigh; |
708 | u1[9] = uuid->bClockSeqLow; |
709 | memcpy(&u1[10], uuid->rgbNode, 6); |
710 | |
711 | ret = _gnutls_buffer_append_str(&buf, "tpmkey:uuid=" ); |
712 | if (ret < 0) |
713 | { |
714 | gnutls_assert(); |
715 | goto cleanup; |
716 | } |
717 | |
718 | ret = _gnutls_buffer_append_printf(&buf, "%.2x%.2x%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x" , |
719 | (unsigned int)u1[0], (unsigned int)u1[1], (unsigned int)u1[2], (unsigned int)u1[3], |
720 | (unsigned int)u1[4], (unsigned int)u1[5], (unsigned int)u1[6], (unsigned int)u1[7], |
721 | (unsigned int)u1[8], (unsigned int)u1[9], (unsigned int)u1[10], (unsigned int)u1[11], |
722 | (unsigned int)u1[12], (unsigned int)u1[13], (unsigned int)u1[14], (unsigned int)u1[15]); |
723 | if (ret < 0) |
724 | { |
725 | gnutls_assert(); |
726 | goto cleanup; |
727 | } |
728 | |
729 | ret = _gnutls_buffer_append_printf(&buf, ";storage=%s" , (storage==TSS_PS_TYPE_USER)?"user" :"system" ); |
730 | if (ret < 0) |
731 | { |
732 | gnutls_assert(); |
733 | goto cleanup; |
734 | } |
735 | |
736 | ret = _gnutls_buffer_to_datum(&buf, &dret); |
737 | if (ret < 0) |
738 | { |
739 | gnutls_assert(); |
740 | goto cleanup; |
741 | } |
742 | |
743 | *url = (char*)dret.data; |
744 | |
745 | return 0; |
746 | cleanup: |
747 | _gnutls_buffer_clear(&buf); |
748 | return ret; |
749 | } |
750 | |
751 | static int decode_tpmkey_url(const char* url, struct tpmkey_url_st *s) |
752 | { |
753 | char* p; |
754 | size_t size; |
755 | int ret; |
756 | unsigned int i, j; |
757 | |
758 | if (strstr (url, "tpmkey:" ) == NULL) |
759 | return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); |
760 | |
761 | memset(s, 0, sizeof(*s)); |
762 | |
763 | p = strstr(url, "file=" ); |
764 | if (p != NULL) |
765 | { |
766 | p += sizeof ("file=" ) - 1; |
767 | size = strlen(p); |
768 | s->filename = gnutls_malloc(size+1); |
769 | if (s->filename == NULL) |
770 | return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); |
771 | |
772 | ret = unescape_string (s->filename, p, &size, ';'); |
773 | if (ret < 0) |
774 | { |
775 | gnutls_assert(); |
776 | goto cleanup; |
777 | } |
778 | s->filename[size] = 0; |
779 | } |
780 | else if ((p = strstr(url, "uuid=" )) != NULL) |
781 | { |
782 | char tmp_uuid[33]; |
783 | uint8_t raw_uuid[16]; |
784 | |
785 | p += sizeof ("uuid=" ) - 1; |
786 | size = strlen(p); |
787 | |
788 | for (j=i=0;i<size;i++) |
789 | { |
790 | if (j==sizeof(tmp_uuid)-1) |
791 | { |
792 | break; |
793 | } |
794 | if (c_isalnum(p[i])) tmp_uuid[j++]=p[i]; |
795 | } |
796 | tmp_uuid[j] = 0; |
797 | |
798 | size = sizeof(raw_uuid); |
799 | ret = _gnutls_hex2bin(tmp_uuid, strlen(tmp_uuid), raw_uuid, &size); |
800 | if (ret < 0) |
801 | { |
802 | gnutls_assert(); |
803 | goto cleanup; |
804 | } |
805 | |
806 | memcpy(&s->uuid.ulTimeLow, raw_uuid, 4); |
807 | memcpy(&s->uuid.usTimeMid, &raw_uuid[4], 2); |
808 | memcpy(&s->uuid.usTimeHigh, &raw_uuid[6], 2); |
809 | s->uuid.bClockSeqHigh = raw_uuid[8]; |
810 | s->uuid.bClockSeqLow = raw_uuid[9]; |
811 | memcpy(&s->uuid.rgbNode, &raw_uuid[10], 6); |
812 | s->uuid_set = 1; |
813 | } |
814 | else |
815 | { |
816 | return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); |
817 | } |
818 | |
819 | if ((p = strstr(url, "storage=user" )) != NULL) |
820 | s->storage = TSS_PS_TYPE_USER; |
821 | else |
822 | s->storage = TSS_PS_TYPE_SYSTEM; |
823 | |
824 | return 0; |
825 | |
826 | cleanup: |
827 | clear_tpmkey_url(s); |
828 | return ret; |
829 | } |
830 | |
831 | /** |
832 | * gnutls_privkey_import_tpm_url: |
833 | * @pkey: The private key |
834 | * @url: The URL of the TPM key to be imported |
835 | * @srk_password: The password for the SRK key (optional) |
836 | * @key_password: A password for the key (optional) |
837 | * @flags: One of the GNUTLS_PRIVKEY_* flags |
838 | * |
839 | * This function will import the given private key to the abstract |
840 | * #gnutls_privkey_t structure. |
841 | * |
842 | * Note that unless %GNUTLS_PRIVKEY_DISABLE_CALLBACKS |
843 | * is specified, if incorrect (or NULL) passwords are given |
844 | * the PKCS11 callback functions will be used to obtain the |
845 | * correct passwords. Otherwise if the SRK password is wrong |
846 | * %GNUTLS_E_TPM_SRK_PASSWORD_ERROR is returned and if the key password |
847 | * is wrong or not provided then %GNUTLS_E_TPM_KEY_PASSWORD_ERROR |
848 | * is returned. |
849 | * |
850 | * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a |
851 | * negative error value. |
852 | * |
853 | * Since: 3.1.0 |
854 | * |
855 | **/ |
856 | int |
857 | gnutls_privkey_import_tpm_url (gnutls_privkey_t pkey, |
858 | const char* url, |
859 | const char *srk_password, |
860 | const char *key_password, |
861 | unsigned int flags) |
862 | { |
863 | struct tpmkey_url_st durl; |
864 | gnutls_datum_t fdata = { NULL, 0 }; |
865 | int ret; |
866 | |
867 | ret = decode_tpmkey_url(url, &durl); |
868 | if (ret < 0) |
869 | return gnutls_assert_val(ret); |
870 | |
871 | if (durl.filename) |
872 | { |
873 | ret = gnutls_load_file(durl.filename, &fdata); |
874 | if (ret < 0) |
875 | { |
876 | gnutls_assert(); |
877 | _gnutls_debug_log("Error loading %s\n" , durl.filename); |
878 | goto cleanup; |
879 | } |
880 | |
881 | ret = gnutls_privkey_import_tpm_raw (pkey, &fdata, GNUTLS_TPMKEY_FMT_CTK_PEM, |
882 | srk_password, key_password, flags); |
883 | if (ret == GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR) |
884 | ret = gnutls_privkey_import_tpm_raw (pkey, &fdata, GNUTLS_TPMKEY_FMT_RAW, |
885 | srk_password, key_password, flags); |
886 | |
887 | if (ret < 0) |
888 | { |
889 | gnutls_assert(); |
890 | goto cleanup; |
891 | } |
892 | } |
893 | else if (durl.uuid_set) |
894 | { |
895 | if (flags & GNUTLS_PRIVKEY_DISABLE_CALLBACKS) |
896 | ret = import_tpm_key (pkey, NULL, 0, &durl.uuid, durl.storage, srk_password, key_password); |
897 | else |
898 | ret = import_tpm_key_cb (pkey, NULL, 0, &durl.uuid, durl.storage, srk_password, key_password); |
899 | if (ret < 0) |
900 | { |
901 | gnutls_assert(); |
902 | goto cleanup; |
903 | } |
904 | } |
905 | |
906 | ret = 0; |
907 | cleanup: |
908 | gnutls_free(fdata.data); |
909 | clear_tpmkey_url(&durl); |
910 | return ret; |
911 | } |
912 | |
913 | |
914 | /* reads the RSA public key from the given TSS key. |
915 | * If psize is non-null it contains the total size of the parameters |
916 | * in bytes */ |
917 | static int read_pubkey(gnutls_pubkey_t pub, TSS_HKEY key_ctx, size_t *psize) |
918 | { |
919 | void* tdata; |
920 | UINT32 tint; |
921 | TSS_RESULT tssret; |
922 | gnutls_datum_t m, e; |
923 | int ret; |
924 | |
925 | /* read the public key */ |
926 | |
927 | tssret = Tspi_GetAttribData(key_ctx, TSS_TSPATTRIB_RSAKEY_INFO, |
928 | TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, &tint, (void*)&tdata); |
929 | if (tssret != 0) |
930 | { |
931 | gnutls_assert(); |
932 | return tss_err(tssret); |
933 | } |
934 | |
935 | m.data = tdata; |
936 | m.size = tint; |
937 | |
938 | tssret = Tspi_GetAttribData(key_ctx, TSS_TSPATTRIB_RSAKEY_INFO, |
939 | TSS_TSPATTRIB_KEYINFO_RSA_EXPONENT, &tint, (void*)&tdata); |
940 | if (tssret != 0) |
941 | { |
942 | gnutls_assert(); |
943 | Tspi_Context_FreeMemory(key_ctx, m.data); |
944 | return tss_err(tssret); |
945 | } |
946 | |
947 | e.data = tdata; |
948 | e.size = tint; |
949 | |
950 | ret = gnutls_pubkey_import_rsa_raw(pub, &m, &e); |
951 | |
952 | Tspi_Context_FreeMemory(key_ctx, m.data); |
953 | Tspi_Context_FreeMemory(key_ctx, e.data); |
954 | |
955 | if (ret < 0) |
956 | return gnutls_assert_val(ret); |
957 | |
958 | if (psize) |
959 | *psize = e.size + m.size; |
960 | |
961 | return 0; |
962 | } |
963 | |
964 | |
965 | |
966 | static int |
967 | import_tpm_pubkey (gnutls_pubkey_t pkey, |
968 | const gnutls_datum_t * fdata, |
969 | gnutls_tpmkey_fmt_t format, |
970 | TSS_UUID *uuid, |
971 | TSS_FLAG storage, |
972 | const char *srk_password) |
973 | { |
974 | int err, ret; |
975 | struct tpm_ctx_st s; |
976 | |
977 | ret = tpm_open_session(&s, srk_password); |
978 | if (ret < 0) |
979 | return gnutls_assert_val(ret); |
980 | |
981 | if (fdata != NULL) |
982 | { |
983 | ret = load_key(s.tpm_ctx, s.srk, fdata, format, &s.tpm_key); |
984 | if (ret < 0) |
985 | { |
986 | gnutls_assert(); |
987 | goto out_session; |
988 | } |
989 | } |
990 | else if (uuid) |
991 | { |
992 | err = |
993 | Tspi_Context_LoadKeyByUUID (s.tpm_ctx, storage, |
994 | *uuid, &s.tpm_key); |
995 | if (err) |
996 | { |
997 | gnutls_assert (); |
998 | ret = tss_err(err); |
999 | goto out_session; |
1000 | } |
1001 | } |
1002 | else |
1003 | { |
1004 | gnutls_assert(); |
1005 | ret = GNUTLS_E_INVALID_REQUEST; |
1006 | goto out_session; |
1007 | } |
1008 | |
1009 | ret = read_pubkey(pkey, s.tpm_key, NULL); |
1010 | if (ret < 0) |
1011 | { |
1012 | gnutls_assert(); |
1013 | goto out_session; |
1014 | } |
1015 | |
1016 | ret = 0; |
1017 | out_session: |
1018 | tpm_close_session(&s); |
1019 | return ret; |
1020 | } |
1021 | |
1022 | static int |
1023 | import_tpm_pubkey_cb (gnutls_pubkey_t pkey, |
1024 | const gnutls_datum_t * fdata, |
1025 | gnutls_tpmkey_fmt_t format, |
1026 | TSS_UUID *uuid, |
1027 | TSS_FLAG storage, |
1028 | const char *srk_password) |
1029 | { |
1030 | unsigned int attempts = 0; |
1031 | char pin1[GNUTLS_PKCS11_MAX_PIN_LEN]; |
1032 | int ret; |
1033 | |
1034 | do |
1035 | { |
1036 | ret = import_tpm_pubkey(pkey, fdata, format, uuid, storage, srk_password); |
1037 | |
1038 | if (attempts > 3) |
1039 | break; |
1040 | |
1041 | if (ret == GNUTLS_E_TPM_SRK_PASSWORD_ERROR) |
1042 | { |
1043 | ret = tpm_pin(&pkey->pin, &srk_uuid, storage, pin1, sizeof(pin1), attempts++); |
1044 | if (ret < 0) |
1045 | { |
1046 | gnutls_assert(); |
1047 | return GNUTLS_E_TPM_SRK_PASSWORD_ERROR; |
1048 | } |
1049 | srk_password = pin1; |
1050 | } |
1051 | } |
1052 | while(ret == GNUTLS_E_TPM_SRK_PASSWORD_ERROR); |
1053 | |
1054 | if (ret < 0) |
1055 | gnutls_assert(); |
1056 | return ret; |
1057 | } |
1058 | |
1059 | |
1060 | /** |
1061 | * gnutls_pubkey_import_tpm_raw: |
1062 | * @pkey: The public key |
1063 | * @fdata: The TPM key to be imported |
1064 | * @format: The format of the private key |
1065 | * @srk_password: The password for the SRK key (optional) |
1066 | * @flags: One of the GNUTLS_PUBKEY_* flags |
1067 | * |
1068 | * This function will import the public key from the provided TPM key |
1069 | * structure. |
1070 | * |
1071 | * With respect to passwords the same as in |
1072 | * gnutls_pubkey_import_tpm_url() apply. |
1073 | * |
1074 | * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a |
1075 | * negative error value. |
1076 | * |
1077 | * Since: 3.1.0 |
1078 | **/ |
1079 | int |
1080 | gnutls_pubkey_import_tpm_raw (gnutls_pubkey_t pkey, |
1081 | const gnutls_datum_t * fdata, |
1082 | gnutls_tpmkey_fmt_t format, |
1083 | const char *srk_password, |
1084 | unsigned int flags) |
1085 | { |
1086 | if (flags & GNUTLS_PUBKEY_DISABLE_CALLBACKS) |
1087 | return import_tpm_pubkey_cb(pkey, fdata, format, NULL, 0, srk_password); |
1088 | else |
1089 | return import_tpm_pubkey(pkey, fdata, format, NULL, 0, srk_password); |
1090 | } |
1091 | |
1092 | /** |
1093 | * gnutls_pubkey_import_tpm_url: |
1094 | * @pkey: The public key |
1095 | * @url: The URL of the TPM key to be imported |
1096 | * @srk_password: The password for the SRK key (optional) |
1097 | * @flags: should be zero |
1098 | * |
1099 | * This function will import the given private key to the abstract |
1100 | * #gnutls_privkey_t structure. |
1101 | * |
1102 | * Note that unless %GNUTLS_PUBKEY_DISABLE_CALLBACKS |
1103 | * is specified, if incorrect (or NULL) passwords are given |
1104 | * the PKCS11 callback functions will be used to obtain the |
1105 | * correct passwords. Otherwise if the SRK password is wrong |
1106 | * %GNUTLS_E_TPM_SRK_PASSWORD_ERROR is returned. |
1107 | * |
1108 | * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a |
1109 | * negative error value. |
1110 | * |
1111 | * Since: 3.1.0 |
1112 | * |
1113 | **/ |
1114 | int |
1115 | gnutls_pubkey_import_tpm_url (gnutls_pubkey_t pkey, |
1116 | const char* url, |
1117 | const char *srk_password, |
1118 | unsigned int flags) |
1119 | { |
1120 | struct tpmkey_url_st durl; |
1121 | gnutls_datum_t fdata = { NULL, 0 }; |
1122 | int ret; |
1123 | |
1124 | ret = decode_tpmkey_url(url, &durl); |
1125 | if (ret < 0) |
1126 | return gnutls_assert_val(ret); |
1127 | |
1128 | if (durl.filename) |
1129 | { |
1130 | |
1131 | ret = gnutls_load_file(durl.filename, &fdata); |
1132 | if (ret < 0) |
1133 | { |
1134 | gnutls_assert(); |
1135 | goto cleanup; |
1136 | } |
1137 | |
1138 | ret = gnutls_pubkey_import_tpm_raw (pkey, &fdata, GNUTLS_TPMKEY_FMT_CTK_PEM, |
1139 | srk_password, flags); |
1140 | if (ret == GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR) |
1141 | ret = gnutls_pubkey_import_tpm_raw (pkey, &fdata, GNUTLS_TPMKEY_FMT_RAW, |
1142 | srk_password, flags); |
1143 | if (ret < 0) |
1144 | { |
1145 | gnutls_assert(); |
1146 | goto cleanup; |
1147 | } |
1148 | } |
1149 | else if (durl.uuid_set) |
1150 | { |
1151 | if (flags & GNUTLS_PUBKEY_DISABLE_CALLBACKS) |
1152 | ret = import_tpm_pubkey (pkey, NULL, 0, &durl.uuid, durl.storage, srk_password); |
1153 | else |
1154 | ret = import_tpm_pubkey_cb (pkey, NULL, 0, &durl.uuid, durl.storage, srk_password); |
1155 | if (ret < 0) |
1156 | { |
1157 | gnutls_assert(); |
1158 | goto cleanup; |
1159 | } |
1160 | } |
1161 | |
1162 | ret = 0; |
1163 | cleanup: |
1164 | gnutls_free(fdata.data); |
1165 | clear_tpmkey_url(&durl); |
1166 | return ret; |
1167 | } |
1168 | |
1169 | |
1170 | /** |
1171 | * gnutls_tpm_privkey_generate: |
1172 | * @pk: the public key algorithm |
1173 | * @bits: the security bits |
1174 | * @srk_password: a password to protect the exported key (optional) |
1175 | * @key_password: the password for the TPM (optional) |
1176 | * @format: the format of the private key |
1177 | * @pub_format: the format of the public key |
1178 | * @privkey: the generated key |
1179 | * @pubkey: the corresponding public key (may be null) |
1180 | * @flags: should be a list of GNUTLS_TPM_* flags |
1181 | * |
1182 | * This function will generate a private key in the TPM |
1183 | * chip. The private key will be generated within the chip |
1184 | * and will be exported in a wrapped with TPM's master key |
1185 | * form. Furthermore the wrapped key can be protected with |
1186 | * the provided @password. |
1187 | * |
1188 | * Note that bits in TPM is quantized value. If the input value |
1189 | * is not one of the allowed values, then it will be quantized to |
1190 | * one of 512, 1024, 2048, 4096, 8192 and 16384. |
1191 | * |
1192 | * Allowed flags are: |
1193 | * |
1194 | * %GNUTLS_TPM_KEY_SIGNING: Generate a signing key instead of a legacy, |
1195 | |
1196 | * %GNUTLS_TPM_REGISTER_KEY: Register the generate key in TPM. In that |
1197 | * case @privkey would contain a URL with the UUID. |
1198 | * |
1199 | * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a |
1200 | * negative error value. |
1201 | * |
1202 | * Since: 3.1.0 |
1203 | **/ |
1204 | int |
1205 | gnutls_tpm_privkey_generate (gnutls_pk_algorithm_t pk, unsigned int bits, |
1206 | const char* srk_password, |
1207 | const char* key_password, |
1208 | gnutls_tpmkey_fmt_t format, |
1209 | gnutls_x509_crt_fmt_t pub_format, |
1210 | gnutls_datum_t* privkey, |
1211 | gnutls_datum_t* pubkey, |
1212 | unsigned int flags) |
1213 | { |
1214 | TSS_FLAG tpm_flags = TSS_KEY_VOLATILE; |
1215 | TSS_HKEY key_ctx; |
1216 | TSS_RESULT tssret; |
1217 | int ret; |
1218 | void* tdata; |
1219 | UINT32 tint; |
1220 | gnutls_datum_t tmpkey = {NULL, 0}; |
1221 | TSS_HPOLICY key_policy; |
1222 | gnutls_pubkey_t pub; |
1223 | struct tpm_ctx_st s; |
1224 | TSS_FLAG storage_type; |
1225 | TSS_HTPM htpm; |
1226 | uint8_t buf[32]; |
1227 | |
1228 | if (flags & GNUTLS_TPM_KEY_SIGNING) |
1229 | tpm_flags |= TSS_KEY_TYPE_SIGNING; |
1230 | else |
1231 | tpm_flags |= TSS_KEY_TYPE_LEGACY; |
1232 | |
1233 | if (flags & GNUTLS_TPM_KEY_USER) |
1234 | storage_type = TSS_PS_TYPE_USER; |
1235 | else |
1236 | storage_type = TSS_PS_TYPE_SYSTEM; |
1237 | |
1238 | if (bits <= 512) |
1239 | tpm_flags |= TSS_KEY_SIZE_512; |
1240 | else if (bits <= 1024) |
1241 | tpm_flags |= TSS_KEY_SIZE_1024; |
1242 | else if (bits <= 2048) |
1243 | tpm_flags |= TSS_KEY_SIZE_2048; |
1244 | else if (bits <= 4096) |
1245 | tpm_flags |= TSS_KEY_SIZE_4096; |
1246 | else if (bits <= 8192) |
1247 | tpm_flags |= TSS_KEY_SIZE_8192; |
1248 | else |
1249 | tpm_flags |= TSS_KEY_SIZE_16384; |
1250 | |
1251 | ret = tpm_open_session(&s, srk_password); |
1252 | if (ret < 0) |
1253 | return gnutls_assert_val(ret); |
1254 | |
1255 | /* put some randomness into TPM. |
1256 | * Let's not trust it completely. |
1257 | */ |
1258 | tssret = Tspi_Context_GetTpmObject(s.tpm_ctx, &htpm); |
1259 | if (tssret != 0) |
1260 | { |
1261 | gnutls_assert(); |
1262 | ret = tss_err(tssret); |
1263 | goto err_cc; |
1264 | } |
1265 | |
1266 | |
1267 | ret = _gnutls_rnd(GNUTLS_RND_RANDOM, buf, sizeof(buf)); |
1268 | if (ret < 0) |
1269 | { |
1270 | gnutls_assert(); |
1271 | goto err_cc; |
1272 | } |
1273 | |
1274 | tssret = Tspi_TPM_StirRandom(htpm, sizeof(buf), buf); |
1275 | if (tssret) |
1276 | { |
1277 | gnutls_assert(); |
1278 | } |
1279 | |
1280 | tssret = Tspi_Context_CreateObject(s.tpm_ctx, TSS_OBJECT_TYPE_RSAKEY, tpm_flags, &key_ctx); |
1281 | if (tssret != 0) |
1282 | { |
1283 | gnutls_assert(); |
1284 | ret = tss_err(tssret); |
1285 | goto err_cc; |
1286 | } |
1287 | |
1288 | tssret = Tspi_SetAttribUint32(key_ctx, TSS_TSPATTRIB_KEY_INFO, TSS_TSPATTRIB_KEYINFO_SIGSCHEME, |
1289 | TSS_SS_RSASSAPKCS1V15_DER); |
1290 | if (tssret != 0) |
1291 | { |
1292 | gnutls_assert(); |
1293 | ret = tss_err(tssret); |
1294 | goto err_sa; |
1295 | } |
1296 | |
1297 | /* set the password of the actual key */ |
1298 | if (key_password) |
1299 | { |
1300 | tssret = Tspi_GetPolicyObject(key_ctx, TSS_POLICY_USAGE, &key_policy); |
1301 | if (tssret != 0) |
1302 | { |
1303 | gnutls_assert(); |
1304 | ret = tss_err(tssret); |
1305 | goto err_sa; |
1306 | } |
1307 | |
1308 | tssret = myTspi_Policy_SetSecret(key_policy, |
1309 | SAFE_LEN(key_password), (void*)key_password); |
1310 | if (tssret != 0) |
1311 | { |
1312 | gnutls_assert(); |
1313 | ret = tss_err(tssret); |
1314 | goto err_sa; |
1315 | } |
1316 | } |
1317 | |
1318 | tssret = Tspi_Key_CreateKey(key_ctx, s.srk, 0); |
1319 | if (tssret != 0) |
1320 | { |
1321 | gnutls_assert(); |
1322 | ret = tss_err(tssret); |
1323 | goto err_sa; |
1324 | } |
1325 | |
1326 | if (flags & GNUTLS_TPM_REGISTER_KEY) |
1327 | { |
1328 | TSS_UUID key_uuid; |
1329 | |
1330 | ret = randomize_uuid(&key_uuid); |
1331 | if (ret < 0) |
1332 | { |
1333 | gnutls_assert(); |
1334 | goto err_sa; |
1335 | } |
1336 | |
1337 | tssret = Tspi_Context_RegisterKey(s.tpm_ctx, key_ctx, storage_type, |
1338 | key_uuid, TSS_PS_TYPE_SYSTEM, srk_uuid); |
1339 | if (tssret != 0) |
1340 | { |
1341 | gnutls_assert(); |
1342 | ret = tss_err(tssret); |
1343 | goto err_sa; |
1344 | } |
1345 | |
1346 | ret = encode_tpmkey_url((char**)&privkey->data, &key_uuid, storage_type); |
1347 | if (ret < 0) |
1348 | { |
1349 | TSS_HKEY tkey; |
1350 | |
1351 | Tspi_Context_UnregisterKey(s.tpm_ctx, storage_type, key_uuid, &tkey); |
1352 | gnutls_assert(); |
1353 | goto err_sa; |
1354 | } |
1355 | privkey->size = strlen((char*)privkey->data); |
1356 | |
1357 | } |
1358 | else /* get the key as blob */ |
1359 | { |
1360 | |
1361 | tssret = Tspi_GetAttribData(key_ctx, TSS_TSPATTRIB_KEY_BLOB, |
1362 | TSS_TSPATTRIB_KEYBLOB_BLOB, &tint, (void*)&tdata); |
1363 | if (tssret != 0) |
1364 | { |
1365 | gnutls_assert(); |
1366 | ret = tss_err(tssret); |
1367 | goto err_sa; |
1368 | } |
1369 | |
1370 | |
1371 | if (format == GNUTLS_TPMKEY_FMT_CTK_PEM) |
1372 | { |
1373 | ret = _gnutls_x509_encode_string(ASN1_ETYPE_OCTET_STRING, tdata, tint, &tmpkey); |
1374 | if (ret < 0) |
1375 | { |
1376 | gnutls_assert(); |
1377 | goto cleanup; |
1378 | } |
1379 | |
1380 | ret = _gnutls_fbase64_encode ("TSS KEY BLOB" , tmpkey.data, tmpkey.size, privkey); |
1381 | if (ret < 0) |
1382 | { |
1383 | gnutls_assert(); |
1384 | goto cleanup; |
1385 | } |
1386 | } |
1387 | else |
1388 | { |
1389 | UINT32 tint2; |
1390 | |
1391 | tmpkey.size = tint + 32; /* spec says no more than 20 */ |
1392 | tmpkey.data = gnutls_malloc(tmpkey.size); |
1393 | if (tmpkey.data == NULL) |
1394 | { |
1395 | gnutls_assert(); |
1396 | ret = GNUTLS_E_MEMORY_ERROR; |
1397 | goto cleanup; |
1398 | } |
1399 | |
1400 | tint2 = tmpkey.size; |
1401 | tssret = Tspi_EncodeDER_TssBlob(tint, tdata, TSS_BLOB_TYPE_PRIVATEKEY, |
1402 | &tint2, tmpkey.data); |
1403 | if (tssret != 0) |
1404 | { |
1405 | gnutls_assert(); |
1406 | ret = tss_err(tssret); |
1407 | goto cleanup; |
1408 | } |
1409 | |
1410 | tmpkey.size = tint2; |
1411 | |
1412 | privkey->data = tmpkey.data; |
1413 | privkey->size = tmpkey.size; |
1414 | tmpkey.data = NULL; |
1415 | } |
1416 | } |
1417 | |
1418 | /* read the public key */ |
1419 | if (pubkey != NULL) |
1420 | { |
1421 | size_t psize; |
1422 | |
1423 | ret = gnutls_pubkey_init(&pub); |
1424 | if (ret < 0) |
1425 | { |
1426 | gnutls_assert(); |
1427 | goto privkey_cleanup; |
1428 | } |
1429 | |
1430 | ret = read_pubkey(pub, key_ctx, &psize); |
1431 | if (ret < 0) |
1432 | { |
1433 | gnutls_assert(); |
1434 | goto privkey_cleanup; |
1435 | } |
1436 | psize+=512; |
1437 | |
1438 | pubkey->data = gnutls_malloc(psize); |
1439 | if (pubkey->data == NULL) |
1440 | { |
1441 | gnutls_assert(); |
1442 | ret = GNUTLS_E_MEMORY_ERROR; |
1443 | goto pubkey_cleanup; |
1444 | } |
1445 | |
1446 | ret = gnutls_pubkey_export(pub, pub_format, pubkey->data, &psize); |
1447 | if (ret < 0) |
1448 | { |
1449 | gnutls_assert(); |
1450 | goto pubkey_cleanup; |
1451 | } |
1452 | pubkey->size = psize; |
1453 | |
1454 | gnutls_pubkey_deinit(pub); |
1455 | } |
1456 | |
1457 | ret = 0; |
1458 | goto cleanup; |
1459 | |
1460 | pubkey_cleanup: |
1461 | gnutls_pubkey_deinit(pub); |
1462 | privkey_cleanup: |
1463 | gnutls_free(privkey->data); |
1464 | privkey->data = NULL; |
1465 | cleanup: |
1466 | gnutls_free(tmpkey.data); |
1467 | tmpkey.data = NULL; |
1468 | err_sa: |
1469 | Tspi_Context_CloseObject(s.tpm_ctx, key_ctx); |
1470 | err_cc: |
1471 | tpm_close_session(&s); |
1472 | return ret; |
1473 | } |
1474 | |
1475 | |
1476 | /** |
1477 | * gnutls_tpm_key_list_deinit: |
1478 | * @list: a list of the keys |
1479 | * |
1480 | * This function will deinitialize the list of stored keys in the TPM. |
1481 | * |
1482 | * Since: 3.1.0 |
1483 | **/ |
1484 | void |
1485 | gnutls_tpm_key_list_deinit (gnutls_tpm_key_list_t list) |
1486 | { |
1487 | if (list->tpm_ctx != 0) Tspi_Context_Close (list->tpm_ctx); |
1488 | gnutls_free(list); |
1489 | } |
1490 | |
1491 | /** |
1492 | * gnutls_tpm_key_list_get_url: |
1493 | * @list: a list of the keys |
1494 | * @idx: The index of the key (starting from zero) |
1495 | * @url: The URL to be returned |
1496 | * @flags: should be zero |
1497 | * |
1498 | * This function will return for each given index a URL of |
1499 | * the corresponding key. |
1500 | * If the provided index is out of bounds then %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE |
1501 | * is returned. |
1502 | * |
1503 | * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a |
1504 | * negative error value. |
1505 | * |
1506 | * Since: 3.1.0 |
1507 | **/ |
1508 | int |
1509 | gnutls_tpm_key_list_get_url (gnutls_tpm_key_list_t list, unsigned int idx, char** url, unsigned int flags) |
1510 | { |
1511 | if (idx >= list->size) |
1512 | return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); |
1513 | |
1514 | return encode_tpmkey_url(url, &list->ki[idx].keyUUID, list->ki[idx].persistentStorageType); |
1515 | } |
1516 | |
1517 | /** |
1518 | * gnutls_tpm_get_registered: |
1519 | * @list: a list to store the keys |
1520 | * |
1521 | * This function will get a list of stored keys in the TPM. The uuid |
1522 | * of those keys |
1523 | * |
1524 | * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a |
1525 | * negative error value. |
1526 | * |
1527 | * Since: 3.1.0 |
1528 | **/ |
1529 | int |
1530 | gnutls_tpm_get_registered (gnutls_tpm_key_list_t *list) |
1531 | { |
1532 | TSS_RESULT tssret; |
1533 | int ret; |
1534 | |
1535 | *list = gnutls_calloc(1, sizeof(struct tpm_key_list_st)); |
1536 | if (*list == NULL) |
1537 | return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); |
1538 | |
1539 | tssret = Tspi_Context_Create (&(*list)->tpm_ctx); |
1540 | if (tssret) |
1541 | { |
1542 | gnutls_assert (); |
1543 | ret = tss_err(tssret); |
1544 | goto cleanup; |
1545 | } |
1546 | |
1547 | tssret = Tspi_Context_Connect ((*list)->tpm_ctx, NULL); |
1548 | if (tssret) |
1549 | { |
1550 | gnutls_assert (); |
1551 | ret = tss_err(tssret); |
1552 | goto cleanup; |
1553 | } |
1554 | |
1555 | tssret = |
1556 | Tspi_Context_GetRegisteredKeysByUUID2((*list)->tpm_ctx, TSS_PS_TYPE_SYSTEM, |
1557 | NULL, &(*list)->size, &(*list)->ki); |
1558 | if (tssret) |
1559 | { |
1560 | gnutls_assert (); |
1561 | ret = tss_err(tssret); |
1562 | goto cleanup; |
1563 | } |
1564 | return 0; |
1565 | |
1566 | cleanup: |
1567 | gnutls_tpm_key_list_deinit(*list); |
1568 | |
1569 | return ret; |
1570 | } |
1571 | |
1572 | /** |
1573 | * gnutls_tpm_privkey_delete: |
1574 | * @url: the URL describing the key |
1575 | * @srk_password: a password for the SRK key |
1576 | * |
1577 | * This function will unregister the private key from the TPM |
1578 | * chip. |
1579 | * |
1580 | * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a |
1581 | * negative error value. |
1582 | * |
1583 | * Since: 3.1.0 |
1584 | **/ |
1585 | int |
1586 | gnutls_tpm_privkey_delete (const char* url, const char* srk_password) |
1587 | { |
1588 | struct tpm_ctx_st s; |
1589 | struct tpmkey_url_st durl; |
1590 | TSS_RESULT tssret; |
1591 | TSS_HKEY tkey; |
1592 | int ret; |
1593 | |
1594 | ret = decode_tpmkey_url(url, &durl); |
1595 | if (ret < 0) |
1596 | return gnutls_assert_val(ret); |
1597 | |
1598 | if (durl.uuid_set == 0) |
1599 | return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); |
1600 | |
1601 | ret = tpm_open_session(&s, srk_password); |
1602 | if (ret < 0) |
1603 | return gnutls_assert_val(ret); |
1604 | |
1605 | tssret = Tspi_Context_UnregisterKey(s.tpm_ctx, durl.storage, durl.uuid, &tkey); |
1606 | if (tssret != 0) |
1607 | { |
1608 | gnutls_assert(); |
1609 | ret = tss_err(tssret); |
1610 | goto err_cc; |
1611 | } |
1612 | |
1613 | ret = 0; |
1614 | err_cc: |
1615 | tpm_close_session(&s); |
1616 | return ret; |
1617 | } |
1618 | #else /* HAVE_TROUSERS */ |
1619 | int |
1620 | gnutls_privkey_import_tpm_raw (gnutls_privkey_t pkey, |
1621 | const gnutls_datum_t * fdata, |
1622 | gnutls_tpmkey_fmt_t format, |
1623 | const char *srk_password, |
1624 | const char *key_password, |
1625 | unsigned int flags) |
1626 | { |
1627 | return GNUTLS_E_UNIMPLEMENTED_FEATURE; |
1628 | } |
1629 | |
1630 | int |
1631 | gnutls_privkey_import_tpm_url (gnutls_privkey_t pkey, |
1632 | const char* url, |
1633 | const char *srk_password, |
1634 | const char *key_password, |
1635 | unsigned int flags) |
1636 | { |
1637 | return GNUTLS_E_UNIMPLEMENTED_FEATURE; |
1638 | } |
1639 | |
1640 | int |
1641 | gnutls_pubkey_import_tpm_raw (gnutls_pubkey_t pkey, |
1642 | const gnutls_datum_t * fdata, |
1643 | gnutls_tpmkey_fmt_t format, |
1644 | const char *srk_password, |
1645 | unsigned int flags) |
1646 | { |
1647 | return GNUTLS_E_UNIMPLEMENTED_FEATURE; |
1648 | } |
1649 | |
1650 | int |
1651 | gnutls_pubkey_import_tpm_url (gnutls_pubkey_t pkey, |
1652 | const char* url, |
1653 | const char *srk_password, |
1654 | unsigned int flags) |
1655 | { |
1656 | return GNUTLS_E_UNIMPLEMENTED_FEATURE; |
1657 | } |
1658 | |
1659 | int |
1660 | gnutls_tpm_privkey_generate (gnutls_pk_algorithm_t pk, unsigned int bits, |
1661 | const char* srk_password, |
1662 | const char* key_password, |
1663 | gnutls_tpmkey_fmt_t format, |
1664 | gnutls_x509_crt_fmt_t pub_format, |
1665 | gnutls_datum_t* privkey, |
1666 | gnutls_datum_t* pubkey, |
1667 | unsigned int flags) |
1668 | { |
1669 | return GNUTLS_E_UNIMPLEMENTED_FEATURE; |
1670 | } |
1671 | |
1672 | void |
1673 | gnutls_tpm_key_list_deinit (gnutls_tpm_key_list_t list) |
1674 | { |
1675 | return; |
1676 | } |
1677 | |
1678 | int |
1679 | gnutls_tpm_key_list_get_url (gnutls_tpm_key_list_t list, unsigned int idx, char** url, unsigned int flags) |
1680 | { |
1681 | return GNUTLS_E_UNIMPLEMENTED_FEATURE; |
1682 | } |
1683 | |
1684 | int |
1685 | gnutls_tpm_get_registered (gnutls_tpm_key_list_t *list) |
1686 | { |
1687 | return GNUTLS_E_UNIMPLEMENTED_FEATURE; |
1688 | } |
1689 | |
1690 | int |
1691 | gnutls_tpm_privkey_delete (const char* url, const char* srk_password) |
1692 | { |
1693 | return GNUTLS_E_UNIMPLEMENTED_FEATURE; |
1694 | } |
1695 | #endif /* HAVE_TROUSERS */ |
1696 | |
1697 | |