azure-iot-sdk-c icon indicating copy to clipboard operation
azure-iot-sdk-c copied to clipboard

Endorsement key completly different with tpm2-pytss api code and tpm_device_provision

Open Dvergatal opened this issue 2 years ago • 32 comments

Hi all, I am currently dealing with a problem that I have written a code in python with the usage of of tpm2-pytss library, which is short and looks like that:


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
from mpa.communication.logger import Logger

logger = Logger(f"{sys.argv[0] if __name__ == '__main__' else __name__}")


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

    def create_ek_pub() -> TPM2B_PUBLIC:
        """ Reading TPM endorsement key is described on https://github.com/tpm2-software/tpm2-pytss/issues/350 """
        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())

To confirm that, it is working, I have verified the output with the one generated by the tpm_device_provision. All was working very well on testing environment on qemu with swtpm configured like that:

swtpm_setup --tpmstate /tmp/mytpm1 --createek --create-ek-cert --create-platform-cert --allow-signing --tpm2
swtpm socket --tpmstate dir=/tmp/mytpm1 --ctrl type=unixio,path=/tmp/swtpm-sock --log level=20 --tpm2 -d

The ek pub key values are the same, but during the tests, with physical tpm, it has occurred, that to my surprise, they are completely different, so we started digging. In time we have discovered on qemu, that it is also not possible to register to azure, with the endorsement key generated by the swtpm and the error is like this:

endorsement key is invalid or does not match the enrollment

and if tpm2_clear method is used my code is still producing the same endorsement key and tpm_device_provision outputs completely new value.

I was also trying to use this https://github.com/tpm2-software/tpm2-tools/blob/master/man/tpm2_getekcertificate.1.md binary to verify, but it produces certificate for me instead of base64 tss structure.

Below is the output with values generated with both binaries which are different:

root@eg > python3 tpm.py
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) 
('hch6u236c4a4hi7w3m7iviz5gahxnmn5mf62swnqskenqhbklvra', 'AToAAQALAAMAsgAgg3GXZ0SEs/gakMyNRqXXJP1S124GUgtk8qHaGzMUaaoABgCAAEMAEAgAAAAAAAEAgvAE8heVwv60baEy5j8XvH7tQNeR/zTuQvCsKDhVacbDugAsLR3KfdHDrLscygZu4eB0p1vTNvT40t82gj2/AnMp/yxrsMWYftPNcZBGSKH3baPrtcxclWFhqxwCPqjO5uvDkz3qGt3024ErA7WamzrBWWnw8ejv95BennOILMeFafgVGVZoX0E1XOi5u8FuZbDWjxN6bLoDkRcWpw2IrWUy1rVGcIbgmkGDi63JxgBl2lsULiBhs1mWamA6mmLiIGryqZVjIw4sL6//E06hXYout37VOtFrs9mmI3hFHn8wRArE3ppLcPOkr3amiiLZD18m+crLWWL3JNY2eqPTpQ==')
root@eg > tpm_device_provision 
Gathering the registration information...

Registration Id:
exvjykmny25qynh3oarky73memadol7cegm4i7yflrbtwl67ipxa

Endorsement Key:
AToAAQALAAMAsgAgg3GXZ0SEs/gakMyNRqXXJP1S124GUgtk8qHaGzMUaaoABgCAAEMAEAgAAAAAAAEAtiUlyTyT7pqQKgvaUOWUT3Ma3kKV0Euq1L5BGhrkZ1ek4YEvmfhceKNPhu9JcRKtec3zM4o0m+b/vQF3F4qC8I1/nciHNHCO4xAsl6VnzgOOWKQTQdwwUrwnNL75LeVXHyKPxQ8D/f5U+dyCPLu6jk3ZowReC7PPbPstJk2Fr2Qx3mdyHsdVWOu5zD1HMK7YcyJsfDle0jfOi3OMUEdZ/IbGBQxBaDs/nje+P+NsH1PhG+UX5K97teiu2tPakieKBtllhgjTxHqBjrKU9oE4TMNi2jfIKru5wAq/OvdIbLAPa/NMplCUYHwQvtLh9l8ooePk51JJ0mirNkZdym/caw==

Press any key to continue:

root@eg > 

Question is what I'm missing?

Dvergatal avatar Jun 27 '22 23:06 Dvergatal

Ok I have also used tpm2_createek binary with commands like:

