Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
Dvergatal opened this issue May 5, 2022 · 70 comments
Closed

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

Dvergatal opened this issue May 5, 2022 · 70 comments

Comments

@Dvergatal
Copy link

Dvergatal commented May 5, 2022

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.

@whooo
Copy link
Contributor

whooo commented May 6, 2022

For getting the EK you can use https://tpm2-pytss.readthedocs.io/en/1.1.0/utils.html#tpm2_pytss.utils.create_ek_template
Check the four first lines of

def test_create_ek_rsa(self):
for a basic usage example

Regarding the base64 output from tpm_device_provision, I don't know what format that is, but it is to small to be a a RSA key and doesn't seem to be a key template either

@Dvergatal
Copy link
Author

Dvergatal commented May 6, 2022

@whooo thx
ok so this part:

        nv_read = NVReadEK(self.ectx)
        _, rsa_template = create_ek_template("EK-RSA2048", nv_read)
        _, rsa, _, _, _ = self.ectx.create_primary(
            TPM2B_SENSITIVE_CREATE(), rsa_template, ESYS_TR.ENDORSEMENT
        )
        self.assertEqual(rsa.publicArea.type, TPM2_ALG.RSA)

should give me this ek_pub, right?

Regarding the base64 output from tpm_device_provision, I don't know what format that is, but it is to small to be a a RSA key and doesn't seem to be a key template either

According to the documentation this should be public part of the key but still it is some how strange because common rsa public key starts completely different i.e.:

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDigN4obCtg61Knvqp9e78RQ02P
ihxs9HBwOabgj9MjxJAwtspdls7OEn4qmQvca4AyWPgUUc0rc1/Kz56dEIWsFGtg
NvIz1Q67hE3uKyRmzJfmnYQPYTy60y/2hfnG/0FcxczI4ciTsnRomPoCYi8Usfhh
lvqLXjdadfNME5cN9QIDAQAB

From what i remember it always starts with MI letters.

@whooo
Copy link
Contributor

whooo commented May 6, 2022

@whooo thx ok so this part:

        nv_read = NVReadEK(self.ectx)
        _, rsa_template = create_ek_template("EK-RSA2048", nv_read)
        _, rsa, _, _, _ = self.ectx.create_primary(
            TPM2B_SENSITIVE_CREATE(), rsa_template, ESYS_TR.ENDORSEMENT
        )
        self.assertEqual(rsa.publicArea.type, TPM2_ALG.RSA)

All but the self.assertEqual part yes

should give me this ek_pub, right?

Regarding the base64 output from tpm_device_provision, I don't know what format that is, but it is to small to be a a RSA key and doesn't seem to be a key template either

According to the documentation this should be public part of the key but still it is some how strange because common rsa public key starts completely different i.e.:

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDigN4obCtg61Knvqp9e78RQ02P
ihxs9HBwOabgj9MjxJAwtspdls7OEn4qmQvca4AyWPgUUc0rc1/Kz56dEIWsFGtg
NvIz1Q67hE3uKyRmzJfmnYQPYTy60y/2hfnG/0FcxczI4ciTsnRomPoCYi8Usfhh
lvqLXjdadfNME5cN9QIDAQAB

From what i remember it always starts with MI letters.

Public part might mean a TPM structure, not a "raw" RSA key, but regardless the example you showed is to small for a RSA key, might be a ECC key tho

@Dvergatal
Copy link
Author

@whooo big thx again.
Hmm as it is a software TPM, it might be possible, that this is in fact ECC key, more over, it is possible that this is a TPM structure. I have to check tpm_device_provision source code and verify it.

@Dvergatal
Copy link
Author

@whooo you were right the endorsment key is much longer and looks like that:

AToAAQALAAMAsgAgg3GXZ0SEs/gakMyNRqXXJP1S124GUgtk8qHaGzMUaaoABgCAAEMAEAgAAAAAAAEAq4+zaAAAv0nsuYwCm+XzzdldDYDWR+565eLh9E2PUe9x9oEn+9xsGS5z2mzj1YY6xUvMkN6pFd8gv2H0d1KBdqyruGSH8qP35QDwLI7t0jcEeeemDJ5te/A92Z+yfzLDrGqypz064VhP3Qrha/zhF5g+crZpOcnlshwsnVNh7awcqNjk0ki5QrmaBbiI9g/D9xv/gFqGXdhkGwaSTnJeBWRefmrXhtF+iGwCRffEjlG/qsxQ/hvRbeAdybwQk+cysdSk4w9jTMrsf7b8AoYBOlkrGSTy2mzk1F2ePJEr2W+gayYDVNLOa3pagnxtkoz0ER2F5BzFS+glrgl6rOjE1w==

