Date   
Re: [PATCH 3/4] Add importable keys

Fredrik Ternerot
 

On Wed, Nov 14, 2018 at 4:56 PM James Bottomley
<James.Bottomley@...> wrote:

Importable keys are a new type of key that can be created without
access to the receiving TPM. The design of importable keys is that
you "wrap" the key (encrypt with a derived key) in such a way that it
can only be unwrapped by the TPM local parent key. To do this
wrapping you only need to know the public key of the parent, which can
be shipped in an ordinary openssl public key file.

The key format of importable keys requires an additional encrypted
secret parameter, which basically encodes the wrapping key using ECDH
so it can be decrypted by the parent on import.

Importable keys are designed to be used everywhere the current
loadable keys are used, the only difference being that the TPM engine
converts the importable key to loadable on first load and thereafter
uses it identically to a loadable key.

The new command line option of importable keys is

create_tpm2_key --import <pubparent> --wrap <private key> tmp.key

Where <pubparent> is the parent public key and <private key> is the
key you are wrapping (obviously since you have no access to the TPM,
this option can only be used to wrap existing private keys).

Signed-off-by: James Bottomley <James.Bottomley@...>
---
create_tpm2_key.1.in | 17 +++
create_tpm2_key.c | 288 ++++++++++++++++++++++++++++++++++++++++++++++-----
e_tpm2.c | 108 ++++++++++++++++---
tpm2-asn.h | 3 +
4 files changed, 377 insertions(+), 39 deletions(-)

diff --git a/create_tpm2_key.1.in b/create_tpm2_key.1.in
index 0e52d86..6ec5f25 100644
--- a/create_tpm2_key.1.in
+++ b/create_tpm2_key.1.in
@@ -7,6 +7,13 @@ Can be used to create a TPM loadable representation of a private key.
The key is either internal to the TPM or wrapped from an existing
private key.

+Note that this command can now create two different types of keys:
+importable and loadable (the default type being loadable). The
+difference between the two is that the creation of loadable keys
+requires the presence of the actual TPM the key will be loaded on. An
+importable key can be created without any TPM contact provided you
+have the public key of the parent the new key will be imported to.
+
Consider to add that the tool currently requires the parent to be of
EC type and use sha256 as name alg.

[files]

Policy File Format
@@ -69,3 +76,13 @@ Create a self-signed cert using the TPM engine:
3. Test using openssl:
$ openssl s_server -cert <certfilename> -www -accept 4433 -keyform engine -engi
ne tpm -key <keyfilename>
+
+Creating an importable key:
+
+1. First get the public key of the storage primary seed (as an elliptic
+ curve key):
+ $ tsscreateprimary -ecc nistp256 -hi o -opem srk.pub
+
+2. Once you have this public key, you can export it anywhere and do the
+ key wrapping
+ $ create_tpm2_key --import srk.pub --wrap my_key.priv my_key.tpm
diff --git a/create_tpm2_key.c b/create_tpm2_key.c
index 899e434..0576c83 100644
--- a/create_tpm2_key.c
+++ b/create_tpm2_key.c
@@ -58,6 +58,7 @@ static struct option long_options[] = {
{"list-curves", 0, 0, 'l'},
{"da", 0, 0, 'd'},
{"key-policy", 1, 0, 'c'},
+ {"import", 1, 0, 'i'},
/*
* The option --deprecated allows us to create old format keys
* for the purposes of testing. It should never be used in
@@ -98,6 +99,8 @@ usage(char *argv0)
"\t Supported curves are bnp256, nisp256, nisp384\n"
"\t-l, --list-curves List all the Elliptic Curves the TPM supports\n"
"\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"
"\n"
"Report bugs to " PACKAGE_BUGREPORT "\n",
argv0);
@@ -270,6 +273,135 @@ TPM_RC tpm2_innerwrap(TPMT_SENSITIVE *s,
return TPM_RC_SUCCESS;
}

+TPM_RC tpm2_outerwrap(EVP_PKEY *parent,
+ TPMT_SENSITIVE *s,
+ TPMT_PUBLIC *pub,
+ TPM2B_PRIVATE *p,
+ TPM2B_ENCRYPTED_SECRET *enc_secret)
+{
+ TPM2B_PRIVATE secret, seed;
+ /* amount of room in the buffer for the integrity TPM2B */
+ const int name_alg_size = TSS_GetDigestSize(pub->nameAlg);
+ const int integrity_skip = name_alg_size + 2;
+ // BYTE *integrity = p->t.buffer;
+ BYTE *sensitive = p->t.buffer + integrity_skip;
+ BYTE *buf;
+ TPM2B *t2b;
+ INT32 size;
+ size_t ssize;
+ UINT16 bsize, written = 0;
+ EVP_PKEY *ephemeral = NULL;
+ EVP_PKEY_CTX *ctx;
+ TPM2B_ECC_POINT pub_pt, ephemeral_pt;
+ const EC_KEY *e_parent, *e_ephemeral;
+ const EC_GROUP *group;
+ unsigned char aeskey[T2_AES_KEY_BYTES];
+ /* hmac follows namealg, so set to max size */
+ TPM2B_KEY hmackey;
+ TPMT_HA hmac;
+ TPM2B_NAME name;
+ TPM2B_DIGEST digest;
+ unsigned char null_iv[AES_128_BLOCK_SIZE_BYTES];
+ TPM2B null_2b;
+
+ null_2b.size = 0;
+
+ if (EVP_PKEY_type(EVP_PKEY_id(parent)) != EVP_PKEY_EC) {
+ printf("Can only currently wrap to EC parent\n");
+ return TPM_RC_ASYMMETRIC;
+ }
+
+ e_parent = EVP_PKEY_get0_EC_KEY(parent);
+ group = EC_KEY_get0_group(e_parent);
+
+ /* marshal the sensitive into a TPM2B */
+ t2b = (TPM2B *)sensitive;
+ buf = t2b->buffer;
+ size = sizeof(p->t.buffer) - integrity_skip;
+ bsize = 0;
+ TSS_TPMT_SENSITIVE_Marshal(s, &bsize, &buf, &size);
+ buf = (BYTE *)&t2b->size;
+ size = 2;
+ TSS_UINT16_Marshal(&bsize, &written, &buf, &size);
+ /* set the total size of the private entity */
+ p->b.size = bsize + sizeof(UINT16) + integrity_skip;
+
+ /* compute the elliptic curve shared (and encrypted) secret */
+ ctx = EVP_PKEY_CTX_new(parent, NULL);
+ if (!ctx)
+ goto openssl_err;
+ if (EVP_PKEY_keygen_init(ctx) != 1)
+ goto openssl_err;
+ EVP_PKEY_keygen(ctx, &ephemeral);
+ if (!ephemeral)
+ goto openssl_err;
+ /* otherwise the ctx free will free the key */
+ EVP_PKEY_up_ref(ephemeral);
+ EVP_PKEY_CTX_free(ctx);
+
+ e_ephemeral = EVP_PKEY_get0_EC_KEY(ephemeral);
+
+ /* now begin again with the ephemeral private key because the
+ * context must be initialised with the private key */
+ ctx = EVP_PKEY_CTX_new(ephemeral, NULL);
+ if (!ctx)
+ goto openssl_err;
+ if (EVP_PKEY_derive_init(ctx) != 1)
+ goto openssl_err;
+ if (EVP_PKEY_derive_set_peer(ctx, parent) != 1)
+ goto openssl_err;
+ ssize = sizeof(secret.t.buffer);
+ if (EVP_PKEY_derive(ctx, secret.b.buffer, &ssize) != 1)
+ goto openssl_err;
+ secret.b.size = ssize;
+ EVP_PKEY_CTX_free(ctx);
+
+ tpm2_get_public_point(&pub_pt, group, EC_KEY_get0_public_key(e_parent));
+ tpm2_get_public_point(&ephemeral_pt, group,
+ EC_KEY_get0_public_key(e_ephemeral));
+
+ /* now pass the secret through KDFe to get the shared secret
+ * The size is the size of the parent name algorithm which we
+ * assume to be sha256 */
+ TSS_KDFE(seed.b.buffer, pub->nameAlg, &secret.b, "DUPLICATE",
+ &ephemeral_pt.point.x.b, &pub_pt.point.x.b,
+ SHA256_DIGEST_LENGTH*8);
+ seed.b.size = SHA256_DIGEST_LENGTH;
+
+ /* and finally through KDFa to get the aes symmetric encryption key */
+ tpm2_ObjectPublic_GetName(&name, pub);
+ TSS_KDFA(aeskey, pub->nameAlg, &seed.b, "STORAGE", &name.b, &null_2b,
+ T2_AES_KEY_BITS);
+ /* and then the outer HMAC key */
+ hmackey.b.size = name_alg_size;
+ TSS_KDFA(hmackey.b.buffer, pub->nameAlg, &seed.b, "INTEGRITY",
+ &null_2b, &null_2b, name_alg_size * 8);
+ /* OK the ephermeral public point is now the encrypted secret */
+ size = sizeof(ephemeral_pt);
+ buf = enc_secret->b.buffer;
+ TSS_TPM2B_ECC_POINT_Marshal(&ephemeral_pt, &written,
+ &buf, &size);
+ enc_secret->b.size = written;
+ memset(null_iv, 0, sizeof(null_iv));
+ TSS_AES_EncryptCFB(sensitive, T2_AES_KEY_BITS, aeskey, null_iv,
+ p->t.size - integrity_skip, sensitive);
+ hmac.hashAlg = pub->nameAlg;
+ TSS_HMAC_Generate(&hmac, &hmackey,
+ p->t.size - integrity_skip, sensitive,
+ name.b.size, name.b.buffer,
+ 0, NULL);
+ digest.b.size = name_alg_size;
+ memcpy(digest.b.buffer, &hmac.digest, digest.b.size);
+ size = integrity_skip;
+ buf = p->t.buffer;
+ TSS_TPM2B_DIGEST_Marshal(&digest, &written, &buf, &size);
+ return TPM_RC_SUCCESS;
+
+ openssl_err:
+ ERR_print_errors_fp(stderr);
+ return TPM_RC_ASYMMETRIC;
+}
+
TPM_RC
parse_policy_file(const char *policy_file, STACK_OF(TSSOPTPOLICY) *sk,
char *auth, TPMT_HA *digest)
@@ -378,7 +510,7 @@ int
openssl_write_tpmfile(const char *file, BYTE *pubkey, int pubkey_len,
BYTE *privkey, int privkey_len, int empty_auth,
TPM_HANDLE parent, STACK_OF(TSSOPTPOLICY) *sk,
- int version)
+ int version, TPM2B_ENCRYPTED_SECRET *secret)
{
union {
TSSLOADABLE tssl;
@@ -406,7 +538,14 @@ openssl_write_tpmfile(const char *file, BYTE *pubkey, int pubkey_len,

PEM_write_bio_TSSLOADABLE(outb, &k.tssl);
} else {
- k.tpk.type = OBJ_txt2obj(OID_loadableKey, 1);
+ if (secret) {
+ k.tpk.type = OBJ_txt2obj(OID_importableKey, 1);
+ k.tpk.secret = ASN1_OCTET_STRING_new();
+ ASN1_STRING_set(k.tpk.secret, secret->t.secret,
+ secret->t.size);
+ } else {
+ k.tpk.type = OBJ_txt2obj(OID_loadableKey, 1);
+ }
k.tpk.emptyAuth = empty_auth;
k.tpk.parent = ASN1_INTEGER_new();
ASN1_INTEGER_set(k.tpk.parent, parent);
@@ -445,6 +584,27 @@ openssl_read_key(char *filename)
return pkey;
}

+EVP_PKEY *
+openssl_read_public_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_PUBKEY(b, NULL, NULL, NULL)) == NULL) {
+ fprintf(stderr, "Reading key %s from disk failed.\n", filename);
+ openssl_print_errors();
+ }
+ BIO_free(b);
+
+ return pkey;
+}
Here you are using spaces instead of tabs for indentation. Same in
openssl_read_key().

+
void tpm2_public_template_rsa(TPMT_PUBLIC *pub)
{
pub->type = TPM_ALG_RSA;
@@ -784,7 +944,7 @@ int main(int argc, char **argv)
Create_Out cout;
TPM2B_PUBLIC *pub;
TPM2B_PRIVATE *priv;
- char *key = NULL, *parent_auth = NULL;
+ char *key = NULL, *parent_auth = NULL, *import = NULL;
TPMI_ECC_CURVE ecc = TPM_ECC_NONE;
int rsa = -1;
uint32_t noda = TPMA_OBJECT_NODA;
@@ -794,10 +954,11 @@ int main(int argc, char **argv)
int version = 1;
uint32_t sizeInBytes;
TPMT_HA digest;
+ TPM2B_ENCRYPTED_SECRET secret, *enc_secret = NULL;

while (1) {
option_index = 0;
- c = getopt_long(argc, argv, "n:s:ab:p:hw:vk:re:ldc:",
+ c = getopt_long(argc, argv, "n:s:ab:p:hw:vk:re:ldc:i:",
long_options, &option_index);
if (c == -1)
break;
@@ -873,6 +1034,9 @@ int main(int argc, char **argv)
case 'c':
policyFilename = optarg;
break;
+ case 'i':
+ import = optarg;
+ break;
case OPT_DEPRECATED:
version = 0;
break;
@@ -910,21 +1074,9 @@ int main(int argc, char **argv)
rsa = 0;
}

- dir = tpm2_set_unique_tssdir();
- rc = tpm2_create(&tssContext, dir);
- if (rc) {
- reason = "TSS_Create";
- goto out_err;
- }
-
- if ((parent & 0xff000000) == 0x40000000) {
- rc = tpm2_load_srk(tssContext, &phandle, parent_auth, NULL, parent, version);
- if (rc) {
- reason = "tpm2_load_srk";
- goto out_delete;
- }
- } else {
- phandle = parent;
+ if (import && !wrap) {
+ fprintf(stderr, "Can only wrap importable keys\n");
This print is a bit misleading. You can actually wrap a loadable key.
I guess this should be "Importable keys must be wrapped" or similar.

+ exit(1);
}

digest.hashAlg = name_alg;
@@ -936,13 +1088,13 @@ int main(int argc, char **argv)
if (!sk) {
rc = NOT_TPM_ERROR;
reason="sk_TSSOPTPOLICY_new_null allocation";
- goto out_flush;
+ goto out_err;
}

rc = parse_policy_file(policyFilename, sk, auth, &digest);
if (rc) {
reason = "parse_policy_file";
- goto out_flush;
+ goto out_free_policy;
}
}

@@ -955,11 +1107,90 @@ int main(int argc, char **argv)
fprintf(stderr, "Passwords do not match\n");
reason = "authorization";
rc = NOT_TPM_ERROR;
- goto out_flush;
+ goto out_free_auth;
}
}
}

+ if (import) {
+ EVP_PKEY *p_pkey = openssl_read_public_key(import);
+ EVP_PKEY *pkey = openssl_read_key(wrap);
+ TPMT_SENSITIVE s;
+
+ /* steal existing private and public areas */
+ pub = &iin.objectPublic;
+ priv = &iout.outPrivate;
+
+ rc = NOT_TPM_ERROR;
+
+ if (!p_pkey || !pkey) {
+ reason = "read openssl key";
+ goto out_err;
+ }
+
+ /* FIXME: should do RSA as well, it's just more complex */
+ if (EVP_PKEY_type(EVP_PKEY_id(p_pkey)) != EVP_PKEY_EC) {
+ reason = "parent not EC key";
+ goto out_err;
+ }
+
+ rc = openssl_to_tpm_public(pub, pkey);
+ if (rc) {
+ reason = "openssl_to_tpm_public";
+ goto out_err;
+ }
+ if (policyFilename) {
+ pub->publicArea.objectAttributes.val &=
+ ~TPMA_OBJECT_USERWITHAUTH;
+ rc = TSS_TPM2B_Create(
+ &pub->publicArea.authPolicy.b,
+ (uint8_t *)&digest.digest, sizeInBytes,
+ sizeof(TPMU_HA));
+ if (rc) {
+ reason = "set policy";
+ goto out_err;
+ }
+ }
+
+ rc = wrap_key(&s, auth, pkey);
+ if (rc) {
+ reason = "wrap_key";
+ goto out_err;
+ }
+
+ /* set the NODA flag */
+ pub->publicArea.objectAttributes.val |= noda;
+
+ rc = tpm2_outerwrap(p_pkey, &s, &pub->publicArea,
+ priv, &secret);
+ if (rc) {
+ reason = "tpm2_outerwrap";
+ goto out_err;
+ }
+
+ enc_secret = &secret;
+
+ /* skip over all the TPM connection stuff */
+ goto write_key;
+ }
+
+ dir = tpm2_set_unique_tssdir();
+ rc = tpm2_create(&tssContext, dir);
+ if (rc) {
+ reason = "TSS_Create";
+ goto out_free_auth;
+ }
+
+ if ((parent & 0xff000000) == 0x40000000) {
+ rc = tpm2_load_srk(tssContext, &phandle, parent_auth, NULL, parent, version);
+ if (rc) {
+ reason = "tpm2_load_srk";
+ goto out_delete;
+ }
+ } else {
+ phandle = parent;
+ }
+
if (wrap) {
EVP_PKEY *pkey;
TPMT_SENSITIVE s;
@@ -1113,6 +1344,10 @@ int main(int argc, char **argv)
priv = &cout.outPrivate;
}
tpm2_flush_srk(tssContext, phandle);
+ TSS_Delete(tssContext);
+ tpm2_rm_tssdir(dir, 0);
+
+ write_key:
buffer = pubkey;
pubkey_len = 0;
size = sizeof(pubkey);
@@ -1123,19 +1358,20 @@ int main(int argc, char **argv)
TSS_TPM2B_PRIVATE_Marshal(priv, &privkey_len, &buffer, &size);
openssl_write_tpmfile(filename, pubkey, pubkey_len,
privkey, privkey_len, auth == NULL, parent, sk,
- version);
+ version, enc_secret);
free_policy(sk);
- TSS_Delete(tssContext);
- tpm2_rm_tssdir(dir, 0);

