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

Using activate_credential #487

Closed
janwytze opened this issue Dec 13, 2022 · 15 comments
Closed

Using activate_credential #487

janwytze opened this issue Dec 13, 2022 · 15 comments

Comments

@janwytze
Copy link
Contributor

I have an API that creates a credential for me, the result has been tested using the tpm2_activatecredential command. I'm now trying to make a Python implementation for this. I'm trying to base it on the activate_credential unittest and the tpm2-tools command example. But I'm already stuck for more than a day :(.

I'm using the following code to load the endorsement key:

with ESAPI() as api:
    nv_read = NVReadEK(api)

    # Using swtpm 0.8.0, so I have an ECC384 cert.
    _, ecc_template = create_ek_template("EK_HIGH_ECC384", nv_read)
    ek_handle, _, _, _, _ = api.create_primary(
        TPM2B_SENSITIVE_CREATE(), ecc_template, ESYS_TR.ENDORSEMENT
    )

I'm using the next code go generate and load an attestation key.

with ESAPI() as api:
    private, public, _, _, _ = api.create(endorsement_key, None, in_public="ECC256")
    ak_handle = api.load(ek_handle, private, public)

My API creates an id_object and a encrypted_secret. The following code should decode them:

with ESAPI() as api:
    decrypted_digest = api.activate_credential(ak_handle, ek_handle, id_object, encrypted_secret)

But I receive the following error:

WARNING:esys:src/tss2-esys/api/Esys_NV_ReadPublic.c:309:Esys_NV_ReadPublic_Finish() Received TPM Error 
ERROR:esys:src/tss2-esys/esys_tr.c:209:Esys_TR_FromTPMPublic_Finish() Error NV_ReadPublic ErrorCode (0x0000018b) 
ERROR:esys:src/tss2-esys/esys_tr.c:320:Esys_TR_FromTPMPublic() Error TR FromTPMPublic ErrorCode (0x0000018b) 
ERROR:esys:src/tss2-esys/esys_iutil.c:1096:esys_GetResourceObject() Error: Esys handle does not exist (70018). 
ERROR:esys:src/tss2-esys/api/Esys_ActivateCredential.c:179:Esys_ActivateCredential_Async() activateHandle unknown. ErrorCode (0x00070018) 
ERROR:esys:src/tss2-esys/api/Esys_ActivateCredential.c:82:Esys_ActivateCredential() Error in async function ErrorCode (0x00070018) 
esapi:The ESYS_TR resource object is bad

So my question in one sentence is: How can I create/load the endorsement key and attestation key correctly, and activate a credential?

I'm aware that the attestation key does not meet the spec and that I'm not using an authsession and policy.


Sorry to bother you with these "how to use" questions.

@whooo
Copy link
Contributor

whooo commented Dec 13, 2022

Do you run all the commands under the same with ESAPI() as api: context?

@janwytze
Copy link
Contributor Author

Do you run all the commands under the same with ESAPI() as api: context?

Yes, I separated them here for readability

@whooo
Copy link
Contributor

whooo commented Dec 13, 2022

Does it work with EK_ECC256? Do you use the right policy?

@janwytze
Copy link
Contributor Author

Does it work with EK_ECC256? Do you use the right policy?

My TPM emulator does not have a EK_ECC256 key, so I can't test that easily right now.

The code given is what I use, so I'm not using a policy at all. I will now test it replicating the policy used in the tpm2_createak command.

I did find out I did not use the same ESAPI context for all calls, when I do use the same context I get the following error in the TSS2_Exception occuring in the activate_credential call:

tpm:parameter(2):structure is the wrong size

@whooo
Copy link
Contributor

whooo commented Dec 13, 2022

right, so encrypted_secret should be the second parameter, what is the type, content and size of encrypted_secret?

@janwytze
Copy link
Contributor Author

janwytze commented Dec 13, 2022

right, so encrypted_secret should be the second parameter, what is the type, content and size of encrypted_secret?

The type is TPM2B_ENCRYPTED_SECRET, the length is 48, and the hex value is 07493c3985e6171f30f3a4840f6f5fe1bd4ee5ee2b41d2f059d6ab81a41c41357bd5d2ffd10ee096a9720ae662e92577.

@whooo
Copy link
Contributor

whooo commented Dec 13, 2022

the size seems small, when doing some basic testing on my laptop (with 256 bit curves), what size is your credential blob?

@janwytze
Copy link
Contributor Author

janwytze commented Dec 13, 2022

the size seems small, when doing some basic testing on my laptop (with 256 bit curves), what size is your credential blob?

The TPM2B_ID_OBJECT reports a size of 32 (44 bytes before unmarshal) with aa8db023c1a988476475b34f6999bf79fd738f6298e87b76069de8180cf288fa as content.

