libtpmtss: Load X.509 certificates from TPM 2.0 NV RAM
authorAndreas Steffen <andreas.steffen@strongswan.org>
Tue, 5 Dec 2017 16:08:55 +0000 (17:08 +0100)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Tue, 5 Dec 2017 20:31:31 +0000 (21:31 +0100)
src/libtpmtss/plugins/tpm/Makefile.am
src/libtpmtss/plugins/tpm/tpm_cert.c [new file with mode: 0644]
src/libtpmtss/plugins/tpm/tpm_cert.h [new file with mode: 0644]
src/libtpmtss/plugins/tpm/tpm_plugin.c
src/libtpmtss/tpm_tss.h
src/libtpmtss/tpm_tss_trousers.c
src/libtpmtss/tpm_tss_tss2.c

index 2812810..27db5cc 100644 (file)
@@ -15,6 +15,7 @@ endif
 
 libstrongswan_tpm_la_SOURCES = \
        tpm_plugin.h tpm_plugin.c \
+       tpm_cert.h tpm_cert.c \
        tpm_private_key.h tpm_private_key.c \
        tpm_rng.h tpm_rng.c
 
diff --git a/src/libtpmtss/plugins/tpm/tpm_cert.c b/src/libtpmtss/plugins/tpm/tpm_cert.c
new file mode 100644 (file)
index 0000000..248da7e
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2017 Andreas Steffen
+ * HSR Hochschule für Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "tpm_cert.h"
+
+#include <tpm_tss.h>
+
+#include <utils/debug.h>
+
+
+/**
+ * See header.
+ */
+certificate_t *tpm_cert_load(certificate_type_t type, va_list args)
+{
+       tpm_tss_t *tpm;
+       chunk_t keyid = chunk_empty, pin = chunk_empty, data = chunk_empty;
+       certificate_t *cert;
+       char handle_str[4];
+       size_t len;
+       uint32_t hierarchy = 0x40000001;  /* TPM_RH_OWNER */
+       uint32_t handle;
+       bool success;
+
+       while (TRUE)
+       {
+               switch (va_arg(args, builder_part_t))
+               {
+                       case BUILD_PKCS11_KEYID:
+                               keyid = va_arg(args, chunk_t);
+                               continue;
+                       case BUILD_PKCS11_SLOT:
+                               hierarchy = va_arg(args, int);
+                               continue;
+                       case BUILD_PKCS11_MODULE:
+                               va_arg(args, char*);
+                               continue;
+                       case BUILD_END:
+                               break;
+                       default:
+                               return NULL;
+               }
+               break;
+       }
+
+       /* convert keyid into 32 bit TPM key object handle */
+       if (!keyid.len)
+       {
+               return NULL;
+       }
+       len = min(keyid.len, 4);
+       memset(handle_str, 0x00, 4);
+       memcpy(handle_str + 4 - len, keyid.ptr + keyid.len - len, len);
+       handle = untoh32(handle_str);
+
+       /* try to find a TPM 2.0 */
+       tpm = tpm_tss_probe(TPM_VERSION_2_0);
+       if (!tpm)
+       {
+               DBG1(DBG_LIB, "no TPM 2.0 found");
+               return NULL;
+       }
+       success = tpm->get_data(tpm, hierarchy, handle, pin, &data);
+       tpm->destroy(tpm);
+
+       if (!success)
+       {
+               DBG1(DBG_LIB, "loading certificate from TPM NV index 0x%08x failed",
+                                          handle);
+               return NULL;
+       }
+       cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
+                                                         BUILD_BLOB_ASN1_DER, data, BUILD_END);
+       free(data.ptr);
+
+       if (!cert)
+       {
+               DBG1(DBG_LIB, "parsing certificate from TPM NV index 0x%08x failed",
+                                          handle);
+               return NULL;
+       }
+       DBG1(DBG_LIB, "loaded certificate from TPM NV index 0x%08x", handle);
+
+       return cert;
+}
diff --git a/src/libtpmtss/plugins/tpm/tpm_cert.h b/src/libtpmtss/plugins/tpm/tpm_cert.h
new file mode 100644 (file)
index 0000000..a6cb345
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 Andreas Steffen
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup tpm_cert tpm_cert
+ * @{ @ingroup tpm
+ */
+
+#ifndef TPM_CERT_H_
+#define TPM_CERT_H_
+
+#include <credentials/certificates/certificate.h>
+
+/**
+ * Load a specific certificate from a TPM
+ *
+ * Requires a BUILD_PKCS11_KEYID argument, and optionally a BUILD_PKCS11_SLOT
+ * to designate the NV storage hierarchy.
+ *
+ * @param type                 certificate type, must be CERT_X509
+ * @param args                 variable argument list, containing BUILD_PKCS11_KEYID.
+ * @return                             loaded certificate, or NULL on failure
+ */
+certificate_t *tpm_cert_load(certificate_type_t type, va_list args);
+
+#endif /** TPM_CERT_H_ @}*/
index b9a4c12..e988998 100644 (file)
@@ -15,6 +15,7 @@
 
 #include "tpm_plugin.h"
 #include "tpm_private_key.h"
+#include "tpm_cert.h"
 #include "tpm_rng.h"
 
 #include <library.h>
@@ -50,13 +51,19 @@ METHOD(plugin_t, get_features, int,
                PLUGIN_REGISTER(PRIVKEY, tpm_private_key_connect, FALSE),
                        PLUGIN_PROVIDE(PRIVKEY, KEY_ANY),
        };
-       static plugin_feature_t f[countof(f_rng) + countof(f_privkey)] = {};
-
+       static plugin_feature_t f_cert[] = {
+               PLUGIN_REGISTER(CERT_DECODE, tpm_cert_load, FALSE),
+                       PLUGIN_PROVIDE(CERT_DECODE, CERT_X509),
+                               PLUGIN_DEPENDS(CERT_DECODE, CERT_X509),
+       };
+       static plugin_feature_t f[countof(f_rng) + countof(f_privkey) +
+                                                         countof(f_cert)] = {};
        static int count = 0;
 
        if (!count)
        {
                plugin_features_add(f, f_privkey, countof(f_privkey), &count);
+               plugin_features_add(f, f_cert, countof(f_cert), &count);
 
                if (lib->settings->get_bool(lib->settings,
                                                                "%s.plugins.tpm.use_rng", FALSE, lib->ns))
index f408d04..bcb7ab9 100644 (file)
@@ -144,6 +144,18 @@ struct tpm_tss_t {
        bool (*get_random)(tpm_tss_t *this, size_t bytes, uint8_t *buffer);
 
        /**
+        * Get a data blob from TPM NV store using its object handle (TPM 2.0 only)
+        *
+        * @param handle                object handle of TPM key to be used for signature
+        * @param hierarchy             hierarchy the TPM key object is attached to
+        * @param pin                   PIN code or empty chunk
+        * @param data                  returns data blob
+        * @return                              TRUE if data retrieval succeeded
+        */
+       bool (*get_data)(tpm_tss_t *this, uint32_t hierarchy, uint32_t handle,
+                                        chunk_t pin, chunk_t *data);
+
+       /**
         * Destroy a tpm_tss_t.
         */
        void (*destroy)(tpm_tss_t *this);
index d5bc2b8..6ed57af 100644 (file)
@@ -595,6 +595,13 @@ METHOD(tpm_tss_t, get_random, bool,
        return FALSE;
 }
 
+METHOD(tpm_tss_t, get_data, bool,
+       private_tpm_tss_trousers_t *this, uint32_t hierarchy, uint32_t handle,
+       chunk_t pin, chunk_t *data)
+{
+       return FALSE;
+}
+
 METHOD(tpm_tss_t, destroy, void,
        private_tpm_tss_trousers_t *this)
 {
@@ -639,6 +646,7 @@ tpm_tss_t *tpm_tss_trousers_create()
                                .quote = _quote,
                                .sign = _sign,
                                .get_random = _get_random,
+                               .get_data = _get_data,
                                .destroy = _destroy,
                        },
                        .load_aik = _load_aik,
index 0136c27..2e33589 100644 (file)
@@ -954,6 +954,78 @@ METHOD(tpm_tss_t, get_random, bool,
        return TRUE;
 }
 
+METHOD(tpm_tss_t, get_data, bool,
+       private_tpm_tss_tss2_t *this, uint32_t hierarchy, uint32_t handle,
+       chunk_t pin, chunk_t *data)
+{
+       uint16_t nv_size, nv_offset = 0;
+       uint32_t rval;
+
+       TPM2B_NAME nv_name = { { sizeof(TPM2B_NAME)-2, } };
+       TPM2B_NV_PUBLIC nv_public = { { 0, } };
+       TPM2B_MAX_NV_BUFFER nv_data = { { sizeof(TPM2B_MAX_NV_BUFFER)-2, } };
+       TPMS_AUTH_COMMAND  session_data_cmd;
+       TPMS_AUTH_RESPONSE session_data_rsp;
+       TSS2_SYS_CMD_AUTHS sessions_data_cmd;
+       TSS2_SYS_RSP_AUTHS sessions_data_rsp;
+       TPMS_AUTH_COMMAND  *session_data_cmd_array[1];
+       TPMS_AUTH_RESPONSE *session_data_rsp_array[1];
+
+       /* get size of NV object */
+       rval = Tss2_Sys_NV_ReadPublic(this->sys_context, handle, 0, &nv_public,
+                                                                                                                               &nv_name, 0);
+       if (rval != TPM_RC_SUCCESS)
+       {
+               DBG1(DBG_PTS,"%s Tss2_Sys_NV_ReadPublic failed: 0x%06x", LABEL, rval);
+               return FALSE;
+       }
+       nv_size = nv_public.t.nvPublic.dataSize;
+       *data = chunk_alloc(nv_size);
+
+       /*prepare NV read session */
+       session_data_cmd_array[0] = &session_data_cmd;
+       session_data_rsp_array[0] = &session_data_rsp;
+
+       sessions_data_cmd.cmdAuths = &session_data_cmd_array[0];
+       sessions_data_rsp.rspAuths = &session_data_rsp_array[0];
+
+       sessions_data_cmd.cmdAuthsCount = 1;
+       sessions_data_rsp.rspAuthsCount = 1;
+
+       session_data_cmd.sessionHandle = TPM_RS_PW;
+       session_data_cmd.nonce.t.size = 0;
+       session_data_cmd.hmac.t.size = 0;
+
+       if (pin.len > 0)
+       {
+               session_data_cmd.hmac.t.size = min(sizeof(session_data_cmd.hmac.t) - 2,
+                                                                                  pin.len);
+               memcpy(session_data_cmd.hmac.t.buffer, pin.ptr,
+                          session_data_cmd.hmac.t.size);
+       }
+       *( (uint8_t *)((void *)&session_data_cmd.sessionAttributes ) ) = 0;
+
+       /* read NV data an NV buffer block at a time */
+       while (nv_size > 0)
+       {
+               rval = Tss2_Sys_NV_Read(this->sys_context, hierarchy, handle,
+                                       &sessions_data_cmd, min(nv_size, MAX_NV_BUFFER_SIZE),
+                                       nv_offset, &nv_data, &sessions_data_rsp);
+
+               if (rval != TPM_RC_SUCCESS)
+               {
+                       DBG1(DBG_PTS,"%s Tss2_Sys_NV_Read failed: 0x%06x", LABEL, rval);
+                       chunk_free(data);
+                       return FALSE;
+               }
+               memcpy(data->ptr + nv_offset, nv_data.t.buffer, nv_data.t.size);
+               nv_offset += nv_data.t.size;
+               nv_size   -= nv_data.t.size;
+       }
+
+       return TRUE;
+}
+
 METHOD(tpm_tss_t, destroy, void,
        private_tpm_tss_tss2_t *this)
 {
@@ -980,6 +1052,7 @@ tpm_tss_t *tpm_tss_tss2_create()
                        .quote = _quote,
                        .sign = _sign,
                        .get_random = _get_random,
+                       .get_data = _get_data,
                        .destroy = _destroy,
                },
        );