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

What is the key creation template used when FAPI.create_key is called? #253

Open
gaetanww opened this issue Oct 26, 2021 · 18 comments
Open

Comments

@gaetanww
Copy link

gaetanww commented Oct 26, 2021

I want to use a primary key with both ESAPI and FAPI with the following workflow:

  1. create primary key in ESAPI and perform some operations
  2. recreate key in FAPI and use it there

However the key created in FAPI and ESAPI are not the same. I assume that's because the templates used during key creation are different. However, I can't find how to make them match.

Here's my test code:

from tpm2_pytss import *
tcti = 'swtpm'
FAPIConfig(profile_name='P_ECCP256SHA256', tcti=tcti, ek_cert_less='yes')
# I create a key in FAPI to retrieve its template
with FAPI() as fapi:
    fapi.provision()
    fapi.create_key('HE/KEY', 'system, sign, restricted')
    (blob_key, _) = fapi.get_esys_blob('HE/KEY')
# Now I create a new key with the same pub value
with ESAPI(tcti=TCTILdr(tcti)) as esapi:
    old_key = esapi.load_blob(blob_key)
    old_pub = esapi.read_public(old_key)[0]
    esapi.flush_context(old_key)
    # this deep copies the template
    new_pub = TPM2B_PUBLIC.unmarshal(old_pub.marshal())[0]
    # I assume the original template had empty unique
    new_pub.publicArea.unique.ecc.x = b''
    new_pub.publicArea.unique.ecc.y = b''
    new_key_handle, new_key_pub, _, _, _ = esapi.create_primary(TPM2B_SENSITIVE_CREATE(), new_pub, ESYS_TR.ENDORSEMENT, '', '')
    # This fails but I expected it to pass
    assert(bytes(new_key_pub.publicArea.unique.ecc.x) == bytes(old_pub.publicArea.unique.ecc.x))
@whooo
Copy link
Contributor

whooo commented Oct 27, 2021

Does have to be a primary key? I suspect that FAPI.create_key doesn't create a primary key.
My recommendation would be to create and store the key with FAPI and use get_esys_blob when you need to use it with ESAPI

@gaetanww
Copy link
Author

I suspect that FAPI.create_key doesn't create a primary key.

It does if the path of the key is directly under a hierarchy (e.g. '/HE/my-key'). There's a mention of it in the specs but it's a bit underdocumented. Anyway, it's present in the C code.

My recommendation would be to create and store the key with FAPI and use get_esys_blob when you need to use it with ESAPI

I was trying to avoid that to not have the FAPI dependency for the first step. Another solution would maybe be to FAPI.Import a primary key created with ESAPI. Do you think #240 would allow exporting a key from ESAPI and import it to FAPI

@whooo
Copy link
Contributor

whooo commented Oct 27, 2021

I suspect that FAPI.create_key doesn't create a primary key.

It does if the path of the key is directly under a hierarchy (e.g. '/HE/my-key'). There's a mention of it in the specs but it's a bit underdocumented. Anyway, it's present in the C code.

Ah, I see.

My recommendation would be to create and store the key with FAPI and use get_esys_blob when you need to use it with ESAPI

I was trying to avoid that to not have the FAPI dependency for the first step. Another solution would maybe be to FAPI.Import a primary key created with ESAPI. Do you think #240 would allow exporting a key from ESAPI and import it to FAPI

Long term I hope so, my plan is to have something the implements parts of the JSON format, those without FAPI requirements, but that will probably take a while.

    fapi.create_key('HE/ZAK', 'system, sign, restricted')
    (blob_key, _) = fapi.get_esys_blob('HE/KEY')

Just to verify, shouldn't HE/KEY be HE/ZAK in this case?

@gaetanww
Copy link
Author

Just to verify, shouldn't HE/KEY be HE/ZAK in this case?

Yes you're right, I corrected it. It was right when I tested it though :)

@whooo
Copy link
Contributor

whooo commented Oct 27, 2021

