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

utils: add function to generate an EK #279

Merged
merged 1 commit into from
Feb 16, 2022

Conversation

whooo
Copy link
Contributor

@whooo whooo commented Dec 6, 2021

I have tested the standard ECC and RSA templates towards a real TPM, but the the NV nonce/template is written according to the specification/tpm2-tools so I'm not 100% they work in real life.

Draft until I add types to arguments and documentation.

Related to #203

@codecov
Copy link

codecov bot commented Dec 6, 2021

Codecov Report

Merging #279 (950a461) into master (c0e9f65) will increase coverage by 0.02%.
The diff coverage is 96.39%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #279      +/-   ##
==========================================
+ Coverage   93.59%   93.61%   +0.02%     
==========================================
  Files          15       17       +2     
  Lines        4665     5212     +547     
  Branches      592      750     +158     
==========================================
+ Hits         4366     4879     +513     
- Misses        218      239      +21     
- Partials       81       94      +13     
Impacted Files Coverage Δ
tpm2_pytss/utils.py 92.07% <94.87%> (+2.41%) ⬆️
tpm2_pytss/internal/templates.py 100.00% <100.00%> (ø)
tpm2_pytss/internal/utils.py 84.96% <0.00%> (-1.93%) ⬇️
tpm2_pytss/internal/crypto.py 92.36% <0.00%> (-1.77%) ⬇️
tpm2_pytss/types.py 95.31% <0.00%> (-0.12%) ⬇️
tpm2_pytss/ESAPI.py 98.10% <0.00%> (-0.01%) ⬇️
tpm2_pytss/encoding.py 95.31% <0.00%> (ø)
tpm2_pytss/constants.py 99.28% <0.00%> (+0.03%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c0e9f65...950a461. Read the comment docs.

Copy link
Member

@williamcroberts williamcroberts left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -226,3 +227,117 @@ def unwrap(
)

return s


def create_ek(ectx, ekalg=TPM2_ALG.ECC, session=ESYS_TR.NONE):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would change this slightly to get_ek_template and be able to pass session1, session2, and session3 here so folks pass everything into the esys calls and then I would have the createprimary call be by the user. This way the session1, which is auth for the ENDORSEMENT hierarchy can be set by the caller to an HMAC session, audit session, etc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh and add type hints to the parameters.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@idesai could you weigh in on this API design with your thoughts as well?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. Something to consider... because it uses a TCG EK credential specific value for authorization policy and attributes get_tcg_ek_template might be a good name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept only one session as the only usefulness I see of passing a session after removing create_primary is for audit and encrypt sessions.
As for the name, I prefer to keep it as close to tpm2_createek as reasonable, to keep it slightly familiar for tpm2-tools users, but I can go either way.

Copy link
Member

@williamcroberts williamcroberts Dec 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can have separate audit and encrypt sessions. I was trying to future proof the API a bit since session1,2,3 would just be the defaults and only if folks are interested in that control will they use them.

Copy link
Contributor Author

@whooo whooo Dec 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I'll change it to session1 and session2, but I'm worried about session3 as that wouldn't be passed to nv_read and would break expectations for session3

@whooo whooo force-pushed the create_ek branch 2 times, most recently from d16411f to 5168cd5 Compare December 6, 2021 20:16
@whooo whooo marked this pull request as ready for review December 6, 2021 20:39
mtempl = b""
while left > 0:
off = tpub.nvPublic.dataSize - left
size = 0x300 if left > 0x300 else left
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this magic number from?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought that was the minimum allowed for TPM2_PT_NV_BUFFER_MAX, but when I checked again and it's apparently 512, not 768 for the PC client profile

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This value should ideally be read from the capability structure

while more:
more, data = ectx.get_capability(
TPM2_CAP.HANDLES,
TPM2_HT.NV_INDEX << 24,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this one include the shift: TPM2_HC.HR_NV_INDEX

nonce = nonce + bytes(data)
left = left - len(data)

if len(nonce) > 0 and template.publicArea.type == TPM2_ALG.RSA:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This padding is only done on RSA 2048 and ECC NIST P256 per the 2.2.1.6 "TPMT_PUBLIC Calculation"

Their are other keys possible, so I don't think we want to always do this AFAICT.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code currently only supports RSA 2048 and NIST P256 keys (unless the TPM manufacturer has put a non-standard template in one of the NV template areas)

session: (ESYS_TR): Optional session, for encrypt/decrypt and audit sessions, defaults to ESYS_TR.NONE

Returns:
The templase as a TPM2B_PUBLIC instance
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spelling, templase --> template

@lgtm-com
Copy link

lgtm-com bot commented Dec 7, 2021

This pull request introduces 1 alert when merging c5ee5eb into 0f48299 - view on LGTM.com

new alerts:

  • 1 for Unused import

@williamcroberts
Copy link
Member

So the overall design is that it will take a callback to read the NV index and return the template. This way folks can use it with any type of TPM context (ESAPI or FAPI) and a userdata pointer. Since this is python, with closure, we don't really need to worry about passing user data just a callable.

@whooo
Copy link
Contributor Author

whooo commented Dec 15, 2021

So the overall design is that it will take a callback to read the NV index and return the template. This way folks can use it with any type of TPM context (ESAPI or FAPI) and a userdata pointer. Since this is python, with closure, we don't really need to worry about passing user data just a callable.

So would it be like something like?:

def create_ek_template(ekalg, nv_read_cb):
    defaults_here
    nonce = nv_read_cb(nonce_index)
    template = nv_read_cb(template_index)
    do_rest_here

@williamcroberts
Copy link
Member

ekalg

Yes, but I forgot to mention ekalg is a string identifier that maps to one of the static templates or one of the NV indices. Let me see if I can figure out what those will be. I don't have them offhand.

@whooo whooo marked this pull request as draft December 16, 2021 14:43
@williamcroberts
Copy link
Member

Here are the strings:

string template address nonce
EK-RSA2048 0x01c00003 0x01c00004
EK-ECC256 0x01c0000b 0x01c0000c
EK-HIGH-RSA2048 0x01c00013
EK-HIGH-ECC256 0x01c00015
EK-HIGH-ECC384 0x01c00017
EK-HIGH-ECC521 0x01c00019
EK-HIGH-ECCSM2P521 0x01c0001b
EK-HIGH-RSA3072 0x01c0001d
EK-HIGH-RSA4096 0x01c0001f

This is the callback signature:

def nvread(index)

So you could something like:

class MyNVReader:
    def __init__(ectx):
        self.ectx = ectx

    def __call__(self, nvindex):
        return self.ectx.nvread(nvindex)

get_ek("EK-RSA2048", MyNVReader())

# or something like
ectx = ESAPI()
#.....
def my_nv_read(nvindex):
    return ectx.nv_read(nvindex)

get_ek("EK-RSA2048", my_nv_read) 

@williamcroberts
Copy link
Member

If it has a nonce, it needs to call the nv reader cb for the templ and the nonce. The calling framework is responsible for handling merging those and unmarshalling everything into the final TPMT_PUBLIC.

@williamcroberts
Copy link
Member

I'm not sure what they are going to be for the default static templates. It's not clear to me in the spec if the static templates are the the same as the ones in the NV Index. So these could be mapped to unique strings or based on if callback is None.

See the templates in B.4.4 and later in:

@whooo
Copy link
Contributor Author

whooo commented Dec 16, 2021

is it clear by the specification what should happen if the NV index isn't defined (low template/nonce) on the TPM?
For example should the NV read callback just return an empty buffer in that case?

@williamcroberts
Copy link
Member

is it clear by the specification what should happen if the NV index isn't defined (low template/nonce) on the TPM? For example should the NV read callback just return an empty buffer in that case?

Whatever nvread failing for that condition would be. The framework would then use the static nonce.

@williamcroberts
Copy link
Member

Also look at section "2.2.1.3" in that EK document. It lays out a table with what is and is not allowed, and things that are discouraged.

@williamcroberts
Copy link
Member

Then the sections on LOW (2.2.1.4) and HIGH (2.2.1.5) layout more of what can be expected. Essentially for the LOW's their SHOULD be an NV index set, however if their isn't it's a default template. For the low range their MAY be a nonce to deal with. For the HIGH range, they're MAY be an NV index for the template, however if not it's the default template.

@lgtm-com
Copy link

lgtm-com bot commented Dec 17, 2021

This pull request introduces 1 alert when merging 6aaa50a into 7cd4cc0 - view on LGTM.com

new alerts:

  • 1 for Unused import

@whooo whooo force-pushed the create_ek branch 2 times, most recently from 74ffa5f to 7ec6d51 Compare December 19, 2021 11:38
@whooo
Copy link
Contributor Author

whooo commented Dec 19, 2021

@williamcroberts, when you have time could you check if the code matches the the general idea?
There are still somethings I'm unsure about for the new function specification, for example is it up to the caller or the function to check if a high range certificate NV index is defined or not?

@williamcroberts
Copy link
Member

@williamcroberts, when you have time could you check if the code matches the the general idea? There are still somethings I'm unsure about for the new function specification, for example is it up to the caller or the function to check if a high range certificate NV index is defined or not?

The callback function should check. We should probably have a specific case for this error to then go get the default template, all other errors propagate to the original caller. The framework function should never go to the TPM, only through the callback.

@lgtm-com
Copy link

lgtm-com bot commented Jan 3, 2022

This pull request introduces 1 alert when merging 41c57bc into f79d3be - view on LGTM.com

new alerts:

  • 1 for Unused import

@whooo whooo force-pushed the create_ek branch 3 times, most recently from 794556e to edde6a3 Compare January 6, 2022 03:27
@whooo whooo marked this pull request as ready for review January 6, 2022 04:53
@whooo
Copy link
Contributor Author

whooo commented Jan 6, 2022

This is the best I could come up with my understanding to the specification.
I added the certificate to the return values as it must be read in many cases regardless


def __call__(self, index: Union[int, TPM2_RH]) -> bytes:
if index not in self._indices:
raise NoSuchIndex(index)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the index was defined between the list population and now. Perhaps it's better to just try and read the NV index and look at the error code to see if it's not defined. Which also reduces the trips we need to make to the TPM. Which then, do we even need this exception class, we could just use TSS2 Exceptions that already exist.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I view the chance as low for this use case as new template/certificate areas shouldn't be created during runtime
The problem with letting the TPM throw an error is that it would break audit sessions (and possibly encrypted sessions) for the scenarios where create_ek_template looks for an index but shouldn't fail if there isn't one
We could have a constant of an TSS2_Exception instead, but something explicit and clear is needed, for example someone writes their own callback using FAPI instead which wouldn't throw the same exception

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I view the chance as low for this use case as new template/certificate areas shouldn't be created during runtime

It's an unlikely race condition, but it's a TOCTOU issue.

The problem with letting the TPM throw an error is that it would break audit sessions (and possibly encrypted sessions) for

I'm not following, what exactly breaks? Does a failing command invalidate the session?

the scenarios where create_ek_template looks for an index but shouldn't fail if there isn't one We could have a constant of an TSS2_Exception instead, but something explicit and clear is needed, for example someone writes their own callback using FAPI instead which wouldn't throw the same exception

True, or if they use their own custom TSS.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I view the chance as low for this use case as new template/certificate areas shouldn't be created during runtime

It's an unlikely race condition, but it's a TOCTOU issue.

The problem with letting the TPM throw an error is that it would break audit sessions (and possibly encrypted sessions) for

I'm not following, what exactly breaks? Does a failing command invalidate the session?

I reread the specification and I was wrong, so that can be dropped, I'll rework the code to not check the index before trying to read and raise a NoSuchIndex exception if the TSS2_Exception matches the correct error, otherwise reraise the TSS2_Exception

return f"NV index 0x{index:08x} does not exist"


class nv_read_ek:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't classes be capitalized?

@lgtm-com
Copy link

lgtm-com bot commented Feb 16, 2022

This pull request introduces 1 alert when merging e321a6d into 584dd8b - view on LGTM.com

new alerts:

  • 1 for Unused import

@williamcroberts williamcroberts merged commit a274a90 into tpm2-software:master Feb 16, 2022
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

Successfully merging this pull request may close these issues.

3 participants