From b890d311191e71da49bba537c0ae16dbb7ae196a Mon Sep 17 00:00:00 2001 From: Petr Gotthard Date: Sat, 25 Apr 2026 20:06:16 +0200 Subject: [PATCH] fix loading of parent handles in TPM TSS2 keys Fixes: #152 --- Makefile.am | 2 ++ src/tpm2-provider-pkey.c | 38 ++++++++++++++++++--------- test/rsa_encodeobject_check.sh | 27 +++++++++++++++++++ test/rsa_encodeobject_check_parent.sh | 29 ++++++++++++++++++++ 4 files changed, 84 insertions(+), 12 deletions(-) create mode 100755 test/rsa_encodeobject_check.sh create mode 100755 test/rsa_encodeobject_check_parent.sh diff --git a/Makefile.am b/Makefile.am index e0163e6..c531141 100644 --- a/Makefile.am +++ b/Makefile.am @@ -100,6 +100,8 @@ TESTS_SHELL = test/list.sh \ test/rsa_genpkey_sign.sh \ test/rsa_genpkey_sign_rawin.sh \ test/rsa_genpkey_auth_parent.sh \ + test/rsa_encodeobject_check.sh \ + test/rsa_encodeobject_check_parent.sh \ test/rsa_createak_auth.sh \ test/rsa_createak_sign_object.sh \ test/rsa_createak_sign_handle.sh \ diff --git a/src/tpm2-provider-pkey.c b/src/tpm2-provider-pkey.c index f739c17..97fb4dd 100644 --- a/src/tpm2-provider-pkey.c +++ b/src/tpm2-provider-pkey.c @@ -53,6 +53,8 @@ int tpm2_keydata_write(const TPM2_KEYDATA *keydata, BIO *bout, TPM2_PKEY_FORMAT format) { TSSPRIVKEY *tpk = NULL; + BIGNUM *bn_parent = NULL; + int res = 0; uint8_t privbuf[sizeof(keydata->priv)]; uint8_t pubbuf[sizeof(keydata->pub)]; @@ -74,13 +76,18 @@ tpm2_keydata_write(const TPM2_KEYDATA *keydata, BIO *bout, TPM2_PKEY_FORMAT form if (!tpk->type) goto error; - // note the ASN1_INTEGER_set is not reliable for uin32_t on 32-bit machines tpk->emptyAuth = ! !keydata->emptyAuth; + + // note the ASN1_INTEGER_set is not reliable for uin32_t on 32-bit machines + if (!(bn_parent = BN_new())) + goto error; + if (keydata->parent != 0) - ASN1_INTEGER_set_uint64(tpk->parent, keydata->parent); + BN_set_word(bn_parent, keydata->parent); else - ASN1_INTEGER_set_uint64(tpk->parent, TPM2_RH_OWNER); + BN_set_word(bn_parent, TPM2_RH_OWNER); + BN_to_ASN1_INTEGER(bn_parent, tpk->parent); ASN1_STRING_set(tpk->privkey, &privbuf[0], privbuf_len); ASN1_STRING_set(tpk->pubkey, &pubbuf[0], pubbuf_len); @@ -95,11 +102,11 @@ tpm2_keydata_write(const TPM2_KEYDATA *keydata, BIO *bout, TPM2_PKEY_FORMAT form goto error; } - TSSPRIVKEY_free(tpk); - return 1; + res = 1; error: + BN_free(bn_parent); TSSPRIVKEY_free(tpk); - return 0; + return res; } /** Deserialize TPM2_KEYDATA from disk @@ -114,9 +121,11 @@ tpm2_keydata_write(const TPM2_KEYDATA *keydata, BIO *bout, TPM2_PKEY_FORMAT form int tpm2_keydata_read(BIO *bin, TPM2_KEYDATA *keydata, TPM2_PKEY_FORMAT format) { - uint64_t parent; + BIGNUM *bn_parent = NULL; + TPM2_HANDLE parent; TSSPRIVKEY *tpk = NULL; char type_oid[64]; + int res = 0; switch (format) { case KEY_FORMAT_PEM: @@ -135,9 +144,14 @@ tpm2_keydata_read(BIO *bin, TPM2_KEYDATA *keydata, TPM2_PKEY_FORMAT format) keydata->emptyAuth = (tpk->emptyAuth != V_ASN1_UNDEF && tpk->emptyAuth); // the ASN1_INTEGER_get on a 32-bit machine will fail for numbers of UINT32_MAX - if (!ASN1_INTEGER_get_uint64(&parent, tpk->parent)) + if (!(bn_parent = ASN1_INTEGER_to_BN(tpk->parent, NULL))) goto error; + if (BN_is_negative(bn_parent)) + parent = ASN1_INTEGER_get(tpk->parent); + else + parent = BN_get_word(bn_parent); + if (parent == 0) keydata->parent = TPM2_RH_OWNER; else if (parent <= UINT32_MAX) @@ -159,11 +173,11 @@ tpm2_keydata_read(BIO *bin, TPM2_KEYDATA *keydata, TPM2_PKEY_FORMAT format) &keydata->pub)) goto error; + res = 1; +error: + BN_free(bn_parent); TSSPRIVKEY_free(tpk); - return 1; - error: - TSSPRIVKEY_free(tpk); - return 0; + return res; } static const TPM2B_PUBLIC primaryRsaTemplate = { diff --git a/test/rsa_encodeobject_check.sh b/test/rsa_encodeobject_check.sh new file mode 100755 index 0000000..8a10d58 --- /dev/null +++ b/test/rsa_encodeobject_check.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: BSD-3-Clause +set -eufx + +# create primary using the same ECC template that tpm2-openssl uses internally +tpm2_createprimary \ + -G ecc256:aes128cfb \ + -g sha256 \ + -a 'fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricted|decrypt' \ + -c primary.ctx + +# generate a private key and store its public portions to PEM +tpm2_create -C primary.ctx -u key.pub -r key.priv -f pem -o pub.pem + +# encode private key to PEM +tpm2_encodeobject -C primary.ctx -u key.pub -r key.priv -o priv.pem + +# print public key modulus +openssl rsa -pubin -in pub.pem -modulus -noout + +# validate the private key +openssl rsa -provider tpm2 -provider base -in priv.pem -passin pass: -check -noout + +# print private key modulus +openssl rsa -provider tpm2 -provider base -in priv.pem -passin pass: -modulus -noout + +rm primary.ctx key.pub key.priv pub.pem priv.pem diff --git a/test/rsa_encodeobject_check_parent.sh b/test/rsa_encodeobject_check_parent.sh new file mode 100755 index 0000000..310088b --- /dev/null +++ b/test/rsa_encodeobject_check_parent.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: BSD-3-Clause +set -eufx + +# create primary key +tpm2_createprimary -c primary.ctx + +# make the primary persistent +HANDLE=$(tpm2_evictcontrol -c primary.ctx | cut -d ' ' -f 2 | head -n 1) + +# generate a private key and store its public portions to PEM +tpm2_create -C primary.ctx -u key.pub -r key.priv -f pem -o pub.pem + +# encode private key to PEM using the persistent parent handle +tpm2_encodeobject -C ${HANDLE} -u key.pub -r key.priv -o priv.pem + +# print public key modulus +openssl rsa -pubin -in pub.pem -modulus -noout + +# validate the private key +openssl rsa -provider tpm2 -provider base -in priv.pem -passin pass: -check -noout + +# print private key modulus +openssl rsa -provider tpm2 -provider base -in priv.pem -passin pass: -modulus -noout + +# release the persistent primary +tpm2_evictcontrol -c ${HANDLE} + +rm primary.ctx key.pub key.priv pub.pem priv.pem