简体   繁体   中英

Pytss: passing arguments to fapi_Sign() method - invalid argument type (Fapi_Sign argument 4 of type uint8_t const *)

As this is my first question here, I'd like to say hello to all community. I always preferred to do it the hard way, although this time I spent several hours searching, trying and nothing points any closer to the solution. So. I have assignment to write about signing documents with TPM. We use virtual devices, and there is one Python library that provides bindings of TPM 2.0 TSS from C - Pytss . I am currently struggling with passing arguments to fapi_Sign() method in order to sign specific document. I have already created context according to docs' examples:

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
#  

And also generated pair of keys:

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)

Problem is, when I try to call fapi_Sign() method, I am not able to successfully pass digest to it:

    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)

The output is:

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 *'

After trying and trying, I was not able to pass that digest argument to the fapi_Sign() method. I tried everything, from ctypes, through struct.pack, to simply using anything that tpm2_pytss.binding does offer (after using dir(tpm2_pytss.binding)). I also realized that SWIG does not really like ctypes, but it was soon after I gave up, hence there is only ctypes example that left to show you. If you could point me in the right direction, any help would be highly appreciated.

[EDIT] as requested, posting here code that reproduces my error (please note you have to have all tpm2 related libs installed)

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 here is just some random text file that will be signed.

I spent several hours recently investigating the issue, and I'd like to put it here so that perhaps it'll ring a bell to somebody. I am not editing my answer 'cause 1 - it's already long and 2 - I actually, partially solved the problem but others came up in the process, so it's a partial answer, but an answer nevertheless, I guess. Alright, so I started with slightly different perspective. Look at this code (it's actually an example from their documentation . Say we have this:

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()

Sorry for a bit misleading function name get_context() , the function was originally meant just to return fapi_ctx and ctx_stack objects, but I began experimenting here just to keep the code simple. The 'XXXX' part will contain different experiments that I'd like to show. If you replace this with:

arr = ctx_stack.enter_context(UINT8_PTR_PTR())
rand = to_bytearray(4, fapi_ctx.GetRandom(4,  arr))
print(rand)

Then running the code gives:

$> 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(')

So as you see, running the code returned bytearray of 4 random bytes. Great. This led me to thinking, maybe passing some combination of UINT8_PTR() and UINT8_PTR_PTR() with function enter_context() is needed to our original problem. But firstly, let's try without engaging context. Now, replace 'XXXX' with:

# 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)

When we run this as previously, we get this error now:

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

So the pytss library wants us to provide some context to variables passed to the function. Now, instead of XXXX part we use:

# 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)

When this is run, we get this error now:

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

So it seems now the function Sign() accepted types of our parameters, but it crashed trying to sign empty digest. So how do we transfer signature of our file into this PTR_SOMETHING() thing? Firstly, let's remind ourselves of how the signature might look like. Having this code run independently:

from hashlib import sha256

document = open("text.doc", "r")
doc_txt  = document.read()
shaobj = sha256()
shaobj.update(doc_txt.encode())
print(shaobj.digest())

The digest() method of shaobj will return just a bunch of bytes b"(...)" containing the hashed contents of our random text file. Can we pass somehow these bytes to the my_digest object? Let's see. Replacing XXXX part with:

random_bytes = b"\x01\x02\x03\x04"
my_digest      = ctx_stack.enter_context(UINT8_PTR(random_bytes))

Gives us this error:

(...)     <=== 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'

The first argument is of course pythonic self , but the second should be UINT8, not some bytes we tried to pass. Problem is, there is no UINT8 object in tpm2_pytss.binding module. However, if we try to pass a single value (replace XXXX with):

my_digest      = ctx_stack.enter_context(UINT8_PTR(3))
print(my_digest)

And run the script, it will gladly output:

(...)
<tpm2_pytss.util.swig.UINT8_PTR object at 0x7fb51f3ffb20>

So we have a UINT8 pointer to a single value, let's see how we can inspect its value (again, replace XXXX with):

my_digest      = ctx_stack.enter_context(UINT8_PTR(3))
print(dir(my_digest))

Which gives:

['__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']

So, now lets try to use the value attribute (replace XXXX with):

my_digest      = ctx_stack.enter_context(UINT8_PTR(3))
print(my_digest.value)

And when the code runs, it gives 3 . So we are able to assign a single value to this pointer, how about an array of values (which is needed as digest will contain surely more than one byte)? This is what I've tried so far:

my_digest      = ctx_stack.enter_context(UINT8_PTR([1,2,3]))
print(my_digest.value)

Gives previously shown error:

(...)
TypeError: in method 'UINT8_PTR_assign', argument 2 of type 'UINT8'

So that's a negative. Let's try some more:

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)

