tpm2-pytss
tpm2-pytss copied to clipboard
[Feedback] Function tr_from_tpmpublic is not easy enough to discover
Hello,
Here is some feedback about what it felt like to use tpm2-pytss 1.0.0-rc0 to perform operations such as reading a SRK or a NV index. Let's start with trying to read a Storage Root Key stored at 0x81000000
. In a shell, this is easy:
$ tpm2_readpublic -c 0x81000000
...
name-alg:
value: sha256
raw: 0xb
attributes:
value: fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt
raw: 0x30072
type:
value: rsa
raw: 0x1
exponent: 65537
bits: 2048
...
But with tpm2-pytss, this is not as straightforward:
>>> from tpm2_pytss import *
>>> ectx = ESAPI()
>>> ectx.read_public(0x81000000)
Traceback (most recent call last):
...
TypeError: expected object_handle to be type ESYS_TR, got <class 'int'>
>>> ectx.read_public(ESYS_TR(0x81000000))
ERROR:esys:src/tss2-esys/esys_iutil.c:1095:esys_GetResourceObject() Error: Esys handle does not exist (70018).
ERROR:esys:src/tss2-esys/api/Esys_ReadPublic.c:170:Esys_ReadPublic_Async() objectHandle unknown. ErrorCode (0x00070018)
ERROR:esys:src/tss2-esys/api/Esys_ReadPublic.c:81:Esys_ReadPublic() Error in async function ErrorCode (0x00070018)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/git/tpm2_pytss/ESAPI.py", line 933, in read_public
_chkrc(
File "/git/tpm2_pytss/internal/utils.py", line 24, in _chkrc
raise TSS2_Exception(rc)
tpm2_pytss.TSS2_Exception.TSS2_Exception: esapi:The ESYS_TR resource object is bad
Reading the documentation of read_public
does not help. And it is actually wrong! https://github.com/tpm2-software/tpm2-pytss/blob/f79d3be51a0c74ac0cd60c7fe34dcb55b7520197/tpm2_pytss/ESAPI.py#L888-L907
This documentation describes args in_private
and in_public
, which do not exist, and does not describe object_handle
.
After more research, I discovered that tpm2-tss uses internal resource handlers and that tr_from_tpmpublic
can be used to map a TPM handle to such a resource:
>>> object_handle = ectx.tr_from_tpmpublic(0x81000000)
>>> hex(object_handle)
'0x40418477'
>>> ectx.read_public(object_handle)
(<tpm2_pytss.types.TPM2B_PUBLIC object at 0x7f67583c8340>, <tpm2_pytss.types.TPM2B_NAME object at 0x7f67583d22b0>, <tpm2_pytss.types.TPM2B_NAME object at 0x7f67583d23a0>)
>>> pub = _[0].publicArea
>>> ectx.tr_close(object_handle)
>>> pub.type
0
>>> str(pub.type)
'error'
>>> hex(pub.objectAttributes)
'0xf57385ea'
>>> pub.parameters.rsaDetail.keyBits
2048
>>> bytes(pub.unique.rsa).hex()
...
The type
and objectAttributes
field contain wrong values (and I do not know why, but my intuition tells me this feels like a use-after-free issue) but I managed to recover my SRK in Python.
On the same TPM, I also have an EK certificate stored at NV index 0x01c00002
. To read it using ectx.nv_read_public
and ectx.nv_read
, I also needed to call ectx.tr_from_tpmpublic(0x01c00002)
first.
How are users of tpm2-pytss expected to discover they need to call tr_from_tpmpublic
(and tr_close
) when using objects through TPM handles? Currently neither the code, the documentation nor the tests contain references to these use-cases, which seem natural for people coming from tpm2-tools.
I suggest adding some words about this use-case in the documentation of functions read_public
and nv_read_public
(and maybe in every nv_...
function too), such as:
object_handle(ESYS_TR): Handle of the object. It can be created from a TPM handle using tr_from_tpmpublic/tr_close.
I also suggest adding a test case which defines a SRK at a defined handle (such as 0x81000000
) and reads it using ectx.tr_from_tpmpublic
, and another one which does the same with a NV index. What do you think?
Here is some feedback about what it felt like to use tpm2-pytss 1.0.0-rc0 to perform operations such as reading a SRK or a NV index. Let's start with trying to read a Storage Root Key stored at
0x81000000
. In a shell, this is easy:$ tpm2_readpublic -c 0x81000000 ... name-alg: value: sha256 raw: 0xb attributes: value: fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt raw: 0x30072 type: value: rsa raw: 0x1 exponent: 65537 bits: 2048 ...
But with tpm2-pytss, this is not as straightforward:
>>> from tpm2_pytss import * >>> ectx = ESAPI() >>> ectx.read_public(0x81000000) Traceback (most recent call last): ... TypeError: expected object_handle to be type ESYS_TR, got <class 'int'> >>> ectx.read_public(ESYS_TR(0x81000000)) ERROR:esys:src/tss2-esys/esys_iutil.c:1095:esys_GetResourceObject() Error: Esys handle does not exist (70018). ERROR:esys:src/tss2-esys/api/Esys_ReadPublic.c:170:Esys_ReadPublic_Async() objectHandle unknown. ErrorCode (0x00070018) ERROR:esys:src/tss2-esys/api/Esys_ReadPublic.c:81:Esys_ReadPublic() Error in async function ErrorCode (0x00070018) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/git/tpm2_pytss/ESAPI.py", line 933, in read_public _chkrc( File "/git/tpm2_pytss/internal/utils.py", line 24, in _chkrc raise TSS2_Exception(rc) tpm2_pytss.TSS2_Exception.TSS2_Exception: esapi:The ESYS_TR resource object is bad
tpm2_readpublic can make sensible assumptions about it's arguments , I don't think we can using ESAPI.
After more research, I discovered that tpm2-tss uses internal resource handlers and that
tr_from_tpmpublic
can be used to map a TPM handle to such a resource: Yes, that's according the to the ESAPI specification.>>> object_handle = ectx.tr_from_tpmpublic(0x81000000) >>> hex(object_handle) '0x40418477' >>> ectx.read_public(object_handle) (<tpm2_pytss.types.TPM2B_PUBLIC object at 0x7f67583c8340>, <tpm2_pytss.types.TPM2B_NAME object at 0x7f67583d22b0>, <tpm2_pytss.types.TPM2B_NAME object at 0x7f67583d23a0>) >>> pub = _[0].publicArea >>> ectx.tr_close(object_handle) >>> pub.type 0 >>> str(pub.type) 'error' >>> hex(pub.objectAttributes) '0xf57385ea' >>> pub.parameters.rsaDetail.keyBits 2048 >>> bytes(pub.unique.rsa).hex() ...
I'm not able to reproduce this issue, which version/from which commit are you using? Also could you try:
from tpm2_pytss.internal import type_mapping
If it's OK, you should not get an error.
On the same TPM, I also have an EK certificate stored at NV index
0x01c00002
. To read it usingectx.nv_read_public
andectx.nv_read
, I also needed to callectx.tr_from_tpmpublic(0x01c00002)
first.How are users of tpm2-pytss expected to discover they need to call
tr_from_tpmpublic
(andtr_close
) when using objects through TPM handles? Currently neither the code, the documentation nor the tests contain references to these use-cases, which seem natural for people coming from tpm2-tools. The primary difference is that tpm2-tools implements tools, while tpm2-pytss provide APIs, I don't think there is any way around having some familiarity with ESAPI (or FAPI) when using tpm2-pytss.
I suggest adding some words about this use-case in the documentation of functions
read_public
andnv_read_public
(and maybe in everynv_...
function too), such as:object_handle(ESYS_TR): Handle of the object. It can be created from a TPM handle using tr_from_tpmpublic/tr_close.
I think adding some examples would be better (outside the method documentation that is. I'm already thinking of writing some example code touching different parts of the different APIs and will keep your input in mind
I also suggest adding a test case which defines a SRK at a defined handle (such as
0x81000000
) and reads it usingectx.tr_from_tpmpublic
, and another one which does the same with a NV index. What do you think?
While we don't have any tests using tr_from_tpmpublic for a NV index, there is test_evict_control in test/test_esapi.py
tpm2_readpublic can make sensible assumptions about it's arguments , I don't think we can using ESAPI.
All right, tpm2_readpublic
tools performs some "under-the-hood magic" which is not as straightforward with ESAPI.read_public
function. Nevertheless my point was that it would be helpful to have something which tells new users of tpm2-pytss bindings how to use ESAPI.read_public
in a way which mimics tpm2_readpublic
. More precisely it would help migrating projects using subprocess.check_output("tpm2_readpublic ...")
to tpm2-pytss.
I'm not able to reproduce this issue, which version/from which commit are you using?
I reported the issue in another GitHub issue (https://github.com/tpm2-software/tpm2-pytss/issues/299), which was fixed in https://github.com/tpm2-software/tpm2-pytss/pull/300.
I think adding some examples would be better (outside the method documentation that is. I'm already thinking of writing some example code touching different parts of the different APIs and will keep your input in mind
Great!
While we don't have any tests using tr_from_tpmpublic for a NV index, there is test_evict_control in test/test_esapi.py
I can work on extending a test using NV index (such as test_plain_nv_define_write_read_undefine
) to use tr_from_tpmpublic
too.