exit(0);

out_flush:
tpm2_flush_srk(tssContext, phandle);
- free_policy(sk);
out_delete:
TSS_Delete(tssContext);
rmdir(dir);
+ out_free_auth:
+ free(auth);
+ out_free_policy:
+ free_policy(sk);
out_err:
if (rc == NOT_TPM_ERROR)
fprintf(stderr, "%s failed\n", reason);
diff --git a/e_tpm2.c b/e_tpm2.c
index 39026e9..84af5e5 100644
--- a/e_tpm2.c
+++ b/e_tpm2.c
@@ -316,7 +316,6 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
struct tpm_ui *ui, void *cb_data)
{
EVP_PKEY *pkey;
- TPM2B_PUBLIC p;
BIO *bf;
TSSLOADABLE *tssl = NULL;
TSSPRIVKEY *tpk = NULL;
@@ -331,6 +330,8 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
ASN1_OCTET_STRING *pubkey;
STACK_OF(TSSOPTPOLICY) *policy;
ASN1_OCTET_STRING *privkey;
+ ASN1_OCTET_STRING *secret = NULL;
+ Import_In iin;

if (!key_id && !bio) {
fprintf(stderr, "key_id or bio is NULL\n");
@@ -367,6 +368,7 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
pubkey = tpk->pubkey;
privkey = tpk->privkey;
policy = tpk->policy;
+ secret = tpk->secret;
} else {
BIO_seek(bf, 0);
tssl = PEM_read_bio_TSSLOADABLE(bf, NULL, NULL, NULL);
@@ -411,8 +413,10 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
goto err;
}
} else if (strcmp(OID_importableKey, oid) == 0) {
- fprintf(stderr, "Importable keys currently unsupported\n");
- goto err;
+ if (!secret) {
+ fprintf(stderr, "Importable keys require an encrypted secret\n");
+ goto err;
+ }
} else {
fprintf(stderr, "Unrecognised object type\n");
goto err;
@@ -435,23 +439,100 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
/* older keys have absent parent */
app_data->parent = TPM_RH_OWNER;

- app_data->priv = OPENSSL_malloc(privkey->length);
- if (!app_data->priv)
- goto err_free;
- app_data->priv_len = privkey->length;
- memcpy(app_data->priv, privkey->data, app_data->priv_len);
-
app_data->pub = OPENSSL_malloc(pubkey->length);
if (!app_data->pub)
goto err_free;
app_data->pub_len = pubkey->length;
memcpy(app_data->pub, pubkey->data, app_data->pub_len);
+
buffer = app_data->pub;
size = app_data->pub_len;
- TPM2B_PUBLIC_Unmarshal(&p, &buffer, &size, FALSE);
- app_data->name_alg = p.publicArea.nameAlg;
+ TPM2B_PUBLIC_Unmarshal(&iin.objectPublic, &buffer, &size, FALSE);
+ app_data->name_alg = iin.objectPublic.publicArea.nameAlg;
+
+ if (strcmp(OID_importableKey, oid) == 0) {
+ TPM_HANDLE session;
+ TSS_CONTEXT *tssContext;
+ TPM_RC rc;
+ const char *reason;
+ TPM2B_PRIVATE priv_2b;
+ BYTE *buf;
+ UINT16 written;
+ INT32 size;
+ Import_Out iout;
+
+ rc = tpm2_create(&tssContext, app_data->dir);
+ if (rc) {
+ reason="tpm2_create";
+ goto import_err;
+ }
+
+ if ((app_data->parent & 0xff000000) == 0x40000000) {
+ tpm2_load_srk(tssContext, &iin.parentHandle,
+ srk_auth, NULL, app_data->parent, 1);
+ } else {
+ iin.parentHandle = app_data->parent;
+ }
+ rc = tpm2_get_session_handle(tssContext, &session,
+ iin.parentHandle,
+ TPM_SE_HMAC,
+ iin.objectPublic.publicArea.nameAlg);
+ if (rc) {
+ reason="tpm2_get_session_handle";
+ goto import_err;
+ }
+
+ /* no inner encryption */
+ iin.encryptionKey.t.size = 0;
+ iin.symmetricAlg.algorithm = TPM_ALG_NULL;
+
+ /* for importable keys the private key is actually the
+ * outer wrapped duplicate structure */
+ buffer = privkey->data;
+ size = privkey->length;
+ TPM2B_PRIVATE_Unmarshal(&iin.duplicate, &buffer, &size);
+
+ buffer = secret->data;
+ size = secret->length;
+ TPM2B_ENCRYPTED_SECRET_Unmarshal(&iin.inSymSeed, &buffer, &size);
+ rc = TSS_Execute(tssContext,
+ (RESPONSE_PARAMETERS *)&iout,
+ (COMMAND_PARAMETERS *)&iin,
+ NULL,
+ TPM_CC_Import,
+ session, srk_auth, 0,
+ TPM_RH_NULL, NULL, 0);
+ if (rc)
+ tpm2_flush_handle(tssContext, session);
+ reason = "TPM2_Import";
+
+ import_err:
+ tpm2_flush_srk(tssContext, iin.parentHandle);
+ TSS_Delete(tssContext);
+ if (rc) {
+ tpm2_error(rc, reason);
+ goto err_free;
+ }
+ buf = priv_2b.t.buffer;
+ size = sizeof(priv_2b.t.buffer);
+ TSS_TPM2B_PRIVATE_Marshal(&iout.outPrivate, &written,
+ &buf, &size);
+ app_data->priv = OPENSSL_malloc(written);
+ if (!app_data->priv)
+ goto err_free;
+ app_data->priv_len = written;
+ memcpy(app_data->priv, priv_2b.t.buffer, written);
+ } else {
+ app_data->priv = OPENSSL_malloc(privkey->length);
+ if (!app_data->priv)
+ goto err_free;
+
+ app_data->priv_len = privkey->length;
+ memcpy(app_data->priv, privkey->data, app_data->priv_len);
+ }
+
/* create the new objects to return */
- pkey = tpm2_to_openssl_public(&p.publicArea);
+ pkey = tpm2_to_openssl_public(&iin.objectPublic.publicArea);
if (!pkey) {
fprintf(stderr, "Failed to allocate a new EVP_KEY\n");
goto err_free;
@@ -463,7 +544,8 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
goto err_free_key;
}

- if (!(p.publicArea.objectAttributes.val & TPMA_OBJECT_USERWITHAUTH))
+ if (!(iin.objectPublic.publicArea.objectAttributes.val &
+ TPMA_OBJECT_USERWITHAUTH))
app_data->req_policy_session = 1;

if (!tpm2_engine_load_key_policy(app_data, policy))
diff --git a/tpm2-asn.h b/tpm2-asn.h
index 15e0f84..82241be 100644
--- a/tpm2-asn.h
+++ b/tpm2-asn.h
@@ -65,6 +65,7 @@ DEFINE_STACK_OF(TSSOPTPOLICY);
* type OBJECT IDENTIFIER
* emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL
* policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL
+ * secret [2] EXPLICIT OCTET STRING OPTIONAL
* parent INTEGER
* pubkey OCTET STRING
* privkey OCTET STRING
@@ -84,6 +85,7 @@ typedef struct {
ASN1_OBJECT *type;
ASN1_BOOLEAN emptyAuth;
STACK_OF(TSSOPTPOLICY) *policy;
+ ASN1_OCTET_STRING *secret;
ASN1_INTEGER *parent;
ASN1_OCTET_STRING *pubkey;
ASN1_OCTET_STRING *privkey;
@@ -120,6 +122,7 @@ ASN1_SEQUENCE(TSSPRIVKEY) = {
ASN1_SIMPLE(TSSPRIVKEY, type, ASN1_OBJECT),
ASN1_EXP_OPT(TSSPRIVKEY, emptyAuth, ASN1_BOOLEAN, 0),
ASN1_EXP_SEQUENCE_OF_OPT(TSSPRIVKEY, policy, TSSOPTPOLICY, 1),
+ ASN1_EXP_OPT(TSSPRIVKEY, secret, ASN1_OCTET_STRING, 2),
ASN1_SIMPLE(TSSPRIVKEY, parent, ASN1_INTEGER),
ASN1_SIMPLE(TSSPRIVKEY, pubkey, ASN1_OCTET_STRING),
ASN1_SIMPLE(TSSPRIVKEY, privkey, ASN1_OCTET_STRING)
--
2.16.4



next branch added to openssl_tpm2_engine.git

James Bottomley
 

By popular request (as in David has been bugging me about it) I've
added a next branch to the git tree. This branch represents the state
of my current patch branch, so it includes most things that have been
sent to the list but not integrated into the master branch. I should
warn you that this branch will rebase pretty regularly because that's
how I develop and I usually import patches from the mailing list rathe
than pulling them from my development branch, so the tree master will
likely not be fast forwardable to the next branch.

James

[PATCH 4/4] Add tests for importable keys

James Bottomley
 

Add two simple tests: creating an importable EC key with auth and
creating an importable RSA key with policy. Verify a self signed
certificate created with each key.

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

diff --git a/tests/Makefile.am b/tests/Makefile.am
index dc07284..0294dd0 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -17,6 +17,7 @@ TESTS = fail_connect.sh \
test_nv_key.sh \
check_enhanced_auth.sh \
check_counter_timer.sh \
+ check_importable.sh \
stop_sw_tpm.sh

AM_TESTS_ENVIRONMENT = TPM_INTERFACE_TYPE=socsim; \
diff --git a/tests/check_importable.sh b/tests/check_importable.sh
new file mode 100755
index 0000000..2b01f5b
--- /dev/null
+++ b/tests/check_importable.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+bindir=${srcdir}/..
+
+# export the parent key as a public key
+prim=$(tsscreateprimary -ecc nistp256 -hi o -opem srk.pub | sed 's/Handle //') || exit 1
+tssflushcontext -ha ${prim} || exit 1
+
+# check an EC key with a cert and password
+openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:prime256v1 -out key.priv || exit 1
+${bindir}/create_tpm2_key --import srk.pub --wrap key.priv -a -k passw0rd key.tpm || exit 1
+openssl req -new -x509 -subj '/CN=test/' -key key.tpm -passin pass:passw0rd -engine tpm2 -keyform engine -out tmp.crt || exit 1
+openssl verify -CAfile tmp.crt --check_ss_sig tmp.crt || exit 1
+
+#check an RSA key with a cert and policy
+openssl genrsa 2048 > key.priv || exit 1
+${bindir}/create_tpm2_key --import srk.pub --wrap key.priv -a -k passw0rd -c policies/policy_authvalue.txt key.tpm || exit 1
+openssl req -new -x509 -subj '/CN=test/' -key key.tpm -passin pass:passw0rd -engine tpm2 -keyform engine -out tmp.crt || exit 1
+openssl verify -CAfile tmp.crt --check_ss_sig tmp.crt || exit 1
+
--
2.16.4

[PATCH 3/4] Add importable keys

James Bottomley
 

Importable keys are a new type of key that can be created without
access to the receiving TPM. The design of importable keys is that
you "wrap" the key (encrypt with a derived key) in such a way that it
can only be unwrapped by the TPM local parent key. To do this
wrapping you only need to know the public key of the parent, which can
be shipped in an ordinary openssl public key file.

The key format of importable keys requires an additional encrypted
secret parameter, which basically encodes the wrapping key using ECDH
so it can be decrypted by the parent on import.

Importable keys are designed to be used everywhere the current
loadable keys are used, the only difference being that the TPM engine
converts the importable key to loadable on first load and thereafter
uses it identically to a loadable key.

The new command line option of importable keys is

create_tpm2_key --import <pubparent> --wrap <private key> tmp.key

Where <pubparent> is the parent public key and <private key> is the
key you are wrapping (obviously since you have no access to the TPM,
this option can only be used to wrap existing private keys).

Signed-off-by: James Bottomley <James.Bottomley@...>
---
create_tpm2_key.1.in | 17 +++
create_tpm2_key.c | 288 ++++++++++++++++++++++++++++++++++++++++++++++-----
e_tpm2.c | 108 ++++++++++++++++---
tpm2-asn.h | 3 +
4 files changed, 377 insertions(+), 39 deletions(-)

diff --git a/create_tpm2_key.1.in b/create_tpm2_key.1.in
index 0e52d86..6ec5f25 100644
--- a/create_tpm2_key.1.in
+++ b/create_tpm2_key.1.in
@@ -7,6 +7,13 @@ Can be used to create a TPM loadable representation of a private key.
The key is either internal to the TPM or wrapped from an existing
private key.

+Note that this command can now create two different types of keys:
+importable and loadable (the default type being loadable). The
+difference between the two is that the creation of loadable keys
+requires the presence of the actual TPM the key will be loaded on. An
+importable key can be created without any TPM contact provided you
+have the public key of the parent the new key will be imported to.
+
[files]

Policy File Format
@@ -69,3 +76,13 @@ Create a self-signed cert using the TPM engine:
3. Test using openssl:
$ openssl s_server -cert <certfilename> -www -accept 4433 -keyform engine -engi
ne tpm -key <keyfilename>
+
+Creating an importable key:
+
+1. First get the public key of the storage primary seed (as an elliptic
+ curve key):
+ $ tsscreateprimary -ecc nistp256 -hi o -opem srk.pub
+
+2. Once you have this public key, you can export it anywhere and do the
+ key wrapping
+ $ create_tpm2_key --import srk.pub --wrap my_key.priv my_key.tpm
diff --git a/create_tpm2_key.c b/create_tpm2_key.c
index 899e434..0576c83 100644
--- a/create_tpm2_key.c
+++ b/create_tpm2_key.c
@@ -58,6 +58,7 @@ static struct option long_options[] = {
{"list-curves", 0, 0, 'l'},
{"da", 0, 0, 'd'},
{"key-policy", 1, 0, 'c'},
+ {"import", 1, 0, 'i'},
/*
* The option --deprecated allows us to create old format keys
* for the purposes of testing. It should never be used in
@@ -98,6 +99,8 @@ usage(char *argv0)
"\t Supported curves are bnp256, nisp256, nisp384\n"
"\t-l, --list-curves List all the Elliptic Curves the TPM supports\n"
"\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"
"\n"
"Report bugs to " PACKAGE_BUGREPORT "\n",
argv0);
@@ -270,6 +273,135 @@ TPM_RC tpm2_innerwrap(TPMT_SENSITIVE *s,
return TPM_RC_SUCCESS;
}

+TPM_RC tpm2_outerwrap(EVP_PKEY *parent,
+ TPMT_SENSITIVE *s,
+ TPMT_PUBLIC *pub,
+ TPM2B_PRIVATE *p,
+ TPM2B_ENCRYPTED_SECRET *enc_secret)
+{
+ TPM2B_PRIVATE secret, seed;
+ /* amount of room in the buffer for the integrity TPM2B */
+ const int name_alg_size = TSS_GetDigestSize(pub->nameAlg);
+ const int integrity_skip = name_alg_size + 2;
+ // BYTE *integrity = p->t.buffer;
+ BYTE *sensitive = p->t.buffer + integrity_skip;
+ BYTE *buf;
+ TPM2B *t2b;
+ INT32 size;
+ size_t ssize;
+ UINT16 bsize, written = 0;
+ EVP_PKEY *ephemeral = NULL;
+ EVP_PKEY_CTX *ctx;
+ TPM2B_ECC_POINT pub_pt, ephemeral_pt;
+ const EC_KEY *e_parent, *e_ephemeral;
+ const EC_GROUP *group;
+ unsigned char aeskey[T2_AES_KEY_BYTES];
+ /* hmac follows namealg, so set to max size */
+ TPM2B_KEY hmackey;
+ TPMT_HA hmac;
+ TPM2B_NAME name;
+ TPM2B_DIGEST digest;
+ unsigned char null_iv[AES_128_BLOCK_SIZE_BYTES];
+ TPM2B null_2b;
+
+ null_2b.size = 0;
+
+ if (EVP_PKEY_type(EVP_PKEY_id(parent)) != EVP_PKEY_EC) {
+ printf("Can only currently wrap to EC parent\n");
+ return TPM_RC_ASYMMETRIC;
+ }
+
+ e_parent = EVP_PKEY_get0_EC_KEY(parent);
+ group = EC_KEY_get0_group(e_parent);
+
+ /* marshal the sensitive into a TPM2B */
+ t2b = (TPM2B *)sensitive;
+ buf = t2b->buffer;
+ size = sizeof(p->t.buffer) - integrity_skip;
+ bsize = 0;
+ TSS_TPMT_SENSITIVE_Marshal(s, &bsize, &buf, &size);
+ buf = (BYTE *)&t2b->size;
+ size = 2;
+ TSS_UINT16_Marshal(&bsize, &written, &buf, &size);
+ /* set the total size of the private entity */
+ p->b.size = bsize + sizeof(UINT16) + integrity_skip;
+
+ /* compute the elliptic curve shared (and encrypted) secret */
+ ctx = EVP_PKEY_CTX_new(parent, NULL);
+ if (!ctx)
+ goto openssl_err;
+ if (EVP_PKEY_keygen_init(ctx) != 1)
+ goto openssl_err;
+ EVP_PKEY_keygen(ctx, &ephemeral);
+ if (!ephemeral)
+ goto openssl_err;
+ /* otherwise the ctx free will free the key */
+ EVP_PKEY_up_ref(ephemeral);
+ EVP_PKEY_CTX_free(ctx);
+
+ e_ephemeral = EVP_PKEY_get0_EC_KEY(ephemeral);
+
+ /* now begin again with the ephemeral private key because the
+ * context must be initialised with the private key */
+ ctx = EVP_PKEY_CTX_new(ephemeral, NULL);
+ if (!ctx)
+ goto openssl_err;
+ if (EVP_PKEY_derive_init(ctx) != 1)
+ goto openssl_err;
+ if (EVP_PKEY_derive_set_peer(ctx, parent) != 1)
+ goto openssl_err;
+ ssize = sizeof(secret.t.buffer);
+ if (EVP_PKEY_derive(ctx, secret.b.buffer, &ssize) != 1)
+ goto openssl_err;
+ secret.b.size = ssize;
+ EVP_PKEY_CTX_free(ctx);
+
+ tpm2_get_public_point(&pub_pt, group, EC_KEY_get0_public_key(e_parent));
+ tpm2_get_public_point(&ephemeral_pt, group,
+ EC_KEY_get0_public_key(e_ephemeral));
+
+ /* now pass the secret through KDFe to get the shared secret
+ * The size is the size of the parent name algorithm which we
+ * assume to be sha256 */
+ TSS_KDFE(seed.b.buffer, pub->nameAlg, &secret.b, "DUPLICATE",
+ &ephemeral_pt.point.x.b, &pub_pt.point.x.b,
+ SHA256_DIGEST_LENGTH*8);
+ seed.b.size = SHA256_DIGEST_LENGTH;
+
+ /* and finally through KDFa to get the aes symmetric encryption key */
+ tpm2_ObjectPublic_GetName(&name, pub);
+ TSS_KDFA(aeskey, pub->nameAlg, &seed.b, "STORAGE", &name.b, &null_2b,
+ T2_AES_KEY_BITS);
+ /* and then the outer HMAC key */
+ hmackey.b.size = name_alg_size;
+ TSS_KDFA(hmackey.b.buffer, pub->nameAlg, &seed.b, "INTEGRITY",
+ &null_2b, &null_2b, name_alg_size * 8);
+ /* OK the ephermeral public point is now the encrypted secret */
+ size = sizeof(ephemeral_pt);
+ buf = enc_secret->b.buffer;
+ TSS_TPM2B_ECC_POINT_Marshal(&ephemeral_pt, &written,
+ &buf, &size);
+ enc_secret->b.size = written;
+ memset(null_iv, 0, sizeof(null_iv));
+ TSS_AES_EncryptCFB(sensitive, T2_AES_KEY_BITS, aeskey, null_iv,
+ p->t.size - integrity_skip, sensitive);
+ hmac.hashAlg = pub->nameAlg;
+ TSS_HMAC_Generate(&hmac, &hmackey,
+ p->t.size - integrity_skip, sensitive,
+ name.b.size, name.b.buffer,
+ 0, NULL);
+ digest.b.size = name_alg_size;
+ memcpy(digest.b.buffer, &hmac.digest, digest.b.size);
+ size = integrity_skip;
+ buf = p->t.buffer;
+ TSS_TPM2B_DIGEST_Marshal(&digest, &written, &buf, &size);
+ return TPM_RC_SUCCESS;
+
+ openssl_err:
+ ERR_print_errors_fp(stderr);
+ return TPM_RC_ASYMMETRIC;
+}
+
TPM_RC
parse_policy_file(const char *policy_file, STACK_OF(TSSOPTPOLICY) *sk,
char *auth, TPMT_HA *digest)
@@ -378,7 +510,7 @@ int
openssl_write_tpmfile(const char *file, BYTE *pubkey, int pubkey_len,
BYTE *privkey, int privkey_len, int empty_auth,
TPM_HANDLE parent, STACK_OF(TSSOPTPOLICY) *sk,
- int version)
+ int version, TPM2B_ENCRYPTED_SECRET *secret)
{
union {
TSSLOADABLE tssl;
@@ -406,7 +538,14 @@ openssl_write_tpmfile(const char *file, BYTE *pubkey, int pubkey_len,

PEM_write_bio_TSSLOADABLE(outb, &k.tssl);
} else {
- k.tpk.type = OBJ_txt2obj(OID_loadableKey, 1);
+ if (secret) {
+ k.tpk.type = OBJ_txt2obj(OID_importableKey, 1);
+ k.tpk.secret = ASN1_OCTET_STRING_new();
+ ASN1_STRING_set(k.tpk.secret, secret->t.secret,
+ secret->t.size);
+ } else {
+ k.tpk.type = OBJ_txt2obj(OID_loadableKey, 1);
+ }
k.tpk.emptyAuth = empty_auth;
k.tpk.parent = ASN1_INTEGER_new();
ASN1_INTEGER_set(k.tpk.parent, parent);
@@ -445,6 +584,27 @@ openssl_read_key(char *filename)
return pkey;
}

+EVP_PKEY *
+openssl_read_public_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_PUBKEY(b, NULL, NULL, NULL)) == NULL) {
+ fprintf(stderr, "Reading key %s from disk failed.\n", filename);
+ openssl_print_errors();
+ }
+ BIO_free(b);
+
+ return pkey;
+}
+
void tpm2_public_template_rsa(TPMT_PUBLIC *pub)
{
pub->type = TPM_ALG_RSA;
@@ -784,7 +944,7 @@ int main(int argc, char **argv)
Create_Out cout;
TPM2B_PUBLIC *pub;
TPM2B_PRIVATE *priv;
- char *key = NULL, *parent_auth = NULL;
+ char *key = NULL, *parent_auth = NULL, *import = NULL;
TPMI_ECC_CURVE ecc = TPM_ECC_NONE;
int rsa = -1;
uint32_t noda = TPMA_OBJECT_NODA;
@@ -794,10 +954,11 @@ int main(int argc, char **argv)
int version = 1;
uint32_t sizeInBytes;
TPMT_HA digest;
+ TPM2B_ENCRYPTED_SECRET secret, *enc_secret = NULL;

while (1) {
option_index = 0;
- c = getopt_long(argc, argv, "n:s:ab:p:hw:vk:re:ldc:",
+ c = getopt_long(argc, argv, "n:s:ab:p:hw:vk:re:ldc:i:",
long_options, &option_index);
if (c == -1)
break;
@@ -873,6 +1034,9 @@ int main(int argc, char **argv)
case 'c':
policyFilename = optarg;
break;
+ case 'i':
+ import = optarg;
+ break;
case OPT_DEPRECATED:
version = 0;
break;
@@ -910,21 +1074,9 @@ int main(int argc, char **argv)
rsa = 0;
}

- dir = tpm2_set_unique_tssdir();
- rc = tpm2_create(&tssContext, dir);
- if (rc) {
- reason = "TSS_Create";
- goto out_err;
- }
-
- if ((parent & 0xff000000) == 0x40000000) {
- rc = tpm2_load_srk(tssContext, &phandle, parent_auth, NULL, parent, version);
- if (rc) {
- reason = "tpm2_load_srk";
- goto out_delete;
- }
- } else {
- phandle = parent;
+ if (import && !wrap) {
+ fprintf(stderr, "Can only wrap importable keys\n");
+ exit(1);
}

digest.hashAlg = name_alg;
@@ -936,13 +1088,13 @@ int main(int argc, char **argv)
if (!sk) {
rc = NOT_TPM_ERROR;
reason="sk_TSSOPTPOLICY_new_null allocation";
- goto out_flush;
+ goto out_err;
}

rc = parse_policy_file(policyFilename, sk, auth, &digest);
if (rc) {
reason = "parse_policy_file";
- goto out_flush;
+ goto out_free_policy;
}
}

@@ -955,11 +1107,90 @@ int main(int argc, char **argv)
fprintf(stderr, "Passwords do not match\n");
reason = "authorization";
rc = NOT_TPM_ERROR;
- goto out_flush;
+ goto out_free_auth;
}
}
}