My console was cutting it and i didn't noticed it.

@whooo
Copy link
Contributor

whooo commented May 7, 2022

I did a b64decode on the data and was able to unmarshal it as a TPM2B_PUBLIC structure

@Dvergatal
Copy link
Author

Dvergatal commented May 7, 2022

So it is indeed an endorsement public key but in tpm format:] glad to here it.

@williamcroberts
Copy link
Member

So it is indeed an endorsement public key but in tpm format:] glad to here it.

Once you have it in a TPM2B_PUBLIC you can call .to_pem on it to get a PEM representation of the public key to use in OpenSSL. For an example, look at: https://github.com/tpm2-software/tpm2-pytss/blob/master/test/test_crypto.py#L332

@Dvergatal
Copy link
Author

@williamcroberts thx, but what I want to do is to remove the usage of this microsoft bullshit program.... I was digging if, that is really the key, because I wanted to verify, what was returning this application.

@Dvergatal
Copy link
Author

@williamcroberts and is there equivalent method to get base64 like i have given as an example?

@Dvergatal
Copy link
Author

BTW. this is my code:

from tpm2_pytss import *
from tpm2_pytss.internal import *
from base64 import b64decode
from hashlib import sha256, sha384

nv_read = NVReadEK(self.ectx)
_, rsa_template = create_ek_template("EK-RSA2048", nv_read)
_, rsa, _, _, _ = self.ectx.create_primary(TPM2B_SENSITIVE_CREATE(), rsa_template, ESYS_TR.ENDORSEMENT)

and I'm getting error:

Traceback (most recent call last):
  File "endorsement_key.py", line 6, in <module>
    nv_read = NVReadEK(self.ectx)

What can cause it? Because according to https://tpm2-pytss.readthedocs.io/en/latest/utils.html NVReadEK is in utils which is in tpm2_pytss, but I'm importing from it * so this should be working.

@whooo
Copy link
Contributor

whooo commented May 12, 2022

BTW. this is my code:

from tpm2_pytss import *
from tpm2_pytss.internal import *
from base64 import b64decode
from hashlib import sha256, sha384

nv_read = NVReadEK(self.ectx)
_, rsa_template = create_ek_template("EK-RSA2048", nv_read)
_, rsa, _, _, _ = self.ectx.create_primary(TPM2B_SENSITIVE_CREATE(), rsa_template, ESYS_TR.ENDORSEMENT)

and I'm getting error:

Traceback (most recent call last):
  File "endorsement_key.py", line 6, in <module>
    nv_read = NVReadEK(self.ectx)

What can cause it? Because according to https://tpm2-pytss.readthedocs.io/en/latest/utils.html NVReadEK is in utils which is in tpm2_pytss, but I'm importing from it * so this should be working.

You shouldn't need to import anything from tpm2_pytss.internal.
the utils aren't included in the wildcard import from tpm2_pytss, so do from tpm2_pytss.utils import NVReadEK, create_ek_template
When that is working, do something like:

rb = rsa.marshal()
b64_rsa = b64encode(rb)

And then you have the base64 encoded public part

@Dvergatal
Copy link
Author

@whoo thx btw. i didn't notice that the example you have given is within a TestUtils class, which inherits from TSS2_BaseTest and i see that i need to make first this setup which is in it for setting this ectx?

@whooo
Copy link
Contributor

whooo commented May 12, 2022

This works for me:

from tpm2_pytss.ESAPI import ESAPI                                                                                                                                                                                                     
from tpm2_pytss.utils import NVReadEK, create_ek_template                                                                                                                                                                              
from tpm2_pytss.types import TPM2B_SENSITIVE_CREATE                                                                                                                                                                                    
from tpm2_pytss.constants import ESYS_TR                                                                                                                                                                                               
from base64 import b64encode                                                                                                                                                                                                           
                                                                                                                                                                                                                                       
