diff --git a/gnutls.h b/gnutls.h index 7e40707b..0e28ed9f 100644 --- a/gnutls.h +++ b/gnutls.h @@ -34,6 +34,13 @@ 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); +int tpm2_rsa_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo, + void *_vpninfo, unsigned int flags, + const gnutls_datum_t *data, gnutls_datum_t *sig); +int tpm2_ec_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo, + void *_vpninfo, unsigned int flags, + const gnutls_datum_t *data, gnutls_datum_t *sig); + /* GnuTLS 3.6.0+ provides this. We have our own for older GnuTLS. There is * also _gnutls_encode_ber_rs_raw() in some older versions, but there were * zero-padding bugs in that, and some of the... less diligently maintained diff --git a/gnutls_tpm2.c b/gnutls_tpm2.c index 34b9f0cb..618fdd18 100644 --- a/gnutls_tpm2.c +++ b/gnutls_tpm2.c @@ -45,6 +45,90 @@ const asn1_static_node tpmkey_asn1_tab[] = { { NULL, 0, NULL } }; +#if GNUTLS_VERSION_NUMBER < 0x030600 +static int tpm2_rsa_sign_fn(gnutls_privkey_t key, void *_vpninfo, + const gnutls_datum_t *data, gnutls_datum_t *sig) +{ + return tpm2_rsa_sign_hash_fn(key, GNUTLS_SIGN_UNKNOWN, _vpninfo, 0, data, sig); +} + + +static int tpm2_ec_sign_fn(gnutls_privkey_t key, void *_vpninfo, + const gnutls_datum_t *data, gnutls_datum_t *sig) +{ + struct openconnect_info *vpninfo = _vpninfo; + gnutls_sign_algorithm_t algo; + + switch (data->size) { + case 20: algo = GNUTLS_SIGN_ECDSA_SHA1; break; + case 32: algo = GNUTLS_SIGN_ECDSA_SHA256; break; + case 48: algo = GNUTLS_SIGN_ECDSA_SHA384; break; + case 64: algo = GNUTLS_SIGN_ECDSA_SHA512; break; + default: + vpn_progress(vpninfo, PRG_ERR, + _("Unknown TPM2 EC digest size %d\n"), + data->size); + return GNUTLS_E_PK_SIGN_FAILED; + } + + return tpm2_ec_sign_hash_fn(key, algo, vpninfo, 0, data, sig); +} +#endif + +#if GNUTLS_VERSION_NUMBER >= 0x030600 +static int rsa_key_info(gnutls_privkey_t key, unsigned int flags, void *_vpninfo) +{ + if (flags & GNUTLS_PRIVKEY_INFO_PK_ALGO) + return GNUTLS_PK_RSA; + + if (flags & GNUTLS_PRIVKEY_INFO_HAVE_SIGN_ALGO) { + gnutls_sign_algorithm_t algo = GNUTLS_FLAGS_TO_SIGN_ALGO(flags); + switch (algo) { + case GNUTLS_SIGN_RSA_RAW: + case GNUTLS_SIGN_RSA_SHA1: + case GNUTLS_SIGN_RSA_SHA256: + case GNUTLS_SIGN_RSA_SHA384: + case GNUTLS_SIGN_RSA_SHA512: + return 1; + + default: + return 0; + } + } + + if (flags & GNUTLS_PRIVKEY_INFO_SIGN_ALGO) + return GNUTLS_SIGN_RSA_RAW; + + return -1; +} +#endif + +#if GNUTLS_VERSION_NUMBER >= 0x030400 +static int ec_key_info(gnutls_privkey_t key, unsigned int flags, void *_vpninfo) +{ + if (flags & GNUTLS_PRIVKEY_INFO_PK_ALGO) + return GNUTLS_PK_EC; + +#ifdef GNUTLS_PRIVKEY_INFO_HAVE_SIGN_ALGO + if (flags & GNUTLS_PRIVKEY_INFO_HAVE_SIGN_ALGO) { + gnutls_sign_algorithm_t algo = GNUTLS_FLAGS_TO_SIGN_ALGO(flags); + switch (algo) { + case GNUTLS_SIGN_ECDSA_SHA1: + case GNUTLS_SIGN_ECDSA_SHA256: + return 1; + + default: + return 0; + } + } +#endif + + if (flags & GNUTLS_PRIVKEY_INFO_SIGN_ALGO) + return GNUTLS_SIGN_ECDSA_SHA256; + + return -1; +} +#endif static int decode_data(ASN1_TYPE n, gnutls_datum_t *r) { @@ -159,6 +243,31 @@ 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); + if (ret < 0) + goto out_tpmkey; + + gnutls_privkey_init(pkey); + + switch(ret) { + case GNUTLS_PK_RSA: +#if GNUTLS_VERSION_NUMBER >= 0x030600 + gnutls_privkey_import_ext4(*pkey, vpninfo, NULL, tpm2_rsa_sign_hash_fn, NULL, NULL, rsa_key_info, 0); +#else + gnutls_privkey_import_ext(*pkey, GNUTLS_PK_RSA, vpninfo, tpm2_rsa_sign_fn, NULL, 0); +#endif + break; + + case GNUTLS_PK_ECDSA: +#if GNUTLS_VERSION_NUMBER >= 0x030600 + gnutls_privkey_import_ext4(*pkey, vpninfo, NULL, tpm2_ec_sign_hash_fn, NULL, NULL, ec_key_info, 0); +#elif GNUTLS_VERSION_NUMBER >= 0x030400 + gnutls_privkey_import_ext3(*pkey, vpninfo, tpm2_ec_sign_fn, NULL, NULL, ec_key_info, 0); +#else + gnutls_privkey_import_ext(*pkey, GNUTLS_PK_EC, vpninfo, tpm2_ec_sign_fn, NULL, 0); +#endif + break; + } + ret = 0; out_tpmkey: asn1_delete_structure(&tpmkey); diff --git a/gnutls_tpm2_esys.c b/gnutls_tpm2_esys.c index 438fea86..677cd6ca 100644 --- a/gnutls_tpm2_esys.c +++ b/gnutls_tpm2_esys.c @@ -312,10 +312,9 @@ static int auth_tpm2_key(struct openconnect_info *vpninfo, ESYS_CONTEXT *ctx, ES #define PKCS1_PAD_OVERHEAD 11 -/* Signing function for TPM privkeys, set with gnutls_privkey_import_ext() */ -static int tpm2_rsa_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo, - void *_vpninfo, unsigned int flags, - const gnutls_datum_t *data, gnutls_datum_t *sig) +int tpm2_rsa_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo, + void *_vpninfo, unsigned int flags, + const gnutls_datum_t *data, gnutls_datum_t *sig) { struct openconnect_info *vpninfo = _vpninfo; int ret = GNUTLS_E_PK_SIGN_FAILED; @@ -389,10 +388,9 @@ static int tpm2_rsa_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t a return ret; } -/* Signing function for TPM privkeys, set with gnutls_privkey_import_ext() */ -static int tpm2_ec_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo, - void *_vpninfo, unsigned int flags, - const gnutls_datum_t *data, gnutls_datum_t *sig) +int tpm2_ec_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo, + void *_vpninfo, unsigned int flags, + const gnutls_datum_t *data, gnutls_datum_t *sig) { struct openconnect_info *vpninfo = _vpninfo; int ret = GNUTLS_E_PK_SIGN_FAILED; @@ -467,91 +465,6 @@ static int tpm2_ec_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t al return ret; } -#if GNUTLS_VERSION_NUMBER < 0x030600 -static int tpm2_rsa_sign_fn(gnutls_privkey_t key, void *_vpninfo, - const gnutls_datum_t *data, gnutls_datum_t *sig) -{ - return tpm2_rsa_sign_hash_fn(key, GNUTLS_SIGN_UNKNOWN, _vpninfo, 0, data, sig); -} - - -static int tpm2_ec_sign_fn(gnutls_privkey_t key, void *_vpninfo, - const gnutls_datum_t *data, gnutls_datum_t *sig) -{ - struct openconnect_info *vpninfo = _vpninfo; - gnutls_sign_algorithm_t algo; - - switch (data->size) { - case 20: algo = GNUTLS_SIGN_ECDSA_SHA1; break; - case 32: algo = GNUTLS_SIGN_ECDSA_SHA256; break; - case 48: algo = GNUTLS_SIGN_ECDSA_SHA384; break; - case 64: algo = GNUTLS_SIGN_ECDSA_SHA512; break; - default: - vpn_progress(vpninfo, PRG_ERR, - _("Unknown TPM2 EC digest size %d\n"), - data->size); - return GNUTLS_E_PK_SIGN_FAILED; - } - - return tpm2_ec_sign_hash_fn(key, algo, vpninfo, 0, data, sig); -} -#endif - -#if GNUTLS_VERSION_NUMBER >= 0x030600 -static int rsa_key_info(gnutls_privkey_t key, unsigned int flags, void *_vpninfo) -{ - if (flags & GNUTLS_PRIVKEY_INFO_PK_ALGO) - return GNUTLS_PK_RSA; - - if (flags & GNUTLS_PRIVKEY_INFO_HAVE_SIGN_ALGO) { - gnutls_sign_algorithm_t algo = GNUTLS_FLAGS_TO_SIGN_ALGO(flags); - switch (algo) { - case GNUTLS_SIGN_RSA_RAW: - case GNUTLS_SIGN_RSA_SHA1: - case GNUTLS_SIGN_RSA_SHA256: - case GNUTLS_SIGN_RSA_SHA384: - case GNUTLS_SIGN_RSA_SHA512: - return 1; - - default: - return 0; - } - } - - if (flags & GNUTLS_PRIVKEY_INFO_SIGN_ALGO) - return GNUTLS_SIGN_RSA_RAW; - - return -1; -} -#endif - -#if GNUTLS_VERSION_NUMBER >= 0x030400 -static int ec_key_info(gnutls_privkey_t key, unsigned int flags, void *_vpninfo) -{ - if (flags & GNUTLS_PRIVKEY_INFO_PK_ALGO) - return GNUTLS_PK_EC; - -#ifdef GNUTLS_PRIVKEY_INFO_HAVE_SIGN_ALGO - if (flags & GNUTLS_PRIVKEY_INFO_HAVE_SIGN_ALGO) { - gnutls_sign_algorithm_t algo = GNUTLS_FLAGS_TO_SIGN_ALGO(flags); - switch (algo) { - case GNUTLS_SIGN_ECDSA_SHA1: - case GNUTLS_SIGN_ECDSA_SHA256: - return 1; - - default: - return 0; - } - } -#endif - - if (flags & GNUTLS_PRIVKEY_INFO_SIGN_ALGO) - return GNUTLS_SIGN_ECDSA_SHA256; - - return -1; -} -#endif - 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) { @@ -574,9 +487,7 @@ int install_tpm2_key(struct openconnect_info *vpninfo, gnutls_privkey_t *pkey, g vpn_progress(vpninfo, PRG_ERR, _("Failed to import TPM2 private key data: 0x%x\n"), r); - err_out: - release_tpm2_ctx(vpninfo); - return -EINVAL; + goto err_out; } r = Tss2_MU_TPM2B_PUBLIC_Unmarshal(pubdata->data, pubdata->size, NULL, @@ -590,37 +501,17 @@ int install_tpm2_key(struct openconnect_info *vpninfo, gnutls_privkey_t *pkey, g vpninfo->tpm2->need_userauth = !emptyauth; - gnutls_privkey_init(pkey); - switch(vpninfo->tpm2->pub.publicArea.type) { - case TPM2_ALG_RSA: -#if GNUTLS_VERSION_NUMBER >= 0x030600 - gnutls_privkey_import_ext4(*pkey, vpninfo, NULL, tpm2_rsa_sign_hash_fn, NULL, NULL, rsa_key_info, 0); -#else - gnutls_privkey_import_ext(*pkey, GNUTLS_PK_RSA, vpninfo, tpm2_rsa_sign_fn, NULL, 0); -#endif - break; - - case TPM2_ALG_ECC: -#if GNUTLS_VERSION_NUMBER >= 0x030600 - gnutls_privkey_import_ext4(*pkey, vpninfo, NULL, tpm2_ec_sign_hash_fn, NULL, NULL, ec_key_info, 0); -#elif GNUTLS_VERSION_NUMBER >= 0x030400 - gnutls_privkey_import_ext3(*pkey, vpninfo, tpm2_ec_sign_fn, NULL, NULL, ec_key_info, 0); -#else - gnutls_privkey_import_ext(*pkey, GNUTLS_PK_EC, vpninfo, tpm2_ec_sign_fn, NULL, 0); -#endif - break; - - default: - vpn_progress(vpninfo, PRG_ERR, - _("Unsupported TPM2 key type %d\n"), - vpninfo->tpm2->pub.publicArea.type); - gnutls_privkey_deinit(*pkey); - *pkey = NULL; - goto err_out; + case TPM2_ALG_RSA: return GNUTLS_PK_RSA; + case TPM2_ALG_ECC: return GNUTLS_PK_ECDSA; } - return 0; + vpn_progress(vpninfo, PRG_ERR, + _("Unsupported TPM2 key type %d\n"), + vpninfo->tpm2->pub.publicArea.type); + err_out: + release_tpm2_ctx(vpninfo); + return -EINVAL; }