tpm2-software / tpm2-pytss

Python bindings for TSS
https://tpm2-pytss.readthedocs.io/en/latest/
BSD 2-Clause "Simplified" License
65 stars 45 forks source link

Read endorsement key with the usage of tpm2-pytss #350

Closed Dvergatal closed 2 years ago

Dvergatal commented 2 years ago

Hi all, just like in the subject I wanted to ask you guys is it possible to read/retrieve the endorsement key - public part - from the TPM with this API?

Moreover, if we are in the subject of endorsement key I have another question to you, because I am currently struggling with Microsoft Azure and its tpm_device_provision binary which is used for DPS provisioning (it retrieves Registration id and Endorsement Key). On Microsoft Azure documentation sites it is written, that this binary retrieves public part of this RSA TPM key, but when i wanted to decode this base64 with the usage of openssl, it says, that this is not a public key.

In our tests we are using SWTPM software TPM implementation on qemu machine and according to the SWTPM documentation it creates it's own ek_key during TPM creation. Question is, if this can be rubbish or does it have some completely different format? I can also paste you here this TPM ek_pub:

Endorsement Key:
AToAAQALAAMAsgAgg3GXZ0SEs/gakMyNRqXXJP1S124GUgtk8qHaGzMUaaoABgCAAEMAEAgAAAAAAAEAq4+zaAAAv0nsuYwCm+XzzdldDYDWR+565eLh9E2PUe9x9oEn+9xsGS5z2mzj=

Moreover I have observed, that it starts completely differently, than any other RSA public key created by openssl.

Dvergatal commented 2 years ago

@whooo OK I have done one more test, which was to confirm my reasoning, meaning I have created EK with the usage of this command:

tpm2_createek -c 0x81010001 -G rsa -u ek.pub

than I have used tpm_device_provision just to generate SRK - it will not regenerate EK because it has been already generated by the tpm2_createek and I have confirmed that in my earlier posts, besides tpm_device_provision returns this new value. With this additional step of SRK creation, I was now able to establish a connection with Azure cloud :) So I am wondering now how I can do this (make EK persistent, generate SRK and make it also persistent) with pytss?

* Make EK Persistent - use ESAPI.EvictControl

  * You can also create it with ESAPI.CreatePrimary with the proper template or use the helper create_ek_template.
  * If you use create_ex_template from utils, I think the proper template is "EK-RSA2048" and you can set the Callable to class NVReadEK or even your own class that on call just returns NoSuchIndex exception but it will be less portable on the off chance the TPM defines a template at an NV index.

OK, whit this I have succeeded, the EK is persistent.

* Generate SRK - Use ESAPI.CreatePrimary with proper template

  * I Wonder if we should add a bunch of constants available for users here.
  * The SRK spec is here in section https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-v2.0-Provisioning-Guidance-Published-v1r1.pdf section 7.5.1 which references the EK Spec https://trustedcomputinggroup.org/wp-content/uploads/TCG_IWG_Credential_Profile_EK_V2.1_R13.pdf. TL;DR the SRK is the EK with the policy zeroed out and a slightly different attribute set. **NOTE** Their is some lore here, the TCG specification for the EK sets the unique field to keysize bytes of 0. So for an RSA 2048 key, the TCG SRK has a unique of 256 bytes of 0.

For this one, I already have given here a template which is written in C, the problem is that I dunno how can I rewrite this template into python.

whooo commented 2 years ago

You have the SRK template in GetSrkTemplate and just have to check the correct parameters as well, so create a TPM2B_PUBLIC instance and use that with create_primary

Dvergatal commented 2 years ago

You have the SRK template in GetSrkTemplate and just have to check the correct parameters as well, so create a TPM2B_PUBLIC instance and use that with create_primary

@whooo that is what I have written:P but GetSrkTemplate() is in C code and I dunno how it should be rewritten in python code.

whooo commented 2 years ago

Look at https://github.com/tpm2-software/tpm2-pytss/blob/8ebc054af8e4e6c5fdadfbe5fbfd7876a98483fa/test/test_esapi.py#L28 for an example, it's the same field names as the C code from azure

Dvergatal commented 2 years ago

@whooo thx

Dvergatal commented 2 years ago