+ if (import) {
+ EVP_PKEY *p_pkey = openssl_read_public_key(import);
+ EVP_PKEY *pkey = openssl_read_key(wrap);
+ TPMT_SENSITIVE s;
+
+ /* steal existing private and public areas */
+ pub = &iin.objectPublic;
+ priv = &iout.outPrivate;
+
+ rc = NOT_TPM_ERROR;
+
+ if (!p_pkey || !pkey) {
+ reason = "read openssl key";
+ goto out_err;
+ }
+
+ /* FIXME: should do RSA as well, it's just more complex */
+ if (EVP_PKEY_type(EVP_PKEY_id(p_pkey)) != EVP_PKEY_EC) {
+ reason = "parent not EC key";
+ goto out_err;
+ }
+
+ rc = openssl_to_tpm_public(pub, pkey);
+ if (rc) {
+ reason = "openssl_to_tpm_public";
+ goto out_err;
+ }
+ if (policyFilename) {
+ pub->publicArea.objectAttributes.val &=
+ ~TPMA_OBJECT_USERWITHAUTH;
+ rc = TSS_TPM2B_Create(
+ &pub->publicArea.authPolicy.b,
+ (uint8_t *)&digest.digest, sizeInBytes,
+ sizeof(TPMU_HA));
+ if (rc) {
+ reason = "set policy";
+ goto out_err;
+ }
+ }
+
+ rc = wrap_key(&s, auth, pkey);
+ if (rc) {
+ reason = "wrap_key";
+ goto out_err;
+ }
+
+ /* set the NODA flag */
+ pub->publicArea.objectAttributes.val |= noda;
+
+ rc = tpm2_outerwrap(p_pkey, &s, &pub->publicArea,
+ priv, &secret);
+ if (rc) {
+ reason = "tpm2_outerwrap";
+ goto out_err;
+ }
+
+ enc_secret = &secret;
+
+ /* skip over all the TPM connection stuff */
+ goto write_key;
+ }
+
+ dir = tpm2_set_unique_tssdir();
+ rc = tpm2_create(&tssContext, dir);
+ if (rc) {
+ reason = "TSS_Create";
+ goto out_free_auth;
+ }
+
+ if ((parent & 0xff000000) == 0x40000000) {
+ rc = tpm2_load_srk(tssContext, &phandle, parent_auth, NULL, parent, version);
+ if (rc) {
+ reason = "tpm2_load_srk";
+ goto out_delete;
+ }
+ } else {
+ phandle = parent;
+ }
+
if (wrap) {
EVP_PKEY *pkey;
TPMT_SENSITIVE s;
@@ -1113,6 +1344,10 @@ int main(int argc, char **argv)
priv = &cout.outPrivate;
}
tpm2_flush_srk(tssContext, phandle);
+ TSS_Delete(tssContext);
+ tpm2_rm_tssdir(dir, 0);
+
+ write_key:
buffer = pubkey;
pubkey_len = 0;
size = sizeof(pubkey);
@@ -1123,19 +1358,20 @@ int main(int argc, char **argv)
TSS_TPM2B_PRIVATE_Marshal(priv, &privkey_len, &buffer, &size);
openssl_write_tpmfile(filename, pubkey, pubkey_len,
privkey, privkey_len, auth == NULL, parent, sk,
- version);
+ version, enc_secret);
free_policy(sk);
- TSS_Delete(tssContext);
- tpm2_rm_tssdir(dir, 0);

exit(0);

