diff options
Diffstat (limited to 'lib/tpm.c')
-rw-r--r-- | lib/tpm.c | 1875 |
1 files changed, 1875 insertions, 0 deletions
diff --git a/lib/tpm.c b/lib/tpm.c new file mode 100644 index 0000000..46b1ca9 --- /dev/null +++ b/lib/tpm.c @@ -0,0 +1,1875 @@ +/* + * OpenConnect (SSL + DTLS) VPN client + * + * Copyright © 2012 Free Software Foundation. + * Copyright © 2008-2012 Intel Corporation. + * Copyright © 2015-2016 Red Hat, Inc. + * + * Author: David Woodhouse <dwmw2@infradead.org> + * Author: Nikos Mavrogiannopoulos + * + * GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +/* + * TPM code based on client-tpm.c from + * Carolin Latze <latze@angry-red-pla.net> and Tobias Soder + */ + +#include <config.h> +#include <gnutls/gnutls.h> +#include <gnutls/abstract.h> +#include <gnutls/tpm.h> +#include "gnutls_int.h" + +#ifdef HAVE_TROUSERS + +#include "errors.h" +#include <pkcs11_int.h> +#include <x509/common.h> +#include <x509_b64.h> +#include <random.h> +#include <pin.h> +#include <c-ctype.h> + +#include <dlfcn.h> +#include <trousers/tss.h> +#include <trousers/trousers.h> + +typedef char *(*Trspi_Error_Layer_func)(TSS_RESULT); +typedef char *(*Trspi_Error_String_func)(TSS_RESULT); +typedef TSS_RESULT (*Trspi_Error_Code_func)(TSS_RESULT); + +typedef TSS_RESULT (*Tspi_Context_CloseObject_func)(TSS_HCONTEXT, TSS_HOBJECT); +typedef TSS_RESULT (*Tspi_Context_Close_func)(TSS_HCONTEXT); +typedef TSS_RESULT (*Tspi_Context_CreateObject_func)(TSS_HCONTEXT, TSS_FLAG, TSS_FLAG, TSS_HOBJECT*); +typedef TSS_RESULT (*Tspi_Context_FreeMemory_func)(TSS_HCONTEXT, BYTE*); +typedef TSS_RESULT (*Tspi_Context_GetTpmObject_func)(TSS_HCONTEXT, TSS_HTPM*); +typedef TSS_RESULT (*Tspi_Context_LoadKeyByUUID_func)(TSS_HCONTEXT, TSS_FLAG, TSS_UUID, TSS_HKEY*); +typedef TSS_RESULT (*Tspi_Context_RegisterKey_func)(TSS_HCONTEXT, TSS_HKEY, TSS_FLAG, TSS_UUID, TSS_FLAG, TSS_UUID); +typedef TSS_RESULT (*Tspi_Context_UnregisterKey_func)(TSS_HCONTEXT, TSS_FLAG, TSS_UUID, TSS_HKEY*); +typedef TSS_RESULT (*Tspi_Key_CreateKey_func)(TSS_HKEY, TSS_HKEY, TSS_HPCRS); +typedef TSS_RESULT (*Tspi_Hash_SetHashValue_func)(TSS_HHASH, UINT32, BYTE*); +typedef TSS_RESULT (*Tspi_Hash_Sign_func)(TSS_HHASH, TSS_HKEY, UINT32*, BYTE**); +typedef TSS_RESULT (*Tspi_Policy_SetSecret_func)(TSS_HPOLICY, TSS_FLAG, UINT32, BYTE*); +typedef TSS_RESULT (*Tspi_Context_Create_func)(TSS_HCONTEXT*); +typedef TSS_RESULT (*Tspi_Context_Connect_func)(TSS_HCONTEXT, TSS_UNICODE*); +typedef TSS_RESULT (*Tspi_GetPolicyObject_func)(TSS_HOBJECT, TSS_FLAG, TSS_HPOLICY*); +typedef TSS_RESULT (*Tspi_DecodeBER_TssBlob_func)(UINT32, BYTE*, UINT32*, UINT32*, BYTE*); +typedef TSS_RESULT (*Tspi_Context_LoadKeyByBlob_func)(TSS_HCONTEXT, TSS_HKEY, UINT32, BYTE*, TSS_HKEY*); +typedef TSS_RESULT (*Tspi_Policy_AssignToObject_func)(TSS_HPOLICY, TSS_HOBJECT); +typedef TSS_RESULT (*Tspi_GetAttribData_func)(TSS_HOBJECT, TSS_FLAG, TSS_FLAG, UINT32*, BYTE**); +typedef TSS_RESULT (*Tspi_GetAttribUint32_func)(TSS_HOBJECT, TSS_FLAG, TSS_FLAG, UINT32*); +typedef TSS_RESULT (*Tspi_TPM_StirRandom_func)(TSS_HTPM, UINT32, BYTE*); +typedef TSS_RESULT (*Tspi_SetAttribUint32_func)(TSS_HOBJECT, TSS_FLAG, TSS_FLAG, UINT32); +typedef TSS_RESULT (*Tspi_EncodeDER_TssBlob_func)(UINT32, BYTE*, UINT32, UINT32*, BYTE*); +typedef TSS_RESULT (*Tspi_Context_GetRegisteredKeysByUUID2_func)(TSS_HCONTEXT, TSS_FLAG, TSS_UUID*, UINT32*, TSS_KM_KEYINFO2**); + +static Tspi_Context_CloseObject_func pTspi_Context_CloseObject; +static Tspi_Context_Close_func pTspi_Context_Close; +static Tspi_Context_CreateObject_func pTspi_Context_CreateObject; +static Tspi_Context_FreeMemory_func pTspi_Context_FreeMemory; +static Tspi_Context_GetTpmObject_func pTspi_Context_GetTpmObject; +static Tspi_Context_LoadKeyByUUID_func pTspi_Context_LoadKeyByUUID; +static Tspi_Context_RegisterKey_func pTspi_Context_RegisterKey; +static Tspi_Context_UnregisterKey_func pTspi_Context_UnregisterKey; +static Tspi_Key_CreateKey_func pTspi_Key_CreateKey; +static Tspi_Hash_SetHashValue_func pTspi_Hash_SetHashValue; +static Tspi_Hash_Sign_func pTspi_Hash_Sign; +static Tspi_Policy_SetSecret_func pTspi_Policy_SetSecret; +static Tspi_Context_Create_func pTspi_Context_Create; +static Tspi_Context_Connect_func pTspi_Context_Connect; +static Tspi_GetPolicyObject_func pTspi_GetPolicyObject; +static Tspi_DecodeBER_TssBlob_func pTspi_DecodeBER_TssBlob; +static Tspi_Context_LoadKeyByBlob_func pTspi_Context_LoadKeyByBlob; +static Tspi_Policy_AssignToObject_func pTspi_Policy_AssignToObject; +static Tspi_GetAttribData_func pTspi_GetAttribData; +static Tspi_GetAttribUint32_func pTspi_GetAttribUint32; +static Tspi_Context_GetTpmObject_func pTspi_Context_GetTpmObject; +static Tspi_TPM_StirRandom_func pTspi_TPM_StirRandom; +static Tspi_SetAttribUint32_func pTspi_SetAttribUint32; +static Tspi_EncodeDER_TssBlob_func pTspi_EncodeDER_TssBlob; +static Tspi_Context_GetRegisteredKeysByUUID2_func pTspi_Context_GetRegisteredKeysByUUID2; + +static Trspi_Error_Layer_func pTrspi_Error_Layer; +static Trspi_Error_String_func pTrspi_Error_String; +static Trspi_Error_Code_func pTrspi_Error_Code; + +static void *tpm_dl = NULL; + +#define _DLSYM(dl, sym) \ + p##sym = dlsym(dl, #sym); \ + if (p##sym == NULL) { \ + dlclose(dl); \ + dl = NULL; \ + return -1; \ + } + +static int check_init(void) +{ + if (tpm_dl == NULL) { + tpm_dl = dlopen(TROUSERS_LIB, RTLD_LAZY); + if (tpm_dl == NULL) { + _gnutls_debug_log("couldn't open %s\n", TROUSERS_LIB); + return -1; + } + + _DLSYM(tpm_dl,Tspi_Context_CloseObject); + _DLSYM(tpm_dl,Tspi_Context_Close); + _DLSYM(tpm_dl,Tspi_Context_CreateObject); + _DLSYM(tpm_dl,Tspi_Context_FreeMemory); + _DLSYM(tpm_dl,Tspi_Context_GetTpmObject); + _DLSYM(tpm_dl,Tspi_Context_LoadKeyByUUID); + _DLSYM(tpm_dl,Tspi_Context_RegisterKey); + _DLSYM(tpm_dl,Tspi_Context_UnregisterKey); + _DLSYM(tpm_dl,Tspi_Key_CreateKey); + _DLSYM(tpm_dl,Tspi_Hash_SetHashValue); + _DLSYM(tpm_dl,Tspi_Hash_Sign); + _DLSYM(tpm_dl,Tspi_Policy_SetSecret); + _DLSYM(tpm_dl,Tspi_Context_Create); + _DLSYM(tpm_dl,Tspi_Context_Connect); + _DLSYM(tpm_dl,Tspi_GetPolicyObject); + _DLSYM(tpm_dl,Tspi_DecodeBER_TssBlob); + _DLSYM(tpm_dl,Tspi_Context_LoadKeyByBlob); + _DLSYM(tpm_dl,Tspi_Policy_AssignToObject); + _DLSYM(tpm_dl,Tspi_GetAttribData); + _DLSYM(tpm_dl,Tspi_GetAttribUint32); + _DLSYM(tpm_dl,Tspi_Context_GetTpmObject); + _DLSYM(tpm_dl,Tspi_TPM_StirRandom); + _DLSYM(tpm_dl,Tspi_SetAttribUint32); + _DLSYM(tpm_dl,Tspi_EncodeDER_TssBlob); + _DLSYM(tpm_dl,Tspi_Context_GetRegisteredKeysByUUID2); + + _DLSYM(tpm_dl,Trspi_Error_Layer); + _DLSYM(tpm_dl,Trspi_Error_String); + _DLSYM(tpm_dl,Trspi_Error_Code); + } + + return 0; +} + +#define CHECK_INIT \ + if (check_init() < 0) return gnutls_assert_val(GNUTLS_E_TPM_NO_LIB) + +#define CHECK_INIT_VOID \ + if (check_init() < 0) return + +void _gnutls_tpm_global_deinit(void) +{ + if (tpm_dl) { + dlclose(tpm_dl); + tpm_dl = NULL; + } +} + +struct tpm_ctx_st { + TSS_HCONTEXT tpm_ctx; + TSS_HKEY tpm_key; + TSS_HPOLICY tpm_key_policy; + TSS_HKEY srk; + TSS_HPOLICY srk_policy; +}; + +struct tpm_key_list_st { + UINT32 size; + TSS_KM_KEYINFO2 *ki; + TSS_HCONTEXT tpm_ctx; +}; + +static void tpm_close_session(struct tpm_ctx_st *s); +static int import_tpm_key(gnutls_privkey_t pkey, + const gnutls_datum_t * fdata, + gnutls_tpmkey_fmt_t format, + TSS_UUID * uuid, + TSS_FLAG storage_type, + const char *srk_password, + const char *key_password); +static int encode_tpmkey_url(char **url, const TSS_UUID * uuid, + TSS_FLAG storage); + +/* TPM URL format: (draft-mavrogiannopoulos-tpmuri-01) + * + * tpmkey:file=/path/to/file + * tpmkey:uuid=7f468c16-cb7f-11e1-824d-b3a4f4b20343;storage=user + * tpmkey:uuid=7f468c16-cb7f-11e1-824d-b3a4f4b20343;storage=system + * + */ + + +static int tss_err_pwd(TSS_RESULT err, int pwd_error) +{ + _gnutls_debug_log("TPM (%s) error: %s (%x)\n", + pTrspi_Error_Layer(err), pTrspi_Error_String(err), + (unsigned int) pTrspi_Error_Code(err)); + + switch (ERROR_LAYER(err)) { + case TSS_LAYER_TPM: + switch (ERROR_CODE(err)) { + case TPM_E_AUTHFAIL: + return pwd_error; + case TPM_E_NOSRK: + return GNUTLS_E_TPM_UNINITIALIZED; + default: + return gnutls_assert_val(GNUTLS_E_TPM_ERROR); + } + case TSS_LAYER_TCS: + case TSS_LAYER_TSP: + switch (ERROR_CODE(err)) { + case TSS_E_COMM_FAILURE: + case TSS_E_NO_CONNECTION: + case TSS_E_CONNECTION_FAILED: + case TSS_E_CONNECTION_BROKEN: + return GNUTLS_E_TPM_SESSION_ERROR; + case TSS_E_PS_KEY_NOTFOUND: + return GNUTLS_E_TPM_KEY_NOT_FOUND; + default: + return gnutls_assert_val(GNUTLS_E_TPM_ERROR); + } + default: + return gnutls_assert_val(GNUTLS_E_TPM_ERROR); + } +} + +#define tss_err(x) tss_err_pwd(x, GNUTLS_E_TPM_SRK_PASSWORD_ERROR) +#define tss_err_key(x) tss_err_pwd(x, GNUTLS_E_TPM_KEY_PASSWORD_ERROR) + +static void tpm_deinit_fn(gnutls_privkey_t key, void *_s) +{ + struct tpm_ctx_st *s = _s; + + pTspi_Context_CloseObject(s->tpm_ctx, s->tpm_key_policy); + pTspi_Context_CloseObject(s->tpm_ctx, s->tpm_key); + + tpm_close_session(s); + gnutls_free(s); +} + +static int +tpm_sign_fn(gnutls_privkey_t key, void *_s, + const gnutls_datum_t * data, gnutls_datum_t * sig) +{ + struct tpm_ctx_st *s = _s; + TSS_HHASH hash; + int err; + + _gnutls_debug_log("TPM sign function called for %u bytes.\n", + data->size); + + err = + pTspi_Context_CreateObject(s->tpm_ctx, + TSS_OBJECT_TYPE_HASH, TSS_HASH_OTHER, + &hash); + if (err) { + gnutls_assert(); + _gnutls_debug_log("Failed to create TPM hash object: %s\n", + pTrspi_Error_String(err)); + return GNUTLS_E_PK_SIGN_FAILED; + } + err = pTspi_Hash_SetHashValue(hash, data->size, data->data); + if (err) { + gnutls_assert(); + _gnutls_debug_log + ("Failed to set value in TPM hash object: %s\n", + pTrspi_Error_String(err)); + pTspi_Context_CloseObject(s->tpm_ctx, hash); + return GNUTLS_E_PK_SIGN_FAILED; + } + err = pTspi_Hash_Sign(hash, s->tpm_key, &sig->size, &sig->data); + pTspi_Context_CloseObject(s->tpm_ctx, hash); + if (err) { + if (s->tpm_key_policy || err != TPM_E_AUTHFAIL) + _gnutls_debug_log + ("TPM hash signature failed: %s\n", + pTrspi_Error_String(err)); + if (err == TPM_E_AUTHFAIL) + return GNUTLS_E_TPM_KEY_PASSWORD_ERROR; + else + return GNUTLS_E_PK_SIGN_FAILED; + } + return 0; +} + +static const unsigned char nullpass[20]; +static const gnutls_datum_t nulldata = { (void *) nullpass, 20 }; + +const TSS_UUID srk_uuid = TSS_UUID_SRK; + +static int tpm_pin(struct pin_info_st *pin_info, const TSS_UUID * uuid, + TSS_FLAG storage, char *pin, unsigned int pin_size, + unsigned int attempts) +{ + unsigned int flags = 0; + const char *label; + char *url = NULL; + int ret; + + if (attempts > 0) + flags |= GNUTLS_PIN_WRONG; + + if (uuid) { + if (memcmp(uuid, &srk_uuid, sizeof(TSS_UUID)) == 0) { + label = "SRK"; + + ret = encode_tpmkey_url(&url, uuid, storage); + if (ret < 0) + return gnutls_assert_val(ret); + } else { + label = "TPM"; + + ret = encode_tpmkey_url(&url, uuid, storage); + if (ret < 0) + return gnutls_assert_val(ret); + } + } else + label = "unknown"; + + ret = _gnutls_retrieve_pin(pin_info, url, label, flags, pin, pin_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_free(url); + return ret; +} + + +static TSS_RESULT myTspi_Policy_SetSecret(TSS_HPOLICY hPolicy, + UINT32 ulSecretLength, + BYTE * rgbSecret) +{ + if (rgbSecret == NULL) { + /* Well known NULL key */ + return pTspi_Policy_SetSecret(hPolicy, + TSS_SECRET_MODE_SHA1, + sizeof(nullpass), + (BYTE *) nullpass); + } else { /* key is given */ + + return pTspi_Policy_SetSecret(hPolicy, + TSS_SECRET_MODE_PLAIN, + ulSecretLength, rgbSecret); + } +} + +#define SAFE_LEN(x) (x==NULL?0:strlen(x)) + +static int tpm_open_session(struct tpm_ctx_st *s, const char *_srk_password, unsigned allow_invalid_pass) +{ + int err, ret; + char *password = NULL; + + err = pTspi_Context_Create(&s->tpm_ctx); + if (err) { + gnutls_assert(); + return tss_err(err); + } + + if (_srk_password != NULL) { + gnutls_datum_t pout; + ret = _gnutls_utf8_password_normalize(_srk_password, strlen(_srk_password), &pout, allow_invalid_pass); + if (ret < 0) { + gnutls_assert(); + goto out_tspi_ctx; + } + password = (char*)pout.data; + } + + err = pTspi_Context_Connect(s->tpm_ctx, NULL); + if (err) { + gnutls_assert(); + ret = tss_err(err); + goto out_tspi_ctx; + } + + err = + pTspi_Context_LoadKeyByUUID(s->tpm_ctx, TSS_PS_TYPE_SYSTEM, + srk_uuid, &s->srk); + if (err) { + gnutls_assert(); + ret = tss_err(err); + goto out_tspi_ctx; + } + + err = + pTspi_GetPolicyObject(s->srk, TSS_POLICY_USAGE, &s->srk_policy); + if (err) { + gnutls_assert(); + ret = tss_err(err); + goto out_srk; + } + + err = myTspi_Policy_SetSecret(s->srk_policy, + SAFE_LEN(password), + (BYTE *) password); + if (err) { + gnutls_assert(); + ret = tss_err(err); + goto out_srkpol; + } + gnutls_free(password); + + return 0; + + out_srkpol: + pTspi_Context_CloseObject(s->tpm_ctx, s->srk_policy); + s->srk_policy = 0; + out_srk: + pTspi_Context_CloseObject(s->tpm_ctx, s->srk); + s->srk = 0; + out_tspi_ctx: + pTspi_Context_Close(s->tpm_ctx); + s->tpm_ctx = 0; + gnutls_free(password); + return ret; + +} + +static void tpm_close_session(struct tpm_ctx_st *s) +{ + pTspi_Context_CloseObject(s->tpm_ctx, s->srk_policy); + s->srk_policy = 0; + pTspi_Context_CloseObject(s->tpm_ctx, s->srk); + s->srk = 0; + pTspi_Context_Close(s->tpm_ctx); + s->tpm_ctx = 0; +} + +static int +import_tpm_key_cb(gnutls_privkey_t pkey, const gnutls_datum_t * fdata, + gnutls_tpmkey_fmt_t format, TSS_UUID * uuid, + TSS_FLAG storage, const char *srk_password, + const char *key_password) +{ + unsigned int attempts = 0; + char pin1[GNUTLS_PKCS11_MAX_PIN_LEN]; + char pin2[GNUTLS_PKCS11_MAX_PIN_LEN]; + int ret, ret2; + + do { + ret = + import_tpm_key(pkey, fdata, format, uuid, storage, + srk_password, key_password); + + if (attempts > 3) + break; + + if (ret == GNUTLS_E_TPM_SRK_PASSWORD_ERROR) { + ret2 = + tpm_pin(&pkey->pin, &srk_uuid, storage, pin1, + sizeof(pin1), attempts++); + if (ret2 < 0) { + gnutls_assert(); + return GNUTLS_E_TPM_SRK_PASSWORD_ERROR; + } + srk_password = pin1; + } + + if (ret == GNUTLS_E_TPM_KEY_PASSWORD_ERROR) { + ret2 = + tpm_pin(&pkey->pin, uuid, storage, pin2, + sizeof(pin2), attempts++); + if (ret2 < 0) { + gnutls_assert(); + return GNUTLS_E_TPM_KEY_PASSWORD_ERROR; + } + key_password = pin2; + } + } + while (ret == GNUTLS_E_TPM_KEY_PASSWORD_ERROR + || ret == GNUTLS_E_TPM_SRK_PASSWORD_ERROR); + + if (ret < 0) + gnutls_assert(); + return ret; +} + +static int load_key(TSS_HCONTEXT tpm_ctx, TSS_HKEY srk, + const gnutls_datum_t * fdata, + gnutls_tpmkey_fmt_t format, TSS_HKEY * tpm_key) +{ + int ret, err; + gnutls_datum_t asn1 = { NULL, 0 }; + + if (format == GNUTLS_TPMKEY_FMT_CTK_PEM) { + gnutls_datum_t td; + + ret = + gnutls_pem_base64_decode2("TSS KEY BLOB", fdata, + &asn1); + if (ret) { + gnutls_assert(); + _gnutls_debug_log + ("Error decoding TSS key blob: %s\n", + gnutls_strerror(ret)); + return ret; + } + + ret = + _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING, + asn1.data, asn1.size, &td, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + gnutls_free(asn1.data); + asn1.data = td.data; + asn1.size = td.size; + } else { /* DER */ + + UINT32 tint2; + UINT32 type; + + asn1.size = fdata->size; + asn1.data = gnutls_malloc(asn1.size); + if (asn1.data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + tint2 = asn1.size; + err = + pTspi_DecodeBER_TssBlob(fdata->size, fdata->data, &type, + &tint2, asn1.data); + if (err != 0) { + gnutls_assert(); + ret = tss_err(err); + goto cleanup; + } + + asn1.size = tint2; + } + + /* ... we get it here instead. */ + err = pTspi_Context_LoadKeyByBlob(tpm_ctx, srk, + asn1.size, asn1.data, tpm_key); + if (err != 0) { + gnutls_assert(); + ret = tss_err(err); + goto cleanup; + } + + ret = 0; + + cleanup: + gnutls_free(asn1.data); + + return ret; +} + +static int +import_tpm_key(gnutls_privkey_t pkey, + const gnutls_datum_t * fdata, + gnutls_tpmkey_fmt_t format, + TSS_UUID * uuid, + TSS_FLAG storage, + const char *srk_password, const char *_key_password) +{ + int err, ret; + struct tpm_ctx_st *s; + gnutls_datum_t tmp_sig; + char *key_password = NULL; + uint32_t authusage; + + s = gnutls_malloc(sizeof(*s)); + if (s == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + if (_key_password != NULL) { + gnutls_datum_t pout; + ret = _gnutls_utf8_password_normalize(_key_password, strlen(_key_password), &pout, 1); + if (ret < 0) { + gnutls_assert(); + goto out_ctx; + } + key_password = (char*)pout.data; + } + + /* normalization of srk_password happens in tpm_open_session() */ + + ret = tpm_open_session(s, srk_password, 1); + if (ret < 0) { + gnutls_assert(); + goto out_ctx; + } + + if (fdata != NULL) { + ret = + load_key(s->tpm_ctx, s->srk, fdata, format, + &s->tpm_key); + if (ret < 0) { + gnutls_assert(); + goto out_session; + } + } else if (uuid) { + err = + pTspi_Context_LoadKeyByUUID(s->tpm_ctx, storage, + *uuid, &s->tpm_key); + + if (err) { + gnutls_assert(); + ret = tss_err(err); + goto out_session; + } + } else { + gnutls_assert(); + ret = GNUTLS_E_INVALID_REQUEST; + goto out_session; + } + + err = pTspi_GetAttribUint32(s->tpm_key, TSS_TSPATTRIB_KEY_INFO, + TSS_TSPATTRIB_KEYINFO_AUTHUSAGE, + &authusage); + if (err) { + gnutls_assert(); + ret = tss_err(err); + goto out_session; + } + + if (authusage) { + if (!_key_password) { + ret = GNUTLS_E_TPM_KEY_PASSWORD_ERROR; + goto out_session; + } + + err = pTspi_Context_CreateObject(s->tpm_ctx, + TSS_OBJECT_TYPE_POLICY, + TSS_POLICY_USAGE, + &s->tpm_key_policy); + if (err) { + gnutls_assert(); + ret = tss_err(err); + goto out_key; + } + + err = pTspi_Policy_AssignToObject(s->tpm_key_policy, + s->tpm_key); + if (err) { + gnutls_assert(); + ret = tss_err(err); + goto out_key_policy; + } + + err = myTspi_Policy_SetSecret(s->tpm_key_policy, + SAFE_LEN(key_password), + (void *) key_password); + + if (err) { + gnutls_assert(); + ret = tss_err_key(err); + goto out_key_policy; + } + } + + ret = + gnutls_privkey_import_ext2(pkey, GNUTLS_PK_RSA, s, + tpm_sign_fn, NULL, tpm_deinit_fn, + 0); + if (ret < 0) { + gnutls_assert(); + goto out_session; + } + + ret = + gnutls_privkey_sign_data(pkey, GNUTLS_DIG_SHA1, 0, &nulldata, + &tmp_sig); + if (ret < 0) { + gnutls_assert(); + goto out_session; + } + + gnutls_free(key_password); + + return 0; + out_key_policy: + pTspi_Context_CloseObject(s->tpm_ctx, s->tpm_key_policy); + s->tpm_key_policy = 0; + out_key: + pTspi_Context_CloseObject(s->tpm_ctx, s->tpm_key); + s->tpm_key = 0; + out_session: + _gnutls_privkey_cleanup(pkey); + tpm_close_session(s); + out_ctx: + gnutls_free(s); + gnutls_free(key_password); + return ret; +} + +/** + * gnutls_privkey_import_tpm_raw: + * @pkey: The private key + * @fdata: The TPM key to be imported + * @format: The format of the private key + * @srk_password: The password for the SRK key (optional) + * @key_password: A password for the key (optional) + * @flags: should be zero + * + * This function will import the given private key to the abstract + * #gnutls_privkey_t type. + * + * With respect to passwords the same as in gnutls_privkey_import_tpm_url() apply. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.0 + * + **/ +int +gnutls_privkey_import_tpm_raw(gnutls_privkey_t pkey, + const gnutls_datum_t * fdata, + gnutls_tpmkey_fmt_t format, + const char *srk_password, + const char *key_password, unsigned int flags) +{ + CHECK_INIT; + + if (flags & GNUTLS_PRIVKEY_DISABLE_CALLBACKS) + return import_tpm_key(pkey, fdata, format, NULL, 0, + srk_password, key_password); + else + return import_tpm_key_cb(pkey, fdata, format, NULL, 0, + srk_password, key_password); +} + +struct tpmkey_url_st { + char *filename; + TSS_UUID uuid; + TSS_FLAG storage; + unsigned int uuid_set; +}; + +static void clear_tpmkey_url(struct tpmkey_url_st *s) +{ + gnutls_free(s->filename); + memset(s, 0, sizeof(*s)); +} + +static int +unescape_string(char *output, const char *input, size_t * size, + char terminator) +{ + gnutls_buffer_st str; + int ret = 0; + char *p; + int len; + + _gnutls_buffer_init(&str); + + /* find terminator */ + p = strchr(input, terminator); + if (p != NULL) + len = p - input; + else + len = strlen(input); + + ret = _gnutls_buffer_append_data(&str, input, len); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_buffer_unescape(&str); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_buffer_append_data(&str, "", 1); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_buffer_pop_data(&str, output, str.length); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + _gnutls_buffer_clear(&str); + + return ret; +} + +#define UUID_SIZE 16 + +static int randomize_uuid(TSS_UUID * uuid) +{ + uint8_t raw_uuid[16]; + int ret; + + ret = gnutls_rnd(GNUTLS_RND_NONCE, raw_uuid, sizeof(raw_uuid)); + if (ret < 0) + return gnutls_assert_val(ret); + + /* mark it as random uuid */ + raw_uuid[6] &= 0x0f; + raw_uuid[6] |= 0x40; + raw_uuid[8] &= 0x0f; + raw_uuid[8] |= 0x80; + + memcpy(&uuid->ulTimeLow, raw_uuid, 4); + memcpy(&uuid->usTimeMid, &raw_uuid[4], 2); + memcpy(&uuid->usTimeHigh, &raw_uuid[6], 2); + uuid->bClockSeqHigh = raw_uuid[8]; + uuid->bClockSeqLow = raw_uuid[9]; + memcpy(&uuid->rgbNode, &raw_uuid[10], 6); + + return 0; +} + +static int encode_tpmkey_url(char **url, const TSS_UUID * uuid, + TSS_FLAG storage) +{ + uint8_t u1[UUID_SIZE]; + gnutls_buffer_st buf; + gnutls_datum_t dret; + int ret; + + _gnutls_buffer_init(&buf); + + memcpy(u1, &uuid->ulTimeLow, 4); + memcpy(&u1[4], &uuid->usTimeMid, 2); + memcpy(&u1[6], &uuid->usTimeHigh, 2); + u1[8] = uuid->bClockSeqHigh; + u1[9] = uuid->bClockSeqLow; + memcpy(&u1[10], uuid->rgbNode, 6); + + ret = _gnutls_buffer_append_str(&buf, "tpmkey:uuid="); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_buffer_append_printf(&buf, + "%.2x%.2x%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x", + (unsigned int) u1[0], + (unsigned int) u1[1], + (unsigned int) u1[2], + (unsigned int) u1[3], + (unsigned int) u1[4], + (unsigned int) u1[5], + (unsigned int) u1[6], + (unsigned int) u1[7], + (unsigned int) u1[8], + (unsigned int) u1[9], + (unsigned int) u1[10], + (unsigned int) u1[11], + (unsigned int) u1[12], + (unsigned int) u1[13], + (unsigned int) u1[14], + (unsigned int) u1[15]); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_buffer_append_printf(&buf, ";storage=%s", + (storage == + TSS_PS_TYPE_USER) ? "user" : + "system"); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_buffer_to_datum(&buf, &dret, 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + *url = (char *) dret.data; + + return 0; + cleanup: + _gnutls_buffer_clear(&buf); + return ret; +} + +static int decode_tpmkey_url(const char *url, struct tpmkey_url_st *s) +{ + char *p; + size_t size; + int ret; + unsigned int i, j; + + if (strstr(url, "tpmkey:") == NULL) + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + + memset(s, 0, sizeof(*s)); + + p = strstr(url, "file="); + if (p != NULL) { + p += sizeof("file=") - 1; + size = strlen(p); + s->filename = gnutls_malloc(size + 1); + if (s->filename == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + ret = unescape_string(s->filename, p, &size, ';'); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + s->filename[size] = 0; + } else if ((p = strstr(url, "uuid=")) != NULL) { + char tmp_uuid[33]; + uint8_t raw_uuid[16]; + gnutls_datum_t tmp; + + p += sizeof("uuid=") - 1; + size = strlen(p); + + for (j = i = 0; i < size; i++) { + if (j == sizeof(tmp_uuid) - 1) { + break; + } + if (c_isalnum(p[i])) + tmp_uuid[j++] = p[i]; + } + tmp_uuid[j] = 0; + + tmp.data = (void*)tmp_uuid; + tmp.size = strlen(tmp_uuid); + size = sizeof(raw_uuid); + ret = + gnutls_hex_decode(&tmp, raw_uuid, + &size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + memcpy(&s->uuid.ulTimeLow, raw_uuid, 4); + memcpy(&s->uuid.usTimeMid, &raw_uuid[4], 2); + memcpy(&s->uuid.usTimeHigh, &raw_uuid[6], 2); + s->uuid.bClockSeqHigh = raw_uuid[8]; + s->uuid.bClockSeqLow = raw_uuid[9]; + memcpy(&s->uuid.rgbNode, &raw_uuid[10], 6); + s->uuid_set = 1; + } else { + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + } + + if (strstr(url, "storage=user") != NULL) + s->storage = TSS_PS_TYPE_USER; + else + s->storage = TSS_PS_TYPE_SYSTEM; + + return 0; + + cleanup: + clear_tpmkey_url(s); + return ret; +} + +/** + * gnutls_privkey_import_tpm_url: + * @pkey: The private key + * @url: The URL of the TPM key to be imported + * @srk_password: The password for the SRK key (optional) + * @key_password: A password for the key (optional) + * @flags: One of the GNUTLS_PRIVKEY_* flags + * + * This function will import the given private key to the abstract + * #gnutls_privkey_t type. + * + * Note that unless %GNUTLS_PRIVKEY_DISABLE_CALLBACKS + * is specified, if incorrect (or NULL) passwords are given + * the PKCS11 callback functions will be used to obtain the + * correct passwords. Otherwise if the SRK password is wrong + * %GNUTLS_E_TPM_SRK_PASSWORD_ERROR is returned and if the key password + * is wrong or not provided then %GNUTLS_E_TPM_KEY_PASSWORD_ERROR + * is returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.0 + * + **/ +int +gnutls_privkey_import_tpm_url(gnutls_privkey_t pkey, + const char *url, + const char *srk_password, + const char *key_password, unsigned int flags) +{ + struct tpmkey_url_st durl; + gnutls_datum_t fdata = { NULL, 0 }; + int ret; + + CHECK_INIT; + + ret = decode_tpmkey_url(url, &durl); + if (ret < 0) + return gnutls_assert_val(ret); + + if (durl.filename) { + ret = gnutls_load_file(durl.filename, &fdata); + if (ret < 0) { + gnutls_assert(); + _gnutls_debug_log("Error loading %s\n", + durl.filename); + goto cleanup; + } + + ret = + gnutls_privkey_import_tpm_raw(pkey, &fdata, + GNUTLS_TPMKEY_FMT_CTK_PEM, + srk_password, + key_password, flags); + if (ret == GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR) + ret = + gnutls_privkey_import_tpm_raw(pkey, &fdata, + GNUTLS_TPMKEY_FMT_RAW, + srk_password, + key_password, + flags); + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } else if (durl.uuid_set) { + if (flags & GNUTLS_PRIVKEY_DISABLE_CALLBACKS) + ret = + import_tpm_key(pkey, NULL, 0, &durl.uuid, + durl.storage, srk_password, + key_password); + else + ret = + import_tpm_key_cb(pkey, NULL, 0, &durl.uuid, + durl.storage, srk_password, + key_password); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + ret = 0; + cleanup: + gnutls_free(fdata.data); + clear_tpmkey_url(&durl); + return ret; +} + + +/* reads the RSA public key from the given TSS key. + * If psize is non-null it contains the total size of the parameters + * in bytes */ +static int read_pubkey(gnutls_pubkey_t pub, TSS_HKEY key_ctx, + size_t * psize) +{ + void *tdata; + UINT32 tint; + TSS_RESULT tssret; + gnutls_datum_t m, e; + int ret; + + /* read the public key */ + + tssret = pTspi_GetAttribData(key_ctx, TSS_TSPATTRIB_RSAKEY_INFO, + TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, + &tint, (void *) &tdata); + if (tssret != 0) { + gnutls_assert(); + return tss_err(tssret); + } + + m.data = tdata; + m.size = tint; + + tssret = pTspi_GetAttribData(key_ctx, TSS_TSPATTRIB_RSAKEY_INFO, + TSS_TSPATTRIB_KEYINFO_RSA_EXPONENT, + &tint, (void *) &tdata); + if (tssret != 0) { + gnutls_assert(); + pTspi_Context_FreeMemory(key_ctx, m.data); + return tss_err(tssret); + } + + e.data = tdata; + e.size = tint; + + ret = gnutls_pubkey_import_rsa_raw(pub, &m, &e); + + pTspi_Context_FreeMemory(key_ctx, m.data); + pTspi_Context_FreeMemory(key_ctx, e.data); + + if (ret < 0) + return gnutls_assert_val(ret); + + if (psize) + *psize = e.size + m.size; + + return 0; +} + + + +static int +import_tpm_pubkey(gnutls_pubkey_t pkey, + const gnutls_datum_t * fdata, + gnutls_tpmkey_fmt_t format, + TSS_UUID * uuid, + TSS_FLAG storage, const char *srk_password) +{ + int err, ret; + struct tpm_ctx_st s; + + ret = tpm_open_session(&s, srk_password, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + if (fdata != NULL) { + ret = + load_key(s.tpm_ctx, s.srk, fdata, format, &s.tpm_key); + if (ret < 0) { + gnutls_assert(); + goto out_session; + } + } else if (uuid) { + err = + pTspi_Context_LoadKeyByUUID(s.tpm_ctx, storage, + *uuid, &s.tpm_key); + if (err) { + gnutls_assert(); + ret = tss_err(err); + goto out_session; + } + } else { + gnutls_assert(); + ret = GNUTLS_E_INVALID_REQUEST; + goto out_session; + } + + ret = read_pubkey(pkey, s.tpm_key, NULL); + if (ret < 0) { + gnutls_assert(); + goto out_session; + } + + ret = 0; + out_session: + tpm_close_session(&s); + return ret; +} + +static int +import_tpm_pubkey_cb(gnutls_pubkey_t pkey, + const gnutls_datum_t * fdata, + gnutls_tpmkey_fmt_t format, + TSS_UUID * uuid, + TSS_FLAG storage, const char *srk_password) +{ + unsigned int attempts = 0; + char pin1[GNUTLS_PKCS11_MAX_PIN_LEN]; + int ret, sret; + + do { + ret = + import_tpm_pubkey(pkey, fdata, format, uuid, storage, + srk_password); + + if (attempts > 3) + break; + + if (ret == GNUTLS_E_TPM_SRK_PASSWORD_ERROR) { + sret = + tpm_pin(&pkey->pin, &srk_uuid, storage, pin1, + sizeof(pin1), attempts++); + if (sret < 0) { + gnutls_assert(); + return GNUTLS_E_TPM_SRK_PASSWORD_ERROR; + } + srk_password = pin1; + } + } + while (ret == GNUTLS_E_TPM_SRK_PASSWORD_ERROR); + + if (ret < 0) + gnutls_assert(); + return ret; +} + + +/** + * gnutls_pubkey_import_tpm_raw: + * @pkey: The public key + * @fdata: The TPM key to be imported + * @format: The format of the private key + * @srk_password: The password for the SRK key (optional) + * @flags: One of the GNUTLS_PUBKEY_* flags + * + * This function will import the public key from the provided TPM key + * structure. + * + * With respect to passwords the same as in + * gnutls_pubkey_import_tpm_url() apply. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.0 + **/ +int +gnutls_pubkey_import_tpm_raw(gnutls_pubkey_t pkey, + const gnutls_datum_t * fdata, + gnutls_tpmkey_fmt_t format, + const char *srk_password, unsigned int flags) +{ + CHECK_INIT; + + if (flags & GNUTLS_PUBKEY_DISABLE_CALLBACKS) + return import_tpm_pubkey_cb(pkey, fdata, format, NULL, 0, + srk_password); + else + return import_tpm_pubkey(pkey, fdata, format, NULL, 0, + srk_password); +} + +/** + * gnutls_pubkey_import_tpm_url: + * @pkey: The public key + * @url: The URL of the TPM key to be imported + * @srk_password: The password for the SRK key (optional) + * @flags: should be zero + * + * This function will import the given private key to the abstract + * #gnutls_privkey_t type. + * + * Note that unless %GNUTLS_PUBKEY_DISABLE_CALLBACKS + * is specified, if incorrect (or NULL) passwords are given + * the PKCS11 callback functions will be used to obtain the + * correct passwords. Otherwise if the SRK password is wrong + * %GNUTLS_E_TPM_SRK_PASSWORD_ERROR is returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.0 + * + **/ +int +gnutls_pubkey_import_tpm_url(gnutls_pubkey_t pkey, + const char *url, + const char *srk_password, unsigned int flags) +{ + struct tpmkey_url_st durl; + gnutls_datum_t fdata = { NULL, 0 }; + int ret; + + CHECK_INIT; + + ret = decode_tpmkey_url(url, &durl); + if (ret < 0) + return gnutls_assert_val(ret); + + if (durl.filename) { + + ret = gnutls_load_file(durl.filename, &fdata); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + gnutls_pubkey_import_tpm_raw(pkey, &fdata, + GNUTLS_TPMKEY_FMT_CTK_PEM, + srk_password, flags); + if (ret == GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR) + ret = + gnutls_pubkey_import_tpm_raw(pkey, &fdata, + GNUTLS_TPMKEY_FMT_RAW, + srk_password, + flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } else if (durl.uuid_set) { + if (flags & GNUTLS_PUBKEY_DISABLE_CALLBACKS) + ret = + import_tpm_pubkey(pkey, NULL, 0, &durl.uuid, + durl.storage, srk_password); + else + ret = + import_tpm_pubkey_cb(pkey, NULL, 0, &durl.uuid, + durl.storage, + srk_password); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + ret = 0; + cleanup: + gnutls_free(fdata.data); + clear_tpmkey_url(&durl); + return ret; +} + + +/** + * gnutls_tpm_privkey_generate: + * @pk: the public key algorithm + * @bits: the security bits + * @srk_password: a password to protect the exported key (optional) + * @key_password: the password for the TPM (optional) + * @format: the format of the private key + * @pub_format: the format of the public key + * @privkey: the generated key + * @pubkey: the corresponding public key (may be null) + * @flags: should be a list of GNUTLS_TPM_* flags + * + * This function will generate a private key in the TPM + * chip. The private key will be generated within the chip + * and will be exported in a wrapped with TPM's master key + * form. Furthermore the wrapped key can be protected with + * the provided @password. + * + * Note that bits in TPM is quantized value. If the input value + * is not one of the allowed values, then it will be quantized to + * one of 512, 1024, 2048, 4096, 8192 and 16384. + * + * Allowed flags are: + * + * %GNUTLS_TPM_KEY_SIGNING: Generate a signing key instead of a legacy, + * + * %GNUTLS_TPM_REGISTER_KEY: Register the generate key in TPM. In that + * case @privkey would contain a URL with the UUID. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.0 + **/ +int +gnutls_tpm_privkey_generate(gnutls_pk_algorithm_t pk, unsigned int bits, + const char *srk_password, + const char *key_password, + gnutls_tpmkey_fmt_t format, + gnutls_x509_crt_fmt_t pub_format, + gnutls_datum_t * privkey, + gnutls_datum_t * pubkey, unsigned int flags) +{ + TSS_FLAG tpm_flags = TSS_KEY_VOLATILE; + TSS_HKEY key_ctx; + TSS_RESULT tssret; + int ret; + void *tdata; + UINT32 tint; + gnutls_datum_t tmpkey = { NULL, 0 }; + TSS_HPOLICY key_policy; + gnutls_pubkey_t pub; + struct tpm_ctx_st s; + TSS_FLAG storage_type; + TSS_HTPM htpm; + uint8_t buf[32]; + + CHECK_INIT; + + privkey->data = NULL; + if (pubkey != NULL) + pubkey->data = NULL; + + if (flags & GNUTLS_TPM_KEY_SIGNING) + tpm_flags |= TSS_KEY_TYPE_SIGNING; + else + tpm_flags |= TSS_KEY_TYPE_LEGACY; + + if (flags & GNUTLS_TPM_KEY_USER) + storage_type = TSS_PS_TYPE_USER; + else + storage_type = TSS_PS_TYPE_SYSTEM; + + if (bits <= 512) + tpm_flags |= TSS_KEY_SIZE_512; + else if (bits <= 1024) + tpm_flags |= TSS_KEY_SIZE_1024; + else if (bits <= 2048) + tpm_flags |= TSS_KEY_SIZE_2048; + else if (bits <= 4096) + tpm_flags |= TSS_KEY_SIZE_4096; + else if (bits <= 8192) + tpm_flags |= TSS_KEY_SIZE_8192; + else + tpm_flags |= TSS_KEY_SIZE_16384; + + ret = tpm_open_session(&s, srk_password, 0); + if (ret < 0) + return gnutls_assert_val(ret); + + /* put some randomness into TPM. + * Let's not trust it completely. + */ + tssret = pTspi_Context_GetTpmObject(s.tpm_ctx, &htpm); + if (tssret != 0) { + gnutls_assert(); + ret = tss_err(tssret); + goto err_cc; + } + + + ret = gnutls_rnd(GNUTLS_RND_RANDOM, buf, sizeof(buf)); + if (ret < 0) { + gnutls_assert(); + goto err_cc; + } + + tssret = pTspi_TPM_StirRandom(htpm, sizeof(buf), buf); + if (tssret) { + gnutls_assert(); + } + + tssret = + pTspi_Context_CreateObject(s.tpm_ctx, TSS_OBJECT_TYPE_RSAKEY, + tpm_flags, &key_ctx); + if (tssret != 0) { + gnutls_assert(); + ret = tss_err(tssret); + goto err_cc; + } + + tssret = + pTspi_SetAttribUint32(key_ctx, TSS_TSPATTRIB_KEY_INFO, + TSS_TSPATTRIB_KEYINFO_SIGSCHEME, + TSS_SS_RSASSAPKCS1V15_DER); + if (tssret != 0) { + gnutls_assert(); + ret = tss_err(tssret); + goto err_sa; + } + + /* set the password of the actual key */ + if (key_password) { + gnutls_datum_t pout; + char *password = NULL; + + tssret = + pTspi_GetPolicyObject(key_ctx, TSS_POLICY_USAGE, + &key_policy); + if (tssret != 0) { + gnutls_assert(); + ret = tss_err(tssret); + goto err_sa; + } + + ret = _gnutls_utf8_password_normalize(key_password, strlen(key_password), &pout, 0); + if (ret < 0) { + gnutls_assert(); + goto err_sa; + } + password = (char*)pout.data; + + tssret = myTspi_Policy_SetSecret(key_policy, + SAFE_LEN(password), + (void *)password); + gnutls_free(password); + if (tssret != 0) { + gnutls_assert(); + ret = tss_err(tssret); + goto err_sa; + } + } + + tssret = pTspi_Key_CreateKey(key_ctx, s.srk, 0); + if (tssret != 0) { + gnutls_assert(); + ret = tss_err(tssret); + goto err_sa; + } + + if (flags & GNUTLS_TPM_REGISTER_KEY) { + TSS_UUID key_uuid; + + ret = randomize_uuid(&key_uuid); + if (ret < 0) { + gnutls_assert(); + goto err_sa; + } + + tssret = + pTspi_Context_RegisterKey(s.tpm_ctx, key_ctx, + storage_type, key_uuid, + TSS_PS_TYPE_SYSTEM, srk_uuid); + if (tssret != 0) { + gnutls_assert(); + ret = tss_err(tssret); + goto err_sa; + } + + ret = + encode_tpmkey_url((char **) &privkey->data, &key_uuid, + storage_type); + if (ret < 0) { + TSS_HKEY tkey; + + pTspi_Context_UnregisterKey(s.tpm_ctx, storage_type, + key_uuid, &tkey); + gnutls_assert(); + goto err_sa; + } + privkey->size = strlen((char *) privkey->data); + + } else { /* get the key as blob */ + + + tssret = + pTspi_GetAttribData(key_ctx, TSS_TSPATTRIB_KEY_BLOB, + TSS_TSPATTRIB_KEYBLOB_BLOB, &tint, + (void *) &tdata); + if (tssret != 0) { + gnutls_assert(); + ret = tss_err(tssret); + goto err_sa; + } + + + if (format == GNUTLS_TPMKEY_FMT_CTK_PEM) { + ret = + _gnutls_x509_encode_string + (ASN1_ETYPE_OCTET_STRING, tdata, tint, + &tmpkey); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = + _gnutls_fbase64_encode("TSS KEY BLOB", + tmpkey.data, + tmpkey.size, privkey); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } else { + UINT32 tint2; + + tmpkey.size = tint + 32; /* spec says no more than 20 */ + tmpkey.data = gnutls_malloc(tmpkey.size); + if (tmpkey.data == NULL) { + gnutls_assert(); + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + tint2 = tmpkey.size; + tssret = + pTspi_EncodeDER_TssBlob(tint, tdata, + TSS_BLOB_TYPE_PRIVATEKEY, + &tint2, tmpkey.data); + if (tssret != 0) { + gnutls_assert(); + ret = tss_err(tssret); + goto cleanup; + } + + tmpkey.size = tint2; + + privkey->data = tmpkey.data; + privkey->size = tmpkey.size; + tmpkey.data = NULL; + } + } + + /* read the public key */ + if (pubkey != NULL) { + size_t psize = 0; + + ret = gnutls_pubkey_init(&pub); + if (ret < 0) { + gnutls_assert(); + goto privkey_cleanup; + } + + ret = read_pubkey(pub, key_ctx, &psize); + if (ret < 0) { + gnutls_assert(); + goto privkey_cleanup; + } + psize += 512; + + pubkey->data = gnutls_malloc(psize); + if (pubkey->data == NULL) { + gnutls_assert(); + ret = GNUTLS_E_MEMORY_ERROR; + goto pubkey_cleanup; + } + + ret = + gnutls_pubkey_export(pub, pub_format, pubkey->data, + &psize); + if (ret < 0) { + gnutls_assert(); + goto pubkey_cleanup; + } + pubkey->size = psize; + + gnutls_pubkey_deinit(pub); + } + + ret = 0; + goto cleanup; + + pubkey_cleanup: + gnutls_pubkey_deinit(pub); + privkey_cleanup: + gnutls_free(privkey->data); + cleanup: + gnutls_free(tmpkey.data); + err_sa: + pTspi_Context_CloseObject(s.tpm_ctx, key_ctx); + err_cc: + tpm_close_session(&s); + return ret; +} + + +/** + * gnutls_tpm_key_list_deinit: + * @list: a list of the keys + * + * This function will deinitialize the list of stored keys in the TPM. + * + * Since: 3.1.0 + **/ +void gnutls_tpm_key_list_deinit(gnutls_tpm_key_list_t list) +{ + CHECK_INIT_VOID; + + if (list->tpm_ctx != 0) + pTspi_Context_Close(list->tpm_ctx); + gnutls_free(list); +} + +/** + * gnutls_tpm_key_list_get_url: + * @list: a list of the keys + * @idx: The index of the key (starting from zero) + * @url: The URL to be returned + * @flags: should be zero + * + * This function will return for each given index a URL of + * the corresponding key. + * If the provided index is out of bounds then %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * is returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.0 + **/ +int +gnutls_tpm_key_list_get_url(gnutls_tpm_key_list_t list, unsigned int idx, + char **url, unsigned int flags) +{ + CHECK_INIT; + + if (idx >= list->size) + return + gnutls_assert_val + (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + return encode_tpmkey_url(url, &list->ki[idx].keyUUID, + list->ki[idx].persistentStorageType); +} + +/** + * gnutls_tpm_get_registered: + * @list: a list to store the keys + * + * This function will get a list of stored keys in the TPM. The uuid + * of those keys + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.0 + **/ +int gnutls_tpm_get_registered(gnutls_tpm_key_list_t * list) +{ + TSS_RESULT tssret; + int ret; + + CHECK_INIT; + + *list = gnutls_calloc(1, sizeof(struct tpm_key_list_st)); + if (*list == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + tssret = pTspi_Context_Create(&(*list)->tpm_ctx); + if (tssret) { + gnutls_assert(); + ret = tss_err(tssret); + goto cleanup; + } + + tssret = pTspi_Context_Connect((*list)->tpm_ctx, NULL); + if (tssret) { + gnutls_assert(); + ret = tss_err(tssret); + goto cleanup; + } + + tssret = + pTspi_Context_GetRegisteredKeysByUUID2((*list)->tpm_ctx, + TSS_PS_TYPE_SYSTEM, NULL, + &(*list)->size, + &(*list)->ki); + if (tssret) { + gnutls_assert(); + ret = tss_err(tssret); + goto cleanup; + } + return 0; + + cleanup: + gnutls_tpm_key_list_deinit(*list); + + return ret; +} + +/** + * gnutls_tpm_privkey_delete: + * @url: the URL describing the key + * @srk_password: a password for the SRK key + * + * This function will unregister the private key from the TPM + * chip. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.1.0 + **/ +int gnutls_tpm_privkey_delete(const char *url, const char *srk_password) +{ + struct tpm_ctx_st s; + struct tpmkey_url_st durl; + TSS_RESULT tssret; + TSS_HKEY tkey; + int ret; + + CHECK_INIT; + + ret = decode_tpmkey_url(url, &durl); + if (ret < 0) + return gnutls_assert_val(ret); + + if (durl.uuid_set == 0) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = tpm_open_session(&s, srk_password, 1); + if (ret < 0) + return gnutls_assert_val(ret); + + tssret = + pTspi_Context_UnregisterKey(s.tpm_ctx, durl.storage, durl.uuid, + &tkey); + if (tssret != 0) { + gnutls_assert(); + ret = tss_err(tssret); + goto err_cc; + } + + ret = 0; + err_cc: + tpm_close_session(&s); + return ret; +} +#else /* HAVE_TROUSERS */ +int +gnutls_privkey_import_tpm_raw(gnutls_privkey_t pkey, + const gnutls_datum_t * fdata, + gnutls_tpmkey_fmt_t format, + const char *srk_password, + const char *key_password, unsigned int flags) +{ + return GNUTLS_E_UNIMPLEMENTED_FEATURE; +} + +int +gnutls_privkey_import_tpm_url(gnutls_privkey_t pkey, + const char *url, + const char *srk_password, + const char *key_password, unsigned int flags) +{ + return GNUTLS_E_UNIMPLEMENTED_FEATURE; +} + +int +gnutls_pubkey_import_tpm_raw(gnutls_pubkey_t pkey, + const gnutls_datum_t * fdata, + gnutls_tpmkey_fmt_t format, + const char *srk_password, unsigned int flags) +{ + return GNUTLS_E_UNIMPLEMENTED_FEATURE; +} + +int +gnutls_pubkey_import_tpm_url(gnutls_pubkey_t pkey, + const char *url, + const char *srk_password, unsigned int flags) +{ + return GNUTLS_E_UNIMPLEMENTED_FEATURE; +} + +int +gnutls_tpm_privkey_generate(gnutls_pk_algorithm_t pk, unsigned int bits, + const char *srk_password, + const char *key_password, + gnutls_tpmkey_fmt_t format, + gnutls_x509_crt_fmt_t pub_format, + gnutls_datum_t * privkey, + gnutls_datum_t * pubkey, unsigned int flags) +{ + return GNUTLS_E_UNIMPLEMENTED_FEATURE; +} + +void gnutls_tpm_key_list_deinit(gnutls_tpm_key_list_t list) +{ + return; +} + +int +gnutls_tpm_key_list_get_url(gnutls_tpm_key_list_t list, unsigned int idx, + char **url, unsigned int flags) +{ + return GNUTLS_E_UNIMPLEMENTED_FEATURE; +} + +int gnutls_tpm_get_registered(gnutls_tpm_key_list_t * list) +{ + return GNUTLS_E_UNIMPLEMENTED_FEATURE; +} + +int gnutls_tpm_privkey_delete(const char *url, const char *srk_password) +{ + return GNUTLS_E_UNIMPLEMENTED_FEATURE; +} +#endif /* HAVE_TROUSERS */ |