diff --git a/gnutls.c b/gnutls.c index b3b61a89..b8bd8b6b 100644 --- a/gnutls.c +++ b/gnutls.c @@ -1320,7 +1320,8 @@ static int load_certificate(struct openconnect_info *vpninfo) } /* Is it a PEM file with a TPM key blob? */ - if (strstr((char *)fdata.data, "-----BEGIN TSS2 PRIVATE KEY-----")) { + if (strstr((char *)fdata.data, "-----BEGIN TSS2 PRIVATE KEY-----") || + strstr((char *)fdata.data, "-----BEGIN TSS2 KEY BLOB-----")) { #ifndef HAVE_TSS2 vpn_progress(vpninfo, PRG_ERR, _("This version of OpenConnect was built without TPM2 support\n")); diff --git a/gnutls.h b/gnutls.h index cd79f409..b25a15ae 100644 --- a/gnutls.h +++ b/gnutls.h @@ -32,7 +32,8 @@ int load_tpm2_key(struct openconnect_info *vpninfo, gnutls_datum_t *fdata, gnutls_privkey_t *pkey, gnutls_datum_t *pkey_sig); void release_tpm2_ctx(struct openconnect_info *info); int install_tpm2_key(struct openconnect_info *vpninfo, gnutls_privkey_t *pkey, gnutls_datum_t *pkey_sig, - unsigned int parent, int emptyauth, gnutls_datum_t *privdata, gnutls_datum_t *pubdata); + unsigned int parent, int emptyauth, int legacy, + gnutls_datum_t *privdata, gnutls_datum_t *pubdata); int tpm2_rsa_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo, void *_vpninfo, unsigned int flags, diff --git a/gnutls_tpm2.c b/gnutls_tpm2.c index abca9c4f..90fc1786 100644 --- a/gnutls_tpm2.c +++ b/gnutls_tpm2.c @@ -51,6 +51,21 @@ const asn1_static_node tpmkey_asn1_tab[] = { { NULL, 0, NULL } }; +const asn1_static_node tpmkey_asn1_tab_old[] = { + { "TPMKey", 536875024, NULL }, + { NULL, 1073741836, NULL }, + { "TPMKey", 536870917, NULL }, + { "type", 1073741836, NULL }, + { "emptyAuth", 1610637316, NULL }, + { NULL, 2056, "0"}, + { "parent", 1610637315, NULL }, + { NULL, 2056, "1"}, + { "pubkey", 1610637319, NULL }, + { NULL, 2056, "2"}, + { "privkey", 7, NULL }, + { NULL, 0, NULL } +}; + #if GNUTLS_VERSION_NUMBER < 0x030600 static int tpm2_rsa_sign_fn(gnutls_privkey_t key, void *_vpninfo, @@ -168,16 +183,22 @@ int load_tpm2_key(struct openconnect_info *vpninfo, gnutls_datum_t *fdata, int emptyauth = 0; unsigned int parent; int err, ret = -EINVAL; + const asn1_static_node *asn1tab; err = gnutls_pem_base64_decode_alloc("TSS2 PRIVATE KEY", fdata, &asn1); - if (err) { - vpn_progress(vpninfo, PRG_ERR, - _("Error decoding TSS2 key blob: %s\n"), - gnutls_strerror(err)); - return -EINVAL; + if (!err) { + asn1tab = tpmkey_asn1_tab; + } else { + if (gnutls_pem_base64_decode_alloc("TSS2 KEY BLOB", fdata, &asn1)) { + /* Report the first error */ + vpn_progress(vpninfo, PRG_ERR, + _("Error decoding TSS2 key blob: %s\n"), + gnutls_strerror(err)); + return -EINVAL; + } + asn1tab = tpmkey_asn1_tab_old; } - - err = asn1_array2tree(tpmkey_asn1_tab, &tpmkey_def, NULL); + err = asn1_array2tree(asn1tab, &tpmkey_def, NULL); if (err != ASN1_SUCCESS) { vpn_progress(vpninfo, PRG_ERR, _("Failed to create ASN.1 type for TPM2: %s\n"), @@ -243,7 +264,8 @@ int load_tpm2_key(struct openconnect_info *vpninfo, gnutls_datum_t *fdata, /* Now we've extracted what we need from the ASN.1, invoke the * actual TPM2 code (whichever implementation we end up with */ - ret = install_tpm2_key(vpninfo, pkey, pkey_sig, parent, emptyauth, &privdata, &pubdata); + ret = install_tpm2_key(vpninfo, pkey, pkey_sig, parent, emptyauth, + asn1tab == tpmkey_asn1_tab_old, &privdata, &pubdata); if (ret < 0) goto out_tpmkey; diff --git a/gnutls_tpm2_esys.c b/gnutls_tpm2_esys.c index 149c7d03..282dcd25 100644 --- a/gnutls_tpm2_esys.c +++ b/gnutls_tpm2_esys.c @@ -66,6 +66,7 @@ struct oc_tpm2_ctx { TPM2B_DIGEST ownerauth; unsigned int need_userauth:1; unsigned int need_ownerauth:1; + unsigned int legacy_srk:1; unsigned int parent; }; @@ -106,6 +107,41 @@ static TPM2B_PUBLIC primaryTemplate = { } }; +static TPM2B_PUBLIC primaryTemplate_legacy = { + .publicArea = { + .type = TPM2_ALG_ECC, + .nameAlg = TPM2_ALG_SHA256, + .objectAttributes = (TPMA_OBJECT_USERWITHAUTH | + TPMA_OBJECT_RESTRICTED | + TPMA_OBJECT_DECRYPT | + TPMA_OBJECT_NODA | + TPMA_OBJECT_SENSITIVEDATAORIGIN), + .authPolicy = { + .size = 0, + }, + .parameters.eccDetail = { + .symmetric = { + .algorithm = TPM2_ALG_AES, + .keyBits.aes = 128, + .mode.aes = TPM2_ALG_CFB, + }, + .scheme = { + .scheme = TPM2_ALG_NULL, + .details = {} + }, + .curveID = TPM2_ECC_NIST_P256, + .kdf = { + .scheme = TPM2_ALG_NULL, + .details = {} + }, + }, + .unique.ecc = { + .x.size = 0, + .y.size = 0 + } + } +}; + static TPM2B_SENSITIVE_CREATE primarySensitive = { .sensitive = { .userAuth = { @@ -175,7 +211,8 @@ static int init_tpm2_primary(struct openconnect_info *vpninfo, } r = Esys_CreatePrimary(ctx, hierarchy, ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE, - &primarySensitive, &primaryTemplate, + &primarySensitive, + vpninfo->tpm2->legacy_srk ? &primaryTemplate_legacy : &primaryTemplate, &allOutsideInfo, &allCreationPCR, primaryHandle, NULL, NULL, NULL, NULL); if (r == KEY_AUTH_FAILED) { @@ -480,7 +517,7 @@ int tpm2_ec_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo, } int install_tpm2_key(struct openconnect_info *vpninfo, gnutls_privkey_t *pkey, gnutls_datum_t *pkey_sig, - unsigned int parent, int emptyauth, gnutls_datum_t *privdata, gnutls_datum_t *pubdata) + unsigned int parent, int emptyauth, int legacy, gnutls_datum_t *privdata, gnutls_datum_t *pubdata) { TSS2_RC r; @@ -517,6 +554,7 @@ int install_tpm2_key(struct openconnect_info *vpninfo, gnutls_privkey_t *pkey, g } vpninfo->tpm2->need_userauth = !emptyauth; + vpninfo->tpm2->legacy_srk = legacy; switch(vpninfo->tpm2->pub.publicArea.type) { case TPM2_ALG_RSA: return GNUTLS_PK_RSA; diff --git a/gnutls_tpm2_ibm.c b/gnutls_tpm2_ibm.c index e5cc28a2..a7584532 100644 --- a/gnutls_tpm2_ibm.c +++ b/gnutls_tpm2_ibm.c @@ -40,6 +40,7 @@ struct oc_tpm2_ctx { TPM2B_PRIVATE priv; char *parent_pass, *key_pass; unsigned int need_userauth:1; + unsigned int legacy_srk:1; unsigned int parent; }; @@ -188,7 +189,8 @@ static void tpm2_flush_srk(TSS_CONTEXT *tssContext, TPM_HANDLE hSRK) static TPM_RC tpm2_load_srk(TSS_CONTEXT *tssContext, TPM_HANDLE *h, - const char *auth, TPM_HANDLE hierarchy) + const char *auth, TPM_HANDLE hierarchy, + int legacy_srk) { TPM_RC rc; CreatePrimary_In in; @@ -215,13 +217,15 @@ static TPM_RC tpm2_load_srk(TSS_CONTEXT *tssContext, TPM_HANDLE *h, in.inPublic.publicArea.type = TPM_ALG_ECC; in.inPublic.publicArea.nameAlg = TPM_ALG_SHA256; in.inPublic.publicArea.objectAttributes.val = - TPMA_OBJECT_FIXEDPARENT | - TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_NODA | TPMA_OBJECT_SENSITIVEDATAORIGIN | TPMA_OBJECT_USERWITHAUTH | TPMA_OBJECT_DECRYPT | TPMA_OBJECT_RESTRICTED; + if (!legacy_srk) + in.inPublic.publicArea.objectAttributes.val |= + TPMA_OBJECT_FIXEDPARENT | TPMA_OBJECT_FIXEDTPM; + in.inPublic.publicArea.parameters.eccDetail.symmetric.algorithm = TPM_ALG_AES; in.inPublic.publicArea.parameters.eccDetail.symmetric.keyBits.aes = 128; in.inPublic.publicArea.parameters.eccDetail.symmetric.mode.aes = TPM_ALG_CFB; @@ -295,7 +299,7 @@ static TPM_HANDLE tpm2_load_key(struct openconnect_info *vpninfo, TSS_CONTEXT ** in.parentHandle = vpninfo->tpm2->parent; } else { reauth_srk: - rc = tpm2_load_srk(tssContext, &in.parentHandle, pass, vpninfo->tpm2->parent); + rc = tpm2_load_srk(tssContext, &in.parentHandle, pass, vpninfo->tpm2->parent, vpninfo->tpm2->legacy_srk); if (rc == KEY_AUTH_FAILED) { free_pass(&pass); if (!request_passphrase(vpninfo, "openconnect_tpm2_hierarchy", &pass, @@ -511,7 +515,8 @@ int tpm2_ec_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo, } int install_tpm2_key(struct openconnect_info *vpninfo, gnutls_privkey_t *pkey, gnutls_datum_t *pkey_sig, - unsigned int parent, int emptyauth, gnutls_datum_t *privdata, gnutls_datum_t *pubdata) + unsigned int parent, int emptyauth, int legacy, + gnutls_datum_t *privdata, gnutls_datum_t *pubdata) { TPM_RC rc; BYTE *der; @@ -531,6 +536,7 @@ int install_tpm2_key(struct openconnect_info *vpninfo, gnutls_privkey_t *pkey, g vpninfo->tpm2->parent = parent; vpninfo->tpm2->need_userauth = !emptyauth; + vpninfo->tpm2->legacy_srk = legacy; der = privdata->data; dersize = privdata->size;