Blob Blame History Raw
From d74b094191f2e09b29cfad4f03322e0cd64497ab Mon Sep 17 00:00:00 2001
From: jetwhiz <charles.munson@ll.mit.edu>
Date: Tue, 5 Feb 2019 17:24:44 -0500
Subject: [PATCH] Add ability to run tpm2_makecredential without a TPM

Used some functions from tpm2_import for off-TPM functionality
 - Move needed, overlap code into tpm2_identity_util
Add new global -X/--openssl-backend option for any tools that can operate w/o TPM

Signed-off-by: jetwhiz <Charles.Munson@ll.mit.edu>
---
 Makefile.am                             |   6 +-
 lib/tpm2_identity_util.c                | 480 ++++++++++++++++++++++++
 lib/tpm2_identity_util.h                | 141 +++++++
 lib/tpm2_openssl.c                      | 162 ++++++++
 lib/tpm2_openssl.h                      | 108 ++++++
 lib/tpm2_options.c                      |  36 +-
 lib/tpm2_options.h                      |   1 +
 man/common/tcti.md                      |   4 +
 man/tpm2_activatecredential.1.md        |   2 +-
 man/tpm2_certify.1.md                   |   2 +-
 man/tpm2_create.1.md                    |   2 +-
 man/tpm2_encryptdecrypt.1.md            |   2 +-
 man/tpm2_getpubak.1.md                  |   2 +-
 man/tpm2_makecredential.1.md            |   3 +-
 test/system/test_tpm2_makecredential.sh |   2 +
 tools/tpm2_makecredential.c             | 103 ++++-
 16 files changed, 1033 insertions(+), 23 deletions(-)
 create mode 100644 lib/tpm2_identity_util.c
 create mode 100644 lib/tpm2_identity_util.h
 create mode 100644 lib/tpm2_openssl.c
 create mode 100644 lib/tpm2_openssl.h

diff --git a/Makefile.am b/Makefile.am
index ffe22f383e3..2195537ce01 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -119,7 +119,11 @@ lib_libcommon_a_SOURCES = \
     lib/tpm2_errata.c \
     lib/tpm2_errata.h \
     lib/tpm2_header.h \
+    lib/tpm2_identity_util.c \
+    lib/tpm2_identity_util.h \
     lib/tpm2_nv_util.h \
+    lib/tpm2_openssl.c \
+    lib/tpm2_openssl.h \
     lib/tpm2_password_util.c \
     lib/tpm2_password_util.h \
     lib/tpm2_policy.c \
@@ -347,4 +351,4 @@ man/man1/%.1 : man/%.1.md $(MARKDOWN_COMMON_DEPS)
 	    -e '/\[object attribute specifiers\]/d' \
 	    < $< | pandoc -s -t man > $@
 
