/* * The Initial Developer of the Original Code is International * Business Machines Corporation. Portions created by IBM * Corporation are Copyright (C) 2005 International Business * Machines Corporation. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the Common Public License as published by * IBM Corporation; either version 1 of the License, or (at your option) * any later version. * * 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 * Common Public License for more details. * * You should have received a copy of the Common Public License * along with this program; if not, a copy can be viewed at * http://www.opensource.org/licenses/cpl1.0.php. */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tpmtok_int.h" #include "tpmtok_defs.h" #define MAX_RSA_KEYLENGTH 512 extern void stlogit(char *fmt, ...); CK_RV token_rng(TSS_HCONTEXT, CK_BYTE *, CK_ULONG); int tok_slot2local(CK_SLOT_ID); CK_RV token_specific_session(CK_SLOT_ID); CK_RV token_specific_final(TSS_HCONTEXT); CK_RV token_specific_rsa_decrypt( TSS_HCONTEXT, CK_BYTE *, CK_ULONG, CK_BYTE *, CK_ULONG *, OBJECT *); CK_RV token_specific_rsa_encrypt( TSS_HCONTEXT, CK_BYTE *, CK_ULONG, CK_BYTE *, CK_ULONG *, OBJECT *); CK_RV token_specific_rsa_sign( TSS_HCONTEXT, CK_BYTE *, CK_ULONG, CK_BYTE *, CK_ULONG *, OBJECT *); CK_RV token_specific_rsa_verify(TSS_HCONTEXT, CK_BYTE *, CK_ULONG, CK_BYTE *, CK_ULONG, OBJECT *); CK_RV token_specific_rsa_generate_keypair(TSS_HCONTEXT, TEMPLATE *, TEMPLATE *); CK_RV token_specific_sha_init(DIGEST_CONTEXT *); CK_RV token_specific_sha_update(DIGEST_CONTEXT *, CK_BYTE *, CK_ULONG); CK_RV token_specific_sha_final(DIGEST_CONTEXT *, CK_BYTE *, CK_ULONG *); CK_RV token_specific_login(TSS_HCONTEXT, CK_USER_TYPE, CK_CHAR_PTR, CK_ULONG); CK_RV token_specific_logout(TSS_HCONTEXT); CK_RV token_specific_init_pin(TSS_HCONTEXT, CK_CHAR_PTR, CK_ULONG); CK_RV token_specific_set_pin(ST_SESSION_HANDLE, CK_CHAR_PTR, CK_ULONG, CK_CHAR_PTR, CK_ULONG); CK_RV token_specific_verify_so_pin(TSS_HCONTEXT, CK_CHAR_PTR, CK_ULONG); static CK_RV token_specific_init(char *, CK_SLOT_ID, TSS_HCONTEXT *); struct token_specific_struct token_specific = { "TPM_Debug", &token_specific_init, NULL, &token_rng, &token_specific_session, &token_specific_final, &token_specific_rsa_decrypt, &token_specific_rsa_encrypt, &token_specific_rsa_sign, &token_specific_rsa_verify, &token_specific_rsa_generate_keypair, NULL, NULL, NULL, &token_specific_login, &token_specific_logout, &token_specific_init_pin, &token_specific_set_pin, &token_specific_verify_so_pin }; /* The context we'll use globally to connect to the TSP */ /* TSP key handles */ TSS_HKEY hPublicRootKey = NULL_HKEY; TSS_HKEY hPublicLeafKey = NULL_HKEY; TSS_HKEY hPrivateRootKey = NULL_HKEY; TSS_HKEY hPrivateLeafKey = NULL_HKEY; TSS_UUID publicRootKeyUUID; TSS_UUID publicLeafKeyUUID; TSS_UUID privateRootKeyUUID; TSS_UUID privateLeafKeyUUID; /* TSP policy handles */ TSS_HPOLICY hDefaultPolicy = NULL_HPOLICY; /* PKCS#11 key handles */ int not_initialized = 0; CK_BYTE current_user_pin_sha[SHA1_DIGEST_LENGTH]; CK_BYTE current_so_pin_sha[SHA1_DIGEST_LENGTH]; static TPM_CAP_VERSION_INFO tpmvinfo; static CK_RV verify_user_pin(TSS_HCONTEXT, CK_BYTE *); static TSS_RESULT tss_assign_secret_key_policy(TSS_HCONTEXT, TSS_FLAG, TSS_HKEY, CK_CHAR *); static TSS_RESULT set_legacy_key_params(TSS_HKEY); static void local_uuid_clear(TSS_UUID *uuid) { if (uuid == NULL) return; (void) memset(uuid, 0, sizeof (TSS_UUID)); } /* convert from TSS_UUID to uuid_t */ static void tss_uuid_convert_from(TSS_UUID *uu, uuid_t ptr) { uint_t tmp; uchar_t *out = ptr; tmp = ntohl(uu->ulTimeLow); out[3] = (uchar_t)tmp; tmp >>= 8; out[2] = (uchar_t)tmp; tmp >>= 8; out[1] = (uchar_t)tmp; tmp >>= 8; out[0] = (uchar_t)tmp; tmp = ntohs(uu->usTimeMid); out[5] = (uchar_t)tmp; tmp >>= 8; out[4] = (uchar_t)tmp; tmp = ntohs(uu->usTimeHigh); out[7] = (uchar_t)tmp; tmp >>= 8; out[6] = (uchar_t)tmp; tmp = uu->bClockSeqHigh; out[8] = (uchar_t)tmp; tmp = uu->bClockSeqLow; out[9] = (uchar_t)tmp; (void) memcpy(out+10, uu->rgbNode, 6); } /* convert from uuid_t to TSS_UUID */ static void tss_uuid_convert_to(TSS_UUID *uuid, uuid_t in) { uchar_t *ptr; uint32_t ltmp; uint16_t stmp; ptr = in; ltmp = *ptr++; ltmp = (ltmp << 8) | *ptr++; ltmp = (ltmp << 8) | *ptr++; ltmp = (ltmp << 8) | *ptr++; uuid->ulTimeLow = ntohl(ltmp); stmp = *ptr++; stmp = (stmp << 8) | *ptr++; uuid->usTimeMid = ntohs(stmp); stmp = *ptr++; stmp = (stmp << 8) | *ptr++; uuid->usTimeHigh = ntohs(stmp); uuid->bClockSeqHigh = *ptr++; uuid->bClockSeqLow = *ptr++; (void) memcpy(uuid->rgbNode, ptr, 6); } static void local_uuid_copy(TSS_UUID *dst, TSS_UUID *src) { uuid_t udst, usrc; tss_uuid_convert_from(dst, udst); tss_uuid_convert_from(src, usrc); uuid_copy(udst, usrc); tss_uuid_convert_to(dst, udst); } static void local_uuid_generate(TSS_UUID *uu) { uuid_t newuuid; uuid_generate(newuuid); tss_uuid_convert_to(uu, newuuid); } static int local_copy_file(char *dst, char *src) { FILE *fdest, *fsrc; char line[BUFSIZ]; fdest = fopen(dst, "w"); if (fdest == NULL) return (-1); fsrc = fopen(src, "r"); if (fsrc == NULL) { (void) fclose(fdest); return (-1); } while (fread(line, sizeof (line), 1, fsrc)) (void) fprintf(fdest, "%s\n", line); (void) fclose(fsrc); (void) fclose(fdest); return (0); } static int remove_uuid(char *keyname) { int ret = 0; FILE *fp, *newfp; char fname[MAXPATHLEN]; char line[BUFSIZ], key[BUFSIZ], idstr[BUFSIZ]; char *tmpfname; char *p = get_tpm_keystore_path(); if (p == NULL) return (-1); (void) snprintf(fname, sizeof (fname), "%s/%s", p, TPMTOK_UUID_INDEX_FILENAME); fp = fopen(fname, "r"); if (fp == NULL) { return (-1); } tmpfname = tempnam("/tmp", "tpmtok"); newfp = fopen(tmpfname, "w+"); if (newfp == NULL) { free(tmpfname); (void) fclose(fp); return (-1); } while (!feof(fp)) { (void) fgets(line, sizeof (line), fp); if (sscanf(line, "%1024s %1024s", key, idstr) == 2) { if (strcmp(key, keyname)) (void) fprintf(newfp, "%s\n", line); } } (void) fclose(fp); (void) fclose(newfp); if (local_copy_file(fname, tmpfname) == 0) (void) unlink(tmpfname); free(tmpfname); return (ret); } static int find_uuid(char *keyname, TSS_UUID *uu) { int ret = 0, found = 0; FILE *fp = NULL; char fname[MAXPATHLEN]; char line[BUFSIZ], key[BUFSIZ], idstr[BUFSIZ]; uuid_t uuid; char *p = get_tpm_keystore_path(); if (p == NULL) return (-1); tss_uuid_convert_from(uu, uuid); (void) snprintf(fname, sizeof (fname), "%s/%s", p, TPMTOK_UUID_INDEX_FILENAME); /* Open UUID Index file */ fp = fopen(fname, "r"); if (fp == NULL) { if (errno == ENOENT) { /* initialize the file */ fp = fopen(fname, "w"); if (fp != NULL) (void) fclose(fp); } return (-1); } while (!feof(fp)) { (void) fgets(line, sizeof (line), fp); if (sscanf(line, "%1024s %1024s", key, idstr) == 2) { if (strcmp(key, keyname) == 0) { ret = uuid_parse(idstr, uuid); if (ret == 0) { found = 1; tss_uuid_convert_to(uu, uuid); } break; } } } (void) fclose(fp); if (!found) ret = -1; return (ret); } static int local_uuid_is_null(TSS_UUID *uu) { uuid_t uuid; int nulluuid; tss_uuid_convert_from(uu, uuid); nulluuid = uuid_is_null(uuid); return (nulluuid); } static int add_uuid(char *keyname, TSS_UUID *uu) { FILE *fp = NULL; char fname[MAXPATHLEN]; char idstr[BUFSIZ]; uuid_t uuid; char *p = get_tpm_keystore_path(); if (p == NULL) return (-1); tss_uuid_convert_from(uu, uuid); if (uuid_is_null(uuid)) return (-1); uuid_unparse(uuid, idstr); (void) snprintf(fname, sizeof (fname), "%s/%s", p, TPMTOK_UUID_INDEX_FILENAME); fp = fopen(fname, "a"); if (fp == NULL) return (-1); (void) fprintf(fp, "%s %s\n", keyname, idstr); (void) fclose(fp); return (0); } static UINT32 util_get_keysize_flag(CK_ULONG size) { switch (size) { case 512: return (TSS_KEY_SIZE_512); break; case 1024: return (TSS_KEY_SIZE_1024); break; case 2048: return (TSS_KEY_SIZE_2048); break; default: break; } return (0); } /* make sure the public exponent attribute is 65537 */ static CK_ULONG util_check_public_exponent(TEMPLATE *tmpl) { CK_BBOOL flag; CK_ATTRIBUTE *publ_exp_attr; CK_BYTE pubexp_bytes[] = { 1, 0, 1 }; CK_ULONG publ_exp, rc = 1; flag = template_attribute_find(tmpl, CKA_PUBLIC_EXPONENT, &publ_exp_attr); if (!flag) { LogError1("Couldn't find public exponent attribute"); return (CKR_TEMPLATE_INCOMPLETE); } switch (publ_exp_attr->ulValueLen) { case 3: rc = memcmp(pubexp_bytes, publ_exp_attr->pValue, 3); break; case sizeof (CK_ULONG): publ_exp = *((CK_ULONG *)publ_exp_attr->pValue); if (publ_exp == 65537) rc = 0; break; default: break; } return (rc); } TSS_RESULT set_public_modulus(TSS_HCONTEXT hContext, TSS_HKEY hKey, unsigned long size_n, unsigned char *n) { UINT64 offset; UINT32 blob_size; BYTE *blob, pub_blob[1024]; TCPA_PUBKEY pub_key; TSS_RESULT result; /* Get the TCPA_PUBKEY blob from the key object. */ result = Tspi_GetAttribData(hKey, TSS_TSPATTRIB_KEY_BLOB, TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY, &blob_size, &blob); if (result != TSS_SUCCESS) { stlogit("Tspi_GetAttribData failed: rc=0x%0x - %s\n", result, Trspi_Error_String(result)); return (result); } offset = 0; result = Trspi_UnloadBlob_PUBKEY(&offset, blob, &pub_key); if (result != TSS_SUCCESS) { stlogit("Trspi_UnloadBlob_PUBKEY failed: rc=0x%0x - %s\n", result, Trspi_Error_String(result)); return (result); } Tspi_Context_FreeMemory(hContext, blob); /* Free the first dangling reference, putting 'n' in its place */ free(pub_key.pubKey.key); pub_key.pubKey.keyLength = size_n; pub_key.pubKey.key = n; offset = 0; Trspi_LoadBlob_PUBKEY(&offset, pub_blob, &pub_key); /* Free the second dangling reference */ free(pub_key.algorithmParms.parms); /* set the public key data in the TSS object */ result = Tspi_SetAttribData(hKey, TSS_TSPATTRIB_KEY_BLOB, TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY, (UINT32)offset, pub_blob); if (result != TSS_SUCCESS) { stlogit("Tspi_SetAttribData failed: rc=0x%0x - %s\n", result, Trspi_Error_String(result)); return (result); } return (result); } /* * Get details about the TPM to put into the token_info structure. */ CK_RV token_get_tpm_info(TSS_HCONTEXT hContext, TOKEN_DATA *td) { TSS_RESULT result; TPM_CAPABILITY_AREA capArea = TSS_TPMCAP_VERSION_VAL; UINT32 datalen; BYTE *data; TSS_HTPM hTPM; if ((result = Tspi_Context_GetTpmObject(hContext, &hTPM))) { stlogit("Tspi_Context_GetTpmObject: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } if ((result = Tspi_TPM_GetCapability(hTPM, capArea, 0, NULL, &datalen, &data)) != 0 || datalen == 0 || data == NULL) { stlogit("Tspi_Context_GetCapability: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } if (datalen > sizeof (tpmvinfo)) { Tspi_Context_FreeMemory(hContext, data); return (CKR_FUNCTION_FAILED); } (void) memcpy(&tpmvinfo, (void *)data, datalen); bzero(td->token_info.manufacturerID, sizeof (td->token_info.manufacturerID)); (void) memset(td->token_info.manufacturerID, ' ', sizeof (td->token_info.manufacturerID) - 1); (void) memcpy(td->token_info.manufacturerID, tpmvinfo.tpmVendorID, sizeof (tpmvinfo.tpmVendorID)); (void) memset(td->token_info.label, ' ', sizeof (td->token_info.label) - 1); (void) memcpy(td->token_info.label, "TPM", 3); td->token_info.hardwareVersion.major = tpmvinfo.version.major; td->token_info.hardwareVersion.minor = tpmvinfo.version.minor; td->token_info.firmwareVersion.major = tpmvinfo.version.revMajor; td->token_info.firmwareVersion.minor = tpmvinfo.version.revMinor; Tspi_Context_FreeMemory(hContext, data); return (CKR_OK); } /*ARGSUSED*/ CK_RV token_specific_session(CK_SLOT_ID slotid) { return (CKR_OK); } CK_RV token_rng(TSS_HCONTEXT hContext, CK_BYTE *output, CK_ULONG bytes) { TSS_RESULT rc; TSS_HTPM hTPM; BYTE *random_bytes = NULL; if ((rc = Tspi_Context_GetTpmObject(hContext, &hTPM))) { stlogit("Tspi_Context_GetTpmObject: 0x%0x - %s", rc, Trspi_Error_String(rc)); return (CKR_FUNCTION_FAILED); } if ((rc = Tspi_TPM_GetRandom(hTPM, bytes, &random_bytes))) { stlogit("Tspi_TPM_GetRandom: 0x%0x - %s", rc, Trspi_Error_String(rc)); return (CKR_FUNCTION_FAILED); } (void) memcpy(output, random_bytes, bytes); Tspi_Context_FreeMemory(hContext, random_bytes); return (CKR_OK); } TSS_RESULT open_tss_context(TSS_HCONTEXT *pContext) { TSS_RESULT result; if ((result = Tspi_Context_Create(pContext))) { stlogit("Tspi_Context_Create: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } if ((result = Tspi_Context_Connect(*pContext, NULL))) { stlogit("Tspi_Context_Connect: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } return (result); } /*ARGSUSED*/ static CK_RV token_specific_init(char *Correlator, CK_SLOT_ID SlotNumber, TSS_HCONTEXT *hContext) { TSS_RESULT result; result = open_tss_context(hContext); if (result) return (CKR_FUNCTION_FAILED); if ((result = Tspi_Context_GetDefaultPolicy(*hContext, &hDefaultPolicy))) { stlogit("Tspi_Context_GetDefaultPolicy: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } local_uuid_clear(&publicRootKeyUUID); local_uuid_clear(&privateRootKeyUUID); local_uuid_clear(&publicLeafKeyUUID); local_uuid_clear(&privateLeafKeyUUID); result = token_get_tpm_info(*hContext, nv_token_data); return (result); } /* * Given a modulus and prime from an RSA key, create a TSS_HKEY object by * wrapping the RSA key with a key from the TPM (SRK or other previously stored * key). */ static CK_RV token_wrap_sw_key( TSS_HCONTEXT hContext, int size_n, unsigned char *n, int size_p, unsigned char *p, TSS_HKEY hParentKey, TSS_FLAG initFlags, TSS_HKEY *phKey) { TSS_RESULT result; UINT32 key_size; key_size = util_get_keysize_flag(size_n * 8); if (initFlags == 0) { return (CKR_FUNCTION_FAILED); } /* create the TSS key object */ result = Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_RSAKEY, TSS_KEY_MIGRATABLE | initFlags | key_size, phKey); if (result != TSS_SUCCESS) { stlogit("Tspi_Context_CreateObject: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } result = set_public_modulus(hContext, *phKey, size_n, n); if (result != TSS_SUCCESS) { Tspi_Context_CloseObject(hContext, *phKey); *phKey = NULL_HKEY; return (CKR_FUNCTION_FAILED); } /* set the private key data in the TSS object */ result = Tspi_SetAttribData(*phKey, TSS_TSPATTRIB_KEY_BLOB, TSS_TSPATTRIB_KEYBLOB_PRIVATE_KEY, size_p, p); if (result != TSS_SUCCESS) { stlogit("Tspi_SetAttribData: 0x%x - %s", result, Trspi_Error_String(result)); Tspi_Context_CloseObject(hContext, *phKey); *phKey = NULL_HKEY; return (CKR_FUNCTION_FAILED); } result = tss_assign_secret_key_policy(hContext, TSS_POLICY_MIGRATION, *phKey, NULL); if (TPMTOK_TSS_KEY_TYPE(initFlags) == TSS_KEY_TYPE_LEGACY) { if ((result = Tspi_SetAttribUint32(*phKey, TSS_TSPATTRIB_KEY_INFO, TSS_TSPATTRIB_KEYINFO_ENCSCHEME, TSS_ES_RSAESPKCSV15))) { stlogit("Tspi_SetAttribUint32: 0x%0x - %s\n", result, Trspi_Error_String(result)); Tspi_Context_CloseObject(hContext, *phKey); return (CKR_FUNCTION_FAILED); } if ((result = Tspi_SetAttribUint32(*phKey, TSS_TSPATTRIB_KEY_INFO, TSS_TSPATTRIB_KEYINFO_SIGSCHEME, TSS_SS_RSASSAPKCS1V15_DER))) { stlogit("Tspi_SetAttribUint32: 0x%0x - %s\n", result, Trspi_Error_String(result)); Tspi_Context_CloseObject(hContext, *phKey); return (CKR_FUNCTION_FAILED); } } result = Tspi_Key_WrapKey(*phKey, hParentKey, NULL_HPCRS); if (result != TSS_SUCCESS) { stlogit("Tspi_Key_WrapKey: 0x%0x - %s", result, Trspi_Error_String(result)); Tspi_Context_CloseObject(hContext, *phKey); *phKey = NULL_HKEY; return (CKR_FUNCTION_FAILED); } return (CKR_OK); } /* * Create a TPM key blob for an imported key. This function is only called when * a key is in active use, so any failure should trickle through. */ static CK_RV token_wrap_key_object(TSS_HCONTEXT hContext, CK_OBJECT_HANDLE ckObject, TSS_HKEY hParentKey, TSS_HKEY *phKey) { CK_RV rc = CKR_OK; CK_ATTRIBUTE *attr = NULL, *new_attr, *prime_attr; CK_ULONG class, key_type; OBJECT *obj; TSS_RESULT result; TSS_FLAG initFlags = 0; BYTE *rgbBlob; UINT32 ulBlobLen; if ((rc = object_mgr_find_in_map1(hContext, ckObject, &obj))) { return (rc); } /* if the object isn't a key, fail */ if (template_attribute_find(obj->template, CKA_KEY_TYPE, &attr) == FALSE) { return (CKR_TEMPLATE_INCOMPLETE); } key_type = *((CK_ULONG *)attr->pValue); if (key_type != CKK_RSA) { return (CKR_TEMPLATE_INCONSISTENT); } if (template_attribute_find(obj->template, CKA_CLASS, &attr) == FALSE) { return (CKR_TEMPLATE_INCOMPLETE); } class = *((CK_ULONG *)attr->pValue); if (class == CKO_PRIVATE_KEY) { /* * In order to create a full TSS key blob using a PKCS#11 * private key object, we need one of the two primes, the * modulus and the private exponent and we need the public * exponent to be correct. */ /* * Check the least likely attribute to exist first, the * primes. */ if (template_attribute_find(obj->template, CKA_PRIME_1, &prime_attr) == FALSE) { if (template_attribute_find(obj->template, CKA_PRIME_2, &prime_attr) == FALSE) { return (CKR_TEMPLATE_INCOMPLETE); } } /* Make sure the public exponent is usable */ if ((rc = util_check_public_exponent(obj->template))) { return (CKR_TEMPLATE_INCONSISTENT); } /* get the modulus */ if (template_attribute_find(obj->template, CKA_MODULUS, &attr) == FALSE) { return (CKR_TEMPLATE_INCOMPLETE); } /* make sure the key size is usable */ initFlags = util_get_keysize_flag(attr->ulValueLen * 8); if (initFlags == 0) { return (CKR_TEMPLATE_INCONSISTENT); } /* generate the software based key */ if ((rc = token_wrap_sw_key(hContext, (int)attr->ulValueLen, attr->pValue, (int)prime_attr->ulValueLen, prime_attr->pValue, hParentKey, TSS_KEY_TYPE_LEGACY | TSS_KEY_NO_AUTHORIZATION, phKey))) { return (rc); } } else if (class == CKO_PUBLIC_KEY) { /* Make sure the public exponent is usable */ if ((util_check_public_exponent(obj->template))) { return (CKR_TEMPLATE_INCONSISTENT); } /* grab the modulus to put into the TSS key object */ if (template_attribute_find(obj->template, CKA_MODULUS, &attr) == FALSE) { return (CKR_TEMPLATE_INCONSISTENT); } /* make sure the key size is usable */ initFlags = util_get_keysize_flag(attr->ulValueLen * 8); if (initFlags == 0) { return (CKR_TEMPLATE_INCONSISTENT); } initFlags |= TSS_KEY_MIGRATABLE | TSS_KEY_NO_AUTHORIZATION | TSS_KEY_TYPE_LEGACY; if ((result = Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_RSAKEY, initFlags, phKey))) { stlogit("Tspi_Context_CreateObject: 0x%0x - %s", result, Trspi_Error_String(result)); return (result); } if ((result = set_public_modulus(hContext, *phKey, attr->ulValueLen, attr->pValue))) { Tspi_Context_CloseObject(hContext, *phKey); *phKey = NULL_HKEY; return (CKR_FUNCTION_FAILED); } result = tss_assign_secret_key_policy(hContext, TSS_POLICY_MIGRATION, *phKey, NULL); if (result) { Tspi_Context_CloseObject(hContext, *phKey); *phKey = NULL_HKEY; return (CKR_FUNCTION_FAILED); } result = set_legacy_key_params(*phKey); if (result) { Tspi_Context_CloseObject(hContext, *phKey); *phKey = NULL_HKEY; return (CKR_FUNCTION_FAILED); } } else { return (CKR_FUNCTION_FAILED); } /* grab the entire key blob to put into the PKCS#11 object */ if ((result = Tspi_GetAttribData(*phKey, TSS_TSPATTRIB_KEY_BLOB, TSS_TSPATTRIB_KEYBLOB_BLOB, &ulBlobLen, &rgbBlob))) { stlogit("Tspi_GetAttribData: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } /* insert the key blob into the object */ if ((rc = build_attribute(CKA_IBM_OPAQUE, rgbBlob, ulBlobLen, &new_attr))) { Tspi_Context_FreeMemory(hContext, rgbBlob); return (rc); } (void) template_update_attribute(obj->template, new_attr); Tspi_Context_FreeMemory(hContext, rgbBlob); /* * If this is a token object, save it with the new attribute * so that we don't have to go down this path again. */ if (!object_is_session_object(obj)) { rc = save_token_object(hContext, obj); } return (rc); } static TSS_RESULT tss_assign_secret_key_policy(TSS_HCONTEXT hContext, TSS_FLAG policyType, TSS_HKEY hKey, CK_CHAR *passHash) { TSS_RESULT result; TSS_HPOLICY hPolicy; if ((result = Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_POLICY, policyType, &hPolicy))) { stlogit("Tspi_Context_CreateObject: 0x%0x - %s", result, Trspi_Error_String(result)); return (result); } if ((result = Tspi_Policy_AssignToObject(hPolicy, hKey))) { stlogit("Tspi_Policy_AssignToObject: 0x%0x - %s", result, Trspi_Error_String(result)); goto done; } if (passHash == NULL) { result = Tspi_Policy_SetSecret(hPolicy, TSS_SECRET_MODE_NONE, 0, NULL); } else { result = Tspi_Policy_SetSecret(hPolicy, TSS_SECRET_MODE_SHA1, SHA1_DIGEST_LENGTH, passHash); } if (result != TSS_SUCCESS) { stlogit("Tspi_Policy_SetSecret: 0x%0x - %s", result, Trspi_Error_String(result)); goto done; } done: if (result != TSS_SUCCESS) Tspi_Context_CloseObject(hContext, hPolicy); return (result); } /* * Take a key from the TSS store (on-disk) and load it into the TPM, wrapped * by an already TPM-resident key and protected with a PIN (optional). */ static CK_RV token_load_key( TSS_HCONTEXT hContext, CK_OBJECT_HANDLE ckKey, TSS_HKEY hParentKey, CK_CHAR_PTR passHash, TSS_HKEY *phKey) { TSS_RESULT result; CK_RV rc; /* * The key blob wasn't found, load the parts of the key * from the object DB and create a new key object that * gets loaded into the TPM, wrapped with the parent key. */ if ((rc = token_wrap_key_object(hContext, ckKey, hParentKey, phKey))) { return (rc); } /* * Assign the PIN hash (optional) to the newly loaded key object, * if this PIN is incorrect, the TPM will not be able to decrypt * the private key and use it. */ result = tss_assign_secret_key_policy(hContext, TSS_POLICY_USAGE, *phKey, passHash); return (result); } /* * Load the SRK into the TPM by referencing its well-known UUID and using the * default SRK PIN (20 bytes of 0x00). * * NOTE - if the SRK PIN is changed by an administrative tool, this code will * fail because it assumes that the well-known PIN is still being used. */ static TSS_RESULT token_load_srk(TSS_HCONTEXT hContext, TSS_HKEY *hSRK) { TSS_HPOLICY hPolicy; TSS_RESULT result; TSS_UUID SRK_UUID = TSS_UUID_SRK; BYTE wellKnown[] = TSS_WELL_KNOWN_SECRET; TSS_HTPM hTPM; if ((result = Tspi_Context_GetTpmObject(hContext, &hTPM))) { stlogit("Tspi_Context_GetTpmObject: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } /* load the SRK */ if ((result = Tspi_Context_LoadKeyByUUID(hContext, TSS_PS_TYPE_SYSTEM, SRK_UUID, hSRK))) { stlogit("Tspi_Context_LoadKeyByUUID: 0x%0x - %s", result, Trspi_Error_String(result)); goto done; } if ((result = Tspi_GetPolicyObject(*hSRK, TSS_POLICY_USAGE, &hPolicy))) { stlogit("Tspi_GetPolicyObject: 0x%0x - %s", result, Trspi_Error_String(result)); goto done; } if ((result = Tspi_Policy_SetSecret(hPolicy, TSS_SECRET_MODE_SHA1, sizeof (wellKnown), wellKnown))) { stlogit("Tspi_Policy_SetSecret: 0x%0x - %s", result, Trspi_Error_String(result)); goto done; } done: return (result); } static TSS_RESULT tss_find_and_load_key(TSS_HCONTEXT hContext, char *keyid, TSS_UUID *uuid, TSS_HKEY hParent, BYTE *hash, TSS_HKEY *hKey) { TSS_RESULT result; if (local_uuid_is_null(uuid) && find_uuid(keyid, uuid)) { /* The UUID was not created or saved yet */ return (1); } result = Tspi_Context_GetKeyByUUID(hContext, TSS_PS_TYPE_USER, *uuid, hKey); if (result) { stlogit("Tspi_Context_GetKeyByUUID: 0x%0x - %s", result, Trspi_Error_String(result)); return (result); } if (hash != NULL) { result = tss_assign_secret_key_policy(hContext, TSS_POLICY_USAGE, *hKey, (CK_BYTE *)hash); if (result) return (result); } result = Tspi_Key_LoadKey(*hKey, hParent); if (result) stlogit("Tspi_Key_LoadKey: 0x%0x - %s", result, Trspi_Error_String(result)); return (result); } static TSS_RESULT token_load_public_root_key(TSS_HCONTEXT hContext) { TSS_RESULT result; TSS_HKEY hSRK; if (hPublicRootKey != NULL_HKEY) return (TSS_SUCCESS); if ((result = token_load_srk(hContext, &hSRK))) { return (result); } result = tss_find_and_load_key(hContext, TPMTOK_PUBLIC_ROOT_KEY_ID, &publicRootKeyUUID, hSRK, NULL, &hPublicRootKey); if (result) return (result); return (result); } static TSS_RESULT set_legacy_key_params(TSS_HKEY hKey) { TSS_RESULT result; if ((result = Tspi_SetAttribUint32(hKey, TSS_TSPATTRIB_KEY_INFO, TSS_TSPATTRIB_KEYINFO_ENCSCHEME, TSS_ES_RSAESPKCSV15))) { stlogit("Tspi_SetAttribUint32: 0x%0x - %s", result, Trspi_Error_String(result)); return (result); } if ((result = Tspi_SetAttribUint32(hKey, TSS_TSPATTRIB_KEY_INFO, TSS_TSPATTRIB_KEYINFO_SIGSCHEME, TSS_SS_RSASSAPKCS1V15_DER))) { stlogit("Tspi_SetAttribUint32: 0x%0x - %s", result, Trspi_Error_String(result)); return (result); } return (result); } static TSS_RESULT tss_generate_key(TSS_HCONTEXT hContext, TSS_FLAG initFlags, BYTE *passHash, TSS_HKEY hParentKey, TSS_HKEY *phKey) { TSS_RESULT result; TSS_HPOLICY hMigPolicy; if ((result = Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_RSAKEY, initFlags, phKey))) { stlogit("Tspi_Context_CreateObject: 0x%0x - %s", result, Trspi_Error_String(result)); return (result); } result = tss_assign_secret_key_policy(hContext, TSS_POLICY_USAGE, *phKey, passHash); if (result) { Tspi_Context_CloseObject(hContext, *phKey); return (result); } if (TPMTOK_TSS_KEY_MIG_TYPE(initFlags) == TSS_KEY_MIGRATABLE) { if ((result = Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_POLICY, TSS_POLICY_MIGRATION, &hMigPolicy))) { stlogit("Tspi_Context_CreateObject: 0x%0x - %s", result, Trspi_Error_String(result)); Tspi_Context_CloseObject(hContext, *phKey); return (result); } if (passHash == NULL) { result = Tspi_Policy_SetSecret(hMigPolicy, TSS_SECRET_MODE_NONE, 0, NULL); } else { result = Tspi_Policy_SetSecret(hMigPolicy, TSS_SECRET_MODE_SHA1, 20, passHash); } if (result != TSS_SUCCESS) { stlogit("Tspi_Policy_SetSecret: 0x%0x - %s", result, Trspi_Error_String(result)); Tspi_Context_CloseObject(hContext, *phKey); Tspi_Context_CloseObject(hContext, hMigPolicy); return (result); } if ((result = Tspi_Policy_AssignToObject(hMigPolicy, *phKey))) { stlogit("Tspi_Policy_AssignToObject: 0x%0x - %s", result, Trspi_Error_String(result)); Tspi_Context_CloseObject(hContext, *phKey); Tspi_Context_CloseObject(hContext, hMigPolicy); return (result); } } if (TPMTOK_TSS_KEY_TYPE(initFlags) == TSS_KEY_TYPE_LEGACY) { result = set_legacy_key_params(*phKey); if (result) { Tspi_Context_CloseObject(hContext, *phKey); Tspi_Context_CloseObject(hContext, hMigPolicy); return (result); } } if ((result = Tspi_Key_CreateKey(*phKey, hParentKey, 0))) { stlogit("Tspi_Key_CreateKey: 0x%0x - %s", result, Trspi_Error_String(result)); Tspi_Context_CloseObject(hContext, *phKey); Tspi_Context_CloseObject(hContext, hMigPolicy); } return (result); } static TSS_RESULT tss_change_auth( TSS_HCONTEXT hContext, TSS_HKEY hObjectToChange, TSS_HKEY hParentObject, TSS_UUID objUUID, TSS_UUID parentUUID, CK_CHAR *passHash) { TSS_RESULT result; TSS_HPOLICY hPolicy; TSS_HKEY oldkey; if ((result = Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE, &hPolicy))) { stlogit("Tspi_Context_CreateObject: 0x%0x - %s", result, Trspi_Error_String(result)); return (result); } if ((result = Tspi_Policy_SetSecret(hPolicy, TSS_SECRET_MODE_SHA1, SHA1_DIGEST_LENGTH, passHash))) { stlogit("Tspi_Policy_SetSecret: 0x%0x - %s", result, Trspi_Error_String(result)); return (result); } if ((result = Tspi_ChangeAuth(hObjectToChange, hParentObject, hPolicy))) { stlogit("Tspi_ChangeAuth: 0x%0x - %s", result, Trspi_Error_String(result)); } /* * Update the PS key by unregistering the key UUID and then * re-registering with the same UUID. This forces the updated * auth data associated with the key to be stored in PS so * the new PIN can be used next time. */ if ((result = Tspi_Context_UnregisterKey(hContext, TSS_PS_TYPE_USER, objUUID, &oldkey))) stlogit("Tspi_Context_UnregisterKey: 0x%0x - %s", result, Trspi_Error_String(result)); if ((result = Tspi_Context_RegisterKey(hContext, hObjectToChange, TSS_PS_TYPE_USER, objUUID, TSS_PS_TYPE_USER, parentUUID))) stlogit("Tspi_Context_RegisterKey: 0x%0x - %s", result, Trspi_Error_String(result)); return (result); } static CK_RV token_generate_leaf_key(TSS_HCONTEXT hContext, int key_type, CK_CHAR_PTR passHash, TSS_HKEY *phKey) { CK_RV rc = CKR_FUNCTION_FAILED; TSS_RESULT result; TSS_HKEY hParentKey; TSS_UUID newuuid, parentUUID; char *keyid; TSS_FLAG initFlags = TSS_KEY_MIGRATABLE | TSS_KEY_TYPE_BIND | TSS_KEY_SIZE_2048 | TSS_KEY_AUTHORIZATION; switch (key_type) { case TPMTOK_PUBLIC_LEAF_KEY: hParentKey = hPublicRootKey; keyid = TPMTOK_PUBLIC_LEAF_KEY_ID; local_uuid_copy(&parentUUID, &publicRootKeyUUID); break; case TPMTOK_PRIVATE_LEAF_KEY: hParentKey = hPrivateRootKey; keyid = TPMTOK_PRIVATE_LEAF_KEY_ID; local_uuid_copy(&parentUUID, &privateRootKeyUUID); break; default: stlogit("Unknown key type 0x%0x", key_type); goto done; break; } if (result = tss_generate_key(hContext, initFlags, passHash, hParentKey, phKey)) { return (rc); } /* * - generate newUUID * - Tspi_Context_RegisterKey(hContext, hPrivateRootKey, * USER, newUUID, USER, parentUUID); * - store newUUID */ (void) local_uuid_generate(&newuuid); result = Tspi_Context_RegisterKey(hContext, *phKey, TSS_PS_TYPE_USER, newuuid, TSS_PS_TYPE_USER, parentUUID); if (result == TSS_SUCCESS) { int ret; /* * Add the UUID to the token UUID index. */ ret = add_uuid(keyid, &newuuid); if (ret) result = Tspi_Context_UnregisterKey(hContext, TSS_PS_TYPE_USER, newuuid, phKey); else rc = CKR_OK; } done: return (rc); } /* * PINs are verified by attempting to bind/unbind random data using a * TPM resident key that has the PIN being tested assigned as its "secret". * If the PIN is incorrect, the unbind operation will fail. */ static CK_RV token_verify_pin(TSS_HCONTEXT hContext, TSS_HKEY hKey) { TSS_HENCDATA hEncData; UINT32 ulUnboundDataLen; BYTE *rgbUnboundData = NULL; BYTE rgbData[16]; TSS_RESULT result; CK_RV rc = CKR_FUNCTION_FAILED; if ((result = Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_ENCDATA, TSS_ENCDATA_BIND, &hEncData))) { stlogit("Tspi_Context_CreateObject: 0x%0x - %s", result, Trspi_Error_String(result)); goto done; } /* Use some random data */ rc = token_rng(hContext, rgbData, sizeof (rgbData)); if (rc) goto done; if ((result = Tspi_Data_Bind(hEncData, hKey, sizeof (rgbData), rgbData))) { stlogit("Tspi_Data_Bind: 0x%0x - %s", result, Trspi_Error_String(result)); goto done; } /* unbind the junk data to test the key's auth data */ result = Tspi_Data_Unbind(hEncData, hKey, &ulUnboundDataLen, &rgbUnboundData); if (result == TPM_E_AUTHFAIL) { rc = CKR_PIN_INCORRECT; stlogit("Tspi_Data_Unbind: 0x%0x - %s", result, Trspi_Error_String(result)); goto done; } else if (result != TSS_SUCCESS) { stlogit("Tspi_Data_Unbind: 0x%0x - %s", result, Trspi_Error_String(result)); rc = CKR_FUNCTION_FAILED; goto done; } if (memcmp(rgbUnboundData, rgbData, ulUnboundDataLen)) rc = CKR_PIN_INCORRECT; else rc = CKR_OK; done: if (rgbUnboundData != NULL) Tspi_Context_FreeMemory(hContext, rgbUnboundData); Tspi_Context_CloseObject(hContext, hEncData); return (rc); } static CK_RV token_create_private_tree(TSS_HCONTEXT hContext, CK_BYTE *pinHash) { CK_RV rc; TSS_RESULT result; int ret; TSS_FLAG initFlags = TSS_KEY_SIZE_2048 | TSS_KEY_NO_AUTHORIZATION | TSS_KEY_TYPE_STORAGE; TSS_UUID SRK_UUID = TSS_UUID_SRK; TSS_HKEY hSRK; if (token_load_srk(hContext, &hSRK)) return (CKR_FUNCTION_FAILED); /* * - create UUID privateRootKeyUUID * - Tspi_Context_RegisterKey(hContext, hPrivateRootKey, * USER, privateRootKeyUUID, system, UUID_SRK); * - store privateRootKeyUUID in users private token space. */ if ((result = tss_generate_key(hContext, initFlags, NULL, hSRK, &hPrivateRootKey))) { return (result); } if (local_uuid_is_null(&privateRootKeyUUID)) local_uuid_generate(&privateRootKeyUUID); result = Tspi_Context_RegisterKey(hContext, hPrivateRootKey, TSS_PS_TYPE_USER, privateRootKeyUUID, TSS_PS_TYPE_SYSTEM, SRK_UUID); if (result) { local_uuid_clear(&privateRootKeyUUID); return (result); } ret = add_uuid(TPMTOK_PRIVATE_ROOT_KEY_ID, &privateRootKeyUUID); if (ret) { result = Tspi_Context_UnregisterKey(hContext, TSS_PS_TYPE_USER, privateRootKeyUUID, &hPrivateRootKey); return (CKR_FUNCTION_FAILED); } if ((result = Tspi_Key_LoadKey(hPrivateRootKey, hSRK))) { stlogit("Tspi_Key_LoadKey: 0x%0x - %s", result, Trspi_Error_String(result)); Tspi_Context_CloseObject(hContext, hPrivateRootKey); (void) remove_uuid(TPMTOK_PRIVATE_ROOT_KEY_ID); local_uuid_clear(&privateRootKeyUUID); hPrivateRootKey = NULL_HKEY; return (CKR_FUNCTION_FAILED); } /* generate the private leaf key */ if ((rc = token_generate_leaf_key(hContext, TPMTOK_PRIVATE_LEAF_KEY, pinHash, &hPrivateLeafKey))) { return (rc); } if ((result = Tspi_Key_LoadKey(hPrivateLeafKey, hPrivateRootKey))) { stlogit("Tspi_Key_LoadKey: 0x%0x - %s", result, Trspi_Error_String(result)); (void) Tspi_Context_UnregisterKey(hContext, TSS_PS_TYPE_USER, privateLeafKeyUUID, &hPrivateLeafKey); (void) remove_uuid(TPMTOK_PRIVATE_LEAF_KEY_ID); local_uuid_clear(&privateLeafKeyUUID); (void) Tspi_Context_UnregisterKey(hContext, TSS_PS_TYPE_USER, privateRootKeyUUID, &hPrivateRootKey); (void) remove_uuid(TPMTOK_PRIVATE_ROOT_KEY_ID); local_uuid_clear(&privateRootKeyUUID); Tspi_Context_CloseObject(hContext, hPrivateRootKey); hPrivateRootKey = NULL_HKEY; Tspi_Context_CloseObject(hContext, hPrivateLeafKey); hPrivateRootKey = NULL_HKEY; return (CKR_FUNCTION_FAILED); } return (rc); } static CK_RV token_create_public_tree(TSS_HCONTEXT hContext, CK_BYTE *pinHash) { CK_RV rc; TSS_RESULT result; int ret; TSS_FLAG initFlags = TSS_KEY_SIZE_2048 | TSS_KEY_NO_AUTHORIZATION | TSS_KEY_TYPE_STORAGE; TSS_UUID srk_uuid = TSS_UUID_SRK; TSS_HKEY hSRK; if (token_load_srk(hContext, &hSRK)) return (CKR_FUNCTION_FAILED); /* * - create publicRootKeyUUID * - Tspi_Context_RegisterKey(hContext, hPublicRootKey, * USER, publicRootKeyUUID, system, UUID_SRK); * - store publicRootKeyUUID in users private token space. */ if ((result = tss_generate_key(hContext, initFlags, NULL, hSRK, &hPublicRootKey))) { return (CKR_FUNCTION_FAILED); } if (local_uuid_is_null(&publicRootKeyUUID)) local_uuid_generate(&publicRootKeyUUID); result = Tspi_Context_RegisterKey(hContext, hPublicRootKey, TSS_PS_TYPE_USER, publicRootKeyUUID, TSS_PS_TYPE_SYSTEM, srk_uuid); if (result) { local_uuid_clear(&publicRootKeyUUID); return (CKR_FUNCTION_FAILED); } ret = add_uuid(TPMTOK_PUBLIC_ROOT_KEY_ID, &publicRootKeyUUID); if (ret) { result = Tspi_Context_UnregisterKey(hContext, TSS_PS_TYPE_USER, publicRootKeyUUID, &hPublicRootKey); /* does result matter here? */ return (CKR_FUNCTION_FAILED); } /* Load the newly created publicRootKey into the TPM using the SRK */ if ((result = Tspi_Key_LoadKey(hPublicRootKey, hSRK))) { stlogit("Tspi_Key_LoadKey: 0x%x - %s", result, Trspi_Error_String(result)); Tspi_Context_CloseObject(hContext, hPublicRootKey); hPublicRootKey = NULL_HKEY; return (CKR_FUNCTION_FAILED); } /* create the SO's leaf key */ if ((rc = token_generate_leaf_key(hContext, TPMTOK_PUBLIC_LEAF_KEY, pinHash, &hPublicLeafKey))) { return (rc); } if ((result = Tspi_Key_LoadKey(hPublicLeafKey, hPublicRootKey))) { stlogit("Tspi_Key_LoadKey: 0x%0x - %s", result, Trspi_Error_String(result)); /* Unregister keys and clear UUIDs */ (void) Tspi_Context_UnregisterKey(hContext, TSS_PS_TYPE_USER, publicLeafKeyUUID, &hPublicLeafKey); (void) remove_uuid(TPMTOK_PUBLIC_LEAF_KEY_ID); (void) Tspi_Context_UnregisterKey(hContext, TSS_PS_TYPE_USER, publicRootKeyUUID, &hPublicRootKey); (void) remove_uuid(TPMTOK_PUBLIC_ROOT_KEY_ID); Tspi_Context_CloseObject(hContext, hPublicRootKey); hPublicRootKey = NULL_HKEY; Tspi_Context_CloseObject(hContext, hPublicLeafKey); hPublicLeafKey = NULL_HKEY; return (CKR_FUNCTION_FAILED); } return (rc); } CK_RV token_specific_login( TSS_HCONTEXT hContext, CK_USER_TYPE userType, CK_CHAR_PTR pPin, CK_ULONG ulPinLen) { CK_RV rc; CK_BYTE hash_sha[SHA1_DIGEST_LENGTH]; TSS_RESULT result; TSS_HKEY hSRK; /* Make sure the SRK is loaded into the TPM */ if ((result = token_load_srk(hContext, &hSRK))) { return (CKR_FUNCTION_FAILED); } if ((rc = compute_sha(pPin, ulPinLen, hash_sha))) { return (CKR_FUNCTION_FAILED); } if (userType == CKU_USER) { /* * If the public root key doesn't exist yet, * the SO hasn't init'd the token. */ if ((result = token_load_public_root_key(hContext))) { if (result == TPM_E_DECRYPT_ERROR) { return (CKR_USER_PIN_NOT_INITIALIZED); } } /* * - find privateRootKeyUUID * - load by UUID (SRK parent) */ if (local_uuid_is_null(&privateRootKeyUUID) && find_uuid(TPMTOK_PRIVATE_ROOT_KEY_ID, &privateRootKeyUUID)) { if (memcmp(hash_sha, default_user_pin_sha, SHA1_DIGEST_LENGTH)) return (CKR_PIN_INCORRECT); not_initialized = 1; return (CKR_OK); } if ((rc = verify_user_pin(hContext, hash_sha))) { return (rc); } (void) memcpy(current_user_pin_sha, hash_sha, SHA1_DIGEST_LENGTH); rc = load_private_token_objects(hContext); if (rc == CKR_OK) { (void) XProcLock(xproclock); global_shm->priv_loaded = TRUE; (void) XProcUnLock(xproclock); } } else { /* * SO login logic: * * - find publicRootKey UUID * - load by UUID wrap with hSRK from above */ if (local_uuid_is_null(&publicRootKeyUUID) && find_uuid(TPMTOK_PUBLIC_ROOT_KEY_ID, &publicRootKeyUUID)) { if (memcmp(hash_sha, default_so_pin_sha, SHA1_DIGEST_LENGTH)) return (CKR_PIN_INCORRECT); not_initialized = 1; return (CKR_OK); } if (hPublicRootKey == NULL_HKEY) { result = tss_find_and_load_key( hContext, TPMTOK_PUBLIC_ROOT_KEY_ID, &publicRootKeyUUID, hSRK, NULL, &hPublicRootKey); if (result) return (CKR_FUNCTION_FAILED); } /* find, load the public leaf key */ if (hPublicLeafKey == NULL_HKEY) { result = tss_find_and_load_key( hContext, TPMTOK_PUBLIC_LEAF_KEY_ID, &publicLeafKeyUUID, hPublicRootKey, hash_sha, &hPublicLeafKey); if (result) return (CKR_FUNCTION_FAILED); } if ((rc = token_verify_pin(hContext, hPublicLeafKey))) { return (rc); } (void) memcpy(current_so_pin_sha, hash_sha, SHA1_DIGEST_LENGTH); } return (rc); } CK_RV token_specific_logout(TSS_HCONTEXT hContext) { if (hPrivateLeafKey != NULL_HKEY) { Tspi_Key_UnloadKey(hPrivateLeafKey); hPrivateLeafKey = NULL_HKEY; } else if (hPublicLeafKey != NULL_HKEY) { Tspi_Key_UnloadKey(hPublicLeafKey); hPublicLeafKey = NULL_HKEY; } local_uuid_clear(&publicRootKeyUUID); local_uuid_clear(&publicLeafKeyUUID); local_uuid_clear(&privateRootKeyUUID); local_uuid_clear(&privateLeafKeyUUID); (void) memset(current_so_pin_sha, 0, SHA1_DIGEST_LENGTH); (void) memset(current_user_pin_sha, 0, SHA1_DIGEST_LENGTH); (void) object_mgr_purge_private_token_objects(hContext); return (CKR_OK); } /*ARGSUSED*/ CK_RV token_specific_init_pin(TSS_HCONTEXT hContext, CK_CHAR_PTR pPin, CK_ULONG ulPinLen) { /* * Since the SO must log in before calling C_InitPIN, we will * be able to return (CKR_OK) automatically here. * This is because the USER key structure is created at the * time of her first login, not at C_InitPIN time. */ return (CKR_OK); } static CK_RV check_pin_properties(CK_USER_TYPE userType, CK_BYTE *pinHash, CK_ULONG ulPinLen) { /* make sure the new PIN is different */ if (userType == CKU_USER) { if (!memcmp(pinHash, default_user_pin_sha, SHA1_DIGEST_LENGTH)) { LogError1("new PIN must not be the default"); return (CKR_PIN_INVALID); } } else { if (!memcmp(pinHash, default_so_pin_sha, SHA1_DIGEST_LENGTH)) { LogError1("new PIN must not be the default"); return (CKR_PIN_INVALID); } } if (ulPinLen > MAX_PIN_LEN || ulPinLen < MIN_PIN_LEN) { LogError1("New PIN is out of size range"); return (CKR_PIN_LEN_RANGE); } return (CKR_OK); } /* * This function is called from set_pin only, where a non-logged-in public * session can provide the user pin which must be verified. This function * assumes that the pin has already been set once, so there's no migration * path option or checking of the default user pin. */ static CK_RV verify_user_pin(TSS_HCONTEXT hContext, CK_BYTE *hash_sha) { CK_RV rc; TSS_RESULT result; TSS_HKEY hSRK; if (token_load_srk(hContext, &hSRK)) return (CKR_FUNCTION_FAILED); /* * Verify the user by loading the privateLeafKey * into the TPM (if it's not already) and then * call the verify_pin operation. * * The hashed PIN is assigned to the private leaf key. * If it is incorrect (not the same as the one originally * used when the key was created), the verify operation * will fail. */ if (hPrivateRootKey == NULL_HKEY) { result = tss_find_and_load_key( hContext, TPMTOK_PRIVATE_ROOT_KEY_ID, &privateRootKeyUUID, hSRK, NULL, &hPrivateRootKey); if (result) return (CKR_FUNCTION_FAILED); } if (hPrivateLeafKey == NULL_HKEY) { result = tss_find_and_load_key( hContext, TPMTOK_PRIVATE_LEAF_KEY_ID, &privateLeafKeyUUID, hPrivateRootKey, hash_sha, &hPrivateLeafKey); if (result) return (CKR_FUNCTION_FAILED); } /* * Verify that the PIN is correct by attempting to wrap/unwrap some * random data. */ if ((rc = token_verify_pin(hContext, hPrivateLeafKey))) { return (rc); } return (CKR_OK); } CK_RV token_specific_set_pin(ST_SESSION_HANDLE session, CK_CHAR_PTR pOldPin, CK_ULONG ulOldPinLen, CK_CHAR_PTR pNewPin, CK_ULONG ulNewPinLen) { SESSION *sess = session_mgr_find(session.sessionh); CK_BYTE oldpin_hash[SHA1_DIGEST_LENGTH]; CK_BYTE newpin_hash[SHA1_DIGEST_LENGTH]; CK_RV rc; TSS_HKEY hSRK; if (!sess) { return (CKR_SESSION_HANDLE_INVALID); } if ((rc = compute_sha(pOldPin, ulOldPinLen, oldpin_hash))) { return (CKR_FUNCTION_FAILED); } if ((rc = compute_sha(pNewPin, ulNewPinLen, newpin_hash))) { return (CKR_FUNCTION_FAILED); } if (token_load_srk(sess->hContext, &hSRK)) { return (CKR_FUNCTION_FAILED); } /* * From the PKCS#11 2.20 spec: "C_SetPIN modifies the PIN of * the user that is currently logged in, or the CKU_USER PIN * if the session is not logged in." * A non R/W session fails with CKR_SESSION_READ_ONLY. */ if (sess->session_info.state == CKS_RW_USER_FUNCTIONS || sess->session_info.state == CKS_RW_PUBLIC_SESSION) { if (not_initialized) { if (memcmp(oldpin_hash, default_user_pin_sha, SHA1_DIGEST_LENGTH)) { return (CKR_PIN_INCORRECT); } if ((rc = check_pin_properties(CKU_USER, newpin_hash, ulNewPinLen))) { return (rc); } if ((rc = token_create_private_tree(sess->hContext, newpin_hash))) { return (CKR_FUNCTION_FAILED); } nv_token_data->token_info.flags &= ~(CKF_USER_PIN_TO_BE_CHANGED); nv_token_data->token_info.flags |= CKF_USER_PIN_INITIALIZED; nv_token_data->token_info.flags &= ~(CKF_USER_PIN_TO_BE_CHANGED); nv_token_data->token_info.flags |= CKF_USER_PIN_INITIALIZED; return (save_token_data(nv_token_data)); } if (sess->session_info.state == CKS_RW_USER_FUNCTIONS) { /* if we're already logged in, just verify the hash */ if (memcmp(current_user_pin_sha, oldpin_hash, SHA1_DIGEST_LENGTH)) { return (CKR_PIN_INCORRECT); } } else { if ((rc = verify_user_pin(sess->hContext, oldpin_hash))) { return (rc); } } if ((rc = check_pin_properties(CKU_USER, newpin_hash, ulNewPinLen))) return (rc); /* change the auth on the TSS object */ if (tss_change_auth(sess->hContext, hPrivateLeafKey, hPrivateRootKey, privateLeafKeyUUID, privateRootKeyUUID, newpin_hash)) return (CKR_FUNCTION_FAILED); } else if (sess->session_info.state == CKS_RW_SO_FUNCTIONS) { if (not_initialized) { if (memcmp(default_so_pin_sha, oldpin_hash, SHA1_DIGEST_LENGTH)) return (CKR_PIN_INCORRECT); if ((rc = check_pin_properties(CKU_SO, newpin_hash, ulNewPinLen))) return (rc); if ((rc = token_create_public_tree(sess->hContext, newpin_hash))) return (CKR_FUNCTION_FAILED); nv_token_data->token_info.flags &= ~(CKF_SO_PIN_TO_BE_CHANGED); return (save_token_data(nv_token_data)); } if (memcmp(current_so_pin_sha, oldpin_hash, SHA1_DIGEST_LENGTH)) return (CKR_PIN_INCORRECT); if ((rc = check_pin_properties(CKU_SO, newpin_hash, ulNewPinLen))) return (rc); /* change auth on the SO's leaf key */ if (tss_change_auth(sess->hContext, hPublicLeafKey, hPublicRootKey, publicLeafKeyUUID, publicRootKeyUUID, newpin_hash)) return (CKR_FUNCTION_FAILED); } else { rc = CKR_SESSION_READ_ONLY; } return (rc); } /* only called at token init time */ CK_RV token_specific_verify_so_pin(TSS_HCONTEXT hContext, CK_CHAR_PTR pPin, CK_ULONG ulPinLen) { CK_BYTE hash_sha[SHA1_DIGEST_LENGTH]; CK_RV rc; TSS_RESULT result; TSS_HKEY hSRK; if ((rc = compute_sha(pPin, ulPinLen, hash_sha))) { return (CKR_FUNCTION_FAILED); } if ((rc = token_load_srk(hContext, &hSRK))) { return (CKR_FUNCTION_FAILED); } /* * TRYME INSTEAD: * - find publicRootKeyUUID * - Load publicRootKey by UUID (SRK parent) * - find publicLeafKeyUUID * - Load publicLeafKey by UUID (publicRootKey parent) * - set password policy on publicLeafKey */ if (local_uuid_is_null(&publicRootKeyUUID) && find_uuid(TPMTOK_PUBLIC_ROOT_KEY_ID, &publicRootKeyUUID)) { /* * The SO hasn't set her PIN yet, compare the * login pin with the hard-coded value. */ if (memcmp(default_so_pin_sha, hash_sha, SHA1_DIGEST_LENGTH)) { return (CKR_PIN_INCORRECT); } return (CKR_OK); } result = Tspi_Context_GetKeyByUUID(hContext, TSS_PS_TYPE_USER, publicRootKeyUUID, &hPublicRootKey); if (result) return (CKR_FUNCTION_FAILED); result = Tspi_Key_LoadKey(hPublicRootKey, hSRK); if (result) return (CKR_FUNCTION_FAILED); if (local_uuid_is_null(&publicLeafKeyUUID) && find_uuid(TPMTOK_PUBLIC_LEAF_KEY_ID, &publicLeafKeyUUID)) return (CKR_FUNCTION_FAILED); result = Tspi_Context_GetKeyByUUID(hContext, TSS_PS_TYPE_USER, publicLeafKeyUUID, &hPublicLeafKey); if (result) return (CKR_FUNCTION_FAILED); result = tss_assign_secret_key_policy(hContext, TSS_POLICY_USAGE, hPublicLeafKey, hash_sha); if (result) return (CKR_FUNCTION_FAILED); result = Tspi_Key_LoadKey(hPublicLeafKey, hPublicRootKey); if (result) return (CKR_FUNCTION_FAILED); /* If the hash given is wrong, the verify will fail */ if ((rc = token_verify_pin(hContext, hPublicLeafKey))) { return (rc); } return (CKR_OK); } CK_RV token_specific_final(TSS_HCONTEXT hContext) { if (hPublicRootKey != NULL_HKEY) { Tspi_Context_CloseObject(hContext, hPublicRootKey); hPublicRootKey = NULL_HKEY; } if (hPublicLeafKey != NULL_HKEY) { Tspi_Context_CloseObject(hContext, hPublicLeafKey); hPublicLeafKey = NULL_HKEY; } if (hPrivateRootKey != NULL_HKEY) { Tspi_Context_CloseObject(hContext, hPrivateRootKey); hPrivateRootKey = NULL_HKEY; } if (hPrivateLeafKey != NULL_HKEY) { Tspi_Context_CloseObject(hContext, hPrivateLeafKey); hPrivateLeafKey = NULL_HKEY; } return (CKR_OK); } /* * Wrap the 20 bytes of auth data and store in an attribute of the two * keys. */ static CK_RV token_wrap_auth_data(TSS_HCONTEXT hContext, CK_BYTE *authData, TEMPLATE *publ_tmpl, TEMPLATE *priv_tmpl) { CK_RV rc; CK_ATTRIBUTE *new_attr; TSS_RESULT ret; TSS_HKEY hParentKey; TSS_HENCDATA hEncData; BYTE *blob; UINT32 blob_size; if ((hPrivateLeafKey == NULL_HKEY) && (hPublicLeafKey == NULL_HKEY)) { return (CKR_FUNCTION_FAILED); } else if (hPublicLeafKey != NULL_HKEY) { hParentKey = hPublicLeafKey; } else { hParentKey = hPrivateLeafKey; } /* create the encrypted data object */ if ((ret = Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_ENCDATA, TSS_ENCDATA_BIND, &hEncData))) { stlogit("Tspi_Context_CreateObject: 0x%0x - %s", ret, Trspi_Error_String(ret)); return (CKR_FUNCTION_FAILED); } if ((ret = Tspi_Data_Bind(hEncData, hParentKey, SHA1_DIGEST_LENGTH, authData))) { stlogit("Tspi_Data_Bind: 0x%0x - %s", ret, Trspi_Error_String(ret)); return (CKR_FUNCTION_FAILED); } /* pull the encrypted data out of the encrypted data object */ if ((ret = Tspi_GetAttribData(hEncData, TSS_TSPATTRIB_ENCDATA_BLOB, TSS_TSPATTRIB_ENCDATABLOB_BLOB, &blob_size, &blob))) { stlogit("Tspi_SetAttribData: 0x%0x - %s", ret, Trspi_Error_String(ret)); return (CKR_FUNCTION_FAILED); } if ((rc = build_attribute(CKA_ENC_AUTHDATA, blob, blob_size, &new_attr))) { return (rc); } (void) template_update_attribute(publ_tmpl, new_attr); if ((rc = build_attribute(CKA_ENC_AUTHDATA, blob, blob_size, &new_attr))) { return (rc); } (void) template_update_attribute(priv_tmpl, new_attr); return (rc); } static CK_RV token_unwrap_auth_data(TSS_HCONTEXT hContext, CK_BYTE *encAuthData, CK_ULONG encAuthDataLen, TSS_HKEY hKey, BYTE **authData) { TSS_RESULT result; TSS_HENCDATA hEncData; BYTE *buf; UINT32 buf_size; if ((result = Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_ENCDATA, TSS_ENCDATA_BIND, &hEncData))) { stlogit("Tspi_Context_CreateObject: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } if ((result = Tspi_SetAttribData(hEncData, TSS_TSPATTRIB_ENCDATA_BLOB, TSS_TSPATTRIB_ENCDATABLOB_BLOB, encAuthDataLen, encAuthData))) { stlogit("Tspi_SetAttribData: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } /* unbind the data, receiving the plaintext back */ if ((result = Tspi_Data_Unbind(hEncData, hKey, &buf_size, &buf))) { stlogit("Tspi_Data_Unbind: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } if (buf_size != SHA1_DIGEST_LENGTH) { return (CKR_FUNCTION_FAILED); } *authData = buf; return (CKR_OK); } CK_RV token_specific_rsa_generate_keypair( TSS_HCONTEXT hContext, TEMPLATE *publ_tmpl, TEMPLATE *priv_tmpl) { CK_ATTRIBUTE *attr = NULL; CK_ULONG mod_bits = 0; CK_BBOOL flag; CK_RV rc; TSS_FLAG initFlags = 0; BYTE authHash[SHA1_DIGEST_LENGTH]; BYTE *authData = NULL; TSS_HKEY hKey = NULL_HKEY; TSS_HKEY hParentKey = NULL_HKEY; TSS_RESULT result; UINT32 ulBlobLen; BYTE *rgbBlob; /* Make sure the public exponent is usable */ if ((util_check_public_exponent(publ_tmpl))) { return (CKR_TEMPLATE_INCONSISTENT); } flag = template_attribute_find(publ_tmpl, CKA_MODULUS_BITS, &attr); if (!flag) { return (CKR_TEMPLATE_INCOMPLETE); } mod_bits = *(CK_ULONG *)attr->pValue; if ((initFlags = util_get_keysize_flag(mod_bits)) == 0) { return (CKR_KEY_SIZE_RANGE); } /* * If we're not logged in, hPrivateLeafKey and hPublicLeafKey * should be NULL. */ if ((hPrivateLeafKey == NULL_HKEY) && (hPublicLeafKey == NULL_HKEY)) { /* public session, wrap key with the PRK */ initFlags |= TSS_KEY_TYPE_LEGACY | TSS_KEY_NO_AUTHORIZATION | TSS_KEY_MIGRATABLE; if ((result = token_load_public_root_key(hContext))) { return (CKR_FUNCTION_FAILED); } hParentKey = hPublicRootKey; } else if (hPrivateLeafKey != NULL_HKEY) { /* logged in USER session */ initFlags |= TSS_KEY_TYPE_LEGACY | TSS_KEY_AUTHORIZATION | TSS_KEY_MIGRATABLE; /* get a random SHA1 hash for the auth data */ if ((rc = token_rng(hContext, authHash, SHA1_DIGEST_LENGTH))) { return (CKR_FUNCTION_FAILED); } authData = authHash; hParentKey = hPrivateRootKey; } else { /* logged in SO session */ initFlags |= TSS_KEY_TYPE_LEGACY | TSS_KEY_AUTHORIZATION | TSS_KEY_MIGRATABLE; /* get a random SHA1 hash for the auth data */ if ((rc = token_rng(hContext, authHash, SHA1_DIGEST_LENGTH))) { return (CKR_FUNCTION_FAILED); } authData = authHash; hParentKey = hPublicRootKey; } if ((result = tss_generate_key(hContext, initFlags, authData, hParentKey, &hKey))) { return (result); } if ((result = Tspi_GetAttribData(hKey, TSS_TSPATTRIB_KEY_BLOB, TSS_TSPATTRIB_KEYBLOB_BLOB, &ulBlobLen, &rgbBlob))) { stlogit("Tspi_GetAttribData: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } if ((rc = build_attribute(CKA_IBM_OPAQUE, rgbBlob, ulBlobLen, &attr))) { Tspi_Context_FreeMemory(hContext, rgbBlob); return (rc); } (void) template_update_attribute(priv_tmpl, attr); if ((rc = build_attribute(CKA_IBM_OPAQUE, rgbBlob, ulBlobLen, &attr))) { Tspi_Context_FreeMemory(hContext, rgbBlob); return (rc); } (void) template_update_attribute(publ_tmpl, attr); Tspi_Context_FreeMemory(hContext, rgbBlob); /* grab the public key to put into the public key object */ if ((result = Tspi_GetAttribData(hKey, TSS_TSPATTRIB_RSAKEY_INFO, TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, &ulBlobLen, &rgbBlob))) { stlogit("Tspi_GetAttribData: 0x%0x - %s", result, Trspi_Error_String(result)); return (result); } /* add the public key blob to the object template */ if ((rc = build_attribute(CKA_MODULUS, rgbBlob, ulBlobLen, &attr))) { Tspi_Context_FreeMemory(hContext, rgbBlob); return (rc); } (void) template_update_attribute(publ_tmpl, attr); /* add the public key blob to the object template */ if ((rc = build_attribute(CKA_MODULUS, rgbBlob, ulBlobLen, &attr))) { Tspi_Context_FreeMemory(hContext, rgbBlob); return (rc); } (void) template_update_attribute(priv_tmpl, attr); Tspi_Context_FreeMemory(hContext, rgbBlob); /* wrap the authdata and put it into an object */ if (authData != NULL) { rc = token_wrap_auth_data(hContext, authData, publ_tmpl, priv_tmpl); } return (rc); } static CK_RV token_rsa_load_key( TSS_HCONTEXT hContext, OBJECT *key_obj, TSS_HKEY *phKey) { TSS_RESULT result; TSS_HPOLICY hPolicy = NULL_HPOLICY; TSS_HKEY hParentKey; BYTE *authData = NULL; CK_ATTRIBUTE *attr; CK_RV rc; CK_OBJECT_HANDLE handle; CK_ULONG class; if (hPrivateLeafKey != NULL_HKEY) { hParentKey = hPrivateRootKey; } else { if ((result = token_load_public_root_key(hContext))) return (CKR_FUNCTION_FAILED); hParentKey = hPublicRootKey; } *phKey = NULL; if (template_attribute_find(key_obj->template, CKA_CLASS, &attr) == FALSE) { return (CKR_TEMPLATE_INCOMPLETE); } class = *((CK_ULONG *)attr->pValue); rc = template_attribute_find(key_obj->template, CKA_IBM_OPAQUE, &attr); /* * A public key cannot use the OPAQUE data attribute so they * must be created in software. A private key may not yet * have its "opaque" data defined and needs to be created * and loaded so it can be used inside the TPM. */ if (class == CKO_PUBLIC_KEY || rc == FALSE) { rc = object_mgr_find_in_map2(hContext, key_obj, &handle); if (rc != CKR_OK) return (CKR_FUNCTION_FAILED); if ((rc = token_load_key(hContext, handle, hParentKey, NULL, phKey))) { return (rc); } } /* * If this is a private key, get the blob and load it in the TPM. * If it is public, the key is already loaded in software. */ if (class == CKO_PRIVATE_KEY) { /* If we already have a handle, just load it */ if (*phKey != NULL) { result = Tspi_Key_LoadKey(*phKey, hParentKey); if (result) { stlogit("Tspi_Context_LoadKeyByBlob: " "0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } } else { /* try again to get the CKA_IBM_OPAQUE attr */ if ((rc = template_attribute_find(key_obj->template, CKA_IBM_OPAQUE, &attr)) == FALSE) { return (rc); } if ((result = Tspi_Context_LoadKeyByBlob(hContext, hParentKey, attr->ulValueLen, attr->pValue, phKey))) { stlogit("Tspi_Context_LoadKeyByBlob: " "0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } } } /* auth data may be required */ if (template_attribute_find(key_obj->template, CKA_ENC_AUTHDATA, &attr) == TRUE && attr) { if ((hPrivateLeafKey == NULL_HKEY) && (hPublicLeafKey == NULL_HKEY)) { return (CKR_FUNCTION_FAILED); } else if (hPublicLeafKey != NULL_HKEY) { hParentKey = hPublicLeafKey; } else { hParentKey = hPrivateLeafKey; } if ((result = token_unwrap_auth_data(hContext, attr->pValue, attr->ulValueLen, hParentKey, &authData))) { return (CKR_FUNCTION_FAILED); } if ((result = Tspi_GetPolicyObject(*phKey, TSS_POLICY_USAGE, &hPolicy))) { stlogit("Tspi_GetPolicyObject: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } /* * If the policy handle returned is the same as the * context's default policy, then a new policy must * be created and assigned to the key. Otherwise, just set the * secret in the policy. */ if (hPolicy == hDefaultPolicy) { if ((result = Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_POLICY, TSS_POLICY_USAGE, &hPolicy))) { stlogit("Tspi_Context_CreateObject: " "0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } if ((result = Tspi_Policy_SetSecret(hPolicy, TSS_SECRET_MODE_SHA1, SHA1_DIGEST_LENGTH, authData))) { stlogit("Tspi_Policy_SetSecret: " "0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } if ((result = Tspi_Policy_AssignToObject(hPolicy, *phKey))) { stlogit("Tspi_Policy_AssignToObject: " "0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } } else if ((result = Tspi_Policy_SetSecret(hPolicy, TSS_SECRET_MODE_SHA1, SHA1_DIGEST_LENGTH, authData))) { stlogit("Tspi_Policy_SetSecret: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } Tspi_Context_FreeMemory(hContext, authData); } return (CKR_OK); } CK_RV tpm_decrypt_data( TSS_HCONTEXT hContext, TSS_HKEY hKey, CK_BYTE * in_data, CK_ULONG in_data_len, CK_BYTE * out_data, CK_ULONG * out_data_len) { TSS_RESULT result; TSS_HENCDATA hEncData = NULL_HENCDATA; UINT32 buf_size = 0, modLen; BYTE *buf = NULL, *modulus = NULL; CK_ULONG chunklen, remain, outlen; /* push the data into the encrypted data object */ if ((result = Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_ENCDATA, TSS_ENCDATA_BIND, &hEncData))) { stlogit("Tspi_Context_CreateObject: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } /* * Figure out the modulus size so we can break the data * into smaller chunks if necessary. */ if ((result = Tspi_GetAttribData(hKey, TSS_TSPATTRIB_RSAKEY_INFO, TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, &modLen, &modulus))) { stlogit("Tspi_GetAttribData: 0x%0x - %s", result, Trspi_Error_String(result)); return (result); } /* we don't need the actual modulus */ Tspi_Context_FreeMemory(hContext, modulus); chunklen = (in_data_len > modLen ? modLen : in_data_len); remain = in_data_len; outlen = 0; while (remain > 0) { if ((result = Tspi_SetAttribData(hEncData, TSS_TSPATTRIB_ENCDATA_BLOB, TSS_TSPATTRIB_ENCDATABLOB_BLOB, chunklen, in_data))) { stlogit("Tspi_SetAttribData: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } /* unbind the data, receiving the plaintext back */ if ((result = Tspi_Data_Unbind(hEncData, hKey, &buf_size, &buf))) { stlogit("Tspi_Data_Unbind: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } if (*out_data_len < buf_size + outlen) { Tspi_Context_FreeMemory(hContext, buf); return (CKR_BUFFER_TOO_SMALL); } (void) memcpy(out_data + outlen, buf, buf_size); outlen += buf_size; in_data += chunklen; remain -= chunklen; Tspi_Context_FreeMemory(hContext, buf); if (chunklen > remain) chunklen = remain; } *out_data_len = outlen; return (CKR_OK); } CK_RV token_specific_rsa_decrypt( TSS_HCONTEXT hContext, CK_BYTE * in_data, CK_ULONG in_data_len, CK_BYTE * out_data, CK_ULONG * out_data_len, OBJECT * key_obj) { CK_RV rc; TSS_HKEY hKey; if ((rc = token_rsa_load_key(hContext, key_obj, &hKey))) { return (rc); } rc = tpm_decrypt_data(hContext, hKey, in_data, in_data_len, out_data, out_data_len); return (rc); } CK_RV token_specific_rsa_verify( TSS_HCONTEXT hContext, CK_BYTE * in_data, CK_ULONG in_data_len, CK_BYTE * sig, CK_ULONG sig_len, OBJECT * key_obj) { TSS_RESULT result; TSS_HHASH hHash; TSS_HKEY hKey; CK_RV rc; if ((rc = token_rsa_load_key(hContext, key_obj, &hKey))) { return (rc); } /* Create the hash object we'll use to sign */ if ((result = Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_HASH, TSS_HASH_OTHER, &hHash))) { stlogit("Tspi_Context_CreateObject: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } /* Insert the data into the hash object */ if ((result = Tspi_Hash_SetHashValue(hHash, in_data_len, in_data))) { stlogit("Tspi_Hash_SetHashValue: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } /* Verify */ result = Tspi_Hash_VerifySignature(hHash, hKey, sig_len, sig); if (result != TSS_SUCCESS && TPMTOK_TSS_ERROR_CODE(result) != TSS_E_FAIL) { stlogit("Tspi_Hash_VerifySignature: 0x%0x - %s", result, Trspi_Error_String(result)); } if (TPMTOK_TSS_ERROR_CODE(result) == TSS_E_FAIL) { rc = CKR_SIGNATURE_INVALID; } else { rc = CKR_OK; } return (rc); } CK_RV token_specific_rsa_sign( TSS_HCONTEXT hContext, CK_BYTE * in_data, CK_ULONG in_data_len, CK_BYTE * out_data, CK_ULONG * out_data_len, OBJECT * key_obj) { TSS_RESULT result; TSS_HHASH hHash; BYTE *sig; UINT32 sig_len; TSS_HKEY hKey; CK_RV rc; if ((rc = token_rsa_load_key(hContext, key_obj, &hKey))) { return (rc); } /* Create the hash object we'll use to sign */ if ((result = Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_HASH, TSS_HASH_OTHER, &hHash))) { stlogit("Tspi_Context_CreateObject: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } /* Insert the data into the hash object */ if ((result = Tspi_Hash_SetHashValue(hHash, in_data_len, in_data))) { stlogit("Tspi_Hash_SetHashValue: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } /* Sign */ if ((result = Tspi_Hash_Sign(hHash, hKey, &sig_len, &sig))) { stlogit("Tspi_Hash_Sign: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_DATA_LEN_RANGE); } if (sig_len > *out_data_len) { Tspi_Context_FreeMemory(hContext, sig); return (CKR_BUFFER_TOO_SMALL); } (void) memcpy(out_data, sig, sig_len); *out_data_len = sig_len; Tspi_Context_FreeMemory(hContext, sig); return (CKR_OK); } CK_RV tpm_encrypt_data( TSS_HCONTEXT hContext, TSS_HKEY hKey, CK_BYTE *in_data, CK_ULONG in_data_len, CK_BYTE *out_data, CK_ULONG *out_data_len) { TSS_RESULT result; TSS_HENCDATA hEncData; BYTE *dataBlob, *modulus; UINT32 dataBlobSize, modLen; CK_ULONG chunklen, remain; CK_ULONG outlen; UINT32 keyusage, scheme, maxsize; if ((result = Tspi_Context_CreateObject(hContext, TSS_OBJECT_TYPE_ENCDATA, TSS_ENCDATA_BIND, &hEncData))) { stlogit("Tspi_Context_CreateObject: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } /* * Figure out the modulus size so we can break the data * into smaller chunks if necessary. */ if ((result = Tspi_GetAttribData(hKey, TSS_TSPATTRIB_RSAKEY_INFO, TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, &modLen, &modulus))) { stlogit("Tspi_GetAttribData: 0x%0x - %s", result, Trspi_Error_String(result)); return (result); } /* we don't need the actual modulus */ Tspi_Context_FreeMemory(hContext, modulus); /* * According to TSS spec for Tspi_Data_Bind (4.3.4.21.5), * Max input data size varies depending on the key type and * encryption scheme. */ if ((result = Tspi_GetAttribUint32(hKey, TSS_TSPATTRIB_KEY_INFO, TSS_TSPATTRIB_KEYINFO_USAGE, &keyusage))) { stlogit("Cannot find USAGE: %s\n", Trspi_Error_String(result)); return (result); } if ((result = Tspi_GetAttribUint32(hKey, TSS_TSPATTRIB_KEY_INFO, TSS_TSPATTRIB_KEYINFO_ENCSCHEME, &scheme))) { stlogit("Cannot find ENCSCHEME: %s\n", Trspi_Error_String(result)); return (result); } switch (scheme) { case TSS_ES_RSAESPKCSV15: if (keyusage == TSS_KEYUSAGE_BIND) maxsize = 16; else /* legacy */ maxsize = 11; break; case TSS_ES_RSAESOAEP_SHA1_MGF1: maxsize = 47; break; default: maxsize = 0; } modLen -= maxsize; chunklen = (in_data_len > modLen ? modLen : in_data_len); remain = in_data_len; outlen = 0; while (remain > 0) { if ((result = Tspi_Data_Bind(hEncData, hKey, chunklen, in_data))) { stlogit("Tspi_Data_Bind: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } if ((result = Tspi_GetAttribData(hEncData, TSS_TSPATTRIB_ENCDATA_BLOB, TSS_TSPATTRIB_ENCDATABLOB_BLOB, &dataBlobSize, &dataBlob))) { stlogit("Tspi_GetAttribData: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } if (outlen + dataBlobSize > *out_data_len) { Tspi_Context_FreeMemory(hContext, dataBlob); return (CKR_DATA_LEN_RANGE); } (void) memcpy(out_data + outlen, dataBlob, dataBlobSize); outlen += dataBlobSize; in_data += chunklen; remain -= chunklen; if (chunklen > remain) chunklen = remain; Tspi_Context_FreeMemory(hContext, dataBlob); } *out_data_len = outlen; return (CKR_OK); } CK_RV token_specific_rsa_encrypt( TSS_HCONTEXT hContext, CK_BYTE * in_data, CK_ULONG in_data_len, CK_BYTE * out_data, CK_ULONG * out_data_len, OBJECT * key_obj) { TSS_HKEY hKey; CK_RV rc; if ((rc = token_rsa_load_key(hContext, key_obj, &hKey))) { return (rc); } rc = tpm_encrypt_data(hContext, hKey, in_data, in_data_len, out_data, out_data_len); return (rc); } /* * RSA Verify Recover * * Public key crypto is done in software, not by the TPM. * We bypass the TSPI library here in favor of calls directly * to OpenSSL because we don't want to add any padding, the in_data (signature) * already contains the data stream to be decrypted and is already * padded and formatted correctly. */ CK_RV token_specific_rsa_verify_recover( TSS_HCONTEXT hContext, CK_BYTE *in_data, /* signature */ CK_ULONG in_data_len, CK_BYTE *out_data, /* decrypted */ CK_ULONG *out_data_len, OBJECT *key_obj) { TSS_HKEY hKey; TSS_RESULT result; CK_RV rc; BYTE *modulus; UINT32 modLen; RSA *rsa = NULL; uchar_t exp[] = { 0x01, 0x00, 0x01 }; int sslrv, num; BYTE temp[MAX_RSA_KEYLENGTH]; BYTE outdata[MAX_RSA_KEYLENGTH]; int i; if ((rc = token_rsa_load_key(hContext, key_obj, &hKey))) { return (rc); } if ((result = Tspi_GetAttribData(hKey, TSS_TSPATTRIB_RSAKEY_INFO, TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, &modLen, &modulus))) { stlogit("Tspi_GetAttribData: 0x%0x - %s", result, Trspi_Error_String(result)); return (CKR_FUNCTION_FAILED); } if (in_data_len != modLen) { rc = CKR_SIGNATURE_LEN_RANGE; goto end; } rsa = RSA_new(); if (rsa == NULL) { rc = CKR_HOST_MEMORY; goto end; } rsa->n = BN_bin2bn(modulus, modLen, rsa->n); rsa->e = BN_bin2bn(exp, sizeof (exp), rsa->e); if (rsa->n == NULL || rsa->e == NULL) { rc = CKR_HOST_MEMORY; goto end; } rsa->flags |= RSA_FLAG_SIGN_VER; /* use RSA_NO_PADDING because the data is already padded (PKCS1) */ sslrv = RSA_public_encrypt(in_data_len, in_data, outdata, rsa, RSA_NO_PADDING); if (sslrv == -1) { rc = CKR_FUNCTION_FAILED; goto end; } /* Strip leading 0's before stripping the padding */ for (i = 0; i < sslrv; i++) if (outdata[i] != 0) break; num = BN_num_bytes(rsa->n); /* Use OpenSSL function for stripping PKCS#1 padding */ sslrv = RSA_padding_check_PKCS1_type_1(temp, sizeof (temp), &outdata[i], sslrv - i, num); if (sslrv < 0) { rc = CKR_FUNCTION_FAILED; goto end; } if (*out_data_len < sslrv) { rc = CKR_BUFFER_TOO_SMALL; *out_data_len = 0; goto end; } /* The return code indicates the number of bytes remaining */ (void) memcpy(out_data, temp, sslrv); *out_data_len = sslrv; end: Tspi_Context_FreeMemory(hContext, modulus); if (rsa) RSA_free(rsa); return (rc); }