out_flush:
tpm2_flush_srk(tssContext, phandle);
- free_policy(sk);
out_delete:
TSS_Delete(tssContext);
rmdir(dir);
+ out_free_auth:
+ free(auth);
+ out_free_policy:
+ free_policy(sk);
out_err:
if (rc == NOT_TPM_ERROR)
fprintf(stderr, "%s failed\n", reason);
diff --git a/e_tpm2.c b/e_tpm2.c
index 39026e9..84af5e5 100644
--- a/e_tpm2.c
+++ b/e_tpm2.c
@@ -316,7 +316,6 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
struct tpm_ui *ui, void *cb_data)
{
EVP_PKEY *pkey;
- TPM2B_PUBLIC p;
BIO *bf;
TSSLOADABLE *tssl = NULL;
TSSPRIVKEY *tpk = NULL;
@@ -331,6 +330,8 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
ASN1_OCTET_STRING *pubkey;
STACK_OF(TSSOPTPOLICY) *policy;
ASN1_OCTET_STRING *privkey;
+ ASN1_OCTET_STRING *secret = NULL;
+ Import_In iin;

if (!key_id && !bio) {
fprintf(stderr, "key_id or bio is NULL\n");
@@ -367,6 +368,7 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
pubkey = tpk->pubkey;
privkey = tpk->privkey;
policy = tpk->policy;
+ secret = tpk->secret;
} else {
BIO_seek(bf, 0);
tssl = PEM_read_bio_TSSLOADABLE(bf, NULL, NULL, NULL);
@@ -411,8 +413,10 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
goto err;
}
} else if (strcmp(OID_importableKey, oid) == 0) {
- fprintf(stderr, "Importable keys currently unsupported\n");
- goto err;
+ if (!secret) {
+ fprintf(stderr, "Importable keys require an encrypted secret\n");
+ goto err;
+ }
} else {
fprintf(stderr, "Unrecognised object type\n");
goto err;
@@ -435,23 +439,100 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
/* older keys have absent parent */
app_data->parent = TPM_RH_OWNER;

- app_data->priv = OPENSSL_malloc(privkey->length);
- if (!app_data->priv)
- goto err_free;
- app_data->priv_len = privkey->length;
- memcpy(app_data->priv, privkey->data, app_data->priv_len);
-
app_data->pub = OPENSSL_malloc(pubkey->length);
if (!app_data->pub)
goto err_free;
app_data->pub_len = pubkey->length;
memcpy(app_data->pub, pubkey->data, app_data->pub_len);
+
buffer = app_data->pub;
size = app_data->pub_len;
- TPM2B_PUBLIC_Unmarshal(&p, &buffer, &size, FALSE);
- app_data->name_alg = p.publicArea.nameAlg;
+ TPM2B_PUBLIC_Unmarshal(&iin.objectPublic, &buffer, &size, FALSE);
+ app_data->name_alg = iin.objectPublic.publicArea.nameAlg;
+
+ if (strcmp(OID_importableKey, oid) == 0) {
+ TPM_HANDLE session;
+ TSS_CONTEXT *tssContext;
+ TPM_RC rc;
+ const char *reason;
+ TPM2B_PRIVATE priv_2b;
+ BYTE *buf;
+ UINT16 written;
+ INT32 size;
+ Import_Out iout;
+
+ rc = tpm2_create(&tssContext, app_data->dir);
+ if (rc) {
+ reason="tpm2_create";
+ goto import_err;
+ }
+
+ if ((app_data->parent & 0xff000000) == 0x40000000) {
+ tpm2_load_srk(tssContext, &iin.parentHandle,
+ srk_auth, NULL, app_data->parent, 1);
+ } else {
+ iin.parentHandle = app_data->parent;
+ }
+ rc = tpm2_get_session_handle(tssContext, &session,
+ iin.parentHandle,
+ TPM_SE_HMAC,
+ iin.objectPublic.publicArea.nameAlg);
+ if (rc) {
+ reason="tpm2_get_session_handle";
+ goto import_err;
+ }
+
+ /* no inner encryption */
+ iin.encryptionKey.t.size = 0;
+ iin.symmetricAlg.algorithm = TPM_ALG_NULL;
+
+ /* for importable keys the private key is actually the
+ * outer wrapped duplicate structure */
+ buffer = privkey->data;
+ size = privkey->length;
+ TPM2B_PRIVATE_Unmarshal(&iin.duplicate, &buffer, &size);
+
+ buffer = secret->data;
+ size = secret->length;
+ TPM2B_ENCRYPTED_SECRET_Unmarshal(&iin.inSymSeed, &buffer, &size);
+ rc = TSS_Execute(tssContext,
+ (RESPONSE_PARAMETERS *)&iout,
+ (COMMAND_PARAMETERS *)&iin,
+ NULL,
+ TPM_CC_Import,
+ session, srk_auth, 0,
+ TPM_RH_NULL, NULL, 0);
+ if (rc)
+ tpm2_flush_handle(tssContext, session);
+ reason = "TPM2_Import";
+
+ import_err:
+ tpm2_flush_srk(tssContext, iin.parentHandle);
+ TSS_Delete(tssContext);
+ if (rc) {
+ tpm2_error(rc, reason);
+ goto err_free;
+ }
+ buf = priv_2b.t.buffer;
+ size = sizeof(priv_2b.t.buffer);
+ TSS_TPM2B_PRIVATE_Marshal(&iout.outPrivate, &written,
+ &buf, &size);
+ app_data->priv = OPENSSL_malloc(written);
+ if (!app_data->priv)
+ goto err_free;
+ app_data->priv_len = written;
+ memcpy(app_data->priv, priv_2b.t.buffer, written);
+ } else {
+ app_data->priv = OPENSSL_malloc(privkey->length);
+ if (!app_data->priv)
+ goto err_free;
+
+ app_data->priv_len = privkey->length;
+ memcpy(app_data->priv, privkey->data, app_data->priv_len);
+ }
+
/* create the new objects to return */
- pkey = tpm2_to_openssl_public(&p.publicArea);
+ pkey = tpm2_to_openssl_public(&iin.objectPublic.publicArea);
if (!pkey) {
fprintf(stderr, "Failed to allocate a new EVP_KEY\n");
goto err_free;
@@ -463,7 +544,8 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
goto err_free_key;
}

- if (!(p.publicArea.objectAttributes.val & TPMA_OBJECT_USERWITHAUTH))
+ if (!(iin.objectPublic.publicArea.objectAttributes.val &
+ TPMA_OBJECT_USERWITHAUTH))
app_data->req_policy_session = 1;

if (!tpm2_engine_load_key_policy(app_data, policy))
diff --git a/tpm2-asn.h b/tpm2-asn.h
index 15e0f84..82241be 100644
--- a/tpm2-asn.h
+++ b/tpm2-asn.h
@@ -65,6 +65,7 @@ DEFINE_STACK_OF(TSSOPTPOLICY);
* type OBJECT IDENTIFIER
* emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL
* policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL
+ * secret [2] EXPLICIT OCTET STRING OPTIONAL
* parent INTEGER
* pubkey OCTET STRING
* privkey OCTET STRING
@@ -84,6 +85,7 @@ typedef struct {
ASN1_OBJECT *type;
ASN1_BOOLEAN emptyAuth;
STACK_OF(TSSOPTPOLICY) *policy;
+ ASN1_OCTET_STRING *secret;
ASN1_INTEGER *parent;
ASN1_OCTET_STRING *pubkey;
ASN1_OCTET_STRING *privkey;
@@ -120,6 +122,7 @@ ASN1_SEQUENCE(TSSPRIVKEY) = {
ASN1_SIMPLE(TSSPRIVKEY, type, ASN1_OBJECT),
ASN1_EXP_OPT(TSSPRIVKEY, emptyAuth, ASN1_BOOLEAN, 0),
ASN1_EXP_SEQUENCE_OF_OPT(TSSPRIVKEY, policy, TSSOPTPOLICY, 1),
+ ASN1_EXP_OPT(TSSPRIVKEY, secret, ASN1_OCTET_STRING, 2),
ASN1_SIMPLE(TSSPRIVKEY, parent, ASN1_INTEGER),
ASN1_SIMPLE(TSSPRIVKEY, pubkey, ASN1_OCTET_STRING),
ASN1_SIMPLE(TSSPRIVKEY, privkey, ASN1_OCTET_STRING)
--
2.16.4

[PATCH 2/4] tpm2-common: remove interfaces only used by create-tpm2-key

James Bottomley
 

The sensitive to duplicate interface is really only used for key
wrapping, and this is only done at key creation time, not on key
processing, so it has no place in the common code.

When moving to create_tpm2_key.c we change the calling convention to
make it more clear that we're not doing full conversion, merely inner
wrapping the key.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
create_tpm2_key.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++-----
tpm2-common.c | 136 +-----------------------------------------------
tpm2-common.h | 9 ----
3 files changed, 142 insertions(+), 156 deletions(-)

diff --git a/create_tpm2_key.c b/create_tpm2_key.c
index 2c7f3c1..899e434 100644
--- a/create_tpm2_key.c
+++ b/create_tpm2_key.c
@@ -15,6 +15,8 @@
#include <fcntl.h>
#include <ctype.h>

+#include <arpa/inet.h>
+
#include <sys/stat.h>
#include <sys/mman.h>

@@ -29,6 +31,7 @@
#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"
@@ -134,6 +137,139 @@ int hex2bin(unsigned char *dst, const char *src, size_t count)
return 0;
}

+TPM_RC tpm2_ObjectPublic_GetName(TPM2B_NAME *name,
+ TPMT_PUBLIC *tpmtPublic)
+{
+ TPM_RC rc = 0;
+ uint16_t written = 0;
+ TPMT_HA digest;
+ uint32_t sizeInBytes;
+ uint8_t buffer[MAX_RESPONSE_SIZE];
+
+ /* marshal the TPMT_PUBLIC */
+ if (rc == 0) {
+ INT32 size = MAX_RESPONSE_SIZE;
+ uint8_t *buffer1 = buffer;
+ rc = TSS_TPMT_PUBLIC_Marshal(tpmtPublic, &written, &buffer1, &size);
+ }
+ /* hash the public area */
+ if (rc == 0) {
+ sizeInBytes = TSS_GetDigestSize(tpmtPublic->nameAlg);
+ digest.hashAlg = tpmtPublic->nameAlg; /* Name digest algorithm */
+ /* generate the TPMT_HA */
+ rc = TSS_Hash_Generate(&digest,
+ written, buffer,
+ 0, NULL);
+ }
+ if (rc == 0) {
+ /* copy the digest */
+ memcpy(name->t.name + sizeof(TPMI_ALG_HASH), (uint8_t *)&digest.digest, sizeInBytes);
+ /* copy the hash algorithm */
+ TPMI_ALG_HASH nameAlgNbo = htons(tpmtPublic->nameAlg);
+ memcpy(name->t.name, (uint8_t *)&nameAlgNbo, sizeof(TPMI_ALG_HASH));
+ /* set the size */
+ name->t.size = sizeInBytes + sizeof(TPMI_ALG_HASH);
+ }
+ return rc;
+}
+
+/*
+ * Cut down version of Part 4 Supporting Routines 7.6.3.10
+ *
+ * Hard coded to symmetrically encrypt with aes128 as the inner
+ * wrapper and no outer wrapper but with a prototype that allows
+ * drop in replacement with a tss equivalent
+ */
+TPM_RC tpm2_innerwrap(TPMT_SENSITIVE *s,
+ TPMT_PUBLIC *pub,
+ TPMT_SYM_DEF_OBJECT *symdef,
+ TPM2B_DATA *innerkey,
+ TPM2B_PRIVATE *p)
+{
+ BYTE *buf = p->t.buffer;
+
+ p->t.size = 0;
+ memset(p, 0, sizeof(*p));
+
+ /* hard code AES CFB */
+ if (symdef->algorithm == TPM_ALG_AES
+ && symdef->mode.aes == TPM_ALG_CFB) {
+ TPMT_HA hash;
+ const TPM_ALG_ID nalg = pub->nameAlg;
+ const int hlen = TSS_GetDigestSize(nalg);
+ TPM2B *digest = (TPM2B *)buf;
+ TPM2B *s2b;
+ int32_t size;
+ unsigned char null_iv[AES_128_BLOCK_SIZE_BYTES];
+ UINT16 bsize, written = 0;
+ TPM2B_NAME name;
+
+ /* WARNING: don't use the static null_iv trick here:
+ * the AES routines alter the passed in iv */
+ memset(null_iv, 0, sizeof(null_iv));
+
+ /* reserve space for hash before the encrypted sensitive */
+ bsize = sizeof(digest->size) + hlen;
+ buf += bsize;
+ p->t.size += bsize;
+ s2b = (TPM2B *)buf;
+
+ /* marshal the digest size */
+ buf = (BYTE *)&digest->size;
+ bsize = hlen;
+ size = 2;
+ TSS_UINT16_Marshal(&bsize, &written, &buf, &size);
+
+ /* marshal the unencrypted sensitive in place */
+ size = sizeof(*s);
+ bsize = 0;
+ buf = s2b->buffer;
+ TSS_TPMT_SENSITIVE_Marshal(s, &bsize, &buf, &size);
+ buf = (BYTE *)&s2b->size;
+ size = 2;
+ TSS_UINT16_Marshal(&bsize, &written, &buf, &size);
+
+ bsize = bsize + sizeof(s2b->size);
+ p->t.size += bsize;
+
+ tpm2_ObjectPublic_GetName(&name, pub);
+ /* compute hash of unencrypted marshalled sensitive and
+ * write to the digest buffer */
+ hash.hashAlg = nalg;
+ TSS_Hash_Generate(&hash, bsize, s2b,
+ name.t.size, name.t.name,
+ 0, NULL);
+ memcpy(digest->buffer, &hash.digest, hlen);
+
+ /* encrypt hash and sensitive in place */
+ TSS_AES_EncryptCFB(p->t.buffer,
+ symdef->keyBits.aes,
+ innerkey->b.buffer,
+ null_iv,
+ p->t.size,
+ p->t.buffer);
+ } else if (symdef->algorithm == TPM_ALG_NULL) {
+ TPM2B *s2b = (TPM2B *)buf;
+ int32_t size = sizeof(*s);
+ UINT16 bsize = 0, written = 0;
+
+ buf = s2b->buffer;
+
+ /* marshal the unencrypted sensitive in place */
+ TSS_TPMT_SENSITIVE_Marshal(s, &bsize, &buf, &size);
+ buf = (BYTE *)&s2b->size;
+ size = 2;
+ TSS_UINT16_Marshal(&bsize, &written, &buf, &size);
+
+ p->b.size += bsize + sizeof(s2b->size);
+ } else {
+ printf("Unknown symmetric algorithm\n");
+ return TPM_RC_SYMMETRIC;
+ }
+
+ return TPM_RC_SUCCESS;
+}
+
TPM_RC
parse_policy_file(const char *policy_file, STACK_OF(TSSOPTPOLICY) *sk,
char *auth, TPMT_HA *digest)
@@ -827,7 +963,6 @@ int main(int argc, char **argv)
if (wrap) {
EVP_PKEY *pkey;
TPMT_SENSITIVE s;
- TPM2B_NAME name;

/* may be needed to decrypt the key */
OpenSSL_add_all_ciphers();
@@ -878,19 +1013,13 @@ int main(int argc, char **argv)

/* set the NODA flag */
iin.objectPublic.publicArea.objectAttributes.val |= noda;
- rc = tpm2_ObjectPublic_GetName(&name,
- &iin.objectPublic.publicArea);
- if (rc) {
- reason = "tpm2_ObjectPublic_GetName";
- goto out_flush;
- }

- rc = tpm2_SensitiveToDuplicate(&s, &name, name_alg, NULL,
- &iin.symmetricAlg,
- &iin.encryptionKey,
- &iin.duplicate);
+ rc = tpm2_innerwrap(&s, &iin.objectPublic.publicArea,
+ &iin.symmetricAlg,
+ &iin.encryptionKey,
+ &iin.duplicate);
if (rc) {
- reason = "tpm2_SensitiveToDuplicate";
+ reason = "tpm2_innerwrap";
goto out_flush;
}

diff --git a/tpm2-common.c b/tpm2-common.c
index 9d1737b..bf950ec 100644
--- a/tpm2-common.c
+++ b/tpm2-common.c
@@ -8,16 +8,14 @@
#include <string.h>
#include <unistd.h>

-#include <arpa/inet.h>
-
#include <openssl/evp.h>
#include <openssl/rsa.h>
+#include <openssl/ec.h>

#define TSSINCLUDE(x) < TSS_INCLUDE/x >
#include TSSINCLUDE(tss.h)
#include TSSINCLUDE(tssresponsecode.h)
#include TSSINCLUDE(tssmarshal.h)
-#include TSSINCLUDE(tsscrypto.h)
#include TSSINCLUDE(tsscryptoh.h)
#include TSSINCLUDE(Unmarshal_fp.h)

@@ -739,138 +737,6 @@ TPM_RC tpm2_init_session(TSS_CONTEXT *tssContext, TPM_HANDLE handle,
return rc;
}

-/*
- * Cut down version of Part 4 Supporting Routines 7.6.3.10
- *
- * Hard coded to symmetrically encrypt with aes128 as the inner
- * wrapper and no outer wrapper but with a prototype that allows
- * drop in replacement with a tss equivalent
- */
-TPM_RC tpm2_SensitiveToDuplicate(TPMT_SENSITIVE *s,
- TPM2B_NAME *name,
- TPM_ALG_ID nalg,
- TPM2B_SEED *seed,
- TPMT_SYM_DEF_OBJECT *symdef,
- TPM2B_DATA *innerkey,
- TPM2B_PRIVATE *p)
-{
- BYTE *buf = p->t.buffer;
-
- p->t.size = 0;
- memset(p, 0, sizeof(*p));
-
- /* hard code AES CFB */
- if (symdef->algorithm == TPM_ALG_AES
- && symdef->mode.aes == TPM_ALG_CFB) {
- TPMT_HA hash;
- const int hlen = TSS_GetDigestSize(nalg);
- TPM2B *digest = (TPM2B *)buf;
- TPM2B *s2b;
- int32_t size;
- unsigned char null_iv[AES_128_BLOCK_SIZE_BYTES];
- UINT16 bsize, written = 0;
-
- /* WARNING: don't use the static null_iv trick here:
- * the AES routines alter the passed in iv */
- memset(null_iv, 0, sizeof(null_iv));
-
- /* reserve space for hash before the encrypted sensitive */
- bsize = sizeof(digest->size) + hlen;
- buf += bsize;
- p->t.size += bsize;
- s2b = (TPM2B *)buf;
-
- /* marshal the digest size */
- buf = (BYTE *)&digest->size;
- bsize = hlen;
- size = 2;
- TSS_UINT16_Marshal(&bsize, &written, &buf, &size);
-
- /* marshal the unencrypted sensitive in place */
- size = sizeof(*s);
- bsize = 0;
- buf = s2b->buffer;
- TSS_TPMT_SENSITIVE_Marshal(s, &bsize, &buf, &size);
- buf = (BYTE *)&s2b->size;
- size = 2;
- TSS_UINT16_Marshal(&bsize, &written, &buf, &size);
-
- bsize = bsize + sizeof(s2b->size);
- p->t.size += bsize;
-
- /* compute hash of unencrypted marshalled sensitive and
- * write to the digest buffer */
- hash.hashAlg = nalg;
- TSS_Hash_Generate(&hash, bsize, s2b,
- name->t.size, name->t.name,
- 0, NULL);
- memcpy(digest->buffer, &hash.digest, hlen);
-
- /* encrypt hash and sensitive in place */
- TSS_AES_EncryptCFB(p->t.buffer,
- symdef->keyBits.aes,
- innerkey->b.buffer,
- null_iv,
- p->t.size,
- p->t.buffer);
- } else if (symdef->algorithm == TPM_ALG_NULL) {
- TPM2B *s2b = (TPM2B *)buf;
- int32_t size = sizeof(*s);
- UINT16 bsize = 0, written = 0;
-
- buf = s2b->buffer;
-
- /* marshal the unencrypted sensitive in place */
- TSS_TPMT_SENSITIVE_Marshal(s, &bsize, &buf, &size);
- buf = (BYTE *)&s2b->size;
- size = 2;
- TSS_UINT16_Marshal(&bsize, &written, &buf, &size);
-
- p->b.size += bsize + sizeof(s2b->size);
- } else {
- printf("Unknown symmetric algorithm\n");
- return TPM_RC_SYMMETRIC;
- }
-
- return TPM_RC_SUCCESS;
-}
-
-TPM_RC tpm2_ObjectPublic_GetName(TPM2B_NAME *name,
- TPMT_PUBLIC *tpmtPublic)
-{
- TPM_RC rc = 0;
- uint16_t written = 0;
- TPMT_HA digest;
- uint32_t sizeInBytes;
- uint8_t buffer[MAX_RESPONSE_SIZE];
-
- /* marshal the TPMT_PUBLIC */
- if (rc == 0) {
- INT32 size = MAX_RESPONSE_SIZE;
- uint8_t *buffer1 = buffer;
- rc = TSS_TPMT_PUBLIC_Marshal(tpmtPublic, &written, &buffer1, &size);
- }
- /* hash the public area */
- if (rc == 0) {
- sizeInBytes = TSS_GetDigestSize(tpmtPublic->nameAlg);
- digest.hashAlg = tpmtPublic->nameAlg; /* Name digest algorithm */
- /* generate the TPMT_HA */
- rc = TSS_Hash_Generate(&digest,
- written, buffer,
- 0, NULL);
- }
- if (rc == 0) {
- /* copy the digest */
- memcpy(name->t.name + sizeof(TPMI_ALG_HASH), (uint8_t *)&digest.digest, sizeInBytes);
- /* copy the hash algorithm */
- TPMI_ALG_HASH nameAlgNbo = htons(tpmtPublic->nameAlg);
- memcpy(name->t.name, (uint8_t *)&nameAlgNbo, sizeof(TPMI_ALG_HASH));
- /* set the size */
- name->t.size = sizeInBytes + sizeof(TPMI_ALG_HASH);
- }
- return rc;
-}
-
TPMI_ECC_CURVE tpm2_curve_name_to_TPMI(const char *name)
{
int i;
diff --git a/tpm2-common.h b/tpm2-common.h
index 1cf3b23..6111243 100644
--- a/tpm2-common.h
+++ b/tpm2-common.h
@@ -23,15 +23,6 @@ TPM_RC tpm2_init_session(TSS_CONTEXT *tssContext, TPM_HANDLE handle,
TPM_ALG_ID name_alg);
TPM_RC tpm2_get_bound_handle(TSS_CONTEXT *tssContext, TPM_HANDLE *handle,
TPM_HANDLE bind, const char *auth);
-TPM_RC tpm2_SensitiveToDuplicate(TPMT_SENSITIVE *s,
- TPM2B_NAME *name,
- TPM_ALG_ID nalg,
- TPM2B_SEED *seed,
- TPMT_SYM_DEF_OBJECT *symdef,
- TPM2B_DATA *innerkey,
- TPM2B_PRIVATE *p);
-TPM_RC tpm2_ObjectPublic_GetName(TPM2B_NAME *name,
- TPMT_PUBLIC *tpmtPublic);
TPMI_ECC_CURVE tpm2_curve_name_to_TPMI(const char *name);
int tpm2_curve_name_to_nid(TPMI_ECC_CURVE curve);
TPMI_ECC_CURVE tpm2_nid_to_curve_name(int nid);
--
2.16.4

[PATCH 1/4] tpm2-common: add point conversion routines

James Bottomley
 

Point conversion from openssl to TPM format is now used in a couple of
places in the current code, so move it into the common routines.

Signed-off-by: James Bottomley <James.Bottomley@...>
---
e_tpm2-ecc.c | 22 +++-------------------
tpm2-common.c | 26 ++++++++++++++++++++++++++
tpm2-common.h | 2 ++
3 files changed, 31 insertions(+), 19 deletions(-)

diff --git a/e_tpm2-ecc.c b/e_tpm2-ecc.c
index e0b0c47..21a636c 100644
--- a/e_tpm2-ecc.c
+++ b/e_tpm2-ecc.c
@@ -236,26 +236,11 @@ static int tpm2_ecc_compute_key(unsigned char **psec, size_t *pseclen,
TPM_SE sessionType;
char *auth;
size_t len;
- const EC_GROUP *group;
- BN_CTX *ctx;
- unsigned char point[MAX_ECC_KEY_BYTES*2 + 1];
int num_commands;
struct policy_command *commands;
TPM_ALG_ID nameAlg;
int ret;

- group = EC_KEY_get0_group(eck);
- ctx = BN_CTX_new();
- if (!ctx)
- return 0;
- BN_CTX_start(ctx);
- len = EC_POINT_point2oct(group, pt, POINT_CONVERSION_UNCOMPRESSED,
- point, sizeof(point), ctx);
- BN_CTX_free(ctx);
-
- len--;
- len >>= 1;
-
in.keyHandle = tpm2_load_key_from_ecc(eck, &tssContext, &auth,
&sessionType, &num_commands,
&commands, &nameAlg);
@@ -263,10 +248,9 @@ static int tpm2_ecc_compute_key(unsigned char **psec, size_t *pseclen,
fprintf(stderr, "Failed to get Key Handle in TPM EC key routines\n");
return 0;
}
- memcpy(in.inPoint.point.x.t.buffer, point + 1, len);
- in.inPoint.point.x.t.size = len;
- memcpy(in.inPoint.point.y.t.buffer, point + 1 + len, len);
- in.inPoint.point.y.t.size = len;
+ len = tpm2_get_public_point(&in.inPoint, EC_KEY_get0_group(eck), pt);
+ if (!len)
+ return 0;

ret = 0;
rc = tpm2_get_session_handle(tssContext, &authHandle, 0, sessionType,
diff --git a/tpm2-common.c b/tpm2-common.c
index 1b1eead..9d1737b 100644
--- a/tpm2-common.c
+++ b/tpm2-common.c
@@ -1022,3 +1022,29 @@ TPM_RC tpm2_create(TSS_CONTEXT **tsscp, const char *dir)

return TPM_RC_SUCCESS;
}
+
+int tpm2_get_public_point(TPM2B_ECC_POINT *tpmpt, const EC_GROUP *group,
+ const EC_POINT *pt)
+{
+ BN_CTX *ctx;
+ size_t len;
+ unsigned char point[MAX_ECC_KEY_BYTES*2 + 1];
+
+ ctx = BN_CTX_new();
+ if (!ctx)
+ return 0;
+ BN_CTX_start(ctx);
+ len = EC_POINT_point2oct(group, pt, POINT_CONVERSION_UNCOMPRESSED,
+ point, sizeof(point), ctx);
+ BN_CTX_free(ctx);
+
+ len--;
+ len >>= 1;
+
+ memcpy(tpmpt->point.x.t.buffer, point + 1, len);
+ tpmpt->point.x.t.size = len;
+ memcpy(tpmpt->point.y.t.buffer, point + 1 + len, len);
+ tpmpt->point.y.t.size = len;
+
+ return len;
+}
diff --git a/tpm2-common.h b/tpm2-common.h
index 14aae40..1cf3b23 100644
--- a/tpm2-common.h
+++ b/tpm2-common.h
@@ -42,4 +42,6 @@ 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);
+int tpm2_get_public_point(TPM2B_ECC_POINT *tpmpt, const EC_GROUP *group,
+ const EC_POINT *pt);
#endif
--
2.16.4

[PATCH 0/4] Add importable keys

James Bottomley
 

This patch adds the capability for importable keys. An importable key
is a duplicate structure with an outer wrapper that is encrypted with a
symmetric key that is itself encrypted to the public key of the parent.
The creation of this encrypted secret is somewhat complex, so the only
current algorithm implemented is ECDH encryption (meaning the parent
key must be an elliptic curve one). We can add RSA later, but it's
more complicated because it must be done with special OAEP padding
which openssl can't produce.

Importable keys require an additional optional parameter in the ASN.1
for the encrypted secret. This makes the full ASN.1 structure now

TPMKey ::= SEQUENCE {
type OBJECT IDENTIFIER
emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL
policy [1] EXPLICIT SEQUENCE OF TPMPolicy
OPTIONAL
secret [2] EXPLICIT OCTET STRING OPTIONAL
parent INTEGER
pubkey OCTET STRING
privkey OCTET STRING
}

The utility of the importable keys is that they can be created without
access to the actual physical TPM; all you need is a representation of
the parent public key, so this enables you to create the TPM
representation in a secure environment away from the use machine.

The way importable keys are implemented in the engine is that as soon
as the key is loaded, if we see it's importable, we run the import
command to convert it internally to loadable form and then run the
engine as normal (so if you have key create with multiple operations,
we only run the import once).

There are also a couple of precursor patches shifting common code
around.

James


---

James Bottomley (4):
tpm2-common: add point conversion routines
tpm2-common: remove interfaces only used by create-tpm2-key
Add importable keys
Add tests for importable keys

create_tpm2_key.1.in | 17 ++
create_tpm2_key.c | 441 ++++++++++++++++++++++++++++++++++++++++++----
e_tpm2-ecc.c | 22 +--
e_tpm2.c | 108 ++++++++++--
tests/Makefile.am | 1 +
tests/check_importable.sh | 20 +++
tpm2-asn.h | 3 +
tpm2-common.c | 162 +++--------------
tpm2-common.h | 11 +-
9 files changed, 571 insertions(+), 214 deletions(-)
create mode 100755 tests/check_importable.sh

--
2.16.4

[PATCH] Enable TPM parameter encryption for RSA encrypt/decrypt

Fredrik Ternerot <fredrik.ternerot@...>
 

Enable TPM parameter encryption for RSA private encrypt/decrypt to
protect sensitive data sent to and received from the TPM.

Signed-off-by: Fredrik Ternerot <fredrikt@...>
---
e_tpm2-rsa.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/e_tpm2-rsa.c b/e_tpm2-rsa.c
index 50c2cac..35b865b 100644
--- a/e_tpm2-rsa.c
+++ b/e_tpm2-rsa.c
@@ -203,7 +203,7 @@ static int tpm2_rsa_priv_dec(int flen,
(COMMAND_PARAMETERS *)&in,
NULL,
TPM_CC_RSA_Decrypt,
- authHandle, auth, 0,
+ authHandle, auth, TPMA_SESSION_ENCRYPT,
TPM_RH_NULL, NULL, 0);
if (rc) {
tpm2_error(rc, "TPM2_RSA_Decrypt");
@@ -282,7 +282,7 @@ static int tpm2_rsa_priv_enc(int flen,
(COMMAND_PARAMETERS *)&in,
NULL,
TPM_CC_RSA_Decrypt,
- authHandle, auth, 0,
+ authHandle, auth, TPMA_SESSION_DECRYPT,
TPM_RH_NULL, NULL, 0);

if (rc) {
--
2.11.0

[PATCH] create_tpm2_key: fix use after free

James Bottomley
 

we use pointers to the public and private areas of the TPM
import/create commands to fill out the OpenSSL TPM key, but these
areas go out of scope and are thus freed before we actually use them.
So fix this by declaring the in/out parameters for these commands in
global instead of local scope.

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

diff --git a/create_tpm2_key.c b/create_tpm2_key.c
index 1f8a479..3c71d50 100644
--- a/create_tpm2_key.c
+++ b/create_tpm2_key.c
@@ -642,6 +642,10 @@ int main(int argc, char **argv)
BYTE pubkey[sizeof(TPM2B_PUBLIC)],privkey[sizeof(TPM2B_PRIVATE)], *buffer;
uint16_t pubkey_len, privkey_len;
int32_t size, key_size = 0;
+ Import_In iin;
+ Import_Out iout;
+ Create_In cin;
+ Create_Out cout;
TPM2B_PUBLIC *pub;
TPM2B_PRIVATE *priv;
char *key = NULL, *parent_auth = NULL;
@@ -821,8 +825,6 @@ int main(int argc, char **argv)
}

if (wrap) {
- Import_In iin;
- Import_Out iout;
EVP_PKEY *pkey;
TPMT_SENSITIVE s;
TPM2B_NAME name;
@@ -917,9 +919,6 @@ int main(int argc, char **argv)
priv = &iout.outPrivate;
} else {
/* create a TPM resident key */
- Create_In cin;
- Create_Out cout;
-
if (rsa) {
tpm2_public_template_rsa(&cin.inPublic.publicArea);
cin.inPublic.publicArea.parameters.rsaDetail.keyBits = key_size;
--
2.16.4

Re: [PATCH v2 1/1] Disable elliptic curve when not supported by openssl

Fredrik Ternerot <fredrik.ternerot@...>
 

On Tue, Nov 06, 2018 at 12:32:44 -0800, James Bottomley wrote:
On Mon, 2018-11-05 at 09:57 +0100, Fredrik Ternerot wrote:
Disable EC support based on OPENSSL_NO_EC to allow compilation for
systems where EC is not supported by openssl.
As a general comment, I think you could lose a lot of the ifdefs. Just
because you don't support ec doesn't mean everything ec related has to
go (for instance, just keep the curve set to TPM_ECC_NONE in
create_tpm_key ... all the condition checks then do the right thing,
you don't need to eliminate them).
Right, this could be improved.


The other big problem is this:

[...]
@@ -253,8 +258,12 @@ TPM_RC tpm2_load_srk(TSS_CONTEXT *tssContext,
TPM_HANDLE *h, const char *auth,TP
/* no PCR state */
in.creationPCR.count = 0;

- /* public parameters for an RSA2048 key */
+ /* public parameters for an ECC/RSA key */
+#ifdef OPENSSL_NO_EC
+ in.inPublic.publicArea.type = TPM_ALG_RSA;
+#else
in.inPublic.publicArea.type = TPM_ALG_ECC;
+#endif
in.inPublic.publicArea.nameAlg = TPM_ALG_SHA256;
in.inPublic.publicArea.objectAttributes.val =
TPMA_OBJECT_NODA |
@@ -262,6 +271,16 @@ TPM_RC tpm2_load_srk(TSS_CONTEXT *tssContext,
TPM_HANDLE *h, const char *auth,TP
TPMA_OBJECT_USERWITHAUTH |
TPMA_OBJECT_DECRYPT |
TPMA_OBJECT_RESTRICTED;
+#ifdef OPENSSL_NO_EC
+ in.inPublic.publicArea.parameters.rsaDetail.symmetric.algori
thm = TPM_ALG_AES;
+ in.inPublic.publicArea.parameters.rsaDetail.symmetric.keyBit
s.aes = 128;
+ in.inPublic.publicArea.parameters.rsaDetail.symmetric.mode.a
es = TPM_ALG_CFB;
+ in.inPublic.publicArea.parameters.rsaDetail.scheme.scheme =
TPM_ALG_NULL;
+ in.inPublic.publicArea.parameters.rsaDetail.keyBits = 2048;
+ in.inPublic.publicArea.parameters.rsaDetail.exponent = 0;
+
+ in.inPublic.publicArea.unique.rsa.t.size = 0;
+#else
Primary RSA templates are really nasty: on my current laptop it takes
50 seconds simply to generate one, which is a huge amount of time to
wait for *every* key operation, so I really think if you want no-EC to
work you can't support any key type with MSO 40 parent (meaning we
create the primary on the fly).
I see. I always use MSO 81 parent so haven't really thought about this.


The other reason this causes problems is that parent templates are used
for the key derivation function of the decryption key of the private
blob: use the wrong template the key won't load, so a MSO 40 parent
created with RSA only can't be loaded into a build that supports EC
(and vice versa) which will cause all sorts of support issues.
I see. Seems a bit problematic to do this change then. Maybe I need to
handle it as a local patch for the parts relevant to me.

--
Fredrik Ternerot

[PATCH 2/2] check_enhanced_auth.sh: add loop over name algorithm type

James Bottomley
 

This test checks for two things, firstly that we get the policy right
with different name algorithms and secondly that we actually get the
name algorithm correctly specified. This all works because with
policyPCR the hash of the expected policy registers has to be done
with the name algorithm hash, so we'll get a mismatch here if either
the name algorithm is wrong (or the policy parsing fails).

Signed-off-by: James Bottomley <James.Bottomley@...>
---
tests/check_enhanced_auth.sh | 154 +++++++++++++++++---------------
tests/policies/policy_authvalue_pcr.txt | 2 -
tests/policies/policy_pcr_authvalue.txt | 2 -
3 files changed, 83 insertions(+), 75 deletions(-)
delete mode 100644 tests/policies/policy_authvalue_pcr.txt
delete mode 100644 tests/policies/policy_pcr_authvalue.txt

diff --git a/tests/check_enhanced_auth.sh b/tests/check_enhanced_auth.sh
index 7006387..8c25ed3 100755
--- a/tests/check_enhanced_auth.sh
+++ b/tests/check_enhanced_auth.sh
@@ -2,10 +2,12 @@

bindir=${srcdir}/..

-tss_pcrreset_cmd=/usr/bin/tsspcrreset
-tss_pcrextend_cmd=/usr/bin/tsspcrextend
+tss_pcrreset_cmd=tsspcrreset
+tss_pcrextend_cmd=tsspcrextend

-if [ ! -e ${tss_pcrreset_cmd} ] || [ ! -e ${tss_pcrextend_cmd} ]; then
+if which ${tss_pcrreset_cmd} && which ${tss_pcrextend_cmd}; then
+ :
+else
echo "TSS utils not found, please specify the correct path."
exit 1
fi
@@ -20,75 +22,85 @@ a=0; while [ $a -lt 5 ]; do
echo "This is a message" | openssl rsautl -sign -engine tpm2 -engine tpm2 -keyform engine -inkey key.tpm -out tmp.msg && exit 1
done

-##
-# test is
-# 1. create TPM internal private key with PolicyAuthValue authorization
-# 2. get the corresponding public key from the engine
-# 3. encode a message using the TPM key
-# 4. verify the message through the public key
-${bindir}/create_tpm2_key -a -k passw0rd key2.tpm -c policies/policy_authvalue.txt && \
-openssl rsa -engine tpm2 -inform engine -passin pass:passw0rd -in key2.tpm -pubout -out key2.pub && \
-echo "This is a message" | openssl rsautl -sign -engine tpm2 -engine tpm2 -keyform engine -inkey key2.tpm -passin pass:passw0rd -out tmp.msg && \
-openssl rsautl -verify -in tmp.msg -inkey key2.pub -pubin || exit 1
+for h in "sha1" "" "sha384"; do
+ echo "Testing Name Parameter: ${h}"
+ if [ -n "${h}" ]; then
+ n="-n ${h}"
+ else
+ n=""
+ fi
+ ##
+ # test is
+ # 1. create TPM internal private key with PolicyAuthValue authorization
+ # 2. get the corresponding public key from the engine
+ # 3. encode a message using the TPM key
+ # 4. verify the message through the public key
+ ${bindir}/create_tpm2_key ${n} -a -k passw0rd key2.tpm -c policies/policy_authvalue.txt && \
+ openssl rsa -engine tpm2 -inform engine -passin pass:passw0rd -in key2.tpm -pubout -out key2.pub && \
+ echo "This is a message" | openssl rsautl -sign -engine tpm2 -engine tpm2 -keyform engine -inkey key2.tpm -passin pass:passw0rd -out tmp.msg && \
+ openssl rsautl -verify -in tmp.msg -inkey key2.pub -pubin || exit 1

-##
-# test is
-# 1. reset PCR 16
-# 2. extend PCR 16 with 'aaa'
-# 3. create TPM internal private key with PolicyPCR authorization (PCR 16 extended with 'aaa')
-# 4. get the corresponding public key from the engine
-# 5. encode a message using the TPM key
-# 6. verify the message through the public key
-${tss_pcrreset_cmd} -ha 16
-${tss_pcrextend_cmd} -ha 16 -ic aaa
-${bindir}/create_tpm2_key key2.tpm -c policies/policy_pcr.txt && \
-openssl rsa -engine tpm2 -inform engine -in key2.tpm -pubout -out key2.pub && \
-echo "This is a message" | openssl rsautl -sign -engine tpm2 -engine tpm2 -keyform engine -inkey key2.tpm -out tmp.msg && \
-openssl rsautl -verify -in tmp.msg -inkey key2.pub -pubin || exit 1
+ ##
+ # test is
+ # 1. reset PCR 16
+ # 2. extend PCR 16 with 'aaa'
+ # 3. create TPM internal private key with PolicyPCR authorization (PCR 16 extended with 'aaa')
+ # 4. get the corresponding public key from the engine
+ # 5. encode a message using the TPM key
+ # 6. verify the message through the public key
+ ${tss_pcrreset_cmd} -ha 16
+ ${tss_pcrextend_cmd} -ha 16 -ic aaa
+ ${bindir}/create_tpm2_key ${n} key2.tpm -c policies/policy_pcr${h}.txt && \
+ openssl rsa -engine tpm2 -inform engine -in key2.tpm -pubout -out key2.pub && \
+ echo "This is a message" | openssl rsautl -sign -engine tpm2 -engine tpm2 -keyform engine -inkey key2.tpm -out tmp.msg && \
+ openssl rsautl -verify -in tmp.msg -inkey key2.pub -pubin || exit 1

-##
-# test is
-# 1. reset PCR 16
-# 2. create TPM internal private key with PolicyPCR authorization (should fail because PCR 16 does not have the correct value)
-# 3. get the corresponding public key from the engine
-# 4. encode a message using the TPM key
-# 5. verify the message through the public key
-${tss_pcrreset_cmd} -ha 16
-${bindir}/create_tpm2_key key2.tpm -c policies/policy_pcr.txt
-openssl rsa -engine tpm2 -inform engine -in key2.tpm -pubout -out key2.pub && \
-echo "This is a message" | openssl rsautl -sign -engine tpm2 -engine tpm2 -keyform engine -inkey key2.tpm -out tmp.msg && \
-openssl rsautl -verify -in tmp.msg -inkey key2.pub -pubin
-if [ $? -ne 1 ]; then
- echo "TPM key should not be accessible"
- exit 1
-fi
+ ##
+ # test is
+ # 1. reset PCR 16
+ # 2. create TPM internal private key with PolicyPCR authorization (should fail because PCR 16 does not have the correct value)
+ # 3. get the corresponding public key from the engine
+ # 4. encode a message using the TPM key
+ # 5. verify the message through the public key
+ ${tss_pcrreset_cmd} -ha 16
+ ${bindir}/create_tpm2_key ${n} key2.tpm -c policies/policy_pcr${h}.txt
+ openssl rsa -engine tpm2 -inform engine -in key2.tpm -pubout -out key2.pub && \
+ echo "This is a message" | openssl rsautl -sign -engine tpm2 -engine tpm2 -keyform engine -inkey key2.tpm -out tmp.msg && \
+ openssl rsautl -verify -in tmp.msg -inkey key2.pub -pubin
+ if [ $? -ne 1 ]; then
+ echo "TPM key should not be accessible"
+ exit 1
+ fi

-##
-# test is
-# 1. reset PCR 16
-# 2. extend PCR 16 with 'aaa'
-# 3. create TPM internal private key with PolicyAuthValue + PolicyPCR authorization
-# 4. get the corresponding public key from the engine
-# 5. encode a message using the TPM key
-# 6. verify the message through the public key
-${tss_pcrreset_cmd} -ha 16
-${tss_pcrextend_cmd} -ha 16 -ic aaa
-${bindir}/create_tpm2_key -a -k passw0rd key2.tpm -c policies/policy_authvalue_pcr.txt && \
-openssl rsa -engine tpm2 -inform engine -passin pass:passw0rd -in key2.tpm -pubout -out key2.pub && \
-echo "This is a message" | openssl rsautl -sign -engine tpm2 -engine tpm2 -keyform engine -inkey key2.tpm -passin pass:passw0rd -out tmp.msg && \
-openssl rsautl -verify -in tmp.msg -inkey key2.pub -pubin || exit 1
+ ##
+ # test is
+ # 1. reset PCR 16
+ # 2. extend PCR 16 with 'aaa'
+ # 3. create TPM internal private key with PolicyAuthValue + PolicyPCR authorization
+ # 4. get the corresponding public key from the engine
+ # 5. encode a message using the TPM key
+ # 6. verify the message through the public key
+ cat policies/policy_authvalue.txt policies/policy_pcr${h}.txt > policy_authvalue_pcr.txt
+ ${tss_pcrreset_cmd} -ha 16
+ ${tss_pcrextend_cmd} -ha 16 -ic aaa
+ ${bindir}/create_tpm2_key ${n} -a -k passw0rd key2.tpm -c policy_authvalue_pcr.txt && \
+ openssl rsa -engine tpm2 -inform engine -passin pass:passw0rd -in key2.tpm -pubout -out key2.pub && \
+ echo "This is a message" | openssl rsautl -sign -engine tpm2 -engine tpm2 -keyform engine -inkey key2.tpm -passin pass:passw0rd -out tmp.msg && \
+ openssl rsautl -verify -in tmp.msg -inkey key2.pub -pubin || exit 1

-##
-# test is
-# 1. reset PCR 16
-# 2. extend PCR 16 with 'aaa'
-# 3. create TPM internal private key with PolicyPCR + PolicyAuthValue authorization
-# 4. get the corresponding public key from the engine
-# 5. encode a message using the TPM key
-# 6. verify the message through the public key
-${tss_pcrreset_cmd} -ha 16
-${tss_pcrextend_cmd} -ha 16 -ic aaa
-${bindir}/create_tpm2_key -a -k passw0rd key2.tpm -c policies/policy_pcr_authvalue.txt && \
-openssl rsa -engine tpm2 -inform engine -passin pass:passw0rd -in key2.tpm -pubout -out key2.pub && \
-echo "This is a message" | openssl rsautl -sign -engine tpm2 -engine tpm2 -keyform engine -inkey key2.tpm -passin pass:passw0rd -out tmp.msg && \
-openssl rsautl -verify -in tmp.msg -inkey key2.pub -pubin
+ ##
+ # test is
+ # 1. reset PCR 16
+ # 2. extend PCR 16 with 'aaa'
+ # 3. create TPM internal private key with PolicyPCR + PolicyAuthValue authorization
+ # 4. get the corresponding public key from the engine
+ # 5. encode a message using the TPM key
+ # 6. verify the message through the public key
+ cat policies/policy_pcr${h}.txt policies/policy_authvalue.txt > policy_pcr_authvalue.txt
+ ${tss_pcrreset_cmd} -ha 16
+ ${tss_pcrextend_cmd} -ha 16 -ic aaa
+ ${bindir}/create_tpm2_key ${n} -a -k passw0rd key2.tpm -c policy_pcr_authvalue.txt && \
+ openssl rsa -engine tpm2 -inform engine -passin pass:passw0rd -in key2.tpm -pubout -out key2.pub && \
+ echo "This is a message" | openssl rsautl -sign -engine tpm2 -engine tpm2 -keyform engine -inkey key2.tpm -passin pass:passw0rd -out tmp.msg && \
+ openssl rsautl -verify -in tmp.msg -inkey key2.pub -pubin || exit 1
+done
diff --git a/tests/policies/policy_authvalue_pcr.txt b/tests/policies/policy_authvalue_pcr.txt
deleted file mode 100644
index c5760d7..0000000
--- a/tests/policies/policy_authvalue_pcr.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-0000016b
-0000017f00000001000b030000012c28901f71751debfba3f3b5bf3be9c54b8b2f8c1411f2c117a0e838ee4e6c13
diff --git a/tests/policies/policy_pcr_authvalue.txt b/tests/policies/policy_pcr_authvalue.txt
deleted file mode 100644
index cb29f1e..0000000
--- a/tests/policies/policy_pcr_authvalue.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-0000017f00000001000b030000012c28901f71751debfba3f3b5bf3be9c54b8b2f8c1411f2c117a0e838ee4e6c13
-0000016b
--
2.16.4

[PATCH 1/2] create_tpm2_key: policy should use the name algorithm

James Bottomley
 

For all keys, policy must use the same algorithm as the name
algorithm, so fix that and add checks for different algorithms.

This also fixes a bug in the PCR policy where we were accidentally
getting 32 from the size of the PCR unmarshal selection rather than
setting the correct PCR hash algorithm size

Signed-off-by: James Bottomley <James.Bottomley@...>
---
create_tpm2_key.c | 6 +++---
e_tpm2-ecc.c | 20 +++++++++++++-------
e_tpm2-rsa.c | 20 +++++++++++++-------
e_tpm2.c | 4 +++-
e_tpm2.h | 1 +
tpm2-common.c | 13 ++++++++-----
tpm2-common.h | 6 ++++--
7 files changed, 45 insertions(+), 25 deletions(-)

diff --git a/create_tpm2_key.c b/create_tpm2_key.c
index 1f8a479..918f288 100644
--- a/create_tpm2_key.c
+++ b/create_tpm2_key.c
@@ -787,7 +787,7 @@ int main(int argc, char **argv)
phandle = parent;
}

- digest.hashAlg = TPM_ALG_SHA256;
+ digest.hashAlg = name_alg;
sizeInBytes = TSS_GetDigestSize(digest.hashAlg);
memset((uint8_t *)&digest.digest, 0, sizeInBytes);

@@ -894,7 +894,7 @@ int main(int argc, char **argv)

/* use salted parameter encryption to hide the key */
rc = tpm2_get_session_handle(tssContext, &authHandle, phandle,
- TPM_SE_HMAC);
+ TPM_SE_HMAC, name_alg);
if (rc) {
reason = "get session handle";
goto out_flush;
@@ -961,7 +961,7 @@ int main(int argc, char **argv)

/* use salted parameter encryption to hide the key */
rc = tpm2_get_session_handle(tssContext, &authHandle, phandle,
- TPM_SE_HMAC);
+ TPM_SE_HMAC, name_alg);
if (rc) {
reason = "get session handle";
goto out_flush;
diff --git a/e_tpm2-ecc.c b/e_tpm2-ecc.c
index e35b730..824a44e 100644
--- a/e_tpm2-ecc.c
+++ b/e_tpm2-ecc.c
@@ -66,7 +66,8 @@ static int ec_app_data = TPM2_ENGINE_EX_DATA_UNINIT;
static TPM_HANDLE tpm2_load_key_from_ecc(const EC_KEY *eck,
TSS_CONTEXT **tssContext, char **auth,
TPM_SE *sessionType, int *num_commands,
- struct policy_command **commands)
+ struct policy_command **commands,
+ TPM_ALG_ID *nameAlg)
{
#if OPENSSL_VERSION_NUMBER < 0x10100000
/* const mess up in openssl 1.0.2 */
@@ -84,6 +85,7 @@ static TPM_HANDLE tpm2_load_key_from_ecc(const EC_KEY *eck,
TPM_SE_POLICY : TPM_SE_HMAC;
*commands = app_data->commands;
*num_commands = app_data->num_commands;
+ *nameAlg = app_data->name_alg;

return tpm2_load_key(tssContext, app_data);
}
@@ -136,6 +138,7 @@ static ECDSA_SIG *tpm2_ecdsa_sign(const unsigned char *dgst, int dgst_len,
BIGNUM *r, *s;
int num_commands;
struct policy_command *commands;
+ TPM_ALG_ID nameAlg;

/* The TPM insists on knowing the digest type, so
* calculate that from the size */
@@ -161,7 +164,7 @@ static ECDSA_SIG *tpm2_ecdsa_sign(const unsigned char *dgst, int dgst_len,

in.keyHandle = tpm2_load_key_from_ecc(eck, &tssContext, &auth,
&sessionType, &num_commands,
- &commands);
+ &commands, &nameAlg);
if (in.keyHandle == 0) {
fprintf(stderr, "Failed to get Key Handle in TPM EC key routines\n");
return NULL;
@@ -175,13 +178,14 @@ static ECDSA_SIG *tpm2_ecdsa_sign(const unsigned char *dgst, int dgst_len,
in.validation.digest.t.size = 0;

sig = NULL;
- rc = tpm2_get_session_handle(tssContext, &authHandle, 0, sessionType);
+ rc = tpm2_get_session_handle(tssContext, &authHandle, 0, sessionType,
+ nameAlg);
if (rc)
goto out;

if (sessionType == TPM_SE_POLICY) {
rc = tpm2_init_session(tssContext, authHandle,
- num_commands, commands);
+ num_commands, commands, nameAlg);
if (rc)
goto out;
}
@@ -237,6 +241,7 @@ static int tpm2_ecc_compute_key(unsigned char **psec, size_t *pseclen,
unsigned char point[MAX_ECC_KEY_BYTES*2 + 1];
int num_commands;
struct policy_command *commands;
+ TPM_ALG_ID nameAlg;
int ret;

group = EC_KEY_get0_group(eck);
@@ -253,7 +258,7 @@ static int tpm2_ecc_compute_key(unsigned char **psec, size_t *pseclen,

in.keyHandle = tpm2_load_key_from_ecc(eck, &tssContext, &auth,
&sessionType, &num_commands,
- &commands);
+ &commands, &nameAlg);
if (in.keyHandle == 0) {
fprintf(stderr, "Failed to get Key Handle in TPM EC key routines\n");
return 0;
@@ -264,13 +269,14 @@ static int tpm2_ecc_compute_key(unsigned char **psec, size_t *pseclen,
in.inPoint.point.y.t.size = len;

ret = 0;
- rc = tpm2_get_session_handle(tssContext, &authHandle, 0, sessionType);
+ rc = tpm2_get_session_handle(tssContext, &authHandle, 0, sessionType,
+ nameAlg);
if (rc)
goto out;

if (sessionType == TPM_SE_POLICY) {
rc = tpm2_init_session(tssContext, authHandle,
- num_commands, commands);
+ num_commands, commands, nameAlg);
if (rc)
goto out;
}
diff --git a/e_tpm2-rsa.c b/e_tpm2-rsa.c
index 50c2cac..9fda63c 100644
--- a/e_tpm2-rsa.c
+++ b/e_tpm2-rsa.c
@@ -104,7 +104,8 @@ static int tpm2_rsa_pub_enc(int flen,
static TPM_HANDLE tpm2_load_key_from_rsa(RSA *rsa, TSS_CONTEXT **tssContext,
char **auth, TPM_SE *sessionType,
int *num_commands,
- struct policy_command **commands)
+ struct policy_command **commands,
+ TPM_ALG_ID *nameAlg)
{
struct app_data *app_data = RSA_get_ex_data(rsa, ex_app_data);

@@ -116,6 +117,7 @@ static TPM_HANDLE tpm2_load_key_from_rsa(RSA *rsa, TSS_CONTEXT **tssContext,
TPM_SE_POLICY : TPM_SE_HMAC;
*commands = app_data->commands;
*num_commands = app_data->num_commands;
+ *nameAlg = app_data->name_alg;

return tpm2_load_key(tssContext, app_data);
}
@@ -165,10 +167,11 @@ static int tpm2_rsa_priv_dec(int flen,
TPM_SE sessionType;
int num_commands;
struct policy_command *commands;
+ TPM_ALG_ID nameAlg;

in.keyHandle = tpm2_load_key_from_rsa(rsa, &tssContext, &auth,
&sessionType, &num_commands,
- &commands);
+ &commands, &nameAlg);

if (in.keyHandle == 0) {
fprintf(stderr, "Failed to get Key Handle in TPM RSA key routines\n");
@@ -187,13 +190,14 @@ static int tpm2_rsa_priv_dec(int flen,
memcpy(in.cipherText.t.buffer, from, flen);
in.label.t.size = 0;

- rc = tpm2_get_session_handle(tssContext, &authHandle, 0, sessionType);
+ rc = tpm2_get_session_handle(tssContext, &authHandle, 0, sessionType,
+ nameAlg);
if (rc)
goto out;

if (sessionType == TPM_SE_POLICY) {
rc = tpm2_init_session(tssContext, authHandle,
- num_commands, commands);
+ num_commands, commands, nameAlg);
if (rc)
goto out;
}
@@ -237,6 +241,7 @@ static int tpm2_rsa_priv_enc(int flen,
TPM_SE sessionType;
int num_commands;
struct policy_command *commands;
+ TPM_ALG_ID nameAlg;

if (padding != RSA_PKCS1_PADDING) {
fprintf(stderr, "Non PKCS1 padding asked for\n");
@@ -245,7 +250,7 @@ static int tpm2_rsa_priv_enc(int flen,

in.keyHandle = tpm2_load_key_from_rsa(rsa, &tssContext, &auth,
&sessionType, &num_commands,
- &commands);
+ &commands, &nameAlg);

if (in.keyHandle == 0) {
fprintf(stderr, "Failed to get Key Handle in TPM RSA routines\n");
@@ -254,13 +259,14 @@ static int tpm2_rsa_priv_enc(int flen,
}

rv = -1;
- rc = tpm2_get_session_handle(tssContext, &authHandle, 0, sessionType);
+ rc = tpm2_get_session_handle(tssContext, &authHandle, 0, sessionType,
+ nameAlg);
if (rc)
goto out;

if (sessionType == TPM_SE_POLICY) {
rc = tpm2_init_session(tssContext, authHandle,
- num_commands, commands);
+ num_commands, commands, nameAlg);
if (rc)
goto out;
}
diff --git a/e_tpm2.c b/e_tpm2.c
index 833dc7c..39026e9 100644
--- a/e_tpm2.c
+++ b/e_tpm2.c
@@ -218,6 +218,7 @@ static int tpm2_engine_load_nvkey(ENGINE *e, EVP_PKEY **ppkey,
rc = tpm2_readpublic(tssContext, key, &p);
if (rc)
goto err_del;
+ app_data->name_alg = p.nameAlg;
pkey = tpm2_to_openssl_public(&p);
if (!pkey) {
fprintf(stderr, "Failed to allocate a new EVP_KEY\n");
@@ -448,6 +449,7 @@ static int tpm2_engine_load_key_core(ENGINE *e, EVP_PKEY **ppkey,
buffer = app_data->pub;
size = app_data->pub_len;
TPM2B_PUBLIC_Unmarshal(&p, &buffer, &size, FALSE);
+ app_data->name_alg = p.publicArea.nameAlg;
/* create the new objects to return */
pkey = tpm2_to_openssl_public(&p.publicArea);
if (!pkey) {
@@ -578,7 +580,7 @@ TPM_HANDLE tpm2_load_key(TSS_CONTEXT **tsscp, struct app_data *app_data)
goto out;
}
rc = tpm2_get_session_handle(tssContext, &session, in.parentHandle,
- TPM_SE_HMAC);
+ TPM_SE_HMAC, app_data->name_alg);
if (rc)
goto out_flush_srk;
rc = TSS_Execute(tssContext,
diff --git a/e_tpm2.h b/e_tpm2.h
index 253231f..5e843d2 100644
--- a/e_tpm2.h
+++ b/e_tpm2.h
@@ -21,6 +21,7 @@ struct app_data {
const char *dir;
int req_policy_session;
int num_commands;
+ unsigned int name_alg;
struct policy_command *commands;
};

diff --git a/tpm2-common.c b/tpm2-common.c
index 6aef4e3..1b1eead 100644
--- a/tpm2-common.c
+++ b/tpm2-common.c
@@ -570,7 +570,8 @@ TPM_RC tpm2_get_bound_handle(TSS_CONTEXT *tssContext, TPM_HANDLE *handle,
}

TPM_RC tpm2_get_session_handle(TSS_CONTEXT *tssContext, TPM_HANDLE *handle,
- TPM_HANDLE salt_key, TPM_SE sessionType)
+ TPM_HANDLE salt_key, TPM_SE sessionType,
+ TPM_ALG_ID name_alg)
{
TPM_RC rc;
StartAuthSession_In in;
@@ -581,7 +582,7 @@ TPM_RC tpm2_get_session_handle(TSS_CONTEXT *tssContext, TPM_HANDLE *handle,
memset(&extra, 0 , sizeof(extra));
in.bind = TPM_RH_NULL;
in.sessionType = sessionType;
- in.authHash = TPM_ALG_SHA256;
+ in.authHash = name_alg;
in.tpmKey = TPM_RH_NULL;
in.symmetric.algorithm = TPM_ALG_AES;
in.symmetric.keyBits.aes = 128;
@@ -614,7 +615,8 @@ TPM_RC tpm2_get_session_handle(TSS_CONTEXT *tssContext, TPM_HANDLE *handle,
}

TPM_RC tpm2_init_session(TSS_CONTEXT *tssContext, TPM_HANDLE handle,
- int num_commands, struct policy_command *commands)
+ int num_commands, struct policy_command *commands,
+ TPM_ALG_ID name_alg)
{
INT32 size;
BYTE *policy;
@@ -622,6 +624,7 @@ TPM_RC tpm2_init_session(TSS_CONTEXT *tssContext, TPM_HANDLE handle,
COMMAND_PARAMETERS in;
int i;
char reason[256];
+ int name_alg_size = TSS_GetDigestSize(name_alg);

reason[0] = '\0';
/* pick a random policy type: they all have the handle first */
@@ -637,9 +640,9 @@ TPM_RC tpm2_init_session(TSS_CONTEXT *tssContext, TPM_HANDLE handle,

rc = TPML_PCR_SELECTION_Unmarshal(
&ppcrin->pcrs, &policy, &size);
- ppcrin->pcrDigest.b.size = size;
+ ppcrin->pcrDigest.b.size = name_alg_size;
memcpy(ppcrin->pcrDigest.b.buffer,
- policy, size);
+ policy, name_alg_size);
sprintf(reason, "PCR Mismatch");
reason_rc = TPM_RC_VALUE;

diff --git a/tpm2-common.h b/tpm2-common.h
index f442c94..14aae40 100644
--- a/tpm2-common.h
+++ b/tpm2-common.h
@@ -16,9 +16,11 @@ 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, TPM_HANDLE hSRK);
TPM_RC tpm2_get_session_handle(TSS_CONTEXT *tssContext, TPM_HANDLE *handle,
- TPM_HANDLE salt_key, TPM_SE sessionType);
+ TPM_HANDLE salt_key, TPM_SE sessionType,
+ TPM_ALG_ID name_alg);
TPM_RC tpm2_init_session(TSS_CONTEXT *tssContext, TPM_HANDLE handle,
- int num_commands, struct policy_command *commands);
+ int num_commands, struct policy_command *commands,
+ TPM_ALG_ID name_alg);
TPM_RC tpm2_get_bound_handle(TSS_CONTEXT *tssContext, TPM_HANDLE *handle,
TPM_HANDLE bind, const char *auth);
TPM_RC tpm2_SensitiveToDuplicate(TPMT_SENSITIVE *s,
--
2.16.4

[PATCH 0/2] remove hardcoded algorithm for policy hashes

James Bottomley
 

Now that we've actually got the name algorithm selection working
correctly, it turns out that policy based keys don't work with a name
algorithm other than sha256. The reason is that the standard specifies
the policy hash algorithm should be the same as the name algorithm and
currently it's hard coded to sha256. This patch series makes the
policy algorithm follow the name algorithm, which is problematic for
PCR selection because the hash of the expected PCR values also has to
be done using the name algorithm. On the bright side, this gives us a
single definitive test that will trip if we ever get a regression in
name algorithm selection.

James

---

James Bottomley (2):
create_tpm2_key: policy should use the name algorithm
check_enhanced_auth.sh: add loop over name algorithm type

create_tpm2_key.c | 6 +-
e_tpm2-ecc.c | 20 +++--
e_tpm2-rsa.c | 20 +++--
e_tpm2.c | 4 +-
e_tpm2.h | 1 +
tests/check_enhanced_auth.sh | 154 +++++++++++++++++---------------
tests/policies/policy_authvalue_pcr.txt | 2 -
tests/policies/policy_pcr_authvalue.txt | 2 -
tpm2-common.c | 13 +--
tpm2-common.h | 6 +-
10 files changed, 128 insertions(+), 100 deletions(-)
delete mode 100644 tests/policies/policy_authvalue_pcr.txt
delete mode 100644 tests/policies/policy_pcr_authvalue.txt

--
2.16.4

Re: [PATCH v2 1/1] Disable elliptic curve when not supported by openssl

James Bottomley
 

On Mon, 2018-11-05 at 09:57 +0100, Fredrik Ternerot wrote:
Disable EC support based on OPENSSL_NO_EC to allow compilation for
systems where EC is not supported by openssl.
As a general comment, I think you could lose a lot of the ifdefs. Just
because you don't support ec doesn't mean everything ec related has to
go (for instance, just keep the curve set to TPM_ECC_NONE in
create_tpm_key ... all the condition checks then do the right thing,
you don't need to eliminate them).

The other big problem is this:

[...]
@@ -253,8 +258,12 @@ TPM_RC tpm2_load_srk(TSS_CONTEXT *tssContext,
TPM_HANDLE *h, const char *auth,TP
/* no PCR state */
in.creationPCR.count = 0;

- /* public parameters for an RSA2048 key */
+ /* public parameters for an ECC/RSA key */
+#ifdef OPENSSL_NO_EC
+ in.inPublic.publicArea.type = TPM_ALG_RSA;
+#else
in.inPublic.publicArea.type = TPM_ALG_ECC;
+#endif
in.inPublic.publicArea.nameAlg = TPM_ALG_SHA256;
in.inPublic.publicArea.objectAttributes.val =
TPMA_OBJECT_NODA |
@@ -262,6 +271,16 @@ TPM_RC tpm2_load_srk(TSS_CONTEXT *tssContext,
TPM_HANDLE *h, const char *auth,TP
TPMA_OBJECT_USERWITHAUTH |
TPMA_OBJECT_DECRYPT |
TPMA_OBJECT_RESTRICTED;
+#ifdef OPENSSL_NO_EC
+ in.inPublic.publicArea.parameters.rsaDetail.symmetric.algori
thm = TPM_ALG_AES;
+ in.inPublic.publicArea.parameters.rsaDetail.symmetric.keyBit
s.aes = 128;
+ in.inPublic.publicArea.parameters.rsaDetail.symmetric.mode.a
es = TPM_ALG_CFB;
+ in.inPublic.publicArea.parameters.rsaDetail.scheme.scheme =
TPM_ALG_NULL;
+ in.inPublic.publicArea.parameters.rsaDetail.keyBits = 2048;
+ in.inPublic.publicArea.parameters.rsaDetail.exponent = 0;
+
+ in.inPublic.publicArea.unique.rsa.t.size = 0;
+#else
Primary RSA templates are really nasty: on my current laptop it takes
50 seconds simply to generate one, which is a huge amount of time to
wait for *every* key operation, so I really think if you want no-EC to
work you can't support any key type with MSO 40 parent (meaning we
create the primary on the fly).

The other reason this causes problems is that parent templates are used
for the key derivation function of the decryption key of the private
blob: use the wrong template the key won't load, so a MSO 40 parent
created with RSA only can't be loaded into a build that supports EC
(and vice versa) which will cause all sorts of support issues.

James

[PATCH] create_tpm2_key: fix name algorithm selection

James Bottomley
 

The results from strcasecmp are zero if it matches, so you need a not
in front of strcasecmp() for the condition to be "it matches". The
current strncasecmp() in the name algorithm selection were missing the
not's resulting in the wrong algorithm being selected if you specified
it on the command line.

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

---

diff --git a/create_tpm2_key.c b/create_tpm2_key.c
index f95f70c..1f8a479 100644
--- a/create_tpm2_key.c
+++ b/create_tpm2_key.c
@@ -65,7 +65,6 @@ static struct option long_options[] = {
};

static TPM_ALG_ID name_alg = TPM_ALG_SHA256;
-static int name_alg_size = SHA256_DIGEST_SIZE;

void
usage(char *argv0)
@@ -676,16 +675,13 @@ int main(int argc, char **argv)
case 'n':
if (!strcasecmp("sha1", optarg)) {
name_alg = TPM_ALG_SHA1;
- name_alg_size = SHA1_DIGEST_SIZE;
- } else if (strcasecmp("sha256", optarg)) {
+ } else if (!strcasecmp("sha256", optarg)) {
/* default, do nothing */
- } else if (strcasecmp("sha384", optarg)) {
+ } 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)) {
+ } else if (!strcasecmp("sha512", optarg)) {
name_alg = TPM_ALG_SHA512;
- name_alg_size = SHA512_DIGEST_SIZE;
#endif
} else {
usage(argv[0]);

[PATCH v2 1/1] Disable elliptic curve when not supported by openssl

Fredrik Ternerot <fredrik.ternerot@...>
 

Disable EC support based on OPENSSL_NO_EC to allow compilation for
systems where EC is not supported by openssl.

Signed-off-by: Fredrik Ternerot <fredrikt@...>
---
create_tpm2_key.c | 32 ++++++++++++++++++++++++++++++++
e_tpm2-ecc.c | 6 ++++++
e_tpm2-rsa.c | 2 ++
e_tpm2.c | 4 ++++
e_tpm2.h | 2 ++
tpm2-common.c | 32 +++++++++++++++++++++++++++++++-
tpm2-common.h | 2 ++
7 files changed, 79 insertions(+), 1 deletion(-)

diff --git a/create_tpm2_key.c b/create_tpm2_key.c
index b7c3167..84cca34 100644
--- a/create_tpm2_key.c
+++ b/create_tpm2_key.c
@@ -297,6 +297,8 @@ void tpm2_public_template_rsa(TPMT_PUBLIC *pub)
pub->parameters.rsaDetail.scheme.scheme = TPM_ALG_NULL;
}

+#ifndef OPENSSL_NO_EC
+
void tpm2_public_template_ecc(TPMT_PUBLIC *pub, TPMI_ECC_CURVE curve)
{
pub->type = TPM_ALG_ECC;
@@ -372,6 +374,8 @@ TPM_RC openssl_to_tpm_public_ecc(TPMT_PUBLIC *pub, EVP_PKEY *pkey)
return rc;
}

+#endif /* OPENSSL_NO_EC */
+
TPM_RC openssl_to_tpm_public_rsa(TPMT_PUBLIC *pub, EVP_PKEY *pkey)
{
RSA *rsa = EVP_PKEY_get1_RSA(pkey);
@@ -419,14 +423,18 @@ TPM_RC openssl_to_tpm_public(TPM2B_PUBLIC *pub, EVP_PKEY *pkey)
switch (EVP_PKEY_type(EVP_PKEY_id(pkey))) {
case EVP_PKEY_RSA:
return openssl_to_tpm_public_rsa(tpub, pkey);
+#ifndef OPENSSL_NO_EC
case EVP_PKEY_EC:
return openssl_to_tpm_public_ecc(tpub, pkey);
+#endif
default:
break;
}
return TPM_RC_ASYMMETRIC;
}

+#ifndef OPENSSL_NO_EC
+
TPM_RC openssl_to_tpm_private_ecc(TPMT_SENSITIVE *s, EVP_PKEY *pkey)
{
const BIGNUM *pk;
@@ -458,6 +466,8 @@ TPM_RC openssl_to_tpm_private_ecc(TPMT_SENSITIVE *s, EVP_PKEY *pkey)
return rc;
}

+#endif /* OPENSSL_NO_EC */
+
TPM_RC openssl_to_tpm_private_rsa(TPMT_SENSITIVE *s, EVP_PKEY *pkey)
{
const BIGNUM *q;
@@ -487,8 +497,10 @@ TPM_RC openssl_to_tpm_private(TPMT_SENSITIVE *priv, EVP_PKEY *pkey)
switch (EVP_PKEY_type(EVP_PKEY_id(pkey))) {
case EVP_PKEY_RSA:
return openssl_to_tpm_private_rsa(priv, pkey);
+#ifndef OPENSSL_NO_EC
case EVP_PKEY_EC:
return openssl_to_tpm_private_ecc(priv, pkey);
+#endif
default:
break;
}
@@ -516,6 +528,8 @@ TPM_RC wrap_key(TPMT_SENSITIVE *s, const char *password, EVP_PKEY *pkey)
return TPM_RC_SUCCESS;
}

+#ifndef OPENSSL_NO_EC
+
static void list_curves(void)
{
TSS_CONTEXT *tssContext;
@@ -566,6 +580,8 @@ static void list_curves(void)
exit(1);
}

+#endif /* OPENSSL_NO_EC */
+
static TPM_HANDLE get_parent(const char *pstr)
{
TPM_HANDLE p;
@@ -618,7 +634,9 @@ int main(int argc, char **argv)
TPM2B_PUBLIC *pub;
TPM2B_PRIVATE *priv;
char *key = NULL, *parent_auth = NULL;
+#ifndef OPENSSL_NO_EC
TPMI_ECC_CURVE ecc = TPM_ECC_NONE;
+#endif
int rsa = -1;
uint32_t noda = TPMA_OBJECT_NODA;
TPM_HANDLE authHandle;
@@ -694,15 +712,25 @@ int main(int argc, char **argv)
rsa = 1;
break;
case 'e':
+#ifndef OPENSSL_NO_EC
ecc = tpm2_curve_name_to_TPMI(optarg);
if (ecc == TPM_ECC_NONE) {
printf("Unknown Curve\n");
exit(1);
}
+#else
+ printf("ECC not supported\n");
+ exit(1);
+#endif
break;
case 'l':
+#ifndef OPENSSL_NO_EC
list_curves();
exit(0);
+#else
+ printf("ECC not supported\n");
+ exit(1);
+#endif
case 'd':
noda = 0;
break;
@@ -736,12 +764,14 @@ int main(int argc, char **argv)
key_size = 2048;
}

+#ifndef OPENSSL_NO_EC
if (rsa == 1 && ecc != TPM_ECC_NONE) {
fprintf(stderr, "Cannot specify both --rsa and --ecc\n");
exit(1);
} else if (ecc != TPM_ECC_NONE) {
rsa = 0;
}
+#endif

dir = tpm2_set_unique_tssdir();
rc = tpm2_create(&tssContext, dir);
@@ -899,8 +929,10 @@ int main(int argc, char **argv)
cin.inPublic.publicArea.parameters.rsaDetail.exponent = 0;
cin.inPublic.publicArea.unique.rsa.t.size = 0;

+#ifndef OPENSSL_NO_EC
} else {
tpm2_public_template_ecc(&cin.inPublic.publicArea, ecc);
+#endif
}

if (policyFilename) {
diff --git a/e_tpm2-ecc.c b/e_tpm2-ecc.c
index e35b730..b0af4c7 100644
--- a/e_tpm2-ecc.c
+++ b/e_tpm2-ecc.c
@@ -5,6 +5,10 @@
*
*/

+#include <openssl/opensslconf.h>
+
+#ifndef OPENSSL_NO_EC
+
#include <stdio.h>
#include <string.h>

@@ -358,3 +362,5 @@ int tpm2_setup_ecc_methods(void)

return 1;
}
+
+#endif /* OPENSSL_NO_EC */
diff --git a/e_tpm2-rsa.c b/e_tpm2-rsa.c
index 50c2cac..f736363 100644
--- a/e_tpm2-rsa.c
+++ b/e_tpm2-rsa.c
@@ -9,7 +9,9 @@
#include <string.h>

#include <openssl/crypto.h>
+#ifndef OPENSSL_NO_EC
#include <openssl/ec.h>
+#endif
#include <openssl/engine.h>
#include <openssl/evp.h>
#include <openssl/objects.h>
diff --git a/e_tpm2.c b/e_tpm2.c
index ed2bbc2..4906aa3 100644
--- a/e_tpm2.c
+++ b/e_tpm2.c
@@ -180,9 +180,11 @@ void tpm2_bind_key_to_engine(EVP_PKEY *pkey, void *data)
case EVP_PKEY_RSA:
tpm2_bind_key_to_engine_rsa(pkey, data);
break;
+#ifndef OPENSSL_NO_EC
case EVP_PKEY_EC:
tpm2_bind_key_to_engine_ecc(pkey, data);
break;
+#endif
default:
break;
}
@@ -485,7 +487,9 @@ static int tpm2_bind_helper(ENGINE * e)
!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) ||
+#ifndef OPENSSL_NO_EC
!tpm2_setup_ecc_methods() ||
+#endif
!tpm2_setup_rsa_methods())
return 0;

diff --git a/e_tpm2.h b/e_tpm2.h
index ef9fd38..50ffba1 100644
--- a/e_tpm2.h
+++ b/e_tpm2.h
@@ -2,7 +2,9 @@
#define _E_TPM2_COMMON_H

#include "e_tpm2-rsa.h"
+#ifndef OPENSSL_NO_EC
#include "e_tpm2-ecc.h"
+#endif

#define TPM2_ENGINE_EX_DATA_UNINIT -1

diff --git a/tpm2-common.c b/tpm2-common.c
index ee3eb4c..90a8534 100644
--- a/tpm2-common.c
+++ b/tpm2-common.c
@@ -27,6 +27,9 @@ struct myTPM2B {
UINT16 s;
BYTE *const b;
};
+
+#ifndef OPENSSL_NO_EC
+
struct tpm2_ECC_Curves {
const char *name;
int nid;
@@ -220,6 +223,8 @@ struct tpm2_ECC_Curves tpm2_supported_curves[] = {
{ .name = NULL, }
};

+#endif /* OPENSSL_NO_EC */
+
void tpm2_error(TPM_RC rc, const char *reason)
{
const char *msg, *submsg, *num;
@@ -253,8 +258,12 @@ TPM_RC tpm2_load_srk(TSS_CONTEXT *tssContext, TPM_HANDLE *h, const char *auth,TP
/* no PCR state */
in.creationPCR.count = 0;

- /* public parameters for an RSA2048 key */
+ /* public parameters for an ECC/RSA key */
+#ifdef OPENSSL_NO_EC
+ in.inPublic.publicArea.type = TPM_ALG_RSA;
+#else
in.inPublic.publicArea.type = TPM_ALG_ECC;
+#endif
in.inPublic.publicArea.nameAlg = TPM_ALG_SHA256;
in.inPublic.publicArea.objectAttributes.val =
TPMA_OBJECT_NODA |
@@ -262,6 +271,16 @@ TPM_RC tpm2_load_srk(TSS_CONTEXT *tssContext, TPM_HANDLE *h, const char *auth,TP
TPMA_OBJECT_USERWITHAUTH |
TPMA_OBJECT_DECRYPT |
TPMA_OBJECT_RESTRICTED;
+#ifdef OPENSSL_NO_EC
+ 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;
+ in.inPublic.publicArea.parameters.rsaDetail.exponent = 0;
+
+ in.inPublic.publicArea.unique.rsa.t.size = 0;
+#else
in.inPublic.publicArea.parameters.eccDetail.symmetric.algorithm = TPM_ALG_AES;
in.inPublic.publicArea.parameters.eccDetail.symmetric.keyBits.aes = 128;
in.inPublic.publicArea.parameters.eccDetail.symmetric.mode.aes = TPM_ALG_CFB;
@@ -271,6 +290,7 @@ TPM_RC tpm2_load_srk(TSS_CONTEXT *tssContext, TPM_HANDLE *h, const char *auth,TP

in.inPublic.publicArea.unique.ecc.x.t.size = 0;
in.inPublic.publicArea.unique.ecc.y.t.size = 0;
+#endif
in.inPublic.publicArea.authPolicy.t.size = 0;

/* use a bound session here because we have no known key objects
@@ -322,6 +342,8 @@ void tpm2_flush_handle(TSS_CONTEXT *tssContext, TPM_HANDLE h)
TPM_RH_NULL, NULL, 0);
}

+#ifndef OPENSSL_NO_EC
+
int tpm2_get_ecc_group(EC_KEY *eck, TPMI_ECC_CURVE curveID)
{
const int nid = tpm2_curve_name_to_nid(curveID);
@@ -444,6 +466,8 @@ static EVP_PKEY *tpm2_to_openssl_public_ecc(TPMT_PUBLIC *pub)
return NULL;
}

+#endif /* OPENSSL_NO_EC */
+
static EVP_PKEY *tpm2_to_openssl_public_rsa(TPMT_PUBLIC *pub)
{
RSA *rsa = RSA_new();
@@ -498,8 +522,10 @@ EVP_PKEY *tpm2_to_openssl_public(TPMT_PUBLIC *pub)
switch (pub->type) {
case TPM_ALG_RSA:
return tpm2_to_openssl_public_rsa(pub);
+#ifndef OPENSSL_NO_EC
case TPM_ALG_ECC:
return tpm2_to_openssl_public_ecc(pub);
+#endif
default:
break;
}
@@ -863,6 +889,8 @@ TPM_RC tpm2_ObjectPublic_GetName(TPM2B_NAME *name,
return rc;
}

+#ifndef OPENSSL_NO_EC
+
TPMI_ECC_CURVE tpm2_curve_name_to_TPMI(const char *name)
{
int i;
@@ -955,6 +983,8 @@ const char *tpm2_curve_name_to_text(TPMI_ECC_CURVE curve)
return NULL;
}

+#endif /* OPENSSL_NO_EC */
+
const char *tpm2_set_unique_tssdir(void)
{
char *prefix = getenv("XDG_RUNTIME_DIR"), *template,
diff --git a/tpm2-common.h b/tpm2-common.h
index dd46f0a..7c3133a 100644
--- a/tpm2-common.h
+++ b/tpm2-common.h
@@ -30,11 +30,13 @@ TPM_RC tpm2_SensitiveToDuplicate(TPMT_SENSITIVE *s,
TPM2B_PRIVATE *p);
TPM_RC tpm2_ObjectPublic_GetName(TPM2B_NAME *name,
TPMT_PUBLIC *tpmtPublic);
+#ifndef OPENSSL_NO_EC
TPMI_ECC_CURVE tpm2_curve_name_to_TPMI(const char *name);
int tpm2_curve_name_to_nid(TPMI_ECC_CURVE curve);
TPMI_ECC_CURVE tpm2_nid_to_curve_name(int nid);
TPMI_ECC_CURVE tpm2_get_curve_name(const EC_GROUP *g);
const char *tpm2_curve_name_to_text(TPMI_ECC_CURVE curve);
+#endif
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,
--
2.11.0

Re: [PATCH] add tests for old keys

David Woodhouse
 

On Mon, 2018-11-05 at 07:25 -0800, James Bottomley wrote:
I'm travelling at the moment but will try to do those tests... unless
you've already done them?
Not with openconnect, no. The test suite uses openssl as the primary
application it tests with, so I've run make check successfully, yes.
I was specifically asking about interop, creating PEM files with your
create_tpm2_key tool and then using them from both OpenConnect+GnuTLS
and the "other" engine. But I've also now done those tests, and it
works fine at least for the default primary parent without auth.

Re: [PATCH] add tests for old keys

James Bottomley
 

On Mon, 2018-11-05 at 10:36 +0100, David Woodhouse wrote:
On Sun, 2018-11-04 at 14:13 -0800, James Bottomley wrote:
To check that old format keys still work, we add a --deprecated
option to create_tpm2_key that allows us to create them still. The
test is to create an old format key and prove the engine still
accepts it.

Signed-off-by: James Bottomley <James.Bottomley@...
om>
These patches look sane enough; thanks. They should interoperate with
the current OpenConnect git HEAD (which should take both old and new
forms; you only need to point it at any HTTPS server to test that
much)... and also with the TCG engine with the patch in
https://github.com/tpm2-software/tpm2-tss-engine/pull/40

I'm travelling at the moment but will try to do those tests... unless
you've already done them?
Not with openconnect, no. The test suite uses openssl as the primary
application it tests with, so I've run make check successfully, yes.

James

Re: [PATCH] move to TCG defined OIDs for the key types

Fredrik Ternerot
 

On Mon, Nov 5, 2018 at 11:04 AM David Woodhouse <dwmw2@...> wrote:

On Mon, 2018-11-05 at 10:52 +0100, Fredrik Ternerot wrote:
Is something missing here? version seems not to be declared anywhere.
This is based on my earlier patch series adding the new TSS2 PRIVATE
KEY format.
Thanks for pointing out. Now I've adapted to the new format as well
and these patches works fine for me regarding using keys in the new
format (for creating keys I'm using something different than
create_tpm2_key, so that part I haven't verified).

Fredrik

Re: [PATCH] add tests for old keys

David Woodhouse
 

On Mon, 2018-11-05 at 10:36 +0100, David Woodhouse wrote:
On Sun, 2018-11-04 at 14:13 -0800, James Bottomley wrote:

To check that old format keys still work, we add a --deprecated
option

to create_tpm2_key that allows us to create them still. The test
is

to create an old format key and prove the engine still accepts it.
Signed-off-by: James Bottomley <
James.Bottomley@...>



These patches look sane enough; thanks. They should interoperate with

the current OpenConnect git HEAD (which should take both old and new

forms; you only need to point it at any HTTPS server to test that

much)... and also with the TCG engine with the patch in

https://github.com/tpm2-software/tpm2-tss-engine/pull/40



I'm travelling at the moment but will try to do those tests... unless

you've already done them?
I created wrapped keys both with the --deprecated option and without.

The GnuTLS build of OpenConnect works with both. The OpenSSL build of
OpenConnect using your engine works with both. With the tpm2tss engine
it works with the new format but not the old, as expected.

I didn't test auth, or using a persistent key as a parent. We know that
auth is broken for both engines anyway because they don't invoke the UI
callbacks in all cases when they need passwords (qv).