with ESAPI() as ectx:                                                                                                                                                                                                                  
    nv_read = NVReadEK(ectx)                                                                                                                                                                                                           
    _, templ = create_ek_template("EK-RSA2048", nv_read)                                                                                                                                                                               
    _, pub, _, _, _ = ectx.create_primary(TPM2B_SENSITIVE_CREATE(), templ, ESYS_TR.ENDORSEMENT)                                                                                                                                        
    pb = pub.marshal()                                                                                                                                                                                                                 
    b64pub = b64encode(pb)                                                                                                                                                                                                             
    print(b64pub)  

@Dvergatal
Copy link
Author

Dvergatal commented May 12, 2022

Yeah big thx i have to learn more about this API, but I have also errors in here:

WARNING:esys:../tpm2-tss-3.2.0/src/tss2-esys/api/Esys_NV_ReadPublic.c:309:Esys_NV_ReadPublic_Finish() Received TPM Error 
ERROR:esys:../tpm2-tss-3.2.0/src/tss2-esys/esys_tr.c:209:Esys_TR_FromTPMPublic_Finish() Error NV_ReadPublic ErrorCode (0x0000018b) 
ERROR:esys:../tpm2-tss-3.2.0/src/tss2-esys/esys_tr.c:320:Esys_TR_FromTPMPublic() Error TR FromTPMPublic ErrorCode (0x0000018b) 
WARNING:esys:../tpm2-tss-3.2.0/src/tss2-esys/api/Esys_NV_ReadPublic.c:309:Esys_NV_ReadPublic_Finish() Received TPM Error 
ERROR:esys:../tpm2-tss-3.2.0/src/tss2-esys/esys_tr.c:209:Esys_TR_FromTPMPublic_Finish() Error NV_ReadPublic ErrorCode (0x0000018b) 
ERROR:esys:../tpm2-tss-3.2.0/src/tss2-esys/esys_tr.c:320:Esys_TR_FromTPMPublic() Error TR FromTPMPublic ErrorCode (0x0000018b) 
WARNING:esys:../tpm2-tss-3.2.0/src/tss2-esys/api/Esys_NV_ReadPublic.c:309:Esys_NV_ReadPublic_Finish() Received TPM Error 
ERROR:esys:../tpm2-tss-3.2.0/src/tss2-esys/esys_tr.c:209:Esys_TR_FromTPMPublic_Finish() Error NV_ReadPublic ErrorCode (0x0000018b) 
ERROR:esys:../tpm2-tss-3.2.0/src/tss2-esys/esys_tr.c:320:Esys_TR_FromTPMPublic() Error TR FromTPMPublic ErrorCode (0x0000018b)

P.S. But the public base64 key is printed perfectly fine
P.S.2 This error is on qemu linux image with SWTPM

@whooo
Copy link
Contributor

whooo commented May 12, 2022

The errors are due to how logging is configured for tpm2-tss and how NVReadEK works, I don't have any real workaround for that besides disabling parts of the logging.
Basically NVReadEK tries to read from a certain set of NV areas (the cert, the template and the nonce), none of the is really required for getting the EK, but if they are defined they affect the EK template logic so the code tries to read from them and as you are using a simulator none of the NV areas are defined so you get three groups of errors/warnings

@Dvergatal
Copy link
Author

Dvergatal commented May 12, 2022

Ahhh i see, so basically on a physical machine i shouldn't get these errors. It's completely OK for me if it is only on qemu machine :] Big thx @whooo for support.

@whooo
Copy link
Contributor

whooo commented May 12, 2022

you would still get some of the errors, I haven't seen any TPM that has all three NV areas defined

@Dvergatal
Copy link
Author

Ah OK sure and it is not possible to somehow turn off these errors?

@whooo
Copy link
Contributor

whooo commented May 12, 2022

trying setting the TSS2_LOG environment variable to "esys+none"

@Dvergatal
Copy link
Author

Nice:) it works thx. Btw. I also need to calculate sha256 hash from the public key for the registration id in the azure, but first I will verify in the C source code of the Microsoft tpm_device_provision from what variable, it is being calculated...

@whooo
Copy link
Contributor

whooo commented May 12, 2022

