Date   
Re: [Ibmtpm20tss-users] [openssl-tpm2-engine] ibmtss

Doug Fraser
 

Morning Ken.

I apologize for my mixed up terminology on this topic. If I am using the wrong terms, point it out, and if possible, reference a section in an existing document.
I have been reading like a fiend and porting/coding as I go.

Isn't the openssl engine dynamically loading the tpm key dynamically each time it uses it?
I thought the key that we generated was just related to the internal key for validation reasons to associate the key with that physical initialized TPM?

From an earlier email I sent to James: (direct quote)

====
It gets shoved into a JSON in base64 format for storage on the device.
The device-tree hooks convert that back to native key text format for presentation in /proc space

From device initialization code (runs just once....)

echo "Generating new unit key..."
send_station "Generating new unit key..."

# use the TPM to create the key…
create_tpm2_key -p 81000001 --ecc prime256v1 /tmp/openssl-key.tpm

jq ".unit.\"tpm2_key#\" = \"$(cat /tmp/openssl-key.tpm | base64 -w0)\"" /tmp/upd.json > /tmp/upd-new.json

json2upd.py --json /tmp/upd-new.json --increment --upd "${FLASH}p$UPD_PRI"
dd "if=${FLASH}p$UPD_PRI" "of=${FLASH}p$UPD_BAK" bs=1M
====

So we have a key that we created on our initialized TPM stored into raw device storage, that get instantiated in /proc device space as its own text representation.

It seems to be keeping openssl (via the engine) completely happy at this point.
I have tested by reinitializing the TPM hardware so the key no longer matched the device, and the engine fails, until I go back and generate a new key.

Douglas Fraser

Re: [Ibmtpm20tss-users] [openssl-tpm2-engine] ibmtss

Ken Goldman <kgold@...>
 

On 1/13/2019 11:45 AM, James Bottomley wrote:
On Sun, 2019-01-13 at 16:07 +0000, Doug Fraser wrote:
James, Ken,

Thanks again for your help and feedback. I am learning this all at a
brisk pace. This past fall was my introduction to TPM as anything
other than a technology buzz word.

James, we are using a TPM key blob from create_tpm2_key that is tied
to a fixed key at 80000001.
Well, firstly, volatile keyhandles aren't deterministic (it won't
always be 80000001), but hopefully you already coped with that in your
script.
Just FYI - not recommended because of resource limitations ...

The TPM does have non-volatile key slots. The usual use is
early in boot, when there is no other key storage. These keys
will have fixed handles.

A typical TPM has 7 slots, reserved by convention as follows:

3 for root keys
1 for the platform OEM
3 for OS and applications

Re: TSS aligned with TPM2 engine

James Bottomley
 

On Mon, 2019-01-14 at 14:47 +0100, Fredrik Ternerot wrote:
On Sat, Dec 22, 2018 at 7:21 PM James Bottomley
<James.Bottomley@...> wrote:

On Fri, 2018-12-21 at 15:22 +0000, Doug Fraser wrote:
[...]
On to openssl-tpm2-engine:

I had to make one small change to openssl-tpm2-engine before
running
bootstrap/configure prior to the build.
Right after pulling the git tree, at the top of the tree I do:

#$ sed -i 's/ create_tpm2_key.1//' Makefile.am

This removes a documentation dependency on help2man.
This is required because I am cross-compiling, and I cannot
execute
`create_tpm2_key --help` on the build host to extract the
document.

It would be helpful if there were a configure option to block
documentation generation completely.
Well, as I said, I've never actually done a cross
compile.  However,
leafing through the somewhat confusing automake documentation on
cross
compiles, I think this is the fix.
I can confirm that this solves the problem with help2man for me.

The changes in configure.ac are present in the latest commit (b43aa97
Version: 2.1.1). Would you mind to add the changes in Makefile.am as
well?
Heh, well, I was supposed to be keeping that local to my tree until
someone tested it, but it must have got partially pushed with the
version update. Thanks for testing, I've added a commit for the rest
of the Makefile stuff. Note, I don't think this is sufficient, but
like I said I got out of cross compiling ages ago mainly because of the
need to run make check, so I only use emulation containers nowadays, so
I'm betting there will be other issues.

James

Re: [Ibmtpm20tss-users] [openssl-tpm2-engine] ibmtss

James Bottomley
 

On Mon, 2019-01-14 at 14:33 +0000, Doug Fraser wrote:
Morning Ken.

I apologize for my mixed up terminology on this topic. If I am using
the wrong terms, point it out, and if possible, reference a section
in an existing document. I have been reading like a fiend and
porting/coding as I go.

Isn't the openssl engine dynamically loading the tpm key dynamically
each time it uses it? I thought the key that we generated was just
related to the internal key for validation reasons to associate the
key with that physical initialized TPM?
Essentially, yes. The file is reduced to the binary key form which is
kept in memory for the lifetime of the engine (so it's not loading a
file each time). But when you ask for a signature (the only universal
operation), the sequence of TPM commands is

TPM2_Load
TPM2_StartAuthSession
TPM2_Sign
TPM2_FlushContext

So it is loading the key from the memory area each time. This pretty
much corresponds to best practice, even internally to a single
application because you want to keep TPM resources tied up for the
smallest amount of time. In theory it is possible to keep the key and
the session loaded in TPM volatile memory, but this can lead to
resource issues if the application uses more than three keys.

If you're worried about time taken by the TPM operations, then actually
the TPM2_Load isn't the problem one (it's a simple aes128 decryption),
the heavy one is TPM2_StartAuthSession because we use a
cryptographically salted session and that means the TPM has to use the
primary storage key to derive the encrypted salt.

James

From an earlier email I sent to James: (direct quote)

====
It gets shoved into a JSON in base64 format for storage on the
device.
The device-tree hooks convert that back to native key text format for
presentation in /proc space

From device initialization code (runs just once....)

    echo "Generating new unit key..."
    send_station "Generating new unit key..."

    # use the TPM to create the key…
    create_tpm2_key -p 81000001 --ecc prime256v1 /tmp/openssl-key.tpm

    jq ".unit.\"tpm2_key#\" = \"$(cat /tmp/openssl-key.tpm | base64
-w0)\"" /tmp/upd.json > /tmp/upd-new.json

    json2upd.py --json /tmp/upd-new.json --increment --upd
"${FLASH}p$UPD_PRI"
    dd "if=${FLASH}p$UPD_PRI" "of=${FLASH}p$UPD_BAK" bs=1M
====

So we have a key that we created on our initialized TPM stored into
raw device storage, that get instantiated in /proc device space as
its own text representation.

It seems to be keeping openssl (via the engine) completely happy at
this point.
I have tested by reinitializing the TPM hardware so the key no longer
matched the device, and the engine fails, until I go back and
generate a new key.

Douglas Fraser




Re: TSS aligned with TPM2 engine

Fredrik Ternerot
 

On Mon, Jan 14, 2019 at 3:38 PM James Bottomley
<James.Bottomley@...> wrote:

On Mon, 2019-01-14 at 14:47 +0100, Fredrik Ternerot wrote:
On Sat, Dec 22, 2018 at 7:21 PM James Bottomley
<James.Bottomley@...> wrote:

On Fri, 2018-12-21 at 15:22 +0000, Doug Fraser wrote:
[...]
On to openssl-tpm2-engine:

I had to make one small change to openssl-tpm2-engine before
running
bootstrap/configure prior to the build.
Right after pulling the git tree, at the top of the tree I do:

#$ sed -i 's/ create_tpm2_key.1//' Makefile.am

This removes a documentation dependency on help2man.
This is required because I am cross-compiling, and I cannot
execute
`create_tpm2_key --help` on the build host to extract the
document.

It would be helpful if there were a configure option to block
documentation generation completely.
Well, as I said, I've never actually done a cross
compile. However,
leafing through the somewhat confusing automake documentation on
cross
compiles, I think this is the fix.
I can confirm that this solves the problem with help2man for me.

The changes in configure.ac are present in the latest commit (b43aa97
Version: 2.1.1). Would you mind to add the changes in Makefile.am as
well?
Heh, well, I was supposed to be keeping that local to my tree until
someone tested it, but it must have got partially pushed with the
version update. Thanks for testing, I've added a commit for the rest
of the Makefile stuff. Note, I don't think this is sufficient, but
like I said I got out of cross compiling ages ago mainly because of the
need to run make check, so I only use emulation containers nowadays, so
I'm betting there will be other issues.
You are right, another issue is the detection of enginesdir in
configure.ac. This is done by generating a test program that is
compiled and executed, which doesn't work when cross compiling. Do you
know any other ways to do it?

Thanks,
Fredrik

Re: TSS aligned with TPM2 engine

James Bottomley
 

On Mon, 2019-01-21 at 12:01 +0100, Fredrik Ternerot wrote:
On Mon, Jan 14, 2019 at 3:38 PM James Bottomley
<James.Bottomley@...> wrote:

On Mon, 2019-01-14 at 14:47 +0100, Fredrik Ternerot wrote:
On Sat, Dec 22, 2018 at 7:21 PM James Bottomley
<James.Bottomley@...> wrote:

On Fri, 2018-12-21 at 15:22 +0000, Doug Fraser wrote:
[...]
On to openssl-tpm2-engine:

I had to make one small change to openssl-tpm2-engine before
running bootstrap/configure prior to the build.
Right after pulling the git tree, at the top of the tree I
do:

#$ sed -i 's/ create_tpm2_key.1//' Makefile.am

This removes a documentation dependency on help2man.
This is required because I am cross-compiling, and I cannot
execute
`create_tpm2_key --help` on the build host to extract the
document.

It would be helpful if there were a configure option to block
documentation generation completely.
Well, as I said, I've never actually done a cross
compile. However, leafing through the somewhat confusing
automake documentation on cross compiles, I think this is the
fix.
I can confirm that this solves the problem with help2man for me.

The changes in configure.ac are present in the latest commit
(b43aa97 Version: 2.1.1). Would you mind to add the changes in
Makefile.am as well?
Heh, well, I was supposed to be keeping that local to my tree until
someone tested it, but it must have got partially pushed with the
version update. Thanks for testing, I've added a commit for the
rest of the Makefile stuff. Note, I don't think this is
sufficient, but like I said I got out of cross compiling ages ago
mainly because of the need to run make check, so I only use
emulation containers nowadays, so I'm betting there will be other
issues.
You are right, another issue is the detection of enginesdir in
configure.ac. This is done by generating a test program that is
compiled and executed, which doesn't work when cross compiling. Do
you know any other ways to do it?
Yes, we can use pkg-config to get that. The reason we didn't before is
that openSUSE actually had the wrong directory in the openssl.pc file
(so you couldn't build working engines on openSUSE unless you detected the engines directory yourself). They've since fixed this as a bug and it should now work on all distributions.

James

[PATCH] configure.ac: Use pkg-config to determine engines directory

Fredrik Ternerot
 

Use pkg-config to determine engines directory based on libcrypto.pc.
Previously the directory was determined by compiling and executing a
test program which caused problem when cross compiling.

Signed-off-by: Fredrik Ternerot <fredrikt@...>
---
configure.ac | 15 +--------------
1 file changed, 1 insertion(+), 14 deletions(-)

diff --git a/configure.ac b/configure.ac
index 3c4127d..a2ff6e4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -52,20 +52,7 @@ AC_ARG_WITH(openssl,
AC_SUBST(OPENSSL_LIB_DIR)
AC_SUBST(OPENSSL_INCLUDE_DIR)])