I was able to get your example working by using fapi.get_tpm_blobs instead of get_esys_blob, I'm not sure why fapi.get_esys_blob doesn't work tho

@williamcroberts
Copy link
Member

What does fapi.get_esys_blob return? Did you check return index 0 for the type, it should either be a FAPI_ESYSBLOB_CONTEXTLOAD or a FAPI_ESYSBLOB_DESERIALIZE which need to be handled differently.

load_blob is designed to handle that, but it has a default selector set to FAPI_ESYSBLOB_CONTEXTLOAD, my guess is it's handing back the FAPI_ESYSBLOB_DESERIALIZE which needs the ESYS_TR unmarshal called on it.

@gaetanww
Copy link
Author

I have update the code but I still get the same error.

Here's the new code:

from tpm2_pytss import *
tcti = 'swtpm'
FAPIConfig(profile_name='P_ECCP256SHA256', tcti=tcti, ek_cert_less='yes')
# I create a key in FAPI to retrieve its template
with FAPI() as fapi:
    fapi.provision()
    fapi.create_key('HE/KEY', 'system, sign, restricted')
    blob_key, blob_slct = fapi.get_esys_blob('HE/KEY')
# Now I create a new key with the same pub value
with ESAPI(tcti=TCTILdr(tcti)) as esapi:
    old_key = esapi.load_blob(blob_key, blob_slct)
    old_pub = esapi.read_public(old_key)[0]
    esapi.flush_context(old_key)
    # this deep copies the template
    new_pub = TPM2B_PUBLIC.unmarshal(old_pub.marshal())[0]
    # I assume the original template had empty unique
    new_pub.publicArea.unique.ecc.x = b''
    new_pub.publicArea.unique.ecc.y = b''
    new_key_handle, new_key_pub, _, _, _ = esapi.create_primary(TPM2B_SENSITIVE_CREATE(), new_pub, ESYS_TR.ENDORSEMENT, '', '')
    # This fails but I expected it to pass
    assert(bytes(new_key_pub.publicArea.unique.ecc.x) == bytes(old_pub.publicArea.unique.ecc.x))

Another strange thing is that the FAPI template contains an error value. I'm not sure if that's supposed to happen:

In [31]: TPM2_ALG.to_string(old_pub.publicArea.parameters.eccDetail.symmetric.mode.sym)
Out[31]: 'TPM2_ALG.ERROR'

@gaetanww
Copy link
Author

I was able to get your example working by using fapi.get_tpm_blobs instead of get_esys_blob, I'm not sure why fapi.get_esys_blob doesn't work tho

Oh you did, that's great :) Could you paste your snippet with the tpm blobs?

@williamcroberts
Copy link
Member

TPM2_ALG.ERROR is 0. So when a template is initialized to 0, the TPM if it reads a spot it expected to be a valid algorithm will complain.

If you want a deep copy, you can just do:

new_pub = TPM2B_PUBLIC(old_pub)

For why theyre not equal, have you thought about modifying tpm2-tss to dump the template fields out and ensure it's working as expected?

You could modify Fapi_CreateKey.c to add some print statements or something. I could try this later if I have time. However, alas, at the moment I do not.

@williamcroberts
Copy link
Member

You could also dump it with the pcap tcti and wireshark it, but I don't think it walks through the individual command fields yet. Only the common header.

@gaetanww
Copy link
Author

TPM2_ALG.ERROR is 0. So when a template is initialized to 0, the TPM if it reads a spot it expected to be a valid algorithm will complain.

Ok, thanks for the info. Does that mean FAPI creates keys with invalid algorithms? This key was created with 'system, sign, restricted'.

For why theyre not equal, have you thought about modifying tpm2-tss to dump the template fields out and ensure it's working as expected?

You could modify Fapi_CreateKey.c to add some print statements or something. I could try this later if I have time. However, alas, at the moment I do not.

Yes, I guess that's the next step. Thanks for helping me! :)

@whooo
Copy link
Contributor