if it's the name you are after, bytes(pub.get_name()) to get the digest of the public part

@Dvergatal
Copy link
Author

@whooo you mean to calculate the hash from this bytes?

@Dvergatal
Copy link
Author

Dvergatal commented May 12, 2022

Ahhh i was wondering why this does not look the same with the result being returned from tpm_device_provision binary and I've found that they are making also some encoding stuff from this sha256 to base32.

I will do step by step analyze of this code. Hope I will succeed with it:]

@Dvergatal
Copy link
Author

OK i have printed sha256 hash calculated by this MS app and it is equal 4A:6B:93:C3:E2:4C:68:AC:1E:FC:C7:27:0D:FD:AE:1E:02:E3:BA:CC:74:DD:F3:F0:E3:35:C6:45:7E:2B:3E:B3 and from the code given by you which i have adjusted:

output = hashlib.sha256(bytes(pub.get_name())).hexdigest()

I'm gettin 3c0ecf0b1570498edb2ebd68b1b79a6a75914a014c0f657930e646427ef38411 which is not equal...

@whooo
Copy link
Contributor

whooo commented May 13, 2022

No need to hash the name, for keys and NV areas it's already the a digest of the nameAlg

@Dvergatal
Copy link
Author

Dvergatal commented May 13, 2022

OK but pub.get_name() is returning me 000bfd906c806f9391b1489fd67c6c4dd92c831e44b1ce59c51aa84d5ce8bbe8caf9 and I need sha256 calculated from ek_pub.

@Dvergatal
Copy link
Author

Dvergatal commented May 13, 2022

Ah i found something more problematic, which i haven't noticed before is that the returned endorsement key value is slight different than the one returned from python code.

This is from tpm_device_provision AToAAQALAAMAsgAgg3GXZ0SEs/gakMyNRqXXJP1S124GUgtk8qHaGzMUaaoABgCAAEMAEAgAAAAAAAEAq4+zaAAAv0nsuYwCm+XzzdldDYDWR+565eLh9E2PUe9x9oEn+9xsGS5z2mzj1YY6xUvMkN6pFd8gv2H0d1KBdqyruGSH8qP35QDwLI7t0jcEeeemDJ5te/A92Z+yfzLDrGqypz064VhP3Qrha/zhF5g+crZpOcnlshwsnVNh7awcqNjk0ki5QrmaBbiI9g/D9xv/gFqGXdhkGwaSTnJeBWRefmrXhtF+iGwCRffEjlG/qsxQ/hvRbeAdybwQk+cysdSk4w9jTMrsf7b8AoYBOlkrGSTy2mzk1F2ePJEr2W+gayYDVNLOa3pagnxtkoz0ER2F5BzFS+glrgl6rOjE1w==

and this is the value from python code AToAAQALAAMAsgAgg3GXZ0SEs/gakMyNRqXXJP1S124GUgtk8qHaGzMUaaoABgCAAEMAEAgAAAAAAAEAnjwGiAAAdMj4D/FS7NdZkzhgEb+zP3Y3GZXUUvy5eakk1zxNzy1+Ok55JDHgaVwLOKM+vqV72PuLoEpLHVWYty+emITUx2R8Vp/G/ojI+M8v7HbSlYlW6TPoynpqdHjxLfwu8YLJvrJSpS4SRqJZEetYXT+ZttFeFiGYVgsjtBioiiP1pw94YjzQ0Zr055dImEYSEhCV9AOfl2kmWBUo9W/mQisJBwoNuhKTq8U69FbWF4h3O47LiVmilw+11GfTGVOj2UUR7z35GFdJ/6ClsX8zfDxQvt7il2QVR2uGi6R9SR2BM3iZzei79hzMOh0Xr0Pv+UGlDTtF71jX2FMM3w==

As you can see it starts the same but at some point it differs.

@Dvergatal
Copy link
Author

No need to hash the name, for keys and NV areas it's already the a digest of the nameAlg

I found that in the code that nameAlg by default is SHA256 so i get it now:D

@Dvergatal
Copy link
Author

Hahahahah now it has been fixed:D the swtpm_setup command has not been started in the docker container with all configuration and thus is why these rubbish data were being produced.... The endorsement keys are now the same. Need also to verifiy this registration id.