@whooo one more thing, from what I see in this example it slightly differs due to usage of ecc and in azure they are using rsa. So there are also this parameters an in case of example it looks like that:

        inPublic.publicArea.parameters.eccDetail.scheme.scheme = TPM2_ALG.ECDSA
        inPublic.publicArea.parameters.eccDetail.scheme.details.ecdsa.hashAlg = (
            TPM2_ALG.SHA256
        )
        inPublic.publicArea.parameters.eccDetail.symmetric.algorithm = TPM2_ALG.NULL
        inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM2_ALG.NULL
        inPublic.publicArea.parameters.eccDetail.curveID = TPM2_ECC.NIST_P256

and in case of Azure:

static TPMS_RSA_PARMS  RsaStorageParams = {
    { TPM_ALG_AES, 128, TPM_ALG_CFB },      // TPMT_SYM_DEF_OBJECT  symmetric
    { TPM_ALG_NULL },                       // TPMT_RSA_SCHEME      scheme
    2048,                                   // TPMI_RSA_KEY_BITS    keyBits
    0                                       // UINT32               exponent
};

How should it inPublic.publicArea.parameters.eccDetail be changed? Meaning eccDetail.

whooo commented 2 years ago

Look at TPMT_PUBLIC in tss2_tpm2_types.h to get a picture of the structure

Dvergatal commented 2 years ago

Look at TPMT_PUBLIC in tss2_tpm2_types.h to get a picture of the structure

I have just found it and it is all written down there:D thx

Dvergatal commented 2 years ago

@whooo I'm not an expert in python rather C/C++, but I wonder if it is possible to write it like in here the assignments instead of passing all these:

        inPublic = TPM2B_PUBLIC()
        inPublic.publicArea.type = TPM2_ALG.ECC
        inPublic.publicArea.nameAlg = TPM2_ALG.SHA1
        inPublic.publicArea.objectAttributes = (
            TPMA_OBJECT.USERWITHAUTH
            | TPMA_OBJECT.SIGN_ENCRYPT
            | TPMA_OBJECT.RESTRICTED
            | TPMA_OBJECT.FIXEDTPM
            | TPMA_OBJECT.FIXEDPARENT
            | TPMA_OBJECT.SENSITIVEDATAORIGIN
        )

        inPublic.publicArea.parameters.eccDetail.scheme.scheme = TPM2_ALG.ECDSA
        inPublic.publicArea.parameters.eccDetail.scheme.details.ecdsa.hashAlg = (
            TPM2_ALG.SHA256
        )
        inPublic.publicArea.parameters.eccDetail.symmetric.algorithm = TPM2_ALG.NULL
        inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM2_ALG.NULL
        inPublic.publicArea.parameters.eccDetail.curveID = TPM2_ECC.NIST_P256

just to write it like that:

rsa_template = TPMT_PUBLIC(
    type=TPM2_ALG.RSA,
    nameAlg=TPM2_ALG.SHA256,
    objectAttributes=TPMA_OBJECT.USERWITHAUTH
    | TPMA_OBJECT.RESTRICTED
    | TPMA_OBJECT.DECRYPT
    | TPMA_OBJECT.NODA
    | TPMA_OBJECT.FIXEDTPM
    | TPMA_OBJECT.FIXEDPARENT
    | TPMA_OBJECT.SENSITIVEDATAORIGIN,
    authPolicy=b"",
    parameters=TPMU_PUBLIC_PARMS(
        rsaDetail=TPMS_RSA_PARMS(
            symmetric=TPMT_SYM_DEF_OBJECT(
                algorithm=TPM2_ALG.AES,
                keyBits=TPMU_SYM_KEY_BITS(aes=128),
                mode=TPMU_SYM_MODE(aes=TPM2_ALG.CFB),
            ),
            scheme=TPMT_RSA_SCHEME(scheme=TPM2_ALG.NULL),
            keyBits=2048,
            exponent=0,
        ),
    ),
)

I'm just curious if TPM2B_PUBLIC() is being returned by TPMT_PUBLIC().

P.S. Do not care about the difference in case of type of asymmetric encryption, I've just copied the code to show my point of view.

whooo commented 2 years ago

It should work, but you won't know until you try. To get a TPM2B_PUBLIC from rsa_template just do TPM2B_PUBLIC(publicArea=rsa_template)

Dvergatal commented 2 years ago

Ok and what about these parameters TPM2B_DIGEST and TPMU_PUBLIC_ID which are set in template as:

        { 0 },                      // TPMA_OBJECT  objectAttributes (set below)
        { 0 },                      // TPM2B_DIGEST         authPolicy
        { 0 },                      // TPMU_PUBLIC_PARMS    parameters (set before use)
        { 0 }                       // TPMU_PUBLIC_ID       unique

