static TSS2_RC (*sym_Esys_ReadPublic)(ESYS_CONTEXT *esysContext, ESYS_TR objectHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_PUBLIC **outPublic, TPM2B_NAME **name, TPM2B_NAME **qualifiedName) = NULL;
static TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle) = NULL;
static TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType) = NULL;
+static TSS2_RC (*sym_Esys_TestParms)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPMT_PUBLIC_PARMS *parameters) = NULL;
static TSS2_RC (*sym_Esys_TR_Deserialize)(ESYS_CONTEXT *esys_context, uint8_t const *buffer, size_t buffer_size, ESYS_TR *esys_handle) = NULL;
static TSS2_RC (*sym_Esys_TR_FromTPMPublic)(ESYS_CONTEXT *esysContext, TPM2_HANDLE tpm_handle, ESYS_TR optionalSession1, ESYS_TR optionalSession2, ESYS_TR optionalSession3, ESYS_TR *object) = NULL;
static TSS2_RC (*sym_Esys_TR_GetName)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_NAME **name) = NULL;
DLSYM_ARG(Esys_ReadPublic),
DLSYM_ARG(Esys_StartAuthSession),
DLSYM_ARG(Esys_Startup),
+ DLSYM_ARG(Esys_TestParms),
DLSYM_ARG(Esys_TR_Deserialize),
DLSYM_ARG(Esys_TR_FromTPMPublic),
DLSYM_ARG(Esys_TR_GetName),
#define tpm2_capability_pcrs(c) ((c)->capability_pcrs)
+/* Get the TPMA_ALGORITHM for a TPM2_ALG_ID.
+ *
+ * Returns 1 if the TPM supports the algorithm and the TPMA_ALGORITHM is provided, or 0 if the TPM does not
+ * support the algorithm, or < 0 for any errors. */
+static int tpm2_get_capability_alg(Tpm2Context *c, TPM2_ALG_ID alg, TPMA_ALGORITHM *ret) {
+ TPMU_CAPABILITIES capability;
+ int r;
+
+ assert(c);
+
+ /* The spec explicitly states the TPM2_ALG_ID should be cast to uint32_t. */
+ r = tpm2_get_capability(c, TPM2_CAP_ALGS, (uint32_t) alg, 1, &capability);
+ if (r < 0)
+ return r;
+
+ TPML_ALG_PROPERTY algorithms = capability.algorithms;
+ if (algorithms.count == 0 || algorithms.algProperties[0].alg != alg) {
+ log_debug("TPM does not support alg 0x%02" PRIx16 ".", alg);
+ return 0;
+ }
+
+ if (ret)
+ *ret = algorithms.algProperties[0].algProperties;
+
+ return 1;
+}
+
+/* Returns 1 if the TPM supports the alg, 0 if the TPM does not support the alg, or < 0 for any error. */
+int tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg) {
+ return tpm2_get_capability_alg(c, alg, NULL);
+}
+
+/* Returns 1 if the TPM supports the parms, or 0 if the TPM does not support the parms. */
+bool tpm2_test_parms(Tpm2Context *c, TPMI_ALG_PUBLIC alg, const TPMU_PUBLIC_PARMS *parms) {
+ TSS2_RC rc;
+
+ assert(c);
+ assert(parms);
+
+ TPMT_PUBLIC_PARMS parameters = {
+ .type = alg,
+ .parameters = *parms,
+ };
+
+ rc = sym_Esys_TestParms(c->esys_context, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, ¶meters);
+ if (rc != TSS2_RC_SUCCESS)
+ /* The spec says if the parms are not supported the TPM returns "...the appropriate
+ * unmarshaling error if a parameter is not valid". Since the spec (currently) defines 15
+ * unmarshaling errors, instead of checking for them all here, let's just assume any error
+ * indicates unsupported parms, and log the specific error text. */
+ log_debug("TPM does not support tested parms: %s", sym_Tss2_RC_Decode(rc));
+
+ return rc == TSS2_RC_SUCCESS;
+}
+
+static inline bool tpm2_supports_tpmt_sym_def_object(Tpm2Context *c, const TPMT_SYM_DEF_OBJECT *parameters) {
+ assert(c);
+ assert(parameters);
+
+ TPMU_PUBLIC_PARMS parms = {
+ .symDetail.sym = *parameters,
+ };
+
+ return tpm2_test_parms(c, TPM2_ALG_SYMCIPHER, &parms);
+}
+
+static inline bool tpm2_supports_tpmt_sym_def(Tpm2Context *c, const TPMT_SYM_DEF *parameters) {
+ assert(c);
+ assert(parameters);
+
+ /* Unfortunately, TPMT_SYM_DEF and TPMT_SYM_DEF_OBEJECT are separately defined, even though they are
+ * functionally identical. */
+ TPMT_SYM_DEF_OBJECT object = {
+ .algorithm = parameters->algorithm,
+ .keyBits = parameters->keyBits,
+ .mode = parameters->mode,
+ };
+
+ return tpm2_supports_tpmt_sym_def_object(c, &object);
+}
+
static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
if (!c)
return NULL;
DEFINE_TRIVIAL_REF_UNREF_FUNC(Tpm2Context, tpm2_context, tpm2_context_free);
+static const TPMT_SYM_DEF SESSION_TEMPLATE_SYM_AES_128_CFB = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB, /* The spec requires sessions to use CFB. */
+};
+
int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
_cleanup_tpm2_context_ Tpm2Context *context = NULL;
TSS2_RC rc;
if (r < 0)
return r;
+ /* We require AES and CFB support for session encryption. */
+ r = tpm2_supports_alg(context, TPM2_ALG_AES);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support AES.");
+
+ r = tpm2_supports_alg(context, TPM2_ALG_CFB);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support CFB.");
+
+ if (!tpm2_supports_tpmt_sym_def(context, &SESSION_TEMPLATE_SYM_AES_128_CFB))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support AES-128-CFB.");
+
*ret_context = TAKE_PTR(context);
return 0;
const Tpm2Handle *bind_key,
Tpm2Handle **ret_session) {
- static const TPMT_SYM_DEF symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- };
const TPMA_SESSION sessionAttributes = TPMA_SESSION_DECRYPT | TPMA_SESSION_ENCRYPT |
TPMA_SESSION_CONTINUESESSION;
TSS2_RC rc;
ESYS_TR_NONE,
NULL,
TPM2_SE_HMAC,
- &symmetric,
+ &SESSION_TEMPLATE_SYM_AES_128_CFB,
TPM2_ALG_SHA256,
&session->esys_handle);
if (rc != TSS2_RC_SUCCESS)
bool trial,
Tpm2Handle **ret_session) {
- static const TPMT_SYM_DEF symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- };
TPM2_SE session_type = trial ? TPM2_SE_TRIAL : TPM2_SE_POLICY;
TSS2_RC rc;
int r;
ESYS_TR_NONE,
NULL,
session_type,
- &symmetric,
+ &SESSION_TEMPLATE_SYM_AES_128_CFB,
TPM2_ALG_SHA256,
&session->esys_handle);
if (rc != TSS2_RC_SUCCESS)
assert_se(digest_check(&d, "7481fd1b116078eb3ac2456e4ad542c9b46b9b8eb891335771ca8e7c8f8e4415"));
}
+TEST(tpm_required_tests) {
+ int r;
+
+ _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ r = tpm2_context_new(NULL, &c);
+ if (r < 0) {
+ log_tests_skipped("Could not find TPM");
+ return;
+ }
+
+ TPMU_PUBLIC_PARMS parms = {
+ .symDetail.sym = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB,
+ },
+ };
+
+ /* Test with invalid parms */
+ assert_se(!tpm2_test_parms(c, TPM2_ALG_CFB, &parms));
+
+ TPMU_PUBLIC_PARMS invalid_parms = parms;
+ invalid_parms.symDetail.sym.keyBits.aes = 1;
+ assert_se(!tpm2_test_parms(c, TPM2_ALG_SYMCIPHER, &invalid_parms));
+
+ /* Test with valid parms */
+ assert_se(tpm2_test_parms(c, TPM2_ALG_SYMCIPHER, &parms));
+
+ /* Test invalid algs */
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_ERROR) == 0);
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_LAST + 1) == 0);
+
+ /* Test valid algs */
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_RSA) == 1);
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_AES) == 1);
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_CFB) == 1);
+}
+
#endif /* HAVE_TPM2 */
DEFINE_TEST_MAIN(LOG_DEBUG);