@Dvergatal
Copy link
Author

Dvergatal commented Jun 29, 2022

The endorsement key is not really burnt in, the endorsement certificate might be. All primary keys are generated by a KDF which has some inputs, which includes the templates and a per hierarchy seed, if you create a primary key under the endorsement hierarchy with a non standard template it will not match any endorsement certificates. I think you should reach out to the Azure IOT SDK team to understand how their tooling works, as both the python code and tpm2_createek generates the same endorsement key it don't think it's a bug in tpm2-pytss

@whooo after digging their code, I have a little bit more knowledge. So first of all they are generating two persistent keys. The first one is Endorsement Key and the second one is Storage Root Key. I have also found templates for them (I'm sorry for the code in c, but it shouldn't be a problem). For the ek it looks like that:

static TPM2B_PUBLIC* GetEkTemplate ()
{
    static TPM2B_PUBLIC EkTemplate = { 0,   // size will be computed during marshaling
    {
        TPM_ALG_RSA,                    // TPMI_ALG_PUBLIC      type
        TPM_ALG_SHA256,                 // TPMI_ALG_HASH        nameAlg
        { 0 },                          // TPMA_OBJECT  objectAttributes (set below)
        {32,
        { 0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xb3, 0xf8,
        0x1a, 0x90, 0xcc, 0x8d, 0x46, 0xa5, 0xd7, 0x24,
        0xfd, 0x52, 0xd7, 0x6e, 0x06, 0x52, 0x0b, 0x64,
        0xf2, 0xa1, 0xda, 0x1b, 0x33, 0x14, 0x69, 0xaa }
        },                              // TPM2B_DIGEST         authPolicy
        { 0 },                          // TPMU_PUBLIC_PARMS    parameters (set below)
        { 0 }                           // TPMU_PUBLIC_ID       unique
    } };
    EkTemplate.publicArea.objectAttributes = ToTpmaObject(
        Restricted | Decrypt | FixedTPM | FixedParent | AdminWithPolicy | SensitiveDataOrigin);
    EkTemplate.publicArea.parameters.rsaDetail = RsaStorageParams;
    return &EkTemplate;
}

and for srk:

static TPM2B_PUBLIC* GetSrkTemplate()
{
    static TPM2B_PUBLIC SrkTemplate = { 0,  // size will be computed during marshaling
    {
        TPM_ALG_RSA,                // TPMI_ALG_PUBLIC      type
        TPM_ALG_SHA256,             // TPMI_ALG_HASH        nameAlg
        { 0 },                      // TPMA_OBJECT  objectAttributes (set below)
        { 0 },                      // TPM2B_DIGEST         authPolicy
        { 0 },                      // TPMU_PUBLIC_PARMS    parameters (set before use)
        { 0 }                       // TPMU_PUBLIC_ID       unique
    } };
    SrkTemplate.publicArea.objectAttributes = ToTpmaObject(
        Restricted | Decrypt | FixedTPM | FixedParent | NoDA | UserWithAuth | SensitiveDataOrigin);
    SrkTemplate.publicArea.parameters.rsaDetail = RsaStorageParams;
    return &SrkTemplate;
}

In addition I have noticed that they are creating these keys under specified addresses, ek is at 0x81010001 and srk at 0x81000001, so I have used tpm2_readpublic to read them and finally I got my answers:D

root@eg ~> tpm2_readpublic -c 0x81010001  -o output.dat -f pem -t primary.handle
name: 000b3a5d31f597c0ffe0b62500e88b5a058cb903e046d2599bfb92a0c8be96b506ef
qualified name: 000b1eeda0d707eed7b6dfb88b591b03be5678e3c6ada2bc9325789b095740a87896
name-alg:
  value: sha256
  raw: 0xb
attributes:
  value: fixedtpm|fixedparent|sensitivedataorigin|adminwithpolicy|restricted|decrypt
  raw: 0x300b2
type:
  value: rsa
  raw: 0x1
exponent: 65537
bits: 2048
scheme:
  value: null
  raw: 0x10
scheme-halg:
  value: (null)
  raw: 0x0
sym-alg:
  value: aes
  raw: 0x6
sym-mode:
  value: cfb
  raw: 0x43