Gives same error as above. Same for:

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)

And again, same for:

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)

At this point, I was out of ideas, so I began studying docs of pytss and looking again and again at provided 2 examples. I came up with:

digest = TPM2B_DIGEST(size=4, buffer=[1,2,3,4])
my_digest      = ctx_stack.enter_context(digest)
print(my_digest.value)

But it throws another error:

(...)
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__'

Then I tried this:

digest = TPM2B_DIGEST(size=4, buffer=[1,2,3,4])
my_digest      = ctx_stack.enter_context(UINT8_PTR(digest))
print(my_digest.value)

Which gives same error as before:

(...)
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'

I even combined something like this:

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)

However, this time i just received segmentation fault. When I tried to run the script again, this error appeared:

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'

So if you struggled with this like for some time, you just need to find what process blocked itself:

$ sudo lsof NVChip

It will give you some info, the one thing you are looking for is PID of the process, then simply just kill it. It savedme from rebooting my virtualbox every time this segfault appeared:

$ sudo kill -9 <PROCESS_PID>

Anyway, the last thing that actually came up to my exhausted brain was:

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))

However, this gave same error as previously:

(...)
TypeError: in method 'UINT8_PTR_PTR_assign', argument 2 of type 'UINT8 *'

And if I tried:

my_arr = [UINT8_PTR(1), UINT8_PTR(2), UINT8_PTR(3), UINT8_PTR(4)]
my_digest      = ctx_stack.enter_context(UINT8_PTR(my_arr))

I received exactly the same error (yesterday it gave me also a segfault, but I can't reproduce it right now). So I think I am out. I spent several hours on it, no avail. I tried everything, I tried standing on my head and looking at the problem from reversed perspective, I tried walking on my ceiling, I tried looking at the problem from the left and from the right, I tried examples, I tried it all. I even went to my good old friend, the Devil, and I offered him my soul in exchange for the solution of this problem. He asked - what problem? - and I showed him the code. He literally just told me to **** off and closed the door. So I decided to post here everything I know so far, perhaps somebody may make some use of it in the future. I think the documentation and help on either pytss or just TPM2 is so scare on the internet, so I guess any contribution to this subject may give some fresh insight to others. I hope. As a side note, this is actually how polish education looks like. They give you an assignment, no one knows nothing how to solve it, if you show the problem to the lecturers the usually can't help, so you spent many sleepless nights on it just to figure out you are not able to solve it, and there are 15 other assignments waiting for you and stacking on each other as the time goes. But that is for another discussion I guess. Thanks anybody who tried to help here, hope my explanation helps somebody in the future.

[EDIT]

As for the comments to my original question, I tried also the simplest cases:

    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)

But it gives error:

(...)
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__'

And trying this:

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)

Gives known previously error:

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 *'

I suspect you are using the latest version (0.1.9) or older. The limitation of not being able to pass UINT8 arrays as a pointer is a known limitation: Issue #36 .

This issue is fixed by a rework of the context mechanism which is not released, yet. Therefore, you need to install tpm2-pytss (including tpm2-swig) from source. You want tpm2-pytss 2f9ebdf and tpm2-swig a3e5e58 or later.

Example: Signature Creation

First, we need an instance of the FAPI context, using the TPM simulator and a temporary keystore.

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)

Then we can create a signing key.

    # create signing key
    my_signing_key_path = "HS/SRK/mySigningKey"
    fapi_ctx.CreateKey(my_signing_key_path, "noDa, sign", "", "")

Now comes the tricky part: after hashing the message, we need to create an UINT8_ARRAY from the hash ( bytes ) and then pass it as a UINT8_PTR . As of now, we need to copy element-wise.

    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

Finally, we can call the TPM sign function. For convenience, we also get the public key and an associated certificate (in this case "" )

    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}")

Lastly, we can verify the signature externally, using the cryptography module.

    # 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()))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM