From ca60b140c81e94b210bc033b26e1a427fe5b1dc8 Mon Sep 17 00:00:00 2001 From: "wenxin.leong" Date: Tue, 14 May 2024 09:33:01 +0800 Subject: [PATCH] Add support for importing TPM2 keys with PKCS11 vendor attributes - Add support for importing TPM2 keys (as persistent handle or key objects) using PKCS11 vendor-specific attributes - Add a new CLI tool: key_import - Add integration test - Add docs/KEY_IMPORT_TOOL.md Signed-off-by: wenxin.leong --- Makefile-integration.am | 6 +- Makefile.am | 14 +- docs/KEY_IMPORT_TOOL.md | 14 + src/lib/attrs.c | 2 + src/lib/attrs.h | 12 +- src/lib/key.c | 248 ++++-- src/lib/object.c | 20 +- src/lib/object.h | 6 +- src/lib/session_ctx.c | 16 +- src/lib/token.c | 38 +- src/lib/tpm.c | 176 +++- src/lib/tpm.h | 49 ++ test/integration/key_import-link-fapi.sh.fapi | 1 + test/integration/key_import-link.sh.nosetup | 449 ++++++++++ tools/key_import/import.c | 767 ++++++++++++++++++ tools/{ => tpm2_ptool}/setup.py | 0 .../{ => tpm2_ptool}/tpm2_pkcs11/__init__.py | 0 tools/{ => tpm2_ptool}/tpm2_pkcs11/command.py | 0 .../tpm2_pkcs11/commandlets_keys.py | 0 .../tpm2_pkcs11/commandlets_store.py | 0 .../tpm2_pkcs11/commandlets_token.py | 0 tools/{ => tpm2_ptool}/tpm2_pkcs11/db.py | 0 tools/{ => tpm2_ptool}/tpm2_pkcs11/objects.py | 0 tools/{ => tpm2_ptool}/tpm2_pkcs11/pkcs11t.py | 0 tools/{ => tpm2_ptool}/tpm2_pkcs11/tpm2.py | 0 .../tpm2_pkcs11/tpm2_ptool.py | 0 tools/{ => tpm2_ptool}/tpm2_pkcs11/utils.py | 0 tools/{ => tpm2_ptool}/tpm2_ptool | 0 tools/{ => tpm2_ptool}/tpm2_ptool.py | 0 29 files changed, 1738 insertions(+), 80 deletions(-) create mode 100644 docs/KEY_IMPORT_TOOL.md create mode 120000 test/integration/key_import-link-fapi.sh.fapi create mode 100755 test/integration/key_import-link.sh.nosetup create mode 100644 tools/key_import/import.c rename tools/{ => tpm2_ptool}/setup.py (100%) rename tools/{ => tpm2_ptool}/tpm2_pkcs11/__init__.py (100%) rename tools/{ => tpm2_ptool}/tpm2_pkcs11/command.py (100%) rename tools/{ => tpm2_ptool}/tpm2_pkcs11/commandlets_keys.py (100%) rename tools/{ => tpm2_ptool}/tpm2_pkcs11/commandlets_store.py (100%) rename tools/{ => tpm2_ptool}/tpm2_pkcs11/commandlets_token.py (100%) rename tools/{ => tpm2_ptool}/tpm2_pkcs11/db.py (100%) rename tools/{ => tpm2_ptool}/tpm2_pkcs11/objects.py (100%) rename tools/{ => tpm2_ptool}/tpm2_pkcs11/pkcs11t.py (100%) rename tools/{ => tpm2_ptool}/tpm2_pkcs11/tpm2.py (100%) rename tools/{ => tpm2_ptool}/tpm2_pkcs11/tpm2_ptool.py (100%) rename tools/{ => tpm2_ptool}/tpm2_pkcs11/utils.py (100%) rename tools/{ => tpm2_ptool}/tpm2_ptool (100%) rename tools/{ => tpm2_ptool}/tpm2_ptool.py (100%) diff --git a/Makefile-integration.am b/Makefile-integration.am index e2255de6..06334987 100644 --- a/Makefile-integration.am +++ b/Makefile-integration.am @@ -10,7 +10,8 @@ integration_scripts = \ test/integration/pkcs11-javarunner.sh.java \ test/integration/nss-tests.sh \ test/integration/ptool-link.sh.nosetup \ - test/integration/python-pkcs11.sh + test/integration/python-pkcs11.sh \ + test/integration/key_import-link.sh.nosetup # Note that -fapi.sh.fapi is symlinked to .sh.nosetup # If we'd use the .fapi extension then .nosetup and .fapi overwrite each others .log @@ -18,7 +19,8 @@ integration_scripts = \ if HAVE_FAPI integration_scripts += \ test/integration/p11-tool-fapi.sh.fapi \ - test/integration/pkcs11-tool-init-fapi.sh.fapi + test/integration/pkcs11-tool-init-fapi.sh.fapi \ + test/integration/key_import-link-fapi.sh.fapi endif EXTRA_DIST += \ diff --git a/Makefile.am b/Makefile.am index 9fa6cd0f..c9f9db6c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -72,6 +72,16 @@ endif AM_DISTCHECK_CONFIGURE_FLAGS = --with-p11kitconfigdir='$$(datarootdir)/p11kitconfigdir' --with-p11kitmoduledir='$$(libdir)' +# The key_import tool +bin_PROGRAMS = tools/key_import/key_import +if ENABLE_ASAN + tools_key_import_key_import_LDFLAGS = $(AM_LDFLAGS) -shared-libasan +else + tools_key_import_key_import_LDFLAGS = $(AM_LDFLAGS) +endif +tools_key_import_key_import_LDADD = $(libtpm2_pkcs11) +tools_key_import_key_import_SOURCES = tools/key_import/import.c + # # Due to limitations in how cmocka works, we build a separate library here so we # can have a PKCS11 shared object with undefined calls into the rest of the lib @@ -113,8 +123,8 @@ AM_TESTS_ENVIRONMENT = \ PYTHON_INTERPRETER=@PYTHON_INTERPRETER@ \ TEST_FUNC_LIB=$(srcdir)/test/integration/scripts/int-test-funcs.sh \ TEST_FIXTURES=$(abs_top_srcdir)/test/integration/fixtures \ - PATH=$(abs_top_srcdir)/tools:./src:$(PATH) \ - PYTHONPATH=$(abs_top_srcdir)/tools:$(PYTHONPATH) \ + PATH=$(abs_top_srcdir)/tools/tpm2_ptool:$(abs_builddir)/tools/key_import:./src:$(PATH) \ + PYTHONPATH=$(abs_top_srcdir)/tools/tpm2_ptool:$(PYTHONPATH) \ TPM2_PKCS11_MODULE=$(abs_builddir)/src/.libs/libtpm2_pkcs11.so \ TEST_JAVA_ROOT=$(JAVAROOT) \ PACKAGE_URL=$(PACKAGE_URL) \ diff --git a/docs/KEY_IMPORT_TOOL.md b/docs/KEY_IMPORT_TOOL.md new file mode 100644 index 00000000..601e4913 --- /dev/null +++ b/docs/KEY_IMPORT_TOOL.md @@ -0,0 +1,14 @@ +# The key_import Tool + +The `key_import` tool in this project is a C program that serves as an example for importing TPM keys into a tpm2-pkcs11 token. The key import mechanism uses PKCS #11 vendor-specific attributes and works with both FAPI and ESYSDB backends. + +Supported modes: +- Key to be imported: Ordinary TPM key with or without an auth value. +- Key Import Formats: Keys can be imported as persistent handle or TSS key objects obtained from `tpm2 create` (`TPM2B_PUBLIC` and `TPM2B_PRIVATE` blobs). + - If key objects are used, the associated parent key must be the same primary key used for token initialization. Parent keys with or without an auth value are supported. + +The PKCS #11 vendor-specific attributes used during the key import procedure are: +- Persistent Handle: `CKA_TPM2_PERSISTENT_HANDLE` and `CKA_TPM2_OBJAUTH`. +- TSS Key Objects: `CKA_TPM2_PUB_BLOB`, `CKA_TPM2_PRIV_BLOB`, and `CKA_TPM2_OBJAUTH`. + +For more details, please refer to `test/integration/key_import-link.sh.nosetup`. diff --git a/src/lib/attrs.c b/src/lib/attrs.c index 0c81e6a4..585a50d0 100644 --- a/src/lib/attrs.c +++ b/src/lib/attrs.c @@ -167,10 +167,12 @@ static attr_handler2 attr_handlers[] = { ADD_ATTR_HANDLER(CKA_WRAP_TEMPLATE, TYPE_BYTE_TEMP_SEQ), ADD_ATTR_HANDLER(CKA_UNWRAP_TEMPLATE, TYPE_BYTE_TEMP_SEQ), ADD_ATTR_HANDLER(CKA_ALLOWED_MECHANISMS, TYPE_BYTE_INT_SEQ), + ADD_ATTR_HANDLER(CKA_TPM2_OBJAUTH, TYPE_BYTE_HEX_STR), ADD_ATTR_HANDLER(CKA_TPM2_OBJAUTH_ENC, TYPE_BYTE_HEX_STR), ADD_ATTR_HANDLER(CKA_TPM2_PUB_BLOB, TYPE_BYTE_HEX_STR), ADD_ATTR_HANDLER(CKA_TPM2_PRIV_BLOB, TYPE_BYTE_HEX_STR), ADD_ATTR_HANDLER(CKA_TPM2_ENC_BLOB, TYPE_BYTE_HEX_STR), + ADD_ATTR_HANDLER(CKA_TPM2_PERSISTENT_HANDLE, TYPE_BYTE_INT), }; static attr_handler2 default_handler = { .memtype = 0, .name="UNKNOWN" }; diff --git a/src/lib/attrs.h b/src/lib/attrs.h index c0c542c5..e28d1754 100644 --- a/src/lib/attrs.h +++ b/src/lib/attrs.h @@ -10,11 +10,13 @@ /* * We will allow these to be accessed, but the values are not stable */ -#define CKA_VENDOR_TPM2_DEFINED 0x0F000000UL -#define CKA_TPM2_OBJAUTH_ENC (CKA_VENDOR_DEFINED|CKA_VENDOR_TPM2_DEFINED|0x1UL) -#define CKA_TPM2_PUB_BLOB (CKA_VENDOR_DEFINED|CKA_VENDOR_TPM2_DEFINED|0x2UL) -#define CKA_TPM2_PRIV_BLOB (CKA_VENDOR_DEFINED|CKA_VENDOR_TPM2_DEFINED|0x3UL) -#define CKA_TPM2_ENC_BLOB (CKA_VENDOR_DEFINED|CKA_VENDOR_TPM2_DEFINED|0x4UL) +#define CKA_VENDOR_TPM2_DEFINED 0x0F000000UL +#define CKA_TPM2_OBJAUTH_ENC (CKA_VENDOR_DEFINED|CKA_VENDOR_TPM2_DEFINED|0x1UL) +#define CKA_TPM2_PUB_BLOB (CKA_VENDOR_DEFINED|CKA_VENDOR_TPM2_DEFINED|0x2UL) +#define CKA_TPM2_PRIV_BLOB (CKA_VENDOR_DEFINED|CKA_VENDOR_TPM2_DEFINED|0x3UL) +#define CKA_TPM2_ENC_BLOB (CKA_VENDOR_DEFINED|CKA_VENDOR_TPM2_DEFINED|0x4UL) +#define CKA_TPM2_OBJAUTH (CKA_VENDOR_DEFINED|CKA_VENDOR_TPM2_DEFINED|0x5UL) +#define CKA_TPM2_PERSISTENT_HANDLE (CKA_VENDOR_DEFINED|CKA_VENDOR_TPM2_DEFINED|0x6UL) /* Invalid values for error detection */ #define CK_OBJECT_CLASS_BAD (~(CK_OBJECT_CLASS)0) diff --git a/src/lib/key.c b/src/lib/key.c index 552b0e70..7cdfccd4 100644 --- a/src/lib/key.c +++ b/src/lib/key.c @@ -13,6 +13,12 @@ #include "session_ctx.h" #include "utils.h" +enum keygen_mode { + keygen_mode_normal, + keygen_mode_kobjs, + keygen_mode_phandle +}; + typedef struct sanity_check_data sanity_check_data; struct sanity_check_data { bool is_extractable; @@ -137,6 +143,36 @@ static CK_RV check_specific_attrs(CK_MECHANISM_TYPE mech, } } +static CK_RV check_tpm_vendor_attrs( + attr_list *pubkey_templ_w_types, attr_list *privkey_templ_w_types, + enum keygen_mode *keygen_mode) { + + /* From pPublicKeyTemplate */ + CK_ATTRIBUTE_PTR a_pub_blob = attr_get_attribute_by_type(pubkey_templ_w_types, CKA_TPM2_PUB_BLOB); + CK_ATTRIBUTE_PTR a_pub_handle = attr_get_attribute_by_type(pubkey_templ_w_types, CKA_TPM2_PERSISTENT_HANDLE); + + /* From pPrivateKeyTemplate */ + CK_ATTRIBUTE_PTR a_priv_auth = attr_get_attribute_by_type(privkey_templ_w_types, CKA_TPM2_OBJAUTH); + CK_ATTRIBUTE_PTR a_priv_blob = attr_get_attribute_by_type(privkey_templ_w_types, CKA_TPM2_PRIV_BLOB); + CK_ATTRIBUTE_PTR a_priv_handle = attr_get_attribute_by_type(privkey_templ_w_types, CKA_TPM2_PERSISTENT_HANDLE); + + if (a_pub_handle && a_priv_handle && !(a_pub_blob || a_priv_blob)) { + /* Persistent key found */ + *keygen_mode = keygen_mode_phandle; + } else if (a_pub_blob && a_priv_blob && !(a_pub_handle || a_priv_handle)) { + /* TPM key objects found */ + *keygen_mode = keygen_mode_kobjs; + } else if (!(a_pub_blob || a_pub_handle || a_priv_auth || a_priv_blob || a_priv_handle)) { + *keygen_mode = keygen_mode_normal; + } else { + /* Invalid combination */ + LOGE("Key import request detected, but the attribute combination is invalid or missing"); + return CKR_ATTRIBUTE_TYPE_INVALID; + } + + return CKR_OK; +} + CK_RV key_gen ( session_ctx *ctx, @@ -153,9 +189,14 @@ CK_RV key_gen ( CK_RV rv = CKR_GENERAL_ERROR; + enum keygen_mode keygen_mode = keygen_mode_normal; + twist newauthhex = NULL; twist newwrapped_auth = NULL; + twist pub_blob = NULL; + twist priv_blob = NULL; + attr_list *pubkey_templ_w_types = NULL; attr_list *privkey_templ_w_types = NULL; @@ -164,6 +205,12 @@ CK_RV key_gen ( tpm_object_data objdata = { 0 }; + CK_ATTRIBUTE_PTR attr_ptr = NULL; + + uint32_t priv_esys_tr = 0, pub_esys_tr = 0; + CK_ULONG priv_persistent_handle = 0; + CK_ULONG pub_persistent_handle = 0; + token *tok = session_ctx_get_token(ctx); assert(tok); @@ -223,66 +270,173 @@ CK_RV key_gen ( goto out; } - rv = utils_new_random_object_auth(&newauthhex); - if (rv != CKR_OK) { - LOGE("Failed to create new object auth"); - goto out; - } - - rv = utils_ctx_wrap_objauth(tok->wrappingkey, newauthhex, &newwrapped_auth); - if (rv != CKR_OK) { - LOGE("Failed to wrap new object auth"); - goto out; - } - - rv = tpm2_generate_key( - tok->tctx, - tok->pobject.handle, - tok->pobject.objauth, - newauthhex, - mechanism, - pubkey_templ_w_types, - privkey_templ_w_types, - &objdata); - if (rv != CKR_OK) { - LOGE("Failed to generate key"); - goto out; - } - - /* set the tpm object handles */ - tobject_set_handle(new_private_tobj, objdata.privhandle); - tobject_set_handle(new_public_tobj, objdata.pubhandle); - + /* Initialize the objects attributes */ new_public_tobj->attrs = pubkey_templ_w_types; new_private_tobj->attrs = privkey_templ_w_types; /* make it clear that tobj now owns these */ pubkey_templ_w_types = privkey_templ_w_types = NULL; - /* - * objects have default required attributes, add them if not present. - */ - rv = attr_add_missing_attrs(&new_public_tobj->attrs, &new_private_tobj->attrs, - objdata.attrs, mechanism->mechanism); - if (rv != CKR_OK) { - LOGE("Failed to add missing rsa attrs"); + /* Check if the import of an existing TPM key (persistent handle or key objects) is requested */ + rv = check_tpm_vendor_attrs( + new_public_tobj->attrs, new_private_tobj->attrs, + &keygen_mode); + if (rv) { goto out; } - /* populate blob data */ - rv = tobject_set_blob_data(new_private_tobj, objdata.pubblob, objdata.privblob); - if (rv != CKR_OK) { - goto out; + if (keygen_mode == keygen_mode_normal) { /* Generate a new TPM key */ + + rv = utils_new_random_object_auth(&newauthhex); + if (rv != CKR_OK) { + LOGE("Failed to create new object auth"); + goto out; + } + + rv = tpm2_generate_key( + tok->tctx, + tok->pobject.handle, + tok->pobject.objauth, + newauthhex, + mechanism, + new_public_tobj->attrs, + new_private_tobj->attrs, + &objdata); + if (rv != CKR_OK) { + LOGE("Failed to generate key"); + goto out; + } + + /* set the tpm object handles */ + tobject_set_esys_tr(new_private_tobj, objdata.privhandle); + tobject_set_esys_tr(new_public_tobj, objdata.pubhandle); + + /* populate blob data */ + rv = tobject_set_blob_data(new_private_tobj, objdata.pubblob, objdata.privblob); + if (rv != CKR_OK) { + goto out; + } + + rv = tobject_set_blob_data(new_public_tobj, objdata.pubblob, NULL); + if (rv != CKR_OK) { + goto out; + } + + } else { /* Import an existing TPM key */ + + /* Read the CKA_TPM2_OBJAUTH */ + attr_ptr = attr_get_attribute_by_type(new_private_tobj->attrs, CKA_TPM2_OBJAUTH); + if (attr_ptr) { + newauthhex = twistbin_new(attr_ptr->pValue, attr_ptr->ulValueLen); + + /* Secure erase the CKA_TPM2_OBJAUTH field in the privkey template */ + memset(attr_ptr->pValue, 0, attr_ptr->ulValueLen); + attr_pfree_cleanse(attr_ptr); + + /* [To-do] delete the CKA_TPM2_OBJAUTH from the privkey template to save storage space */ + } + + if (keygen_mode == keygen_mode_phandle) { /* The key to import is a persistent handle */ + + attr_ptr = attr_get_attribute_by_type(new_private_tobj->attrs, CKA_TPM2_PERSISTENT_HANDLE); + rv = attr_CK_ULONG(attr_ptr, &priv_persistent_handle); + if (rv != CKR_OK) { + LOGE("Failed to get private key persistent handle"); + goto out; + } + + attr_ptr = attr_get_attribute_by_type(new_public_tobj->attrs, CKA_TPM2_PERSISTENT_HANDLE); + rv = attr_CK_ULONG(attr_ptr, &pub_persistent_handle); + if (rv != CKR_OK) { + LOGE("Failed to get public key persistent handle"); + goto out; + } + + if (priv_persistent_handle != pub_persistent_handle) { + LOGE("The public and private key persistent handles are not the same"); + return CKR_ATTRIBUTE_TYPE_INVALID; + } + + rv = tpm_get_esys_tr(tok->tctx, (uint32_t)priv_persistent_handle, + &priv_esys_tr, &pub_esys_tr); + if (rv != CKR_OK) { + LOGE("Failed to get ESYS_TR of privkey"); + goto out; + } + + tobject_set_persistent_handle(new_private_tobj, priv_persistent_handle); + tobject_set_persistent_handle(new_public_tobj, pub_persistent_handle); + tobject_set_esys_tr(new_private_tobj, priv_esys_tr); + tobject_set_esys_tr(new_public_tobj, pub_esys_tr); + + } else if (keygen_mode == keygen_mode_kobjs) { /* The key to import includes both public and private blobs */ + + attr_ptr = attr_get_attribute_by_type(new_public_tobj->attrs, CKA_TPM2_PUB_BLOB); + pub_blob = twistbin_new(attr_ptr->pValue, attr_ptr->ulValueLen); + + attr_ptr = attr_get_attribute_by_type(new_private_tobj->attrs, CKA_TPM2_PRIV_BLOB); + priv_blob = twistbin_new(attr_ptr->pValue, attr_ptr->ulValueLen); + + rv = tpm_loadobj(tok->tctx, tok->pobject.handle, tok->pobject.objauth, + pub_blob, NULL, &pub_esys_tr); + if (rv != CKR_OK) { + LOGE("Failed to load key objects"); + goto out; + } + + rv = tpm_loadobj(tok->tctx, tok->pobject.handle, tok->pobject.objauth, + pub_blob, priv_blob, &priv_esys_tr); + if (rv != CKR_OK) { + LOGE("Failed to load key objects"); + goto out; + } + + tobject_set_esys_tr(new_private_tobj, priv_esys_tr); + tobject_set_esys_tr(new_public_tobj, pub_esys_tr); + + rv = tobject_set_blob_data(new_private_tobj, pub_blob, priv_blob); + if (rv != CKR_OK) { + goto out; + } + + rv = tobject_set_blob_data(new_public_tobj, pub_blob, NULL); + if (rv != CKR_OK) { + goto out; + } + } else { /* Will not reach here */ + assert(0); + } + + /* Populate the mandatory attributes based on the TPM key */ + rv = tpm_parse_key_to_attrs(tok->tctx, priv_esys_tr, mechanism, + new_public_tobj->attrs, new_private_tobj->attrs, + &objdata); + if (rv != CKR_OK) { + goto out; + } } - rv = tobject_set_blob_data(new_public_tobj, objdata.pubblob, NULL); - if (rv != CKR_OK) { - goto out; + if (newauthhex) { + rv = utils_ctx_wrap_objauth(tok->wrappingkey, newauthhex, &newwrapped_auth); + if (rv != CKR_OK) { + LOGE("Failed to wrap new object auth"); + goto out; + } + + /* populate auth data, public objects do not need an auth */ + rv = tobject_set_auth(new_private_tobj, newauthhex, newwrapped_auth); + if (rv != CKR_OK) { + goto out; + } } - /* populate auth data, public objects do not need an auth */ - rv = tobject_set_auth(new_private_tobj, newauthhex, newwrapped_auth); + /* + * objects have default required attributes, add them if not present. + */ + rv = attr_add_missing_attrs(&new_public_tobj->attrs, &new_private_tobj->attrs, + objdata.attrs, mechanism->mechanism); if (rv != CKR_OK) { + LOGE("Failed to add missing rsa attrs"); goto out; } @@ -317,6 +471,8 @@ CK_RV key_gen ( tpm_objdata_free(&objdata); twist_free(newauthhex); twist_free(newwrapped_auth); + twist_free(pub_blob); + twist_free(priv_blob); attr_list_free(pubkey_templ_w_types); attr_list_free(privkey_templ_w_types); diff --git a/src/lib/object.c b/src/lib/object.c index 5564a5de..b422fe55 100644 --- a/src/lib/object.c +++ b/src/lib/object.c @@ -827,10 +827,16 @@ CK_RV tobject_set_auth(tobject *tobj, twist authbin, twist wrappedauthhex) { return r ? CKR_OK : CKR_GENERAL_ERROR; } -void tobject_set_handle(tobject *tobj, uint32_t handle) { +void tobject_set_esys_tr(tobject *tobj, uint32_t esys_tr) { assert(tobj); - tobj->tpm_handle = handle; + tobj->tpm_esys_tr = esys_tr; +} + +void tobject_set_persistent_handle(tobject *tobj, uint32_t handle) { + assert(tobj); + + tobj->tpm_persistent_handle = handle; } void tobject_set_id(tobject *tobj, unsigned id) { @@ -1299,6 +1305,16 @@ CK_RV object_init_from_attrs(tobject *tobj) { } } + a = attr_get_attribute_by_type(tobj->attrs, CKA_TPM2_PERSISTENT_HANDLE); + if (a && a->pValue && a->ulValueLen) { + CK_ULONG handle = 0; + if (attr_CK_ULONG(a, &handle) != CKR_OK) { + LOGE("Failed to read the tpm persistent handle"); + goto error; + } + tobj->tpm_persistent_handle = (uint32_t)handle; + } + return CKR_OK; error: diff --git a/src/lib/object.h b/src/lib/object.h index 08996c74..799c812f 100644 --- a/src/lib/object.h +++ b/src/lib/object.h @@ -38,7 +38,8 @@ struct tobject { twist unsealed_auth; /** unwrapped auth value */ - uint32_t tpm_handle; /** loaded tpm handle */ + uint32_t tpm_esys_tr; /** loaded tpm handle */ + uint32_t tpm_persistent_handle; /** persistent TPM handle **/ bool is_authenticated; /** true if a context specific login has authenticated use of the object */ }; @@ -73,7 +74,8 @@ CK_RV tobject_set_blob_data(tobject *tobj, twist pub, twist priv); */ CK_RV tobject_set_auth(tobject *tobj, twist authbin, twist wrappedauthhex); -void tobject_set_handle(tobject *tobj, uint32_t handle); +void tobject_set_esys_tr(tobject *tobj, uint32_t esys_tr); +void tobject_set_persistent_handle(tobject *tobj, uint32_t handle); void tobject_set_id(tobject *tobj, unsigned id); void tobject_free(tobject *tobj); diff --git a/src/lib/session_ctx.c b/src/lib/session_ctx.c index d597951a..6c4846b0 100644 --- a/src/lib/session_ctx.c +++ b/src/lib/session_ctx.c @@ -317,11 +317,21 @@ CK_RV session_ctx_logout(session_ctx *ctx) { attr_pfree_cleanse(a); } - if (tobj->tpm_handle) { - bool result = tpm_flushcontext(tpm, tobj->tpm_handle); + /* + * Do not perform tpm_flushcontext when: + * - tpm_esys_tr is 0, or + * - the object is private and the associated key in the TPM is persistent. + * + * If the object is public and the associated key in the TPM is persistent, + * tpm_flushcontext is still necessary because, during the initialization of the object, + * a transient TPM key with only the public component is created from the persistent key. + */ + if (tobj->tpm_esys_tr && + !(cka_private && tobj->tpm_persistent_handle)) { + bool result = tpm_flushcontext(tpm, tobj->tpm_esys_tr); assert(result); UNUSED(result); - tobj->tpm_handle = 0; + tobj->tpm_esys_tr = 0; /* Clear the unwrapped auth value for tertiary objects */ twist_free(tobj->unsealed_auth); diff --git a/src/lib/token.c b/src/lib/token.c index 555621a7..e67a2e30 100644 --- a/src/lib/token.c +++ b/src/lib/token.c @@ -631,18 +631,40 @@ CK_RV token_load_object(token *tok, CK_OBJECT_HANDLE key, tobject **loaded_tobj) * The object may already be loaded by the TPM or may just be * a public key object not-resident in the TPM. */ - if (tobj->tpm_handle || !tobj->pub) { + if (tobj->tpm_esys_tr || (!tobj->pub && !tobj->tpm_persistent_handle)) { *loaded_tobj = tobj; return CKR_OK; } - rv = tpm_loadobj( - tpm, - tok->pobject.handle, tok->pobject.objauth, - tobj->pub, tobj->priv, - &tobj->tpm_handle); - if (rv != CKR_OK) { - return rv; + if (tobj->tpm_persistent_handle && v != CKO_SECRET_KEY) { + if (v == CKO_PRIVATE_KEY) { + rv = tpm_get_esys_tr( + tpm, + tobj->tpm_persistent_handle, + &tobj->tpm_esys_tr, + NULL); + if (rv != CKR_OK) { + return rv; + } + } else { + rv = tpm_get_esys_tr( + tpm, + tobj->tpm_persistent_handle, + NULL, + &tobj->tpm_esys_tr); + if (rv != CKR_OK) { + return rv; + } + } + } else { + rv = tpm_loadobj( + tpm, + tok->pobject.handle, tok->pobject.objauth, + tobj->pub, tobj->priv, + &tobj->tpm_esys_tr); + if (rv != CKR_OK) { + return rv; + } } rv = utils_ctx_unwrap_objauth(tok->wrappingkey, tobj->objauth, diff --git a/src/lib/tpm.c b/src/lib/tpm.c index 16466240..a51b8ad4 100644 --- a/src/lib/tpm.c +++ b/src/lib/tpm.c @@ -1234,7 +1234,7 @@ static CK_RV tpm_hmac_large(tpm_op_data *opdata, CK_BYTE_PTR data, CK_ULONG data assert(tctx); twist auth = tobj->unsealed_auth; - TPMI_DH_OBJECT handle = tobj->tpm_handle; + TPMI_DH_OBJECT handle = tobj->tpm_esys_tr; ESYS_TR session = tctx->hmac_session; @@ -1358,7 +1358,7 @@ static CK_RV tpm_hmac(tpm_op_data *opdata, CK_BYTE_PTR data, CK_ULONG datalen, C assert(tctx); twist auth = tobj->unsealed_auth; - TPMI_DH_OBJECT handle = tobj->tpm_handle; + TPMI_DH_OBJECT handle = tobj->tpm_esys_tr; ESYS_TR session = tctx->hmac_session; @@ -1417,7 +1417,7 @@ CK_RV tpm_sign(tpm_op_data *opdata, CK_BYTE_PTR data, CK_ULONG datalen, CK_BYTE_ assert(tctx); twist auth = tobj->unsealed_auth; - TPMI_DH_OBJECT handle = tobj->tpm_handle; + TPMI_DH_OBJECT handle = tobj->tpm_esys_tr; ESYS_CONTEXT *ectx = tctx->esys_ctx; ESYS_TR session = tctx->hmac_session; TPMT_SIG_SCHEME *scheme = NULL; @@ -1614,7 +1614,7 @@ CK_RV tpm_rsa_oaep_get_opdata(mdetail *m, tpm_ctx *tctx, CK_MECHANISM_PTR mech, * TPM is hardcoded to MGF1 + in the TPM, make sure what is requested is supported */ CK_RSA_PKCS_MGF_TYPE supported_mgf; - CK_RV rv = get_oaep_mgf1_alg(tctx, tobj->tpm_handle, &supported_mgf); + CK_RV rv = get_oaep_mgf1_alg(tctx, tobj->tpm_esys_tr, &supported_mgf); if (rv != CKR_OK) { return rv; } @@ -2231,7 +2231,7 @@ CK_RV tpm_rsa_decrypt(tpm_op_data *tpm_enc_data, memcpy(tpm_ctext.buffer, ctext, ctextlen); twist auth = tpm_enc_data->tobj->unsealed_auth; - ESYS_TR handle = tpm_enc_data->tobj->tpm_handle; + ESYS_TR handle = tpm_enc_data->tobj->tpm_esys_tr; bool result = set_esys_auth(ctx->esys_ctx, handle, auth); if (!result) { return CKR_GENERAL_ERROR; @@ -2492,7 +2492,7 @@ static CK_RV do_buffered_encdec(tpm_op_data *tpm_enc_data, TPM2B_IV *iv = &tpm_enc_data->sym.iv; twist auth = tpm_enc_data->tobj->unsealed_auth; - ESYS_TR handle = tpm_enc_data->tobj->tpm_handle; + ESYS_TR handle = tpm_enc_data->tobj->tpm_esys_tr; /* final calls don't have input data, they just exhaust the internal buffer if present */ bool is_final = !in; @@ -4572,7 +4572,7 @@ CK_RV tpm_get_pss_sig_state(tpm_ctx *tctx, tobject *tobj, .digest = { 0 } }; - bool res = set_esys_auth(tctx->esys_ctx, tobj->tpm_handle, + bool res = set_esys_auth(tctx->esys_ctx, tobj->tpm_esys_tr, tobj->unsealed_auth); if (!res) { return CKR_GENERAL_ERROR; @@ -4580,7 +4580,7 @@ CK_RV tpm_get_pss_sig_state(tpm_ctx *tctx, tobject *tobj, TSS2_RC rval = Esys_Sign( tctx->esys_ctx, - tobj->tpm_handle, + tobj->tpm_esys_tr, ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE, @@ -4654,12 +4654,12 @@ CK_RV tpm_ec_ecdh1_derive(tpm_ctx *tctx, tobject *tobj, uint8_t *ecc_point, return rv; } - bool res = set_esys_auth(tctx->esys_ctx, tobj->tpm_handle, tobj->unsealed_auth); + bool res = set_esys_auth(tctx->esys_ctx, tobj->tpm_esys_tr, tobj->unsealed_auth); if (!res) { return CKR_GENERAL_ERROR; } - rv = Esys_ECDH_ZGen(tctx->esys_ctx, tobj->tpm_handle, ESYS_TR_PASSWORD, + rv = Esys_ECDH_ZGen(tctx->esys_ctx, tobj->tpm_esys_tr, ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE, &in_point, &out_point); if (rv != CKR_OK) { return rv; @@ -4680,3 +4680,159 @@ CK_RV tpm_ec_ecdh1_derive(tpm_ctx *tctx, tobject *tobj, uint8_t *ecc_point, return rv; } +CK_RV tpm_get_esys_tr( + tpm_ctx *ctx, + uint32_t persistent_handle, + uint32_t *esys_tr, + uint32_t *esys_tr_pub) +{ + ESYS_TR tr; + TPM2B_PUBLIC *public = NULL; + + TSS2_RC rval; + CK_RV rv = CKR_OK; + + rval = Esys_TR_FromTPMPublic( + ctx->esys_ctx, + persistent_handle, + ESYS_TR_NONE, + ESYS_TR_NONE, + ESYS_TR_NONE, + &tr); + if (rval != TSS2_RC_SUCCESS) { + LOGE("Esys_TR_FromTPMPublic: %s:", Tss2_RC_Decode(rval)); + rv = CKR_GENERAL_ERROR; + goto exit; + } + + if (esys_tr_pub) { + if ((rv = tpm_readpub(ctx, tr, &public, NULL, NULL))) { + goto exit_flush; + } + + if (!tpm_loadexternal(ctx, public, esys_tr_pub)) { + rv = CKR_GENERAL_ERROR; + goto exit_flush; + } + } + + if (esys_tr) { + *esys_tr = tr; + goto exit; + } + +exit_flush: + if (!tpm_flushcontext(ctx, tr)) { + rv = CKR_GENERAL_ERROR; + } +exit: + free(public); + return rv; +} + +CK_RV tpm_parse_key_to_attrs( + tpm_ctx *ctx, + uint32_t esys_tr, + CK_MECHANISM_PTR mechanism, + attr_list *pub_attrs, + attr_list *priv_attrs, + tpm_object_data *obj_data) +{ + + CK_RV rv = CKR_GENERAL_ERROR; + TPM2B_PUBLIC *public = NULL; + + assert(pub_attrs); + assert(priv_attrs); + assert(obj_data); + + rv = sanity_check_mech(mechanism); + if (rv != CKR_OK) { + goto exit; + } + + if ((rv = tpm_readpub(ctx, esys_tr, &public, NULL, NULL))) { + goto exit; + } + + /* Check if the TPM key type is consistent with the PKCS11 mechanism */ + switch(mechanism->mechanism) { + case CKM_RSA_PKCS_KEY_PAIR_GEN: + if (public->publicArea.type != TPM2_ALG_RSA) { + LOGE("Mismatch in the given TPM key type with the mechanism"); + rv = CKR_MECHANISM_INVALID; + goto exit; + } + break; + + case CKM_EC_KEY_PAIR_GEN: + if (public->publicArea.type != TPM2_ALG_ECC) { + LOGE("Mismatch in the given TPM key type with the mechanism"); + rv = CKR_MECHANISM_INVALID; + goto exit; + } + break; + } + + /* Initialize the attribute list */ + + obj_data->attrs = attr_list_new(); + if (!obj_data->attrs) { + LOGE("oom"); + rv = CKR_HOST_MEMORY; + goto exit; + } + + /* Populate key type-specific attributes for pub & priv */ + + switch(mechanism->mechanism) { + case CKM_RSA_PKCS_KEY_PAIR_GEN: + rv = tpm_object_data_populate_rsa(public, obj_data); + if (rv != CKR_OK) { + goto exit_attr_list_free; + } + break; + + case CKM_EC_KEY_PAIR_GEN: + rv = tpm_object_data_populate_ecc(public, obj_data); + if (rv != CKR_OK) { + goto exit_attr_list_free; + } + break; + } + + /* Populate non-key-type-specific attributes for pub & priv */ + + TPMA_OBJECT objattrs = public->publicArea.objectAttributes; + + CK_BBOOL extractable = !!!(objattrs & (TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT)); + CK_BBOOL sensitive = !extractable; + CK_BBOOL never_extractable = !extractable; + CK_BBOOL local = !!(objattrs & TPMA_OBJECT_SENSITIVEDATAORIGIN); + CK_BBOOL decrypt = !!(objattrs & TPMA_OBJECT_DECRYPT); + CK_BBOOL sign = !!(objattrs & TPMA_OBJECT_SIGN_ENCRYPT); + + if (!attr_list_add_bool(obj_data->attrs, CKA_EXTRACTABLE, extractable) || + !attr_list_add_bool(obj_data->attrs, CKA_ALWAYS_SENSITIVE, sensitive) || + !attr_list_add_bool(obj_data->attrs, CKA_NEVER_EXTRACTABLE, never_extractable) || + !attr_list_add_bool(obj_data->attrs, CKA_LOCAL, local) || + !attr_list_add_bool(obj_data->attrs, CKA_DECRYPT, decrypt) || + !attr_list_add_bool(obj_data->attrs, CKA_VERIFY, decrypt) || /* decrypt and verify are the same */ + !attr_list_add_bool(obj_data->attrs, CKA_SIGN, sign) || + !attr_list_add_bool(obj_data->attrs, CKA_ENCRYPT, sign)) { /* sign and encrypt are same */ + rv = CKR_HOST_MEMORY; + goto exit_attr_list_free; + } + + /* Other public/private specific attributes are covered by attr_add_missing_attrs() */ + + (void) pub_attrs; + (void) priv_attrs; + + goto exit; +exit_attr_list_free: + attr_list_free(obj_data->attrs); +exit: + free(public); + return rv; +} diff --git a/src/lib/tpm.h b/src/lib/tpm.h index 24246082..090c7641 100644 --- a/src/lib/tpm.h +++ b/src/lib/tpm.h @@ -199,8 +199,57 @@ CK_RV tpm_create_transient_primary_from_template(tpm_ctx *tpm, CK_RV tpm_get_pss_sig_state(tpm_ctx *tctx, tobject *tobj, bool *pss_sigs_good); +/** + * Retrieve the associated ESYS_TR from the given TPM2_HANDLE. + * + * @param ctx + * The TPM API context. + * @param persistent_handle + * The TPM persistent handle (TPM2_HANDLE). + * @param esys_tr + * An optional output (can be NULL). + * The associated ESYS_TR is returned here. + * @param esys_tr_pub + * An optional output (can be NULL). + * A new key is created with only the public component from the + * persistent handle. The associated ESYS_TR is returned here. + * @return + * CKR_OK on success; otherwise, an error code. + */ +CK_RV tpm_get_esys_tr( + tpm_ctx *ctx, + uint32_t persistent_handle, + uint32_t *esys_tr, + uint32_t *esys_tr_pub); + bool tpm_get_name(tpm_ctx *ctx, uint32_t handle, twist *name); +/** + * Populate the CK_ATTRIBUTE list based on the given TPM key + * + * @param ctx + * The TPM API context. + * @param esys_tr + * The TPM key ESYS_TR. + * @param mechanism + * The mechanism. + * @param pub_attrs + * The public key's CK_ATTRIBUTE list is returned here. + * @param priv_attrs + * The private key's CK_ATTRIBUTE list is returned here. + * @param obj_data + * The struct tpm_object_data is returned here. + * @return + * CKR_OK on success; otherwise, an error code. + */ +CK_RV tpm_parse_key_to_attrs( + tpm_ctx *tpm, + uint32_t esys_tr, + CK_MECHANISM_PTR mechanism, + attr_list *pub_attrs, + attr_list *priv_attrs, + tpm_object_data *obj_data); + void tpm_init(void); void tpm_destroy(void); diff --git a/test/integration/key_import-link-fapi.sh.fapi b/test/integration/key_import-link-fapi.sh.fapi new file mode 120000 index 00000000..5e2a4875 --- /dev/null +++ b/test/integration/key_import-link-fapi.sh.fapi @@ -0,0 +1 @@ +key_import-link.sh.nosetup \ No newline at end of file diff --git a/test/integration/key_import-link.sh.nosetup b/test/integration/key_import-link.sh.nosetup new file mode 100755 index 00000000..5f283447 --- /dev/null +++ b/test/integration/key_import-link.sh.nosetup @@ -0,0 +1,449 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2024 Infineon Technologies AG +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE + +set -xe + +if [ -z "$T" ]; then + export T="$(cd "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)" +fi + +source "$T/test/integration/scripts/helpers.sh" + +check_openssl_version + +export TPM2OPENSSL_TCTI="$TPM2TOOLS_TCTI" + +tempdir=$(mktemp -d) + +function cleanup() { + rm -rf "$tempdir" +} +trap cleanup EXIT + +onerror() { + echo "$BASH_COMMAND on line ${BASH_LINENO[0]} failed: $?" + exit 1 +} +trap onerror ERR + +setup_asan + +key_import -h + +if [ -z "$modpath" ]; then + modpath="$PWD/src/.libs/libtpm2_pkcs11.so" +fi + +echo "modpath=$modpath" + +pkcs11_tool() { + pkcs11-tool --module "$modpath" "$@" + return $? +} + +# ==================================================================== +# FUNCTION: create_key +# DESCRIPTION: Provision ordinary TPM keys with/without parent auth, +# with/without key auth +# PARAMETERS: +# $1 - algo +# $2 - parent context +# $3 - parent auth +# $4 - key name +# $5 - persistent handle +# $6 - key auth +# ==================================================================== +create_key() { + if [ -z "$3" ]; then + if [ -z "$6" ]; then + tpm2_create -C $2 -g sha256 -G $1 \ + -u ${tempdir}/${4}.pub -r ${tempdir}/${4}.priv + else + tpm2_create -C $2 -p $6 -g sha256 -G $1 \ + -u ${tempdir}/${4}.pub -r ${tempdir}/${4}.priv + fi + tpm2_load -C $2 -u ${tempdir}/${4}.pub -r ${tempdir}/${4}.priv \ + -c ${tempdir}/${4}.ctx + else + if [ -z "$6" ]; then + tpm2_create -C $2 -P $3 -g sha256 -G $1 \ + -u ${tempdir}/${4}.pub -r ${tempdir}/${4}.priv + else + tpm2_create -C $2 -P $3 -p $6 -g sha256 -G $1 \ + -u ${tempdir}/${4}.pub -r ${tempdir}/${4}.priv + fi + tpm2_load -C $2 -P $3 -u ${tempdir}/${4}.pub -r ${tempdir}/${4}.priv \ + -c ${tempdir}/${4}.ctx + fi + tpm2_evictcontrol -C o -c ${tempdir}/${4}.ctx $5 + tpm2_readpublic -c $5 +} + +# ==================================================================== +# FUNCTION: rsakey_tests +# DESCRIPTION: A helper function for testing RSA key. +# PARAMETERS: +# $1 - slot index +# $2 - key label +# ==================================================================== +rsakey_tests() { + rm -f ${tempdir}/rsa.pub.pem ${tempdir}/rsa.pub.der + + # The value of id is the hex representation of the ASCII text of the label + id=$(echo -n $2 | xxd -p) + + # Read the public component + pkcs11_tool --slot-index $1 --login --pin myuserpin --id $id --type pubkey \ + --read-object --output-file ${tempdir}/rsa.pub.der + openssl rsa -inform DER -outform PEM -in ${tempdir}/rsa.pub.der -pubin \ + -out ${tempdir}/rsa.pub.pem + cat ${tempdir}/rsa.pub.pem + + # Create a file with random data + pkcs11_tool --slot-index $1 --generate-random 32 --output-file ${tempdir}/data.plain + + # Perform RSA encryption and decryption + openssl rsautl -encrypt -inkey ${tempdir}/rsa.pub.pem -in ${tempdir}/data.plain \ + -pubin -out ${tempdir}/data.cipher + pkcs11_tool --slot-index $1 --login --pin myuserpin --id $id --decrypt \ + --mechanism RSA-PKCS --input-file ${tempdir}/data.cipher \ + --output-file ${tempdir}/data.decipher + diff ${tempdir}/data.plain ${tempdir}/data.decipher + + # Perform sign and verification + pkcs11_tool --slot-index $1 --id $id --login --pin myuserpin --sign \ + --mechanism SHA256-RSA-PKCS --input-file ${tempdir}/data.plain \ + --output-file ${tempdir}/data.rsa.sig + openssl dgst -sha256 -verify ${tempdir}/rsa.pub.pem \ + -signature ${tempdir}/data.rsa.sig ${tempdir}/data.plain +} + +# ==================================================================== +# FUNCTION: eckey_tests +# DESCRIPTION: A helper function for testing EC key. +# PARAMETERS: +# $1 - slot index +# $2 - key label +# ==================================================================== +eckey_tests() { + rm -f ${tempdir}/ec.pub.pem ${tempdir}/ec.pub.der + + # The value of id is the hex representation of the ASCII text of the label + id=$(echo -n $2 | xxd -p) + + # Read the public component + pkcs11_tool --slot-index $1 --login --pin myuserpin --id $id --type pubkey \ + --read-object --output-file ${tempdir}/ec.pub.der + openssl ec -inform DER -outform PEM -in ${tempdir}/ec.pub.der -pubin \ + -out ${tempdir}/ec.pub.pem + cat ${tempdir}/ec.pub.pem + + # Create a file with random data + pkcs11_tool --slot-index $1 --generate-random 32 --output-file ${tempdir}/data.plain + + # Perform sign and verification + pkcs11_tool --slot-index $1 --id $id --login --pin myuserpin --sign \ + --mechanism ECDSA-SHA1 --signature-format openssl \ + --input-file ${tempdir}/data.plain --output-file ${tempdir}/data.ec.sig + openssl dgst -sha1 -verify ${tempdir}/ec.pub.pem \ + -signature ${tempdir}/data.ec.sig ${tempdir}/data.plain +} + +export TPM2_PKCS11_STORE="$tempdir" + +echo "TPM2_PKCS11_STORE=$TPM2_PKCS11_STORE" + +echo "testdata">${tempdir}/data + +echo "Should have uninitialized token" +pkcs11_tool -T | grep -q uninitialized +echo "Found uninitialized token" + +# pkcs11_tool slot index starts from 0 +pkcs11tool_slot_index=0 + +# tpm2_pkcs11 slot index starts from 1 +tpm2pkcs11_slot_index=1 + +###################### +# Provision keys (primary key without auth value) +##################### + +echo "Provision keys (primary key without auth value) starts here" + +# Check if TPM2_PKCS11_BACKEND is set to fapi +if [ "$TPM2_PKCS11_BACKEND" != "fapi" ]; then + tpm2_clear -c p + tpm2_createprimary -G ecc -c ${tempdir}/primarynoauth.ctx + tpm2_evictcontrol -c ${tempdir}/primarynoauth.ctx 0x81000001 +fi + +# Create an RSA key with no auth value +create_key rsa 0x81000001 "" primnoauth_rsakeynoauth 0x81000002 "" + +# Create an RSA key with auth value +create_key rsa 0x81000001 "" primnoauth_rsakeywithauth 0x81000003 rsakeyauth123 + +# Create an ECC key with no auth value +create_key ecc 0x81000001 "" primnoauth_eckeynoauth 0x81000004 "" + +# Create an ECC key with auth value +create_key ecc 0x81000001 "" primnoauth_eckeywithauth 0x81000005 eckeyauth123 + +######## +# Tests (primary key without auth value) +######## + +echo "Testing (primary key without auth value) starts here" + +# Initialize the token in the first slot +pkcs11_tool --slot-index $pkcs11tool_slot_index --init-token --label tpm2-token-primary-no-auth --so-pin mysopin +pkcs11_tool --slot-index $pkcs11tool_slot_index --login --login-type="so" --init-pin --so-pin mysopin --pin myuserpin + +pkcs11_tool --list-token-slots +pkcs11_tool --list-token-slots | grep "Slot 0 (0x1): tpm2-token-primary-no-auth" +pkcs11_tool --list-token-slots | grep -Pz "Slot 1 \(0x2\): \n token state: uninitialized" + +# Import keys using their persistent handles +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --persistent-handle 0x81000002 \ + --key-label rsakeynoauth-persist +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --persistent-handle 0x81000003 --key-auth rsakeyauth123 \ + --key-label rsakeywithauth-persist +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --persistent-handle 0x81000004 \ + --key-label eckeynoauth-persist +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --persistent-handle 0x81000005 --key-auth eckeyauth123 \ + --key-label eckeywithauth-persist + +# Import keys using their object files +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --parent-persistent-handle 0x81000001 \ + --public ${tempdir}/primnoauth_rsakeynoauth.pub \ + --private ${tempdir}/primnoauth_rsakeynoauth.priv \ + --key-label rsakeynoauth-objects +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --parent-persistent-handle 0x81000001 \ + --public ${tempdir}/primnoauth_rsakeywithauth.pub \ + --private ${tempdir}/primnoauth_rsakeywithauth.priv \ + --key-auth rsakeyauth123 \ + --key-label rsakeywithauth-objects +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --parent-persistent-handle 0x81000001 \ + --public ${tempdir}/primnoauth_eckeynoauth.pub \ + --private ${tempdir}/primnoauth_eckeynoauth.priv \ + --key-label eckeynoauth-objects +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --parent-persistent-handle 0x81000001 \ + --public ${tempdir}/primnoauth_eckeywithauth.pub \ + --private ${tempdir}/primnoauth_eckeywithauth.priv \ + --key-auth eckeyauth123 \ + --key-label eckeywithauth-objects + +pkcs11_tool --slot-index $pkcs11tool_slot_index --list-objects --login --pin myuserpin + +# Iterate through all the imported keys: +rsakey_tests $pkcs11tool_slot_index rsakeynoauth-persist +rsakey_tests $pkcs11tool_slot_index rsakeywithauth-persist +eckey_tests $pkcs11tool_slot_index eckeynoauth-persist +eckey_tests $pkcs11tool_slot_index eckeywithauth-persist +rsakey_tests $pkcs11tool_slot_index rsakeynoauth-objects +rsakey_tests $pkcs11tool_slot_index rsakeywithauth-objects +eckey_tests $pkcs11tool_slot_index eckeynoauth-objects +eckey_tests $pkcs11tool_slot_index eckeywithauth-objects + +################## +# Negative testing +################## + +echo "Negative testing starts here" + +set +e + +key_import bad 2> ${tempdir}/error.log && exit 1 +cat ${tempdir}/error.log | grep "Missing inputs" || exit 1 + +key_import -bad 2> ${tempdir}/error.log && exit 1 +cat ${tempdir}/error.log | grep "invalid option" || exit 1 + +key_import --bad 2> ${tempdir}/error.log && exit 1 +cat ${tempdir}/error.log | grep "unrecognized option" || exit 1 + +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --persistent-handle 0x81000002 \ + --key-label rsakeynoauth-persist 2> ${tempdir}/error.log && exit 1 +cat ${tempdir}/error.log | grep "Missing inputs" || exit 1 + +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --persistent-handle 0x81000002 2> ${tempdir}/error.log && exit 1 +cat ${tempdir}/error.log | grep "Missing inputs" || exit 1 + +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --persistent-handle 0x81000002 2> ${tempdir}/error.log && exit 1 +cat ${tempdir}/error.log | grep "Missing inputs" || exit 1 + +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --persistent-handle 0xbad \ + --key-label rsakeynoauth-persist 2> ${tempdir}/error.log && exit 1 +cat ${tempdir}/error.log | grep "Expecting an 8-character long hexadecimal" || exit 1 + +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --parent-persistent-handle 0x81000001 \ + --public ${tempdir}/bad.pub \ + --private ${tempdir}/primnoauth_rsakeynoauth.priv \ + --key-label rsakeynoauth-objects 2> ${tempdir}/error.log && exit 1 +cat ${tempdir}/error.log | grep "Could not open the file \"${tempdir}/bad.pub\"" || exit 1 + +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --parent-persistent-handle 0x81000001 \ + --public ${tempdir}/primnoauth_rsakeynoauth.pub \ + --private ${tempdir}/bad.priv \ + --key-label rsakeynoauth-objects 2> ${tempdir}/error.log && exit 1 +cat ${tempdir}/error.log | grep "Could not open the file \"${tempdir}/bad.priv\"" || exit 1 + +set -e + +# Check if TPM2_PKCS11_BACKEND is set to fapi +if [ "$TPM2_PKCS11_BACKEND" == "fapi" ]; then + exit 0 +fi + +################### +# Provision keys (primary key with auth value) +################### + +echo "Provision keys (primary key with auth value) starts here" + +tpm2_createprimary -G ecc -c ${tempdir}/primarywithauth.ctx -p parentauth123 +tpm2_evictcontrol -c ${tempdir}/primarywithauth.ctx 0x81000011 + +# Create an RSA key with no auth value +create_key rsa 0x81000011 parentauth123 primwithauth_rsakeynoauth 0x81000012 "" + +# Create an RSA key with auth value +create_key rsa 0x81000011 parentauth123 primwithauth_rsakeywithauth 0x81000013 rsakeyauth123 + +# Create an ECC key with no auth value +create_key ecc 0x81000011 parentauth123 primwithauth_eckeynoauth 0x81000014 "" + +# Create an ECC key with auth value +create_key ecc 0x81000011 parentauth123 primwithauth_eckeywithauth 0x81000015 eckeyauth123 + +######## +# Tests (primary key with auth value) +######## + +echo "Testing (primary key with auth value) starts here" + +pkcs11tool_slot_index=$(expr $pkcs11tool_slot_index + 1) +tpm2pkcs11_slot_index=$(expr $tpm2pkcs11_slot_index + 1) + +# Initialize the token in the second slot +pid=$(tpm2_ptool init --primary-handle 0x81000011 --primary-auth parentauth123 \ + --path $TPM2_PKCS11_STORE | grep id | sed 's/id: //') +tpm2_ptool addtoken --pid $pid --sopin mysopin --userpin myuserpin \ + --label tpm2-token-primary-with-auth --path $TPM2_PKCS11_STORE + +pkcs11_tool --list-token-slots +pkcs11_tool --list-token-slots | grep "Slot 0 (0x1): tpm2-token-primary-no-auth" +pkcs11_tool --list-token-slots | grep "Slot 1 (0x2): tpm2-token-primary-with-auth" +pkcs11_tool --list-token-slots | grep -Pz "Slot 2 \(0x3\): \n token state: uninitialized" + +# Import keys using their persistent handles +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --persistent-handle 0x81000012 \ + --key-label rsakeynoauth-persist +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --persistent-handle 0x81000013 --key-auth rsakeyauth123 \ + --key-label rsakeywithauth-persist +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --persistent-handle 0x81000014 \ + --key-label eckeynoauth-persist +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --persistent-handle 0x81000015 --key-auth eckeyauth123 \ + --key-label eckeywithauth-persist + +# Import keys using their object files +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --parent-persistent-handle 0x81000011 \ + --parent-auth parentauth123 \ + --public ${tempdir}/primwithauth_rsakeynoauth.pub \ + --private ${tempdir}/primwithauth_rsakeynoauth.priv \ + --key-label rsakeynoauth-objects +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --parent-persistent-handle 0x81000011 \ + --parent-auth parentauth123 \ + --public ${tempdir}/primwithauth_rsakeywithauth.pub \ + --private ${tempdir}/primwithauth_rsakeywithauth.priv \ + --key-auth rsakeyauth123 \ + --key-label rsakeywithauth-objects +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --parent-persistent-handle 0x81000011 \ + --parent-auth parentauth123 \ + --public ${tempdir}/primwithauth_eckeynoauth.pub \ + --private ${tempdir}/primwithauth_eckeynoauth.priv \ + --key-label eckeynoauth-objects +key_import --slot-id $tpm2pkcs11_slot_index --user-pin myuserpin \ + --tcti "$TPM2TOOLS_TCTI" \ + --parent-persistent-handle 0x81000011 \ + --parent-auth parentauth123 \ + --public ${tempdir}/primwithauth_eckeywithauth.pub \ + --private ${tempdir}/primwithauth_eckeywithauth.priv \ + --key-auth eckeyauth123 \ + --key-label eckeywithauth-objects + +pkcs11_tool --slot-index $pkcs11tool_slot_index --list-objects --login --pin myuserpin + +# Iterate through all the imported keys: +rsakey_tests $pkcs11tool_slot_index rsakeynoauth-persist +rsakey_tests $pkcs11tool_slot_index rsakeywithauth-persist +eckey_tests $pkcs11tool_slot_index eckeynoauth-persist +eckey_tests $pkcs11tool_slot_index eckeywithauth-persist +rsakey_tests $pkcs11tool_slot_index rsakeynoauth-objects +rsakey_tests $pkcs11tool_slot_index rsakeywithauth-objects +eckey_tests $pkcs11tool_slot_index eckeynoauth-objects +eckey_tests $pkcs11tool_slot_index eckeywithauth-objects + +exit 0 diff --git a/tools/key_import/import.c b/tools/key_import/import.c new file mode 100644 index 00000000..caa4a972 --- /dev/null +++ b/tools/key_import/import.c @@ -0,0 +1,767 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2024 Infineon Technologies AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "attrs.h" +#include "db.h" +#include "pkcs11.h" +#include "utils.h" + +#ifdef DEBUG +#define PRINT_E(...) \ + fprintf(stderr, "%s:%d:%s() ", __FILE__, __LINE__, __func__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n") +#else +#define PRINT_E(...) \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n") +#endif + +#define ADD_ATTR(a, t, l, v) \ + do { \ + CK_ATTRIBUTE_PTR p = a; \ + p->type = t; \ + p->ulValueLen = l; \ + p->pValue = v; \ + } while (0) + +static const struct { + CK_RV rv; + const char *str; +} ckr_str_map[] = { + { CKR_OK, "CKR_OK" }, + { CKR_CANCEL, "CKR_CANCEL" }, + { CKR_HOST_MEMORY, "CKR_HOST_MEMORY" }, + { CKR_SLOT_ID_INVALID, "CKR_SLOT_ID_INVALID" }, + { CKR_GENERAL_ERROR, "CKR_GENERAL_ERROR" }, + { CKR_FUNCTION_FAILED, "CKR_FUNCTION_FAILED" }, + { CKR_ARGUMENTS_BAD, "CKR_ARGUMENTS_BAD" }, + { CKR_NO_EVENT, "CKR_NO_EVENT" }, + { CKR_NEED_TO_CREATE_THREADS, "CKR_NEED_TO_CREATE_THREADS" }, + { CKR_CANT_LOCK, "CKR_CANT_LOCK" }, + { CKR_ATTRIBUTE_READ_ONLY, "CKR_ATTRIBUTE_READ_ONLY" }, + { CKR_ATTRIBUTE_SENSITIVE, "CKR_ATTRIBUTE_SENSITIVE" }, + { CKR_ATTRIBUTE_TYPE_INVALID, "CKR_ATTRIBUTE_TYPE_INVALID" }, + { CKR_ATTRIBUTE_VALUE_INVALID, "CKR_ATTRIBUTE_VALUE_INVALID" }, + { CKR_DATA_INVALID, "CKR_DATA_INVALID" }, + { CKR_DATA_LEN_RANGE, "CKR_DATA_LEN_RANGE" }, + { CKR_DEVICE_ERROR, "CKR_DEVICE_ERROR" }, + { CKR_DEVICE_MEMORY, "CKR_DEVICE_MEMORY" }, + { CKR_DEVICE_REMOVED, "CKR_DEVICE_REMOVED" }, + { CKR_ENCRYPTED_DATA_INVALID, "CKR_ENCRYPTED_DATA_INVALID" }, + { CKR_ENCRYPTED_DATA_LEN_RANGE, "CKR_ENCRYPTED_DATA_LEN_RANGE" }, + { CKR_FUNCTION_CANCELED, "CKR_FUNCTION_CANCELED" }, + { CKR_FUNCTION_NOT_PARALLEL, "CKR_FUNCTION_NOT_PARALLEL" }, + { CKR_FUNCTION_NOT_SUPPORTED, "CKR_FUNCTION_NOT_SUPPORTED" }, + { CKR_KEY_HANDLE_INVALID, "CKR_KEY_HANDLE_INVALID" }, + { CKR_KEY_SIZE_RANGE, "CKR_KEY_SIZE_RANGE" }, + { CKR_KEY_TYPE_INCONSISTENT, "CKR_KEY_TYPE_INCONSISTENT" }, + { CKR_KEY_NOT_NEEDED, "CKR_KEY_NOT_NEEDED" }, + { CKR_KEY_CHANGED, "CKR_KEY_CHANGED" }, + { CKR_KEY_NEEDED, "CKR_KEY_NEEDED" }, + { CKR_KEY_INDIGESTIBLE, "CKR_KEY_INDIGESTIBLE" }, + { CKR_KEY_FUNCTION_NOT_PERMITTED, "CKR_KEY_FUNCTION_NOT_PERMITTED" }, + { CKR_KEY_NOT_WRAPPABLE, "CKR_KEY_NOT_WRAPPABLE" }, + { CKR_KEY_UNEXTRACTABLE, "CKR_KEY_UNEXTRACTABLE" }, + { CKR_MECHANISM_INVALID, "CKR_MECHANISM_INVALID" }, + { CKR_MECHANISM_PARAM_INVALID, "CKR_MECHANISM_PARAM_INVALID" }, + { CKR_OBJECT_HANDLE_INVALID, "CKR_OBJECT_HANDLE_INVALID" }, + { CKR_OPERATION_ACTIVE, "CKR_OPERATION_ACTIVE" }, + { CKR_OPERATION_NOT_INITIALIZED, "CKR_OPERATION_NOT_INITIALIZED" }, + { CKR_PIN_INCORRECT, "CKR_PIN_INCORRECT" }, + { CKR_PIN_INVALID, "CKR_PIN_INVALID" }, + { CKR_PIN_LEN_RANGE, "CKR_PIN_LEN_RANGE" }, + { CKR_PIN_EXPIRED, "CKR_PIN_EXPIRED" }, + { CKR_PIN_LOCKED, "CKR_PIN_LOCKED" }, + { CKR_SESSION_CLOSED, "CKR_SESSION_CLOSED" }, + { CKR_SESSION_COUNT, "CKR_SESSION_COUNT" }, + { CKR_SESSION_HANDLE_INVALID, "CKR_SESSION_HANDLE_INVALID" }, + { CKR_SESSION_PARALLEL_NOT_SUPPORTED, "CKR_SESSION_PARALLEL_NOT_SUPPORTED" }, + { CKR_SESSION_READ_ONLY, "CKR_SESSION_READ_ONLY" }, + { CKR_SESSION_EXISTS, "CKR_SESSION_EXISTS" }, + { CKR_SESSION_READ_ONLY_EXISTS, "CKR_SESSION_READ_ONLY_EXISTS" }, + { CKR_SESSION_READ_WRITE_SO_EXISTS, "CKR_SESSION_READ_WRITE_SO_EXISTS" }, + { CKR_SIGNATURE_INVALID, "CKR_SIGNATURE_INVALID" }, + { CKR_SIGNATURE_LEN_RANGE, "CKR_SIGNATURE_LEN_RANGE" }, + { CKR_TEMPLATE_INCOMPLETE, "CKR_TEMPLATE_INCOMPLETE" }, + { CKR_TEMPLATE_INCONSISTENT, "CKR_TEMPLATE_INCONSISTENT" }, + { CKR_TOKEN_NOT_PRESENT, "CKR_TOKEN_NOT_PRESENT" }, + { CKR_TOKEN_NOT_RECOGNIZED, "CKR_TOKEN_NOT_RECOGNIZED" }, + { CKR_TOKEN_WRITE_PROTECTED, "CKR_TOKEN_WRITE_PROTECTED" }, + { CKR_UNWRAPPING_KEY_HANDLE_INVALID, "CKR_UNWRAPPING_KEY_HANDLE_INVALID" }, + { CKR_UNWRAPPING_KEY_SIZE_RANGE, "CKR_UNWRAPPING_KEY_SIZE_RANGE" }, + { CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT, "CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT" }, + { CKR_USER_ALREADY_LOGGED_IN, "CKR_USER_ALREADY_LOGGED_IN" }, + { CKR_USER_NOT_LOGGED_IN, "CKR_USER_NOT_LOGGED_IN" }, + { CKR_USER_PIN_NOT_INITIALIZED, "CKR_USER_PIN_NOT_INITIALIZED" }, + { CKR_USER_TYPE_INVALID, "CKR_USER_TYPE_INVALID" }, + { CKR_USER_ANOTHER_ALREADY_LOGGED_IN, "CKR_USER_ANOTHER_ALREADY_LOGGED_IN" }, + { CKR_USER_TOO_MANY_TYPES, "CKR_USER_TOO_MANY_TYPES" }, + { CKR_WRAPPED_KEY_INVALID, "CKR_WRAPPED_KEY_INVALID" }, + { CKR_WRAPPED_KEY_LEN_RANGE, "CKR_WRAPPED_KEY_LEN_RANGE" }, + { CKR_WRAPPING_KEY_HANDLE_INVALID, "CKR_WRAPPING_KEY_HANDLE_INVALID" }, + { CKR_WRAPPING_KEY_SIZE_RANGE, "CKR_WRAPPING_KEY_SIZE_RANGE" }, + { CKR_WRAPPING_KEY_TYPE_INCONSISTENT, "CKR_WRAPPING_KEY_TYPE_INCONSISTENT" }, + { CKR_RANDOM_SEED_NOT_SUPPORTED, "CKR_RANDOM_SEED_NOT_SUPPORTED" }, + { CKR_RANDOM_NO_RNG, "CKR_RANDOM_NO_RNG" }, + { CKR_DOMAIN_PARAMS_INVALID, "CKR_DOMAIN_PARAMS_INVALID" }, + { CKR_BUFFER_TOO_SMALL, "CKR_BUFFER_TOO_SMALL" }, + { CKR_SAVED_STATE_INVALID, "CKR_SAVED_STATE_INVALID" }, + { CKR_INFORMATION_SENSITIVE, "CKR_INFORMATION_SENSITIVE" }, + { CKR_STATE_UNSAVEABLE, "CKR_STATE_UNSAVEABLE" }, + { CKR_CRYPTOKI_NOT_INITIALIZED, "CKR_CRYPTOKI_NOT_INITIALIZED" }, + { CKR_CRYPTOKI_ALREADY_INITIALIZED, "CKR_CRYPTOKI_ALREADY_INITIALIZED" }, + { CKR_MUTEX_BAD, "CKR_MUTEX_BAD" }, + { CKR_MUTEX_NOT_LOCKED, "CKR_MUTEX_NOT_LOCKED" }, + { CKR_VENDOR_DEFINED, "CKR_VENDOR_DEFINED" }, +}; + +static const char * +ckr_to_string(CK_RV rv) { + for (size_t i = 0; i < ARRAY_LEN(ckr_str_map); i++) { + if (ckr_str_map[i].rv == rv) { + return ckr_str_map[i].str; + } + } + + return "unknown error"; +} + +int +tpm2_ec_alg_to_asn1(TPM2_ALGORITHM_ID alg, uint8_t **der) { + int ret = -1; + int nid = NID_undef; + ASN1_OBJECT *obj = NULL; + unsigned char *d = NULL; + int d_len = 0; + + switch (alg) { + case TPM2_ECC_NIST_P192: + nid = NID_X9_62_prime192v1; + break; + case TPM2_ECC_NIST_P224: + nid = NID_secp224r1; + break; + case TPM2_ECC_NIST_P256: + nid = NID_X9_62_prime256v1; + break; + case TPM2_ECC_NIST_P384: + nid = NID_secp384r1; + break; + case TPM2_ECC_NIST_P521: + nid = NID_secp521r1; + break; + default: + PRINT_E("Unsupported TPM EC algorithm: %d", alg); + goto exit; + } + + obj = OBJ_nid2obj(nid); + if (obj == NULL) { + PRINT_E("Failed to convert NID to ASN1_OBJECT"); + goto exit; + } + + d_len = i2d_ASN1_OBJECT(obj, &d); + if (!d_len || !d) { + PRINT_E("i2d_ASN1_OBJECT has failed"); + goto exit; + } + + *der = malloc(d_len); + if (!der) { + PRINT_E("calloc has failed"); + goto exit; + } + + memcpy(*der, d, d_len); + + ret = d_len; +exit: + ASN1_OBJECT_free(obj); + OPENSSL_free(d); + return ret; +} + +int +c_initialize(void) { + CK_RV rv; + CK_C_INITIALIZE_ARGS args = { + .CreateMutex = NULL, + .DestroyMutex = NULL, + .LockMutex = NULL, + .UnlockMutex = NULL, + .flags = CKF_LIBRARY_CANT_CREATE_OS_THREADS, + }; + + rv = C_Initialize(&args); + if (rv != CKR_OK) { + PRINT_E("C_Initialize has failed: %s", ckr_to_string(rv)); + return 1; + } + + return 0; +} + +int +c_finalize(void) { + CK_RV rv; + + rv = C_Finalize(NULL_PTR); + if (rv != CKR_OK && rv != CKR_CRYPTOKI_NOT_INITIALIZED) { + PRINT_E("C_Finalize has failed: %s", ckr_to_string(rv)); + return 1; + } + + return 0; +} + +int +c_get_info(void) { + CK_RV rv; + CK_INFO info; + + rv = C_GetInfo(&info); + if (rv != CKR_OK) { + PRINT_E("C_GetInfo has failed: %s", ckr_to_string(rv)); + return 1; + } + + return 0; +} + +int +c_get_slot(CK_SLOT_ID slot_id) { + CK_RV rv; + CK_TOKEN_INFO info = { 0 }; + + rv = C_GetTokenInfo(slot_id, &info); + if (rv != CKR_OK) { + PRINT_E("C_GetTokenInfo has failed: %s", ckr_to_string(rv)); + return 1; + } + + if (info.flags & CKF_TOKEN_INITIALIZED) { + printf("Slot id: %ld\n", slot_id); + + info.label[sizeof(info.label) - 1] = '\0'; + printf("Token label: %s\n", info.label); + + info.manufacturerID[sizeof(info.manufacturerID) - 1] = '\0'; + printf("Token manufacturer id: %s\n", info.manufacturerID); + + info.model[sizeof(info.model) - 1] = '\0'; + printf("Token model: %s\n", info.model); + + info.serialNumber[sizeof(info.serialNumber) - 1] = '\0'; + printf("Token serial number: %s\n", info.serialNumber); + } else { + PRINT_E("Token is not initialized."); + return 1; + } + + return 0; +} + +int +c_open_session(CK_SLOT_ID slot_id, CK_SESSION_HANDLE *session) { + CK_RV rv; + CK_FLAGS flags = CKF_SERIAL_SESSION | CKF_RW_SESSION; + CK_SESSION_HANDLE s = CK_INVALID_HANDLE; + + rv = C_OpenSession(slot_id, flags, NULL, NULL, &s); + if (rv != CKR_OK) { + PRINT_E("C_OpenSession has failed: %s", ckr_to_string(rv)); + return 1; + } + + *session = s; + + return 0; +} + +int +c_close_session(CK_SESSION_HANDLE session) { + CK_RV rv; + + if (session == CK_INVALID_HANDLE) { + return 0; + } + + rv = C_CloseSession(session); + if (rv != CKR_OK) { + PRINT_E("C_CloseSession has failed: %s", ckr_to_string(rv)); + return 1; + } + + return 0; +} + +int +c_login(CK_SESSION_HANDLE session, CK_UTF8CHAR_PTR user_pin) { + CK_RV rv; + CK_USER_TYPE user_type = CKU_USER; + CK_ULONG user_pin_len = 0; + + if (session == CK_INVALID_HANDLE) { + return 0; + } + + if (user_pin) { + user_pin_len = strlen((char *)user_pin); + } + + rv = C_Login(session, user_type, user_pin, user_pin_len); + if (rv != CKR_OK) { + PRINT_E("C_Login has failed: %s", ckr_to_string(rv)); + return 1; + } + + return 0; +} + +int +c_generate_keypair(CK_SESSION_HANDLE session, + CK_MECHANISM *mech, + CK_ATTRIBUTE *pub, + CK_ULONG pub_len, + CK_ATTRIBUTE *priv, + CK_ULONG priv_len) { + CK_RV rv; + CK_OBJECT_HANDLE pubkey; + CK_OBJECT_HANDLE privkey; + + rv = C_GenerateKeyPair(session, mech, pub, pub_len, priv, priv_len, &pubkey, &privkey); + if (rv != CKR_OK) { + PRINT_E("C_GenerateKeyPair has failed: %s", ckr_to_string(rv)); + return 1; + } + + return 0; +} + +int +main(int argc, char **argv) { + + int ret = 1; + + /* tpm2-tss variables */ + + TSS2_RC tss2_rc; + TPM2_HANDLE parent_persistent_handle = 0; + TPM2_HANDLE persistent_handle = 0; + ESYS_CONTEXT *esys_ctx = NULL; + TSS2_TCTI_CONTEXT *tcti = NULL; + TPM2B_PUBLIC *key_public = NULL; + TPM2B_PUBLIC pub_tpm2b = { 0 }; + TPM2B_PRIVATE priv_tpm2b = { 0 }; + ESYS_TR parent_esys_tr = 0; + ESYS_TR esys_tr = 0; + TPM2B_DIGEST parent_auth = { 0 }; + unsigned long tpm2_handle = 0; + char *pub_path = NULL; + char *priv_path = NULL; + size_t offset = 0; + + /* PKCS #11 variables */ + + CK_UTF8CHAR_PTR key_label = NULL; + CK_UTF8CHAR_PTR parent_auth_value = NULL; + CK_UTF8CHAR_PTR auth_value = NULL; + CK_ULONG pubkey_attrs_count = 0; + CK_ULONG privkey_attrs_count = 0; + CK_MECHANISM mech + = { .mechanism = CKR_MECHANISM_INVALID, .pParameter = NULL, .ulParameterLen = 0 }; + CK_BBOOL ck_true = CK_TRUE; + CK_ATTRIBUTE pubkey_attrs[4] = { 0 }; + CK_ATTRIBUTE privkey_attrs[5] = { 0 }; + int pub_attrs_ind = 0; + int priv_attrs_ind = 0; + CK_SESSION_HANDLE session = CK_INVALID_HANDLE; + CK_SLOT_ID slot_id = 0; + CK_UTF8CHAR_PTR user_pin = NULL; + uint8_t *cka_ec_params = NULL; + int cka_ec_params_len = 0; + + /* getopt variables */ + + int opt; + int opt_index = 0; + const char *short_opts = "A:a:C:c:hk:l:r:s:u:t:"; + static struct option long_opts[] + = { { "key-auth", required_argument, NULL, 'a' }, + { "persistent-handle", required_argument, NULL, 'c' }, + { "help", no_argument, NULL, 'h' }, + { "user-pin", required_argument, NULL, 'k' }, + { "key-label", required_argument, NULL, 'l' }, + { "parent-persistent-handle", required_argument, NULL, 'C' }, + { "parent-auth", required_argument, NULL, 'A' }, + { "private", required_argument, NULL, 'r' }, + { "slot-id", required_argument, NULL, 's' }, + { "public", required_argument, NULL, 'u' }, + { "tcti", required_argument, NULL, 't' }, + { 0, 0, 0, 0 } }; + + /* File reading variables */ + + FILE *pub_f = NULL, *priv_f = NULL; + uint8_t *pub_blob = NULL, *priv_blob = NULL; + size_t pub_sz = 0, priv_sz = 0; + + /* End of local variable declarations */ + + while ((opt = getopt_long(argc, argv, short_opts, long_opts, &opt_index)) != -1) { + switch (opt) { + case 'A': + parent_auth_value = (unsigned char *)optarg; + break; + + case 'a': + auth_value = (unsigned char *)optarg; + break; + + case 'C': + if (strlen(optarg) != 10 || strncmp(optarg, "0x", 2)) { + PRINT_E("Invalid input format. Expecting an 8-character long" + " hexadecimal with the prefix '0x', e.g., 0x81000001."); + goto exit; + } + + parent_persistent_handle = strtol(optarg, NULL, 16); + + if (parent_persistent_handle < TPM2_PERSISTENT_FIRST + || parent_persistent_handle > TPM2_PERSISTENT_LAST) { + PRINT_E("Invalid parent persistent handle. The value must be in the range of " + "0x%08x to 0x%08x.", + TPM2_PERSISTENT_FIRST, TPM2_PERSISTENT_LAST); + goto exit; + } + + break; + + case 'c': + if (strlen(optarg) != 10 || strncmp(optarg, "0x", 2)) { + PRINT_E("Invalid input format. Expecting an 8-character long" + " hexadecimal with the prefix '0x', e.g., 0x81000001."); + goto exit; + } + + persistent_handle = strtol(optarg, NULL, 16); + + if (persistent_handle < TPM2_PERSISTENT_FIRST + || persistent_handle > TPM2_PERSISTENT_LAST) { + PRINT_E("Invalid persistent handle. The value must be in the range of 0x%08x to " + "0x%08x.", + TPM2_PERSISTENT_FIRST, TPM2_PERSISTENT_LAST); + goto exit; + } + + break; + + case 'h': + printf("A TPM key import tool for tpm2-pkcs11 tokens, " + "capable of importing keys as either persistent handles or key objects.\n"); + printf("Usage: key_import []\n"); + printf("Options:\n"); + printf(" -A, --parent-auth The authorization value of the\n"); + printf(" parent key.\n"); + printf(" -a, --key-auth The TPM key's authorization value.\n"); + printf(" -C, --parent-persistent-handle The persistent handle of the parent key\n"); + printf(" to which the key objects are associated.\n"); + printf(" -c, --persistent-handle The persistent handle of the TPM key\n"); + printf(" to be imported.\n"); + printf(" If this option is selected, do not\n"); + printf(" specify -r, -u, -C, or -A.\n"); + printf(" -h, --help Show this help message.\n"); + printf(" -k, --user-pin The PKCS#11 token user PIN.\n"); + printf(" -l, --key-label The PKCS#11 key label to assign to the\n"); + printf(" TPM key.\n"); + printf(" -r, --private A file containing the sensitive portion\n"); + printf(" of the TPM key object. This option is\n"); + printf(" specified alongside -u, -C, and -A.\n"); + printf(" If this option is selected, do not\n"); + printf(" specify -c.\n"); + printf(" -s, --slot-id The PKCS#11 slot ID where the token\n"); + printf(" resides (default is 0).\n"); + printf(" -u, --public A file containing the public portion of\n"); + printf(" the TPM key object. This option is\n"); + printf(" specified alongside -r, -C, and -A.\n"); + printf(" If this option is selected, do not\n"); + printf(" specify -c.\n"); + printf(" -t, --tcti The transmission interface with the TPM.\n"); + + ret = 0; + goto exit; + + case 'k': + user_pin = (unsigned char *)optarg; + break; + + case 'l': + key_label = (unsigned char *)optarg; + break; + + case 'r': + priv_path = optarg; + break; + + case 's': + slot_id = strtol(optarg, NULL, 10); + if (slot_id > MAX_TOKEN_CNT) { + PRINT_E("Slot ID cannot be greater than %d", MAX_TOKEN_CNT); + goto exit; + } + break; + + case 'u': + pub_path = optarg; + break; + + case 't': + tss2_rc = Tss2_TctiLdr_Initialize(optarg, &tcti); + if (tss2_rc != TSS2_RC_SUCCESS) { + PRINT_E("Tcti initialization has failed with: %s", Tss2_RC_Decode(tss2_rc)); + goto exit; + } + + tss2_rc = Esys_Initialize(&esys_ctx, tcti, NULL); + if (tss2_rc != TSS2_RC_SUCCESS) { + PRINT_E("Esys initialization has failed with: %s", Tss2_RC_Decode(tss2_rc)); + goto exit; + } + + break; + + case '?': + /* The option is marked as required_argument, but no argument is provided. */ + goto exit; + + default: + /* The option is unrecognized. */ + PRINT_E("Invalid option. Check the command usage by running: %s --help", argv[0]); + goto exit; + } + } + + if (!user_pin || !esys_ctx || !key_label) { + PRINT_E("Missing inputs. Check the command usage by running: %s --help", argv[0]); + goto exit; + } + + if (persistent_handle && pub_path && priv_path && parent_persistent_handle) { + PRINT_E("Ambiguous options detected. Persistent handle and key objects cannot" + " be used together. Check the command usage by running: %s --help", + argv[0]); + goto exit; + } + + ADD_ATTR(&pubkey_attrs[pub_attrs_ind++], CKA_ID, strlen((char *)key_label), key_label); + ADD_ATTR(&pubkey_attrs[pub_attrs_ind++], CKA_LABEL, strlen((char *)key_label), key_label); + + ADD_ATTR(&privkey_attrs[priv_attrs_ind++], CKA_ID, strlen((char *)key_label), key_label); + ADD_ATTR(&privkey_attrs[priv_attrs_ind++], CKA_LABEL, strlen((char *)key_label), key_label); + ADD_ATTR(&privkey_attrs[priv_attrs_ind++], CKA_SENSITIVE, sizeof(ck_true), &ck_true); + if (auth_value) { + ADD_ATTR(&privkey_attrs[priv_attrs_ind++], CKA_TPM2_OBJAUTH, strlen((char *)auth_value), + auth_value); + } + + if (persistent_handle) { + + tpm2_handle = persistent_handle; + ADD_ATTR(&pubkey_attrs[pub_attrs_ind++], CKA_TPM2_PERSISTENT_HANDLE, sizeof(tpm2_handle), + &tpm2_handle); + ADD_ATTR(&privkey_attrs[priv_attrs_ind++], CKA_TPM2_PERSISTENT_HANDLE, sizeof(tpm2_handle), + &tpm2_handle); + + /* Create the ESYS_TR object from the persistent handle */ + + tss2_rc = Esys_TR_FromTPMPublic(esys_ctx, persistent_handle, ESYS_TR_NONE, ESYS_TR_NONE, + ESYS_TR_NONE, &esys_tr); + if (tss2_rc != TSS2_RC_SUCCESS) { + PRINT_E("Esys_TR_FromTPMPublic has failed with: %s", Tss2_RC_Decode(tss2_rc)); + goto exit; + } + + } else if (pub_path && priv_path && parent_persistent_handle) { + + /* Read the public and private key blobs */ + + pub_f = fopen(pub_path, "rb"); + if (!pub_f) { + PRINT_E("Could not open the file \"%s\": %s", pub_path, strerror(errno)); + goto exit; + } + + priv_f = fopen(priv_path, "rb"); + if (!priv_f) { + PRINT_E("Could not open the file \"%s\": %s", priv_path, strerror(errno)); + goto exit; + } + + pub_sz = sizeof(TPM2B_PUBLIC); + priv_sz = sizeof(TPM2B_PRIVATE); + pub_blob = calloc(1, pub_sz); + priv_blob = calloc(1, priv_sz); + if (!pub_blob || !priv_blob) { + PRINT_E("calloc has failed"); + goto exit; + } + + pub_sz = fread(pub_blob, 1, sizeof(TPM2B_PUBLIC), pub_f); + if (!feof(pub_f)) { + PRINT_E("Failed to read from pub_f"); + goto exit; + } + + priv_sz = fread(priv_blob, 1, sizeof(TPM2B_PRIVATE), priv_f); + if (!feof(priv_f)) { + PRINT_E("Failed to read from priv_f"); + goto exit; + } + + tss2_rc = Tss2_MU_TPM2B_PUBLIC_Unmarshal(pub_blob, pub_sz, &offset, &pub_tpm2b); + if (tss2_rc != TSS2_RC_SUCCESS) { + PRINT_E("Tss2_MU_PUBLIC_Unmarshal has failed with: %s", Tss2_RC_Decode(tss2_rc)); + goto exit; + } + + offset = 0; + tss2_rc = Tss2_MU_TPM2B_PRIVATE_Unmarshal(priv_blob, priv_sz, &offset, &priv_tpm2b); + if (tss2_rc != TSS2_RC_SUCCESS) { + PRINT_E("Tss2_MU_PRIVATE_Unmarshal has failed with: %s", Tss2_RC_Decode(tss2_rc)); + goto exit; + } + + /* Parent authorization */ + + tss2_rc = Esys_TR_FromTPMPublic(esys_ctx, parent_persistent_handle, ESYS_TR_NONE, + ESYS_TR_NONE, ESYS_TR_NONE, &parent_esys_tr); + if (tss2_rc != TSS2_RC_SUCCESS) { + PRINT_E("Esys_TR_FromTPMPublic has failed with: %s", Tss2_RC_Decode(tss2_rc)); + goto exit; + } + + if (parent_auth_value) { + parent_auth.size = (UINT16)snprintf( + (char *)parent_auth.buffer, sizeof(parent_auth.buffer), "%s", parent_auth_value); + + tss2_rc = Esys_TR_SetAuth(esys_ctx, parent_esys_tr, &parent_auth); + if (tss2_rc != TPM2_RC_SUCCESS) { + PRINT_E("Esys_TR_SetAuth has failed with: %s", Tss2_RC_Decode(tss2_rc)); + goto exit; + } + } + + /* Load the child key objects and create the ESYS_TR object */ + + tss2_rc = Esys_Load(esys_ctx, parent_esys_tr, ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE, + &priv_tpm2b, &pub_tpm2b, &esys_tr); + if (tss2_rc != TSS2_RC_SUCCESS) { + PRINT_E("Esys_Load has failed with: %s", Tss2_RC_Decode(tss2_rc)); + goto exit; + } + + ADD_ATTR(&pubkey_attrs[pub_attrs_ind++], CKA_TPM2_PUB_BLOB, pub_sz, pub_blob); + ADD_ATTR(&privkey_attrs[priv_attrs_ind++], CKA_TPM2_PRIV_BLOB, priv_sz, priv_blob); + + } else { + PRINT_E("Missing inputs. Check the command usage by running: %s --help", argv[0]); + goto exit; + } + + /* Set the mechanism and ECC attributes */ + + tss2_rc = Esys_ReadPublic(esys_ctx, esys_tr, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, + &key_public, NULL, NULL); + if (tss2_rc != TSS2_RC_SUCCESS) { + PRINT_E("Esys_ReadPublic has failed with: %s", Tss2_RC_Decode(tss2_rc)); + goto exit; + } + + switch (key_public->publicArea.type) { + case TPM2_ALG_RSA: + mech.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; + break; + case TPM2_ALG_ECC: + mech.mechanism = CKM_EC_KEY_PAIR_GEN; + cka_ec_params_len = tpm2_ec_alg_to_asn1(key_public->publicArea.parameters.eccDetail.curveID, + &cka_ec_params); + if (!cka_ec_params_len || !cka_ec_params) { + goto exit; + } + + ADD_ATTR(&pubkey_attrs[pub_attrs_ind++], CKA_EC_PARAMS, cka_ec_params_len, cka_ec_params); + break; + default: + PRINT_E("The given TPM key type is not supported."); + goto exit; + } + + /* Finalize the public and private key templates */ + + assert(pub_attrs_ind <= ARRAY_LEN(pubkey_attrs)); + assert(priv_attrs_ind <= ARRAY_LEN(privkey_attrs)); + + pubkey_attrs_count = pub_attrs_ind; + privkey_attrs_count = priv_attrs_ind; + + /* Start the key import process */ + + if (c_initialize() || c_get_info() || c_get_slot(slot_id) || c_open_session(slot_id, &session) + || c_login(session, user_pin) + || c_generate_keypair(session, &mech, pubkey_attrs, pubkey_attrs_count, privkey_attrs, + privkey_attrs_count)) { + goto exit_c; + } + + ret = 0; + +exit_c: + c_close_session(session); + c_finalize(); +exit: + pub_f ? fclose(pub_f) : 0; + priv_f ? fclose(priv_f) : 0; + free(cka_ec_params); + free(pub_blob); + free(priv_blob); + esys_ctx ? Esys_Finalize(&esys_ctx) : 0; + tcti ? Tss2_TctiLdr_Finalize(&tcti) : 0; + free(key_public); + return ret; +} diff --git a/tools/setup.py b/tools/tpm2_ptool/setup.py similarity index 100% rename from tools/setup.py rename to tools/tpm2_ptool/setup.py diff --git a/tools/tpm2_pkcs11/__init__.py b/tools/tpm2_ptool/tpm2_pkcs11/__init__.py similarity index 100% rename from tools/tpm2_pkcs11/__init__.py rename to tools/tpm2_ptool/tpm2_pkcs11/__init__.py diff --git a/tools/tpm2_pkcs11/command.py b/tools/tpm2_ptool/tpm2_pkcs11/command.py similarity index 100% rename from tools/tpm2_pkcs11/command.py rename to tools/tpm2_ptool/tpm2_pkcs11/command.py diff --git a/tools/tpm2_pkcs11/commandlets_keys.py b/tools/tpm2_ptool/tpm2_pkcs11/commandlets_keys.py similarity index 100% rename from tools/tpm2_pkcs11/commandlets_keys.py rename to tools/tpm2_ptool/tpm2_pkcs11/commandlets_keys.py diff --git a/tools/tpm2_pkcs11/commandlets_store.py b/tools/tpm2_ptool/tpm2_pkcs11/commandlets_store.py similarity index 100% rename from tools/tpm2_pkcs11/commandlets_store.py rename to tools/tpm2_ptool/tpm2_pkcs11/commandlets_store.py diff --git a/tools/tpm2_pkcs11/commandlets_token.py b/tools/tpm2_ptool/tpm2_pkcs11/commandlets_token.py similarity index 100% rename from tools/tpm2_pkcs11/commandlets_token.py rename to tools/tpm2_ptool/tpm2_pkcs11/commandlets_token.py diff --git a/tools/tpm2_pkcs11/db.py b/tools/tpm2_ptool/tpm2_pkcs11/db.py similarity index 100% rename from tools/tpm2_pkcs11/db.py rename to tools/tpm2_ptool/tpm2_pkcs11/db.py diff --git a/tools/tpm2_pkcs11/objects.py b/tools/tpm2_ptool/tpm2_pkcs11/objects.py similarity index 100% rename from tools/tpm2_pkcs11/objects.py rename to tools/tpm2_ptool/tpm2_pkcs11/objects.py diff --git a/tools/tpm2_pkcs11/pkcs11t.py b/tools/tpm2_ptool/tpm2_pkcs11/pkcs11t.py similarity index 100% rename from tools/tpm2_pkcs11/pkcs11t.py rename to tools/tpm2_ptool/tpm2_pkcs11/pkcs11t.py diff --git a/tools/tpm2_pkcs11/tpm2.py b/tools/tpm2_ptool/tpm2_pkcs11/tpm2.py similarity index 100% rename from tools/tpm2_pkcs11/tpm2.py rename to tools/tpm2_ptool/tpm2_pkcs11/tpm2.py diff --git a/tools/tpm2_pkcs11/tpm2_ptool.py b/tools/tpm2_ptool/tpm2_pkcs11/tpm2_ptool.py similarity index 100% rename from tools/tpm2_pkcs11/tpm2_ptool.py rename to tools/tpm2_ptool/tpm2_pkcs11/tpm2_ptool.py diff --git a/tools/tpm2_pkcs11/utils.py b/tools/tpm2_ptool/tpm2_pkcs11/utils.py similarity index 100% rename from tools/tpm2_pkcs11/utils.py rename to tools/tpm2_ptool/tpm2_pkcs11/utils.py diff --git a/tools/tpm2_ptool b/tools/tpm2_ptool/tpm2_ptool similarity index 100% rename from tools/tpm2_ptool rename to tools/tpm2_ptool/tpm2_ptool diff --git a/tools/tpm2_ptool.py b/tools/tpm2_ptool/tpm2_ptool.py similarity index 100% rename from tools/tpm2_ptool.py rename to tools/tpm2_ptool/tpm2_ptool.py