sym-keybits: 128
rsa: b62525c93c93ee9a902a0bda50e5944f731ade4295d04baad4be411a1ae46757a4e1812f99f85c78a34f86ef497112ad79cdf3338a349be6ffbd0177178a82f08d7f9dc88734708ee3102c97a567ce038e58a41341dc3052bc2734bef92de5571f228fc50f03fdfe54f9dc823cbbba8e4dd9a3045e0bb3cf6cfb2d264d85af6431de67721ec75558ebb9cc3d4730aed873226c7c395ed237ce8b738c504759fc86c6050c41683b3f9e37be3fe36c1f53e11be517e4af7bb5e8aedad3da92278a06d9658608d3c47a818eb294f681384cc362da37c82abbb9c00abf3af7486cb00f6bf34ca65094607c10bed2e1f65f28a1e3e4e75249d268ab36465dca6fdc6b
authorization policy: 837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa
root@eg ~> tpm2_readpublic -c 0x81000001  -o output.dat -f pem -t primary.handle
name: 000bcc53626cf97ae5799adba7dfa52206857f4c87af1f070ace5f46804a69d3e152
qualified name: 000ba879ac6817c5fe65d5259e92392bc9ca0a7af9ccd25636248225ff3e924b39a0
name-alg:
  value: sha256
  raw: 0xb
attributes:
  value: fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricted|decrypt
  raw: 0x30472
type:
  value: rsa
  raw: 0x1
exponent: 65537
bits: 2048
scheme:
  value: null
  raw: 0x10
scheme-halg:
  value: (null)
  raw: 0x0
sym-alg:
  value: aes
  raw: 0x6
sym-mode:
  value: cfb
  raw: 0x43
sym-keybits: 128
rsa: d392079163ddc12b8d22cce0f1c5be47f4379df7ac72f1e56d11ddef26c720f1eb8f4a289b920bafd6b75517876b69965142a67ee4d8320a41a48bcead8ddff4017ad6282e721cbce1bf262f2cc4ae1e4dd8e7360871e1d54bd7ae904660c1cc8e0e0d8cf577d5939c53f90d2d8cc0066153f338882a4ec61cd99cffbcdd8a52a8f58d03f5ebfdd0fcad92755db481db38ef7c07b55be0e52307a686ef5bff8a8acc62d902d688b795ddb15856f2e25724eedd5983558b0fa886986cd2def58448005ff1492d3cda67fe243520973dadd1f38148d817c8eb337ef61351c34db362dd92acd88561fd5cf5c93747b0aebcefa9b264c17198aca87e30ada1f80cb9

As you can see the authorization policy for the ek is the same. Probably this is missing in python code, as well as generation of srk.

I have also used tpm2_clear to verify, if it would be deleted and yes it has been. Additionally these keys are always different. Correction only SRK differs, EK is always the same, probably it is due to how this template looks.

@whooo
Copy link
Contributor

whooo commented Jun 30, 2022

The authorization_policy is the same in the python code, if both the python code tpm2_createek creates the same key the same issue would be in both the tools and the python code, which I suspect it doesn't
Can you paste tpm.py

@Dvergatal
Copy link
Author

@whooo:

import sys
from typing import Tuple
from hashlib import sha256
from base64 import b64encode, b32encode
from tpm2_pytss.ESAPI import ESAPI
from tpm2_pytss.utils import NVReadEK, create_ek_template
from tpm2_pytss.types import TPM2B_SENSITIVE_CREATE, TPM2B_PUBLIC
from tpm2_pytss.constants import ESYS_TR

def get_data_from_tpm_module() -> Tuple[str, str]:

    def create_ek_pub() -> TPM2B_PUBLIC:
        with ESAPI() as ectx:
            nv_read = NVReadEK(ectx)
            _, templ = create_ek_template("EK-RSA2048", nv_read)
            _, pub, _, _, _ = ectx.create_primary(TPM2B_SENSITIVE_CREATE(), templ, ESYS_TR.ENDORSEMENT)
            return pub

    pub = create_ek_pub()
    hash_function = sha256()
    hash_function.update(pub.marshal())
    reg_id = b32encode(hash_function.digest()).replace(b'=', b'').decode().lower()
    end_key = b64encode(pub.marshal()).decode()

    return reg_id, end_key

print(get_data_from_tpm_module())