Does authPolicy=b"" is equivalent with { 0 }, // TPM2B_DIGEST authPolicy? I do not see any TPMU_PUBLIC_ID field in the documentation.

whooo commented 2 years ago

yes, authPolicy=b"" means an empty policy (zero size), you don't need to set it as it's empty by default (cffi zeroes all allocations by default). The fields are to the right, so the field name is unique, not TPMU_PUBLIC_ID and I see it in tss2_tpm2_types.h You shouldn't need to set the unique field unless you want to add some nonce to the key generation

Dvergatal commented 2 years ago

yes, authPolicy=b"" means an empty policy (zero size), you don't need to set it as it's empty by default (cffi zeroes all allocations by default).

Ah ok so i can remove this field in my code.

The fields are to the right, so the field name is unique, not TPMU_PUBLIC_ID and I see it in tss2_tpm2_types.h You shouldn't need to set the unique field unless you want to add some nonce to the key generation

Hmmm, ok so the EK may differ between Azure and tpm2-tools only due to this nonce given to the key generation, but as you could see, the template for EK doesn't give any unique id to it. What should I look for?

whooo commented 2 years ago

The nonce part was mostly a guess/assumption from my side as the cause, but if it's not set by the azure stuff I was wrong

Dvergatal commented 2 years ago

Nope it's not, I'm reading this Azure code and even all variables like TPML_PCR_SELECTION are the same. This is their function for creating primary:

TPM_RC TSS_CreatePrimary(TSS_DEVICE *tpm, TSS_SESSION *sess,
    TPM_HANDLE hierarchy, TPM2B_PUBLIC *inPub,
    TPM_HANDLE *outHandle, TPM2B_PUBLIC *outPub)
{
    TPM2B_SENSITIVE_CREATE  sensCreate = { 0 };
    TPM2B_DATA              outsideInfo = { {0} };
    TPML_PCR_SELECTION      creationPCR = { 0 };

    return TPM2_CreatePrimary(tpm, sess, hierarchy, &sensCreate,
        inPub, &outsideInfo, &creationPCR,
        outHandle, outPub, NULL, NULL, NULL);
}

Arrr, this Azure code is really ugly. I think I need to ping them, because as you can see this sensCreate is equal {0} and I see further that it is being marshaled with some stupid define TSS_MARSHAL(TPM2B_SENSITIVE_CREATE, inSensitive); which looks like that:

#define TSS_MARSHAL(Type, pValue) \
{                                                                           \
    TSS_CHECK_PTR(pValue)                                                   \
    cmdCtx->ParamSize += Type##_Marshal(pValue, &paramBuf, &sizeParamBuf);  \
}

and cmdCtx is a structure, but it's rather not important. Than this Type##_Marshal is TPM2B_SENSITIVE_CREATE_Marshal and it starts to be really unreadable...

Dvergatal commented 2 years ago

@whooo @williamcroberts First of all I'm sorry for bothering you with problem, which is not of your concerns. Together with guys which are still working there we are trying to solve this issue here.

I have also a question to you guys, because this method:

TPM_RC
TPM2_CreatePrimary(
    TSS_DEVICE *tpm,
    TSS_SESSION *session,
    TPMI_DH_OBJECT primaryHandle,
    TPM2B_SENSITIVE_CREATE *inSensitive,
    TPM2B_PUBLIC *inPublic,
    TPM2B_DATA *outsideInfo,
    TPML_PCR_SELECTION *creationPCR,
    TPM_HANDLE *objectHandle,
    TPM2B_PUBLIC *outPublic,
    TPM2B_CREATION_DATA *creationData,
    TPM2B_DIGEST *creationHash,
    TPMT_TK_CREATION *creationTicket
)
{
        TSS_CMD_CONTEXT CmdCtx;
        TPM_RC cmdResult = (TPM_RC)(0x000);
        TSS_CMD_CONTEXT *cmdCtx = &CmdCtx;
        INT32 sizeParamBuf = sizeof(cmdCtx->ParamBuffer);
        BYTE *paramBuf = cmdCtx->ParamBuffer;
        (void)sizeParamBuf;
        (void)paramBuf;
        cmdCtx->ParamSize = 0;
        {
                void* p = inSensitive;
                (void)p;
        }
        cmdCtx->ParamSize += TPM2B_SENSITIVE_CREATE_Marshal(inSensitive, &paramBuf, &sizeParamBuf);
        {
                void* p = inPublic;
                (void)p;
        }
        cmdCtx->ParamSize += TPM2B_PUBLIC_Marshal(inPublic, &paramBuf, &sizeParamBuf);
        {
                void* p = outsideInfo;
                (void)p;
        }
        cmdCtx->ParamSize += TPM2B_DATA_Marshal(outsideInfo, &paramBuf, &sizeParamBuf);
        {
                void* p = creationPCR;
                (void)p;
        }
        cmdCtx->ParamSize += TPML_PCR_SELECTION_Marshal(creationPCR, &paramBuf, &sizeParamBuf);
        cmdResult = TSS_DispatchCmd(tpm, (TPM_CC)(0x00000131), &primaryHandle, 1, &session, 1, cmdCtx);
        if (cmdResult != (TPM_RC)(0x000)) return cmdResult;;
        *objectHandle = cmdCtx->RetHandle;
        {
                if ( TPM2B_PUBLIC_Unmarshal(outPublic, &cmdCtx->RespBufPtr, (INT32*)&cmdCtx->RespBytesLeft, 1) != (TPM_RC)(0x000)) return (TPM_RC)((TPM_RC)(0x080)+0x01A);
        };
        if (!(creationData)) {
                TPM2B_CREATION_DATA val;
                {
                        if ( TPM2B_CREATION_DATA_Unmarshal(&val, &cmdCtx->RespBufPtr, (INT32*)&cmdCtx->RespBytesLeft) != (TPM_RC)(0x000)) return (TPM_RC)((TPM_RC)(0x080)+0x01A);
                };
        }
        else {
                if ( TPM2B_CREATION_DATA_Unmarshal(creationData, &cmdCtx->RespBufPtr, (INT32*)&cmdCtx->RespBytesLeft) != (TPM_RC)(0x000)) return (TPM_RC)((TPM_RC)(0x080)+0x01A);
        };      
        if (!(creationHash)) {
                TPM2B_DIGEST val;
                {
                        if ( TPM2B_DIGEST_Unmarshal(&val, &cmdCtx->RespBufPtr, (INT32*)&cmdCtx->RespBytesLeft) != (TPM_RC)(0x000)) return (TPM_RC)((TPM_RC)(0x080)+0x01A);
                };
        }
        else {
                if ( TPM2B_DIGEST_Unmarshal(creationHash, &cmdCtx->RespBufPtr, (INT32*)&cmdCtx->RespBytesLeft) != (TPM_RC)(0x000)) return (TPM_RC)((TPM_RC)(0x080)+0x01A);
        };      
        if (!(creationTicket)) {
                TPMT_TK_CREATION val;
                {
                        if ( TPMT_TK_CREATION_Unmarshal(&val, &cmdCtx->RespBufPtr, (INT32*)&cmdCtx->RespBytesLeft) != (TPM_RC)(0x000)) return (TPM_RC)((TPM_RC)(0x080)+0x01A);
                };
        }
        else {
                if ( TPMT_TK_CREATION_Unmarshal(creationTicket, &cmdCtx->RespBufPtr, (INT32*)&cmdCtx->RespBytesLeft) != (TPM_RC)(0x000)) return (TPM_RC)((TPM_RC)(0x080)+0x01A);
        };      
        return cmdResult;
}

as the name suggests creates the primary and now I have feeling that PCR used in it is wrong. Can you look at it and maybe you may have some thoughts?

williamcroberts commented 2 years ago

@Dvergatal i'll try and look at this, I am recovering from surgery so please be patient. Perhaps @idesai can look at it and spot why their is a delta?

Dvergatal commented 2 years ago

@Dvergatal i'll try and look at this, I am recovering from surgery so please be patient. Perhaps @idesai can look at it and spot why their is a delta?

@williamcroberts it's OK. I fully understand you, as I am supposed to have surgery myself soon. I was also thinking about this delta and I do not fully understand what is this delta in the code. I suppose, as mentioned, that we would really need help from @idesai.

Dvergatal commented 2 years ago

@williamcroberts and others I have pasted you in my previous post the link to issue on Azure github. Please join there, so that we can solve this problem together with the people from Microsoft.

Dvergatal commented 2 years ago

After we (I mean by that, you @williamcroberts :P) have finally solved this issue, I think it can be closed. Thx a lot to all involved.