tpm2-software/tpm2-tss

Create Primary fails with Authorization is Unknown when provisioning the Feature API

chopinrlz opened this issue · 9 comments

I am trying to integrate tpm2-tss into my PowerPass library on Linux to enable TPM key encryption for filesystem data and have been stuck on a problem for a while. I have reviewed YouTube videos and read as much documentation as I can find on the topic, but cannot get past this issue to successfully generate a key in the TPM for encryption. I am compiling a C program on Ubuntu 22.04.4 LTS and I can successfully initialize the FAPI context as well as fetch the TPM info using Fapi_GetInfo but I am stuck trying to create a key and I do not understand how to remedy what is going wrong.

My C code contains the following definitions in the header file for the key path, key type, and lockout.

// powerpasstpm.h

// Path for the new key and key type
const char __POWERPASS_KEY_PATH[] = "/P_ECCP256SHA256/HS/srk/daltas-powerpass-locker";
const char __POWERPASS_KEY_TYPE[] = "sign,decrypt";

// Randomly-generated string
const char __POWERPASS_AUTH_LOCKOUT[] = "c5ce0468588540c8979b09fa71e8b11d";

I have a function in my C application which runs initialize, provision, and create key to create a key in the TPM for encryption.

// powerpasstpm.c
int pptpm_init(void) {
    TSS2_RC res;
    FAPI_CONTEXT* context;
    res = Fapi_Initialize( &context, NULL );
    if( res == TSS2_RC_SUCCESS ) {
        res = Fapi_Provision( context, NULL, NULL, __POWERPASS_AUTH_LOCKOUT );
        if( res == TSS2_RC_SUCCESS ) {
            res = Fapi_CreateKey( context, __POWERPASS_KEY_PATH, __POWERPASS_KEY_TYPE, NULL, NULL );
            switch( res ) {
                case TSS2_RC_SUCCESS:
                    printf( "Successfully created key at %s\n", __POWERPASS_KEY_PATH );
                    break;
                case TSS2_FAPI_RC_PATH_ALREADY_EXISTS:
                    printf( "Key already exists\n" );
                    break;
                default:
                    printf( "Error creating TPM key for PowerPass Locker\n" );
                    break;
            }
        } else {
            printf( "Error provisioning Feature API instance: %d\n", res );
        }
        Fapi_Finalize( &context );
    } else {
        printf( "Error initializing FAPI context\n" );
    }

    // Check return code
    if( res != TSS2_RC_SUCCESS ) {
        const char* decoded = Tss2_RC_Decode( res );
        printf( "Decoded error message: %s\n", decoded );
        return 1;
    } else {
        return 0;
    }
}

When I run my console application through the shell, I get the following output and do not understand what I am doing wrong, or if this is an issue with the Feature API, or if this is a limitation of the physical TPM on the PC where this code is running.

login@server:~/powerpass/tpm$ ./powerpasstpm init
WARNING:esys:src/tss2-esys/api/Esys_CreatePrimary.c:401:Esys_CreatePrimary_Finish() Received TPM Error
ERROR:fapi:src/tss2-fapi/fapi_util.c:456:ifapi_set_auth() Authorization callback not defined. ErrorCode (0x0006002a)
ERROR:fapi:src/tss2-fapi/fapi_util.c:768:ifapi_init_primary_finish() ErrorCode (0x0006002a) CreatePrimary
ERROR:fapi:src/tss2-fapi/api/Fapi_Provision.c:655:Fapi_Provision_Finish() Init primary finish ErrorCode (0x0006002a)
ERROR:fapi:src/tss2-fapi/api/Fapi_Provision.c:177:Fapi_Provision() ErrorCode (0x0006002a) Provision
Error provisioning Feature API instance: 393258
Decoded error message: fapi:Authorization is unknown
login@server:~/powerpass/tpm$

When my code calls Fapi_Provision, the ESYS API reports a TPM error, but does not state what the error was. From there you can see that an authorization callback is not defined, implying a NULL pointer to a callback function that is not being initialized. I decode the TSS2_RC return code from Fapi_Provision which maps to the Authorization is unknown error message, but it is unclear how to resolve this. I am hoping this is a simple oversight or programming error.

I am using the latest version of tpm2-tss from Github, I pulled down the latest build and compiled my code against the latest build of tpm2-tss to ensure it was not a bug already fixed.

The full code for PowerPass is located on Github here in the tpm directory of the PowerPass project.

Fapi provisioning assumes that there is no auth value set for the hierarchies and tries to call the authorization callback if BAD_AUTH is returned for create primary. It looks as if an auth value is required for the endorsement hierarchy or the storage hierarchy.
The following simplified example of the required callback (call: Fapi_SetAuthCB(fapi_context, auth_callback, NULL);) can be used to determine for which hierarchy the auth value is required:

#define MAX_PASSWORD_LENGTH 100
char password[MAX_PASSWORD_LENGTH];