Btw. I have verified one thing, that if I use this code than this command:

tpm2_readpublic -c 0x81010001 -o ek_pub.pem -f pem -t primary.handle

this command does not return me the key but only error:

WARNING:esys:../tpm2-tss-3.2.0/src/tss2-esys/api/Esys_ReadPublic.c:320:Esys_ReadPublic_Finish() Received TPM Error 
ERROR:esys:../tpm2-tss-3.2.0/src/tss2-esys/esys_tr.c:230:Esys_TR_FromTPMPublic_Finish() Error ReadPublic ErrorCode (0x0000018b) 
ERROR:esys:../tpm2-tss-3.2.0/src/tss2-esys/esys_tr.c:320:Esys_TR_FromTPMPublic() Error TR FromTPMPublic ErrorCode (0x0000018b) 
ERROR: Esys_TR_FromTPMPublic(0x18B) - tpm:handle(1):the handle is not correct for the use
ERROR: Unable to run tpm2_readpublic

Moreover the tpm_device_provision takes some time to generate their EK and SRK keys if they aren't any on the addresses I have given you yesterday and python code together with tpm2_createek is really fast.

@Dvergatal
Copy link
Author

@whooo nevertheless these EK values are different I was also wondering if it really matters, because I am able to register it but the connection isn't able to be establish due to missing SRK. Could we do some test also to generate SRK in this python code for this EK in order to verify if my assumptions are OK?

@Dvergatal
Copy link
Author

@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?

@williamcroberts
Copy link
Member

williamcroberts commented Jul 1, 2022

@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.
  • Generate SRK - Use ESAPI.CreatePrimary with proper template
  • Make SRK Persistent - Use ESAPI.EvictControl to address 0x81000001.

@Dvergatal
Copy link
Author

Dvergatal commented Jul 5, 2022

* 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.

As @whooo has given code sample, which I'm using and it uses ESAPI.CreatePrimary with template from create_ek_template and Callable to class NVReadEK it doesn't make EK persistent.

Moreover, how can I first read that EK public, to verify if it is already persistent? I'm trying to use read_public, but there is not persistent_handle like in tpm2_readpublic -c 0x81010001 -o ek_pub.pem -f pem -t primary.handle command, but only variable object_handle, which I'm setting to ESYS_TR.ENDORSEMENT i.e. pub, _, _ = ectx.read_public(ESYS_TR.ENDORSEMENT) but it throws an exception tpm2_pytss.TSS2_Exception.TSS2_Exception: tpm:handle(1):value is out of range or is not correct for the context

@whooo
Copy link
Contributor

whooo commented Jul 5, 2022

Use https://tpm2-pytss.readthedocs.io/en/latest/esys.html#tpm2_pytss.ESAPI.tr_from_tpmpublic to get an ESYS_TR handle from the persistent handle, then you can pass that to read_public

@Dvergatal
Copy link
Author

Dvergatal commented Jul 5, 2022

Ok I have found informations how to do that in issue #298 and niooss-ledger is right, that it is not as straightforward with ESAPI.read_public function.

P.S. @whooo I haven't noticed that you have already written the solution :P

@Dvergatal
Copy link
Author

Dvergatal commented Jul 6, 2022

@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
Copy link
Contributor

whooo commented Jul 6, 2022

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
Copy link
Author

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
Copy link
Contributor

whooo commented Jul 6, 2022

Look at

def test_create_primary(self):
for an example, it's the same field names as the C code from azure

@Dvergatal
Copy link
Author

@whooo thx

@Dvergatal
Copy link
Author

@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
Copy link
Contributor

whooo commented Jul 6, 2022

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

@Dvergatal
Copy link
Author

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
Copy link
Author

Dvergatal commented Jul 6, 2022

@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
Copy link
Contributor

whooo commented Jul 6, 2022

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
Copy link
Author

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
Copy link
Contributor

whooo commented Jul 6, 2022

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
Copy link
Author

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
Copy link
Contributor

whooo commented Jul 6, 2022

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
Copy link
Author

Dvergatal commented Jul 6, 2022

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
Copy link
Author

Dvergatal commented Jul 21, 2022

@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
Copy link
Member

@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
Copy link
Author

@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
Copy link
Author

@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
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants