Date   
Re: [tpm2] Support for EAP-TLS with openssl TPM2 engine

James Bottomley
 

On Thu, 2019-03-14 at 09:41 -0700, David Woodhouse wrote:
On Thu, 2019-03-14 at 09:19 -0700, Andersen, John wrote:
Sweet! I'd been working on this too, I just got the hostapd /
wpa_supplicant test stuff working and was going to try setting
pkcs11_engine_path=/usr/lib/opensc/engine_pkcs11.so

To the tpm2-software/tpm2-pcks11 library.

From the amount of changes it took you, it seems like your approach
is cleaner, so, out of curiousity, what was your ratoinal for going
with this approach? I was going to try the pcks11 way because I
came across

https://w1.fi/cgit/hostap/plain/wpa_supplicant/examples/openCryptok
i.conf

But, I haven't gotten to it yet as I got sidetracked right after I
got the tests up and running.
If you are using the TPMv2 PKCS#11 token (or indeed any PKCS#11
token) and it's installed correctly, it ought to Just Work.
The big problem is which token? There are enough TPM PKCS11
implementations to fill a small warehouse. The Thomas Habets one seems
to be the best thought out:

https://github.com/ThomasHabets/simple-tpm-pk11

But it's TPM1.2. The big problem with any TPM to PKCS11 interface is
that PKCS11 expects resident keys and the TPM expects to use volatile
ones this causes massive provisioning and use pain because it's not
just drop in and go. This als seems to be the source of the broken
for some use cases problem in each of these implementations ... and
obviously when you find that an implementation is broken for your use
case you write your own ...

You should be able to just give a PKCS#11 URI in place of a filename
for any key or certificate, and any well-behaved application will do
the right thing. I believe wpa_supplicant meets that definition of
"well-behaved application", by automatically using the PKCS#11 ENGINE
when the "filename" it's given is actually a PKCS#11 URI.
As I said, my solution was a generic OpenSSL engine to PKCS11 exporter,
but it still needs a config file.

James

Re: [tpm2] Support for EAP-TLS with openssl TPM2 engine

David Woodhouse
 

On Thu, 2019-03-14 at 09:19 -0700, Andersen, John wrote:
Sweet! I'd been working on this too, I just got the hostapd / wpa_supplicant
test stuff working and was going to try setting
pkcs11_engine_path=/usr/lib/opensc/engine_pkcs11.so

To the tpm2-software/tpm2-pcks11 library.

From the amount of changes it took you, it seems like your approach is cleaner,
so, out of curiousity, what was your ratoinal for going with this approach? I
was going to try the pcks11 way because I came across

https://w1.fi/cgit/hostap/plain/wpa_supplicant/examples/openCryptoki.conf

But, I haven't gotten to it yet as I got sidetracked right after I got the tests
up and running.
If you are using the TPMv2 PKCS#11 token (or indeed any PKCS#11 token)
and it's installed correctly, it ought to Just Work.

You should be able to just give a PKCS#11 URI in place of a filename
for any key or certificate, and any well-behaved application will do
the right thing. I believe wpa_supplicant meets that definition of
"well-behaved application", by automatically using the PKCS#11 ENGINE
when the "filename" it's given is actually a PKCS#11 URI.

(Except for the fact that all ENGINE loading is broken in
wpa_supplicant right now because the init call was removed, as noted).

Re: [tpm2] Support for EAP-TLS with openssl TPM2 engine

James Bottomley
 

On Thu, 2019-03-14 at 09:19 -0700, Andersen, John wrote:
On Wed, Mar 13, 2019 at 04:56:17PM -0700, David Woodhouse wrote:
On Sat, 2019-01-12 at 12:59 +0100, huger@... wrote:
Hi

I am working on a plan to operate mobile Linux computers and
would like them to use corporate wifi with EAP TLS. Security
requires us to safely store the client key. Easiest solution
would be in the computer's TPM 2.0 device.

So far we were not successful trying to set this up with
wpa_supplicant and pkcs11 engine. Even if successful a solution
with these multiple layers of abstraction is quite complex and
therefore might be prone to updates of any of the components.

So I wonder if there are plans to directly support openssl tpm2
engine?
Here's a quick hack to make it work by abusing the OpenSC engine
config, as a proof of concept. Making it work cleanly so that it
can be merged is left as an exercise for the reader, or perhaps an
interested party in one of the mailing lists I've added to Cc.
Well, you can't have the engine name hard coded ... that really needs
to be some type of parameter, which is going to be 99% of the hassle
making a proper patch ...

I have no idea why wpa_supplicant doesn't call
ENGINE_load_dynamic() for current versions of OpenSSL. That seems
to be a bug, which is why I've removed the #ifdefs around it.

Signed-off-by: David Woodhouse <dwmw2@...> (not that
you'd want to apply it as-is).

diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 705fa29a3..546d3a598 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -864,7 +864,7 @@ static int tls_engine_load_dynamic_pkcs11(const
char *pkcs11_so_path,
*/
static int tls_engine_load_dynamic_opensc(const char
*opensc_so_path)
{
- char *engine_id = "opensc";
+ char *engine_id = "tpm2";
const char *pre_cmd[] = {
"SO_PATH", NULL /* opensc_so_path */,
"ID", NULL /* engine_id */,
@@ -1034,10 +1034,8 @@ void * tls_init(const struct tls_config
*conf)

#ifndef OPENSSL_NO_ENGINE
wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine");
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
ERR_load_ENGINE_strings();
ENGINE_load_dynamic();
-#endif /* OPENSSL_VERSION_NUMBER */

if (conf &&
(conf->opensc_engine_path || conf->pkcs11_engine_path
||
diff --git a/wpa_supplicant/wpa_supplicant.conf
b/wpa_supplicant/wpa_supplicant.conf
index 1bd43b226..b3045fb3d 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -174,7 +174,7 @@ fast_reauth=1
# private_key option appear to be a PKCS#11 URI, and these options
# should not need to be used explicitly.
# make the opensc engine available
-#opensc_engine_path=/usr/lib/opensc/engine_opensc.so
+opensc_engine_path=/home/dwmw/git/openssl_tpm2_engine/.libs/libtpm
2.so
# make the pkcs11 engine available
#pkcs11_engine_path=/usr/lib/opensc/engine_pkcs11.so
# configure the path to the pkcs11 module required by the pkcs11
engine
_______________________________________________
tpm2 mailing list
tpm2@...
https://lists.01.org/mailman/listinfo/tpm2
Sweet! I'd been working on this too, I just got the hostapd /
wpa_supplicant test stuff working and was going to try setting
pkcs11_engine_path=/usr/lib/opensc/engine_pkcs11.so
Just on this particular part: I recently got annoyed with the inability
to use TPM keys on firefox. I did look at the tpm pkcs11 projects but
they all looked deficient to say the least, so I put together this

https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl-pkcs11-export.git

It's a generic engine key to pkcs11 exporter (will work for any openssl
engine) driven by a simple ini like config file. The big advantage it
has is that now I can use openssl engines with gnutls.

To the tpm2-software/tpm2-pcks11 library.

From the amount of changes it took you, it seems like your approach
is cleaner, so, out of curiousity, what was your ratoinal for going
with this approach? I was going to try the pcks11 way because I came
across

https://w1.fi/cgit/hostap/plain/wpa_supplicant/examples/openCryptoki.
conf

But, I haven't gotten to it yet as I got sidetracked right after I
got the tests up and running.
Going the pkcs11 route is definitely the heath robinson approach, so
the direct engine route is definitely much better.

James

Re: [tpm2] Support for EAP-TLS with openssl TPM2 engine

Andersen, John <john.s.andersen@...>
 

On Wed, Mar 13, 2019 at 04:56:17PM -0700, David Woodhouse wrote:
On Sat, 2019-01-12 at 12:59 +0100, huger@... wrote:
Hi

I am working on a plan to operate mobile Linux computers and would like
them to use corporate wifi with EAP TLS. Security requires us to safely
store the client key. Easiest solution would be in the computer's TPM
2.0 device.

So far we were not successful trying to set this up with wpa_supplicant
and pkcs11 engine. Even if successful a solution with these multiple
layers of abstraction is quite complex and therefore might be prone to
updates of any of the components.

So I wonder if there are plans to directly support openssl tpm2 engine?
Here's a quick hack to make it work by abusing the OpenSC engine
config, as a proof of concept. Making it work cleanly so that it can be
merged is left as an exercise for the reader, or perhaps an interested
party in one of the mailing lists I've added to Cc.

I have no idea why wpa_supplicant doesn't call ENGINE_load_dynamic()
for current versions of OpenSSL. That seems to be a bug, which is why
I've removed the #ifdefs around it.

Signed-off-by: David Woodhouse <dwmw2@...> (not that you'd
want to apply it as-is).

diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 705fa29a3..546d3a598 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -864,7 +864,7 @@ static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path,
*/
static int tls_engine_load_dynamic_opensc(const char *opensc_so_path)
{
- char *engine_id = "opensc";
+ char *engine_id = "tpm2";
const char *pre_cmd[] = {
"SO_PATH", NULL /* opensc_so_path */,
"ID", NULL /* engine_id */,
@@ -1034,10 +1034,8 @@ void * tls_init(const struct tls_config *conf)

#ifndef OPENSSL_NO_ENGINE
wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine");
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
ERR_load_ENGINE_strings();
ENGINE_load_dynamic();
-#endif /* OPENSSL_VERSION_NUMBER */

if (conf &&
(conf->opensc_engine_path || conf->pkcs11_engine_path ||
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 1bd43b226..b3045fb3d 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -174,7 +174,7 @@ fast_reauth=1
# private_key option appear to be a PKCS#11 URI, and these options
# should not need to be used explicitly.
# make the opensc engine available
-#opensc_engine_path=/usr/lib/opensc/engine_opensc.so
+opensc_engine_path=/home/dwmw/git/openssl_tpm2_engine/.libs/libtpm2.so
# make the pkcs11 engine available
#pkcs11_engine_path=/usr/lib/opensc/engine_pkcs11.so
# configure the path to the pkcs11 module required by the pkcs11 engine
_______________________________________________
tpm2 mailing list
tpm2@...
https://lists.01.org/mailman/listinfo/tpm2
Sweet! I'd been working on this too, I just got the hostapd / wpa_supplicant
test stuff working and was going to try setting
pkcs11_engine_path=/usr/lib/opensc/engine_pkcs11.so

To the tpm2-software/tpm2-pcks11 library.

From the amount of changes it took you, it seems like your approach is cleaner,
so, out of curiousity, what was your ratoinal for going with this approach? I
was going to try the pcks11 way because I came across

https://w1.fi/cgit/hostap/plain/wpa_supplicant/examples/openCryptoki.conf

But, I haven't gotten to it yet as I got sidetracked right after I got the tests
up and running.

Thanks,
John

Re: Support for EAP-TLS with openssl TPM2 engine

David Woodhouse
 

On Sat, 2019-01-12 at 12:59 +0100, huger@... wrote:
Hi

I am working on a plan to operate mobile Linux computers and would like
them to use corporate wifi with EAP TLS. Security requires us to safely
store the client key. Easiest solution would be in the computer's TPM
2.0 device.

So far we were not successful trying to set this up with wpa_supplicant
and pkcs11 engine. Even if successful a solution with these multiple
layers of abstraction is quite complex and therefore might be prone to
updates of any of the components.

So I wonder if there are plans to directly support openssl tpm2 engine?
Here's a quick hack to make it work by abusing the OpenSC engine
config, as a proof of concept. Making it work cleanly so that it can be
merged is left as an exercise for the reader, or perhaps an interested
party in one of the mailing lists I've added to Cc.

I have no idea why wpa_supplicant doesn't call ENGINE_load_dynamic()
for current versions of OpenSSL. That seems to be a bug, which is why
I've removed the #ifdefs around it.

Signed-off-by: David Woodhouse <dwmw2@...> (not that you'd
want to apply it as-is).

diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 705fa29a3..546d3a598 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -864,7 +864,7 @@ static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path,
*/
static int tls_engine_load_dynamic_opensc(const char *opensc_so_path)
{
- char *engine_id = "opensc";
+ char *engine_id = "tpm2";
const char *pre_cmd[] = {
"SO_PATH", NULL /* opensc_so_path */,
"ID", NULL /* engine_id */,
@@ -1034,10 +1034,8 @@ void * tls_init(const struct tls_config *conf)

#ifndef OPENSSL_NO_ENGINE
wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine");
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
ERR_load_ENGINE_strings();
ENGINE_load_dynamic();
-#endif /* OPENSSL_VERSION_NUMBER */

if (conf &&
(conf->opensc_engine_path || conf->pkcs11_engine_path ||
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 1bd43b226..b3045fb3d 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -174,7 +174,7 @@ fast_reauth=1
# private_key option appear to be a PKCS#11 URI, and these options
# should not need to be used explicitly.
# make the opensc engine available
-#opensc_engine_path=/usr/lib/opensc/engine_opensc.so
+opensc_engine_path=/home/dwmw/git/openssl_tpm2_engine/.libs/libtpm2.so
# make the pkcs11 engine available
#pkcs11_engine_path=/usr/lib/opensc/engine_pkcs11.so
# configure the path to the pkcs11 module required by the pkcs11 engine

[PATCH v2 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@...>

---

v2: add more tests: EC parents and import tests
---
tests/Makefile.am | 1 +
tests/restricted_parent.sh | 82 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 83 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..4f6f11e
--- /dev/null
+++ b/tests/restricted_parent.sh
@@ -0,0 +1,82 @@
+#!/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
+# now generate permanent wrapped keys for the NV indexes
+openssl genrsa 2048 > keyrsa.priv || exit 1;
+${bindir}/create_tpm2_key --restricted -w keyrsa.priv keyrsa.tpm || exit 1
+openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:prime256v1 -pkeyopt ec_param_enc:named_curve -out keyecc.priv || exit 1
+${bindir}/create_tpm2_key --restricted -w keyecc.priv keyecc.tpm || exit 1
+##
+# now lodge the RSA and EC parents at NV and NV2
+##
+${bindir}/load_tpm2_key keyrsa.tpm ${NV} || exit 1
+${bindir}/load_tpm2_key keyecc.tpm ${NV2} || 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.
+##
+for parent in ${NV2} ${NV}; do
+ ${bindir}/create_tpm2_key -p ${parent} key.tpm || exit 1
+ openssl rsa -engine tpm2 -inform engine -in key.tpm -pubout -out key.pub || exit 1
+ echo "This is a test of moveable keys" | openssl rsautl -sign -engine tpm2 -engine tpm2 -keyform engine -inkey key.tpm -out tmp.msg || exit 1
+ openssl rsautl -verify -in tmp.msg -inkey key.pub -pubin || exit 1
+done
+# on exit key 1 is parented to ${NV}
+tssclear -hi p || exit 1
+${bindir}/create_tpm2_key --restricted -w keyrsa.priv keyrsa.tpm || exit 1
+${bindir}/load_tpm2_key keyrsa.tpm ${NV} || exit 1
+
+echo "This is a test of moveable keys" | openssl rsautl -sign -engine tpm2 -engine tpm2 -keyform engine -inkey key.tpm -out tmp.msg || exit 1
+openssl rsautl -verify -in tmp.msg -inkey key.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
+##
+# finally try importable keys. At the moment these only work for ecc parents
+##
+tssclear -hi p
+${bindir}/create_tpm2_key --restricted -w keyecc.priv keyecc.tpm || exit 1
+${bindir}/load_tpm2_key keyecc.tpm ${NV2} || exit 1
+openssl pkey -engine tpm2 -inform engine -in //nvkey:${NV2} -pubout -out keyecc.pub || exit 1
+openssl genrsa 2048 > key.priv || exit 1
+openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:prime256v1 -pkeyopt ec_param_enc:named_curve -out key1.priv
+for key in key.priv key1.priv; do
+ ${bindir}/create_tpm2_key --parent ${NV2} --import keyecc.pub --wrap ${key} key.tpm || exit 1
+ openssl req -new -x509 -subj '/CN=test/' -key key.tpm -engine tpm2 -keyform engine -out tmp.crt || exit 1
+ openssl verify -CAfile tmp.crt -check_ss_sig tmp.crt || exit 1
+done
+
+exit 0
--
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

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

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

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: [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

[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