tpm2-software / tpm2-tss

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

Trying to load a primary key into a persistent hand in TPM, but get an error #2363

Closed danielwangksu closed 2 years ago

danielwangksu commented 2 years ago

Hi all, I encountered an error using the EAPI on Windows 10. My simple goal is to create a primary key and load it into TPM at a fixed location - 0x81000001 and then create a keypair under the primary and leave the handle in a folder.

Here is my setup for TPM using PowerShell (reference https://docs.microsoft.com/en-us/powershell/module/trustedplatformmodule/?view=windowsserver2022-ps):

Clear-Tpm

ConvertTo-TpmOwnerAuth -Passphrase "ownerauth"

Import-TpmOwnerAuth -OwnerAuthorization "5AtOuyKBA/UsW1xrqAwjxfSA1nM="

Initialize-Tpm

After this the PowerShelel shows TPM is ready:

PS C:\WINDOWS\system32> Get-Tpm

TpmPresent                : True
TpmReady                  : True
TpmEnabled                : True
TpmActivated              : True
TpmOwned                  : True
RestartPending            : False
ManufacturerId            : 1398033696
ManufacturerIdTxt         : STM
ManufacturerVersion       : 1.258.0.0
ManufacturerVersionFull20 : 1.258.0.0

ManagedAuthLevel          : Full
OwnerAuth                 : 5AtOuyKBA/UsW1xrqAwjxfSA1nM=
OwnerClearDisabled        : False
AutoProvisioning          : Enabled
LockedOut                 : False
LockoutHealTime           : 2 hours
LockoutCount              : 0
LockoutMax                : 32
SelfTest                  : {}

In my C code, it is simply trying to create a persistent primary

// I called initialization first
Esys_Initialize(&ectx, NULL, NULL);

// then, not sure if this is the right way to use it.
TPM2B_AUTH hieararchy_auth = { 0 };
Esys_TR_SetAuth(ectx, ESYS_TR_RH_OWNER, &hieararchy_auth );

// then create primary, this call did not fail
Esys_CreatePrimary(ectx, ESYS_TR_RH_OWNER, ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE, ..., &handle, ...);

// but this failed
Esys_EvictControl(ectx,
            ESYS_TR_RH_OWNER,
            handle,
            ESYS_TR_PASSWORD,
            ESYS_TR_NONE,
            ESYS_TR_NONE,
            0x81000001,
            &new_handle);

This is the error I got:

ERROR:esys:api\Esys_EvictControl.c:334:Esys_EvictControl_Finish() Received a non-TPM Error
ERROR:esys:api\Esys_EvictControl.c:114:Esys_EvictControl() Esys Finish ErrorCode (0x80280400)

Esys_EvictControl: 40:0x400:

I guess I'm confused when and how to pass the OWNER password "ownerauth". Any help is greatly appreciated! Thank you!

williamcroberts commented 2 years ago

The call below teaches ESYS, "hey, when you need the owner auth use this".

Esys_TR_SetAuth(ectx, ESYS_TR_RH_OWNER, &hieararchy_auth );

But here you never set the password:

TPM2B_AUTH hieararchy_auth = { 0 };

The struct has a size and buffer field. You need to set buffer to the password and update the size field.

    const char password[] = "5AtOuyKBA/UsW1xrqAwjxfSA1nM=";
    TPM2B_AUTH hieararchy_auth = {
         .size = sizeof(password) - sizeof(char)
    };
    memcpy(hieararchy_auth.buffer, password, hieararchy_auth.size);

I usually roll that into a setpassword function since it gets used a lot, here is a sample:

static TSS2_RC set_password(ESYS_CONTEXT *ectx, ESYS_TR handle, const char *password)
{
    TPM2B_AUTH auth = { 0 };

    if (strlen(password) > sizeof(auth.buffer)) {
        // todo make better error
        return 1;
    }

    auth.size = strlen(password);
    memcpy(auth.buffer, password, auth.size);

    return Esys_TR_SetAuth(ectx, handle, &auth);
}

int main(int argc, char *argv[]) {

    ESYS_CONTEXT *ectx = NULL;
    Esys_Initialize(&ectx, NULL, NULL);
    return set_password(ectx, ESYS_TR_RH_OWNER, "5AtOuyKBA/UsW1xrqAwjxfSA1nM=");
}
danielwangksu commented 2 years ago

Thank you so much @williamcroberts this saved my day! I was down in the robbit hole trying to explore Esys_StartAuthSession and Esys_TRSess_SetAttributes to somehow pass the password!

danielwangksu commented 2 years ago

Sorry, maybe I missed anything. I tested the above solution works on Linux but on Windows I got the following error:

WARNING:esys:api\Esys_CreatePrimary.c:400:Esys_CreatePrimary_Finish() Received TPM Error
ERROR:esys:api\Esys_CreatePrimary.c:135:Esys_CreatePrimary() Esys Finish ErrorCode (0x000000a2)

Esys_CreatePrimary: tpm:handle(unk):authorization failure without DA implications:

Any further suggestions?

idesai commented 2 years ago

Sorry, maybe I missed anything. I tested the above solution works on Linux but on Windows I got the following error:

WARNING:esys:api\Esys_CreatePrimary.c:400:Esys_CreatePrimary_Finish() Received TPM Error
ERROR:esys:api\Esys_CreatePrimary.c:135:Esys_CreatePrimary() Esys Finish ErrorCode (0x000000a2)

Esys_CreatePrimary: tpm:handle(unk):authorization failure without DA implications:

Any further suggestions? I think Windows sets the ownership password and so you may not be keying in the right password.

williamcroberts commented 2 years ago

Perhaps the value that is getting stored into the registry isn't being setup in provision tpm call. Maybe try testing that the owner auth you're requesting was actually set to the TPM:

Set-TpmOwnerAuth -OwnerAuthorization "5AtOuyKBA/UsW1xrqAwjxfSA1nM=" -NewOwnerAuthorization "5AtOuyKBA/UsW1xrqAwjxfSA1nM="

See:

danielwangksu commented 2 years ago

Thank you for all your help! It still has the same error. Here is what I did, and the return value (only showing the related fields):

Clear-Tpm

TpmPresent                : True
TpmReady                  : False
...
OwnerAuth                 :
Import-TpmOwnerAuth -OwnerAuthorization "5AtOuyKBA/UsW1xrqAwjxfSA1nM="

TpmPresent                : True
TpmReady                  : True
...
OwnerAuth                 : 5AtOuyKBA/UsW1xrqAwjxfSA1nM=
Set-TpmOwnerAuth -NewOwnerAuthorization "5AtOuyKBA/UsW1xrqAwjxfSA1nM=" 

TpmPresent                : True
TpmReady                  : True
...
OwnerAuth                 : 

I'm not sure why Import-TpmOwnerAuth turns TpmReady to True, and makes OwnerAuth shows the value, but Set-TpmOwnerAuth removes the OwnerAuth value. But either way I'm using the same password value.

I also tried to run Set-TpmOwnerAuth -NewOwnerAuthorization "5AtOuyKBA/UsW1xrqAwjxfSA1nM=" the second time, but this time I got an error. After the first time, I had to use this command Set-TpmOwnerAuth -OwnerAuthorization "5AtOuyKBA/UsW1xrqAwjxfSA1nM=" -NewOwnerAuthorization "5AtOuyKBA/UsW1xrqAwjxfSA1nM=". So I think the password should be set at this point, but the C program keep getting "tpm:handle(unk):authorization failure without DA implications"

danielwangksu commented 2 years ago

Come back to report what I found. I thought maybe Windows 10 has some special session for taking ownership. So I cleaned TPM using PowerShell.

PS C:\WINDOWS\system32> Get-Tpm

TpmPresent                : True
TpmReady                  : False
TpmEnabled                : True
TpmActivated              : True
TpmOwned                  : False
RestartPending            : False
ManufacturerId            : 1398033696
ManufacturerIdTxt         : STM
ManufacturerVersion       : 1.258.0.0
ManufacturerVersionFull20 : 1.258.0.0

ManagedAuthLevel          : Delegated
OwnerAuth                 :
OwnerClearDisabled        : False
AutoProvisioning          : Enabled
LockedOut                 : False
LockoutHealTime           : 2 hours
LockoutCount              : 0
LockoutMax                : 32
SelfTest                  : {}

In the C program I called the following functions:

ESYS_TR hierarchy = ESYS_TR_RH_OWNER;
TPM2B_AUTH hieararchy_auth = { 0 };
Esys_TR_SetAuth(ectx, hierarchy, &hieararchy_auth);

// take the ownership and setup an password I chose
Esys_HierarchyChangeAuth(ectx, hierarchy, ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE, new_auth);

TPM2B_PUBLIC pub_template = {
        .size = 0,
        .publicArea = {
            .type = TPM2_ALG_ECC,
            .nameAlg = TPM2_ALG_SHA256,
            .objectAttributes = (TPMA_OBJECT_USERWITHAUTH |
                                 TPMA_OBJECT_RESTRICTED |
                                 TPMA_OBJECT_DECRYPT |
                                 TPMA_OBJECT_FIXEDTPM |
                                 TPMA_OBJECT_FIXEDPARENT |
                                 TPMA_OBJECT_SENSITIVEDATAORIGIN),
            .authPolicy = {
                 .size = 0,
             },
            .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,
                      .details = {}}
             },
            .unique.ecc = {
                 .x = {.size = 0,.buffer = {}},
                 .y = {.size = 0,.buffer = {}},
             },
        },
    };
    TPM2B_DATA outside_info = { 0 };
    TPML_PCR_SELECTION pcrs = { 0 };
    TPM2B_PUBLIC *out_pub = NULL;
    TPM2B_CREATION_DATA *data = NULL;
    TPM2B_DIGEST *hash = NULL;
    TPMT_TK_CREATION *ticket = NULL;