tpm2_createek --ek-context rsa_ek.ctx  --key-algorithm rsa --public rsa_ek.pub
base64 -w0 rsa_ek.pub >rsa_ek.pub.base64

to see if it differs and it produces me the same output like the python code:

cat rsa_ek.pub.base64
AToAAQALAAMAsgAgg3GXZ0SEs/gakMyNRqXXJP1S124GUgtk8qHaGzMUaaoABgCAAEMAEAgAAAAAAAEAgvAE8heVwv60baEy5j8XvH7tQNeR/zTuQvCsKDhVacbDugAsLR3KfdHDrLscygZu4eB0p1vTNvT40t82gj2/AnMp/yxrsMWYftPNcZBGSKH3baPrtcxclWFhqxwCPqjO5uvDkz3qGt3024ErA7WamzrBWWnw8ejv95BennOILMeFafgVGVZoX0E1XOi5u8FuZbDWjxN6bLoDkRcWpw2IrWUy1rVGcIbgmkGDi63JxgBl2lsULiBhs1mWamA6mmLiIGryqZVjIw4sL6//E06hXYout37VOtFrs9mmI3hFHn8wRArE3ppLcPOkr3amiiLZD18m+crLWWL3JNY2eqPTpQ==

So question is what is the difference and where it comes from?

Dvergatal avatar Jun 28 '22 09:06 Dvergatal

OK, I have managed to provision with the EK generated by python code and also with tpm tools like tpm2_createek. I was missing EK being persistent on the TPM and SRK generated and also persistent on the TPM.

Also on qemu the code for creating EK:

swtpm_setup --tpmstate /tmp/mytpm1 --createek --create-ek-cert --create-platform-cert --allow-signing --tpm2

was wrong due to EK purposes. The --allow-signing should be changed to --decryption and than EK attributes are proper:

attributes:
  value: fixedtpm|fixedparent|sensitivedataorigin|userwithauth|noda|restricted|decrypt

The procedure to make it provision with tpm tools looks like that:

# clear tpm
tpm2_clear
# read EK, should be empty
tpm2_readpublic -c 0x81010001 -o ek_pub.pem -f pem -t primary.handle
# create EK and make it persistent
tpm2_createek -c 0x81010001 -G rsa -u ek.pub
# read EK and now it should be filled
tpm2_readpublic -c 0x81010001 -o ek_pub.pem -f pem -t primary.handle
# create auth session
tpm2_startauthsession --policy-session -S session.ctx
# start session
tpm2_policysecret -S session.ctx -c 0x4000000B
# create SRK
tpm2_create -C ek.ctx -P session:session.ctx -G rsa2048 -u srk.pub -r srk.priv -a 'restricted|decrypt|fixedtpm|fixedparent|sensitivedataorigin|userwithauth' -c srk.ctx
# read SRK, should be empty
tpm2_readpublic -c 0x81000001 -o srk_pub.pem -f pem -t primary.handle
# make SRK persistent
tpm2_evictcontrol -C o -c srk.ctx 0x81000001
# read SRK and now it should be filled
tpm2_readpublic -c 0x81000001 -o srk_pub.pem -f pem -t primary.handle

, but still the EK differs between tpm_device_provision and tpm2_createek - python code produces identical values.

I have also verified, if EK generated and made persistent with swtpm is the same as with tpm2_tools and it is, so there is definitely something, maybe wrong with your code or maybe you are doing something additionally.

Dvergatal avatar Jul 06 '22 14:07 Dvergatal

Hi guys, I have gotten yesterday an idea to verify if NV certificate written by the producer of TPM has exactly the same public key value (in this case modulus, because it is RSA algorithm) with EK generated and written by tpm_device_provision in the persistent memory and EK generated and written by tpm2_pytss api/tpm2_createek in the persistent memory.

It occurs that public EK value generated by Intel code is exactly the same as public key value written in NV certificate by the TPM producer. In case of your code these values differs, so imho I suspect that there might be some kind of an error in your code or maybe you are doing some additional procedures, which are not documented. But from my knowledge these values should be exactly the same. Could you please deal with this problem and solve it with me?

Dvergatal avatar Jul 09 '22 20:07 Dvergatal

@Dvergatal , we no longer recommend using the DPS-TPM protocol. Instead, we recommend using DPS-X509 mutual-authentication. The dTPM can be used in conjunction with the OpenSSL TPM engine to store the device private key as documented here.

