* [RFC 0/1] TPM2 engine support for openssl
@ 2016-12-22 4:55 James Bottomley
2016-12-22 4:56 ` [RFC 1/1] add TPM2 version of create_tpm2_key and libtpm2.so engine James Bottomley
[not found] ` <1482382526.2350.57.camel-d9PhHud1JfjCXq6kfMZ53/egYHeGw8Jk@public.gmane.org>
0 siblings, 2 replies; 3+ messages in thread
From: James Bottomley @ 2016-12-22 4:55 UTC (permalink / raw)
To: tpmdd-devel, trousers-tech, ibmtpm20tss-users, openssl-dev
This is a basic RFC to show that it's possible to get TPM2 to act as an
engine for openssl as well as TPM1.2. The format follows as closely as
possible what happens in TPM1.2. The file format is full blown ASN.1
because we have to include both a public and private key blob. I chose
to use TSS2 KEY BLOB as the guards to distinguish it from the TPM1.2
file.
TPM2 has significant limitations over TPM1.2 in what it will sign:
basically it must recognise the signature algorithm (that's why all the
signature parsing in the rsa_priv_enc() routine). There's also another
problem in that a primary asymmetric key of the SPS must be provisioned
every time we perform this operation (which is time consuming and
annoying). I think we need to do something about this under Linux, but
I'll take that off the openssl list because they likely won't be
interested.
The authority handling is missing at the moment, but I'll add that
shortly. We should probably discuss how policy based authorisation
should be handled: I think as extensions to the key ASN.1 file.
Because of the signature recognition problem, you have to test this out
with x509 certificates:
openssl genrsa 2048 > tmp.key
create_tpm2_key -w tmp.key tmp.bin
openssl req -new -engine tpm2 -key tmp.bin -keyform e > tmp.csr
openssl x509 -req -engine tpm2 -in tmp.csr -signkey tmp.bin -keyform e -out tmp.crt
openssl x509 -text -in tmp.crt
The last step will validate you've got a genuine x509 self signed
certificate with the key from the TPM.
This kit is constructed using the IBM TSS2:
https://sourceforge.net/projects/ibmtpm20tss/
And, at the moment, it's only been validated on a software TPM2.
James
---
James Bottomley (1):
add TPM2 version of create_tpm2_key and libtpm2.so engine
Makefile.am | 12 +-
create_tpm2_key.c | 381 ++++++++++++++++++++++++++++++++++++++++++
e_tpm2.c | 482 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
tpm2-asn.h | 35 ++++
tpm2-common.c | 172 +++++++++++++++++++
tpm2-common.h | 10 ++
6 files changed, 1090 insertions(+), 2 deletions(-)
create mode 100644 create_tpm2_key.c
create mode 100644 e_tpm2.c
create mode 100644 tpm2-asn.h
create mode 100644 tpm2-common.c
create mode 100644 tpm2-common.h
--
2.6.6
------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/intel
^ permalink raw reply [flat|nested] 3+ messages in thread
* [RFC 1/1] add TPM2 version of create_tpm2_key and libtpm2.so engine
2016-12-22 4:55 [RFC 0/1] TPM2 engine support for openssl James Bottomley
@ 2016-12-22 4:56 ` James Bottomley
[not found] ` <1482382526.2350.57.camel-d9PhHud1JfjCXq6kfMZ53/egYHeGw8Jk@public.gmane.org>
1 sibling, 0 replies; 3+ messages in thread
From: James Bottomley @ 2016-12-22 4:56 UTC (permalink / raw)
To: tpmdd-devel, trousers-tech, ibmtpm20tss-users, openssl-dev
Proof of concept patch to wrap RSA (and eventually other) keys in TPM2
format and use them to load into a TPM2 to perform signatures.
This scheme has significant limitations over TPM1.2 in that TPM2
insists on knowing and validating the signature, so it will only sign
stuff it knows the OID to. That's also why the signature has to be
parsed into an X509_SIG before signing.
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
---
Makefile.am | 12 +-
create_tpm2_key.c | 381 ++++++++++++++++++++++++++++++++++++++++++
e_tpm2.c | 482 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
tpm2-asn.h | 35 ++++
tpm2-common.c | 172 +++++++++++++++++++
tpm2-common.h | 10 ++
6 files changed, 1090 insertions(+), 2 deletions(-)
create mode 100644 create_tpm2_key.c
create mode 100644 e_tpm2.c
create mode 100644 tpm2-asn.h
create mode 100644 tpm2-common.c
create mode 100644 tpm2-common.h
diff --git a/Makefile.am b/Makefile.am
index 4932fae..a704d8c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,12 +2,20 @@ SUBDIRS=. test
EXTRA_DIST = README openssl.cnf.sample
-openssl_engine_LTLIBRARIES=libtpm.la
-bin_PROGRAMS=create_tpm_key
+openssl_engine_LTLIBRARIES=libtpm.la libtpm2.la
+bin_PROGRAMS=create_tpm_key create_tpm2_key
openssl_enginedir=@libdir@/openssl/engines
libtpm_la_LIBADD=-lcrypto -lc -ltspi
libtpm_la_SOURCES=e_tpm.c e_tpm.h e_tpm_err.c
+libtpm2_la_LIBADD=-lcrypto -lc -ltss
+libtpm2_la_SOURCES=e_tpm2.c tpm2-common.c
+libtpm2_la_CFLAGS=-g -Werror
+
create_tpm_key_SOURCES=create_tpm_key.c
create_tpm_key_LDADD=-lcrypto -ltspi
+
+create_tpm2_key_SOURCES=create_tpm2_key.c tpm2-common.c
+create_tpm2_key_LDADD=-lcrypto -ltss
+create_tpm2_key_CFLAGS=-Werror
diff --git a/create_tpm2_key.c b/create_tpm2_key.c
new file mode 100644
index 0000000..001bf1d
--- /dev/null
+++ b/create_tpm2_key.c
@@ -0,0 +1,381 @@
+/*
+ *
+ * Copyright (C) 2016 James Bottomley <James.Bottomley@HansenPartnership.com>
+ *
+ * GPLv2
+ */
+
+
+#include <stdio.h>
+#include <getopt.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+#include <tss2/tss.h>
+#include <tss2/tssutils.h>
+#include <tss2/tssmarshal.h>
+
+#include "tpm2-asn.h"
+#include "tpm2-common.h"
+
+static struct option long_options[] = {
+ {"enc-scheme", 1, 0, 'e'},
+ {"name-scheme", 1, 0, 'n'},
+ {"key-size", 1, 0, 's'},
+ {"auth", 0, 0, 'a'},
+ {"popup", 0, 0, 'p'},
+ {"wrap", 1, 0, 'w'},
+ {"help", 0, 0, 'h'},
+ {0, 0, 0, 0}
+};
+
+static TPM_ALG_ID name_alg = TPM_ALG_SHA256;
+static int name_alg_size = SHA256_DIGEST_SIZE;
+
+void
+usage(char *argv0)
+{
+ fprintf(stderr, "\t%s: create a TPM key and write it to disk\n"
+ "\tusage: %s [options] <filename>\n\n"
+ "\tOptions:\n"
+ "\t\t-e|--enc-scheme encryption scheme to use [PKCSV15] or OAEP\n"
+ "\t\t-n|--name-scheme name algorithm to use sha1 [sha256] sha384 sha512\n"
+ "\t\t-s|--key-size key size in bits [2048]\n"
+ "\t\t-a|--auth require a password for the key [NO]\n"
+ "\t\t-p|--popup use TSS GUI popup dialogs to get the password "
+ "for the\n\t\t\t\t key [NO] (implies --auth)\n"
+ "\t\t-w|--wrap [file] wrap an existing openssl PEM key\n"
+ "\t\t-h|--help print this help message\n"
+ "\nReport bugs to %s\n",
+ argv0, argv0, PACKAGE_BUGREPORT);
+ exit(-1);
+}
+
+void
+openssl_print_errors()
+{
+ ERR_load_ERR_strings();
+ ERR_load_crypto_strings();
+ ERR_print_errors_fp(stderr);
+}
+
+int
+openssl_write_tpmfile(const char *file, BYTE *pubkey, int pubkey_len,
+ BYTE *privkey, int privkey_len)
+{
+ TSSLOADABLE tssl;
+ BIO *outb;
+
+ if ((outb = BIO_new_file(file, "w")) == NULL) {
+ fprintf(stderr, "Error opening file for write: %s\n", file);
+ return 1;
+ }
+ tssl.pubkey = ASN1_OCTET_STRING_new();
+ tssl.privkey = ASN1_OCTET_STRING_new();
+ printf("setting pubkey len %d\n", pubkey_len);
+ ASN1_STRING_set(tssl.pubkey, pubkey, pubkey_len);
+ printf("setting privkey len %d\n", privkey_len);
+ ASN1_STRING_set(tssl.privkey, privkey, privkey_len);
+
+ PEM_write_bio_TSSLOADABLE(outb, &tssl);
+ BIO_free(outb);
+ return 0;
+}
+
+EVP_PKEY *
+openssl_read_key(char *filename)
+{
+ BIO *b = NULL;
+ EVP_PKEY *pkey;
+
+ b = BIO_new_file(filename, "r");
+ if (b == NULL) {
+ fprintf(stderr, "Error opening file for read: %s\n", filename);
+ return NULL;
+ }
+
+ if ((pkey = PEM_read_bio_PrivateKey(b, NULL, PEM_def_callback, NULL)) == NULL) {
+ fprintf(stderr, "Reading key %s from disk failed.\n", filename);
+ openssl_print_errors();
+ }
+ BIO_free(b);
+
+ return pkey;
+}
+
+TPM_RC openssl_to_tpm_public_rsa(TPMT_PUBLIC *pub, EVP_PKEY *pkey)
+{
+ RSA *rsa = EVP_PKEY_get1_RSA(pkey);
+ BIGNUM *n, *e;
+ int size = RSA_size(rsa);
+ unsigned long exp;
+
+ if (size > MAX_RSA_KEY_BYTES)
+ return TPM_RC_KEY_SIZE;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+ n = rsa->n;
+ e = rsa->e;
+#else
+ RSA_get0_key(&n, &e, NULL);
+#endif
+ exp = BN_get_word(e);
+ /* TPM limitations means exponents must be under a word in size */
+ if (exp == 0xffffffffL)
+ return TPM_RC_KEY_SIZE;
+
+ pub->type = TPM_ALG_RSA;
+ pub->nameAlg = name_alg;
+ pub->objectAttributes.val = TPMA_OBJECT_NODA |
+ TPMA_OBJECT_SIGN |
+ TPMA_OBJECT_DECRYPT |
+ TPMA_OBJECT_USERWITHAUTH;
+ pub->authPolicy.t.size = 0;
+ pub->parameters.rsaDetail.symmetric.algorithm = TPM_ALG_NULL;
+ pub->parameters.rsaDetail.scheme.scheme = TPM_ALG_NULL;
+ pub->parameters.rsaDetail.keyBits = size*8;
+ if (exp == 0x10001)
+ pub->parameters.rsaDetail.exponent = 0;
+ else
+ pub->parameters.rsaDetail.exponent = exp;
+
+ pub->unique.rsa.t.size = BN_bn2bin(n, pub->unique.rsa.t.buffer);
+
+ return 0;
+}
+
+TPM_RC openssl_to_tpm_public(TPM2B_PUBLIC *pub, EVP_PKEY *pkey)
+{
+ TPMT_PUBLIC *tpub = &pub->publicArea;
+ pub->size = sizeof(*pub);
+
+ switch (EVP_PKEY_type(pkey->type)) {
+ case EVP_PKEY_RSA:
+ return openssl_to_tpm_public_rsa(tpub, pkey);
+ default:
+ break;
+ }
+ return TPM_RC_ASYMMETRIC;
+}
+
+TPM_RC openssl_to_tpm_private_rsa(TPMT_SENSITIVE *s, EVP_PKEY *pkey)
+{
+ BIGNUM *q;
+ TPM2B_PRIVATE_KEY_RSA *t2brsa = &s->sensitive.rsa;
+ RSA *rsa = EVP_PKEY_get1_RSA(pkey);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+ q = rsa->q;
+#else
+ BIGNUM *p;
+
+ RSA_get0_factors(rsa, &p, &q);
+#endif
+
+ if (!q)
+ return TPM_RC_ASYMMETRIC;
+
+ s->sensitiveType = TPM_ALG_RSA;
+ s->seedValue.b.size = 0;
+
+ t2brsa->t.size = BN_bn2bin(q, t2brsa->t.buffer);
+ return 0;
+}
+
+TPM_RC openssl_to_tpm_private(TPMT_SENSITIVE *priv, EVP_PKEY *pkey)
+{
+ switch (EVP_PKEY_type(pkey->type)) {
+ case EVP_PKEY_RSA:
+ return openssl_to_tpm_private_rsa(priv, pkey);
+ default:
+ break;
+ }
+ return TPM_RC_ASYMMETRIC;
+}
+
+TPM_RC wrap_key(TPM2B_PRIVATE *priv, const char *password, EVP_PKEY *pkey)
+{
+ TPMT_SENSITIVE s;
+ TPM2B_SENSITIVE b;
+ BYTE *buf;
+ int32_t size;
+ TPM_RC rc;
+
+ memset(&b, 0, sizeof(b));
+ memset(&s, 0, sizeof(s));
+
+ openssl_to_tpm_private(&s, pkey);
+
+ if (password) {
+ int len = strlen(password);
+
+ memcpy(s.authValue.b.buffer, password, len);
+ s.authValue.b.size = len;
+ } else {
+ s.authValue.b.size = 0;
+ }
+ size = sizeof(s);
+ buf = b.b.buffer;
+ rc = TSS_TPMT_SENSITIVE_Marshal(&s, &b.b.size, &buf, &size);
+ if (rc)
+ tpm2_error(rc, "TSS_TPMT_SENSITIVE_Marshal");
+
+ size = sizeof(*priv);
+ buf = priv->b.buffer;
+ priv->b.size = 0;
+ /* no encryption means innerIntegrity and outerIntegrity are
+ * absent, so the TPM2B_PRIVATE is a TPMT_SENSITIVE*/
+ rc = TSS_TPM2B_PRIVATE_Marshal((TPM2B_PRIVATE *)&b, &priv->b.size, &buf, &size);
+ if (rc)
+ tpm2_error(rc, "TSS_TPM2B_PRIVATE_Marshal");
+
+ return TPM_RC_ASYMMETRIC;
+}
+
+int main(int argc, char **argv)
+{
+ char *filename, c, *wrap = NULL, *auth = NULL;
+ int option_index;
+ const char *reason;
+ TSS_CONTEXT *tssContext = NULL;
+ TPM_HANDLE parent = 0;
+ TPM_RC rc = 0;
+ BYTE pubkey[sizeof(TPM2B_PUBLIC)],privkey[sizeof(TPM2B_PRIVATE)], *buffer;
+ uint16_t pubkey_len, privkey_len;
+ int32_t size;
+
+
+ while (1) {
+ option_index = 0;
+ c = getopt_long(argc, argv, "n:ahw:",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'a':
+ auth = malloc(128);
+ break;
+ case 'h':
+ usage(argv[0]);
+ break;
+ case 'n':
+ if (!strcasecmp("sha1", optarg)) {
+ name_alg = TPM_ALG_SHA1;
+ name_alg_size = SHA1_DIGEST_SIZE;
+ } else if (strcasecmp("sha256", optarg)) {
+ /* default, do nothing */
+ } else if (strcasecmp("sha384", optarg)) {
+ name_alg = TPM_ALG_SHA384;
+ name_alg_size = SHA384_DIGEST_SIZE;
+#ifdef TPM_ALG_SHA512
+ } else if (strcasecmp("sha512", optarg)) {
+ name_alg = TPM_ALG_SHA512;
+ name_alg_size = SHA512_DIGEST_SIZE;
+#endif
+ } else {
+ usage(argv[0]);
+ }
+ break;
+ case 'w':
+ wrap = optarg;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ filename = argv[argc - 1];
+
+ if (argc < 2)
+ usage(argv[0]);
+
+ if (auth) {
+ if (EVP_read_pw_string(auth, 128, "Enter TPM key authority: ", 1)) {
+ fprintf(stderr, "Passwords do not match\n");
+ exit(1);
+ }
+ }
+
+ rc = TSS_Create(&tssContext);
+ if (rc) {
+ reason = "TSS_Create";
+ goto out_err;
+ }
+
+ if (wrap) {
+ Import_In iin;
+ Import_Out iout;
+ EVP_PKEY *pkey;
+
+ /* may be needed to decrypt the key */
+ OpenSSL_add_all_ciphers();
+ pkey = openssl_read_key(wrap);
+ if (!pkey) {
+ reason = "unable to read key";
+ goto out_delete;
+ }
+
+ rc = tpm2_load_srk(tssContext, &parent, NULL, NULL);
+ if (rc) {
+ reason = "tpm2_load_srk";
+ goto out_delete;
+ }
+ iin.parentHandle = parent;
+ iin.encryptionKey.t.size = 0;
+ openssl_to_tpm_public(&iin.objectPublic, pkey);
+ /* set random iin.symSeed */
+ iin.inSymSeed.t.size = 0;
+ iin.symmetricAlg.algorithm = TPM_ALG_NULL;
+ wrap_key(&iin.duplicate, auth, pkey);
+ openssl_to_tpm_public(&iin.objectPublic, pkey);
+ rc = TSS_Execute(tssContext,
+ (RESPONSE_PARAMETERS *)&iout,
+ (COMMAND_PARAMETERS *)&iin,
+ NULL,
+ TPM_CC_Import,
+ TPM_RS_PW, NULL, 0,
+ TPM_RH_NULL, NULL, 0,
+ TPM_RH_NULL, NULL, 0,
+ TPM_RH_NULL, NULL, 0);
+ if (rc) {
+ reason = "TPM2_Import";
+ goto out_flush;
+ }
+ tpm2_flush_handle(tssContext, parent);
+ buffer = pubkey;
+ pubkey_len = 0;
+ size = sizeof(pubkey);
+ TSS_TPM2B_PUBLIC_Marshal(&iin.objectPublic, &pubkey_len, &buffer, &size);
+ printf("MARSHAL to %d of %ld\n", (pubkey[0]*256) + pubkey[1],
+ sizeof(pubkey));
+ buffer = privkey;
+ privkey_len = 0;
+ size = sizeof(privkey);
+ TSS_TPM2B_PRIVATE_Marshal(&iout.outPrivate, &privkey_len, &buffer, &size);
+ printf("MARSHAL to %d of %ld\n", (privkey[0]*256) + privkey[1],
+ sizeof(privkey));
+ } else {
+ pubkey_len = 0;
+ privkey_len = 0;
+ }
+ openssl_write_tpmfile(filename, pubkey, pubkey_len, privkey, privkey_len);
+ exit(0);
+
+ out_flush:
+ tpm2_flush_handle(tssContext, parent);
+ out_delete:
+ TSS_Delete(tssContext);
+ out_err:
+ tpm2_error(rc, reason);
+
+ exit(1);
+}
diff --git a/e_tpm2.c b/e_tpm2.c
new file mode 100644
index 0000000..298282c
--- /dev/null
+++ b/e_tpm2.c
@@ -0,0 +1,482 @@
+
+/*
+ * Copyright (C) 2016 James.Bottomley@HansenPartnership.com
+ *
+ * GPLv2
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <openssl/crypto.h>
+#include <openssl/dso.h>
+#include <openssl/engine.h>
+#include <openssl/evp.h>
+#include <openssl/objects.h>
+#include <openssl/sha.h>
+#include <openssl/bn.h>
+#include <openssl/pem.h>
+#include <openssl/x509.h>
+
+#include <tss2/tss.h>
+#include <tss2/tssutils.h>
+#include <tss2/tssmarshal.h>
+#include <tss2/tssresponsecode.h>
+#include <tss2/Unmarshal_fp.h>
+
+#include "tpm2-asn.h"
+#include "tpm2-common.h"
+
+#define TPM2_ENGINE_EX_DATA_UNINIT -1
+
+/* structure pointed to by the RSA object's app_data pointer */
+struct rsa_app_data
+{
+ TPM_HANDLE hKey;
+};
+
+static TSS_CONTEXT *tssContext;
+static char *srk_auth;
+
+static int tpm2_engine_init(ENGINE * e)
+{
+ TPM_RC rc;
+
+ rc = TSS_Create(&tssContext);
+ if (!rc)
+ return 1;
+
+ tpm2_error(rc, "TSS_Create");
+ return 0;
+}
+
+static int tpm2_engine_finish(ENGINE * e)
+{
+ tpm2_flush_srk(tssContext);
+ if (tssContext)
+ TSS_Delete(tssContext);
+
+ return 1;
+}
+
+static int tpm2_create_srk_policy(char *secret)
+{
+ int len;
+
+ if (!secret) {
+ OPENSSL_free(srk_auth);
+ srk_auth = NULL;
+ } else {
+ len = strlen(secret);
+ srk_auth = OPENSSL_malloc(len);
+ strcpy(srk_auth, secret);
+ }
+ return 1;
+}
+
+#define TPM_CMD_PIN ENGINE_CMD_BASE
+
+static int tpm2_engine_ctrl(ENGINE * e, int cmd, long i, void *p, void (*f) ())
+{
+ if (!tssContext) {
+ fprintf(stderr, "tpm2: engine not initialized\n");
+ return 0;
+ }
+
+
+ switch (cmd) {
+ case TPM_CMD_PIN:
+ return tpm2_create_srk_policy(p);
+ default:
+ break;
+ }
+ fprintf(stderr, "tpm2: engine command not implemented\n");
+
+ return 0;
+}
+
+
+#ifndef OPENSSL_NO_RSA
+/* rsa functions */
+static int tpm2_rsa_init(RSA *rsa);
+static int tpm2_rsa_finish(RSA *rsa);
+static int tpm2_rsa_pub_dec(int, const unsigned char *, unsigned char *, RSA *, int);
+static int tpm2_rsa_pub_enc(int, const unsigned char *, unsigned char *, RSA *, int);
+static int tpm2_rsa_priv_dec(int, const unsigned char *, unsigned char *, RSA *, int);
+static int tpm2_rsa_priv_enc(int, const unsigned char *, unsigned char *, RSA *, int);
+//static int tpm2_rsa_sign(int, const unsigned char *, unsigned int, unsigned char *, unsigned int *, const RSA *);
+#endif
+
+/* The definitions for control commands specific to this engine */
+#define TPM2_CMD_PIN ENGINE_CMD_BASE
+static const ENGINE_CMD_DEFN tpm2_cmd_defns[] = {
+ {TPM2_CMD_PIN,
+ "PIN",
+ "Specifies the secret for the SRK (default is plaintext, else set SECRET_MODE)",
+ ENGINE_CMD_FLAG_STRING},
+ /* end */
+ {0, NULL, NULL, 0}
+};
+
+#ifndef OPENSSL_NO_RSA
+static RSA_METHOD tpm2_rsa = {
+ "TPM RSA method",
+ tpm2_rsa_pub_enc,
+ tpm2_rsa_pub_dec,
+ tpm2_rsa_priv_enc,
+ tpm2_rsa_priv_dec,
+ NULL, /* set in tpm2_engine_init */
+ BN_mod_exp_mont,
+ tpm2_rsa_init,
+ tpm2_rsa_finish,
+ (RSA_FLAG_SIGN_VER | RSA_FLAG_NO_BLINDING),
+ NULL,
+ NULL, /* sign */
+ NULL, /* verify */
+ NULL, /* keygen */
+};
+#endif
+
+/* varibles used to get/set CRYPTO_EX_DATA values */
+static int ex_app_data = TPM2_ENGINE_EX_DATA_UNINIT;
+
+static EVP_PKEY *tpm2_engine_load_key(ENGINE *e, const char *key_id,
+ UI_METHOD *ui, void *cb_data)
+{
+ TPM_HANDLE key,srk;
+ TPM_RC rc;
+ EVP_PKEY *pkey;
+ RSA *rsa;
+ BIO *bf;
+ TSSLOADABLE *tssl;
+ Load_In in;
+ Load_Out out;
+ struct rsa_app_data *app_data;
+ BYTE *buffer;
+ INT32 size;
+
+ if (!key_id) {
+ fprintf(stderr, "key_id is NULL\n");
+ return NULL;
+ }
+
+ rc = tpm2_load_srk(tssContext, &srk, srk_auth, NULL);
+ if (rc) {
+ tpm2_error(rc, "tpm2_load_srk");
+ return NULL;
+ }
+
+ bf = BIO_new_file(key_id, "r");
+ if (!bf) {
+ fprintf(stderr, "File %s does not exist or cannot be read\n", key_id);
+ goto err;
+ }
+
+ tssl = PEM_read_bio_TSSLOADABLE(bf, NULL, NULL, NULL);
+ if (!tssl) {
+ fprintf(stderr, "Failed to parse file %s\n", key_id);
+ BIO_free(bf);
+ goto err;
+ }
+
+ BIO_free(bf);
+
+ in.parentHandle = srk;
+ buffer = tssl->privkey->data;
+ size = tssl->privkey->length;
+ TPM2B_PRIVATE_Unmarshal(&in.inPrivate, &buffer, &size);
+ buffer = tssl->pubkey->data;
+ size = tssl->pubkey->length;
+ TPM2B_PUBLIC_Unmarshal(&in.inPublic, &buffer, &size, FALSE);
+
+ rc = TSS_Execute(tssContext,
+ (RESPONSE_PARAMETERS *)&out,
+ (COMMAND_PARAMETERS *)&in,
+ NULL,
+ TPM_CC_Load,
+ TPM_RS_PW, NULL, 0,
+ TPM_RH_NULL, NULL, 0,
+ TPM_RH_NULL, NULL, 0,
+ TPM_RH_NULL, NULL, 0);
+ TSSLOADABLE_free(tssl);
+ if (rc) {
+ tpm2_error(rc, "TPM2_Load");
+ goto err;
+ }
+ key = out.objectHandle;
+
+ app_data = OPENSSL_malloc(sizeof(struct rsa_app_data));
+ if (!app_data) {
+ fprintf(stderr, "Failed to allocate app_data\n");
+ goto err_key;
+ }
+ /* create the new objects to return */
+ pkey = tpm2_to_openssl_public(&in.inPublic.publicArea);
+ if (!pkey) {
+ fprintf(stderr, "Failed to allocate a new EVP_KEY\n");
+ goto err_key;
+ }
+ app_data->hKey = key;
+
+ rsa = EVP_PKEY_get1_RSA(pkey);
+ RSA_set_ex_data(rsa, ex_app_data, app_data);
+ rsa->meth = &tpm2_rsa;
+ /* call our local init function here */
+ rsa->meth->init(rsa);
+
+ /* release the reference EVP_PKEY_get1_RSA obtained */
+ RSA_free(rsa);
+ return pkey;
+ err_key:
+ tpm2_flush_handle(tssContext, key);
+ err:
+ tpm2_flush_handle(tssContext, srk);
+
+ return NULL;
+}
+
+/* Constants used when creating the ENGINE */
+static const char *engine_tpm2_id = "tpm2";
+static const char *engine_tpm2_name = "TPM2 hardware engine support";
+
+/* This internal function is used by ENGINE_tpm() and possibly by the
+ * "dynamic" ENGINE support too */
+static int tpm2_bind_helper(ENGINE * e)
+{
+ if (!ENGINE_set_id(e, engine_tpm2_id) ||
+ !ENGINE_set_name(e, engine_tpm2_name) ||
+#ifndef OPENSSL_NO_RSA
+ !ENGINE_set_RSA(e, &tpm2_rsa) ||
+#endif
+ !ENGINE_set_init_function(e, tpm2_engine_init) ||
+ !ENGINE_set_finish_function(e, tpm2_engine_finish) ||
+ !ENGINE_set_ctrl_function(e, tpm2_engine_ctrl) ||
+ !ENGINE_set_load_pubkey_function(e, tpm2_engine_load_key) ||
+ !ENGINE_set_load_privkey_function(e, tpm2_engine_load_key) ||
+ !ENGINE_set_cmd_defns(e, tpm2_cmd_defns))
+ return 0;
+
+ return 1;
+}
+
+
+#ifndef OPENSSL_NO_RSA
+static int tpm2_rsa_init(RSA *rsa)
+{
+ if (ex_app_data == TPM2_ENGINE_EX_DATA_UNINIT)
+ ex_app_data = RSA_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+
+ if (ex_app_data == TPM2_ENGINE_EX_DATA_UNINIT) {
+ fprintf(stderr, "Failed to get memory for external data\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int tpm2_rsa_finish(RSA *rsa)
+{
+ struct rsa_app_data *app_data = RSA_get_ex_data(rsa, ex_app_data);
+
+ if (!app_data)
+ return 1;
+
+ if (app_data->hKey) {
+ tpm2_flush_handle(tssContext, app_data->hKey);
+ tpm2_flush_srk(tssContext);
+ app_data->hKey = 0;
+ }
+
+ OPENSSL_free(app_data);
+
+ return 1;
+}
+
+static int tpm2_rsa_pub_dec(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ int rv;
+
+ rv = RSA_PKCS1_SSLeay()->rsa_pub_dec(flen, from, to, rsa,
+ padding);
+ if (rv < 0) {
+ fprintf(stderr, "rsa_pub_dec failed\n");
+ return 0;
+ }
+
+ return rv;
+}
+
+static int tpm2_rsa_priv_dec(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ struct rsa_app_data *app_data = RSA_get_ex_data(rsa, ex_app_data);
+ int rv;
+
+ if (!app_data) {
+ rv = RSA_PKCS1_SSLeay()->rsa_priv_dec(flen, from, to, rsa,
+ padding);
+ if (rv < 0)
+ fprintf(stderr, "rsa_priv_dec failed\n");
+
+ return rv;
+ }
+
+ if (!app_data->hKey) {
+ fprintf(stderr, "No TPM key defined\n");
+ return 0;
+ }
+
+ fprintf(stderr, "rsa_priv_dec currently unimplemented\n");
+ return 0;
+}
+
+static int tpm2_rsa_pub_enc(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ struct rsa_app_data *app_data = RSA_get_ex_data(rsa, ex_app_data);
+ int rv;
+
+ if (!app_data) {
+ rv = RSA_PKCS1_SSLeay()->rsa_pub_enc(flen, from, to, rsa,
+ padding);
+ if (rv < 0)
+ fprintf(stderr, "rsa_pub_enc failed\n");
+
+ return rv;
+ }
+ fprintf(stderr, "rsa_pub_enc not implemented");
+ return 0;
+}
+
+static int tpm2_rsa_priv_enc(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ struct rsa_app_data *app_data = RSA_get_ex_data(rsa, ex_app_data);
+ TPM_RC rc;
+ int rv;
+ Sign_In in;
+ Sign_Out out;
+ X509_SIG *x509_sig;
+ const unsigned char *buf = from;
+ TPM_ALG_ID hash_alg;
+
+ if (!app_data) {
+ rv = RSA_PKCS1_SSLeay()->rsa_priv_enc(flen, from, to, rsa,
+ padding);
+ if (rv < 0)
+ fprintf(stderr, "pass through signing failed\n");
+
+ return rv;
+ }
+
+ rv = -1;
+ if (padding != RSA_PKCS1_PADDING) {
+ fprintf(stderr, "Non PKCS1 padding asked for\n");
+ return rv;
+ }
+
+ if (!app_data->hKey) {
+ fprintf(stderr, "No RSA key set for signature\n");
+ return rv;
+ }
+
+ x509_sig = d2i_X509_SIG(NULL, &buf, flen);
+ if (!x509_sig) {
+ fprintf(stderr, "buffer for signing is not an X509_SIG type\n");
+ return rv;
+ }
+
+ switch (OBJ_obj2nid(x509_sig->algor->algorithm)) {
+ case NID_sha1:
+ hash_alg = TPM_ALG_SHA1;
+ break;
+ case NID_sha256:
+ hash_alg = TPM_ALG_SHA256;
+ break;
+ case NID_sha384:
+ hash_alg = TPM_ALG_SHA384;
+ break;
+#ifdef TPM_ALG_SHA512
+ case NID_sha512:
+ hash_alg = TPM_ALG_SHA512;
+ break;
+#endif
+ default: {
+ char buf[512];
+ OBJ_obj2txt(buf, sizeof(buf), x509_sig->algor->algorithm, 0);
+ fprintf(stderr, "unrecognised signature algorithm %s\n", buf);
+ goto out_free;
+ }
+ }
+
+ in.keyHandle = app_data->hKey;
+ in.digest.t.size = x509_sig->digest->length;
+ memcpy(&in.digest.t.buffer, x509_sig->digest->data,
+ x509_sig->digest->length);
+ in.inScheme.scheme = TPM_ALG_RSASSA;
+ in.inScheme.details.any.hashAlg = hash_alg;
+ /* no proof means NULL validation ticket */
+ in.validation.tag = TPM_ST_HASHCHECK;
+ in.validation.hierarchy = TPM_RH_NULL;
+ in.validation.digest.t.size = 0;
+
+ rc = TSS_Execute(tssContext,
+ (RESPONSE_PARAMETERS *)&out,
+ (COMMAND_PARAMETERS *)&in,
+ NULL,
+ TPM_CC_Sign,
+ TPM_RS_PW, NULL, 0,
+ TPM_RH_NULL, NULL, 0,
+ TPM_RH_NULL, NULL, 0,
+ TPM_RH_NULL, NULL, 0);
+
+ if (rc) {
+ tpm2_error(rc, "TPM2_Sign");
+ goto out_free;
+ }
+
+
+ memcpy(to, out.signature.signature.rsassa.sig.t.buffer,
+ out.signature.signature.rsassa.sig.t.size);
+
+ rv = out.signature.signature.rsassa.sig.t.size;
+
+ out_free:
+ X509_SIG_free(x509_sig);
+ return rv;
+}
+
+#endif
+
+/* This stuff is needed if this ENGINE is being compiled into a self-contained
+ * shared-library. */
+static int tpm2_bind_fn(ENGINE * e, const char *id)
+{
+ if (id && (strcmp(id, engine_tpm2_id) != 0)) {
+ fprintf(stderr, "Called for id %s != my id %s\n",
+ id, engine_tpm2_id);
+ return 0;
+ }
+ if (!tpm2_bind_helper(e)) {
+ fprintf(stderr, "tpm2_bind_helper failed\n");
+ return 0;
+ }
+ return 1;
+}
+
+IMPLEMENT_DYNAMIC_CHECK_FN()
+IMPLEMENT_DYNAMIC_BIND_FN(tpm2_bind_fn)
diff --git a/tpm2-asn.h b/tpm2-asn.h
new file mode 100644
index 0000000..abbe294
--- /dev/null
+++ b/tpm2-asn.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2016 James Bottomley <James.Bottomley@HansenPartnership.com>
+ *
+ * GPLv2
+ */
+#ifndef _TPM2_ASN_H
+#define _TPM2_ASN_H
+
+#include <openssl/asn1t.h>
+
+/*
+ * Define the format of a TSS2 key file. The current format is a
+ * symmetrically encrypted private key produced by TSS2_Import and
+ * the TPM2 format public key which contains things like the policy but
+ * which is cryptographically tied to the private key
+ */
+
+typedef struct {
+ ASN1_OCTET_STRING *pubkey;
+ ASN1_OCTET_STRING *privkey;
+} TSSLOADABLE;
+
+ASN1_SEQUENCE(TSSLOADABLE) = {
+ ASN1_SIMPLE(TSSLOADABLE, pubkey, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(TSSLOADABLE, privkey, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(TSSLOADABLE)
+
+IMPLEMENT_ASN1_FUNCTIONS(TSSLOADABLE);
+
+/* This is the PEM guard tag */
+#define TSSLOADABLE_PEM_STRING "TSS2 KEY BLOB"
+
+static IMPLEMENT_PEM_write_bio(TSSLOADABLE, TSSLOADABLE, TSSLOADABLE_PEM_STRING, TSSLOADABLE)
+static IMPLEMENT_PEM_read_bio(TSSLOADABLE, TSSLOADABLE, TSSLOADABLE_PEM_STRING, TSSLOADABLE)
+
+#endif
diff --git a/tpm2-common.c b/tpm2-common.c
new file mode 100644
index 0000000..b544f2f
--- /dev/null
+++ b/tpm2-common.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2016 James Bottomley <James.Bottomley@HansenPartnership.com>
+ *
+ * GPLv2
+ */
+
+#include <stdio.h>
+
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+
+#include <tss2/tss.h>
+#include <tss2/tssresponsecode.h>
+
+#include "tpm2-common.h"
+
+void tpm2_error(TPM_RC rc, const char *reason)
+{
+ const char *msg, *submsg, *num;
+
+ fprintf(stderr, "%s failed with %d\n", reason, rc);
+ TSS_ResponseCode_toString(&msg, &submsg, &num, rc);
+ fprintf(stderr, "%s%s%s\n", msg, submsg, num);
+}
+
+
+static TPM_HANDLE hSRK = 0;
+
+TPM_HANDLE tpm2_load_srk(TSS_CONTEXT *tssContext, TPM_HANDLE *h, const char *auth,TPM2B_PUBLIC *pub)
+{
+ static TPM2B_PUBLIC srk_pub;
+ TPM_RC rc;
+ CreatePrimary_In in;
+ CreatePrimary_Out out;
+
+ if (hSRK)
+ goto out;
+
+ /* SPS owner */
+ in.primaryHandle = TPM_RH_OWNER;
+ /* assume no owner password */
+ in.inSensitive.sensitive.userAuth.t.size = 0;
+ /* no sensitive date for storage keys */
+ in.inSensitive.sensitive.data.t.size = 0;
+ /* no outside info */
+ in.outsideInfo.t.size = 0;
+ /* no PCR state */
+ in.creationPCR.count = 0;
+
+ /* public parameters for an RSA2048 key */
+ in.inPublic.publicArea.type = TPM_ALG_RSA;
+ in.inPublic.publicArea.nameAlg = TPM_ALG_SHA256;
+ in.inPublic.publicArea.objectAttributes.val =
+ TPMA_OBJECT_NODA |
+ TPMA_OBJECT_SENSITIVEDATAORIGIN |
+ TPMA_OBJECT_USERWITHAUTH |
+ TPMA_OBJECT_DECRYPT |
+ TPMA_OBJECT_RESTRICTED;
+ in.inPublic.publicArea.parameters.rsaDetail.symmetric.algorithm = TPM_ALG_AES;
+ in.inPublic.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = 128;
+ in.inPublic.publicArea.parameters.rsaDetail.symmetric.mode.aes = TPM_ALG_CFB;
+ in.inPublic.publicArea.parameters.rsaDetail.scheme.scheme = TPM_ALG_NULL;
+ in.inPublic.publicArea.parameters.rsaDetail.keyBits = 2048;
+ /* means conventional 2^16+1 */
+ in.inPublic.publicArea.parameters.rsaDetail.exponent = 0;
+ in.inPublic.publicArea.unique.rsa.t.size = 0;
+ in.inPublic.publicArea.authPolicy.t.size = 0;
+
+ rc = TSS_Execute(tssContext,
+ (RESPONSE_PARAMETERS *)&out,
+ (COMMAND_PARAMETERS *)&in,
+ NULL,
+ TPM_CC_CreatePrimary,
+ TPM_RS_PW, NULL, 0,
+ TPM_RH_NULL, NULL, 0,
+ TPM_RH_NULL, NULL, 0,
+ TPM_RH_NULL, NULL, 0);
+ if (rc)
+ return rc;
+
+ hSRK = out.objectHandle;
+ srk_pub = out.outPublic;
+ out:
+ *h = hSRK;
+ if (pub)
+ *pub = srk_pub;
+
+ return 0;
+}
+
+void tpm2_flush_srk(TSS_CONTEXT *tssContext)
+{
+ if (hSRK)
+ tpm2_flush_handle(tssContext, hSRK);
+ hSRK = 0;
+}
+
+void tpm2_flush_handle(TSS_CONTEXT *tssContext, TPM_HANDLE h)
+{
+ FlushContext_In in;
+
+ if (!h)
+ return;
+
+ in.flushHandle = h;
+ TSS_Execute(tssContext, NULL,
+ (COMMAND_PARAMETERS *)&in,
+ NULL,
+ TPM_CC_FlushContext,
+ TPM_RH_NULL, NULL, 0);
+}
+
+static EVP_PKEY *tpm2_to_openssl_public_rsa(TPMT_PUBLIC *pub)
+{
+ RSA *rsa = RSA_new();
+ EVP_PKEY *pkey;
+ unsigned long exp;
+ BIGNUM *n, *e;
+
+ if (!rsa)
+ return NULL;
+ pkey = EVP_PKEY_new();
+ if (!pkey)
+ goto err_free_rsa;
+ e = BN_new();
+ if (!e)
+ goto err_free_pkey;
+ n = BN_new();
+ if (!n)
+ goto err_free_e;
+ if (pub->parameters.rsaDetail.exponent == 0)
+ exp = 0x10001;
+ else
+ exp = pub->parameters.rsaDetail.exponent;
+ if (!BN_set_word(e, exp))
+ goto err_free;
+ if (!BN_bin2bn(pub->unique.rsa.t.buffer, pub->unique.rsa.t.size, n))
+ goto err_free;
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+ rsa->n = n;
+ rsa->e = e;
+#else
+ RSA_set0_key(rsa, n, e, NULL);
+#endif
+ if (!EVP_PKEY_assign_RSA(pkey, rsa))
+ goto err_free;
+
+ return pkey;
+
+ err_free:
+ BN_free(n);
+ err_free_e:
+ BN_free(e);
+ err_free_pkey:
+ EVP_PKEY_free(pkey);
+ err_free_rsa:
+ RSA_free(rsa);
+
+ return NULL;
+}
+
+EVP_PKEY *tpm2_to_openssl_public(TPMT_PUBLIC *pub)
+{
+ switch (pub->type) {
+ case TPM_ALG_RSA:
+ return tpm2_to_openssl_public_rsa(pub);
+ default:
+ break;
+ }
+ return NULL;
+}
+
diff --git a/tpm2-common.h b/tpm2-common.h
new file mode 100644
index 0000000..197e552
--- /dev/null
+++ b/tpm2-common.h
@@ -0,0 +1,10 @@
+#ifndef _TPM2_COMMON_H
+#define _TPM2_COMMON_H
+
+void tpm2_error(TPM_RC rc, const char *reason);
+TPM_HANDLE tpm2_load_srk(TSS_CONTEXT *tssContext, TPM_HANDLE *h, const char *auth, TPM2B_PUBLIC *pub);
+void tpm2_flush_handle(TSS_CONTEXT *tssContext, TPM_HANDLE h);
+EVP_PKEY *tpm2_to_openssl_public(TPMT_PUBLIC *pub);
+void tpm2_flush_srk(TSS_CONTEXT *tssContext);
+
+#endif
--
2.6.6
--
openssl-dev mailing list
To unsubscribe: https://mta.openssl.org/mailman/listinfo/openssl-dev
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [RFC 0/1] TPM2 engine support for openssl
[not found] ` <1482382526.2350.57.camel-d9PhHud1JfjCXq6kfMZ53/egYHeGw8Jk@public.gmane.org>
@ 2016-12-22 16:42 ` James Bottomley
0 siblings, 0 replies; 3+ messages in thread
From: James Bottomley @ 2016-12-22 16:42 UTC (permalink / raw)
To: tpmdd-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
trousers-tech-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
ibmtpm20tss-users-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
[openssl-dev cut; they're likely not interested in this]
On Wed, 2016-12-21 at 20:55 -0800, James Bottomley wrote:
> There's also another problem in that a primary asymmetric key of the
> SPS must be provisioned every time we perform this operation (which
> is time consuming and annoying). I think we need to do something
> about this under Linux, but I'll take that off the openssl list
> because they likely won't be interested.
I talked to Microsoft about what they do. Apparently there is an
unpublished TPM 2.0 provisioning guide which specifies how the SRK
should be handled, and a published one for the EK:
http://www.trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf
the SRK template is identical to the EK one except that
userWithAuth = 1
adminWithPolicy = 0
noDA = 1
authPolicy = empty policy
The persistent handles for these two are EK: 0x81010001; SRK:
0x81000001. Conventionally the SRK is provisioned with empty auth.
I think as part of our tpm2 take ownership, we should provision the
owner and lockout auth and create these two primary objects if they
don't already exist.
That would mean I can get rid of the primary object stuff in my tpm2
engine code and simply look for the well known handle.
James
------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today.http://sdm.link/intel
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2016-12-22 16:42 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-12-22 4:55 [RFC 0/1] TPM2 engine support for openssl James Bottomley
2016-12-22 4:56 ` [RFC 1/1] add TPM2 version of create_tpm2_key and libtpm2.so engine James Bottomley
[not found] ` <1482382526.2350.57.camel-d9PhHud1JfjCXq6kfMZ53/egYHeGw8Jk@public.gmane.org>
2016-12-22 16:42 ` [RFC 0/1] TPM2 engine support for openssl James Bottomley
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.