-CLEANFILES = $(man1_MANS)
+CLEANFILES = $(man1_MANS)
\ No newline at end of file
diff --git a/lib/tpm2_identity_util.c b/lib/tpm2_identity_util.c
new file mode 100644
index 00000000000..70bf03647eb
--- /dev/null
+++ b/lib/tpm2_identity_util.c
@@ -0,0 +1,480 @@
+//**********************************************************************;
+// Copyright (c) 2017, Intel Corporation
+// Copyright (c) 2019 Massachusetts Institute of Technology
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+// THE POSSIBILITY OF SUCH DAMAGE.
+//**********************************************************************
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <openssl/aes.h>
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/hmac.h>
+#include <openssl/obj_mac.h>
+#include <openssl/pem.h>
+#include <openssl/rand.h>
+#include <openssl/rsa.h>
+
+#include <tss2/tss2_mu.h>
+#include <tss2/tss2_sys.h>
+
+#include "files.h"
+#include "log.h"
+#include "tpm2_alg_util.h"
+#include "tpm_kdfa.h"
+#include "tpm2_openssl.h"
+#include "tpm2_identity_util.h"
+#include "tpm2_util.h"
+
+
+// Identity-related functionality that the TPM normally does, but using OpenSSL
+
+#if defined(LIBRESSL_VERSION_NUMBER)
+static int RSA_padding_add_PKCS1_OAEP_mgf1(unsigned char *to, int tlen,
+        const unsigned char *from, int flen, const unsigned char *param, int plen,
+        const EVP_MD *md, const EVP_MD *mgf1md) {
+
+    int ret = 0;
+    int i, emlen = tlen - 1;
+    unsigned char *db, *seed;
+    unsigned char *dbmask, seedmask[EVP_MAX_MD_SIZE];
+    int mdlen;
+
+    if (md == NULL)
+        md = EVP_sha1();
+    if (mgf1md == NULL)
+        mgf1md = md;
+
+    mdlen = EVP_MD_size(md);
+
+    if (flen > emlen - 2 * mdlen - 1) {
+        RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP,
+               RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+        return 0;
+    }
+
+    if (emlen < 2 * mdlen + 1) {
+        RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP,
+               RSA_R_KEY_SIZE_TOO_SMALL);
+        return 0;
+    }
+
+    to[0] = 0;
+    seed = to + 1;
+    db = to + mdlen + 1;
+
+    if (!EVP_Digest((void *)param, plen, db, NULL, md, NULL))
+        return 0;
+    memset(db + mdlen, 0, emlen - flen - 2 * mdlen - 1);
+    db[emlen - flen - mdlen - 1] = 0x01;
+    memcpy(db + emlen - flen - mdlen, from, (unsigned int)flen);
+    if (RAND_bytes(seed, mdlen) <= 0)
+        return 0;
+
+    dbmask = OPENSSL_malloc(emlen - mdlen);
+    if (dbmask == NULL) {
+        RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    if (PKCS1_MGF1(dbmask, emlen - mdlen, seed, mdlen, mgf1md) < 0)
+        goto err;
+    for (i = 0; i < emlen - mdlen; i++)
+        db[i] ^= dbmask[i];
+
+    if (PKCS1_MGF1(seedmask, mdlen, db, emlen - mdlen, mgf1md) < 0)
+        goto err;
+    for (i = 0; i < mdlen; i++)
+        seed[i] ^= seedmask[i];
+
+    ret = 1;
+
+ err:
+    OPENSSL_free(dbmask);
+
+    return ret;
+}
+#endif
+
+static TPM2_KEY_BITS get_pub_asym_key_bits(TPM2B_PUBLIC *public) {
+
+    TPMU_PUBLIC_PARMS *p = &public->publicArea.parameters;
+    switch(public->publicArea.type) {
+    case TPM2_ALG_ECC:
+        /* fall-thru */
+    case TPM2_ALG_RSA:
+        return p->asymDetail.symmetric.keyBits.sym;
+    /* no default */
+    }
+
+    return 0;
+}
+
+static bool encrypt_seed_with_tpm2_rsa_public_key(TPM2B_DIGEST *protection_seed,
+        TPM2B_PUBLIC *parent_pub, unsigned char *label, int labelLen,
+        TPM2B_ENCRYPTED_SECRET *encrypted_protection_seed) {
+    bool rval = false;
+    RSA *rsa = NULL;
+
+    // Public modulus (RSA-only!)
+    TPMI_RSA_KEY_BITS mod_size_bits = parent_pub->publicArea.parameters.rsaDetail.keyBits;
+    UINT16 mod_size = mod_size_bits / 8;
+    TPM2B *pub_key_val = (TPM2B *)&parent_pub->publicArea.unique.rsa;
+    unsigned char *pub_modulus = malloc(mod_size);
+    if (pub_modulus == NULL) {
+        LOG_ERR("Failed to allocate memory to store public key's modulus.");
+        return false;
+    }
+    memcpy(pub_modulus, pub_key_val->buffer, mod_size);
+
+    TPMI_ALG_HASH parent_name_alg = parent_pub->publicArea.nameAlg;
+
+    /*
+     * This is the biggest buffer value, so it should always be sufficient.
+     */
+    unsigned char encoded[TPM2_MAX_DIGEST_BUFFER];
+    int return_code = RSA_padding_add_PKCS1_OAEP_mgf1(encoded,
+            mod_size, protection_seed->buffer, protection_seed->size, label, labelLen,
+            tpm2_openssl_halg_from_tpmhalg(parent_name_alg), NULL);
+    if (return_code != 1) {
+        LOG_ERR("Failed RSA_padding_add_PKCS1_OAEP_mgf1\n");
+        goto error;
+    }
+    BIGNUM* bne = BN_new();
+    if (!bne) {
+        LOG_ERR("BN_new for bne failed\n");
+        goto error;
+    }
+    return_code = BN_set_word(bne, RSA_F4);
+    if (return_code != 1) {
+        LOG_ERR("BN_set_word failed\n");
+        BN_free(bne);
+        goto error;
+    }
+    rsa = RSA_new();
+    if (!rsa) {
+        LOG_ERR("RSA_new failed\n");
+        BN_free(bne);
+        goto error;
+    }
+    return_code = RSA_generate_key_ex(rsa, mod_size_bits, bne, NULL);
+    BN_free(bne);
+    if (return_code != 1) {
+        LOG_ERR("RSA_generate_key_ex failed\n");
+        goto error;
+    }
+    BIGNUM *n = BN_bin2bn(pub_modulus, mod_size, NULL);
+    if (n == NULL) {
+        LOG_ERR("BN_bin2bn failed\n");
+        goto error;
+    }
+    if (!RSA_set0_key(rsa, n, NULL, NULL)) {
+        LOG_ERR("RSA_set0_key failed\n");
+        BN_free(n);
+        goto error;
+    }
+    // Encrypting
+    encrypted_protection_seed->size = mod_size;
+    return_code = RSA_public_encrypt(mod_size, encoded,
+            encrypted_protection_seed->secret, rsa, RSA_NO_PADDING);
+    if (return_code < 0) {
+        LOG_ERR("Failed RSA_public_encrypt\n");
+        goto error;
+    }
+
+    rval = true;
+
+error:
+    free(pub_modulus);
+    RSA_free(rsa);
+    return rval;
+}
+
+bool tpm2_identity_util_calc_outer_integrity_hmac_key_and_dupsensitive_enc_key(
+        TPM2B_PUBLIC *parent_pub,
+        TPM2B_NAME *pubname,
+        TPM2B_DIGEST *protection_seed,
+        TPM2B_MAX_BUFFER *protection_hmac_key,
+        TPM2B_MAX_BUFFER *protection_enc_key) {
+
+    TPM2B null_2b = { .size = 0 };
+
+    TPMI_ALG_HASH parent_alg = parent_pub->publicArea.nameAlg;
+    UINT16 parent_hash_size = tpm2_alg_util_get_hash_size(parent_alg);
+
+    TSS2_RC rval = tpm_kdfa(parent_alg, (TPM2B *)protection_seed, "INTEGRITY",
+            &null_2b, &null_2b, parent_hash_size * 8, protection_hmac_key);
+    if (rval != TPM2_RC_SUCCESS) {
+        return false;
+    }
+
+    TPM2_KEY_BITS pub_key_bits = get_pub_asym_key_bits(parent_pub);
+
+    rval = tpm_kdfa(parent_alg, (TPM2B *)protection_seed, "STORAGE",
+            (TPM2B *)pubname, &null_2b, pub_key_bits,
+            protection_enc_key);
+    if (rval != TPM2_RC_SUCCESS) {
+        return false;
+    }
+
+    return true;
+}
+
+
+bool tpm2_identity_util_encrypt_seed_with_public_key(TPM2B_DIGEST *protection_seed,
+        TPM2B_PUBLIC *parent_pub, unsigned char *label, int labelLen,
+        TPM2B_ENCRYPTED_SECRET *encrypted_protection_seed) {
+    bool result = false;
+    TPMI_ALG_PUBLIC alg = parent_pub->publicArea.type;
+    
+    switch (alg) {
+    case TPM2_ALG_RSA:
+        result = encrypt_seed_with_tpm2_rsa_public_key(protection_seed, 
+                parent_pub, label, labelLen, encrypted_protection_seed);
+        break;
+    case TPM2_ALG_ECC:
+        LOG_ERR("Algorithm '%s' not supported yet", tpm2_alg_util_algtostr(alg));
+        result = false;
+        break;
+    default:
+        LOG_ERR("Cannot handle algorithm, got: %s", tpm2_alg_util_algtostr(alg));
+        return false;
+    }
+    
+    return result;
+}
+
+static const EVP_CIPHER *tpm_alg_to_ossl(TPMT_SYM_DEF_OBJECT *sym) {
+
+    switch(sym->algorithm) {
+        case TPM2_ALG_AES: {
+            switch (sym->keyBits.aes) {
+            case 128:
+                return EVP_aes_128_cfb();
+            case 256:
+                return EVP_aes_256_cfb();
+            /* no default */
+            }
+        }
+        /* no default */
+    }
+
+    LOG_ERR("Unsupported parent key symmetric parameters");
+
+    return NULL;
+}
+
+static bool aes_encrypt_buffers(TPMT_SYM_DEF_OBJECT *sym, uint8_t *encryption_key,
+        uint8_t *buf1, size_t buf1_len,
+        uint8_t *buf2, size_t buf2_len,
+        TPM2B_MAX_BUFFER *cipher_text) {
+
+    bool result = false;
+
+    unsigned offset = 0;
+    size_t total_len = buf1_len + buf2_len;
+
+    if (total_len > sizeof(cipher_text->buffer)) {
+        LOG_ERR("Plaintext too big, got %zu, expected less then %zu",
+                total_len, sizeof(cipher_text->buffer));
+        return false;
+    }
+
+    const EVP_CIPHER *cipher = tpm_alg_to_ossl(sym);
+    if (!cipher) {
+        return false;
+    }
+
+    const unsigned char iv[512] = { 0 };
+
+    if (((unsigned long)EVP_CIPHER_iv_length(cipher)) > sizeof(iv)) {
+        LOG_ERR("IV size is bigger then IV buffer size");
+        return false;
+    }
+
+    EVP_CIPHER_CTX *ctx = tpm2_openssl_cipher_new();
+
+    int rc = EVP_EncryptInit_ex(ctx, cipher, NULL, encryption_key, iv);
+    if (!rc) {
+        return false;
+    }
+
+    EVP_CIPHER_CTX_set_padding(ctx, 0);
+
+    uint8_t *bufs[2] = {
+        buf1,
+        buf2
+    };
+
+    size_t lens[ARRAY_LEN(bufs)] = {
+        buf1_len,
+        buf2_len
+    };
+
+    unsigned i;
+    for (i=0; i < ARRAY_LEN(bufs); i++) {
+
+        uint8_t *b = bufs[i];
+        size_t l = lens[i];
+
+        if (!b) {
+            continue;
+        }
+
+        int output_len = total_len - offset;
+
+        rc = EVP_EncryptUpdate(ctx, &cipher_text->buffer[offset], &output_len, b, l);
+        if (!rc) {
+            LOG_ERR("Encrypt failed");
+            goto out;
+        }
+
+        offset += l;
+    }
+
+    int tmp_len = 0;
+    rc = EVP_EncryptFinal_ex(ctx, NULL, &tmp_len);
+    if (!rc) {
+        LOG_ERR("Encrypt failed");
+        goto out;
+    }
+
+    cipher_text->size = total_len;
+
+    result = true;
+
+out:
+    tpm2_openssl_cipher_free(ctx);
+
+    return result;
+}
+
+static void hmac_outer_integrity(
+        TPMI_ALG_HASH parent_name_alg,
+        uint8_t *buffer1, uint16_t buffer1_size,
+        uint8_t *buffer2, uint16_t buffer2_size, uint8_t *hmac_key,
+        TPM2B_DIGEST *outer_integrity_hmac) {
+
+    uint8_t to_hmac_buffer[TPM2_MAX_DIGEST_BUFFER];
+    memcpy(to_hmac_buffer, buffer1, buffer1_size);
+    memcpy(to_hmac_buffer + buffer1_size, buffer2, buffer2_size);
+    uint32_t size = 0;
+
+    UINT16 hash_size = tpm2_alg_util_get_hash_size(parent_name_alg);
+
+    HMAC(tpm2_openssl_halg_from_tpmhalg(parent_name_alg), hmac_key, hash_size, to_hmac_buffer,
+            buffer1_size + buffer2_size, outer_integrity_hmac->buffer, &size);
+    outer_integrity_hmac->size = size;
+}
+
+bool tpm2_identity_util_calculate_inner_integrity(
+        TPMI_ALG_HASH name_alg, 
+        TPM2B_SENSITIVE *sensitive, 
+        TPM2B_NAME *pubname, 
+        TPM2B_DATA *enc_sensitive_key,
+        TPMT_SYM_DEF_OBJECT *sym_alg,
+        TPM2B_MAX_BUFFER *encrypted_inner_integrity) {
+
+    //Marshal sensitive area
+    uint8_t buffer_marshalled_sensitiveArea[TPM2_MAX_DIGEST_BUFFER] = { 0 };
+    size_t marshalled_sensitive_size = 0;
+    Tss2_MU_TPMT_SENSITIVE_Marshal(&sensitive->sensitiveArea,
+            buffer_marshalled_sensitiveArea + sizeof(uint16_t), TPM2_MAX_DIGEST_BUFFER,
+            &marshalled_sensitive_size);
+    size_t marshalled_sensitive_size_info = 0;
+    Tss2_MU_UINT16_Marshal(marshalled_sensitive_size, buffer_marshalled_sensitiveArea,
+            sizeof(uint16_t), &marshalled_sensitive_size_info);
+
+    //concatenate NAME
+    memcpy(
+            buffer_marshalled_sensitiveArea + marshalled_sensitive_size
+                    + marshalled_sensitive_size_info,
+            pubname->name,
+            pubname->size);
+
+    //Digest marshalled-sensitive || name
+    uint8_t *marshalled_sensitive_and_name_digest =
+            buffer_marshalled_sensitiveArea + marshalled_sensitive_size
+                    + marshalled_sensitive_size_info
+                    + pubname->size;
+    size_t digest_size_info = 0;
+    UINT16 hash_size = tpm2_alg_util_get_hash_size(name_alg);
+    Tss2_MU_UINT16_Marshal(hash_size, marshalled_sensitive_and_name_digest,
+            sizeof(uint16_t), &digest_size_info);
+
+    digester d = tpm2_openssl_halg_to_digester(name_alg);
+    d(buffer_marshalled_sensitiveArea,
+            marshalled_sensitive_size_info + marshalled_sensitive_size
+                    + pubname->size,
+            marshalled_sensitive_and_name_digest + digest_size_info);
+
+    //Inner integrity
+    encrypted_inner_integrity->size = marshalled_sensitive_size_info
+            + marshalled_sensitive_size + pubname->size;
+
+    return aes_encrypt_buffers(
+            sym_alg,
+            enc_sensitive_key->buffer,
+            marshalled_sensitive_and_name_digest, 
+            hash_size + digest_size_info,
+            buffer_marshalled_sensitiveArea, 
+            marshalled_sensitive_size_info + marshalled_sensitive_size,
+            encrypted_inner_integrity);
+}
+
+void tpm2_identity_util_calculate_outer_integrity(
+        TPMI_ALG_HASH parent_name_alg,
+        TPM2B_NAME *pubname,
+        TPM2B_MAX_BUFFER *marshalled_sensitive,
+        TPM2B_MAX_BUFFER *protection_hmac_key,
+        TPM2B_MAX_BUFFER *protection_enc_key,
+        TPMT_SYM_DEF_OBJECT *sym_alg,
+        TPM2B_MAX_BUFFER *encrypted_duplicate_sensitive,
+        TPM2B_DIGEST *outer_hmac) {
+
+    //Calculate dupSensitive
+    encrypted_duplicate_sensitive->size =
+            marshalled_sensitive->size;
+
+    aes_encrypt_buffers(
+            sym_alg,
+            protection_enc_key->buffer,
+            marshalled_sensitive->buffer,
+            marshalled_sensitive->size,
+            NULL, 0,
+            encrypted_duplicate_sensitive);
+    //Calculate outerHMAC
+    hmac_outer_integrity(
+            parent_name_alg,
+            encrypted_duplicate_sensitive->buffer,
+            encrypted_duplicate_sensitive->size,
+            pubname->name,
+            pubname->size, 
+            protection_hmac_key->buffer,
+            outer_hmac);
+}
diff --git a/lib/tpm2_identity_util.h b/lib/tpm2_identity_util.h
new file mode 100644
index 00000000000..49f231a3347
--- /dev/null
+++ b/lib/tpm2_identity_util.h
@@ -0,0 +1,141 @@
+//**********************************************************************;
+// Copyright (c) 2017, Intel Corporation
+// Copyright (c) 2019 Massachusetts Institute of Technology
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+// THE POSSIBILITY OF SUCH DAMAGE.
+//**********************************************************************
+
+#ifndef LIB_TPM2_IDENTITY_UTIL_H_
+#define LIB_TPM2_IDENTITY_UTIL_H_
+
+#include <tss2/tss2_sys.h>
+
+#include <openssl/err.h>
+#include <openssl/hmac.h>
+#include <openssl/rsa.h>
+
+
+/**
+ * Generates HMAC integrity and symmetric encryption keys for TPM2 identies.
+ *
+ * @param parent_pub
+ *  The public key used for seed generation and protection.
+ * @param pubname
+ *  The Name object associated with the parent_pub credential.
+ * @param protection_seed
+ *  The symmetric seed value used to generate protection keys.
+ * @param protection_hmac_key
+ *  The HMAC integrity key to populate.
+ * @param protection_enc_key
+ *  The symmetric encryption key to populate.
+ * @return
+ *  True on success, false on failure.
+ */
+bool tpm2_identity_util_calc_outer_integrity_hmac_key_and_dupsensitive_enc_key(
+        TPM2B_PUBLIC *parent_pub,
+        TPM2B_NAME *pubname,
+        TPM2B_DIGEST *protection_seed,
+        TPM2B_MAX_BUFFER *protection_hmac_key,
+        TPM2B_MAX_BUFFER *protection_enc_key);
+
+/**
+ * Encrypts seed with parent public key for TPM2 credential protection process.
+ *
+ * @param protection_seed
+ *  The identity structure protection seed that is to be encrypted.
+ * @param parent_pub
+ *  The public key used for encryption.
+ * @param label
+ *  Indicates label for the seed, such as "IDENTITY" or "DUPLICATE".
+ * @param labelLen
+ *  Length of label.
+ * @param encrypted_protection_seed
+ *  The encrypted protection seed to populate.
+ * @return
+ *  True on success, false on failure.
+ */
+bool tpm2_identity_util_encrypt_seed_with_public_key(
+        TPM2B_DIGEST *protection_seed,
+        TPM2B_PUBLIC *parent_pub,
+        unsigned char *label,
+        int labelLen,
+        TPM2B_ENCRYPTED_SECRET *encrypted_protection_seed);
+
+/**
+ * Marshalls Credential Value and encrypts it with the symmetric encryption key.
+ *
+ * @param name_alg
+ *  Hash algorithm used to compute Name of the public key.
+ * @param sensitive
+ *  The Credential Value to be marshalled and encrypted with symmetric key.
+ * @param pubname
+ *  The Name object corresponding to the public key.
+ * @param enc_sensitive_key
+ *  The symmetric encryption key.
+ * @param sym_alg
+ *  The algorithm used for the symmetric encryption key.
+ * @param encrypted_inner_integrity
+ *  The encrypted, marshalled Credential Value to populate.
+ * @return
+ *  True on success, false on failure.
+ */
+bool tpm2_identity_util_calculate_inner_integrity(
+        TPMI_ALG_HASH name_alg,
+        TPM2B_SENSITIVE *sensitive,
+        TPM2B_NAME *pubname,
+        TPM2B_DATA *enc_sensitive_key,
+        TPMT_SYM_DEF_OBJECT *sym_alg,
+        TPM2B_MAX_BUFFER *encrypted_inner_integrity);
+
+/**
+ * Encrypts Credential Value with enc key and calculates HMAC with hmac key.
+ *
+ * @param parent_name_alg
+ *  Hash algorithm used to compute Name of the public key.
+ * @param pubname
+ *  The Name object corresponding to the public key.
+ * @param marshalled_sensitive
+ *  Marshalled Credential Value to be encrypted with symmetric encryption key.
+ * @param protection_hmac_key
+ *  The HMAC integrity key.
+ * @param protection_enc_key
+ *  The symmetric encryption key.
+ * @param sym_alg
+ *  The algorithm used for the symmetric encryption key.
+ * @param encrypted_duplicate_sensitive
+ *  The encrypted Credential Value to populate.
+ * @param outer_hmac
+ *  The outer HMAC structure to populate.
+ */
+void tpm2_identity_util_calculate_outer_integrity(
+        TPMI_ALG_HASH parent_name_alg,
+        TPM2B_NAME *pubname,
+        TPM2B_MAX_BUFFER *marshalled_sensitive,
+        TPM2B_MAX_BUFFER *protection_hmac_key,
+        TPM2B_MAX_BUFFER *protection_enc_key,
+        TPMT_SYM_DEF_OBJECT *sym_alg,
+        TPM2B_MAX_BUFFER *encrypted_duplicate_sensitive,
+        TPM2B_DIGEST *outer_hmac);
+
+#endif /* LIB_TPM2_IDENTITY_UTIL_H_ */
diff --git a/lib/tpm2_openssl.c b/lib/tpm2_openssl.c
new file mode 100644
index 00000000000..0bfc95bd1ef
--- /dev/null
+++ b/lib/tpm2_openssl.c
@@ -0,0 +1,162 @@
+//**********************************************************************;
+// Copyright (c) 2017, Intel Corporation
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+// THE POSSIBILITY OF SUCH DAMAGE.
+//**********************************************************************
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <openssl/aes.h>
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/hmac.h>
+#include <openssl/obj_mac.h>
+#include <openssl/pem.h>
+#include <openssl/rand.h>
+#include <openssl/rsa.h>
+
+#include <tss2/tss2_sys.h>
+
+#include "files.h"
+#include "log.h"
+#include "tpm2_alg_util.h"
+#include "tpm2_openssl.h"
+#include "tpm2_util.h"
+
+
+const EVP_MD *tpm2_openssl_halg_from_tpmhalg(TPMI_ALG_HASH algorithm) {
+
+    switch (algorithm) {
+    case TPM2_ALG_SHA1:
+        return EVP_sha1();
+    case TPM2_ALG_SHA256:
+        return EVP_sha256();
+    case TPM2_ALG_SHA384:
+        return EVP_sha384();
+    case TPM2_ALG_SHA512:
+        return EVP_sha512();
+    default:
+        return NULL;
+    }
+    /* no return, not possible */
+}
+
+#if defined(LIB_TPM2_OPENSSL_OPENSSL_PRE11)
+int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) {
+
+    if ((r->n == NULL && n == NULL) || (r->e == NULL && e == NULL)) {
+        return 0;
+    }
+
+    if (n != NULL) {
+        BN_free(r->n);
+        r->n = n;
+    }
+
+    if (e != NULL) {
+        BN_free(r->e);
+        r->e = e;
+    }
+
+    if (d != NULL) {
+        BN_free(r->d);
+        r->d = d;
+    }
+
+    return 1;
+}
+#endif
+
+static inline const char *get_openssl_err(void) {
+    return ERR_error_string(ERR_get_error(), NULL);
+}
+
+
+EVP_CIPHER_CTX *tpm2_openssl_cipher_new(void) {
+    EVP_CIPHER_CTX *ctx;
+#if defined(LIB_TPM2_OPENSSL_OPENSSL_PRE11)
+    ctx = malloc(sizeof(*ctx));
+#else
+    ctx = EVP_CIPHER_CTX_new();
+#endif
+    if (!ctx)
+        return NULL;
+
+#if defined(LIB_TPM2_OPENSSL_OPENSSL_PRE11)
+    EVP_CIPHER_CTX_init(ctx);
+#endif
+
+    return ctx;
+}
+
+void tpm2_openssl_cipher_free(EVP_CIPHER_CTX *ctx) {
+#if defined(LIB_TPM2_OPENSSL_OPENSSL_PRE11)
+    EVP_CIPHER_CTX_cleanup(ctx);
+    free(ctx);
+#else
+    EVP_CIPHER_CTX_free(ctx);
+#endif
+}
+
+digester tpm2_openssl_halg_to_digester(TPMI_ALG_HASH halg) {
+
+    switch(halg) {
+    case TPM2_ALG_SHA1:
+        return SHA1;
+    case TPM2_ALG_SHA256:
+        return SHA256;
+    case TPM2_ALG_SHA384:
+        return SHA384;
+    case TPM2_ALG_SHA512:
+        return SHA512;
+    /* no default */
+    }
+
+    return NULL;
+}
+
+/*
+ * Per man openssl(1), handle the following --passin formats:
+ *     pass:password
+ *             the actual password is password. Since the password is visible to utilities (like 'ps' under Unix) this form should only be used where security is not
+ *             important.
+ *
+ *   env:var   obtain the password from the environment variable var. Since the environment of other processes is visible on certain platforms (e.g. ps under certain
+ *             Unix OSes) this option should be used with caution.
+ *
+ *   file:pathname
+ *             the first line of pathname is the password. If the same pathname argument is supplied to -passin and -passout arguments then the first line will be used
+ *             for the input password and the next line for the output password. pathname need not refer to a regular file: it could for example refer to a device or
+ *             named pipe.
+ *
+ *   fd:number read the password from the file descriptor number. This can be used to send the data via a pipe for example.
+ *
+ *   stdin     read the password from standard input.
+ *
+ */
+
+typedef bool (*pfn_ossl_pw_handler)(const char *passin, char **pass);
diff --git a/lib/tpm2_openssl.h b/lib/tpm2_openssl.h
new file mode 100644
index 00000000000..d749cb350ac
--- /dev/null
+++ b/lib/tpm2_openssl.h
@@ -0,0 +1,108 @@
+//**********************************************************************;
+// Copyright (c) 2017, Intel Corporation
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+// THE POSSIBILITY OF SUCH DAMAGE.
+//**********************************************************************
+
+#ifndef LIB_TPM2_OPENSSL_H_
+#define LIB_TPM2_OPENSSL_H_
+
+#include <tss2/tss2_sys.h>
+
+#include <openssl/err.h>
+#include <openssl/hmac.h>
+#include <openssl/rsa.h>
+
+#if (OPENSSL_VERSION_NUMBER < 0x1010000fL && !defined(LIBRESSL_VERSION_NUMBER)) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) /* OpenSSL 1.1.0 */
+#define LIB_TPM2_OPENSSL_OPENSSL_PRE11
+#endif
+
+
+#if defined(LIB_TPM2_OPENSSL_OPENSSL_PRE11)
+int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d);
+#endif
+
+
+/**
+ * Function prototype for a hashing routine.
+ *
+ * This is a wrapper around OSSL SHA256|384 and etc digesters.
+ *
+ * @param d
+ *  The data to digest.
+ * @param n
+ *  The length of the data to digest.
+ * @param md
+ *  The output message digest.
+ * @return
+ * A pointer to the digest or NULL on error.
+ */
+typedef unsigned char *(*digester)(const unsigned char *d, size_t n, unsigned char *md);
+
+/**
+ * Get an openssl message digest from a tpm hashing algorithm.
+ * @param algorithm
+ *  The tpm algorithm to get the corresponding openssl version of.
+ * @return
+ *  A pointer to a message digester or NULL on failure.
+ */
+const EVP_MD *tpm2_openssl_halg_from_tpmhalg(TPMI_ALG_HASH algorithm);
+
+/**
+ * Obtains an OpenSSL EVP_CIPHER_CTX dealing with version
+ * API changes in OSSL.
+ *
+ * @return
+ *  An Initialized OpenSSL EVP_CIPHER_CTX.
+ */
+EVP_CIPHER_CTX *tpm2_openssl_cipher_new(void);
+
+/**
+ * Free's an EVP_CIPHER_CTX obtained via tpm2_openssl_cipher_new()
+ * dealing with OSSL API version changes.
+ * @param ctx
+ *  The EVP_CIPHER_CTX to free.
+ */
+void tpm2_openssl_cipher_free(EVP_CIPHER_CTX *ctx);
+
+/**
+ * Returns a function pointer capable of performing the
+ * given digest from a TPMI_HASH_ALG.
+ *
+ * @param halg
+ *  The hashing algorithm to use.
+ * @return
+ *  NULL on failure or a valid digester on success.
+ */
+digester tpm2_openssl_halg_to_digester(TPMI_ALG_HASH halg);
+
+typedef enum tpm2_openssl_load_rc tpm2_openssl_load_rc;
+enum tpm2_openssl_load_rc {
+    lprc_error     = 0,      /* an error has occurred */
+    lprc_private   = 1 << 0, /* successfully loaded a private portion of object */
+    lprc_public    = 1 << 1, /* successfully loaded a public portion of object */
+};
+
+
+#endif /* LIB_TPM2_OPENSSL_H_ */
diff --git a/lib/tpm2_options.c b/lib/tpm2_options.c
index c2962ee95d4..8c3f36cacea 100644
--- a/lib/tpm2_options.c
+++ b/lib/tpm2_options.c
@@ -268,9 +268,8 @@ static char* parse_socket_tcti(void) {
 
 static tcti_conf tcti_get_config(const char *optstr) {
 
-    tcti_conf conf = {
-        .name = NULL
-    };
+    /* set up the default configuration */
+    tcti_conf conf = { 0 };
 
     /* no tcti config supplied, get it from env */
     if (!optstr) {
@@ -294,6 +293,11 @@ static tcti_conf tcti_get_config(const char *optstr) {
             }
         }
     } else {
+        /* handle case of TCTI set as "-T none" */
+        if (!strcmp(optstr, "none")) {
+            return conf;
+        }
+
         parse_env_tcti(optstr, &conf);
     }
 
@@ -400,7 +404,7 @@ tpm2_option_code tpm2_handle_options (int argc, char **argv,
      * grep -rn case\ \'[a-zA-Z]\' | awk '{print $3}' | sed s/\'//g | sed s/\://g | sort | uniq | less
      */
     struct option long_options [] = {
-        { "tcti",          required_argument, NULL, 'T' },
+        { "tcti",          optional_argument, NULL, 'T' },
         { "help",          no_argument,       NULL, 'h' },
         { "verbose",       no_argument,       NULL, 'V' },
         { "quiet",         no_argument,       NULL, 'Q' },
@@ -492,17 +496,23 @@ tpm2_option_code tpm2_handle_options (int argc, char **argv,
     if (!tool_opts || !(tool_opts->flags & TPM2_OPTIONS_NO_SAPI)) {
         tcti_conf conf = tcti_get_config(tcti_conf_option);
 
-        *tcti = tpm2_tcti_ldr_load(conf.name, conf.opts);
-        if (!*tcti) {
-            LOG_ERR("Could not load tcti, got: \"%s\"", conf.name);
-            goto out;
-        }
+        /* name can be NULL for optional SAPI tools */
+        if (conf.name) {
+            *tcti = tpm2_tcti_ldr_load(conf.name, conf.opts);
+            if (!*tcti) {
+                LOG_ERR("Could not load tcti, got: \"%s\"", conf.name);
+                goto out;
+            }
 
-        if (!flags->enable_errata) {
-            flags->enable_errata = !!getenv (TPM2TOOLS_ENV_ENABLE_ERRATA);
+            if (!flags->enable_errata) {
+                flags->enable_errata = !!getenv (TPM2TOOLS_ENV_ENABLE_ERRATA);
+            }
+            free(conf.name);
+            free(conf.opts);
+        } else if (!tool_opts || !(tool_opts->flags & TPM2_OPTIONS_OPTIONAL_SAPI)) {
+            LOG_ERR("Requested no tcti, but tool requires TCTI.");
+            goto out;
         }
-        free(conf.name);
-        free(conf.opts);
     }
 
     rc = tpm2_option_code_continue;
diff --git a/lib/tpm2_options.h b/lib/tpm2_options.h
index 860d9b0deee..e16c5205044 100644
--- a/lib/tpm2_options.h
+++ b/lib/tpm2_options.h
@@ -105,6 +105,7 @@ typedef bool (*tpm2_arg_handler)(int argc, char **argv);
  */
 #define TPM2_OPTIONS_SHOW_USAGE 0x1
 #define TPM2_OPTIONS_NO_SAPI 0x2
+#define TPM2_OPTIONS_OPTIONAL_SAPI 0x4
 
 struct tpm2_options {
     struct {
diff --git a/man/common/tcti.md b/man/common/tcti.md
index fd5f1683dfe..0cb06e3c403 100644
--- a/man/common/tcti.md
+++ b/man/common/tcti.md
@@ -18,6 +18,10 @@ The variables respected depend on how the software was configured.
 	* socket - Typically used with the old resource manager, or talking directly to
 	           a simulator.
 	* device - Used when talking directly to a TPM device file.
+	* none   - Do not initialize a connection with the TPM. Some tools allow for off-tpm
+               options and thus support not using a TCTI. Tools that do not support it
+               will error when attempted to be used without a TCTI connection. Does not
+               support *ANY* options and *MUST BE* presented as the exact text of "none".
 
   * _TPM2TOOLS\_DEVICE\_FILE_:
 	When using the device TCTI, specify the TPM device file. The default is
diff --git a/man/tpm2_activatecredential.1.md b/man/tpm2_activatecredential.1.md
index 25478790baf..b15569ae5cf 100644
--- a/man/tpm2_activatecredential.1.md
+++ b/man/tpm2_activatecredential.1.md
@@ -56,7 +56,7 @@ These options control the object verification:
 ```
 tpm2_activatecredential -H 0x81010002 -k 0x81010001 -P abc123 -e abc123 -f <filePath> -o <filePath>
 tpm2_activatecredential -c ak.context -C ek.context -P abc123 -e abc123 -f <filePath> -o <filePath>
-tpm2_activatecredential -H 0x81010002 -k 0x81010001 -P 123abc -e 1a1b1c -X -f <filePath> -o <filePath>
+tpm2_activatecredential -H 0x81010002 -k 0x81010001 -P 123abc -e 1a1b1c -f <filePath> -o <filePath>
 ```
 
 # RETURNS
diff --git a/man/tpm2_certify.1.md b/man/tpm2_certify.1.md
index fba4a8f7c65..f4a78c70c00 100644
--- a/man/tpm2_certify.1.md
+++ b/man/tpm2_certify.1.md
@@ -71,7 +71,7 @@ These options control the ceritifcation:
 ```
 tpm2_certify -H 0x81010002 -k 0x81010001 -P 0x0011 -K 0x00FF -g 0x00B -a <fileName> -s <fileName>
 tpm2_certify -C obj.context -c key.context -P 0x0011 -K 0x00FF -g 0x00B -a <fileName> -s <fileName>
-tpm2_certify -H 0x81010002 -k 0x81010001 -P 0011 -K 00FF -X -g 0x00B -a <fileName> -s <fileName>
+tpm2_certify -H 0x81010002 -k 0x81010001 -P 0011 -K 00FF -g 0x00B -a <fileName> -s <fileName>
 ```
 
 # RETURNS
diff --git a/man/tpm2_create.1.md b/man/tpm2_create.1.md
index f6aed4a7756..51d71db2c75 100644
--- a/man/tpm2_create.1.md
+++ b/man/tpm2_create.1.md
@@ -86,7 +86,7 @@ These options for creating the tpm entity:
 ```
 tpm2_create -H 0x81010001 -P abc123 -K def456 -g sha256 -G keyedhash-I data.File
 tpm2_create -c parent.context -P abc123 -K def456 -g sha256 -G keyedhash -I data.File
-tpm2_create -H 0x81010001 -P 123abc -K 456def -X -g sha256 -G keyedhash -I data.File
+tpm2_create -H 0x81010001 -P 123abc -K 456def -g sha256 -G keyedhash -I data.File
 ```
 
 # RETURNS
diff --git a/man/tpm2_encryptdecrypt.1.md b/man/tpm2_encryptdecrypt.1.md
index ea349bec1e8..350737182dd 100644
--- a/man/tpm2_encryptdecrypt.1.md
+++ b/man/tpm2_encryptdecrypt.1.md
@@ -48,7 +48,7 @@ specified symmetric key.
 ```
 tpm2_encryptdecrypt -k 0x81010001 -P abc123 -D NO -I <filePath> -o <filePath>
 tpm2_encryptdecrypt -c key.context -P abc123 -D NO -I <filePath> -o <filePath>
-tpm2_encryptdecrypt -k 0x81010001 -P 123abca -X -D NO -I <filePath> -o <filePath>
+tpm2_encryptdecrypt -k 0x81010001 -P 123abca -D NO -I <filePath> -o <filePath>
 ```
 
 # RETURNS
diff --git a/man/tpm2_getpubak.1.md b/man/tpm2_getpubak.1.md
index 22ade8f0b33..1a71b49c110 100644
--- a/man/tpm2_getpubak.1.md
+++ b/man/tpm2_getpubak.1.md
@@ -80,7 +80,7 @@ loaded-key:
 
 ```
 tpm2_getpubak -e abc123 -P abc123 -o passwd -E 0x81010001 -k 0x81010002 -f ./ak.pub -n ./ak.name
-tpm2_getpubak -e 1a1b1c -P 123abc -o 1a1b1c -X -E 0x81010001 -k 0x81010002 -f ./ak.pub -n ./ak.name
+tpm2_getpubak -e 1a1b1c -P 123abc -o 1a1b1c -E 0x81010001 -k 0x81010002 -f ./ak.pub -n ./ak.name
 ```
 
 # RETURNS
diff --git a/man/tpm2_makecredential.1.md b/man/tpm2_makecredential.1.md
index 736ead591b8..1008682fe7f 100644
--- a/man/tpm2_makecredential.1.md
+++ b/man/tpm2_makecredential.1.md
@@ -14,7 +14,8 @@ TPM.
 # DESCRIPTION
 
 **tpm2_makecredential**(1) - Use a TPM public key to protect a secret that is used
-to encrypt the AK certififcate.
+to encrypt the AK certificate.  This can be used without a TPM by using 
+the **none** TCTI option.
 
 # OPTIONS
 
diff --git a/test/system/test_tpm2_makecredential.sh b/test/system/test_tpm2_makecredential.sh
index cc920ccae07..84f77b3647b 100755
--- a/test/system/test_tpm2_makecredential.sh
+++ b/test/system/test_tpm2_makecredential.sh
@@ -72,4 +72,6 @@ Loadkeyname=`cat $output_ak_pub_name | xxd -p -c $file_size`
 
 tpm2_makecredential -Q -e $output_ek_pub  -s $file_input_data  -n $Loadkeyname -o $output_mkcredential
 
+tpm2_makecredential -T none -Q -e $output_ek_pub  -s $file_input_data  -n $Loadkeyname -o $output_mkcredential
+
 exit 0
diff --git a/tools/tpm2_makecredential.c b/tools/tpm2_makecredential.c
index c8f49fe0207..259d39f30f3 100644
--- a/tools/tpm2_makecredential.c
+++ b/tools/tpm2_makecredential.c
@@ -1,5 +1,6 @@
 //**********************************************************************;
 // Copyright (c) 2015-2018, Intel Corporation
+// Copyright (c) 2019 Massachusetts Institute of Technology
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
@@ -38,12 +39,15 @@
 
 #include <tss2/tss2_sys.h>
 #include <tss2/tss2_mu.h>
+#include <openssl/rand.h>
 
 #include "files.h"
 #include "tpm2_options.h"
 #include "log.h"
 #include "files.h"
-#include "tpm2_options.h"
+#include "tpm2_alg_util.h"
+#include "tpm2_openssl.h"
+#include "tpm2_identity_util.h"
 #include "tpm2_tool.h"
 #include "tpm2_util.h"
 
@@ -117,6 +121,93 @@ out:
     return result;
 }
 
+static bool make_external_credential_and_save() {
+
+    /*
+     * Get name_alg from the public key
+     */
+    TPMI_ALG_HASH name_alg = ctx.public.publicArea.nameAlg;
+
+
+    /* 
+     * Generate and encrypt seed
+     */
+    TPM2B_DIGEST seed = TPM2B_TYPE_INIT(TPM2B_DIGEST, buffer);
+    seed.size = tpm2_alg_util_get_hash_size(name_alg);
+    RAND_bytes(seed.buffer, seed.size);
+
+    TPM2B_ENCRYPTED_SECRET encrypted_seed = TPM2B_EMPTY_INIT;
+    unsigned char label[10] = { 'I', 'D', 'E', 'N', 'T', 'I', 'T', 'Y', 0 };
+    bool res = tpm2_identity_util_encrypt_seed_with_public_key(&seed,
+            &ctx.public, label, 9,
+            &encrypted_seed);
+    if (!res) {
+        LOG_ERR("Failed Seed Encryption\n");
+        return false;
+    }
+
+    /* 
+     * Perform identity structure calculations (off of the TPM)
+     */
+    TPM2B_MAX_BUFFER hmac_key;
+    TPM2B_MAX_BUFFER enc_key;
+    tpm2_identity_util_calc_outer_integrity_hmac_key_and_dupsensitive_enc_key(
+            &ctx.public,
+            &ctx.object_name,
+            &seed,
+            &hmac_key,
+            &enc_key);
+
+    /*
+     * The ctx.credential needs to be marshalled into struct with 
+     * both size and contents together (to be encrypted as a block)
+     */
+    TPM2B_MAX_BUFFER marshalled_inner_integrity = TPM2B_EMPTY_INIT;
+    marshalled_inner_integrity.size = ctx.credential.size + sizeof(ctx.credential.size);
+    UINT16 credSize = ctx.credential.size;
+    if (!tpm2_util_is_big_endian()) {
+        credSize = tpm2_util_endian_swap_16(credSize);
+    }
+    memcpy(marshalled_inner_integrity.buffer, &credSize, sizeof(credSize));
+    memcpy(&marshalled_inner_integrity.buffer[2], ctx.credential.buffer, ctx.credential.size);
+
+    /*
+     * Perform inner encryption (encIdentity) and outer HMAC (outerHMAC)
+     */
+    TPM2B_DIGEST outer_hmac = TPM2B_EMPTY_INIT;
+    TPM2B_MAX_BUFFER encrypted_sensitive = TPM2B_EMPTY_INIT;
+    tpm2_identity_util_calculate_outer_integrity(
+            name_alg,
+            &ctx.object_name,
+            &marshalled_inner_integrity,
+            &hmac_key,
+            &enc_key,
+            &ctx.public.publicArea.parameters.rsaDetail.symmetric,
+            &encrypted_sensitive,
+            &outer_hmac);
+
+    /*
+     * Package up the info to save
+     * cred_bloc = outer_hmac || encrypted_sensitive
+     * secret = encrypted_seed (with pubEK)
+     */
+    TPM2B_ID_OBJECT cred_blob = TPM2B_TYPE_INIT(TPM2B_ID_OBJECT, credential);
+
+    UINT16 outer_hmac_size = outer_hmac.size;
+    if (!tpm2_util_is_big_endian()) {
+        outer_hmac_size = tpm2_util_endian_swap_16(outer_hmac_size);
+    }
+    int offset = 0;
+    memcpy(cred_blob.credential + offset, &outer_hmac_size, sizeof(outer_hmac.size));offset += sizeof(outer_hmac.size);
+    memcpy(cred_blob.credential + offset, outer_hmac.buffer, outer_hmac.size);offset += outer_hmac.size;
+    //NOTE: do NOT include the encrypted_sensitive size, since it is encrypted with the blob!
+    memcpy(cred_blob.credential + offset, encrypted_sensitive.buffer, encrypted_sensitive.size);
+
+    cred_blob.size = outer_hmac.size + encrypted_sensitive.size + sizeof(outer_hmac.size);
+
+    return write_cred_and_secret(ctx.out_file_path, &cred_blob, &encrypted_seed);
+}
+
 static bool make_credential_and_save(TSS2_SYS_CONTEXT *sapi_context)
 {
     TSS2L_SYS_AUTH_RESPONSE sessions_data_out;
@@ -198,7 +289,8 @@ bool tpm2_tool_onstart(tpm2_options **opts) {
     };
 
     *opts = tpm2_options_new("e:s:n:o:", ARRAY_LEN(topts), topts,
-            on_option, NULL, TPM2_OPTIONS_SHOW_USAGE);
+            on_option, NULL, 
+            TPM2_OPTIONS_SHOW_USAGE | TPM2_OPTIONS_OPTIONAL_SAPI);
 
     return *opts != NULL;
 }
@@ -212,5 +304,10 @@ int tpm2_tool_onrun(TSS2_SYS_CONTEXT *sapi_context, tpm2_option_flags flags) {
         return false;
     }
 
-    return make_credential_and_save(sapi_context) != true;
+    printf("make credential has SAPI CTX: %p", sapi_context);
+
+    bool result = sapi_context ? make_credential_and_save(sapi_context) : 
+            make_external_credential_and_save();
+
+    return result != true;
 }
-- 
2.21.0