diff options
author | Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> | 2016-04-17 18:27:33 +0300 |
---|---|---|
committer | Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> | 2016-04-17 18:27:33 +0300 |
commit | 26780b384359790714dd8e87a7cb7dd7484ff6da (patch) | |
tree | b50b0658fc3b20c1d164fa9fc5e0de05750ac78c | |
download | tpm2-scripts-26780b384359790714dd8e87a7cb7dd7484ff6da.tar.gz |
first commit
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rwxr-xr-x | keyctl-smoke.sh | 12 | ||||
-rwxr-xr-x | tpm2-flush | 62 | ||||
-rwxr-xr-x | tpm2-list-handles | 61 | ||||
-rwxr-xr-x | tpm2-pcr-policy | 94 | ||||
-rwxr-xr-x | tpm2-read-pcrs | 63 | ||||
-rwxr-xr-x | tpm2-reset-da-lock | 57 | ||||
-rwxr-xr-x | tpm2-root-key | 62 | ||||
-rwxr-xr-x | tpm2-simulator-vtpm | 88 | ||||
-rw-r--r-- | tpm2.py | 799 | ||||
-rwxr-xr-x | tpm2_smoke.py | 165 |
12 files changed, 1466 insertions, 0 deletions
@@ -0,0 +1 @@ +Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> diff --git a/README.md b/README.md new file mode 100644 index 0000000..09ed281 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +This is a set of scripts for testing the Linux TPM driver TPM2 2.0 +functionality. diff --git a/keyctl-smoke.sh b/keyctl-smoke.sh new file mode 100755 index 0000000..9ed9701 --- /dev/null +++ b/keyctl-smoke.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +KEYHANDLE=$(sudo ./tpm2-root-key) +POLICYDIGEST=$(sudo ./tpm2-pcr-policy --pcr 16 --name-alg=sha256 --bank=sha1 --trial) +POLICYHANDLE=$(sudo ./tpm2-pcr-policy --pcr 16 --name-alg=sha256 --bank=sha1) + +KEYID=$(keyctl add trusted kmk "new 32 keyhandle=$KEYHANDLE hash=sha256 policydigest=$POLICYDIGEST" @u) +keyctl pipe $KEYID > blob.hex +keyctl clear @u +keyctl add trusted kmk "load `cat blob.hex` keyhandle=$KEYHANDLE policyhandle=0x03000000" @u +keyctl clear @u +sudo ./tpm2-flush $KEYHANDLE diff --git a/tpm2-flush b/tpm2-flush new file mode 100755 index 0000000..41dbab7 --- /dev/null +++ b/tpm2-flush @@ -0,0 +1,62 @@ +#! /usr/bin/env python +# Copyright (c) 2015, 2016 Intel Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from argparse import ArgumentParser +from argparse import FileType +import os +import sys +import tpm2 + +def main(): + parser = ArgumentParser(description='Create a storage root key') + parser.add_argument('--debug', + action='store_true', + help = 'dump TPM commands and replies') + parser.add_argument('handles', metavar='H', type = (lambda x: int(x, 0)), + nargs='+', help = 'a TPM resource handle') + args = parser.parse_args() + + flags = 0 + + if args.debug: + flags |= tpm2.Client.FLAG_DEBUG + + client = tpm2.Client(flags) + + if len(args.handles) == 0: + parser.print_help() + sys.exit(1) + + try: + for h in args.handles: + client.flush_context(h) + except tpm2.ProtocolError, e: + sys.stderr.write(str(e) + os.linesep) + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/tpm2-list-handles b/tpm2-list-handles new file mode 100755 index 0000000..1c1e791 --- /dev/null +++ b/tpm2-list-handles @@ -0,0 +1,61 @@ +#! /usr/bin/env python +# Copyright (c) 2015, 2016 Intel Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from argparse import ArgumentParser +from argparse import FileType +import os +import sys +import tpm2 + + +def main(): + parser = ArgumentParser(description='Create a storage root key') + parser.add_argument('--debug', + action='store_true', + help='dump TPM commands and replies') + args = parser.parse_args() + + flags = 0 + + if args.debug: + flags |= tpm2.Client.FLAG_DEBUG + + client = tpm2.Client(flags) + + try: + handles = [] + handles += client.get_cap(tpm2.TPM2_CAP_HANDLES, tpm2.HR_LOADED_SESSION) + handles += client.get_cap(tpm2.TPM2_CAP_HANDLES, tpm2.HR_TRANSIENT) + except tpm2.ProtocolError, e: + sys.stderr.write(str(e) + os.linesep) + sys.exit(1) + + for h in handles: + print(format(h, '#010x')) + +if __name__ == '__main__': + main() diff --git a/tpm2-pcr-policy b/tpm2-pcr-policy new file mode 100755 index 0000000..252b730 --- /dev/null +++ b/tpm2-pcr-policy @@ -0,0 +1,94 @@ +#! /usr/bin/env python +# Copyright (c) 2015, 2016 Intel Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from argparse import ArgumentParser +from argparse import FileType +import os +import sys +import tpm2 + + +def create_policy(client, session_handle, pcrs, bank_alg, name_alg): + if pcrs: + client.policy_pcr(session_handle, pcrs, bank_alg, name_alg) + + client.policy_password(session_handle) + + +def main(): + parser = ArgumentParser(description='Create a storage root key') + parser.add_argument('--debug', + action='store_true', + help='dump TPM commands and replies') + parser.add_argument('--trial', + action='store_true', + help='a trial policy') + parser.add_argument('--pcr', dest='pcr', metavar='PCR', action='append', + type=int, help='PCR index') + parser.add_argument('--name-alg', dest='name_alg', metavar='NAMEALG', + help='Hash algorithm used for the policy session', + type=tpm2.get_algorithm, default='sha1') + parser.add_argument('--bank', dest='bank', metavar='BANK', + help='PCR bank', type=tpm2.get_algorithm, + default='sha1') + args = parser.parse_args() + + flags = 0 + + if args.debug: + flags |= tpm2.Client.FLAG_DEBUG + + client = tpm2.Client(flags) + + try: + if args.trial: + session_handle = client.start_auth_session(tpm2.TPM2_SE_TRIAL, + args.name_alg) + try: + create_policy(client, session_handle, args.pcr, args.bank, + args.name_alg) + print(client.get_policy_digest(session_handle).encode('hex')) + finally: + client.flush_context(session_handle) + else: + session_handle = client.start_auth_session(tpm2.TPM2_SE_POLICY, + name_alg = args.name_alg) + try: + create_policy(client, session_handle, args.pcr, args.bank, + args.name_alg) + except: + client.flush_context(session_handle) + raise + + print(format(session_handle, '#010x')) + except tpm2.ProtocolError, e: + sys.stderr.write(str(e) + os.linesep) + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/tpm2-read-pcrs b/tpm2-read-pcrs new file mode 100755 index 0000000..b6fcf3f --- /dev/null +++ b/tpm2-read-pcrs @@ -0,0 +1,63 @@ +#! /usr/bin/env python +# Copyright (c) 2015, 2016 Intel Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from argparse import ArgumentParser +from argparse import FileType +import os +import sys +import tpm2 + + +def main(): + parser = ArgumentParser(description='Create a storage root key') + parser.add_argument('--debug', + action='store_true', + help='dump TPM commands and replies') + parser.add_argument('--name-alg', dest='name_alg', metavar='NAMEALG', + help='hash algorithm', + type=tpm2.get_algorithm, default='sha1') + + args = parser.parse_args() + + flags = 0 + + if args.debug: + flags |= tpm2.Client.FLAG_DEBUG + + client = tpm2.Client(flags) + + try: + for i in xrange(0, 16): + h = client.read_pcr(i, bank_alg=args.name_alg) + print("%02d: %s" % (i, h.encode('hex'))) + + except tpm2.ProtocolError, e: + sys.stderr.write(str(e) + os.linesep) + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/tpm2-reset-da-lock b/tpm2-reset-da-lock new file mode 100755 index 0000000..fb85f17 --- /dev/null +++ b/tpm2-reset-da-lock @@ -0,0 +1,57 @@ +#! /usr/bin/env python +# Copyright (c) 2015, 2016 Intel Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from argparse import ArgumentParser +from argparse import FileType +import os +import sys +import tpm2 + + +def main(): + parser = ArgumentParser(description='Create a storage root key') + parser.add_argument('--debug', + action='store_true', + help='dump TPM commands and replies') + args = parser.parse_args() + + flags = 0 + + if args.debug: + flags |= tpm2.Client.FLAG_DEBUG + + client = tpm2.Client(flags) + + try: + client.reset_da_lock() + except tpm2.ProtocolError, e: + sys.stderr.write(str(e) + os.linesep) + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/tpm2-root-key b/tpm2-root-key new file mode 100755 index 0000000..36ec74b --- /dev/null +++ b/tpm2-root-key @@ -0,0 +1,62 @@ +#! /usr/bin/env python +# Copyright (c) 2015, 2016 Intel Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from argparse import ArgumentParser +from argparse import FileType +import os +import sys +import tpm2 + + +def main(): + parser = ArgumentParser(description='Create a storage root key') + parser.add_argument('--debug', + action='store_true', + help='dump TPM commands and replies') + parser.add_argument('--auth-value', dest='auth_value', metavar='AUTH_VALUE', + type=lambda x: getattr(x, 'decode')('hex'), + default='', + help='20 byte auth value in hex') + args = parser.parse_args() + + flags = 0 + + if args.debug: + flags |= tpm2.Client.FLAG_DEBUG + + client = tpm2.Client(flags) + + try: + handle = client.create_root_key(args.auth_value) + except tpm2.ProtocolError, e: + sys.stderr.write(str(e) + os.linesep) + sys.exit(1) + + print(format(handle, '#010x')) + +if __name__ == '__main__': + main() diff --git a/tpm2-simulator-vtpm b/tpm2-simulator-vtpm new file mode 100755 index 0000000..de19970 --- /dev/null +++ b/tpm2-simulator-vtpm @@ -0,0 +1,88 @@ +#! /usr/bin/env python +# +# Copyright (c) 2016, Intel Corporation +# Copyright (c) 2015, Peter Huewe +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import io +import socket +import struct +import tpm2 +from argparse import ArgumentParser +from argparse import FileType +from fcntl import ioctl +from ctypes import Structure +from ctypes import c_uint32 +from ctypes import addressof + +VTPM_FLAG_TPM2 = 1 +VTPM_IOC_NEW_DEV = 0x4014a100 + + +class c_vtpm_new_dev(Structure): + _fields_ = \ + [('flags', c_uint32), + ('tpm_num', c_uint32), + ('fd', c_uint32), + ('major', c_uint32), + ('minor', c_uint32)] + + +def main(): + parser = ArgumentParser(description='Run a TPM 2.0 simulator proxy') + parser.add_argument('--host', dest='host', metavar='NAMEALG', + help='Address of the simulator', + type=str, default='localhost') + args = parser.parse_args() + + sim = tpm2.Simulator(args.host) + + client = tpm2.Client(flags = tpm2.Client.FLAG_DEBUG, simulator = sim) + startup_cmd = struct.pack('B' * 12, + 0x80, 0x01, + 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x01, 0x44, + 0x00, 0x00) + client.send_cmd(startup_cmd) + + new_dev = c_vtpm_new_dev(flags = VTPM_FLAG_TPM2) + + with open("/dev/vtpmx", "rb+") as vtpmx: + ioctl(vtpmx, VTPM_IOC_NEW_DEV, addressof(new_dev)) + + fp = io.open(new_dev.fd, 'rb+', buffering = 0) + + while True: + stream = fp.read(4096) + try: + resp = client.send_cmd(stream) + except tpm2.ProtocolError, e: + print str(e) + fp.write(resp) + + +if __name__ == '__main__': + main() @@ -0,0 +1,799 @@ +# The BSD License +# +# Copyright (c) 2016, Intel Corporation +# Copyright (c) 2015, Peter Huewe +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# The MIT License (MIT) +# +# Copyright (c) 2015 Microsoft Corporation +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import hashlib +import os +import socket +import struct +import sys +import unittest + + +TPM2_ST_NO_SESSIONS = 0x8001 +TPM2_ST_SESSIONS = 0x8002 + +TPM2_CC_CREATE_PRIMARY = 0x0131 +TPM2_CC_DICTIONARY_ATTACK_LOCK_RESET = 0x0139 +TPM2_CC_CREATE = 0x0153 +TPM2_CC_LOAD = 0x0157 +TPM2_CC_UNSEAL = 0x015E +TPM2_CC_FLUSH_CONTEXT = 0x0165 +TPM2_CC_START_AUTH_SESSION = 0x0176 +TPM2_CC_GET_CAPABILITY = 0x017A +TPM2_CC_PCR_READ = 0x017E +TPM2_CC_POLICY_PCR = 0x017F +TPM2_CC_PCR_EXTEND = 0x0182 +TPM2_CC_POLICY_PASSWORD = 0x018C +TPM2_CC_POLICY_GET_DIGEST = 0x0189 + +TPM2_SE_POLICY = 0x01 +TPM2_SE_TRIAL = 0x03 + +TPM2_ALG_RSA = 0x0001 +TPM2_ALG_SHA1 = 0x0004 +TPM2_ALG_AES = 0x0006 +TPM2_ALG_KEYEDHASH = 0x0008 +TPM2_ALG_SHA256 = 0x000B +TPM2_ALG_NULL = 0x0010 +TPM2_ALG_CBC = 0x0042 +TPM2_ALG_CFB = 0x0043 + +TPM2_RH_OWNER = 0x40000001 +TPM2_RH_NULL = 0x40000007 +TPM2_RH_LOCKOUT = 0x4000000A +TPM2_RS_PW = 0x40000009 + +TPM2_RC_SIZE = 0x000001D5 +TPM2_RC_AUTH_FAIL = 0x0000098E +TPM2_RC_POLICY_FAIL = 0x0000099D + +TPM2_CAP_HANDLES = 0x00000001 +TPM2_CAP_COMMANDS = 0x00000002 +TPM2_CAP_TPM_PROPERTIES = 0x00000006 + +TPM2_PT_FIXED = 0x100 +TPM2_PT_TOTAL_COMMANDS = TPM2_PT_FIXED + 41 + +HR_SHIFT = 24 +HR_LOADED_SESSION = 0x02000000 +HR_TRANSIENT = 0x80000000 + +SHA1_DIGEST_SIZE = 20 +SHA256_DIGEST_SIZE = 32 + +TPM2_VER0_ERRORS = { + 0x000: "TPM_RC_SUCCESS", + 0x030: "TPM_RC_BAD_TAG", +} + +TPM2_VER1_ERRORS = { + 0x000: "TPM_RC_FAILURE", + 0x001: "TPM_RC_FAILURE", + 0x003: "TPM_RC_SEQUENCE", + 0x00B: "TPM_RC_PRIVATE", + 0x019: "TPM_RC_HMAC", + 0x020: "TPM_RC_DISABLED", + 0x021: "TPM_RC_EXCLUSIVE", + 0x024: "TPM_RC_AUTH_TYPE", + 0x025: "TPM_RC_AUTH_MISSING", + 0x026: "TPM_RC_POLICY", + 0x027: "TPM_RC_PCR", + 0x028: "TPM_RC_PCR_CHANGED", + 0x02D: "TPM_RC_UPGRADE", + 0x02E: "TPM_RC_TOO_MANY_CONTEXTS", + 0x02F: "TPM_RC_AUTH_UNAVAILABLE", + 0x030: "TPM_RC_REBOOT", + 0x031: "TPM_RC_UNBALANCED", + 0x042: "TPM_RC_COMMAND_SIZE", + 0x043: "TPM_RC_COMMAND_CODE", + 0x044: "TPM_RC_AUTHSIZE", + 0x045: "TPM_RC_AUTH_CONTEXT", + 0x046: "TPM_RC_NV_RANGE", + 0x047: "TPM_RC_NV_SIZE", + 0x048: "TPM_RC_NV_LOCKED", + 0x049: "TPM_RC_NV_AUTHORIZATION", + 0x04A: "TPM_RC_NV_UNINITIALIZED", + 0x04B: "TPM_RC_NV_SPACE", + 0x04C: "TPM_RC_NV_DEFINED", + 0x050: "TPM_RC_BAD_CONTEXT", + 0x051: "TPM_RC_CPHASH", + 0x052: "TPM_RC_PARENT", + 0x053: "TPM_RC_NEEDS_TEST", + 0x054: "TPM_RC_NO_RESULT", + 0x055: "TPM_RC_SENSITIVE", + 0x07F: "RC_MAX_FM0", +} + +TPM2_FMT1_ERRORS = { + 0x001: "TPM_RC_ASYMMETRIC", + 0x002: "TPM_RC_ATTRIBUTES", + 0x003: "TPM_RC_HASH", + 0x004: "TPM_RC_VALUE", + 0x005: "TPM_RC_HIERARCHY", + 0x007: "TPM_RC_KEY_SIZE", + 0x008: "TPM_RC_MGF", + 0x009: "TPM_RC_MODE", + 0x00A: "TPM_RC_TYPE", + 0x00B: "TPM_RC_HANDLE", + 0x00C: "TPM_RC_KDF", + 0x00D: "TPM_RC_RANGE", + 0x00E: "TPM_RC_AUTH_FAIL", + 0x00F: "TPM_RC_NONCE", + 0x010: "TPM_RC_PP", + 0x012: "TPM_RC_SCHEME", + 0x015: "TPM_RC_SIZE", + 0x016: "TPM_RC_SYMMETRIC", + 0x017: "TPM_RC_TAG", + 0x018: "TPM_RC_SELECTOR", + 0x01A: "TPM_RC_INSUFFICIENT", + 0x01B: "TPM_RC_SIGNATURE", + 0x01C: "TPM_RC_KEY", + 0x01D: "TPM_RC_POLICY_FAIL", + 0x01F: "TPM_RC_INTEGRITY", + 0x020: "TPM_RC_TICKET", + 0x021: "TPM_RC_RESERVED_BITS", + 0x022: "TPM_RC_BAD_AUTH", + 0x023: "TPM_RC_EXPIRED", + 0x024: "TPM_RC_POLICY_CC", + 0x025: "TPM_RC_BINDING", + 0x026: "TPM_RC_CURVE", + 0x027: "TPM_RC_ECC_POINT", +} + +TPM2_WARN_ERRORS = { + 0x001: "TPM_RC_CONTEXT_GAP", + 0x002: "TPM_RC_OBJECT_MEMORY", + 0x003: "TPM_RC_SESSION_MEMORY", + 0x004: "TPM_RC_MEMORY", + 0x005: "TPM_RC_SESSION_HANDLES", + 0x006: "TPM_RC_OBJECT_HANDLES", + 0x007: "TPM_RC_LOCALITY", + 0x008: "TPM_RC_YIELDED", + 0x009: "TPM_RC_CANCELED", + 0x00A: "TPM_RC_TESTING", + 0x010: "TPM_RC_REFERENCE_H0", + 0x011: "TPM_RC_REFERENCE_H1", + 0x012: "TPM_RC_REFERENCE_H2", + 0x013: "TPM_RC_REFERENCE_H3", + 0x014: "TPM_RC_REFERENCE_H4", + 0x015: "TPM_RC_REFERENCE_H5", + 0x016: "TPM_RC_REFERENCE_H6", + 0x018: "TPM_RC_REFERENCE_S0", + 0x019: "TPM_RC_REFERENCE_S1", + 0x01A: "TPM_RC_REFERENCE_S2", + 0x01B: "TPM_RC_REFERENCE_S3", + 0x01C: "TPM_RC_REFERENCE_S4", + 0x01D: "TPM_RC_REFERENCE_S5", + 0x01E: "TPM_RC_REFERENCE_S6", + 0x020: "TPM_RC_NV_RATE", + 0x021: "TPM_RC_LOCKOUT", + 0x022: "TPM_RC_RETRY", + 0x023: "TPM_RC_NV_UNAVAILABLE", + 0x7F: "TPM_RC_NOT_USED", +} + +RC_VER1 = 0x100 +RC_FMT1 = 0x080 +RC_WARN = 0x900 + +ALG_DIGEST_SIZE_MAP = { + TPM2_ALG_SHA1: SHA1_DIGEST_SIZE, + TPM2_ALG_SHA256: SHA256_DIGEST_SIZE, +} + +ALG_HASH_FUNCTION_MAP = { + TPM2_ALG_SHA1: hashlib.sha1, + TPM2_ALG_SHA256: hashlib.sha256 +} + +NAME_ALG_MAP = { + "sha1": TPM2_ALG_SHA1, + "sha256": TPM2_ALG_SHA256, +} + + +class UnknownAlgorithmIdError(Exception): + def __init__(self, alg): + self.alg = alg + + def __str__(self): + return '0x%0x' % (alg) + + +class UnknownAlgorithmNameError(Exception): + def __init__(self, name): + self.name = name + + def __str__(self): + return name + + +class UnknownPCRBankError(Exception): + def __init__(self, alg): + self.alg = alg + + def __str__(self): + return '0x%0x' % (alg) + + +class ProtocolError(Exception): + def __init__(self, cc, rc, rsp): + self.cc = cc + self.rc = rc + self.rsp = rsp + + if (rc & RC_FMT1) == RC_FMT1: + self.name = TPM2_FMT1_ERRORS[rc & 0x3f] + elif (rc & RC_WARN) == RC_WARN: + self.name = TPM2_WARN_ERRORS[rc & 0x7f] + elif (rc & RC_VER1) == RC_VER1: + self.name = TPM2_VER1_ERRORS[rc & 0x7f] + else: + self.name = TPM2_VER0_ERRORS[rc & 0x7f] + + def __str__(self): + return '%s: cc=0x%08x, rc=0x%08x' % (self.name, self.cc, self.rc) + + +class AuthCommand(object): + """TPMS_AUTH_COMMAND""" + + def __init__(self, session_handle=TPM2_RS_PW, nonce='', session_attributes=0, + hmac=''): + self.session_handle = session_handle + self.nonce = nonce + self.session_attributes = session_attributes + self.hmac = hmac + + def __str__(self): + fmt = '>I H%us B H%us' % (len(self.nonce), len(self.hmac)) + return struct.pack(fmt, self.session_handle, len(self.nonce), + self.nonce, self.session_attributes, len(self.hmac), + self.hmac) + + def __len__(self): + fmt = '>I H%us B H%us' % (len(self.nonce), len(self.hmac)) + return struct.calcsize(fmt) + + +class SensitiveCreate(object): + """TPMS_SENSITIVE_CREATE""" + + def __init__(self, user_auth='', data=''): + self.user_auth = user_auth + self.data = data + + def __str__(self): + fmt = '>H%us H%us' % (len(self.user_auth), len(self.data)) + return struct.pack(fmt, len(self.user_auth), self.user_auth, + len(self.data), self.data) + + def __len__(self): + fmt = '>H%us H%us' % (len(self.user_auth), len(self.data)) + return struct.calcsize(fmt) + + +class Public(object): + """TPMT_PUBLIC""" + + FIXED_TPM = (1 << 1) + FIXED_PARENT = (1 << 4) + SENSITIVE_DATA_ORIGIN = (1 << 5) + USER_WITH_AUTH = (1 << 6) + RESTRICTED = (1 << 16) + DECRYPT = (1 << 17) + + def __fmt(self): + return '>HHIH%us%usH%us' % \ + (len(self.auth_policy), len(self.parameters), len(self.unique)) + + def __init__(self, object_type, name_alg, object_attributes, auth_policy='', + parameters='', unique=''): + self.object_type = object_type + self.name_alg = name_alg + self.object_attributes = object_attributes + self.auth_policy = auth_policy + self.parameters = parameters + self.unique = unique + + def __str__(self): + return struct.pack(self.__fmt(), + self.object_type, + self.name_alg, + self.object_attributes, + len(self.auth_policy), + self.auth_policy, + self.parameters, + len(self.unique), + self.unique) + + def __len__(self): + return struct.calcsize(self.__fmt()) + + +def get_digest_size(alg): + ds = ALG_DIGEST_SIZE_MAP.get(alg) + if not ds: + raise UnknownAlgorithmIdError(alg) + return ds + + +def get_hash_function(alg): + f = ALG_HASH_FUNCTION_MAP.get(alg) + if not f: + raise UnknownAlgorithmIdError(alg) + return f + + +def get_algorithm(name): + alg = NAME_ALG_MAP.get(name) + if not alg: + raise UnknownAlgorithmNameError(name) + return alg + + +def hex_dump(d): + d = [format(ord(x), '02x') for x in d] + d = [d[i: i + 16] for i in xrange(0, len(d), 16)] + d = [' '.join(x) for x in d] + d = os.linesep.join(d) + + return d + + +class SimulatorError(Exception): + def __init__(self, str): + self.str = str + + def __str__(self): + return self.str + + +class Simulator: + SIM_POWER_ON = 1 + SIM_POWER_OFF = 2 + SIM_SEND_COMMAND = 8 + SIM_NV_ON = 11 + SIM_NV_OFF = 12 + SIM_REMOTE_HANDSHAKE = 15 + + def __init__(self, host = 'localhost'): + self.cmd_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.cmd_socket.connect((host, 2321)) + self.sig_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sig_socket.connect((host, 2322)) + + self.cmd_socket.sendall(struct.pack('>II', Simulator.SIM_REMOTE_HANDSHAKE, 1)) + + ver = struct.unpack('>I', self.cmd_socket.recv(4))[0] + if ver != 1: + raise SimulatorError('Incompatible TPM/proxy') + + self.cmd_socket.recv(4) + self.__get_ack(self.cmd_socket) + + self.__send_sig(self.SIM_POWER_ON) + self.__send_sig(self.SIM_NV_ON) + self.host = host + + def __del__(self): + self.__send_sig(self.SIM_POWER_OFF) + self.__send_sig(self.SIM_NV_OFF) + + def send_cmd(self, cmd): + cmd = struct.pack('>IBI%us' % len(cmd), Simulator.SIM_SEND_COMMAND, 0, + len(cmd), str(cmd)) + self.cmd_socket.sendall(cmd) + + cnt = 0 + while cnt == 0: + cnt = struct.unpack('>I', self.cmd_socket.recv(4))[0] + + rsp = b'' + while len(rsp) != cnt: + rsp += self.cmd_socket.recv(cnt - len(rsp)) + + return str(rsp) + + def __send_sig(self, sig): + self.sig_socket.sendall(struct.pack('>I', sig)) + self.__get_ack(self.sig_socket) + + def __get_ack(self, sock): + rc = struct.unpack('>I', sock.recv(4))[0] + if rc != 0: + if rc == 1: + raise SimulatorError("Operation failed") + else: + raise SimulatorError("Bad end tag for operation") + +class Client: + FLAG_DEBUG = 0x01 + + def __init__(self, flags = 0, simulator = None): + self.flags = flags + self.simulator = simulator + + def send_cmd(self, cmd): + if self.simulator: + rsp = self.simulator.send_cmd(cmd) + else: + f = open('/dev/tpm0', 'r+b') + f.write(cmd) + rsp = f.read() + f.close() + + if (self.flags & Client.FLAG_DEBUG) != 0: + sys.stderr.write('cmd' + os.linesep) + sys.stderr.write(hex_dump(cmd) + os.linesep) + sys.stderr.write('rsp' + os.linesep) + sys.stderr.write(hex_dump(rsp) + os.linesep) + + rc = struct.unpack('>I', rsp[6:10])[0] + if rc != 0: + cc = struct.unpack('>I', cmd[6:10])[0] + raise ProtocolError(cc, rc, rsp) + + return rsp + + def read_pcr(self, i, bank_alg = TPM2_ALG_SHA1): + pcrsel_len = max((i >> 3) + 1, 3) + pcrsel = [0] * pcrsel_len + pcrsel[i >> 3] = 1 << (i & 7) + pcrsel = ''.join(map(chr, pcrsel)) + + fmt = '>HII IHB%us' % (pcrsel_len) + cmd = struct.pack(fmt, + TPM2_ST_NO_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_PCR_READ, + 1, + bank_alg, + pcrsel_len, pcrsel) + + rsp = self.send_cmd(cmd) + + pcr_update_cnt, pcr_select_cnt = struct.unpack('>II', rsp[10:18]) + assert pcr_select_cnt == 1 + rsp = rsp[18:] + + alg2, pcrsel_len2 = struct.unpack('>HB', rsp[:3]) + assert bank_alg == alg2 and pcrsel_len == pcrsel_len2 + rsp = rsp[3 + pcrsel_len:] + + digest_cnt = struct.unpack('>I', rsp[:4])[0] + if digest_cnt == 0: + return None + rsp = rsp[6:] + + return rsp + + def extend_pcr(self, i, dig, bank_alg = TPM2_ALG_SHA1): + ds = get_digest_size(bank_alg) + assert(ds == len(dig)) + + auth_cmd = AuthCommand() + + fmt = '>HII I I%us IH%us' % (len(auth_cmd), ds) + cmd = struct.pack( + fmt, + TPM2_ST_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_PCR_EXTEND, + i, + len(auth_cmd), + str(auth_cmd), + 1, bank_alg, dig) + + self.send_cmd(cmd) + + def start_auth_session(self, session_type, name_alg = TPM2_ALG_SHA1): + fmt = '>HII IIH16sHBHH' + cmd = struct.pack(fmt, + TPM2_ST_NO_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_START_AUTH_SESSION, + TPM2_RH_NULL, + TPM2_RH_NULL, + 16, + '\0' * 16, + 0, + session_type, + TPM2_ALG_NULL, + name_alg) + + return struct.unpack('>I', self.send_cmd(cmd)[10:14])[0] + + def __calc_pcr_digest(self, pcrs, bank_alg = TPM2_ALG_SHA1, + digest_alg = TPM2_ALG_SHA1): + x = [] + f = get_hash_function(digest_alg) + + for i in pcrs: + pcr = self.read_pcr(i, bank_alg) + if pcr == None: + return None + x += pcr + + return f(bytearray(x)).digest() + + def policy_pcr(self, handle, pcrs, bank_alg = TPM2_ALG_SHA1, + name_alg = TPM2_ALG_SHA1): + ds = get_digest_size(name_alg) + dig = self.__calc_pcr_digest(pcrs, bank_alg, name_alg) + if not dig: + raise UnknownPCRBankError(bank_alg) + + pcrsel_len = max((max(pcrs) >> 3) + 1, 3) + pcrsel = [0] * pcrsel_len + for i in pcrs: + pcrsel[i >> 3] |= 1 << (i & 7) + pcrsel = ''.join(map(chr, pcrsel)) + + fmt = '>HII IH%usIHB3s' % ds + cmd = struct.pack(fmt, + TPM2_ST_NO_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_POLICY_PCR, + handle, + len(dig), str(dig), + 1, + bank_alg, + pcrsel_len, pcrsel) + + self.send_cmd(cmd) + + def policy_password(self, handle): + fmt = '>HII I' + cmd = struct.pack(fmt, + TPM2_ST_NO_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_POLICY_PASSWORD, + handle) + + self.send_cmd(cmd) + + def get_policy_digest(self, handle): + fmt = '>HII I' + cmd = struct.pack(fmt, + TPM2_ST_NO_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_POLICY_GET_DIGEST, + handle) + + return self.send_cmd(cmd)[12:] + + def flush_context(self, handle): + fmt = '>HIII' + cmd = struct.pack(fmt, + TPM2_ST_NO_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_FLUSH_CONTEXT, + handle) + + self.send_cmd(cmd) + + def create_root_key(self, auth_value = ''): + attributes = \ + Public.FIXED_TPM | \ + Public.FIXED_PARENT | \ + Public.SENSITIVE_DATA_ORIGIN | \ + Public.USER_WITH_AUTH | \ + Public.RESTRICTED | \ + Public.DECRYPT + + auth_cmd = AuthCommand() + sensitive = SensitiveCreate(user_auth=auth_value) + + public_parms = struct.pack( + '>HHHHHI', + TPM2_ALG_AES, + 128, + TPM2_ALG_CFB, + TPM2_ALG_NULL, + 2048, + 0) + + public = Public( + object_type=TPM2_ALG_RSA, + name_alg=TPM2_ALG_SHA1, + object_attributes=attributes, + parameters=public_parms) + + fmt = '>HIII I%us H%us H%us HI' % \ + (len(auth_cmd), len(sensitive), len(public)) + cmd = struct.pack( + fmt, + TPM2_ST_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_CREATE_PRIMARY, + TPM2_RH_OWNER, + len(auth_cmd), + str(auth_cmd), + len(sensitive), + str(sensitive), + len(public), + str(public), + 0, 0) + + return struct.unpack('>I', self.send_cmd(cmd)[10:14])[0] + + def seal(self, parent_key, data, auth_value, policy_dig, + name_alg = TPM2_ALG_SHA1): + ds = get_digest_size(name_alg) + assert(not policy_dig or ds == len(policy_dig)) + + attributes = 0 + if not policy_dig: + attributes |= Public.USER_WITH_AUTH + policy_dig = '' + + auth_cmd = AuthCommand() + sensitive = SensitiveCreate(user_auth=auth_value, data=data) + + public = Public( + object_type=TPM2_ALG_KEYEDHASH, + name_alg=name_alg, + object_attributes=attributes, + auth_policy=policy_dig, + parameters=struct.pack('>H', TPM2_ALG_NULL)) + + fmt = '>HIII I%us H%us H%us HI' % \ + (len(auth_cmd), len(sensitive), len(public)) + cmd = struct.pack( + fmt, + TPM2_ST_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_CREATE, + parent_key, + len(auth_cmd), + str(auth_cmd), + len(sensitive), + str(sensitive), + len(public), + str(public), + 0, 0) + + rsp = self.send_cmd(cmd) + + return rsp[14:] + + def unseal(self, parent_key, blob, auth_value, policy_handle): + private_len = struct.unpack('>H', blob[0:2])[0] + public_start = private_len + 2 + public_len = struct.unpack('>H', blob[public_start:public_start + 2])[0] + blob = blob[:private_len + public_len + 4] + + auth_cmd = AuthCommand() + + fmt = '>HII I I%us %us' % (len(auth_cmd), len(blob)) + cmd = struct.pack( + fmt, + TPM2_ST_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_LOAD, + parent_key, + len(auth_cmd), + str(auth_cmd), + blob) + + data_handle = struct.unpack('>I', self.send_cmd(cmd)[10:14])[0] + + if policy_handle: + auth_cmd = AuthCommand(session_handle=policy_handle, hmac=auth_value) + else: + auth_cmd = AuthCommand(hmac=auth_value) + + fmt = '>HII I I%us' % (len(auth_cmd)) + cmd = struct.pack( + fmt, + TPM2_ST_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_UNSEAL, + data_handle, + len(auth_cmd), + str(auth_cmd)) + + try: + rsp = self.send_cmd(cmd) + finally: + self.flush_context(data_handle) + + data_len = struct.unpack('>I', rsp[10:14])[0] - 2 + + return rsp[16:16 + data_len] + + def reset_da_lock(self): + auth_cmd = AuthCommand() + + fmt = '>HII I I%us' % (len(auth_cmd)) + cmd = struct.pack( + fmt, + TPM2_ST_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_DICTIONARY_ATTACK_LOCK_RESET, + TPM2_RH_LOCKOUT, + len(auth_cmd), + str(auth_cmd)) + + self.send_cmd(cmd) + + def __get_cap_cnt(self, cap, pt, cnt): + handles = [] + fmt = '>HII III' + + cmd = struct.pack(fmt, + TPM2_ST_NO_SESSIONS, + struct.calcsize(fmt), + TPM2_CC_GET_CAPABILITY, + cap, pt, cnt) + + rsp = self.send_cmd(cmd)[10:] + more_data, cap, cnt = struct.unpack('>BII', rsp[:9]) + rsp = rsp[9:] + + for i in xrange(0, cnt): + handle = struct.unpack('>I', rsp[:4])[0] + handles.append(handle) + rsp = rsp[4:] + + return handles, more_data + + def get_cap(self, cap, pt): + handles = [] + + more_data = True + while more_data: + next_handles, more_data = self.__get_cap_cnt(cap, pt, 1) + handles += next_handles + pt += 1 + + return handles diff --git a/tpm2_smoke.py b/tpm2_smoke.py new file mode 100755 index 0000000..80bb4aa --- /dev/null +++ b/tpm2_smoke.py @@ -0,0 +1,165 @@ +#! /usr/bin/env python +# Copyright (c) 2015, 2016 Intel Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from argparse import ArgumentParser +from argparse import FileType +import os +import sys +import tpm2 +from tpm2 import ProtocolError +import unittest + + +class SmokeTest(unittest.TestCase): + def setUp(self): + self.client = tpm2.Client() + self.root_key = self.client.create_root_key() + + def tearDown(self): + self.client.flush_context(self.root_key) + + def test_seal_with_auth(self): + data = 'X' * 64 + auth = 'A' * 15 + + blob = self.client.seal(self.root_key, data, auth, None) + result = self.client.unseal(self.root_key, blob, auth, None) + self.assertEqual(data, result) + + def test_seal_with_policy(self): + handle = self.client.start_auth_session(tpm2.TPM2_SE_TRIAL) + + data = 'X' * 64 + auth = 'A' * 15 + pcrs = [16] + + try: + self.client.policy_pcr(handle, pcrs) + self.client.policy_password(handle) + + policy_dig = self.client.get_policy_digest(handle) + finally: + self.client.flush_context(handle) + + blob = self.client.seal(self.root_key, data, auth, policy_dig) + + handle = self.client.start_auth_session(tpm2.TPM2_SE_POLICY) + + try: + self.client.policy_pcr(handle, pcrs) + self.client.policy_password(handle) + + result = self.client.unseal(self.root_key, blob, auth, handle) + except: + self.client.flush_context(handle) + raise + + self.assertEqual(data, result) + + def test_unseal_with_wrong_auth(self): + data = 'X' * 64 + auth = 'A' * 20 + rc = 0 + + blob = self.client.seal(self.root_key, data, auth, None) + try: + result = self.client.unseal(self.root_key, blob, auth[:-1] + 'B', None) + except ProtocolError, e: + rc = e.rc + + self.assertEqual(rc, tpm2.TPM2_RC_AUTH_FAIL) + + def test_unseal_with_wrong_policy(self): + handle = self.client.start_auth_session(tpm2.TPM2_SE_TRIAL) + + data = 'X' * 64 + auth = 'A' * 17 + pcrs = [16] + + try: + self.client.policy_pcr(handle, pcrs) + self.client.policy_password(handle) + + policy_dig = self.client.get_policy_digest(handle) + finally: + self.client.flush_context(handle) + + blob = self.client.seal(self.root_key, data, auth, policy_dig) + + # Extend first a PCR that is not part of the policy and try to unseal. + # This should succeed. + + ds = tpm2.get_digest_size(tpm2.TPM2_ALG_SHA1) + self.client.extend_pcr(1, 'X' * ds) + + handle = self.client.start_auth_session(tpm2.TPM2_SE_POLICY) + + try: + self.client.policy_pcr(handle, pcrs) + self.client.policy_password(handle) + + result = self.client.unseal(self.root_key, blob, auth, handle) + except: + self.client.flush_context(handle) + raise + + self.assertEqual(data, result) + + # Then, extend a PCR that is part of the policy and try to unseal. + # This should fail. + self.client.extend_pcr(16, 'X' * ds) + + handle = self.client.start_auth_session(tpm2.TPM2_SE_POLICY) + + rc = 0 + + try: + self.client.policy_pcr(handle, pcrs) + self.client.policy_password(handle) + + result = self.client.unseal(self.root_key, blob, auth, handle) + except ProtocolError, e: + rc = e.rc + self.client.flush_context(handle) + except: + self.client.flush_context(handle) + raise + + self.assertEqual(rc, tpm2.TPM2_RC_POLICY_FAIL) + + def test_seal_with_too_long_auth(self): + ds = tpm2.get_digest_size(tpm2.TPM2_ALG_SHA1) + data = 'X' * 64 + auth = 'A' * (ds + 1) + + rc = 0 + try: + blob = self.client.seal(self.root_key, data, auth, None) + except ProtocolError, e: + rc = e.rc + + self.assertEqual(rc, tpm2.TPM2_RC_SIZE) |