If you still need to use TPM with Python and our library, I suspect the keys are different either because of the way they are extracted by our uTPM driver or because we might use a different Base64 encoding. Adding @RonaldAi in case he has more information on how uTPM extracts the EK. The question has been asked / partially answered before: https://github.com/Azure/azure-iot-sdk-c/issues/1502 where there are several reasons given why the EK form can be different.

CIPop avatar Jul 18 '22 23:07 CIPop

@Dvergatal , we no longer recommend using the DPS-TPM protocol. Instead, we recommend using DPS-X509 mutual-authentication. The dTPM can be used in conjunction with the OpenSSL TPM engine to store the device private key as documented here.

I'm sorry what private key? Endorsement Key is being burned during the production of TPM and absolutely no one has access to it.

If you still need to use TPM with Python and our library, I suspect the keys are different either because of the way they are extracted by our uTPM driver or because we might use a different Base64 encoding.

The base64 encoding is the same, because if you lookj at both EK values, the first one generated, by Intel code:

AToAAQALAAMAsgAgg3GXZ0SEs/gakMyNRqXXJP1S124GUgtk8qHaGzMUaaoABgCAAEMAEAgAAAAAAAEAgvAE8heVwv60baEy5j8XvH7tQNeR/zTuQvCsKDhVacbDugAsLR3KfdHDrLscygZu4eB0p1vTNvT40t82gj2/AnMp/yxrsMWYftPNcZBGSKH3baPrtcxclWFhqxwCPqjO5uvDkz3qGt3024ErA7WamzrBWWnw8ejv95BennOILMeFafgVGVZoX0E1XOi5u8FuZbDWjxN6bLoDkRcWpw2IrWUy1rVGcIbgmkGDi63JxgBl2lsULiBhs1mWamA6mmLiIGryqZVjIw4sL6//E06hXYout37VOtFrs9mmI3hFHn8wRArE3ppLcPOkr3amiiLZD18m+crLWWL3JNY2eqPTpQ==

and second one by your binary:

AToAAQALAAMAsgAgg3GXZ0SEs/gakMyNRqXXJP1S124GUgtk8qHaGzMUaaoABgCAAEMAEAgAAAAAAAEAtiUlyTyT7pqQKgvaUOWUT3Ma3kKV0Euq1L5BGhrkZ1ek4YEvmfhceKNPhu9JcRKtec3zM4o0m+b/vQF3F4qC8I1/nciHNHCO4xAsl6VnzgOOWKQTQdwwUrwnNL75LeVXHyKPxQ8D/f5U+dyCPLu6jk3ZowReC7PPbPstJk2Fr2Qx3mdyHsdVWOu5zD1HMK7YcyJsfDle0jfOi3OMUEdZ/IbGBQxBaDs/nje+P+NsH1PhG+UX5K97teiu2tPakieKBtllhgjTxHqBjrKU9oE4TMNi2jfIKru5wAq/OvdIbLAPa/NMplCUYHwQvtLh9l8ooePk51JJ0mirNkZdym/caw==

you will notice that the fronts are identical AToAAQALAAMAsgAgg3GXZ0SEs/gakMyNRqXXJP1S124GUgtk8qHaGzMUaaoABgCAAEMAEAgAAAAAAAEA which means that this is the part of the TPM key template structure with identical parameters. If we go further and use tpm2_readpublic for both cases, meaning generating EK + SRK in first case with the usage of Intel code and in second case with the usage of tpm_device_provision we will notice, that persistent objects are almost identical with slight difference for the rsa public key value.

This is the EK public template value generated by Intel code:

root@eg ~> tpm2_readpublic -c 0x81010001 -o ek_pub.pem -f pem -t primary.handle
name: 000b4aae76f174a0c581d67b033c8bf64087be0746493b5822519b90b5fbcbb07aa7
qualified name: 000b72577c457408af04eaf2552dbf41c6f405e7ca74caf32139417e1882a8e8c793
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: 82f004f21795c2feb46da132e63f17bc7eed40d791ff34ee42f0ac28385569c6c3ba002c2d1dca7dd1c3acbb1cca066ee1e074a75bd336f4f8d2df36823dbf027329ff2c6bb0c5987ed3cd71904648a1f76da3ebb5cc5c956161ab1c023ea8cee6ebc3933dea1addf4db812b03b59a9b3ac15969f0f1e8eff7905e9e73882cc78569f8151956685f41355ce8b9bbc16e65b0d68f137a6cba03911716a70d88ad6532d6b5467086e09a41838badc9c60065da5b142e2061b359966a603a9a62e2206af2a99563230e2c2fafff134ea15d8a2eb77ed53ad16bb3d9a62378451e7f30440ac4de9a4b70f3a4af76a68a22d90f5f26f9cacb5962f724d6367aa3d3a5
authorization policy: 837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa

and this is the same EK public template value generated by your code:

root@eg ~> tpm2_readpublic -c 0x81010001 -o ek_pub.pem -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

As I have already written the only difference is in rsa values:

82f004f21795c2feb46da132e63f17bc7eed40d791ff34ee42f0ac28385569c6c3ba002c2d1dca7dd1c3acbb1cca066ee1e074a75bd336f4f8d2df36823dbf027329ff2c6bb0c5987ed3cd71904648a1f76da3ebb5cc5c956161ab1c023ea8cee6ebc3933dea1addf4db812b03b59a9b3ac15969f0f1e8eff7905e9e73882cc78569f8151956685f41355ce8b9bbc16e65b0d68f137a6cba03911716a70d88ad6532d6b5467086e09a41838badc9c60065da5b142e2061b359966a603a9a62e2206af2a99563230e2c2fafff134ea15d8a2eb77ed53ad16bb3d9a62378451e7f30440ac4de9a4b70f3a4af76a68a22d90f5f26f9cacb5962f724d6367aa3d3a5

and

b62525c93c93ee9a902a0bda50e5944f731ade4295d04baad4be411a1ae46757a4e1812f99f85c78a34f86ef497112ad79cdf3338a349be6ffbd0177178a82f08d7f9dc88734708ee3102c97a567ce038e58a41341dc3052bc2734bef92de5571f228fc50f03fdfe54f9dc823cbbba8e4dd9a3045e0bb3cf6cfb2d264d85af6431de67721ec75558ebb9cc3d4730aed873226c7c395ed237ce8b738c504759fc86c6050c41683b3f9e37be3fe36c1f53e11be517e4af7bb5e8aedad3da92278a06d9658608d3c47a818eb294f681384cc362da37c82abbb9c00abf3af7486cb00f6bf34ca65094607c10bed2e1f65f28a1e3e4e75249d268ab36465dca6fdc6b

And as I have also written before:

Hi guys, I have gotten yesterday an idea to verify if NV certificate written by the producer of TPM has exactly the same public key value (in this case modulus, because it is RSA algorithm) with EK generated and written by tpm_device_provision in the persistent memory and EK generated and written by tpm2_pytss api/tpm2_createek in the persistent memory.

It occurs that public EK value generated by Intel code is exactly the same as public key value written in NV certificate by the TPM producer. In case of your code these values differs, so imho I suspect that there might be some kind of an error in your code or maybe you are doing some additional procedures, which are not documented. But from my knowledge these values should be exactly the same. Could you please deal with this problem and solve it with me?

The EK is not being "generated" by the user, but only by the producer of the TPM, meaning that it is being generated by the KDF from seed stored in NV or read from NV certificate, so I think that you are creating a primary key under the endorsement hierarchy with a non standard template, which will not match any endorsement certificates.

Adding @RonaldAi in case he has more information on how uTPM extracts the EK. The question has been asked / partially answered before: #1502 where there are several reasons given why the EK form can be different.

I've read it and it doesn't answer my questions.

Dvergatal avatar Jul 19 '22 07:07 Dvergatal

I haven't worked with this code before, so needed to dig around. It appears that tpm_device_provision uses this code to create the EK: https://github.com/Azure/azure-iot-sdk-c/blob/bba2267a150226e1089ee4686145eab2527f1d37/provisioning_client/adapters/hsm_client_tpm.c#L455 That could be different from your sample code. Specifically, the way the EK template is determined (https://github.com/Azure/azure-iot-sdk-c/blob/bba2267a150226e1089ee4686145eab2527f1d37/provisioning_client/adapters/hsm_client_tpm.c#L63).

RonaldAi avatar Jul 19 '22 15:07 RonaldAi

I'm sorry what private key? Endorsement Key is being burned during the production of TPM and absolutely no one has access to it.