The TPM2B_ENCRYPTED_SECRET has a byte size of 100 before the unmarshal, the 48 is reported by the size attribute.


I've rewritten it in a single file using EC-256 and put it on a Raspberry Pi with a TPM chip and found the following:

    with ESAPI() as api:
        # --------------------
        # This does not work: "authValue or authPolicy is not available for selected entity"
        nv_read = NVReadEK(api)
        _, rsa_template = create_ek_template("EK-ECC256", nv_read)
        ek_handle, ek_public, _, _, _ = api.create_primary(TPM2B_SENSITIVE_CREATE(), rsa_template, ESYS_TR.ENDORSEMENT)

        # This works.
        ek_handle, ek_public, _, _, _ = api.create_primary(None, "ecc256", ESYS_TR.ENDORSEMENT)
        # --------------------

        # Create AK.
        private, ak_public, creation_data, digest, creation = api.create(ek_handle, None, in_public="ecc256")
        ak_handle = api.load(ek_handle, private, ak_public)

        # Should be done server side.
        id_object, encrypted_secret = make_credential(ek_public, b"goedendag", ak_public.get_name())

        decrypted_digest = api.activate_credential(ak_handle, ek_handle, id_object, encrypted_secret)
        decrypted_bytes = bytes(decrypted_digest)

        # Prints goedendag.
        print(str(decrypted_bytes))

Creating the EK with create_ek_template seems to cause some issues (Also on rsa2048). The problem is that I need the create_ek_template function because I need an Endorsement Key that is equal to the one in the certificate.

@whooo
Copy link
Contributor

whooo commented Dec 13, 2022

The following works for me:

from tpm2_pytss.ESAPI import ESAPI                                                                                                                                                                                                     
from tpm2_pytss.utils import make_credential, NVReadEK, create_ek_template                                                                                                                                                             
from tpm2_pytss.types import TPM2B_SENSITIVE_CREATE, TPMT_SYM_DEF                                                                                                                                                                      
from tpm2_pytss.constants import ESYS_TR, TPM2_SE, TPM2_ALG, TPMA_SESSION                                                                                                                                                              
                                                                                                                                                                                                                                       
                                                                                                                                                                                                                                       
def low_ek_session(ectx):                                                                                                                                                                                                              
    symmetric = TPMT_SYM_DEF(algorithm=TPM2_ALG.NULL)                                                                                                                                                                                  
    session = ectx.start_auth_session(                                                                                                                                                                                                 
        tpm_key=ESYS_TR.NONE,                                                                                                                                                                                                          
        bind=ESYS_TR.NONE,                                                                                                                                                                                                             
        session_type=TPM2_SE.POLICY,                                                                                                                                                                                                   
        symmetric=symmetric,                                                                                                                                                                                                           
        auth_hash=TPM2_ALG.SHA256,                                                                                                                                                                                                     
    )                                                                                                                                                                                                                                  
    ectx.policy_secret(ESYS_TR.ENDORSEMENT, session, b"", b"", b"", 0)                                                                                                                                                                 
    return session                                                                                                                                                                                                                     
                                                                                                                                                                                                                                       
with ESAPI() as ectx:                                                                                                                                                                                                              
    nvr = NVReadEK(ectx)                                                                                                                                                                                                               
    _, templ = create_ek_template("EK-ECC256", nvr)                                                                                                                                                                                    
    parent, ppub, _, _, _ = ectx.create_primary(TPM2B_SENSITIVE_CREATE(), templ)                                                                                                                                                       
    session = low_ek_session(ectx)                                                                                                                                                                                                     
    priv, pub, _, _, _ = ectx.create(parent, None, "ecc256", session1=session)                                                                                                                                                         
    session = low_ek_session(ectx)                                                                                                                                                                                                     
    handle = ectx.load(parent, priv, pub, session1=session)                                                                                                                                                                            
    cred, secret = make_credential(ppub, b"falafel", pub.get_name())                                                                                                                                                                   
    print(len(cred), len(secret))                                                                                                                                                                                                      
    session = low_ek_session(ectx)                                                                                                                                                                                                     
    out = ectx.activate_credential(handle, parent, cred, secret, session2=session)                                                                                                                                                     
    print(bytes(out))  

Excluding the warnings/errors produced by tpm2-tss when running NVReadEK, I get:

43 68
b'falafel'

@janwytze
Copy link
Contributor Author

Thank you very much!!! When I find out how to extract the right TPM2B_PUBLIC from the certificate bytes I will post it here!!

@whooo
Copy link
Contributor

whooo commented Dec 13, 2022

There is a simple chance that the higher EK templates are wrong, I don't think they have been tested on actual hardware

@janwytze
Copy link
Contributor Author

janwytze commented Dec 14, 2022

The device that I am developing for only has the low EK templates, so I should just configure my emulator differently. The following code uses the EK certificate to make a credential, not the TPM2B_PUBLIC generated by create_primary command.

from cryptography.hazmat.primitives import serialization
from cryptography.x509 import load_der_x509_certificate
from tpm2_pytss import (
    ESAPI,
    ESYS_TR,
    TPM2_ALG,
    TPM2_SE,
    TPM2B_PUBLIC,
    TPM2B_SENSITIVE_CREATE,
    TPMT_SYM_DEF,
)
from tpm2_pytss.utils import NVReadEK, create_ek_template, make_credential


def low_ek_session(ectx: ESAPI):
    session = ectx.start_auth_session(
        tpm_key=ESYS_TR.NONE,
        bind=ESYS_TR.NONE,
        session_type=TPM2_SE.POLICY,
        symmetric=TPMT_SYM_DEF(algorithm=TPM2_ALG.NULL),
        auth_hash=TPM2_ALG.SHA256,
    )
    ectx.policy_secret(ESYS_TR.ENDORSEMENT, session, b"", b"", b"", 0)
    return session


with ESAPI() as ectx:
    # Load EK and EK cert.
    nv_read = NVReadEK(ectx)
    cert_bytes, ec_template = create_ek_template("EK-ECC256", nv_read)
    cert = load_der_x509_certificate(cert_bytes)
    ek_handle, _, _, _, _ = ectx.create_primary(
        TPM2B_SENSITIVE_CREATE(), ec_template, ESYS_TR.ENDORSEMENT
    )

    # Create and load AK.
    session = low_ek_session(ectx)
    private, ak_public, creation_data, digest, creation = ectx.create(
        ek_handle, None, in_public="ecc256", session1=session
    )
    session = low_ek_session(ectx)
    ak_handle = ectx.load(ek_handle, private, ak_public, session1=session)

    # Load the public key from the certificate, so the server side only needs to receive the certificate.
    ek_public = TPM2B_PUBLIC.from_pem(
        cert.public_bytes(encoding=serialization.Encoding.DER),
        symmetric="aes128cfb",
    )
    id_object, encrypted_secret = make_credential(
        ek_public, b"dankjewel!", ak_public.get_name()
    )

    session = low_ek_session(ectx)
    decrypted_digest = ectx.activate_credential(
        ak_handle, ek_handle, id_object, encrypted_secret, session2=session
    )
    print(str(bytes(decrypted_digest)))

There is a simple chance that the higher EK templates are wrong, I don't think they have been tested on actual hardware

I could not run this code in the simulator using the EK-HIGH-ECC384 template. I get an tpm:session(1):a policy check failed error. But I don't understand policies that well yet so I don't know if the problem lays at the session, or at the EK extraction part.

@whooo
Copy link
Contributor

whooo commented Dec 14, 2022

The policy setup for high EK templates are different, I'll see if I can create some example code

@janwytze
Copy link
Contributor Author

janwytze commented Dec 16, 2022

I figured out how to create a P256 cert in swtpm, so I don't need the high EK templates anymore :)

tpm2_createek -G ecc -c - -u ek.pub -f der
PUBLIC_KEY=$(openssl ec -pubin -in ek.pub -inform DER -pubout -text | head -n 7 | tail -n 5 | tr -d '[:space:]:' | tail -c128)
PUBLIC_KEY_X=$(echo -n $PUBLIC_KEY | head -c64)
PUBLIC_KEY_Y=$(echo -n $PUBLIC_KEY | tail -c64)

swtpm_localca --tpm2 --type ek --tpm-manufacturer 00001014 --tpm-model swtpm --tpm-version 20221208 --tpm-spec-family 2.0 --tpm-spec-level 0 --tpm-spec-revision 146 --ek x=$PUBLIC_KEY_X,y=$PUBLIC_KEY_Y,id=secp256r1

tpm2_nvdefine 0x01c0000a -C p -a 'ppwrite|writedefine|ppread|ownerread|authread|no_da|platformcreate' -s $(stat --printf="%s" ek.cert)
tpm2_nvwrite 0x01c0000a -C p -i ek.cert
tpm2_nvwritelock 0x01c0000a -C p

I could not get the higher templates to work. I changed the low_ek_session function to use SHA384 and use 256 AES bits. Which I found in B.4.6 Template H-3: ECC NIST P384 (Storage)

@whooo
Copy link
Contributor

whooo commented Dec 20, 2022

Created #489 so this isn't forgotten, I'll see if I have any time for it during my Christmas vacation.

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

2 participants