TSS2_RC auth_callback(
    char const *objectPath,
    char const *description,
    const char **auth,
    void *userData)
{
    if (!objectPath) {
        return TSS2_FAPI_RC_BAD_VALUE;
    }
    printf("Enter your password for: %s\n", objectPath);

    fgets(password, sizeof(password), stdin);
     for (int i = 0; i < MAX_PASSWORD_LENGTH; i++) {
        if (password[i] == '\n') {
            password[i] = '\0';
            break;
        }
    }
    *auth = password;
    return TSS2_RC_SUCCESS;
}

@JuergenReppSIT thank you very much, this is extremely helpful in understanding how to properly use the FAPI. As I was pondering what I was doing wrong I was thinking to myself "There must be some mechanism for protecting access to a generated key" and here it is. Thank you for helping me understand this better.

@JuergenReppSIT I changed the key path to /srk/shwatech-test-key for testing. I'm discovering the Endorsement Hierarchy is requiring authorization. I generated random passwords for testing purposes. When the auth callback is invoked in my code, I'm assigning *auth to the random password. What happens next is that provision fails with error code 2466. I dumped everything out to the shell and here's what the execution looks like.

powerpasstpm: calling Fapi_Initialize
powerpasstpm: calling Fapi_SetAuthCB
powerpasstpm: calling Fapi_Provision
WARNING:esys:src/tss2-esys/api/Esys_CreatePrimary.c:401:Esys_CreatePrimary_Finish() Received TPM Error
powerpasstpm: Auth callback invoked for /HE
powerpasstpm: Auth callback description: Endorsement Hierarchy
WARNING:esys:src/tss2-esys/api/Esys_CreatePrimary.c:401:Esys_CreatePrimary_Finish() Received TPM Error
ERROR:fapi:src/tss2-fapi/fapi_util.c:789:ifapi_init_primary_finish() ErrorCode (0x000009a2) FAPI Provision
ERROR:fapi:src/tss2-fapi/api/Fapi_Provision.c:655:Fapi_Provision_Finish() Init primary finish ErrorCode (0x000009a2)
ERROR:fapi:src/tss2-fapi/api/Fapi_Provision.c:177:Fapi_Provision() ErrorCode (0x000009a2) Provision
powerpasstpm: Error provisioning Feature API instance: 2466
powerpasstpm: Decoded error message: tpm:session(1):authorization failure without DA implications

I've tried searching for causes behind the authorization failure without DA implications to understand what I'm doing wrong, but not having any luck.

Is it possible some other piece of software provisioned the FAPI with an Endorsement Hierarchy authorization value that I wouldn't have access to? Here is what my auth callback looks like.

TSS2_RC pptpm_provision_authcallback(
    const char* objectPath,
    const char* description,
    const char** auth,
    void* userData ) {
    if( !objectPath ) {
        printf( "powerpasstpm: authcallback has no objectPath\n" );
        return TSS2_FAPI_RC_BAD_VALUE;
    } else {
        printf( "powerpasstpm: Auth callback invoked for %s\n", objectPath );
        if( description ) {
            printf( "powerpasstpm: Auth callback description: %s\n", description );
        }
        *auth = __POWERPASS_AUTH_SECRET;
        return TSS2_RC_SUCCESS;
    }
}

The __POWERPASS_AUTH_SECRET is a randomly generated const char array with some random numbers and letters.

@chopinrlz There must be a difference between the string __POWERPASS_AUTH_SECRET and the auth value used for the endorsement hierarchy. How did you set the auth value for the endorsement hierarchy?

@JuergenReppSIT I've never deliberately set the auth value for the endorsement hierarchy before. This is the first time running Fapi_Provision on this particular PC as this particular user. When I invoke Fapi_Provision I pass NULL in for both the authValueEh and authValueSh arguments. Should I be using Fapi_ChangeAuth if I'm using the FAPI for the first time?

If you have installed the tpm tools you could check whether an auth value exists for the hierarchies with the command
tpm2_getcap properties-variable (TPM2_PT_PERMANENT). If the auth value for the lockout hierarchy is set, you have to be careful. Your pptpm_provision_authcallback must also handle this case when the provisioning is executed.

@JuergenReppSIT so here is what I have from the tpm2_getcap output:

TPM2_PT_PERMANENT:
  ownerAuthSet:              1
  endorsementAuthSet:        1
  lockoutAuthSet:            1
  reserved1:                 0
  disableClear:              1
  inLockout:                 0
  tpmGeneratedEPS:           0
  reserved2:                 0

Is it possible that during my testing I could have accidentally set the owner/endorsement/lockout auth values? I'm sort of at a loss as to where to go from here if I do not have these auth values.

Does the FAPI have something like a "big red button" -- do not press unless you want to blow away everything and start over? Granted anything signed/encrypted by said keys would become useless at that point.

@chopinrlz Yes perhaps you have set these auth values accidentally. Unfortunately also disableClear is set. So you can't execute the command tpm2_clear to reset the auth values. To clear this flag you could try tpm2_clearcontrol -C p c. But you would need the auth value of the platform hierarchy (-P). If this does not work perhaps you could try to reset the TPM in the BIOS.

Thanks @JuergenReppSIT for all the help. I must have set the values accidentally in the past and no longer have them handy. This has been a real learning experience for me and thank you for the help. I'm going to test this on a VM next rather than my actual server.