-AC_LANG(C)
-AC_LANG_CONFTEST(
- [AC_LANG_PROGRAM(
- [[#define HEADER_CRYPTLIB_H
- #include <openssl/crypto.h>
- #include <stdio.h>]],
- [[#if OPENSSL_VERSION_NUMBER < 0x10100000
- puts(ENGINESDIR);
- #else
- puts(OpenSSL_version(OPENSSL_ENGINES_DIR));
- #endif
- ]])])
-gcc $CFLAGS conftest.c -lcrypto
-enginesdir=`./a.out|sed 's/ENGINESDIR: //'`
+PKG_CHECK_VAR([enginesdir], [libcrypto], [enginesdir])
if test -z "$enginesdir" ; then
AC_MSG_FAILURE([Failed to find SSL engines directory])
fi
--
2.11.0

Re: [PATCH] configure.ac: Use pkg-config to determine engines directory

James Bottomley
 

On Tue, 2019-01-22 at 15:12 +0100, Fredrik Ternerot wrote:
Use pkg-config to determine engines directory based on libcrypto.pc.
Previously the directory was determined by compiling and executing a
test program which caused problem when cross compiling.

Signed-off-by: Fredrik Ternerot <fredrikt@...>
---
configure.ac | 15 +--------------
1 file changed, 1 insertion(+), 14 deletions(-)

diff --git a/configure.ac b/configure.ac
index 3c4127d..a2ff6e4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -52,20 +52,7 @@ AC_ARG_WITH(openssl,
AC_SUBST(OPENSSL_LIB_DIR)
AC_SUBST(OPENSSL_INCLUDE_DIR)])

-AC_LANG(C)
-AC_LANG_CONFTEST(
- [AC_LANG_PROGRAM(
- [[#define HEADER_CRYPTLIB_H
- #include <openssl/crypto.h>
- #include <stdio.h>]],
- [[#if OPENSSL_VERSION_NUMBER < 0x10100000
- puts(ENGINESDIR);
- #else
- puts(OpenSSL_version(OPENSSL_ENGINES_DIR));
- #endif
- ]])])
-gcc $CFLAGS conftest.c -lcrypto
-enginesdir=`./a.out|sed 's/ENGINESDIR: //'`
+PKG_CHECK_VAR([enginesdir], [libcrypto], [enginesdir])
if test -z "$enginesdir" ; then
AC_MSG_FAILURE([Failed to find SSL engines directory])
fi
I'm afraid I already tried this. It doesn't work on older versions of
openSUSE because the package config bug in openssl isn't fixed there.
Would this work instead? It's what I currently have for fixing all the
engine problems (including naming). It allows you to pass in the
engines directory as --with-enginesdir=<dir>?

James

---

From b90ea81c299282e1ab59761464bfb2a9df34ecf7 Mon Sep 17 00:00:00 2001
From: James Bottomley <James.Bottomley@...>
Date: Mon, 21 Jan 2019 12:59:06 -0800
Subject: [PATCH] Fix up engine installation

Add an additional --with-enginesdir parameter to allow for specifying
the engine directory instad of probing for it. In addition install both
engine.so and libengine.so because we don't know which one openssl
will be looking for since it depends on the openssl version.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
Makefile.am | 4 ++++
configure.ac | 41 +++++++++++++++++++++++++++++++----------
2 files changed, 35 insertions(+), 10 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 7d3b645..48c6189 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,4 +23,8 @@ create_tpm2_key_CFLAGS=${DEPS_CFLAGS} -Werror
$(builddir)/%.1: $(srcdir)/%.1.in $(top_builddir)/%
$(HELP2MAN) --no-info -i $< -o $@ $(top_builddir)/$*

+# openssl from 1.1.0 looks for engine.so not libengine.so
+install-data-hook:
+ cd '$(DESTDIR)$(openssl_enginedir)' && $(LN_S) -f libtpm2@SHREXT@ tpm2@SHREXT@
+
SUBDIRS = tests
diff --git a/configure.ac b/configure.ac
index 3c4127d..5260ff4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,9 +3,10 @@
#

AC_INIT(openssl-tpm2-engine, 2.1.1, <James.Bottomley@...>)
-AM_INIT_AUTOMAKE(1.6.3)
+AM_INIT_AUTOMAKE([foreign 1.6.3])
AC_CANONICAL_HOST
AM_CONDITIONAL(NATIVE_BUILD, test "x$cross_compiling" = "xno")
+PKG_PROG_PKG_CONFIG

AM_MISSING_PROG(HELP2MAN, help2man)

@@ -28,6 +29,7 @@ fi
##
# ibmtss >= 1234 now requires TPM_POSIX be set
CFLAGS="$CFLAGS -DTPM_POSIX"
+CPPFLAGS="$CPPFLAGS -DTPM_POSIX"

#if test "${OPENSSL_LIB_DIR+set}" != set; then
# OPENSSL_LIB_DIR="/usr/local/ssl/lib"
@@ -53,26 +55,35 @@ AC_ARG_WITH(openssl,
AC_SUBST(OPENSSL_INCLUDE_DIR)])

AC_LANG(C)
-AC_LANG_CONFTEST(
- [AC_LANG_PROGRAM(
- [[#define HEADER_CRYPTLIB_H
+AC_ARG_WITH(
+ [enginesdir],
+ [AC_HELP_STRING([--with-enginesdir], [OpenSSL engines directory])],
+ [enginesdir="${withval}"],
+ [AC_LANG_CONFTEST([AC_LANG_PROGRAM(
+ [[
+ #define HEADER_CRYPTLIB_H
#include <openssl/crypto.h>
- #include <stdio.h>]],
- [[#if OPENSSL_VERSION_NUMBER < 0x10100000
+ #include <stdio.h>
+ ]],
+ [[
+ #if OPENSSL_VERSION_NUMBER < 0x10100000
puts(ENGINESDIR);
#else
puts(OpenSSL_version(OPENSSL_ENGINES_DIR));
#endif
- ]])])
-gcc $CFLAGS conftest.c -lcrypto
-enginesdir=`./a.out|sed 's/ENGINESDIR: //'`
+ ]]
+ )])
+ gcc $CFLAGS conftest.c -lcrypto
+ enginesdir=`./a.out|sed 's/ENGINESDIR: //'`
+ ]
+ )
+
if test -z "$enginesdir" ; then
AC_MSG_FAILURE([Failed to find SSL engines directory])
fi

AC_SUBST(enginesdir)

-PKG_PROG_PKG_CONFIG
PKG_CHECK_MODULES([DEPS], [libcrypto])

AC_SEARCH_LIBS([TSS_Create], [tss ibmtss], [], [
@@ -85,14 +96,24 @@ AM_PROG_CC_C_O
AC_USE_SYSTEM_EXTENSIONS
AC_SYS_LARGEFILE
AC_PROG_LIBTOOL
+AC_PROG_LN_S

AC_CHECK_HEADER([tss2/tss.h],[AC_DEFINE(TSS_INCLUDE,tss2)],
AC_CHECK_HEADER([ibmtss/tss.h],[AC_DEFINE(TSS_INCLUDE,ibmtss)],
AC_MSG_ERROR([No TSS2 include directory found])))

CFLAGS="$CFLAGS -Wall"
+SHREXT=$shrext_cmds
AC_SUBST(CFLAGS)
AC_SUBST(TSS_INCLUDE)
+AC_SUBST(SHREXT)

AC_OUTPUT([Makefile tests/Makefile])

+cat <<EOF
+
+CFLAGS: ${CFLAGS}
+openssl engines directory: ${enginesdir}
+
+EOF
+
--
2.16.4

Re: [PATCH] configure.ac: Use pkg-config to determine engines directory

Fredrik Ternerot
 

On Tue, Jan 22, 2019 at 4:19 PM James Bottomley
<James.Bottomley@...> wrote:

On Tue, 2019-01-22 at 15:12 +0100, Fredrik Ternerot wrote:
Use pkg-config to determine engines directory based on libcrypto.pc.
Previously the directory was determined by compiling and executing a
test program which caused problem when cross compiling.

Signed-off-by: Fredrik Ternerot <fredrikt@...>
---
configure.ac | 15 +--------------
1 file changed, 1 insertion(+), 14 deletions(-)

diff --git a/configure.ac b/configure.ac
index 3c4127d..a2ff6e4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -52,20 +52,7 @@ AC_ARG_WITH(openssl,
AC_SUBST(OPENSSL_LIB_DIR)
AC_SUBST(OPENSSL_INCLUDE_DIR)])

-AC_LANG(C)
-AC_LANG_CONFTEST(
- [AC_LANG_PROGRAM(
- [[#define HEADER_CRYPTLIB_H
- #include <openssl/crypto.h>
- #include <stdio.h>]],
- [[#if OPENSSL_VERSION_NUMBER < 0x10100000
- puts(ENGINESDIR);
- #else
- puts(OpenSSL_version(OPENSSL_ENGINES_DIR));
- #endif
- ]])])
-gcc $CFLAGS conftest.c -lcrypto
-enginesdir=`./a.out|sed 's/ENGINESDIR: //'`
+PKG_CHECK_VAR([enginesdir], [libcrypto], [enginesdir])
if test -z "$enginesdir" ; then
AC_MSG_FAILURE([Failed to find SSL engines directory])
fi
I'm afraid I already tried this. It doesn't work on older versions of
openSUSE because the package config bug in openssl isn't fixed there.
Would this work instead? It's what I currently have for fixing all the
engine problems (including naming). It allows you to pass in the
engines directory as --with-enginesdir=<dir>?
This is definitely in the right direction. I would however still need
to use pkg-config because I need to support building for different
openssl versions (using a common recipe in OpenEmbedded). What do you
think about adding a configure option to control whether pkg-config or
test program should be used? The default could then be test program
but would allow overriding when feasible.

Is there a way to detect if the enginesdir from pkg-config in old
openSUSE is wrong and in that case fallback to use test program? Would
it be as easy as checking if the directory exists? Then maybe we
wouldn't need any extra configure option for this.

Also, when using test program, it should probably be conditional on
NATIVE_BUILD.

Fredrik

---

From b90ea81c299282e1ab59761464bfb2a9df34ecf7 Mon Sep 17 00:00:00 2001
From: James Bottomley <James.Bottomley@...>
Date: Mon, 21 Jan 2019 12:59:06 -0800
Subject: [PATCH] Fix up engine installation

Add an additional --with-enginesdir parameter to allow for specifying
the engine directory instad of probing for it. In addition install both
engine.so and libengine.so because we don't know which one openssl
will be looking for since it depends on the openssl version.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
Makefile.am | 4 ++++
configure.ac | 41 +++++++++++++++++++++++++++++++----------
2 files changed, 35 insertions(+), 10 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 7d3b645..48c6189 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,4 +23,8 @@ create_tpm2_key_CFLAGS=${DEPS_CFLAGS} -Werror
$(builddir)/%.1: $(srcdir)/%.1.in $(top_builddir)/%
$(HELP2MAN) --no-info -i $< -o $@ $(top_builddir)/$*

+# openssl from 1.1.0 looks for engine.so not libengine.so
+install-data-hook:
+ cd '$(DESTDIR)$(openssl_enginedir)' && $(LN_S) -f libtpm2@SHREXT@ tpm2@SHREXT@
+
SUBDIRS = tests
diff --git a/configure.ac b/configure.ac
index 3c4127d..5260ff4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,9 +3,10 @@
#

AC_INIT(openssl-tpm2-engine, 2.1.1, <James.Bottomley@...>)
-AM_INIT_AUTOMAKE(1.6.3)
+AM_INIT_AUTOMAKE([foreign 1.6.3])
AC_CANONICAL_HOST
AM_CONDITIONAL(NATIVE_BUILD, test "x$cross_compiling" = "xno")
+PKG_PROG_PKG_CONFIG

AM_MISSING_PROG(HELP2MAN, help2man)

@@ -28,6 +29,7 @@ fi
##
# ibmtss >= 1234 now requires TPM_POSIX be set
CFLAGS="$CFLAGS -DTPM_POSIX"
+CPPFLAGS="$CPPFLAGS -DTPM_POSIX"

#if test "${OPENSSL_LIB_DIR+set}" != set; then
# OPENSSL_LIB_DIR="/usr/local/ssl/lib"
@@ -53,26 +55,35 @@ AC_ARG_WITH(openssl,
AC_SUBST(OPENSSL_INCLUDE_DIR)])

AC_LANG(C)
-AC_LANG_CONFTEST(
- [AC_LANG_PROGRAM(
- [[#define HEADER_CRYPTLIB_H
+AC_ARG_WITH(
+ [enginesdir],
+ [AC_HELP_STRING([--with-enginesdir], [OpenSSL engines directory])],
+ [enginesdir="${withval}"],
+ [AC_LANG_CONFTEST([AC_LANG_PROGRAM(
+ [[
+ #define HEADER_CRYPTLIB_H
#include <openssl/crypto.h>
- #include <stdio.h>]],
- [[#if OPENSSL_VERSION_NUMBER < 0x10100000
+ #include <stdio.h>
+ ]],
+ [[
+ #if OPENSSL_VERSION_NUMBER < 0x10100000
puts(ENGINESDIR);
#else
puts(OpenSSL_version(OPENSSL_ENGINES_DIR));
#endif
- ]])])
-gcc $CFLAGS conftest.c -lcrypto
-enginesdir=`./a.out|sed 's/ENGINESDIR: //'`
+ ]]
+ )])
+ gcc $CFLAGS conftest.c -lcrypto
+ enginesdir=`./a.out|sed 's/ENGINESDIR: //'`
+ ]
+ )
+
if test -z "$enginesdir" ; then
AC_MSG_FAILURE([Failed to find SSL engines directory])
fi

AC_SUBST(enginesdir)

-PKG_PROG_PKG_CONFIG
PKG_CHECK_MODULES([DEPS], [libcrypto])

AC_SEARCH_LIBS([TSS_Create], [tss ibmtss], [], [
@@ -85,14 +96,24 @@ AM_PROG_CC_C_O
AC_USE_SYSTEM_EXTENSIONS
AC_SYS_LARGEFILE
AC_PROG_LIBTOOL
+AC_PROG_LN_S

AC_CHECK_HEADER([tss2/tss.h],[AC_DEFINE(TSS_INCLUDE,tss2)],
AC_CHECK_HEADER([ibmtss/tss.h],[AC_DEFINE(TSS_INCLUDE,ibmtss)],
AC_MSG_ERROR([No TSS2 include directory found])))

CFLAGS="$CFLAGS -Wall"
+SHREXT=$shrext_cmds
AC_SUBST(CFLAGS)
AC_SUBST(TSS_INCLUDE)
+AC_SUBST(SHREXT)

AC_OUTPUT([Makefile tests/Makefile])

+cat <<EOF
+
+CFLAGS: ${CFLAGS}
+openssl engines directory: ${enginesdir}
+
+EOF
+
--
2.16.4



Re: TSS aligned with TPM2 engine

Fredrik Ternerot
 

On Mon, Jan 14, 2019 at 2:48 PM Fredrik Ternerot via Groups.Io
<fredrik.trot=gmail.com@groups.io> wrote:

On Sat, Dec 22, 2018 at 7:21 PM James Bottomley
<James.Bottomley@...> wrote:

On Fri, 2018-12-21 at 15:22 +0000, Doug Fraser wrote:
[...]
On to openssl-tpm2-engine:

I had to make one small change to openssl-tpm2-engine before running
bootstrap/configure prior to the build.
Right after pulling the git tree, at the top of the tree I do:

#$ sed -i 's/ create_tpm2_key.1//' Makefile.am

This removes a documentation dependency on help2man.
This is required because I am cross-compiling, and I cannot execute
`create_tpm2_key --help` on the build host to extract the document.

It would be helpful if there were a configure option to block
documentation generation completely.
Well, as I said, I've never actually done a cross compile. However,
leafing through the somewhat confusing automake documentation on cross
compiles, I think this is the fix.
I can confirm that this solves the problem with help2man for me.
This did solve it for me when it comes to building for target, but
when building for native (needed for unittests) I still have problem
because help2man is not available in my build environment. As
previously suggested, it would be helpful if there was a configure
option to disable man page generation completely. Would that be an
acceptable option to add?

Fredrik

[PATCH] e_tpm-rsa.c: add additional padding types

James Bottomley
 

Up to now we've only handled PKCS1 padding, but since this is being
deprecated by NIST, we need to handle all the others. It turns out
that the RSA layer of openssl can only really handle OAEP with
mgf1(sha1) or unpadded, so add both of those to the engine. All other
sophisticated padding in openssl is handled at the pmeth layer and
thus only requires an unpadded key operation at the RSA layer, which
we've now added, so the tpm2 engine should now work for any padding
type supported by openssl.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
e_tpm2-rsa.c | 49 +++++++++++++++++++++++++++++----------------
tests/Makefile.am | 1 +
tests/check_rsa_oaep_pss.sh | 25 +++++++++++++++++++++++
3 files changed, 58 insertions(+), 17 deletions(-)
create mode 100755 tests/check_rsa_oaep_pss.sh

diff --git a/e_tpm2-rsa.c b/e_tpm2-rsa.c
index 3be6302..fe82bc6 100644
--- a/e_tpm2-rsa.c
+++ b/e_tpm2-rsa.c
@@ -180,12 +180,19 @@ static int tpm2_rsa_priv_dec(int flen,
}

rv = -1;
- if (padding != RSA_PKCS1_PADDING) {
- fprintf(stderr, "Non PKCS1 padding asked for\n");
+ if (padding == RSA_PKCS1_PADDING) {
+ in.inScheme.scheme = TPM_ALG_RSAES;
+ } else if (padding == RSA_NO_PADDING) {
+ in.inScheme.scheme = TPM_ALG_NULL;
+ } else if (padding == RSA_PKCS1_OAEP_PADDING) {
+ in.inScheme.scheme = TPM_ALG_OAEP;
+ /* for openssl RSA, the padding is hard coded */
+ in.inScheme.details.oaep.hashAlg = TPM_ALG_SHA1;
+ } else {
+ fprintf(stderr, "Can't process padding type: %d\n", padding);
goto out;
}

- in.inScheme.scheme = TPM_ALG_RSAES;
in.cipherText.t.size = flen;
memcpy(in.cipherText.t.buffer, from, flen);
in.label.t.size = 0;
@@ -243,8 +250,28 @@ static int tpm2_rsa_priv_enc(int flen,
struct policy_command *commands;
TPM_ALG_ID nameAlg;

- if (padding != RSA_PKCS1_PADDING) {
- fprintf(stderr, "Non PKCS1 padding asked for\n");
+ /* this is slightly paradoxical that we're doing a Decrypt
+ * operation: the only material difference between decrypt and
+ * encrypt is where the padding is applied or checked, so if
+ * you apply your own padding up to the RSA block size and use
+ * TPM_ALG_NULL, which means no padding check, a decrypt
+ * operation effectively becomes an encrypt */
+ size = RSA_size(rsa);
+ in.inScheme.scheme = TPM_ALG_NULL;
+ in.cipherText.t.size = size;
+ in.label.t.size = 0;
+
+ /* note: currently openssl doesn't do OAEP signatures and all
+ * PSS signatures are padded and handled in the RSA layer
+ * as a no-padding private encryption */
+ if (padding == RSA_PKCS1_PADDING) {
+ RSA_padding_add_PKCS1_type_1(in.cipherText.t.buffer, size,
+ from, flen);
+ } else if (padding == RSA_NO_PADDING) {
+ /* do nothing, we're already doing a no padding encrypt */
+ memcpy(in.cipherText.t.buffer, from, size);
+ } else {
+ fprintf(stderr, "Can't process padding type: %d\n", padding);
return -1;
}

@@ -271,18 +298,6 @@ static int tpm2_rsa_priv_enc(int flen,
goto out;
}

- /* this is slightly paradoxical that we're doing a Decrypt
- * operation: the only material difference between decrypt and
- * encrypt is where the padding is applied or checked, so if
- * you apply your own padding up to the RSA block size and use
- * TPM_ALG_NULL, which means no padding check, a decrypt
- * operation effectively becomes an encrypt */
- size = RSA_size(rsa);
- in.inScheme.scheme = TPM_ALG_NULL;
- in.cipherText.t.size = size;
- RSA_padding_add_PKCS1_type_1(in.cipherText.t.buffer, size, from, flen);
- in.label.t.size = 0;
-
rc = TSS_Execute(tssContext,
(RESPONSE_PARAMETERS *)&out,
(COMMAND_PARAMETERS *)&in,
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 0294dd0..d9cb3b8 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -18,6 +18,7 @@ TESTS = fail_connect.sh \
check_enhanced_auth.sh \
check_counter_timer.sh \
check_importable.sh \
+ check_rsa_oaep_pss.sh \
stop_sw_tpm.sh

AM_TESTS_ENVIRONMENT = TPM_INTERFACE_TYPE=socsim; \
diff --git a/tests/check_rsa_oaep_pss.sh b/tests/check_rsa_oaep_pss.sh
new file mode 100755
index 0000000..76793a6
--- /dev/null
+++ b/tests/check_rsa_oaep_pss.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+bindir=${srcdir}/..
+
+openssl genrsa 2048 > key.priv || exit 1
+openssl rsa -in key.priv -out key.pub -pubout || exit 1
+${bindir}/create_tpm2_key --wrap key.priv -a -k passw0rd key.tpm || exit 1
+echo "Checked encryption of OAEP PSS" > tmp.txt
+openssl rsautl -encrypt -oaep -in tmp.txt -out tmp.msg -inkey key.pub -pubin || exit 1
+openssl rsautl -decrypt -oaep -in tmp.msg -engine tpm2 -keyform engine -inkey key.tpm -passin pass:passw0rd || exit 1
+##
+# this PSS signature will be padded manually and done as an unpadded encrypt
+# by the TPM
+##
+openssl sha256 -out tmp.md -binary tmp.txt || exit 1
+openssl pkeyutl -sign -engine tpm2 -keyform engine -inkey key.tpm -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha256 -pkeyopt rsa_mgf1_md:sha256 -in tmp.md -out tmp.msg -passin pass:passw0rd || exit 1
+# OpenSSL bug in some versions returns false for correct signature
+openssl pkeyutl -verify -inkey key.pub -pubin -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha256 -pkeyopt rsa_mgf1_md:sha256 -in tmp.md -sigfile tmp.msg|grep 'Signature Verified Successfully'|| exit 1
+##
+# finally an OAEP encrypt which triggers an unpadded decrypt
+##
+openssl pkeyutl -encrypt -inkey key.pub -pubin -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256 -in tmp.txt -out tmp.msg || exit 1
+openssl pkeyutl -decrypt -engine tpm2 -keyform engine -inkey key.tpm -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256 -in tmp.msg -out recover.txt -passin pass:passw0rd || exit 1
+diff -q tmp.txt recover.txt || exit 1
+
--
2.16.4

[PATCH 0/4] add restricted parents allowing the same loadable key to be used by different TPMs

James Bottomley
 

This patch series adds restricted keys, which may be used as storage
keys and have child keys. For wrapped keys, we use the public and
private parameters to create the symmetric seed, thus meaning that the
wrapping of the same private key always has the same symmetric seed.
Since the symmetric seed is used in all child protections, this allows
keys parented to the wrapped key to be transported seamlessly between
different TPMs. The use case for this is the cloud one, where we'd
like to seed a set of physical systems with these wrapped parents such
that a child key may be loaded correctly and used by every such
physical system in the cloud.

James Bottomley (4):
create_tpm2_key: add a --restricted option
Make removal of key files from the temporary directory explicit
load_tpm2_key: add new command to load a key file to a NV handle
Add tests for restricted keys

.gitignore | 1 +
Makefile.am | 10 +-
create_tpm2_key.c | 76 ++++++++++++++-
e_tpm2.c | 3 +-
load_tpm2_key.1.in | 27 ++++++
load_tpm2_key.c | 234 +++++++++++++++++++++++++++++++++++++++++++++
tests/Makefile.am | 1 +
tests/restricted_parent.sh | 63 ++++++++++++
tpm2-common.c | 13 ++-
tpm2-common.h | 3 +-
10 files changed, 418 insertions(+), 13 deletions(-)
create mode 100644 load_tpm2_key.1.in
create mode 100644 load_tpm2_key.c
create mode 100755 tests/restricted_parent.sh

--
2.16.4

[PATCH 1/4] create_tpm2_key: add a --restricted option

James Bottomley
 

Right at the moment we create unrestricted signing and decryption
keys. These keys are the most useful for cryptographic operations,
but they cannot be used as parents for any other key. The addition of
the --restricted option allows the creation of restricted decryption
keys (aka storage keys) which can be used as parents for other keys.

One of the requirements of storage keys is that they must have a
symmetric seed that can be used to protect the sensitive parts of
child keys. For wrapped keys, we derive the symmetric seed from the
public and private parts of the wrapped key, meaning the same wrapped
key always has the same symmetric seed. This allows child keys of a
wrapped parent to be transported between TPMs.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
create_tpm2_key.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)

diff --git a/create_tpm2_key.c b/create_tpm2_key.c
index 1412cb7..598ad61 100644
--- a/create_tpm2_key.c
+++ b/create_tpm2_key.c
@@ -42,6 +42,7 @@
#define NOT_TPM_ERROR (0xffffffff)

#define OPT_DEPRECATED 0x1ff
+#define OPT_RESTRICTED 0x1fe

static struct option long_options[] = {
{"auth", 0, 0, 'a'},
@@ -59,6 +60,7 @@ static struct option long_options[] = {
{"da", 0, 0, 'd'},
{"key-policy", 1, 0, 'c'},
{"import", 1, 0, 'i'},
+ {"restricted", 0, 0, OPT_RESTRICTED},
/*
* The option --deprecated allows us to create old format keys
* for the purposes of testing. It should never be used in
@@ -101,6 +103,10 @@ usage(char *argv0)
"\t-c, --key-policy Specify a policy for the TPM key\n"
"\t-i, --import <pubkey> Create an importable key with the outer\n"
" wrapper encrypted to <pubkey>\n"
+ "\t--restricted Create a restricted key. A restricted key\n"
+ " may not be used for general signing or\n"
+ " decryption but may be the parent of other\n"
+ " keys (i.e. it is a storage key)\n"
"\n"
"Report bugs to " PACKAGE_BUGREPORT "\n",
argv0);
@@ -933,6 +939,54 @@ void free_policy(STACK_OF(TSSOPTPOLICY) *sk)
sk_TSSOPTPOLICY_free(sk);
}

+/*
+ * A restricted key needs a symmetric seed and algorithm so it can
+ * derive a symmetric encryption key used to protect the sensitive
+ * parts of child objects. The requirement is that the symmetric seed
+ * be the same size as the name algorithm hash. We elect to generate
+ * the symmetric seed from the hash of the public and private parts of
+ * the key meaning the same wrapped private key always generates the
+ * same symmetric seed. This means that any child key will be
+ * loadable by any parent created from the wrapped key (including a
+ * parent wrapped for a different TPM)
+ */
+void generate_symmetric(TPMT_PUBLIC *pub, TPMT_SENSITIVE *priv)
+{
+ TPMT_HA digest;
+
+ digest.hashAlg = pub->nameAlg;
+
+ switch (pub->type) {
+ case TPM_ALG_RSA:
+ TSS_Hash_Generate(&digest,
+ pub->unique.rsa.t.size, pub->unique.rsa.t.buffer,
+ priv->sensitive.rsa.t.size, priv->sensitive.rsa.t.buffer,
+ 0, NULL);
+ pub->parameters.rsaDetail.symmetric.algorithm = TPM_ALG_AES;
+ pub->parameters.rsaDetail.symmetric.keyBits.aes = 128;
+ pub->parameters.rsaDetail.symmetric.mode.aes = TPM_ALG_CFB;
+ break;
+ case TPM_ALG_ECC:
+ TSS_Hash_Generate(&digest,
+ pub->unique.ecc.x.t.size, pub->unique.ecc.x.t.buffer,
+ pub->unique.ecc.y.t.size, pub->unique.ecc.y.t.buffer,
+ priv->sensitive.ecc.t.size, priv->sensitive.ecc.t.buffer,
+ 0, NULL);
+ pub->parameters.eccDetail.symmetric.algorithm = TPM_ALG_AES;
+ pub->parameters.eccDetail.symmetric.keyBits.aes = 128;
+ pub->parameters.eccDetail.symmetric.mode.aes = TPM_ALG_CFB;
+ break;
+ default:
+ /* impossible */
+ break;
+ }
+ priv->seedValue.b.size = TSS_GetDigestSize(digest.hashAlg);
+ memcpy(priv->seedValue.b.buffer, digest.digest.tssmax, priv->seedValue.b.size);
+ pub->objectAttributes.val |= TPMA_OBJECT_RESTRICTED;
+ /* a restricted key can't sign */
+ pub->objectAttributes.val &= ~TPMA_OBJECT_SIGN;
+}
+
int main(int argc, char **argv)
{
char *filename, *wrap = NULL, *auth = NULL, *policyFilename = NULL;
@@ -961,6 +1015,7 @@ int main(int argc, char **argv)
uint32_t sizeInBytes;
TPMT_HA digest;
TPM2B_ENCRYPTED_SECRET secret, *enc_secret = NULL;
+ int restricted = 0;

OpenSSL_add_all_digests();
/* may be needed to decrypt the key */
@@ -1050,6 +1105,9 @@ int main(int argc, char **argv)
case OPT_DEPRECATED:
version = 0;
break;
+ case OPT_RESTRICTED:
+ restricted = 1;
+ break;
default:
printf("Unknown option '%c'\n", c);
usage(argv[0]);
@@ -1171,6 +1229,9 @@ int main(int argc, char **argv)
/* set the NODA flag */
pub->publicArea.objectAttributes.val |= noda;

+ if (restricted)
+ generate_symmetric(&pub->publicArea, &s);
+
rc = tpm2_outerwrap(p_pkey, &s, &pub->publicArea,
priv, &secret);
if (rc) {
@@ -1253,6 +1314,9 @@ int main(int argc, char **argv)
/* set the NODA flag */
iin.objectPublic.publicArea.objectAttributes.val |= noda;

+ if (restricted)
+ generate_symmetric(&iin.objectPublic.publicArea, &s);
+
rc = tpm2_innerwrap(&s, &iin.objectPublic.publicArea,
&iin.symmetricAlg,
&iin.encryptionKey,
@@ -1313,6 +1377,15 @@ int main(int argc, char **argv)
cin.inPublic.publicArea.objectAttributes.val |=
noda |
TPMA_OBJECT_SENSITIVEDATAORIGIN;
+ if (restricted) {
+ cin.inPublic.publicArea.objectAttributes.val |=
+ TPMA_OBJECT_RESTRICTED;
+ cin.inPublic.publicArea.objectAttributes.val &=
+ ~TPMA_OBJECT_SIGN;
+ cin.inPublic.publicArea.parameters.asymDetail.symmetric.algorithm = TPM_ALG_AES;
+ cin.inPublic.publicArea.parameters.asymDetail.symmetric.keyBits.aes = 128;
+ cin.inPublic.publicArea.parameters.asymDetail.symmetric.mode.aes = TPM_ALG_CFB;
+ }
if (auth) {
int len = strlen(auth);
memcpy(&cin.inSensitive.sensitive.userAuth.b.buffer,
--
2.16.4

[PATCH 2/4] Make removal of key files from the temporary directory explicit

James Bottomley
 

We've been obscuring a bug in tpm2_rm_tssdir() for a while in that we
create a key file for the parent non volatile key but don't remove it
again. We fixed it up in tpm2_rm_tssdir() by hard coding the removal
of the key file belonging to 81000001. However, this won't work if we
can have an arbitrary NV parent, so make the key file removal
explicit.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
create_tpm2_key.c | 3 ++-
e_tpm2.c | 3 ++-
tpm2-common.c | 13 ++++++-------
tpm2-common.h | 3 ++-
4 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/create_tpm2_key.c b/create_tpm2_key.c
index 598ad61..5534fe3 100644
--- a/create_tpm2_key.c
+++ b/create_tpm2_key.c
@@ -1426,7 +1426,8 @@ int main(int argc, char **argv)
}
tpm2_flush_srk(tssContext, phandle);
TSS_Delete(tssContext);
- tpm2_rm_tssdir(dir, 0);
+ tpm2_rm_keyfile(dir, phandle);
+ tpm2_rm_tssdir(dir);

write_key:
buffer = pubkey;
diff --git a/e_tpm2.c b/e_tpm2.c
index 167c3bf..17e997f 100644
--- a/e_tpm2.c
+++ b/e_tpm2.c
@@ -710,7 +710,8 @@ void tpm2_delete(struct app_data *app_data)
OPENSSL_free(app_data->priv);
OPENSSL_free(app_data->pub);

- tpm2_rm_tssdir(app_data->dir, app_data->key);
+ tpm2_rm_keyfile(app_data->dir, app_data->parent);
+ tpm2_rm_tssdir(app_data->dir);

OPENSSL_free((void *)app_data->dir);

diff --git a/tpm2-common.c b/tpm2-common.c
index bf950ec..1152777 100644
--- a/tpm2-common.c
+++ b/tpm2-common.c
@@ -852,7 +852,7 @@ const char *tpm2_set_unique_tssdir(void)
return dir;
}

-static void tpm2_rm_keyfile(const char *dir, TPM_HANDLE key)
+void tpm2_rm_keyfile(const char *dir, TPM_HANDLE key)
{
char keyfile[1024];

@@ -862,13 +862,12 @@ static void tpm2_rm_keyfile(const char *dir, TPM_HANDLE key)
unlink(keyfile);
}

-void tpm2_rm_tssdir(const char *dir, TPM_HANDLE extrakey)
+void tpm2_rm_tssdir(const char *dir)
{
- if (extrakey)
- tpm2_rm_keyfile(dir, extrakey);
- tpm2_rm_keyfile(dir, 0x81000001);
- if (rmdir(dir) < 0)
- perror("Unlinking TPM_DATA_DIR");
+ if (rmdir(dir) < 0) {
+ fprintf(stderr, "Unlinking %s", dir);
+ perror(":");
+ }
}

TPM_RC tpm2_create(TSS_CONTEXT **tsscp, const char *dir)
diff --git a/tpm2-common.h b/tpm2-common.h
index 6111243..f22422b 100644
--- a/tpm2-common.h
+++ b/tpm2-common.h
@@ -32,7 +32,8 @@ const char *tpm2_set_unique_tssdir(void);
TPM_RC tpm2_create(TSS_CONTEXT **tsscp, const char *dir);
TPM_RC tpm2_readpublic(TSS_CONTEXT *tssContext, TPM_HANDLE handle,
TPMT_PUBLIC *pub);
-void tpm2_rm_tssdir(const char *dir, TPM_HANDLE extrakey);
+void tpm2_rm_tssdir(const char *dir);
+void tpm2_rm_keyfile(const char *dir, TPM_HANDLE key);
int tpm2_get_public_point(TPM2B_ECC_POINT *tpmpt, const EC_GROUP *group,
const EC_POINT *pt);
#endif
--
2.16.4

[PATCH 3/4] load_tpm2_key: add new command to load a key file to a NV handle

James Bottomley
 

In order to use restricted keys as parents, they have to be loaded
into the TPMs NV handle area, so introduce a new command to do that.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
.gitignore | 1 +
Makefile.am | 10 ++-
load_tpm2_key.1.in | 27 +++++++
load_tpm2_key.c | 234 +++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 269 insertions(+), 3 deletions(-)
create mode 100644 load_tpm2_key.1.in
create mode 100644 load_tpm2_key.c

diff --git a/.gitignore b/.gitignore
index c48a990..9eca341 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,6 +18,7 @@ libtool
ltmain.sh
missing
create_tpm2_key
+load_tpm2_key
test-driver
tests/*.log
tests/*.trs
diff --git a/Makefile.am b/Makefile.am
index dd52b2c..734bfc5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,14 +1,14 @@
EXTRA_DIST = README openssl.cnf.sample

if NATIVE_BUILD
-EXTRA_DIST += create_tpm2_key.1
-man1_MANS = create_tpm2_key.1
+EXTRA_DIST += create_tpm2_key.1 load_tpm2_key.1
+man1_MANS = create_tpm2_key.1 load_tpm2_key.1

CLEANFILES = $(man1_MANS)
endif

openssl_engine_LTLIBRARIES=libtpm2.la
-bin_PROGRAMS=create_tpm2_key
+bin_PROGRAMS=create_tpm2_key load_tpm2_key
openssl_enginedir=@enginesdir@

libtpm2_la_LDFLAGS= -no-undefined -avoid-version
@@ -20,6 +20,10 @@ create_tpm2_key_SOURCES=create_tpm2_key.c tpm2-common.c
create_tpm2_key_LDADD=${DEPS_LIBS}
create_tpm2_key_CFLAGS=${DEPS_CFLAGS} -Werror

+load_tpm2_key_SOURCES=load_tpm2_key.c tpm2-common.c
+load_tpm2_key_LDADD=${DEPS_LIBS}
+load_tpm2_key_CFLAGS=${DEPS_CFLAGS} -Werror
+
$(builddir)/%.1: $(srcdir)/%.1.in $(top_builddir)/%
$(HELP2MAN) --no-info -i $< -o $@ $(top_builddir)/$*

diff --git a/load_tpm2_key.1.in b/load_tpm2_key.1.in
new file mode 100644
index 0000000..20914e3
--- /dev/null
+++ b/load_tpm2_key.1.in
@@ -0,0 +1,27 @@
+[name]
+load_tpm2_key - load a tpm2 key at a permanent index
+
+[description]
+
+Used to load keys created by create_tpm2_key(1) to a permanent
+NV index.
+
+The reasons for doing this are either to have an unrestricted key
+always accessible to the TPM without needing a key file or to have a
+new restricted decryption key parent from which other keys can be
+created as children.
+
+[examples]
+
+Create a TPM internal key and load it at index 81000101
+
+ create_tpm2_key -p 81000001 tmp.key
+ load_tpm2_key tmp.key 81000101
+
+Create a wrapped restricted decryption key at 81000101 and use it
+as the parent of a new key
+
+ openssl genrsa 2048 > key.priv
+ create_tpm2_key --restricted -w key.priv key.tpm
+ load_tpm2_key key.tpm 81000101
+ create_tpm2_key -p 81000101 newkey.tpm
diff --git a/load_tpm2_key.c b/load_tpm2_key.c
new file mode 100644
index 0000000..123cf9f
--- /dev/null
+++ b/load_tpm2_key.c
@@ -0,0 +1,234 @@
+/*
+ *
+ * Copyright (C) 2016 James Bottomley <James.Bottomley@...>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ */
+
+
+#include <stdio.h>
+#include <getopt.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <arpa/inet.h>
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+#define TSSINCLUDE(x) < TSS_INCLUDE/x >
+#include TSSINCLUDE(tss.h)
+#include TSSINCLUDE(tssutils.h)
+#include TSSINCLUDE(tssmarshal.h)
+#include TSSINCLUDE(Unmarshal_fp.h)
+#include TSSINCLUDE(tsscrypto.h)
+#include TSSINCLUDE(tsscryptoh.h)
+
+#include "tpm2-asn.h"
+#include "tpm2-common.h"
+
+/* for use as a TPM_RC return type to indicate this is
+ * not a TPM error, so don't process the rc as one */
+#define NOT_TPM_ERROR (0xffffffff)
+
+static struct option long_options[] = {
+ {"auth-parent", 1, 0, 'b'},
+ {"force", 0, 0, 'f'},
+ {"help", 0, 0, 'h'},
+ {"version", 0, 0, 'v'},
+ {0, 0, 0, 0}
+};
+
+void
+usage(char *argv0)
+{
+ fprintf(stdout, "Usage: %s [options] <filename> <nvindex>\n\n"
+ "Options:\n"
+ "\t-b, --auth-parent <pwd> Specify the parent key password\n"
+ "\t (default EmptyAuth)\n"
+ "\t-f, --force force loading of key with policy\n"
+ "\t-h, --help print this help message\n"
+ "\n"
+ "Report bugs to " PACKAGE_BUGREPORT "\n",
+ argv0);
+ exit(-1);
+}
+
+void
+openssl_print_errors()
+{
+ ERR_load_ERR_strings();
+ ERR_load_crypto_strings();
+ ERR_print_errors_fp(stderr);
+}
+
+int main(int argc, char **argv)
+{
+ char *filename;
+ TPM_HANDLE nvindex;
+ const char *tssdir;
+ TSSPRIVKEY *tpk;
+ BIO *bf;
+ int option_index, c;
+ int force = 0;
+ TSS_CONTEXT *tssContext;
+ TPM_RC rc;
+ Load_In lin;
+ Load_Out lout;
+ EvictControl_In ein;
+ BYTE *buffer;
+ INT32 size;
+ char *auth = NULL;
+ TPM_HANDLE session, parent;
+ int ret = 1;
+
+ while (1) {
+ option_index = 0;
+ c = getopt_long(argc, argv, "b:fhv",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'f':
+ force = 1;
+ break;
+ case 'h':
+ usage(argv[0]);
+ break;
+ case 'b':
+ auth = optarg;
+ break;
+ case 'v':
+ fprintf(stdout, "%s " VERSION "\n"
+ "Copyright 2019 by James Bottomley\n"
+ "License LGPL-2.1-only\n"
+ "Written by James Bottomley <James.Bottomley@...>\n",
+ argv[0]);
+ exit(0);
+ default:
+ printf("Unknown option '%c'\n", c);
+ usage(argv[0]);
+ break;
+ }
+ }
+ if (optind >= argc - 1) {
+ printf("Too few arguments: Expected filename and nvindex");
+ usage(argv[0]);
+ }
+
+ filename = argv[argc - 2];
+ nvindex = strtoul(argv[argc - 1], NULL, 16);
+
+ if (optind < argc - 2) {
+ printf("Unexpected additional arguments\n");
+ usage(argv[0]);
+ }
+
+ if ((nvindex & 0xff000000) != 0x81000000) {
+ printf("nvindex must have MSO 81\n");
+ exit(1);
+ }
+
+ bf = BIO_new_file(filename, "r");
+ if (!bf) {
+ fprintf(stderr, "File %s does not exist or cannot be read\n", filename);
+ exit(1);
+ }
+ tpk = PEM_read_bio_TSSPRIVKEY(bf, NULL, NULL, NULL);
+ BIO_free(bf);
+
+ if (!tpk) {
+ fprintf(stderr, "Failed to parse file %s\n", filename);
+ exit(1);
+ }
+ if (tpk->policy && !force) {
+ fprintf(stderr, "Warning: key %s has associated policy\n"
+ "Policy keys are hard to use, specify --force if this is really what you want\n",
+ filename);
+ goto out_free;
+ }
+
+ buffer = tpk->privkey->data;
+ size = tpk->privkey->length;
+ TPM2B_PRIVATE_Unmarshal(&lin.inPrivate, &buffer, &size);
+
+ buffer = tpk->pubkey->data;
+ size = tpk->pubkey->length;
+ TPM2B_PUBLIC_Unmarshal(&lin.inPublic, &buffer, &size, FALSE);
+
+ parent = ASN1_INTEGER_get(tpk->parent);
+ TSSPRIVKEY_free(tpk);
+ tssdir = tpm2_set_unique_tssdir();
+ rc = tpm2_create(&tssContext, tssdir);
+ if (rc) {
+ tpm2_error(rc, "tpm2_create");
+ exit(1);
+ }
+
+ if ((parent & 0xff000000) == 0x81000000) {
+ lin.parentHandle = parent;
+ } else {
+ rc = tpm2_load_srk(tssContext, &lin.parentHandle, auth, NULL,
+ parent, 1);
+ if (rc)
+ goto out;
+ }
+ rc = tpm2_get_session_handle(tssContext, &session, lin.parentHandle,
+ TPM_SE_HMAC, TPM_ALG_SHA256);
+ if (rc)
+ goto out_flush_srk;
+ rc = TSS_Execute(tssContext,
+ (RESPONSE_PARAMETERS *)&lout,
+ (COMMAND_PARAMETERS *)&lin,
+ NULL,
+ TPM_CC_Load,
+ session, auth, 0,
+ TPM_RH_NULL, NULL, 0);
+ if (rc) {
+ tpm2_error(rc, "TPM2_Load");
+ tpm2_flush_handle(tssContext, session);
+ }
+ out_flush_srk:
+ tpm2_flush_srk(tssContext, lin.parentHandle);
+ if (rc)
+ goto out;
+
+ ein.auth = TPM_RH_OWNER;
+ ein.objectHandle = lout.objectHandle;
+ ein.persistentHandle = nvindex;
+ rc = TSS_Execute(tssContext,
+ NULL,
+ (COMMAND_PARAMETERS *)&ein,
+ NULL,
+ TPM_CC_EvictControl,
+ TPM_RS_PW, NULL, 0,
+ TPM_RH_NULL, NULL, 0);
+ if (rc)
+ tpm2_error(rc, "TPM2_EvictControl");
+ else
+ ret = 0;
+
+ tpm2_flush_handle(tssContext, lout.objectHandle);
+
+ out:
+ TSS_Delete(tssContext);
+ tpm2_rm_keyfile(tssdir, parent);
+ tpm2_rm_keyfile(tssdir, nvindex);
+ tpm2_rm_tssdir(tssdir);
+ exit(ret);
+
+ out_free:
+ TSSPRIVKEY_free(tpk);
+ exit(1);
+}
--
2.16.4

[PATCH 4/4] Add tests for restricted keys

James Bottomley
 

Add general tests of the new command plus a specific test of the
ability to create a key to a wrapped parent, clear the TPM (thus
effectively creating a new tpm), re-wrapping the key and demonstrating
that the old parented key can still be used.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
tests/Makefile.am | 1 +
tests/restricted_parent.sh | 63 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 64 insertions(+)
create mode 100755 tests/restricted_parent.sh

diff --git a/tests/Makefile.am b/tests/Makefile.am
index d9cb3b8..21da53d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -19,6 +19,7 @@ TESTS = fail_connect.sh \
check_counter_timer.sh \
check_importable.sh \
check_rsa_oaep_pss.sh \
+ restricted_parent.sh \
stop_sw_tpm.sh

AM_TESTS_ENVIRONMENT = TPM_INTERFACE_TYPE=socsim; \
diff --git a/tests/restricted_parent.sh b/tests/restricted_parent.sh
new file mode 100755
index 0000000..5121363
--- /dev/null
+++ b/tests/restricted_parent.sh
@@ -0,0 +1,63 @@
+#!/bin/bash
+set -x
+
+
+bindir=${srcdir}/..
+NV=81000101
+NV2=81000102
+
+##
+# basic restricted key creation tests for rsa, ecc both internal and wrapped
+##
+${bindir}/create_tpm2_key --restricted --rsa key.tpm || exit 1
+${bindir}/create_tpm2_key --restricted --ecc prime256v1 key.tpm || exit 1
+openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:prime256v1 -pkeyopt ec_param_enc:named_curve -out key.priv || exit 1
+${bindir}/create_tpm2_key --restricted -w key.priv key.tpm || exit 1
+openssl genrsa 2048 > key.priv || exit 1;
+${bindir}/create_tpm2_key --restricted -w key.priv key.tpm || exit 1
+
+##
+# Using the already created RSA restricted wrapped key the tests are:
+# 1. Load the restricted key into NV memory
+# 2. parent a TPM internal key1 to the new NV key
+# 3. generate a public key from key1
+# 4. Sign and verify to prove key1 works
+# 5. Clear the TPM, this renders all the existing keys unusable and
+# regenerates the storage primary seed
+# 6. re-wrap the original private key to the new TPM and move it to NV
+# 7. Sign and verify to prove key1 still works despite clearing the TPM.
+##
+${bindir}/load_tpm2_key key.tpm ${NV} || exit 1
+${bindir}/create_tpm2_key -p ${NV} key1.tpm || exit 1
+openssl rsa -engine tpm2 -inform engine -in key1.tpm -pubout -out key1.pub || exit 1
+echo "This is a test of moveable keys" | openssl rsautl -sign -engine tpm2 -engine tpm2 -keyform engine -inkey key1.tpm -out tmp.msg || exit 1
+openssl rsautl -verify -in tmp.msg -inkey key1.pub -pubin || exit 1
+
+tssclear -hi p || exit 1
+${bindir}/create_tpm2_key --restricted -w key.priv key.tpm || exit 1
+${bindir}/load_tpm2_key key.tpm ${NV} || exit 1
+
+echo "This is a test of moveable keys" | openssl rsautl -sign -engine tpm2 -engine tpm2 -keyform engine -inkey key1.tpm -out tmp.msg || exit 1
+openssl rsautl -verify -in tmp.msg -inkey key1.pub -pubin || exit 1
+
+##
+# A few more tests of the load_tpm2_key command
+# 1. check that a key with policy requires to be forced
+# 2. check the use of parent auth to load the NV area
+##
+tssclear -hi p
+${bindir}/create_tpm2_key --restricted -c policies/policy_pcr.txt key2.tpm || exit 1
+${bindir}/load_tpm2_key key2.tpm ${NV} && exit 1
+${bindir}/load_tpm2_key --force key2.tpm ${NV} || exit 1
+
+##
+# now try to parent to a key with authorization
+##
+tssclear -hi p
+${bindir}/create_tpm2_key --auth --password Passw0rd --restricted key2.tpm || exit 1
+${bindir}/load_tpm2_key key2.tpm ${NV} || exit 1
+${bindir}/create_tpm2_key --auth-parent Passw0rd --parent ${NV} key3.tpm || exit 1
+${bindir}/load_tpm2_key --auth-parent Passw0rd key3.tpm ${NV2} || exit 1
+
+
+exit 0
--
2.16.4

[PATCH v2 0/4] add restricted parents allowing the same loadable key to be used by different TPMs

James Bottomley
 

This is a tiny code change (missed one case of key file removal in
patch 2) but a big increase of the amount of testing done (patch 4)

Original cover letter:

This patch series adds restricted keys, which may be used as storage
keys and have child keys. For wrapped keys, we use the public and
private parameters to create the symmetric seed, thus meaning that the
wrapping of the same private key always has the same symmetric seed.
Since the symmetric seed is used in all child protections, this allows
keys parented to the wrapped key to be transported seamlessly between
different TPMs. The use case for this is the cloud one, where we'd
like to seed a set of physical systems with these wrapped parents such
that a child key may be loaded correctly and used by every such
physical system in the cloud.

James Bottomley (4):
create_tpm2_key: add a --restricted option
Make removal of key files from the temporary directory explicit
load_tpm2_key: add new command to load a key file to a NV handle
Add tests for restricted keys

.gitignore | 1 +
Makefile.am | 10 +-
create_tpm2_key.c | 76 ++++++++++++++-
e_tpm2.c | 5 +-
load_tpm2_key.1.in | 27 ++++++
load_tpm2_key.c | 234 +++++++++++++++++++++++++++++++++++++++++++++
tests/Makefile.am | 1 +
tests/restricted_parent.sh | 82 ++++++++++++++++
tpm2-common.c | 13 ++-
tpm2-common.h | 3 +-
10 files changed, 439 insertions(+), 13 deletions(-)
create mode 100644 load_tpm2_key.1.in
create mode 100644 load_tpm2_key.c
create mode 100755 tests/restricted_parent.sh

--
2.16.4

[PATCH v2 1/4] create_tpm2_key: add a --restricted option

James Bottomley
 

Right at the moment we create unrestricted signing and decryption
keys. These keys are the most useful for cryptographic operations,
but they cannot be used as parents for any other key. The addition of
the --restricted option allows the creation of restricted decryption
keys (aka storage keys) which can be used as parents for other keys.

One of the requirements of storage keys is that they must have a
symmetric seed that can be used to protect the sensitive parts of
child keys. For wrapped keys, we derive the symmetric seed from the
public and private parts of the wrapped key, meaning the same wrapped
key always has the same symmetric seed. This allows child keys of a
wrapped parent to be transported between TPMs.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
create_tpm2_key.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)

diff --git a/create_tpm2_key.c b/create_tpm2_key.c
index 1412cb7..598ad61 100644
--- a/create_tpm2_key.c
+++ b/create_tpm2_key.c
@@ -42,6 +42,7 @@
#define NOT_TPM_ERROR (0xffffffff)

#define OPT_DEPRECATED 0x1ff
+#define OPT_RESTRICTED 0x1fe

static struct option long_options[] = {
{"auth", 0, 0, 'a'},
@@ -59,6 +60,7 @@ static struct option long_options[] = {
{"da", 0, 0, 'd'},
{"key-policy", 1, 0, 'c'},
{"import", 1, 0, 'i'},
+ {"restricted", 0, 0, OPT_RESTRICTED},
/*
* The option --deprecated allows us to create old format keys
* for the purposes of testing. It should never be used in
@@ -101,6 +103,10 @@ usage(char *argv0)
"\t-c, --key-policy Specify a policy for the TPM key\n"
"\t-i, --import <pubkey> Create an importable key with the outer\n"
" wrapper encrypted to <pubkey>\n"
+ "\t--restricted Create a restricted key. A restricted key\n"
+ " may not be used for general signing or\n"
+ " decryption but may be the parent of other\n"
+ " keys (i.e. it is a storage key)\n"
"\n"
"Report bugs to " PACKAGE_BUGREPORT "\n",
argv0);
@@ -933,6 +939,54 @@ void free_policy(STACK_OF(TSSOPTPOLICY) *sk)
sk_TSSOPTPOLICY_free(sk);
}

+/*
+ * A restricted key needs a symmetric seed and algorithm so it can
+ * derive a symmetric encryption key used to protect the sensitive
+ * parts of child objects. The requirement is that the symmetric seed
+ * be the same size as the name algorithm hash. We elect to generate
+ * the symmetric seed from the hash of the public and private parts of
+ * the key meaning the same wrapped private key always generates the
+ * same symmetric seed. This means that any child key will be
+ * loadable by any parent created from the wrapped key (including a
+ * parent wrapped for a different TPM)
+ */
+void generate_symmetric(TPMT_PUBLIC *pub, TPMT_SENSITIVE *priv)
+{
+ TPMT_HA digest;
+
+ digest.hashAlg = pub->nameAlg;
+
+ switch (pub->type) {
+ case TPM_ALG_RSA:
+ TSS_Hash_Generate(&digest,
+ pub->unique.rsa.t.size, pub->unique.rsa.t.buffer,
+ priv->sensitive.rsa.t.size, priv->sensitive.rsa.t.buffer,
+ 0, NULL);
+ pub->parameters.rsaDetail.symmetric.algorithm = TPM_ALG_AES;
+ pub->parameters.rsaDetail.symmetric.keyBits.aes = 128;
+ pub->parameters.rsaDetail.symmetric.mode.aes = TPM_ALG_CFB;
+ break;
+ case TPM_ALG_ECC:
+ TSS_Hash_Generate(&digest,
+ pub->unique.ecc.x.t.size, pub->unique.ecc.x.t.buffer,
+ pub->unique.ecc.y.t.size, pub->unique.ecc.y.t.buffer,
+ priv->sensitive.ecc.t.size, priv->sensitive.ecc.t.buffer,
+ 0, NULL);
+ pub->parameters.eccDetail.symmetric.algorithm = TPM_ALG_AES;
+ pub->parameters.eccDetail.symmetric.keyBits.aes = 128;
+ pub->parameters.eccDetail.symmetric.mode.aes = TPM_ALG_CFB;
+ break;
+ default:
+ /* impossible */
+ break;
+ }
+ priv->seedValue.b.size = TSS_GetDigestSize(digest.hashAlg);
+ memcpy(priv->seedValue.b.buffer, digest.digest.tssmax, priv->seedValue.b.size);
+ pub->objectAttributes.val |= TPMA_OBJECT_RESTRICTED;
+ /* a restricted key can't sign */
+ pub->objectAttributes.val &= ~TPMA_OBJECT_SIGN;
+}
+
int main(int argc, char **argv)
{
char *filename, *wrap = NULL, *auth = NULL, *policyFilename = NULL;
@@ -961,6 +1015,7 @@ int main(int argc, char **argv)
uint32_t sizeInBytes;
TPMT_HA digest;
TPM2B_ENCRYPTED_SECRET secret, *enc_secret = NULL;
+ int restricted = 0;

OpenSSL_add_all_digests();
/* may be needed to decrypt the key */
@@ -1050,6 +1105,9 @@ int main(int argc, char **argv)
case OPT_DEPRECATED:
version = 0;
break;
+ case OPT_RESTRICTED:
+ restricted = 1;
+ break;
default:
printf("Unknown option '%c'\n", c);
usage(argv[0]);
@@ -1171,6 +1229,9 @@ int main(int argc, char **argv)
/* set the NODA flag */
pub->publicArea.objectAttributes.val |= noda;

+ if (restricted)
+ generate_symmetric(&pub->publicArea, &s);
+
rc = tpm2_outerwrap(p_pkey, &s, &pub->publicArea,
priv, &secret);
if (rc) {
@@ -1253,6 +1314,9 @@ int main(int argc, char **argv)
/* set the NODA flag */
iin.objectPublic.publicArea.objectAttributes.val |= noda;

+ if (restricted)
+ generate_symmetric(&iin.objectPublic.publicArea, &s);
+
rc = tpm2_innerwrap(&s, &iin.objectPublic.publicArea,
&iin.symmetricAlg,
&iin.encryptionKey,
@@ -1313,6 +1377,15 @@ int main(int argc, char **argv)
cin.inPublic.publicArea.objectAttributes.val |=
noda |
TPMA_OBJECT_SENSITIVEDATAORIGIN;
+ if (restricted) {
+ cin.inPublic.publicArea.objectAttributes.val |=
+ TPMA_OBJECT_RESTRICTED;
+ cin.inPublic.publicArea.objectAttributes.val &=
+ ~TPMA_OBJECT_SIGN;
+ cin.inPublic.publicArea.parameters.asymDetail.symmetric.algorithm = TPM_ALG_AES;
+ cin.inPublic.publicArea.parameters.asymDetail.symmetric.keyBits.aes = 128;
+ cin.inPublic.publicArea.parameters.asymDetail.symmetric.mode.aes = TPM_ALG_CFB;
+ }
if (auth) {
int len = strlen(auth);
memcpy(&cin.inSensitive.sensitive.userAuth.b.buffer,
--
2.16.4

[PATCH v2 2/4] Make removal of key files from the temporary directory explicit

James Bottomley
 

We've been obscuring a bug in tpm2_rm_tssdir() for a while in that we
create a key file for the parent non volatile key but don't remove it
again. We fixed it up in tpm2_rm_tssdir() by hard coding the removal
of the key file belonging to 81000001. However, this won't work if we
can have an arbitrary NV parent, so make the key file removal
explicit.

Signed-off-by: James Bottomley <James.Bottomley@...>

---
v2: remove app_data->key in case of nvkeys
---
create_tpm2_key.c | 3 ++-
e_tpm2.c | 5 ++++-
tpm2-common.c | 13 ++++++-------
tpm2-common.h | 3 ++-
4 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/create_tpm2_key.c b/create_tpm2_key.c
index 598ad61..5534fe3 100644
--- a/create_tpm2_key.c
+++ b/create_tpm2_key.c
@@ -1426,7 +1426,8 @@ int main(int argc, char **argv)
}
tpm2_flush_srk(tssContext, phandle);
TSS_Delete(tssContext);
- tpm2_rm_tssdir(dir, 0);
+ tpm2_rm_keyfile(dir, phandle);
+ tpm2_rm_tssdir(dir);

write_key:
buffer = pubkey;
diff --git a/e_tpm2.c b/e_tpm2.c
index 167c3bf..720d88b 100644
--- a/e_tpm2.c
+++ b/e_tpm2.c
@@ -710,7 +710,10 @@ void tpm2_delete(struct app_data *app_data)
OPENSSL_free(app_data->priv);
OPENSSL_free(app_data->pub);

- tpm2_rm_tssdir(app_data->dir, app_data->key);
+ tpm2_rm_keyfile(app_data->dir, app_data->parent);
+ /* if key was nv key, flush may not have removed file */
+ tpm2_rm_keyfile(app_data->dir, app_data->key);
+ tpm2_rm_tssdir(app_data->dir);

OPENSSL_free((void *)app_data->dir);

diff --git a/tpm2-common.c b/tpm2-common.c
index bf950ec..1152777 100644
--- a/tpm2-common.c
+++ b/tpm2-common.c
@@ -852,7 +852,7 @@ const char *tpm2_set_unique_tssdir(void)
return dir;
}

-static void tpm2_rm_keyfile(const char *dir, TPM_HANDLE key)
+void tpm2_rm_keyfile(const char *dir, TPM_HANDLE key)
{
char keyfile[1024];

@@ -862,13 +862,12 @@ static void tpm2_rm_keyfile(const char *dir, TPM_HANDLE key)
unlink(keyfile);
}

-void tpm2_rm_tssdir(const char *dir, TPM_HANDLE extrakey)
+void tpm2_rm_tssdir(const char *dir)
{
- if (extrakey)
- tpm2_rm_keyfile(dir, extrakey);
- tpm2_rm_keyfile(dir, 0x81000001);
- if (rmdir(dir) < 0)
- perror("Unlinking TPM_DATA_DIR");
+ if (rmdir(dir) < 0) {
+ fprintf(stderr, "Unlinking %s", dir);
+ perror(":");
+ }
}

TPM_RC tpm2_create(TSS_CONTEXT **tsscp, const char *dir)
diff --git a/tpm2-common.h b/tpm2-common.h
index 6111243..f22422b 100644
--- a/tpm2-common.h
+++ b/tpm2-common.h
@@ -32,7 +32,8 @@ const char *tpm2_set_unique_tssdir(void);
TPM_RC tpm2_create(TSS_CONTEXT **tsscp, const char *dir);
TPM_RC tpm2_readpublic(TSS_CONTEXT *tssContext, TPM_HANDLE handle,
TPMT_PUBLIC *pub);
-void tpm2_rm_tssdir(const char *dir, TPM_HANDLE extrakey);
+void tpm2_rm_tssdir(const char *dir);
+void tpm2_rm_keyfile(const char *dir, TPM_HANDLE key);
int tpm2_get_public_point(TPM2B_ECC_POINT *tpmpt, const EC_GROUP *group,
const EC_POINT *pt);
#endif
--
2.16.4

[PATCH v2 3/4] load_tpm2_key: add new command to load a key file to a NV handle

James Bottomley
 

In order to use restricted keys as parents, they have to be loaded
into the TPMs NV handle area, so introduce a new command to do that.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
.gitignore | 1 +
Makefile.am | 10 ++-
load_tpm2_key.1.in | 27 +++++++
load_tpm2_key.c | 234 +++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 269 insertions(+), 3 deletions(-)
create mode 100644 load_tpm2_key.1.in
create mode 100644 load_tpm2_key.c

diff --git a/.gitignore b/.gitignore
index c48a990..9eca341 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,6 +18,7 @@ libtool
ltmain.sh
missing
create_tpm2_key
+load_tpm2_key
test-driver
tests/*.log
tests/*.trs
diff --git a/Makefile.am b/Makefile.am
index dd52b2c..734bfc5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,14 +1,14 @@
EXTRA_DIST = README openssl.cnf.sample

if NATIVE_BUILD
-EXTRA_DIST += create_tpm2_key.1
-man1_MANS = create_tpm2_key.1
+EXTRA_DIST += create_tpm2_key.1 load_tpm2_key.1
+man1_MANS = create_tpm2_key.1 load_tpm2_key.1

CLEANFILES = $(man1_MANS)
endif

openssl_engine_LTLIBRARIES=libtpm2.la
-bin_PROGRAMS=create_tpm2_key
+bin_PROGRAMS=create_tpm2_key load_tpm2_key
openssl_enginedir=@enginesdir@

libtpm2_la_LDFLAGS= -no-undefined -avoid-version
@@ -20,6 +20,10 @@ create_tpm2_key_SOURCES=create_tpm2_key.c tpm2-common.c
create_tpm2_key_LDADD=${DEPS_LIBS}
create_tpm2_key_CFLAGS=${DEPS_CFLAGS} -Werror

+load_tpm2_key_SOURCES=load_tpm2_key.c tpm2-common.c
+load_tpm2_key_LDADD=${DEPS_LIBS}
+load_tpm2_key_CFLAGS=${DEPS_CFLAGS} -Werror
+
$(builddir)/%.1: $(srcdir)/%.1.in $(top_builddir)/%
$(HELP2MAN) --no-info -i $< -o $@ $(top_builddir)/$*

diff --git a/load_tpm2_key.1.in b/load_tpm2_key.1.in
new file mode 100644
index 0000000..20914e3
--- /dev/null
+++ b/load_tpm2_key.1.in
@@ -0,0 +1,27 @@
+[name]
+load_tpm2_key - load a tpm2 key at a permanent index
+
+[description]
+
+Used to load keys created by create_tpm2_key(1) to a permanent
+NV index.
+
+The reasons for doing this are either to have an unrestricted key
+always accessible to the TPM without needing a key file or to have a
+new restricted decryption key parent from which other keys can be
+created as children.
+
+[examples]
+
+Create a TPM internal key and load it at index 81000101
+
+ create_tpm2_key -p 81000001 tmp.key
+ load_tpm2_key tmp.key 81000101
+
+Create a wrapped restricted decryption key at 81000101 and use it
+as the parent of a new key
+
+ openssl genrsa 2048 > key.priv
+ create_tpm2_key --restricted -w key.priv key.tpm
+ load_tpm2_key key.tpm 81000101
+ create_tpm2_key -p 81000101 newkey.tpm
diff --git a/load_tpm2_key.c b/load_tpm2_key.c
new file mode 100644
index 0000000..123cf9f
--- /dev/null
+++ b/load_tpm2_key.c
@@ -0,0 +1,234 @@
+/*
+ *
+ * Copyright (C) 2016 James Bottomley <James.Bottomley@...>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ */
+
+
+#include <stdio.h>
+#include <getopt.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <arpa/inet.h>
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+#define TSSINCLUDE(x) < TSS_INCLUDE/x >
+#include TSSINCLUDE(tss.h)
+#include TSSINCLUDE(tssutils.h)
+#include TSSINCLUDE(tssmarshal.h)
+#include TSSINCLUDE(Unmarshal_fp.h)
+#include TSSINCLUDE(tsscrypto.h)
+#include TSSINCLUDE(tsscryptoh.h)
+
+#include "tpm2-asn.h"
+#include "tpm2-common.h"
+
+/* for use as a TPM_RC return type to indicate this is
+ * not a TPM error, so don't process the rc as one */
+#define NOT_TPM_ERROR (0xffffffff)
+
+static struct option long_options[] = {
+ {"auth-parent", 1, 0, 'b'},
+ {"force", 0, 0, 'f'},
+ {"help", 0, 0, 'h'},
+ {"version", 0, 0, 'v'},
+ {0, 0, 0, 0}
+};
+
+void
+usage(char *argv0)
+{
+ fprintf(stdout, "Usage: %s [options] <filename> <nvindex>\n\n"
+ "Options:\n"
+ "\t-b, --auth-parent <pwd> Specify the parent key password\n"
+ "\t (default EmptyAuth)\n"
+ "\t-f, --force force loading of key with policy\n"
+ "\t-h, --help print this help message\n"
+ "\n"
+ "Report bugs to " PACKAGE_BUGREPORT "\n",
+ argv0);
+ exit(-1);
+}
+
+void
+openssl_print_errors()
+{
+ ERR_load_ERR_strings();
+ ERR_load_crypto_strings();
+ ERR_print_errors_fp(stderr);
+}
+
+int main(int argc, char **argv)
+{
+ char *filename;
+ TPM_HANDLE nvindex;
+ const char *tssdir;
+ TSSPRIVKEY *tpk;
+ BIO *bf;
+ int option_index, c;
+ int force = 0;
+ TSS_CONTEXT *tssContext;
+ TPM_RC rc;
+ Load_In lin;
+ Load_Out lout;
+ EvictControl_In ein;
+ BYTE *buffer;
+ INT32 size;
+ char *auth = NULL;
+ TPM_HANDLE session, parent;
+ int ret = 1;
+
+ while (1) {
+ option_index = 0;
+ c = getopt_long(argc, argv, "b:fhv",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'f':
+ force = 1;
+ break;
+ case 'h':
+ usage(argv[0]);
+ break;
+ case 'b':
+ auth = optarg;
+ break;
+ case 'v':
+ fprintf(stdout, "%s " VERSION "\n"
+ "Copyright 2019 by James Bottomley\n"
+ "License LGPL-2.1-only\n"
+ "Written by James Bottomley <James.Bottomley@...>\n",
+ argv[0]);
+ exit(0);
+ default:
+ printf("Unknown option '%c'\n", c);
+ usage(argv[0]);
+ break;
+ }
+ }
+ if (optind >= argc - 1) {
+ printf("Too few arguments: Expected filename and nvindex");
+ usage(argv[0]);
+ }
+
+ filename = argv[argc - 2];
+ nvindex = strtoul(argv[argc - 1], NULL, 16);
+
+ if (optind < argc - 2) {
+ printf("Unexpected additional arguments\n");
+ usage(argv[0]);
+ }
+
+ if ((nvindex & 0xff000000) != 0x81000000) {
+ printf("nvindex must have MSO 81\n");
+ exit(1);
+ }
+
+ bf = BIO_new_file(filename, "r");
+ if (!bf) {
+ fprintf(stderr, "File %s does not exist or cannot be read\n", filename);
+ exit(1);
+ }
+ tpk = PEM_read_bio_TSSPRIVKEY(bf, NULL, NULL, NULL);
+ BIO_free(bf);
+
+ if (!tpk) {
+ fprintf(stderr, "Failed to parse file %s\n", filename);
+ exit(1);
+ }
+ if (tpk->policy && !force) {
+ fprintf(stderr, "Warning: key %s has associated policy\n"
+ "Policy keys are hard to use, specify --force if this is really what you want\n",
+ filename);
+ goto out_free;
+ }
+
+ buffer = tpk->privkey->data;
+ size = tpk->privkey->length;
+ TPM2B_PRIVATE_Unmarshal(&lin.inPrivate, &buffer, &size);
+
+ buffer = tpk->pubkey->data;
+ size = tpk->pubkey->length;
+ TPM2B_PUBLIC_Unmarshal(&lin.inPublic, &buffer, &size, FALSE);
+
+ parent = ASN1_INTEGER_get(tpk->parent);
+ TSSPRIVKEY_free(tpk);
+ tssdir = tpm2_set_unique_tssdir();
+ rc = tpm2_create(&tssContext, tssdir);
+ if (rc) {
+ tpm2_error(rc, "tpm2_create");
+ exit(1);
+ }
+
+ if ((parent & 0xff000000) == 0x81000000) {
+ lin.parentHandle = parent;
+ } else {
+ rc = tpm2_load_srk(tssContext, &lin.parentHandle, auth, NULL,
+ parent, 1);
+ if (rc)
+ goto out;
+ }
+ rc = tpm2_get_session_handle(tssContext, &session, lin.parentHandle,
+ TPM_SE_HMAC, TPM_ALG_SHA256);
+ if (rc)
+ goto out_flush_srk;
+ rc = TSS_Execute(tssContext,
+ (RESPONSE_PARAMETERS *)&lout,
+ (COMMAND_PARAMETERS *)&lin,
+ NULL,
+ TPM_CC_Load,
+ session, auth, 0,
+ TPM_RH_NULL, NULL, 0);
+ if (rc) {
+ tpm2_error(rc, "TPM2_Load");
+ tpm2_flush_handle(tssContext, session);
+ }
+ out_flush_srk:
+ tpm2_flush_srk(tssContext, lin.parentHandle);
+ if (rc)
+ goto out;
+
+ ein.auth = TPM_RH_OWNER;
+ ein.objectHandle = lout.objectHandle;
+ ein.persistentHandle = nvindex;
+ rc = TSS_Execute(tssContext,
+ NULL,
+ (COMMAND_PARAMETERS *)&ein,
+ NULL,
+ TPM_CC_EvictControl,
+ TPM_RS_PW, NULL, 0,
+ TPM_RH_NULL, NULL, 0);
+ if (rc)
+ tpm2_error(rc, "TPM2_EvictControl");
+ else
+ ret = 0;
+
+ tpm2_flush_handle(tssContext, lout.objectHandle);
+
+ out:
+ TSS_Delete(tssContext);
+ tpm2_rm_keyfile(tssdir, parent);
+ tpm2_rm_keyfile(tssdir, nvindex);
+ tpm2_rm_tssdir(tssdir);
+ exit(ret);
+
+ out_free:
+ TSSPRIVKEY_free(tpk);
+ exit(1);
+}
--
2.16.4