Pytss:将参数传递给 fapi_Sign() 方法 - 无效的参数类型(Fapi_Sign 类型为 uint8_t const * 的参数 4)
Pytss: passing arguments to fapi_Sign() method - invalid argument type (Fapi_Sign argument 4 of type uint8_t const *)
这是我的第一个问题,我想向所有社区问好。我总是喜欢用困难的方式来做,尽管这次我花了几个小时搜索、尝试,但没有任何东西更接近解决方案。所以。
我的任务是写关于使用 TPM 签署文件的文章。我们使用虚拟设备,并且有一个 Python 库提供来自 C 的 TPM 2.0 TSS 绑定 - Pytss。
我目前正在努力将参数传递给 fapi_Sign() 方法以签署特定文档。我已经根据文档的示例创建了上下文:
import random
import tempfile
import contextlib
from tpm2_pytss.fapi import FAPI, FAPIDefaultConfig
from tpm2_pytss.binding import *
#import tpm2_pytss.binding as bd
from tpm2_pytss.util.simulator import Simulator
def get_context():
# Create a context stack
ctx_stack = contextlib.ExitStack()
# Create a simulator
simulator = ctx_stack.enter_context(Simulator())
# Create temporary directories to separate this example's state
user_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
log_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
system_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
# Create the FAPI object
fapi = FAPI(
FAPIDefaultConfig._replace(
user_dir=user_dir,
system_dir=system_dir,
log_dir=log_dir,
tcti="mssim:port=%d" % (simulator.port,),
tcti_retry=100,
ek_cert_less=1,
)
)
# Enter the context, create TCTI connection
fapi_ctx = ctx_stack.enter_context(fapi)
# Call Fapi_Provision
fapi_ctx.Provision(None, None, None)
return fapi_ctx, ctx_stack
#
并且还生成了一对密钥:
def sign(fapi_ctx, fapi_stck, path_to_key, password, document):
# Create pair of public, private keys
fapi_ctx.CreateKey(path_to_key, None, None, password)
问题是,当我尝试调用 fapi_Sign() 方法时,我无法成功将摘要传递给它:
import hashlib
import ctypes
shaobj = hashlib.sha256()
shaobj.update(document.encode())
digest_pointer = ctypes.cast(shaobj.digest(), ctypes.POINTER(ctypes.c_uint8 * shaobj.digest_size))
digest_size = shaobj.digest_size
fapi_ctx.Sign(path_to_key, None, digest_pointer, digest_size, sig_holder, None, None, None)
输出为:
Traceback (most recent call last):
File "lab5.py", line 84, in <module>
sign(ctx, stck, "HS/lab5_key", "qwe123", doc_txt)
File "lab5.py", line 77, in sign
fapi_ctx.Sign(path_to_key, None, digest_pointer, 32, sig_holder, None, None, None)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/context.py", line 78, in wrapper
return custom_wrap(func)(self.ctxp, *args, **kwds)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/context.py", line 112, in wrapper
result = func(self, *args, **kwds)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/swig.py", line 173, in wrapper
return func(*args, **kwargs)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/exceptions.py", line 48, in wrapper
rc = func(*args, **kwargs)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/fapi_binding.py", line 14997, in Fapi_Sign
return _fapi_binding.Fapi_Sign(context, keyPath, padding, digest, digestSize, signature, signatureSize, publicKey, certificate)
TypeError: in method 'Fapi_Sign', argument 4 of type 'uint8_t const *'
经过反复尝试,我无法将 digest 参数传递给 fapi_Sign() 方法。我尝试了一切,从 ctypes 到 struct.pack,再到简单地使用 tpm2_pytss.binding 提供的任何东西(在使用 dir(tpm2_pytss.binding) 之后)。我还意识到 SWIG 并不真正喜欢 ctypes,但我很快就放弃了,因此只剩下 ctypes 示例向您展示。如果您能指出正确的方向,我们将不胜感激。
[编辑] 根据要求,在此处发布重现我的错误的代码(请注意,您必须安装所有与 tpm2 相关的库)
import random
import tempfile
import contextlib
from tpm2_pytss.fapi import FAPI, FAPIDefaultConfig
from tpm2_pytss.binding import *
import tpm2_pytss.binding as tm
from tpm2_pytss.util.simulator import Simulator
from hashlib import sha256
def get_context():
# Create a context stack
ctx_stack = contextlib.ExitStack()
# Create a simulator
simulator = ctx_stack.enter_context(Simulator())
# Create temporary directories to separate this example's state
user_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
log_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
system_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
# Create the FAPI object
fapi = FAPI(
FAPIDefaultConfig._replace(
user_dir=user_dir,
system_dir=system_dir,
log_dir=log_dir,
tcti="mssim:port=%d" % (simulator.port,),
tcti_retry=100,
ek_cert_less=1,
)
)
# Enter the context, create TCTI connection
fapi_ctx = ctx_stack.enter_context(fapi)
# Call Fapi_Provision
fapi_ctx.Provision(None, None, None)
return fapi_ctx, ctx_stack
#
def sign(fapi_ctx, fapi_stck, path_to_key, password, document):
# Create pair of public, private keys
fapi_ctx.CreateKey(path_to_key, None, None, password)
sig_holder = fapi_stck.enter_context(UINT8_PTR_PTR())
# Get SHA256 digest of the document
shaobj = sha256()
shaobj.update(document.encode())
fapi_ctx.Sign(path_to_key, None, shaobj.digest(), shaobj.digest_size, sig_holder, None, None, None)
#
document = open("example.txt", "r")
doc_txt = document.read()
ctx, stck = get_context()
sign(ctx, stck, "HS/some_key", "qwe123", doc_txt)
example.txt 这里只是一些将被签名的随机文本文件。
我最近花了几个小时调查这个问题,我想把它放在这里,也许它会给某些人敲响警钟。我没有编辑我的答案,因为 1 - 它已经很长了 2 - 我实际上部分解决了问题,但其他人在这个过程中出现了,所以这是一个部分答案,但我想仍然是一个答案。
好吧,所以我从稍微不同的角度开始。看看这个代码(它实际上是他们 documentation 的一个例子。假设我们有这个:
import random
import tempfile
import contextlib
from tpm2_pytss.fapi import FAPI, FAPIDefaultConfig
from tpm2_pytss.binding import *
import tpm2_pytss.binding as tm
#import tpm2_pytss.binding as bd
from tpm2_pytss.util.simulator import Simulator
from hashlib import sha256
from ctypes import c_uint8, cast, POINTER, pointer
from numpy import array
def get_context():
# Create a context stack
ctx_stack = contextlib.ExitStack()
# Create a simulator
simulator = ctx_stack.enter_context(Simulator())
# Create temporary directories to separate this example's state
user_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
log_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
system_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
# Create the FAPI object
fapi = FAPI(
FAPIDefaultConfig._replace(
user_dir=user_dir,
system_dir=system_dir,
log_dir=log_dir,
tcti="mssim:port=%d" % (simulator.port,),
tcti_retry=100,
ek_cert_less=1,
)
)
# Enter the context, create TCTI connection
fapi_ctx = ctx_stack.enter_context(fapi)
# Call Fapi_Provision
fapi_ctx.Provision(None, None, None)
XXXX
get_context()
抱歉有点误导函数名称 get_context(),该函数最初只是为了 return fapi_ctx 和 ctx_stack 对象,但我开始在这里进行试验只是为了保持代码简单。 'XXXX' 部分将包含我想展示的不同实验。如果将其替换为:
arr = ctx_stack.enter_context(UINT8_PTR_PTR())
rand = to_bytearray(4, fapi_ctx.GetRandom(4, arr))
print(rand)
然后 运行ning 代码给出:
$> sudo python3 experiments.py
WARNING:fapi:src/tss2-fapi/ifapi_io.c:282:ifapi_io_check_create_dir() Directory /tmp/tmpctp8i4zr/policy does not exist, creating
bytearray(b'1L\xfd(')
如您所见,运行将代码 returned 为 4 个随机字节的字节数组。伟大的。这让我开始思考,也许我们需要传递 UINT8_PTR() 和 UINT8_PTR_PTR() 与函数 enter_context() 的某种组合原来的问题。但首先,让我们尝试不涉及上下文。现在,将 'XXXX' 替换为:
# Create pair of keys
fapi_ctx.CreateKey("HS/my_keypair", None, None, None)
# Prepare values to sign the file
my_digest = UINT8_PTR()
my_sig_handler = UINT8_PTR_PTR()
fapi_ctx.Sign("HS/my_keypair", None, my_digest, 0, my_sig_handler, None, None, None)
当我们像以前一样运行时,我们现在得到这个错误:
WARNING:fapi:src/tss2-fapi/ifapi_io.c:282:ifapi_io_check_create_dir() Directory /tmp/tmpvhxrb_1i/policy does not exist, creating
Traceback (most recent call last):
File "lab5.py", line 185, in <module>
ctx, stck = get_context()
File "lab5.py", line 50, in get_context
fapi_ctx.Sign("HS/lab5_key", None, my_digest, 0, my_sig_handler, None, None, None)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/context.py", line 78, in wrapper
return custom_wrap(func)(self.ctxp, *args, **kwds)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/context.py", line 112, in wrapper
result = func(self, *args, **kwds)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/swig.py", line 161, in wrapper
modifed = modify(parameter.name, parameter.annotation, value)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/binding.py", line 428, in call_mod_context_managed_pointer_class
raise UsedWithoutEnteringContext(name)
tpm2_pytss.binding.UsedWithoutEnteringContext: digest
因此 pytss 库希望我们为传递给函数的变量提供一些上下文。现在,我们使用的不是 XXXX 部分:
# Create pair of keys
fapi_ctx.CreateKey("HS/my_keypair", None, None, None)
# Prepare values to sign the file
my_digest = ctx_stack.enter_context(UINT8_PTR())
my_sig_handler = ctx_stack.enter_context(UINT8_PTR_PTR())
fapi_ctx.Sign("HS/my_keypair", None, my_digest, 0, my_sig_handler, None, None, None)
当这是 运行 时,我们现在得到这个错误:
WARNING:fapi:src/tss2-fapi/ifapi_io.c:282:ifapi_io_check_create_dir() Directory /tmp/tmp9y61w0rl/policy does not exist, creating
ERROR:fapi:src/tss2-fapi/fapi_util.c:3617:ifapi_get_sig_scheme() ErrorCode (0x0006000b) Invalid digest size.
ERROR:fapi:src/tss2-fapi/fapi_util.c:2742:ifapi_key_sign() Get signature scheme ErrorCode (0x0006000b)
ERROR:fapi:src/tss2-fapi/api/Fapi_Sign.c:299:Fapi_Sign_Finish() Fapi sign. ErrorCode (0x0006000b)
ERROR:fapi:src/tss2-fapi/api/Fapi_Sign.c:130:Fapi_Sign() ErrorCode (0x0006000b) Key_Sign
Traceback (most recent call last):
File "lab5.py", line 185, in <module>
ctx, stck = get_context()
File "lab5.py", line 50, in get_context
fapi_ctx.Sign("HS/my_keypair", None, my_digest, 0, my_sig_handler, None, None, None)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/context.py", line 78, in wrapper
return custom_wrap(func)(self.ctxp, *args, **kwds)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/context.py", line 112, in wrapper
result = func(self, *args, **kwds)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/swig.py", line 173, in wrapper
return func(*args, **kwargs)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/exceptions.py", line 50, in wrapper
raise TPM2Error(rc)
tpm2_pytss.exceptions.TPM2Error: fapi:A parameter has a bad value
所以现在函数 Sign() 似乎接受了我们的参数类型,但它在尝试签署空摘要时崩溃了。那么我们如何将文件的签名传输到这个 PTR_SOMETHING() 中呢?首先,让我们提醒自己签名的样子。独立拥有此代码运行:
from hashlib import sha256
document = open("text.doc", "r")
doc_txt = document.read()
shaobj = sha256()
shaobj.update(doc_txt.encode())
print(shaobj.digest())
shaobj 的 digest() 方法将 return 只是一堆字节 b"(...)" 包含我们的随机文本文件的散列内容.我们能否以某种方式将这些字节传递给 my_digest 对象?让我们来看看。将 XXXX 部分替换为:
random_bytes = b"\x01\x02\x03\x04"
my_digest = ctx_stack.enter_context(UINT8_PTR(random_bytes))
给我们这个错误:
(...) <=== I decided to omit the first line containing "(...) does not exist, creating"
Traceback (most recent call last):
File "lab5.py", line 186, in <module>
ctx, stck = get_context()
File "lab5.py", line 48, in get_context
my_digest = ctx_stack.enter_context(UINT8_PTR(random_bytes))
File "/usr/lib/python3.8/contextlib.py", line 425, in enter_context
result = _cm_type.__enter__(cm)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/swig.py", line 46, in __enter__
self.value = self._init_value
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/swig.py", line 35, in value
self._assign(self.ptr, value)
TypeError: in method 'UINT8_PTR_assign', argument 2 of type 'UINT8'
第一个参数当然是 pythonic self,但第二个参数应该是 UINT8,而不是我们试图传递的一些字节。问题是,tpm2_pytss.binding 模块中没有 UINT8 对象。但是,如果我们尝试传递单个值(将 XXXX 替换为):
my_digest = ctx_stack.enter_context(UINT8_PTR(3))
print(my_digest)
和运行脚本,它很乐意输出:
(...)
<tpm2_pytss.util.swig.UINT8_PTR object at 0x7fb51f3ffb20>
所以我们有一个指向单个值的 UINT8 指针,让我们看看如何检查它的值(同样,将 XXXX 替换为):
my_digest = ctx_stack.enter_context(UINT8_PTR(3))
print(dir(my_digest))
给出:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_assign', '_copy', '_delete', '_init_value', '_new', '_value', 'frompointer', 'ptr', 'value']
那么,现在让我们尝试使用 value 属性(将 XXXX 替换为):
my_digest = ctx_stack.enter_context(UINT8_PTR(3))
print(my_digest.value)
而当代码运行s时,它给出3。所以我们可以为这个指针分配一个值,一个值数组怎么样(这是必需的,因为摘要肯定包含多个字节)?这是我到目前为止尝试过的:
my_digest = ctx_stack.enter_context(UINT8_PTR([1,2,3]))
print(my_digest.value)
给出之前显示的错误:
(...)
TypeError: in method 'UINT8_PTR_assign', argument 2 of type 'UINT8'
所以这是负面的。让我们再试试:
import struct
random_bytes = b"\x01\x02\x03\x04"
vals = struct.pack("<s", random_bytes)
my_digest = ctx_stack.enter_context(UINT8_PTR(vals))
print(my_digest.value)
给出与上述相同的错误。同样适用于:
from ctypes import c_uint8
digest_arr = (c_uint8 * 4)(*[1,2,3,4])
my_digest = ctx_stack.enter_context(UINT8_PTR(digest_arr))
print(my_digest.value)
同样,同样适用于:
from ctypes import c_uint8, POINTER, cast
random_bytes = b"\x01\x02\x03\x04"
digest_arr = cast(random_bytes, POINTER(c_uint8 * 4))
my_digest = ctx_stack.enter_context(UINT8_PTR(digest_arr))
print(my_digest.value)
在这一点上,我没有想法,所以我开始研究 pytss 的文档,并反复查看提供的 2 个示例。我想到了:
digest = TPM2B_DIGEST(size=4, buffer=[1,2,3,4])
my_digest = ctx_stack.enter_context(digest)
print(my_digest.value)
但它抛出另一个错误:
(...)
Traceback (most recent call last):
File "lab5.py", line 203, in <module>
ctx, stck = get_context()
File "lab5.py", line 61, in get_context
my_digest = ctx_stack.enter_context(digest)
File "/usr/lib/python3.8/contextlib.py", line 424, in enter_context
_exit = _cm_type.__exit__
AttributeError: type object 'TPM2B_DIGEST' has no attribute '__exit__'
然后我试了这个:
digest = TPM2B_DIGEST(size=4, buffer=[1,2,3,4])
my_digest = ctx_stack.enter_context(UINT8_PTR(digest))
print(my_digest.value)
给出与之前相同的错误:
(...)
Traceback (most recent call last):
File "lab5.py", line 198, in <module>
ctx, stck = get_context()
File "lab5.py", line 59, in get_context
my_digest = ctx_stack.enter_context(UINT8_PTR(digest))
File "/usr/lib/python3.8/contextlib.py", line 425, in enter_context
result = _cm_type.__enter__(cm)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/swig.py", line 46, in __enter__
self.value = self._init_value
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/swig.py", line 35, in value
self._assign(self.ptr, value)
TypeError: in method 'UINT8_PTR_assign', argument 2 of type 'UINT8'
我什至结合了这样的东西:
digest = TPM2B_DIGEST(size=4, buffer=[1,2,3,4])
my_digest = ctx_stack.enter_context(UINT8_PTR())
md2 = my_digest.frompointer(digest)
print(md2.value)
然而,这次我刚收到分段错误。当我再次尝试 运行 脚本时,出现了这个错误:
Traceback (most recent call last):
File "lab5.py", line 201, in <module>
ctx, stck = get_context()
File "lab5.py", line 20, in get_context
simulator = ctx_stack.enter_context(Simulator())
File "/usr/lib/python3.8/contextlib.py", line 425, in enter_context
result = _cm_type.__enter__(cm)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/simulator.py", line 128, in __enter__
self.start()
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/simulator.py", line 94, in start
os.unlink("NVChip")
OSError: [Errno 26] Text file busy: 'NVChip'
因此,如果您为此苦苦挣扎了一段时间,您只需要找出是什么进程阻塞了自己:
$ sudo lsof NVChip
它会给你一些信息,你正在寻找的一件事是进程的 PID,然后简单地杀死它。每次出现此段错误时,它都让我免于重新启动我的虚拟机:
$ sudo kill -9 <PROCESS_PID>
无论如何,我疲惫的大脑最后想到的是:
my_arr = [UINT8_PTR(1), UINT8_PTR(2), UINT8_PTR(3), UINT8_PTR(4)]
my_digest = ctx_stack.enter_context(UINT8_PTR_PTR(my_arr))
然而,这给出了与之前相同的错误:
(...)
TypeError: in method 'UINT8_PTR_PTR_assign', argument 2 of type 'UINT8 *'
如果我尝试过:
my_arr = [UINT8_PTR(1), UINT8_PTR(2), UINT8_PTR(3), UINT8_PTR(4)]
my_digest = ctx_stack.enter_context(UINT8_PTR(my_arr))
我收到了完全相同的错误(昨天它也给了我一个段错误,但我现在无法重现)。
所以我觉得我out了。我花了几个小时在上面,但无济于事。我什么都试过了,我试过倒立,从相反的角度看问题,我试过在天花板上行走,我试过从左边和右边看问题,我试过例子,我都试过了。我什至去找我的老朋友魔鬼,我把我的灵魂献给他,以换取这个问题的解决方案。他问——什么问题? - 我给他看了代码。他真的只是告诉我****离开并关上了门。所以我决定 post 把我目前所知道的一切都放在这里,也许将来有人会用到它。我认为关于 pytss 或只是 TPM2 的文档和帮助在互联网上是如此可怕,所以我想对这个主题的任何贡献都可能会给其他人一些新的见解。我希望。作为旁注,这实际上就是波兰教育的样子。他们给你一个作业,没人知道怎么解决,如果你把问题给讲师看,他们通常帮不上忙,所以你花了很多个不眠之夜才发现自己无法解决,随着时间的推移,还有 15 项其他任务在等着您,并相互叠加。但我想那是另一个讨论。感谢任何试图在这里提供帮助的人,希望我的解释对以后的人有所帮助。
[编辑]
至于对我原问题的评论,我也尝试了最简单的情况:
random_bytes = b"\x01\x02\x03\x04"
my_digest = ctx_stack.enter_context(random_bytes)
my_sig_handler = ctx_stack.enter_context(UINT8_PTR_PTR())
fapi_ctx.Sign("HS/my_keypair", None, my_digest, 0, my_sig_handler, None, None, None)
但是报错:
(...)
Traceback (most recent call last):
File "lab5.py", line 209, in <module>
ctx, stck = get_context()
File "lab5.py", line 59, in get_context
my_digest = ctx_stack.enter_context(random_bytes)
File "/usr/lib/python3.8/contextlib.py", line 424, in enter_context
_exit = _cm_type.__exit__
AttributeError: type object 'bytes' has no attribute '__exit__'
然后试试这个:
random_bytes = b"\x01\x02\x03\x04"
my_sig_handler = ctx_stack.enter_context(UINT8_PTR_PTR())
fapi_ctx.Sign("HS/my_keypair", None, random_bytes, 0, my_sig_handler, None, None, None)
给出先前已知的错误:
Traceback (most recent call last):
File "lab5.py", line 209, in <module>
ctx, stck = get_context()
File "lab5.py", line 61, in get_context
fapi_ctx.Sign("HS/my_keypair", None, random_bytes, 0, my_sig_handler, None, None, None)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/context.py", line 78, in wrapper
return custom_wrap(func)(self.ctxp, *args, **kwds)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/context.py", line 112, in wrapper
result = func(self, *args, **kwds)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/swig.py", line 173, in wrapper
return func(*args, **kwargs)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/exceptions.py", line 48, in wrapper
rc = func(*args, **kwargs)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/fapi_binding.py", line 14997, in Fapi_Sign
return _fapi_binding.Fapi_Sign(context, keyPath, padding, digest, digestSize, signature, signatureSize, publicKey, certificate)
TypeError: in method 'Fapi_Sign', argument 4 of type 'uint8_t const *'
我怀疑您使用的是最新版本 (0.1.9) 或更旧版本。无法将 UINT8
数组作为指针传递的限制是一个已知限制:Issue #36.
此问题已通过尚未发布的上下文机制返工解决。因此,您需要从源代码安装 tpm2-pytss(包括 tpm2-swig)。您需要 tpm2-pytss 2f9ebdf and tpm2-swig a3e5e58 或更高版本。
示例:签名创建
首先,我们需要一个 FAPI 上下文的实例,使用 TPM 模拟器和一个临时密钥库。
import contextlib
import tempfile
from cryptography import x509
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import load_pem_public_key
from tpm2_pytss.binding import *
from tpm2_pytss.fapi import FAPI, FAPIDefaultConfig
from tpm2_pytss.util.simulator import Simulator
with contextlib.ExitStack() as ctx_stack:
simulator = ctx_stack.enter_context(Simulator())
user_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
log_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
system_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
fapi = FAPI(
FAPIDefaultConfig._replace(
user_dir=user_dir,
system_dir=system_dir,
log_dir=log_dir,
tcti="mssim:port=%d" % (simulator.port,),
tcti_retry=100,
ek_cert_less=1,
)
)
fapi_ctx = ctx_stack.enter_context(fapi)
fapi_ctx.Provision(None, None, None)
然后我们可以创建一个签名密钥。
# create signing key
my_signing_key_path = "HS/SRK/mySigningKey"
fapi_ctx.CreateKey(my_signing_key_path, "noDa, sign", "", "")
现在是棘手的部分:对消息进行哈希处理后,我们需要从哈希 (bytes
) 中创建一个 UINT8_ARRAY
,然后将其作为 UINT8_PTR
传递。截至目前,我们需要逐元素复制。
message = b"Hello World"
# message has to be hashed: use SHA256
digest = hashes.Hash(hashes.SHA256())
digest.update(message)
digest = digest.finalize()
# create UINT8_ARRAY
data_size = len(digest)
data = UINT8_ARRAY(nelements=data_size)
for i, byte in enumerate(digest):
data[i] = byte
最后,我们可以调用TPM签名函数了。为了方便起见,我们还获得了 public 密钥和关联的证书(在本例中为 ""
)
padding = None
signature_der, public_key_pem, _certificate_pem = fapi_ctx.Sign(
my_signing_key_path, padding, data.cast(), data_size
)
signature_hex = "".join(f"{b:02x}" for b in signature_der)
print(f"Signature[{len(signature_der)}]: {signature_hex}")
最后,我们可以使用 cryptography
模块从外部验证签名。
# verify signature using cryptography
public_key = load_pem_public_key(bytes(public_key_pem, encoding="utf-8"))
public_key.verify(bytes(signature_der), message, ec.ECDSA(hashes.SHA256()))
这是我的第一个问题,我想向所有社区问好。我总是喜欢用困难的方式来做,尽管这次我花了几个小时搜索、尝试,但没有任何东西更接近解决方案。所以。 我的任务是写关于使用 TPM 签署文件的文章。我们使用虚拟设备,并且有一个 Python 库提供来自 C 的 TPM 2.0 TSS 绑定 - Pytss。 我目前正在努力将参数传递给 fapi_Sign() 方法以签署特定文档。我已经根据文档的示例创建了上下文:
import random
import tempfile
import contextlib
from tpm2_pytss.fapi import FAPI, FAPIDefaultConfig
from tpm2_pytss.binding import *
#import tpm2_pytss.binding as bd
from tpm2_pytss.util.simulator import Simulator
def get_context():
# Create a context stack
ctx_stack = contextlib.ExitStack()
# Create a simulator
simulator = ctx_stack.enter_context(Simulator())
# Create temporary directories to separate this example's state
user_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
log_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
system_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
# Create the FAPI object
fapi = FAPI(
FAPIDefaultConfig._replace(
user_dir=user_dir,
system_dir=system_dir,
log_dir=log_dir,
tcti="mssim:port=%d" % (simulator.port,),
tcti_retry=100,
ek_cert_less=1,
)
)
# Enter the context, create TCTI connection
fapi_ctx = ctx_stack.enter_context(fapi)
# Call Fapi_Provision
fapi_ctx.Provision(None, None, None)
return fapi_ctx, ctx_stack
#
并且还生成了一对密钥:
def sign(fapi_ctx, fapi_stck, path_to_key, password, document):
# Create pair of public, private keys
fapi_ctx.CreateKey(path_to_key, None, None, password)
问题是,当我尝试调用 fapi_Sign() 方法时,我无法成功将摘要传递给它:
import hashlib
import ctypes
shaobj = hashlib.sha256()
shaobj.update(document.encode())
digest_pointer = ctypes.cast(shaobj.digest(), ctypes.POINTER(ctypes.c_uint8 * shaobj.digest_size))
digest_size = shaobj.digest_size
fapi_ctx.Sign(path_to_key, None, digest_pointer, digest_size, sig_holder, None, None, None)
输出为:
Traceback (most recent call last):
File "lab5.py", line 84, in <module>
sign(ctx, stck, "HS/lab5_key", "qwe123", doc_txt)
File "lab5.py", line 77, in sign
fapi_ctx.Sign(path_to_key, None, digest_pointer, 32, sig_holder, None, None, None)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/context.py", line 78, in wrapper
return custom_wrap(func)(self.ctxp, *args, **kwds)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/context.py", line 112, in wrapper
result = func(self, *args, **kwds)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/swig.py", line 173, in wrapper
return func(*args, **kwargs)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/exceptions.py", line 48, in wrapper
rc = func(*args, **kwargs)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/fapi_binding.py", line 14997, in Fapi_Sign
return _fapi_binding.Fapi_Sign(context, keyPath, padding, digest, digestSize, signature, signatureSize, publicKey, certificate)
TypeError: in method 'Fapi_Sign', argument 4 of type 'uint8_t const *'
经过反复尝试,我无法将 digest 参数传递给 fapi_Sign() 方法。我尝试了一切,从 ctypes 到 struct.pack,再到简单地使用 tpm2_pytss.binding 提供的任何东西(在使用 dir(tpm2_pytss.binding) 之后)。我还意识到 SWIG 并不真正喜欢 ctypes,但我很快就放弃了,因此只剩下 ctypes 示例向您展示。如果您能指出正确的方向,我们将不胜感激。
[编辑] 根据要求,在此处发布重现我的错误的代码(请注意,您必须安装所有与 tpm2 相关的库)
import random
import tempfile
import contextlib
from tpm2_pytss.fapi import FAPI, FAPIDefaultConfig
from tpm2_pytss.binding import *
import tpm2_pytss.binding as tm
from tpm2_pytss.util.simulator import Simulator
from hashlib import sha256
def get_context():
# Create a context stack
ctx_stack = contextlib.ExitStack()
# Create a simulator
simulator = ctx_stack.enter_context(Simulator())
# Create temporary directories to separate this example's state
user_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
log_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
system_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
# Create the FAPI object
fapi = FAPI(
FAPIDefaultConfig._replace(
user_dir=user_dir,
system_dir=system_dir,
log_dir=log_dir,
tcti="mssim:port=%d" % (simulator.port,),
tcti_retry=100,
ek_cert_less=1,
)
)
# Enter the context, create TCTI connection
fapi_ctx = ctx_stack.enter_context(fapi)
# Call Fapi_Provision
fapi_ctx.Provision(None, None, None)
return fapi_ctx, ctx_stack
#
def sign(fapi_ctx, fapi_stck, path_to_key, password, document):
# Create pair of public, private keys
fapi_ctx.CreateKey(path_to_key, None, None, password)
sig_holder = fapi_stck.enter_context(UINT8_PTR_PTR())
# Get SHA256 digest of the document
shaobj = sha256()
shaobj.update(document.encode())
fapi_ctx.Sign(path_to_key, None, shaobj.digest(), shaobj.digest_size, sig_holder, None, None, None)
#
document = open("example.txt", "r")
doc_txt = document.read()
ctx, stck = get_context()
sign(ctx, stck, "HS/some_key", "qwe123", doc_txt)
example.txt 这里只是一些将被签名的随机文本文件。
我最近花了几个小时调查这个问题,我想把它放在这里,也许它会给某些人敲响警钟。我没有编辑我的答案,因为 1 - 它已经很长了 2 - 我实际上部分解决了问题,但其他人在这个过程中出现了,所以这是一个部分答案,但我想仍然是一个答案。 好吧,所以我从稍微不同的角度开始。看看这个代码(它实际上是他们 documentation 的一个例子。假设我们有这个:
import random
import tempfile
import contextlib
from tpm2_pytss.fapi import FAPI, FAPIDefaultConfig
from tpm2_pytss.binding import *
import tpm2_pytss.binding as tm
#import tpm2_pytss.binding as bd
from tpm2_pytss.util.simulator import Simulator
from hashlib import sha256
from ctypes import c_uint8, cast, POINTER, pointer
from numpy import array
def get_context():
# Create a context stack
ctx_stack = contextlib.ExitStack()
# Create a simulator
simulator = ctx_stack.enter_context(Simulator())
# Create temporary directories to separate this example's state
user_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
log_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
system_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
# Create the FAPI object
fapi = FAPI(
FAPIDefaultConfig._replace(
user_dir=user_dir,
system_dir=system_dir,
log_dir=log_dir,
tcti="mssim:port=%d" % (simulator.port,),
tcti_retry=100,
ek_cert_less=1,
)
)
# Enter the context, create TCTI connection
fapi_ctx = ctx_stack.enter_context(fapi)
# Call Fapi_Provision
fapi_ctx.Provision(None, None, None)
XXXX
get_context()
抱歉有点误导函数名称 get_context(),该函数最初只是为了 return fapi_ctx 和 ctx_stack 对象,但我开始在这里进行试验只是为了保持代码简单。 'XXXX' 部分将包含我想展示的不同实验。如果将其替换为:
arr = ctx_stack.enter_context(UINT8_PTR_PTR())
rand = to_bytearray(4, fapi_ctx.GetRandom(4, arr))
print(rand)
然后 运行ning 代码给出:
$> sudo python3 experiments.py
WARNING:fapi:src/tss2-fapi/ifapi_io.c:282:ifapi_io_check_create_dir() Directory /tmp/tmpctp8i4zr/policy does not exist, creating
bytearray(b'1L\xfd(')
如您所见,运行将代码 returned 为 4 个随机字节的字节数组。伟大的。这让我开始思考,也许我们需要传递 UINT8_PTR() 和 UINT8_PTR_PTR() 与函数 enter_context() 的某种组合原来的问题。但首先,让我们尝试不涉及上下文。现在,将 'XXXX' 替换为:
# Create pair of keys
fapi_ctx.CreateKey("HS/my_keypair", None, None, None)
# Prepare values to sign the file
my_digest = UINT8_PTR()
my_sig_handler = UINT8_PTR_PTR()
fapi_ctx.Sign("HS/my_keypair", None, my_digest, 0, my_sig_handler, None, None, None)
当我们像以前一样运行时,我们现在得到这个错误:
WARNING:fapi:src/tss2-fapi/ifapi_io.c:282:ifapi_io_check_create_dir() Directory /tmp/tmpvhxrb_1i/policy does not exist, creating
Traceback (most recent call last):
File "lab5.py", line 185, in <module>
ctx, stck = get_context()
File "lab5.py", line 50, in get_context
fapi_ctx.Sign("HS/lab5_key", None, my_digest, 0, my_sig_handler, None, None, None)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/context.py", line 78, in wrapper
return custom_wrap(func)(self.ctxp, *args, **kwds)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/context.py", line 112, in wrapper
result = func(self, *args, **kwds)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/swig.py", line 161, in wrapper
modifed = modify(parameter.name, parameter.annotation, value)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/binding.py", line 428, in call_mod_context_managed_pointer_class
raise UsedWithoutEnteringContext(name)
tpm2_pytss.binding.UsedWithoutEnteringContext: digest
因此 pytss 库希望我们为传递给函数的变量提供一些上下文。现在,我们使用的不是 XXXX 部分:
# Create pair of keys
fapi_ctx.CreateKey("HS/my_keypair", None, None, None)
# Prepare values to sign the file
my_digest = ctx_stack.enter_context(UINT8_PTR())
my_sig_handler = ctx_stack.enter_context(UINT8_PTR_PTR())
fapi_ctx.Sign("HS/my_keypair", None, my_digest, 0, my_sig_handler, None, None, None)
当这是 运行 时,我们现在得到这个错误:
WARNING:fapi:src/tss2-fapi/ifapi_io.c:282:ifapi_io_check_create_dir() Directory /tmp/tmp9y61w0rl/policy does not exist, creating
ERROR:fapi:src/tss2-fapi/fapi_util.c:3617:ifapi_get_sig_scheme() ErrorCode (0x0006000b) Invalid digest size.
ERROR:fapi:src/tss2-fapi/fapi_util.c:2742:ifapi_key_sign() Get signature scheme ErrorCode (0x0006000b)
ERROR:fapi:src/tss2-fapi/api/Fapi_Sign.c:299:Fapi_Sign_Finish() Fapi sign. ErrorCode (0x0006000b)
ERROR:fapi:src/tss2-fapi/api/Fapi_Sign.c:130:Fapi_Sign() ErrorCode (0x0006000b) Key_Sign
Traceback (most recent call last):
File "lab5.py", line 185, in <module>
ctx, stck = get_context()
File "lab5.py", line 50, in get_context
fapi_ctx.Sign("HS/my_keypair", None, my_digest, 0, my_sig_handler, None, None, None)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/context.py", line 78, in wrapper
return custom_wrap(func)(self.ctxp, *args, **kwds)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/context.py", line 112, in wrapper
result = func(self, *args, **kwds)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/swig.py", line 173, in wrapper
return func(*args, **kwargs)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/exceptions.py", line 50, in wrapper
raise TPM2Error(rc)
tpm2_pytss.exceptions.TPM2Error: fapi:A parameter has a bad value
所以现在函数 Sign() 似乎接受了我们的参数类型,但它在尝试签署空摘要时崩溃了。那么我们如何将文件的签名传输到这个 PTR_SOMETHING() 中呢?首先,让我们提醒自己签名的样子。独立拥有此代码运行:
from hashlib import sha256
document = open("text.doc", "r")
doc_txt = document.read()
shaobj = sha256()
shaobj.update(doc_txt.encode())
print(shaobj.digest())
shaobj 的 digest() 方法将 return 只是一堆字节 b"(...)" 包含我们的随机文本文件的散列内容.我们能否以某种方式将这些字节传递给 my_digest 对象?让我们来看看。将 XXXX 部分替换为:
random_bytes = b"\x01\x02\x03\x04"
my_digest = ctx_stack.enter_context(UINT8_PTR(random_bytes))
给我们这个错误:
(...) <=== I decided to omit the first line containing "(...) does not exist, creating"
Traceback (most recent call last):
File "lab5.py", line 186, in <module>
ctx, stck = get_context()
File "lab5.py", line 48, in get_context
my_digest = ctx_stack.enter_context(UINT8_PTR(random_bytes))
File "/usr/lib/python3.8/contextlib.py", line 425, in enter_context
result = _cm_type.__enter__(cm)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/swig.py", line 46, in __enter__
self.value = self._init_value
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/swig.py", line 35, in value
self._assign(self.ptr, value)
TypeError: in method 'UINT8_PTR_assign', argument 2 of type 'UINT8'
第一个参数当然是 pythonic self,但第二个参数应该是 UINT8,而不是我们试图传递的一些字节。问题是,tpm2_pytss.binding 模块中没有 UINT8 对象。但是,如果我们尝试传递单个值(将 XXXX 替换为):
my_digest = ctx_stack.enter_context(UINT8_PTR(3))
print(my_digest)
和运行脚本,它很乐意输出:
(...)
<tpm2_pytss.util.swig.UINT8_PTR object at 0x7fb51f3ffb20>
所以我们有一个指向单个值的 UINT8 指针,让我们看看如何检查它的值(同样,将 XXXX 替换为):
my_digest = ctx_stack.enter_context(UINT8_PTR(3))
print(dir(my_digest))
给出:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_assign', '_copy', '_delete', '_init_value', '_new', '_value', 'frompointer', 'ptr', 'value']
那么,现在让我们尝试使用 value 属性(将 XXXX 替换为):
my_digest = ctx_stack.enter_context(UINT8_PTR(3))
print(my_digest.value)
而当代码运行s时,它给出3。所以我们可以为这个指针分配一个值,一个值数组怎么样(这是必需的,因为摘要肯定包含多个字节)?这是我到目前为止尝试过的:
my_digest = ctx_stack.enter_context(UINT8_PTR([1,2,3]))
print(my_digest.value)
给出之前显示的错误:
(...)
TypeError: in method 'UINT8_PTR_assign', argument 2 of type 'UINT8'
所以这是负面的。让我们再试试:
import struct
random_bytes = b"\x01\x02\x03\x04"
vals = struct.pack("<s", random_bytes)
my_digest = ctx_stack.enter_context(UINT8_PTR(vals))
print(my_digest.value)
给出与上述相同的错误。同样适用于:
from ctypes import c_uint8
digest_arr = (c_uint8 * 4)(*[1,2,3,4])
my_digest = ctx_stack.enter_context(UINT8_PTR(digest_arr))
print(my_digest.value)
同样,同样适用于:
from ctypes import c_uint8, POINTER, cast
random_bytes = b"\x01\x02\x03\x04"
digest_arr = cast(random_bytes, POINTER(c_uint8 * 4))
my_digest = ctx_stack.enter_context(UINT8_PTR(digest_arr))
print(my_digest.value)
在这一点上,我没有想法,所以我开始研究 pytss 的文档,并反复查看提供的 2 个示例。我想到了:
digest = TPM2B_DIGEST(size=4, buffer=[1,2,3,4])
my_digest = ctx_stack.enter_context(digest)
print(my_digest.value)
但它抛出另一个错误:
(...)
Traceback (most recent call last):
File "lab5.py", line 203, in <module>
ctx, stck = get_context()
File "lab5.py", line 61, in get_context
my_digest = ctx_stack.enter_context(digest)
File "/usr/lib/python3.8/contextlib.py", line 424, in enter_context
_exit = _cm_type.__exit__
AttributeError: type object 'TPM2B_DIGEST' has no attribute '__exit__'
然后我试了这个:
digest = TPM2B_DIGEST(size=4, buffer=[1,2,3,4])
my_digest = ctx_stack.enter_context(UINT8_PTR(digest))
print(my_digest.value)
给出与之前相同的错误:
(...)
Traceback (most recent call last):
File "lab5.py", line 198, in <module>
ctx, stck = get_context()
File "lab5.py", line 59, in get_context
my_digest = ctx_stack.enter_context(UINT8_PTR(digest))
File "/usr/lib/python3.8/contextlib.py", line 425, in enter_context
result = _cm_type.__enter__(cm)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/swig.py", line 46, in __enter__
self.value = self._init_value
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/swig.py", line 35, in value
self._assign(self.ptr, value)
TypeError: in method 'UINT8_PTR_assign', argument 2 of type 'UINT8'
我什至结合了这样的东西:
digest = TPM2B_DIGEST(size=4, buffer=[1,2,3,4])
my_digest = ctx_stack.enter_context(UINT8_PTR())
md2 = my_digest.frompointer(digest)
print(md2.value)
然而,这次我刚收到分段错误。当我再次尝试 运行 脚本时,出现了这个错误:
Traceback (most recent call last):
File "lab5.py", line 201, in <module>
ctx, stck = get_context()
File "lab5.py", line 20, in get_context
simulator = ctx_stack.enter_context(Simulator())
File "/usr/lib/python3.8/contextlib.py", line 425, in enter_context
result = _cm_type.__enter__(cm)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/simulator.py", line 128, in __enter__
self.start()
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/simulator.py", line 94, in start
os.unlink("NVChip")
OSError: [Errno 26] Text file busy: 'NVChip'
因此,如果您为此苦苦挣扎了一段时间,您只需要找出是什么进程阻塞了自己:
$ sudo lsof NVChip
它会给你一些信息,你正在寻找的一件事是进程的 PID,然后简单地杀死它。每次出现此段错误时,它都让我免于重新启动我的虚拟机:
$ sudo kill -9 <PROCESS_PID>
无论如何,我疲惫的大脑最后想到的是:
my_arr = [UINT8_PTR(1), UINT8_PTR(2), UINT8_PTR(3), UINT8_PTR(4)]
my_digest = ctx_stack.enter_context(UINT8_PTR_PTR(my_arr))
然而,这给出了与之前相同的错误:
(...)
TypeError: in method 'UINT8_PTR_PTR_assign', argument 2 of type 'UINT8 *'
如果我尝试过:
my_arr = [UINT8_PTR(1), UINT8_PTR(2), UINT8_PTR(3), UINT8_PTR(4)]
my_digest = ctx_stack.enter_context(UINT8_PTR(my_arr))
我收到了完全相同的错误(昨天它也给了我一个段错误,但我现在无法重现)。 所以我觉得我out了。我花了几个小时在上面,但无济于事。我什么都试过了,我试过倒立,从相反的角度看问题,我试过在天花板上行走,我试过从左边和右边看问题,我试过例子,我都试过了。我什至去找我的老朋友魔鬼,我把我的灵魂献给他,以换取这个问题的解决方案。他问——什么问题? - 我给他看了代码。他真的只是告诉我****离开并关上了门。所以我决定 post 把我目前所知道的一切都放在这里,也许将来有人会用到它。我认为关于 pytss 或只是 TPM2 的文档和帮助在互联网上是如此可怕,所以我想对这个主题的任何贡献都可能会给其他人一些新的见解。我希望。作为旁注,这实际上就是波兰教育的样子。他们给你一个作业,没人知道怎么解决,如果你把问题给讲师看,他们通常帮不上忙,所以你花了很多个不眠之夜才发现自己无法解决,随着时间的推移,还有 15 项其他任务在等着您,并相互叠加。但我想那是另一个讨论。感谢任何试图在这里提供帮助的人,希望我的解释对以后的人有所帮助。
[编辑]
至于对我原问题的评论,我也尝试了最简单的情况:
random_bytes = b"\x01\x02\x03\x04"
my_digest = ctx_stack.enter_context(random_bytes)
my_sig_handler = ctx_stack.enter_context(UINT8_PTR_PTR())
fapi_ctx.Sign("HS/my_keypair", None, my_digest, 0, my_sig_handler, None, None, None)
但是报错:
(...)
Traceback (most recent call last):
File "lab5.py", line 209, in <module>
ctx, stck = get_context()
File "lab5.py", line 59, in get_context
my_digest = ctx_stack.enter_context(random_bytes)
File "/usr/lib/python3.8/contextlib.py", line 424, in enter_context
_exit = _cm_type.__exit__
AttributeError: type object 'bytes' has no attribute '__exit__'
然后试试这个:
random_bytes = b"\x01\x02\x03\x04"
my_sig_handler = ctx_stack.enter_context(UINT8_PTR_PTR())
fapi_ctx.Sign("HS/my_keypair", None, random_bytes, 0, my_sig_handler, None, None, None)
给出先前已知的错误:
Traceback (most recent call last):
File "lab5.py", line 209, in <module>
ctx, stck = get_context()
File "lab5.py", line 61, in get_context
fapi_ctx.Sign("HS/my_keypair", None, random_bytes, 0, my_sig_handler, None, None, None)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/context.py", line 78, in wrapper
return custom_wrap(func)(self.ctxp, *args, **kwds)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/context.py", line 112, in wrapper
result = func(self, *args, **kwds)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/util/swig.py", line 173, in wrapper
return func(*args, **kwargs)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/exceptions.py", line 48, in wrapper
rc = func(*args, **kwargs)
File "/media/sf_LNX_FILES/SEM2/ZIO/tpm2-pytss/tpm2_pytss/fapi_binding.py", line 14997, in Fapi_Sign
return _fapi_binding.Fapi_Sign(context, keyPath, padding, digest, digestSize, signature, signatureSize, publicKey, certificate)
TypeError: in method 'Fapi_Sign', argument 4 of type 'uint8_t const *'
我怀疑您使用的是最新版本 (0.1.9) 或更旧版本。无法将 UINT8
数组作为指针传递的限制是一个已知限制:Issue #36.
此问题已通过尚未发布的上下文机制返工解决。因此,您需要从源代码安装 tpm2-pytss(包括 tpm2-swig)。您需要 tpm2-pytss 2f9ebdf and tpm2-swig a3e5e58 或更高版本。
示例:签名创建
首先,我们需要一个 FAPI 上下文的实例,使用 TPM 模拟器和一个临时密钥库。
import contextlib
import tempfile
from cryptography import x509
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import load_pem_public_key
from tpm2_pytss.binding import *
from tpm2_pytss.fapi import FAPI, FAPIDefaultConfig
from tpm2_pytss.util.simulator import Simulator
with contextlib.ExitStack() as ctx_stack:
simulator = ctx_stack.enter_context(Simulator())
user_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
log_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
system_dir = ctx_stack.enter_context(tempfile.TemporaryDirectory())
fapi = FAPI(
FAPIDefaultConfig._replace(
user_dir=user_dir,
system_dir=system_dir,
log_dir=log_dir,
tcti="mssim:port=%d" % (simulator.port,),
tcti_retry=100,
ek_cert_less=1,
)
)
fapi_ctx = ctx_stack.enter_context(fapi)
fapi_ctx.Provision(None, None, None)
然后我们可以创建一个签名密钥。
# create signing key
my_signing_key_path = "HS/SRK/mySigningKey"
fapi_ctx.CreateKey(my_signing_key_path, "noDa, sign", "", "")
现在是棘手的部分:对消息进行哈希处理后,我们需要从哈希 (bytes
) 中创建一个 UINT8_ARRAY
,然后将其作为 UINT8_PTR
传递。截至目前,我们需要逐元素复制。
message = b"Hello World"
# message has to be hashed: use SHA256
digest = hashes.Hash(hashes.SHA256())
digest.update(message)
digest = digest.finalize()
# create UINT8_ARRAY
data_size = len(digest)
data = UINT8_ARRAY(nelements=data_size)
for i, byte in enumerate(digest):
data[i] = byte
最后,我们可以调用TPM签名函数了。为了方便起见,我们还获得了 public 密钥和关联的证书(在本例中为 ""
)
padding = None
signature_der, public_key_pem, _certificate_pem = fapi_ctx.Sign(
my_signing_key_path, padding, data.cast(), data_size
)
signature_hex = "".join(f"{b:02x}" for b in signature_der)
print(f"Signature[{len(signature_der)}]: {signature_hex}")
最后,我们可以使用 cryptography
模块从外部验证签名。
# verify signature using cryptography
public_key = load_pem_public_key(bytes(public_key_pem, encoding="utf-8"))
public_key.verify(bytes(signature_der), message, ec.ECDSA(hashes.SHA256()))