whooo commented Oct 27, 2021

I was able to get your example working by using fapi.get_tpm_blobs instead of get_esys_blob, I'm not sure why fapi.get_esys_blob doesn't work tho

Oh you did, that's great :) Could you paste your snippet with the tpm blobs?

Here it is, I mostly added some code.
I don't remember why get_tpm_blobs returns cdata, it should probably return the types.

from tpm2_pytss import *
import sys
tcti = 'swtpm'
FAPIConfig(profile_name='P_ECCP256SHA256', tcti=tcti, ek_cert_less='yes')
# I create a key in FAPI to retrieve its template
with FAPI() as fapi:
    fapi.provision()
    sys.stderr.write("fapi create\n")
    fapi.create_key('HE/KEY', 'system, sign, restricted')
    sys.stderr.write("fapi post create\n")
    (blob_key, _) = fapi.get_esys_blob('HE/KEY')
    (fpub, fpriv, _) = fapi.get_tpm_blobs("HE/KEY")
# Now I create a new key with the same pub value
with ESAPI(tcti=TCTILdr(tcti)) as esapi:
    old_key = esapi.load_blob(blob_key)
    old_pub = esapi.read_public(old_key)[0]
    esapi.flush_context(old_key)
    old_pub = TPM2B_PUBLIC(_cdata=fpub)
    # this deep copies the template
    new_pub = TPM2B_PUBLIC.unmarshal(old_pub.marshal())[0]
    # I assume the original template had empty unique
    new_pub.publicArea.unique.ecc.x = b''
    new_pub.publicArea.unique.ecc.y = b''
    sys.stderr.write("esapi create\n")
    new_key_handle, new_key_pub, _, _, _ = esapi.create_primary(TPM2B_SENSITIVE_CREATE(), new_pub, ESYS_TR.ENDORSEMENT, '', '')
    sys.stderr.write("esapi post create\n")
    # This fails but I expected it to pass
    print(new_key_pub.publicArea.unique.ecc.x)
    print(old_pub.publicArea.unique.ecc.x)
    assert(new_key_pub.publicArea.authPolicy == old_pub.publicArea.authPolicy)
    assert(new_key_pub.publicArea.unique.ecc.x.size == old_pub.publicArea.unique.ecc.x.size)
    assert(new_key_pub.publicArea.unique.ecc.x == old_pub.publicArea.unique.ecc.x)
    #assert(new_pub.publicArea.objectAttributes == old_pub.publicArea.objectAttributes)

@williamcroberts
Copy link
Member

Thats odd that it's returning the raw cdata types, I don't think that's right. Also, the return signature of Tuple[Any, Any, Str] is suspect. It should really be Tuple[TPM2B_PUBLIC, TPM2B_PRIVATE, str ].

The tests could also be fortified to use them through esapi.

@whooo
Copy link
Contributor

whooo commented Nov 19, 2021

@gaetanww, are you happy with 85cd872 as a solution to this issue?

@gaetanww
Copy link
Author

I think the original issue about the template is solved.

However, we unearthed a bug in the get_esys_blob -> load_blob workflow. So I suggest we could close this issue and open a new one, either here or in tpm2-tss or both. What do you think?

@whooo
Copy link
Contributor

whooo commented Nov 26, 2021

I think the original issue about the template is solved.

However, we unearthed a bug in the get_esys_blob -> load_blob workflow. So I suggest we could close this issue and open a new one, either here or in tpm2-tss or both. What do you think?

Have you tried reproducing the bug using the C API?

@gaetanww
Copy link
Author

gaetanww commented Dec 3, 2021

No I haven't, unfortunately I don't have the bandwidth for the next few weeks, sorry...

@whooo
Copy link
Contributor

whooo commented Jan 8, 2022

I was able to recreate the issue using the C API (3.1.0), https://gist.github.com/whooo/d18c4ae5460ae22b5a71c281f6b0cfd0

I was also able to reproduce the issue using tpm2-tss master.

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