@Dvergatal I was referring to X509 certificates which require a public and private key. Instead of using TPM primitives such as EK / SRK, we now recommend using the TPM-TSS OpenSSL Engine to use the TPM to "store" X509 private keys (I wrote "store" in quotes because my understanding is that the keys are not really stored within the TPM. Instead, a TPM-key context is used to encrypt the files on disk).

CIPop avatar Jul 19 '22 16:07 CIPop

I haven't worked with this code before, so needed to dig around. It appears that tpm_device_provision uses this code to create the EK:

https://github.com/Azure/azure-iot-sdk-c/blob/bba2267a150226e1089ee4686145eab2527f1d37/provisioning_client/adapters/hsm_client_tpm.c#L455

Yes, this is the function which I have also found, then it goes to: https://github.com/Azure/azure-utpm-c/blob/6d9cc86b4dcd2be757534f2dfc0a6c6b23af2376/src/tpm_codec.c#L201 and if none is persistent at 0x81010001 calls this one: https://github.com/Azure/azure-utpm-c/blob/6d9cc86b4dcd2be757534f2dfc0a6c6b23af2376/src/tpm_codec.c#L220 and than in here: https://github.com/Azure/azure-utpm-c/blob/6d9cc86b4dcd2be757534f2dfc0a6c6b23af2376/src/tpm_codec.c#L701 which finally calls this one: https://github.com/Azure/azure-utpm-c/blob/6d9cc86b4dcd2be757534f2dfc0a6c6b23af2376/src/tpm_codec.c#L790 and you can now read all these macros...

With gcc we have decoded this macros and the code develops to something like this:

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;
}

and I suspect that the usage of this PCR might be wrong, but I'm not sure. As I said in my previous posts EK is created by the producer of the TPM, it may be located in certain NV areas (the cert, the template and the nonce).