// authorize the handle with the created password
Esys_TR_SetAuth(...);
Esys_CreatePrimary(ectx,
            hierarchy,
            ESYS_TR_PASSWORD,
            ESYS_TR_NONE,
            ESYS_TR_NONE,
            &sens,
            &pub_template,
            &outside_info,
            &pcrs,
            &handle,
            &out_pub,
            &data,
            &hash,
            &ticket);

Esys_EvictControl(ectx,
            hierarchy,
            handle,
            ESYS_TR_PASSWORD,
            ESYS_TR_NONE,
            ESYS_TR_NONE,
            DEFAULT_SRK_HANDLE,
            &new_handle);

This works on Linux with virtual TPM, but failed again on Windows, even there it shows TpmOwned: False. ERROR:esys:api\Esys_HierarchyChangeAuth.c:313:Esys_HierarchyChangeAuth_Finish() Received a non-TPM Error ERROR:esys:api\Esys_HierarchyChangeAuth.c:114:Esys_HierarchyChangeAuth() Esys Finish ErrorCode (0x80280400)

Not sure why, maybe cooperate IT has special locks (I do not think so)? I guess Windows really do not want me to use TPM LOL.

danielwangksu commented 2 years ago

Tried nothing but Esys_Clear(ectx, ESYS_TR_RH_LOCKOUT, ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE); still get the same error:

