gnutls_tpm2_esys.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. /*
  2. * OpenConnect (SSL + DTLS) VPN client
  3. *
  4. * Copyright © 2018 David Woodhouse.
  5. *
  6. * Author: David Woodhouse <dwmw2@infradead.org>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public License
  10. * version 2.1, as published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. */
  17. /* Portions taken from tpm2-tss-engine, copyright as below: */
  18. /*******************************************************************************
  19. * Copyright 2017-2018, Fraunhofer SIT sponsored by Infineon Technologies AG
  20. * All rights reserved.
  21. *
  22. * Redistribution and use in source and binary forms, with or without
  23. * modification, are permitted provided that the following conditions are met:
  24. *
  25. * 1. Redistributions of source code must retain the above copyright notice,
  26. * this list of conditions and the following disclaimer.
  27. *
  28. * 2. Redistributions in binary form must reproduce the above copyright notice,
  29. * this list of conditions and the following disclaimer in the documentation
  30. * and/or other materials provided with the distribution.
  31. *
  32. * 3. Neither the name of tpm2-tss-engine nor the names of its contributors
  33. * may be used to endorse or promote products derived from this software
  34. * without specific prior written permission.
  35. *
  36. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  37. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  38. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  39. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  40. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  41. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  42. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  43. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  44. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  45. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  46. * THE POSSIBILITY OF SUCH DAMAGE.
  47. ******************************************************************************/
  48. #include <config.h>
  49. #include "openconnect-internal.h"
  50. #include "gnutls.h"
  51. #include <tss2/tss2_mu.h>
  52. #include <tss2/tss2_esys.h>
  53. #include <tss2/tss2_tctildr.h>
  54. #include <errno.h>
  55. #include <stdio.h>
  56. #include <string.h>
  57. struct oc_tpm2_ctx {
  58. TSS2_TCTI_CONTEXT *tcti_ctx;
  59. TPM2B_PUBLIC pub;
  60. TPM2B_PRIVATE priv;
  61. TPM2B_DIGEST userauth;
  62. TPM2B_DIGEST ownerauth;
  63. unsigned int need_userauth:1;
  64. unsigned int need_ownerauth:1;
  65. unsigned int did_ownerauth:1;
  66. unsigned int legacy_srk:1;
  67. unsigned int parent;
  68. };
  69. static const TPM2B_PUBLIC primaryTemplate = {
  70. .publicArea = {
  71. .type = TPM2_ALG_ECC,
  72. .nameAlg = TPM2_ALG_SHA256,
  73. .objectAttributes = (TPMA_OBJECT_USERWITHAUTH |
  74. TPMA_OBJECT_RESTRICTED |
  75. TPMA_OBJECT_DECRYPT |
  76. TPMA_OBJECT_FIXEDTPM |
  77. TPMA_OBJECT_FIXEDPARENT |
  78. TPMA_OBJECT_NODA |
  79. TPMA_OBJECT_SENSITIVEDATAORIGIN),
  80. .authPolicy = {
  81. .size = 0,
  82. },
  83. .parameters.eccDetail = {
  84. .symmetric = {
  85. .algorithm = TPM2_ALG_AES,
  86. .keyBits.aes = 128,
  87. .mode.aes = TPM2_ALG_CFB,
  88. },
  89. .scheme = {
  90. .scheme = TPM2_ALG_NULL,
  91. .details = {}
  92. },
  93. .curveID = TPM2_ECC_NIST_P256,
  94. .kdf = {
  95. .scheme = TPM2_ALG_NULL,
  96. .details = {}
  97. },
  98. },
  99. .unique.ecc = {
  100. .x.size = 0,
  101. .y.size = 0
  102. }
  103. }
  104. };
  105. static const TPM2B_PUBLIC primaryTemplate_legacy = {
  106. .publicArea = {
  107. .type = TPM2_ALG_ECC,
  108. .nameAlg = TPM2_ALG_SHA256,
  109. .objectAttributes = (TPMA_OBJECT_USERWITHAUTH |
  110. TPMA_OBJECT_RESTRICTED |
  111. TPMA_OBJECT_DECRYPT |
  112. TPMA_OBJECT_NODA |
  113. TPMA_OBJECT_SENSITIVEDATAORIGIN),
  114. .authPolicy = {
  115. .size = 0,
  116. },
  117. .parameters.eccDetail = {
  118. .symmetric = {
  119. .algorithm = TPM2_ALG_AES,
  120. .keyBits.aes = 128,
  121. .mode.aes = TPM2_ALG_CFB,
  122. },
  123. .scheme = {
  124. .scheme = TPM2_ALG_NULL,
  125. .details = {}
  126. },
  127. .curveID = TPM2_ECC_NIST_P256,
  128. .kdf = {
  129. .scheme = TPM2_ALG_NULL,
  130. .details = {}
  131. },
  132. },
  133. .unique.ecc = {
  134. .x.size = 0,
  135. .y.size = 0
  136. }
  137. }
  138. };
  139. static const TPM2B_SENSITIVE_CREATE primarySensitive = {
  140. .sensitive = {
  141. .userAuth = {
  142. .size = 0,
  143. },
  144. .data = {
  145. .size = 0,
  146. }
  147. }
  148. };
  149. static const TPM2B_DATA allOutsideInfo = {
  150. .size = 0,
  151. };
  152. static const TPML_PCR_SELECTION allCreationPCR = {
  153. .count = 0,
  154. };
  155. #define rc_is_key_auth_failed(rc) (((rc) & 0xff) == TPM2_RC_BAD_AUTH)
  156. #define rc_is_parent_auth_failed(rc) (((rc) & 0xff) == TPM2_RC_AUTH_FAIL)
  157. static void install_tpm_passphrase(struct openconnect_info *vpninfo, TPM2B_DIGEST *auth, char *pass)
  158. {
  159. int pwlen = strlen(pass);
  160. if (pwlen > sizeof(auth->buffer) - 1) {
  161. vpn_progress(vpninfo, PRG_ERR,
  162. _("TPM2 password too long; truncating\n"));
  163. pwlen = sizeof(auth->buffer) - 1;
  164. }
  165. auth->size = pwlen;
  166. memcpy(auth->buffer, pass, pwlen);
  167. pass[pwlen] = 0;
  168. free_pass(&pass);
  169. }
  170. static int init_tpm2_primary(struct openconnect_info *vpninfo, struct cert_info *certinfo,
  171. ESYS_CONTEXT *ctx, ESYS_TR *primaryHandle)
  172. {
  173. TSS2_RC r;
  174. const char *hierarchy_name;
  175. ESYS_TR hierarchy;
  176. switch(certinfo->tpm2->parent) {
  177. case TPM2_RH_OWNER: hierarchy = ESYS_TR_RH_OWNER; hierarchy_name = _("owner"); break;
  178. case TPM2_RH_NULL: hierarchy = ESYS_TR_RH_NULL; hierarchy_name = _("null"); break;
  179. case TPM2_RH_ENDORSEMENT:hierarchy = ESYS_TR_RH_ENDORSEMENT; hierarchy_name = _("endorsement"); break;
  180. case TPM2_RH_PLATFORM: hierarchy = ESYS_TR_RH_PLATFORM; hierarchy_name = _("platform"); break;
  181. default: return -EINVAL;
  182. }
  183. vpn_progress(vpninfo, PRG_DEBUG, _("Creating primary key under %s hierarchy.\n"), hierarchy_name);
  184. reauth:
  185. if (certinfo->tpm2->need_ownerauth) {
  186. char *pass = NULL;
  187. if (request_passphrase(vpninfo,
  188. certinfo_string(certinfo, "openconnect_tpm2_hierarchy",
  189. "openconnect_secondary_tpm2_hierarchy"),
  190. &pass,
  191. _("Enter TPM2 %s hierarchy password:"), hierarchy_name))
  192. return -EPERM;
  193. install_tpm_passphrase(vpninfo, &certinfo->tpm2->ownerauth, pass);
  194. certinfo->tpm2->need_ownerauth = 0;
  195. }
  196. r = Esys_TR_SetAuth(ctx, hierarchy, &certinfo->tpm2->ownerauth);
  197. if (r) {
  198. vpn_progress(vpninfo, PRG_ERR,
  199. _("TPM2 Esys_TR_SetAuth failed: 0x%x\n"),
  200. r);
  201. return -EPERM;
  202. }
  203. r = Esys_CreatePrimary(ctx, hierarchy,
  204. ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
  205. &primarySensitive,
  206. certinfo->tpm2->legacy_srk ? &primaryTemplate_legacy : &primaryTemplate,
  207. &allOutsideInfo, &allCreationPCR,
  208. primaryHandle, NULL, NULL, NULL, NULL);
  209. if (rc_is_key_auth_failed(r)) {
  210. vpn_progress(vpninfo, PRG_DEBUG,
  211. _("TPM2 Esys_CreatePrimary owner auth failed\n"));
  212. certinfo->tpm2->need_ownerauth = 1;
  213. goto reauth;
  214. } else if (r) {
  215. vpn_progress(vpninfo, PRG_ERR,
  216. _("TPM2 Esys_CreatePrimary failed: 0x%x\n"),
  217. r);
  218. return -EIO;
  219. }
  220. return 0;
  221. }
  222. #define parent_is_generated(parent) ((parent) >> TPM2_HR_SHIFT == TPM2_HT_PERMANENT)
  223. #define parent_is_persistent(parent) ((parent) >> TPM2_HR_SHIFT == TPM2_HT_PERSISTENT)
  224. static int init_tpm2_key(ESYS_CONTEXT **ctx, ESYS_TR *keyHandle,
  225. struct openconnect_info *vpninfo, struct cert_info *certinfo)
  226. {
  227. ESYS_TR parentHandle = ESYS_TR_NONE;
  228. TSS2_RC r;
  229. *keyHandle = ESYS_TR_NONE;
  230. vpn_progress(vpninfo, PRG_DEBUG,
  231. _("Establishing connection with TPM.\n"));
  232. r = Esys_Initialize(ctx, certinfo->tpm2->tcti_ctx, NULL);
  233. if (r) {
  234. vpn_progress(vpninfo, PRG_ERR,
  235. _("TPM2 Esys_Initialize failed: 0x%x\n"),
  236. r);
  237. goto error;
  238. }
  239. r = Esys_Startup(*ctx, TPM2_SU_CLEAR);
  240. if (r == TPM2_RC_INITIALIZE) {
  241. vpn_progress(vpninfo, PRG_DEBUG,
  242. _("TPM2 was already started up thus false positive failing in tpm2tss log.\n"));
  243. } else if (r) {
  244. vpn_progress(vpninfo, PRG_ERR,
  245. _("TPM2 Esys_Startup failed: 0x%x\n"),
  246. r);
  247. goto error;
  248. }
  249. if (parent_is_generated(certinfo->tpm2->parent)) {
  250. if (init_tpm2_primary(vpninfo, certinfo, *ctx, &parentHandle))
  251. goto error;
  252. } else {
  253. r = Esys_TR_FromTPMPublic(*ctx, certinfo->tpm2->parent,
  254. ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &parentHandle);
  255. if (r) {
  256. vpn_progress(vpninfo, PRG_ERR,
  257. _("Esys_TR_FromTPMPublic failed for handle 0x%x: 0x%x\n"),
  258. certinfo->tpm2->parent, r);
  259. goto error;
  260. }
  261. /* If we don't already have a password (and haven't already authenticated
  262. * successfully), check the NODA flag on the parent and demand one if DA
  263. * protection is enabled (since that strongly implies there is a non-empty
  264. * password). */
  265. if (!certinfo->tpm2->did_ownerauth && !certinfo->tpm2->ownerauth.size) {
  266. TPM2B_PUBLIC *pub = NULL;
  267. r = Esys_ReadPublic(*ctx, parentHandle, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
  268. &pub, NULL, NULL);
  269. if (!r && !(pub->publicArea.objectAttributes & TPMA_OBJECT_NODA))
  270. certinfo->tpm2->need_ownerauth = 1;
  271. Esys_Free(pub);
  272. }
  273. reauth:
  274. if (certinfo->tpm2->need_ownerauth) {
  275. char *pass = NULL;
  276. if (request_passphrase(vpninfo,
  277. certinfo_string(certinfo, "openconnect_tpm2_parent",
  278. "openconnect_secondary_tpm2_parent"),
  279. &pass,
  280. certinfo_string(certinfo, _("Enter TPM2 parent key password:"),
  281. _("Enter secondary TPM2 parent key password:"))))
  282. return -EPERM;
  283. install_tpm_passphrase(vpninfo, &certinfo->tpm2->ownerauth, pass);
  284. certinfo->tpm2->need_ownerauth = 0;
  285. }
  286. r = Esys_TR_SetAuth(*ctx, parentHandle, &certinfo->tpm2->ownerauth);
  287. if (r) {
  288. vpn_progress(vpninfo, PRG_ERR,
  289. _("TPM2 Esys_TR_SetAuth failed: 0x%x\n"),
  290. r);
  291. goto error;
  292. }
  293. }
  294. vpn_progress(vpninfo, PRG_DEBUG, _("Loading TPM2 key blob, parent %x.\n"), parentHandle);
  295. r = Esys_Load(*ctx, parentHandle,
  296. ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
  297. &certinfo->tpm2->priv, &certinfo->tpm2->pub,
  298. keyHandle);
  299. if (rc_is_parent_auth_failed(r)) {
  300. vpn_progress(vpninfo, PRG_DEBUG,
  301. _("TPM2 Esys_Load auth failed\n"));
  302. certinfo->tpm2->need_ownerauth = 1;
  303. goto reauth;
  304. }
  305. if (r) {
  306. vpn_progress(vpninfo, PRG_ERR,
  307. _("TPM2 Esys_Load failed: 0x%x\n"),
  308. r);
  309. goto error;
  310. }
  311. certinfo->tpm2->did_ownerauth = 1;
  312. if (parent_is_generated(certinfo->tpm2->parent)) {
  313. r = Esys_FlushContext(*ctx, parentHandle);
  314. if (r) {
  315. vpn_progress(vpninfo, PRG_ERR,
  316. _("TPM2 Esys_FlushContext for generated primary failed: 0x%x\n"),
  317. r);
  318. }
  319. /* But it's non-fatal. */
  320. }
  321. return 0;
  322. error:
  323. if (parent_is_generated(certinfo->tpm2->parent) && parentHandle != ESYS_TR_NONE)
  324. Esys_FlushContext(*ctx, parentHandle);
  325. if (*keyHandle != ESYS_TR_NONE)
  326. Esys_FlushContext(*ctx, *keyHandle);
  327. *keyHandle = ESYS_TR_NONE;
  328. Esys_Finalize(ctx);
  329. return -EIO;
  330. }
  331. static int auth_tpm2_key(struct openconnect_info *vpninfo, struct cert_info *certinfo,
  332. ESYS_CONTEXT *ctx, ESYS_TR key_handle)
  333. {
  334. TSS2_RC r;
  335. if (certinfo->tpm2->need_userauth) {
  336. char *pass = NULL;
  337. if (certinfo->password) {
  338. pass = certinfo->password;
  339. certinfo->password = NULL;
  340. } else {
  341. int err = request_passphrase(vpninfo,
  342. certinfo_string(certinfo, "openconnect_tpm2_key",
  343. "openconnect_secondary_tpm2_key"),
  344. &pass,
  345. certinfo_string(certinfo, _("Enter TPM2 key password:"),
  346. _("Enter secondary TPM2 key password:")));
  347. if (err)
  348. return err;
  349. }
  350. install_tpm_passphrase(vpninfo, &certinfo->tpm2->userauth, pass);
  351. certinfo->tpm2->need_userauth = 0;
  352. }
  353. r = Esys_TR_SetAuth(ctx, key_handle, &certinfo->tpm2->userauth);
  354. if (r) {
  355. vpn_progress(vpninfo, PRG_ERR,
  356. _("TPM2 Esys_TR_SetAuth failed: 0x%x\n"),
  357. r);
  358. return -EIO;
  359. }
  360. return 0;
  361. }
  362. int tpm2_rsa_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo,
  363. void *_certinfo, unsigned int flags,
  364. const gnutls_datum_t *data, gnutls_datum_t *sig)
  365. {
  366. struct cert_info *certinfo = _certinfo;
  367. struct openconnect_info *vpninfo = certinfo->vpninfo;
  368. int ret = GNUTLS_E_PK_SIGN_FAILED;
  369. ESYS_CONTEXT *ectx = NULL;
  370. TPM2B_PUBLIC_KEY_RSA digest, *tsig = NULL;
  371. TPM2B_DATA label = { .size = 0 };
  372. TPMT_RSA_DECRYPT inScheme = { .scheme = TPM2_ALG_NULL };
  373. ESYS_TR key_handle = ESYS_TR_NONE;
  374. TSS2_RC r;
  375. vpn_progress(vpninfo, PRG_DEBUG,
  376. _("TPM2 RSA sign function called for %d bytes, algo %s\n"),
  377. data->size, gnutls_sign_get_name(algo));
  378. digest.size = certinfo->tpm2->pub.publicArea.unique.rsa.size;
  379. if (oc_pad_rsasig(vpninfo, algo, digest.buffer, digest.size, data,
  380. certinfo->tpm2->pub.publicArea.parameters.rsaDetail.keyBits))
  381. return GNUTLS_E_PK_SIGN_FAILED;
  382. if (init_tpm2_key(&ectx, &key_handle, vpninfo, certinfo))
  383. goto out;
  384. reauth:
  385. if (auth_tpm2_key(vpninfo, certinfo, ectx, key_handle))
  386. goto out;
  387. r = Esys_RSA_Decrypt(ectx, key_handle,
  388. ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
  389. &digest, &inScheme, &label, &tsig);
  390. if (rc_is_key_auth_failed(r)) {
  391. vpn_progress(vpninfo, PRG_DEBUG,
  392. _("TPM2 Esys_RSA_Decrypt auth failed\n"));
  393. certinfo->tpm2->need_userauth = 1;
  394. goto reauth;
  395. }
  396. if (r) {
  397. vpn_progress(vpninfo, PRG_ERR,
  398. _("TPM2 failed to generate RSA signature: 0x%x\n"),
  399. r);
  400. goto out;
  401. }
  402. sig->data = malloc(tsig->size);
  403. if (!sig->data)
  404. goto out;
  405. memcpy(sig->data, tsig->buffer, tsig->size);
  406. sig->size = tsig->size;
  407. ret = 0;
  408. out:
  409. Esys_Free(tsig);
  410. if (key_handle != ESYS_TR_NONE)
  411. Esys_FlushContext(ectx, key_handle);
  412. if (ectx)
  413. Esys_Finalize(&ectx);
  414. return ret;
  415. }
  416. int tpm2_ec_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo,
  417. void *_certinfo, unsigned int flags,
  418. const gnutls_datum_t *data, gnutls_datum_t *sig)
  419. {
  420. struct cert_info *certinfo = _certinfo;
  421. struct openconnect_info *vpninfo = certinfo->vpninfo;
  422. int ret = GNUTLS_E_PK_SIGN_FAILED;
  423. ESYS_CONTEXT *ectx = NULL;
  424. TPM2B_DIGEST digest;
  425. TPMT_SIGNATURE *tsig = NULL;
  426. ESYS_TR key_handle = ESYS_TR_NONE;
  427. TSS2_RC r;
  428. TPMT_TK_HASHCHECK validation = { .tag = TPM2_ST_HASHCHECK,
  429. .hierarchy = TPM2_RH_NULL,
  430. .digest.size = 0 };
  431. TPMT_SIG_SCHEME inScheme = { .scheme = TPM2_ALG_ECDSA };
  432. gnutls_datum_t sig_r, sig_s;
  433. vpn_progress(vpninfo, PRG_DEBUG,
  434. _("TPM2 EC sign function called for %d bytes.\n"),
  435. data->size);
  436. /* FIPS-186-4 §6.4 says "When the length of the output of the hash
  437. * function is greater than the bit length of n, then the leftmost
  438. * n bits of the hash function output block shall be used in any
  439. * calculation using the hash function output during the generation
  440. * or verification of a digital signature."
  441. *
  442. * So GnuTLS is expected to *truncate* a larger hash to fit the
  443. * curve bit length, and then we lie to the TPM about which hash
  444. * it was because the TPM only really cares about the size of the
  445. * data anyway. */
  446. switch (data->size) {
  447. case SHA1_SIZE: inScheme.details.ecdsa.hashAlg = TPM2_ALG_SHA1; break;
  448. case SHA256_SIZE: inScheme.details.ecdsa.hashAlg = TPM2_ALG_SHA256; break;
  449. case SHA384_SIZE: inScheme.details.ecdsa.hashAlg = TPM2_ALG_SHA384; break;
  450. case SHA512_SIZE: inScheme.details.ecdsa.hashAlg = TPM2_ALG_SHA512; break;
  451. default:
  452. vpn_progress(vpninfo, PRG_ERR,
  453. _("Unknown TPM2 EC digest size %d for algo 0x%x\n"),
  454. data->size, algo);
  455. return GNUTLS_E_PK_SIGN_FAILED;
  456. }
  457. memcpy(digest.buffer, data->data, data->size);
  458. digest.size = data->size;
  459. if (init_tpm2_key(&ectx, &key_handle, vpninfo, certinfo))
  460. goto out;
  461. reauth:
  462. if (auth_tpm2_key(vpninfo, certinfo, ectx, key_handle))
  463. goto out;
  464. r = Esys_Sign(ectx, key_handle,
  465. ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
  466. &digest, &inScheme, &validation,
  467. &tsig);
  468. if (rc_is_key_auth_failed(r)) {
  469. vpn_progress(vpninfo, PRG_DEBUG,
  470. _("TPM2 Esys_Sign auth failed\n"));
  471. certinfo->tpm2->need_userauth = 1;
  472. goto reauth;
  473. }
  474. if (r) {
  475. vpn_progress(vpninfo, PRG_ERR,
  476. _("TPM2 failed to generate RSA signature: 0x%x\n"),
  477. r);
  478. goto out;
  479. }
  480. sig_r.data = tsig->signature.ecdsa.signatureR.buffer;
  481. sig_r.size = tsig->signature.ecdsa.signatureR.size;
  482. sig_s.data = tsig->signature.ecdsa.signatureS.buffer;
  483. sig_s.size = tsig->signature.ecdsa.signatureS.size;
  484. ret = gnutls_encode_rs_value(sig, &sig_r, &sig_s);
  485. out:
  486. Esys_Free(tsig);
  487. if (key_handle != ESYS_TR_NONE)
  488. Esys_FlushContext(ectx, key_handle);
  489. if (ectx)
  490. Esys_Finalize(&ectx);
  491. return ret;
  492. }
  493. int install_tpm2_key(struct openconnect_info *vpninfo, struct cert_info *certinfo,
  494. gnutls_privkey_t *pkey, gnutls_datum_t *pkey_sig,
  495. unsigned int parent, int emptyauth, int legacy,
  496. gnutls_datum_t *privdata, gnutls_datum_t *pubdata)
  497. {
  498. TSS2_RC r;
  499. if (!parent_is_persistent(parent) &&
  500. parent != TPM2_RH_OWNER && parent != TPM2_RH_NULL &&
  501. parent != TPM2_RH_ENDORSEMENT && parent != TPM2_RH_PLATFORM) {
  502. vpn_progress(vpninfo, PRG_ERR,
  503. _("Invalid TPM2 parent handle 0x%08x\n"), parent);
  504. return -EINVAL;
  505. }
  506. certinfo->tpm2 = calloc(1, sizeof(*certinfo->tpm2));
  507. if (!certinfo->tpm2)
  508. return -ENOMEM;
  509. certinfo->tpm2->parent = parent;
  510. /* This is the variable which the *IBM* TSS uses, to force it to use
  511. * the swtpm; it happens in the library automatically. To allow the
  512. * swtpm test to work on platforms where a real TPM is available,
  513. * emulate the same thing. Not really intended for production use. */
  514. const char *tpm_type = getenv("TPM_INTERFACE_TYPE");
  515. if (tpm_type && !strcmp(tpm_type, "socsim")) {
  516. vpn_progress(vpninfo, PRG_DEBUG,
  517. _("Using SWTPM due to TPM_INTERFACE_TYPE environment variable\n"));
  518. r = Tss2_TctiLdr_Initialize("swtpm", &certinfo->tpm2->tcti_ctx);
  519. if (r) {
  520. vpn_progress(vpninfo, PRG_ERR,
  521. _("TSS2_TctiLdr_Initialize failed for swtpm: 0x%x\n"),
  522. r);
  523. goto err_out;
  524. }
  525. }
  526. r = Tss2_MU_TPM2B_PRIVATE_Unmarshal(privdata->data, privdata->size, NULL,
  527. &certinfo->tpm2->priv);
  528. if (r) {
  529. vpn_progress(vpninfo, PRG_ERR,
  530. _("Failed to import TPM2 private key data: 0x%x\n"),
  531. r);
  532. goto err_out;
  533. }
  534. r = Tss2_MU_TPM2B_PUBLIC_Unmarshal(pubdata->data, pubdata->size, NULL,
  535. &certinfo->tpm2->pub);
  536. if (r) {
  537. vpn_progress(vpninfo, PRG_ERR,
  538. _("Failed to import TPM2 public key data: 0x%x\n"),
  539. r);
  540. goto err_out;
  541. }
  542. certinfo->tpm2->need_userauth = !emptyauth;
  543. certinfo->tpm2->legacy_srk = legacy;
  544. switch(certinfo->tpm2->pub.publicArea.type) {
  545. case TPM2_ALG_RSA: return GNUTLS_PK_RSA;
  546. case TPM2_ALG_ECC: return GNUTLS_PK_ECC;
  547. }
  548. vpn_progress(vpninfo, PRG_ERR,
  549. _("Unsupported TPM2 key type %d\n"),
  550. certinfo->tpm2->pub.publicArea.type);
  551. err_out:
  552. release_tpm2_ctx(vpninfo, certinfo);
  553. return -EINVAL;
  554. }
  555. uint16_t tpm2_key_curve(struct openconnect_info *vpninfo, struct cert_info *certinfo)
  556. {
  557. return certinfo->tpm2->pub.publicArea.parameters.eccDetail.curveID;
  558. }
  559. int tpm2_rsa_key_bits(struct openconnect_info *vpninfo, struct cert_info *certinfo)
  560. {
  561. return certinfo->tpm2->pub.publicArea.parameters.rsaDetail.keyBits;
  562. }
  563. void release_tpm2_ctx(struct openconnect_info *vpninfo, struct cert_info *certinfo)
  564. {
  565. if (certinfo->tpm2) {
  566. clear_mem(certinfo->tpm2->ownerauth.buffer, sizeof(certinfo->tpm2->ownerauth.buffer));
  567. clear_mem(certinfo->tpm2->userauth.buffer, sizeof(certinfo->tpm2->userauth.buffer));
  568. if (certinfo->tpm2->tcti_ctx)
  569. Tss2_TctiLdr_Finalize(&certinfo->tpm2->tcti_ctx);
  570. free(certinfo->tpm2);
  571. }
  572. certinfo->tpm2 = NULL;
  573. }