/* * 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. */ /* (C) COPYRIGHT International Business Machines Corp. 2001, 2002, 2005 */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include "tpmtok_int.h" static CK_SLOT_INFO slot_info; // Function: dlist_add_as_first() // // Adds the specified node to the start of the list // // Returns: pointer to the start of the list // DL_NODE * dlist_add_as_first(DL_NODE *list, void *data) { DL_NODE *node = NULL; if (! data) return (list); node = (DL_NODE *)malloc(sizeof (DL_NODE)); if (! node) return (NULL); node->data = data; node->prev = NULL; node->next = list; if (list) list->prev = node; return (node); } // Function: dlist_add_as_last() // // Adds the specified node to the end of the list // // Returns: pointer to the start of the list // DL_NODE * dlist_add_as_last(DL_NODE *list, void *data) { DL_NODE *node = NULL; if (! data) return (list); node = (DL_NODE *)malloc(sizeof (DL_NODE)); if (! node) return (NULL); node->data = data; node->next = NULL; if (! list) { node->prev = NULL; return (node); } else { DL_NODE *temp = dlist_get_last(list); temp->next = node; node->prev = temp; return (list); } } // Function: dlist_find() // DL_NODE * dlist_find(DL_NODE *list, void *data) { DL_NODE *node = list; while (node && node->data != data) node = node->next; return (node); } // Function: dlist_get_first() // // Returns the last node in the list or NULL if list is empty // DL_NODE * dlist_get_first(DL_NODE *list) { DL_NODE *temp = list; if (! list) return (NULL); while (temp->prev != NULL) temp = temp->prev; return (temp); } // Function: dlist_get_last() // // Returns the last node in the list or NULL if list is empty // DL_NODE * dlist_get_last(DL_NODE *list) { DL_NODE *temp = list; if (! list) return (NULL); while (temp->next != NULL) temp = temp->next; return (temp); } // // CK_ULONG dlist_length(DL_NODE *list) { DL_NODE *temp = list; CK_ULONG len = 0; while (temp) { len++; temp = temp->next; } return (len); } // // DL_NODE * dlist_next(DL_NODE *node) { if (! node) return (NULL); return (node->next); } // // DL_NODE * dlist_prev(DL_NODE *node) { if (! node) return (NULL); return (node->prev); } // // void dlist_purge(DL_NODE *list) { DL_NODE *node; if (! list) return; do { node = list->next; free(list); list = node; } while (list); } // Function: dlist_remove_node() // // Attempts to remove the specified node from the list. The caller is // responsible for freeing the data associated with the node prior to // calling this routine // DL_NODE * dlist_remove_node(DL_NODE *list, DL_NODE *node) { DL_NODE *temp = list; if (! list || ! node) return (NULL); // special case: removing head of the list // if (list == node) { temp = list->next; if (temp) temp->prev = NULL; free(list); return (temp); } // we have no guarantee that the node is in the list // so search through the list to find it // while ((temp != NULL) && (temp->next != node)) temp = temp->next; if (temp != NULL) { DL_NODE *next = node->next; temp->next = next; if (next) next->prev = temp; free(node); } return (list); } extern void set_perm(int); void CreateXProcLock(void *xproc) { pthread_mutexattr_t mtxattr; (void) pthread_mutexattr_init(&mtxattr); (void) pthread_mutexattr_setpshared(&mtxattr, PTHREAD_PROCESS_SHARED); (void) pthread_mutex_init((pthread_mutex_t *)xproc, &mtxattr); } int DestroyXProcLock(void *xproc) { return (pthread_mutex_destroy((pthread_mutex_t *)xproc)); } int XProcLock(void *xproc) { return (pthread_mutex_lock((pthread_mutex_t *)xproc)); } int XProcUnLock(void *xproc) { return (pthread_mutex_unlock((pthread_mutex_t *)xproc)); } // // // is_attribute_defined() // // determine whether the specified attribute is defined by Cryptoki // CK_BBOOL is_attribute_defined(CK_ATTRIBUTE_TYPE type) { if (type >= CKA_VENDOR_DEFINED) return (TRUE); switch (type) { case CKA_CLASS: case CKA_TOKEN: case CKA_PRIVATE: case CKA_LABEL: case CKA_APPLICATION: case CKA_VALUE: case CKA_CERTIFICATE_TYPE: case CKA_ISSUER: case CKA_SERIAL_NUMBER: case CKA_KEY_TYPE: case CKA_SUBJECT: case CKA_ID: case CKA_SENSITIVE: case CKA_ENCRYPT: case CKA_DECRYPT: case CKA_WRAP: case CKA_UNWRAP: case CKA_SIGN: case CKA_SIGN_RECOVER: case CKA_VERIFY: case CKA_VERIFY_RECOVER: case CKA_DERIVE: case CKA_START_DATE: case CKA_END_DATE: case CKA_MODULUS: case CKA_MODULUS_BITS: case CKA_PUBLIC_EXPONENT: case CKA_PRIVATE_EXPONENT: case CKA_PRIME_1: case CKA_PRIME_2: case CKA_EXPONENT_1: case CKA_EXPONENT_2: case CKA_COEFFICIENT: case CKA_PRIME: case CKA_SUBPRIME: case CKA_BASE: case CKA_VALUE_BITS: case CKA_VALUE_LEN: case CKA_EXTRACTABLE: case CKA_LOCAL: case CKA_NEVER_EXTRACTABLE: case CKA_ALWAYS_SENSITIVE: case CKA_MODIFIABLE: case CKA_ECDSA_PARAMS: case CKA_EC_POINT: case CKA_HW_FEATURE_TYPE: case CKA_HAS_RESET: case CKA_RESET_ON_INIT: case CKA_KEY_GEN_MECHANISM: case CKA_PRIME_BITS: case CKA_SUBPRIME_BITS: case CKA_OBJECT_ID: case CKA_AC_ISSUER: case CKA_OWNER: case CKA_ATTR_TYPES: case CKA_TRUSTED: return (TRUE); } return (FALSE); } void init_slot_info(TOKEN_DATA *td) { /* * Much of the token info is pulled from the TPM itself when * C_Initialize is called. */ (void) (void) memset(&slot_info.slotDescription, ' ', sizeof (slot_info.slotDescription) - 1); (void) (void) memset(&slot_info.manufacturerID, ' ', sizeof (slot_info.manufacturerID) - 1); (void) (void) memcpy(&slot_info.slotDescription, "PKCS#11 Interface for TPM", strlen("PKCS#11 Interface for TPM")); (void) (void) memcpy(&slot_info.manufacturerID, td->token_info.manufacturerID, strlen((char *)td->token_info.manufacturerID)); slot_info.hardwareVersion = nv_token_data->token_info.hardwareVersion; slot_info.firmwareVersion = nv_token_data->token_info.firmwareVersion; slot_info.flags = CKF_TOKEN_PRESENT | CKF_HW_SLOT; } /*ARGSUSED*/ void copy_slot_info(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR sinfo) { if (sinfo != NULL) (void) memcpy(sinfo, &slot_info, sizeof (slot_info)); } static void init_token_info(TOKEN_DATA *td) { CK_TOKEN_INFO *token_info = NULL; token_info = &td->token_info; (void) memset(token_info->model, ' ', sizeof (token_info->model)); (void) memset(token_info->serialNumber, ' ', sizeof (token_info->serialNumber)); // // I don't see any API support for changing the clock so // we will use the system clock for the token's clock. // token_info->flags = CKF_RNG | CKF_LOGIN_REQUIRED | CKF_CLOCK_ON_TOKEN | CKF_SO_PIN_TO_BE_CHANGED; if (memcmp(td->user_pin_sha, "00000000000000000000", SHA1_DIGEST_LENGTH) != 0) token_info->flags |= CKF_USER_PIN_INITIALIZED; else token_info->flags |= CKF_USER_PIN_TO_BE_CHANGED; // For the release, we made these // values as CK_UNAVAILABLE_INFORMATION // token_info->ulMaxSessionCount = (CK_ULONG)CK_UNAVAILABLE_INFORMATION; token_info->ulSessionCount = (CK_ULONG)CK_UNAVAILABLE_INFORMATION; token_info->ulMaxRwSessionCount = (CK_ULONG)CK_UNAVAILABLE_INFORMATION; token_info->ulRwSessionCount = (CK_ULONG)CK_UNAVAILABLE_INFORMATION; token_info->ulMaxPinLen = MAX_PIN_LEN; token_info->ulMinPinLen = MIN_PIN_LEN; token_info->ulTotalPublicMemory = (CK_ULONG)CK_UNAVAILABLE_INFORMATION; token_info->ulFreePublicMemory = (CK_ULONG)CK_UNAVAILABLE_INFORMATION; token_info->ulTotalPrivateMemory = (CK_ULONG)CK_UNAVAILABLE_INFORMATION; token_info->ulFreePrivateMemory = (CK_ULONG)CK_UNAVAILABLE_INFORMATION; (void) memset(token_info->utcTime, ' ', sizeof (token_info->utcTime)); } CK_RV init_token_data(TSS_HCONTEXT hContext, TOKEN_DATA *td) { CK_RV rc; (void) memset((char *)td, 0, sizeof (nv_token_data)); // // the normal USER pin is not set when the token is initialized // (void) memcpy(td->user_pin_sha, "00000000000000000000", SHA1_DIGEST_LENGTH); (void) memcpy(td->so_pin_sha, default_so_pin_sha, SHA1_DIGEST_LENGTH); (void) memset(user_pin_md5, 0x0, MD5_DIGEST_LENGTH); (void) memcpy(so_pin_md5, default_so_pin_md5, MD5_DIGEST_LENGTH); (void) memcpy(td->next_token_object_name, "00000000", 8); td->tweak_vector.allow_key_mods = TRUE; init_token_info(td); rc = token_get_tpm_info(hContext, td); if (rc != CKR_OK) return (rc); rc = save_token_data(td); return (rc); } // Function: compute_next_token_obj_name() // // Given a token object name (8 bytes in the range [0 - 9A - Z]) // increment by one adjusting as necessary // // This gives us a namespace of 36^8 = 2, 821, 109, 907, 456 // objects before wrapping around. // CK_RV compute_next_token_obj_name(CK_BYTE *current, CK_BYTE *next) { int val[8]; int i; if (! current || ! next) { return (CKR_FUNCTION_FAILED); } // Convert to integral base 36 // for (i = 0; i < 8; i++) { if (current[i] >= '0' && current[i] <= '9') val[i] = current[i] - '0'; if (current[i] >= 'A' && current[i] <= 'Z') val[i] = current[i] - 'A' + 10; } val[0]++; i = 0; while (val[i] > 35) { val[i] = 0; if (i + 1 < 8) { val[i + 1]++; i++; } else { val[0]++; i = 0; // start pass 2 } } // now, convert back to [0 - 9A - Z] // for (i = 0; i < 8; i++) { if (val[i] < 10) next[i] = '0' + val[i]; else next[i] = 'A' + val[i] - 10; } return (CKR_OK); } // // CK_RV build_attribute(CK_ATTRIBUTE_TYPE type, CK_BYTE *data, CK_ULONG data_len, CK_ATTRIBUTE **attrib) { CK_ATTRIBUTE *attr = NULL; attr = (CK_ATTRIBUTE *)malloc(sizeof (CK_ATTRIBUTE) + data_len); if (! attr) { return (CKR_DEVICE_MEMORY); } attr->type = type; attr->ulValueLen = data_len; if (data_len > 0) { attr->pValue = (CK_BYTE *)attr + sizeof (CK_ATTRIBUTE); (void) memcpy(attr->pValue, data, data_len); } else attr->pValue = NULL; *attrib = attr; return (CKR_OK); } CK_RV add_pkcs_padding(CK_BYTE * ptr, UINT32 block_size, UINT32 data_len, UINT32 total_len) { UINT32 i, pad_len; CK_BYTE pad_value; pad_len = block_size - (data_len % block_size); pad_value = (CK_BYTE)pad_len; if (data_len + pad_len > total_len) { return (CKR_FUNCTION_FAILED); } for (i = 0; i < pad_len; i++) ptr[i] = pad_value; return (CKR_OK); } CK_RV strip_pkcs_padding( CK_BYTE *ptr, UINT32 total_len, UINT32 *data_len) { CK_BYTE pad_value; pad_value = ptr[total_len - 1]; /* We have 'pad_value' bytes of 'pad_value' appended to the end */ *data_len = total_len - pad_value; return (CKR_OK); } CK_RV remove_leading_zeros(CK_ATTRIBUTE *attr) { CK_BYTE *ptr = NULL; CK_ULONG new_len, i; ptr = attr->pValue; for (i = 0; i < attr->ulValueLen; i++) { if (ptr[i] != 0x0) break; } new_len = attr->ulValueLen - i; (void) memcpy(ptr, ptr + i, new_len); attr->ulValueLen = new_len; return (CKR_OK); } CK_RV parity_is_odd(CK_BYTE b) { b = ((b >> 4) ^ b) & 0x0f; b = ((b >> 2) ^ b) & 0x03; b = ((b >> 1) ^ b) & 0x01; if (b == 1) return (TRUE); else return (FALSE); } CK_RV attach_shm() { if (global_shm != NULL) return (CKR_OK); global_shm = (LW_SHM_TYPE *)calloc(1, sizeof (LW_SHM_TYPE)); if (global_shm == NULL) { return (CKR_HOST_MEMORY); } CreateXProcLock(&global_shm->mutex); xproclock = (void *)&global_shm->mutex; return (CKR_OK); } CK_RV detach_shm() { if (global_shm != NULL) { free(global_shm); global_shm = NULL; } return (CKR_OK); } CK_RV compute_sha(CK_BYTE *data, CK_ULONG_32 len, CK_BYTE * hash) { SHA1_CTX ctx; SHA1Init(&ctx); SHA1Update(&ctx, data, len); SHA1Final(hash, &ctx); return (CKR_OK); }