ERROR:esys:api\Esys_Clear.c:295:Esys_Clear_Finish() Received a non-TPM Error
ERROR:esys:api\Esys_Clear.c:97:Esys_Clear() Esys Finish ErrorCode (0x80280400)

Error: Esys_Clear: 40:0x400:
williamcroberts commented 2 years ago

Those are some weird error codes.... WTH is layer 40.

williamcroberts commented 2 years ago

I would try the MS TSS (https://github.com/microsoft/TSS.MSR), just see if it bahves differently, then maybe we can figure out where the bug is. It would narrow it down to TPM or TSS.

danielwangksu commented 2 years ago

Thank you @williamcroberts I tried TSS.MSR's TSS.CPP Samples.exe before. "TSS.CPP Samples.exe" sys gives me error:

TpmCppTester: Wrong response session tag
Exiting...

I will write a simply cpp to see what error got reported. But I'm not hopeful :(

danielwangksu commented 2 years ago

I think Windows just does not allow us to use owner password. With tpm2-tss I was able to create primary key without specific auth value, it just when I try to make it persistent using EvictControl, it fails. The behavior is same using TSS.MSR, I can create primary key, but as soon as it reach EvictControl just abort. Same for calling tpm2-clear(). I'm trying to see if I can load primary key, and create signing key without auth on Windows.

williamcroberts commented 2 years ago

I think Windows just does not allow us to use owner password. With tpm2-tss I was able to create primary key without specific auth value, it just when I try to make it persistent using EvictControl, it fails. The behavior is same using TSS.MSR, I can create primary key, but as soon as it reach EvictControl just abort. Same for calling tpm2-clear(). I'm trying to see if I can load primary key, and create signing key without auth on Windows.

Their resource manager, TBS, implements some command blocking, I wonder if you need to tweak that policy

danielwangksu commented 2 years ago

Hi @williamcroberts indeed! Thank you for your response. Here is what I found through experiments on Windows 10. If I set TPM2B_AUTH to all 0, I can create primary key, create/load signing key, etc. The only things you cannot do is Esys_Clear and Esys_EvictControl. This made me believe Windows has some kind of policies that control in TBS. I cannot make the primary key in persistent, but I guess I can live with this issue, as long as I can recreate primary key every reboot.

I understand in TPM as long as the parameters of creating primary keys are identical, the key would be identical. So my question is what exactly are those parameters? For example, if I use the following setup, can I assume the primary key is same every time the code is executed?

TPM2B_PUBLIC pub_template = {
        .size = 0,
        .publicArea = {
            .type = TPM2_ALG_RSA,
            .nameAlg = TPM2_ALG_SHA256,
            .objectAttributes = (TPMA_OBJECT_FIXEDTPM |
                                 TPMA_OBJECT_FIXEDPARENT |
                                 TPMA_OBJECT_SENSITIVEDATAORIGIN |
                                 TPMA_OBJECT_USERWITHAUTH |
                                 TPMA_OBJECT_RESTRICTED |
                                 TPMA_OBJECT_NODA |
                                 TPMA_OBJECT_DECRYPT),
            .authPolicy = {
                .size = 0,
            },
            .parameters.rsaDetail = {
                .symmetric = {
                    .algorithm = TPM2_ALG_AES,
                    .keyBits.aes = 128,
                    .mode.aes = TPM2_ALG_CFB,
                },
                .scheme = {
                    .scheme = TPM2_ALG_NULL,
                    .details = {}
                },
                .keyBits = 2048,
                .exponent = 0,
            },
        },
    };

...
Esys_CreatePrimary(...);
williamcroberts commented 2 years ago

Hi @williamcroberts indeed! Thank you for your response. Here is what I found through experiments on Windows 10. If I set TPM2B_AUTH to all 0, I can create primary key, create/load signing key, etc. The only things you cannot do is Esys_Clear and Esys_EvictControl. This made me believe Windows has some kind of policies that control in TBS. I cannot make the primary key in persistent, but I guess I can live with this issue, as long as I can recreate primary key every reboot.

I understand in TPM as long as the parameters of creating primary keys are identical, the key would be identical. So my question is what exactly are those parameters? For example, if I use the following setup, can I assume the primary key is same every time the code is executed?

Yep, exactly right.

danielwangksu commented 2 years ago

Thank you @williamcroberts for your help! I'm closing this issues.