tpm2-software / tpm2-tss

OSS implementation of the TCG TPM2 Software Stack (TSS2)
https://tpm2-software.github.io
BSD 2-Clause "Simplified" License
755 stars 366 forks source link

The value of TPM2_TRANSIENT_LAST is incorrect (off-by-one) #2864

Open polarina opened 4 months ago

polarina commented 4 months ago

The value of TPM2_TRANSIENT_LAST is defined as 0x80fffffe in tss2/tss2_tpm2_types.h. The defined value should be 0x80ffffff.

The Linux kernel's TPM2 resource manager assigns transient handles counting from 0x80ffffff downwards: https://github.com/torvalds/linux/blob/v6.10/drivers/char/tpm/tpm2-space.c#L374

Here is a program I wrote that asserts whether the TPM2 handle of a transient key (as created by Esys_CreatePrimary) falls within the range of a transient handle.

The program crashes with an assertion failure:

$ gcc esys-transient.c -o esys-transient -ltss2-esys 
$ sudo ./esys-transient
esys-transient: esys-transient.c:64: main: Assertion `esys_is_transient(ctx, primarykey_handle)' failed.
Aborted
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <tss2/tss2_esys.h>

const TPM2B_PUBLIC tpm2_template_null = {
    .publicArea = {
        .type = TPM2_ALG_ECC,
        .nameAlg = TPM2_ALG_SHA256,
        .objectAttributes =
            TPMA_OBJECT_FIXEDTPM |
            TPMA_OBJECT_FIXEDPARENT |
            TPMA_OBJECT_SENSITIVEDATAORIGIN |
            TPMA_OBJECT_USERWITHAUTH |
            TPMA_OBJECT_NODA |
            TPMA_OBJECT_RESTRICTED |
            TPMA_OBJECT_DECRYPT,
        .parameters.eccDetail = {
            .symmetric = {
                .algorithm = TPM2_ALG_AES,
                .keyBits.aes = 128,
                .mode.aes = TPM2_ALG_CFB,
            },
            .scheme.scheme = TPM2_ALG_NULL,
            .curveID = TPM2_ECC_NIST_P256,
            .kdf.scheme = TPM2_ALG_NULL,
        },
    },
};

static bool esys_is_transient(ESYS_CONTEXT *ctx, ESYS_TR handle) {
    TPM2_HANDLE tpm2handle;
    TSS2_RC rc = Esys_TR_GetTpmHandle(ctx, handle, &tpm2handle);
    assert(!rc);

    return tpm2handle >= TPM2_TRANSIENT_FIRST && tpm2handle <= TPM2_TRANSIENT_LAST;
}

int main(void) {
    TSS2_RC rc;
    ESYS_CONTEXT *ctx = NULL;

    ESYS_TR primarykey_handle;
    TPM2B_PUBLIC *primarykey_public = NULL;

    rc = Esys_Initialize(&ctx, NULL, NULL);
    if (rc) goto error;

    rc = Esys_CreatePrimary(ctx,
        /* primaryHandle */  ESYS_TR_RH_NULL,
        /* shandleX */       ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
        /* inSensitive */    &(const TPM2B_SENSITIVE_CREATE) { 0 },
        /* inPublic */       &tpm2_template_null,
        /* outsideInfo */    NULL,
        /* creationPCR */    &(const TPML_PCR_SELECTION) { 0 },
        /* objectHandle */   &primarykey_handle,
        /* outPublic */      &primarykey_public,
        /* creationData */   NULL,
        /* creationHash */   NULL,
        /* creationTicket */ NULL);
    if (rc) goto error;

    assert(esys_is_transient(ctx, primarykey_handle));

    rc = Esys_FlushContext(ctx, primarykey_handle);
    if (rc) goto error;

error:
    printf("rc = %d\n", rc);

    free(primarykey_public);
    if (ctx) Esys_Finalize(&ctx);

    return !!rc;
}
joholl commented 4 months ago

TPM reference code (not very helpful here):

TRANSIENT_LAST = TRANSIENT_FIRST + MAX_LOADED_OBJECTS - 1
               = 0x80000000 + 3 - 1

Spec:

A similar approach is used for transient objects with the first assigned handle at the bottom of the range defined by TPM_HT_TRANSIENT and the top of the range determined by the implementation-dependent value of MAX_LOADED_OBJECTS.

I guess we need to look for the next handle range which starts at TPM_HT_PERSISTENT = 0x81000000, so I think you are right.