That could be different from your sample code. Specifically, the way the EK template is determined (

https://github.com/Azure/azure-iot-sdk-c/blob/bba2267a150226e1089ee4686145eab2527f1d37/provisioning_client/adapters/hsm_client_tpm.c#L63 ).

Nope these templates are exactly the same. Together with guys from Intel for the tpm2 packages, we have it already verified. Moreover as I have showed you in my previous post:

This is the EK public template value generated by Intel code:

root@eg ~> tpm2_readpublic -c 0x81010001 -o ek_pub.pem -f pem -t primary.handle
name: 000b4aae76f174a0c581d67b033c8bf64087be0746493b5822519b90b5fbcbb07aa7
qualified name: 000b72577c457408af04eaf2552dbf41c6f405e7ca74caf32139417e1882a8e8c793
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: 82f004f21795c2feb46da132e63f17bc7eed40d791ff34ee42f0ac28385569c6c3ba002c2d1dca7dd1c3acbb1cca066ee1e074a75bd336f4f8d2df36823dbf027329ff2c6bb0c5987ed3cd71904648a1f76da3ebb5cc5c956161ab1c023ea8cee6ebc3933dea1addf4db812b03b59a9b3ac15969f0f1e8eff7905e9e73882cc78569f8151956685f41355ce8b9bbc16e65b0d68f137a6cba03911716a70d88ad6532d6b5467086e09a41838badc9c60065da5b142e2061b359966a603a9a62e2206af2a99563230e2c2fafff134ea15d8a2eb77ed53ad16bb3d9a62378451e7f30440ac4de9a4b70f3a4af76a68a22d90f5f26f9cacb5962f724d6367aa3d3a5
authorization policy: 837197674484b3f81a90cc8d46a5d724fd52d76e06520b64f2a1da1b331469aa

and this is the same EK public template value generated by your code:

root@eg ~> tpm2_readpublic -c 0x81010001 -o ek_pub.pem -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

are identical only rsa value is different and also name together with qualified name, but these parameters are different due to different rsa values (name and qualified name values are being generated from key value)

I'm sorry what private key? Endorsement Key is being burned during the production of TPM and absolutely no one has access to it.

@Dvergatal I was referring to X509 certificates which require a public and private key. Instead of using TPM primitives such as EK / SRK, we now recommend using the TPM-TSS OpenSSL Engine to use the TPM to "store" X509 private keys

Ah OK, now I get it, still we need to support TPM individual provisioning due to our customers, besides I would use rathed tpm2-pkcs11 engine instead of tpm-tss due to more flexibility.

(I wrote "store" in quotes because my understanding is that the keys are not really stored within the TPM. Instead, a TPM-key context is used to encrypt the files on disk).

From what I remember, it stores on the tpm. When you are using tpm2-pkcs11, than it is like you are saying, but as far as I remember @williamcroberts was telling me, that it is also possible or would be, can't remember now, to write it directly on a tpm.

Dvergatal avatar Jul 19 '22 22:07 Dvergatal

Sorry for the delay. If the EK templates are the same, then the only explanation that I have for the difference in RSA and name is (as was previously mentioned) the use of a different seed (that is used in the key derivation). Has the EK been created with the same instance of the SW tpm or TPM device?

RonaldAi avatar Jul 26 '22 20:07 RonaldAi

Sorry for the delay.

No problem.

If the EK templates are the same, then the only explanation that I have for the difference in RSA and name is (as was previously mentioned) the use of a different seed (that is used in the key derivation). Has the EK been created with the same instance of the SW tpm or TPM device?

Yes the EK has been created with exactly the same instance of SW tpm and also on the same physical machine and still it differs. I have asked @williamcroberts in the issue, which is mentioned above, to look at the code which we have decoded from your macros and he said that there is a usage of delta in it, which he does not fully understand why it is there.

I think that the best solution would be to ask ppl from Intel to join this thread for support, because otherwise we will go around and look for the cause in the dark.

Dvergatal avatar Jul 26 '22 21:07 Dvergatal

@Dvergatal I'm not sure if you've done this, but we could find the delta by modifying the the code to dump the TPM2B_PUBLIC being used, is that possible? If their is no difference between the templates than perhaps dumping all the parameters to createprimary? We can diff what the python code is sending versus the Azure code?

williamcroberts avatar Aug 01 '22 15:08 williamcroberts

I think I see the issue, the unique field in the azure code is set to { 0 }, which means size field is 0. The unique field should be set to the key length in bytes of zeroes. See the azure code here: https://github.com/Azure/azure-iot-sdk-c/blob/32942abb994f6855edc2a4dee8afd80735df4151/provisioning_client/src/sec_device_module_tpm.c#L72

I see that template code copied and pasted throughout the code base and they all appear wrong.

This covered in the spec in the templates within Annex B: https://trustedcomputinggroup.org/wp-content/uploads/TCG_IWG_EKCredentialProfile_v2p4_r3.pdf

Specifically Anex B3.3 Template L-1: RSA 2048 (Storage). The python code properly sets this unique size.

williamcroberts avatar Aug 01 '22 17:08 williamcroberts

So to be clear the azure code needs updating and we can verify this is the issue by setting the unique field to 0 in the python code, then you should get matching EK's.

williamcroberts avatar Aug 01 '22 18:08 williamcroberts

@Dvergatal I'm not sure if you've done this, but we could find the delta by modifying the the code to dump the TPM2B_PUBLIC being used, is that possible? If their is no difference between the templates than perhaps dumping all the parameters to createprimary? We can diff what the python code is sending versus the Azure code?

Hi @williamcroberts sorry I've been busy playing with my daughter.

I think I see the issue, the unique field in the azure code is set to { 0 }, which means size field is 0. The unique field should be set to the key length in bytes of zeroes. See the azure code here:

https://github.com/Azure/azure-iot-sdk-c/blob/32942abb994f6855edc2a4dee8afd80735df4151/provisioning_client/src/sec_device_module_tpm.c#L72

I see that template code copied and pasted throughout the code base and they all appear wrong.

This covered in the spec in the templates within Annex B: https://trustedcomputinggroup.org/wp-content/uploads/TCG_IWG_EKCredentialProfile_v2p4_r3.pdf

Specifically Anex B3.3 Template L-1: RSA 2048 (Storage). The python code properly sets this unique size.

I see it, these are the values for it:

unique TPM2B_PUBLIC_KEY_RSA
size UINT16 256
buffer BYTE All 0

So to be clear the azure code needs updating and we can verify this is the issue by setting the unique field to 0 in the python code, then you should get matching EK's.

Hmmm and where do you propose to change it in python code? Because currently I'm using _, templ = create_ek_template("EK-RSA2048", nv_read) and I would probably need to generate my own template instead of this templ right?

Dvergatal avatar Aug 01 '22 20:08 Dvergatal

Hmmm and where do you propose to change it in python code? Because currently I'm using _, templ = create_ek_template("EK-RSA2048", nv_read) and I would probably need to generate my own template instead of this templ right?

Personally I would just take the returned template and zero out the unique field.

williamcroberts avatar Aug 01 '22 23:08 williamcroberts

Hmmm and where do you propose to change it in python code? Because currently I'm using _, templ = create_ek_template("EK-RSA2048", nv_read) and I would probably need to generate my own template instead of this templ right?

Personally I would just take the returned template and zero out the unique field.

I was thinking about that:D Can you tell me what is the variable name for it?

Dvergatal avatar Aug 01 '22 23:08 Dvergatal

pub.publicArea.unique.rsa = b''

williamcroberts avatar Aug 01 '22 23:08 williamcroberts

when your done size should be zero if you check it:

>>> p.publicArea.unique.rsa.size
0

williamcroberts avatar Aug 01 '22 23:08 williamcroberts

pub.publicArea.unique.rsa = b''

This is for the templ which is TPM2B_PUBLIC?

Dvergatal avatar Aug 01 '22 23:08 Dvergatal

Yes you are right:D

Dvergatal avatar Aug 01 '22 23:08 Dvergatal

HA @williamcroberts you bloody did it:D You have found the bug in Azure code :+1:

P.S. More over I suspect that this bug may be inherited to other Azure iotedge source code... P.S.2 I really have to admit it, I wouldn't find it without you, this is one of this shitty bugs, which you can look at and not notice it P.S.3 I have forgotten to thank you very much for your support

Dvergatal avatar Aug 01 '22 23:08 Dvergatal

With that change in the Python code, can you now enroll the device in Azure?

RonaldAi avatar Aug 01 '22 23:08 RonaldAi

With that change in the Python code, can you now enroll the device in Azure?

I was able to do that even without that change. The problem was that the EK returned by the tpm_device_provision was different than by python code and I was confused. Still the bug is in Azure code which needs to be fixed.

Dvergatal avatar Aug 01 '22 23:08 Dvergatal

With that change in the Python code, can you now enroll the device in Azure?

I was able to do that even without that change. The problem was that the EK returned by the tpm_device_provision was different than by python code. Still the bug is in Azure code which needs to be fixed.

I'm trying to understand if a fix in the client side code will also require a change in the server side code. That's why I was asking if the device enrollment worked with and/or without the change. If it worked without the change, then it appears that a client side fix will work independent from server side changes.

RonaldAi avatar Aug 01 '22 23:08 RonaldAi

With that change in the Python code, can you now enroll the device in Azure?

I was able to do that even without that change. The problem was that the EK returned by the tpm_device_provision was different than by python code. Still the bug is in Azure code which needs to be fixed.

I'm trying to understand if a fix in the client side code will also require a change in the server side code. That's why I was asking if the device enrollment worked with and/or without the change. If it worked without the change, then it appears that a client side fix will work independent from server side changes.

Ah, I get your point. I dunno yet. We would have to verify it, making some more tests.

Let me check one thing, because I have also noticed that, if iotedge service is up and running, it creates by itself this EK and SRK keys on tpm.

Dvergatal avatar Aug 01 '22 23:08 Dvergatal

OK, I have verified it and it creates the values stored before, so it is perfectly fine :+1:

I suspect that it was working, because this wrong EK, was still a proper key, but it was not the root key of the TPM, maybe @williamcroberts, have something in addition in EK+SRK scheme?

Dvergatal avatar Aug 02 '22 00:08 Dvergatal

OK, I have verified it and it creates the values stored before, so it is perfectly fine +1

I suspect that it was working, because this wrong EK, was still a proper key, but it was not the root key of the TPM, maybe @williamcroberts, have something in addition in EK+SRK scheme?

The EK generated by Azure IoT shouldn't verify against the EK Certificate issued by the manufacturer.

williamcroberts avatar Aug 02 '22 13:08 williamcroberts

@williamcroberts so the connection with Azure shouldn't work right?

Dvergatal avatar Aug 02 '22 14:08 Dvergatal

@williamcroberts so the connection with Azure shouldn't work right?

Depends on how they verify everything. But if they verify the EK to the manufacturer certificate then no, it shouldn't work. But I've seen designs where you just enroll this key pair, so it's arbitrary.

williamcroberts avatar Aug 02 '22 14:08 williamcroberts

AFAICT Azure code is wrong... but I don't have the time to find and fix all their templates.

williamcroberts avatar Aug 02 '22 14:08 williamcroberts