static void *my_malloc(void *ref, size_t n) { register void *mem; #ifndef DIAG_MEM_ERRORS mem = os_alloc_memory(n, GFP_KERNEL); #else { uint32_t rand; /* are we feeling lucky ? */ os_get_random_bytes(&rand, sizeof(rand)); if ((rand % DIAG_MEM_CONST) == 0) { mem = 0; } else { mem = os_alloc_memory(n, GFP_ATOMIC); } } #endif /* DIAG_MEM_ERRORS */ #ifdef DIAG_MEM sprintf(Diag_msg, "API kmalloc: %p for %d\n", mem, n); LOG_KDIAG(Diag_msg); #endif ref = 0; /* unused param warning */ return mem; }
/*! * Get random data. * * @param user_ctx A user context from #fsl_shw_register_user(). * @param length The number of octets of @a data being requested. * @param data A pointer to a location of @a length octets to where * random data will be returned. * * @return FSL_RETURN_NO_RESOURCE_S A return code of type #fsl_shw_return_t. * FSL_RETURN_OK_S */ fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t * user_ctx, uint32_t length, uint8_t * data) { fsl_shw_return_t return_code = FSL_RETURN_NO_RESOURCE_S; /* Boost up length to cover any 'missing' bytes at end of a word */ uint32_t *buf = os_alloc_memory(length + 3, 0); volatile rng_work_entry_t *work = os_alloc_memory(sizeof(*work), 0); if ((rng_availability != RNG_STATUS_OK) || (buf == NULL) || (work == NULL)) { if (rng_availability != RNG_STATUS_OK) { LOG_KDIAG_ARGS("rng not available: %d\n", rng_availability); } else { LOG_KDIAG_ARGS ("Resource allocation failure: %d or %d bytes", length, sizeof(*work)); } /* Cannot perform function. Clean up and clear out. */ if (buf != NULL) { os_free_memory(buf); } if (work != NULL) { os_free_memory((void *)work); } } else { unsigned blocking = user_ctx->flags & FSL_UCO_BLOCKING_MODE; work->hdr.user_ctx = user_ctx; work->hdr.flags = user_ctx->flags; work->hdr.callback = user_ctx->callback; work->hdr.user_ref = user_ctx->user_ref; work->hdr.postprocess = finish_random; work->length = length; work->data_local = buf; work->data_user = data; RNG_ADD_WORK_ENTRY((rng_work_entry_t *) work); if (blocking) { os_sleep(rng_wait_queue, work->completed != FALSE, FALSE); finish_random((shw_queue_entry_t *) work); return_code = work->hdr.code; os_free_memory((void *)work); } else { return_code = FSL_RETURN_OK_S; } } return return_code; } /* fsl_shw_get_entropy */
/****************************************************************************** * * CAUTION: This function may sleep in low-memory situations, as it uses * kmalloc ( ..., GFP_KERNEL). ******************************************************************************/ sah_Queue *sah_Queue_Construct (void) { sah_Queue *q = (sah_Queue *)os_alloc_memory (sizeof(sah_Queue), GFP_KERNEL); if (q != NULL) { /* Initialise the queue to an empty state. */ q->head = NULL; q->tail = NULL; q->count = 0; } #ifdef DIAG_DRV_QUEUE else { LOG_KDIAG ("kmalloc() failed."); } #endif return q; }
/*! * Add entropy to random number generator. * * @param user_ctx A user context from #fsl_shw_register_user(). * @param length Number of bytes at @a data. * @param data Entropy to add to random number generator. * * @return A return code of type #fsl_shw_return_t. */ fsl_shw_return_t fsl_shw_add_entropy(fsl_shw_uco_t * user_ctx, uint32_t length, uint8_t * data) { fsl_shw_return_t return_code = FSL_RETURN_NO_RESOURCE_S; #if defined(FSL_HAVE_RNGC) /* No Entropy Register in RNGC */ return_code = FSL_RETURN_OK_S; #else uint32_t *local_data = NULL; if (rng_availability == RNG_STATUS_OK) { /* make 32-bit aligned place to hold data */ local_data = os_alloc_memory(length + 3, 0); if (local_data == NULL) { return_code = FSL_RETURN_NO_RESOURCE_S; } else { memcpy(local_data, data, length); /* Copy one word at a time to hardware */ while (TRUE) { register uint32_t *ptr = local_data; RNG_ADD_ENTROPY(*ptr++); if (length <= 4) { break; } length -= 4; } return_code = FSL_RETURN_OK_S; os_free_memory(local_data); } /* else local_data not NULL */ } #endif /* rng_availability is OK */ return return_code; } /* fsl_shw_add_entropy */
fsl_shw_return_t shw_kso_init_data(fsl_shw_uco_t *user_ctx, void **user_data) { int retval = FSL_RETURN_ERROR_S; keystore_data_t *keystore_data = NULL; fsl_shw_pco_t *capabilities = fsl_shw_get_capabilities(user_ctx); uint32_t partition_size; uint32_t slot_count; uint32_t keystore_data_size; uint8_t UMID[16] = { 0x42, 0, 0, 0, 0x43, 0, 0, 0, 0x19, 0, 0, 0, 0x59, 0, 0, 0}; uint32_t permissions = FSL_PERM_TH_R | FSL_PERM_TH_W | FSL_PERM_HD_R | FSL_PERM_HD_W | FSL_PERM_HD_X; /* Look up the size of a partition to see how big to make the keystore */ partition_size = fsl_shw_pco_get_spo_size_bytes(capabilities); /* Calculate the required size of the keystore data structure, based on the * number of keys that can fit in the partition. */ slot_count = partition_size / KEYSTORE_SLOT_SIZE; keystore_data_size = sizeof(keystore_data_t) + slot_count * sizeof(keystore_data_slot_info_t); #ifdef __KERNEL__ keystore_data = os_alloc_memory(keystore_data_size, GFP_KERNEL); #else keystore_data = malloc(keystore_data_size); #endif if (keystore_data == NULL) { retval = FSL_RETURN_NO_RESOURCE_S; goto out; } /* Clear the memory (effectively clear all key assignments) */ memset(keystore_data, 0, keystore_data_size); /* Place the slot information structure directly after the keystore data * structure. */ keystore_data->slot = (keystore_data_slot_info_t *) (keystore_data + 1); keystore_data->slot_count = slot_count; /* Retrieve a secure partition to put the keystore in. */ keystore_data->base_address = fsl_shw_smalloc(user_ctx, partition_size, UMID, permissions); if (keystore_data->base_address == NULL) { retval = FSL_RETURN_NO_RESOURCE_S; goto out; } *user_data = keystore_data; retval = FSL_RETURN_OK_S; out:if (retval != FSL_RETURN_OK_S) { if (keystore_data != NULL) { if (keystore_data->base_address != NULL) fsl_shw_sfree(NULL, keystore_data->base_address); #ifdef __KERNEL__ os_free_memory(keystore_data); #else free(keystore_data); #endif } } return retval; }
/*! * Perform wrapping of a black key from a RED slot (or the PK register) * * @param user_ctx A user context from #fsl_shw_register_user(). * @param[in,out] key_info The information about the key to be which will * be wrapped... key length, slot info, etc. * @param black_key Place to store encrypted key * * @return A return code of type #fsl_shw_return_t. */ static fsl_shw_return_t wrap(fsl_shw_uco_t * user_ctx, fsl_shw_sko_t * key_info, uint8_t * black_key) { fsl_shw_return_t ret = FSL_RETURN_OK_S; fsl_shw_sko_t t_key_info; /* for holding T */ fsl_shw_scco_t t_key_ctx; fsl_shw_sko_t kek_key_info; fsl_shw_scco_t kek_ctx; unsigned original_key_length = key_info->key_length; unsigned rounded_key_length; uint8_t T[T_LENGTH]; uint8_t kek[KEK_LENGTH + 20]; uint8_t *red_key = 0; int red_key_malloced = 0; /* bool */ int pk_was_held = 0; /* bool */ uint8_t saved_pk[21]; uint8_t pk_needs_restoration; /* bool */ di_return_t di_code; ret = check_wrap_key(user_ctx->wrap_key); if (ret != FSL_RETURN_OK_S) { goto out; } if (black_key == NULL) { ret = FSL_RETURN_ERROR_S; goto out; } if (key_info->flags & FSL_SKO_KEY_SELECT_PF_KEY) { if ((key_info->pf_key != FSL_SHW_PF_KEY_PRG) && (key_info->pf_key != FSL_SHW_PF_KEY_IIM_PRG)) { ret = FSL_RETURN_ERROR_S; goto out; } } else { if (!(key_info->flags & FSL_SKO_KEY_ESTABLISHED)) { ret = FSL_RETURN_BAD_FLAG_S; /* not established! */ goto out; } } black_key[ALGORITHM_OFFSET] = key_info->algorithm; #ifndef DO_REPEATABLE_WRAP /* Compute T = RND() */ ret = fsl_shw_get_random(user_ctx, T_LENGTH, T); if (ret != FSL_RETURN_OK_S) { goto out; } #else memcpy(T, T_block, T_LENGTH); #endif /* Compute KEK = SHA256(T | ownerid). */ ret = calc_kek((uint8_t *) & key_info->userid, sizeof(key_info->userid), T, kek); if (ret != FSL_RETURN_OK_S) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG("Calculation of KEK failed\n"); #endif /*DIAG_SECURITY_FUNC */ goto out; } rounded_key_length = ROUND_LENGTH(original_key_length); di_code = dryice_get_programmed_key(saved_pk, 8 * 21); if (di_code != DI_SUCCESS) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG_ARGS("Could not save current PK: %s\n", di_error_string(di_code)); #endif ret = FSL_RETURN_ERROR_S; goto out; } /* * Load KEK into DI PKR. Note that we are NOT permuting it before loading, * so we are using it as though it is a 168-bit key ready for the SCC. */ di_code = dryice_set_programmed_key(kek, 8 * 21, 0); if (di_code == DI_ERR_INUSE) { /* Temporarily reprogram the PK out from under the user */ pk_was_held = 1; dryice_release_programmed_key(); di_code = dryice_set_programmed_key(kek, 8 * 21, 0); } if (di_code != DI_SUCCESS) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG_ARGS("Could not program KEK: %s\n", di_error_string(di_code)); #endif ret = FSL_RETURN_ERROR_S; goto out; } pk_needs_restoration = 1; dryice_release_programmed_key(); /* Find red key */ if (key_info->flags & FSL_SKO_KEY_SELECT_PF_KEY) { black_key[LENGTH_OFFSET] = 21; rounded_key_length = 24; red_key = saved_pk; } else { black_key[LENGTH_OFFSET] = key_info->key_length; red_key = os_alloc_memory(key_info->key_length, 0); if (red_key == NULL) { ret = FSL_RETURN_NO_RESOURCE_S; goto out; } red_key_malloced = 1; ret = fsl_shw_read_key(user_ctx, key_info, red_key); if (ret != FSL_RETURN_OK_S) { goto out; } } #ifdef DIAG_SECURITY_FUNC dump("KEY", red_key, black_key[LENGTH_OFFSET]); #endif /* Compute KEY' = TDES-encrypt(KEK, KEY) */ fsl_shw_sko_init_pf_key(&kek_key_info, FSL_KEY_ALG_TDES, FSL_SHW_PF_KEY_PRG); fsl_shw_sko_set_key_length(&kek_key_info, KEK_LENGTH); fsl_shw_scco_init(&kek_ctx, FSL_KEY_ALG_TDES, FSL_SYM_MODE_CBC); fsl_shw_scco_set_flags(&kek_ctx, FSL_SYM_CTX_LOAD); fsl_shw_scco_set_context(&kek_ctx, (uint8_t *) & key_info->userid); ret = fsl_shw_symmetric_encrypt(user_ctx, &kek_key_info, &kek_ctx, rounded_key_length, red_key, black_key + KEY_PRIME_OFFSET); if (ret != FSL_RETURN_OK_S) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG("Encryption of KEY failed\n"); #endif /*DIAG_SECURITY_FUNC */ goto out; } /* Set up flags info */ black_key[FLAGS_OFFSET] = 0; if (key_info->flags & FSL_SKO_KEY_SW_KEY) { black_key[FLAGS_OFFSET] |= FLAGS_SW_KEY; } #ifdef DIAG_SECURITY_FUNC dump("KEY'", black_key + KEY_PRIME_OFFSET, rounded_key_length); #endif /* Compute and store ICV into Black Key */ ret = calc_icv(T, (uint8_t *) & key_info->userid, sizeof(key_info->userid), black_key, original_key_length, black_key + ICV_OFFSET); if (ret != FSL_RETURN_OK_S) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG("Calculation of ICV failed\n"); #endif /*DIAG_SECURITY_FUNC */ goto out; } /* Compute T' = 3des-enc-ecb(wrap_key, T); Result goes to Black Key */ init_wrap_key(user_ctx->wrap_key, &t_key_info, &t_key_ctx); ret = fsl_shw_symmetric_encrypt(user_ctx, &t_key_info, &t_key_ctx, T_LENGTH, T, black_key + T_PRIME_OFFSET); if (ret != FSL_RETURN_OK_S) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG("Encryption of nonce failed"); #endif goto out; } #ifdef DIAG_SECURITY_FUNC dump("black", black_key, KEY_PRIME_OFFSET + black_key[LENGTH_OFFSET]); #endif out: if (pk_needs_restoration) { dryice_set_programmed_key(saved_pk, 8 * 21, 0); } if (!pk_was_held) { dryice_release_programmed_key(); } if (red_key_malloced) { memset(red_key, 0, key_info->key_length); os_free_memory(red_key); } key_info->key_length = original_key_length; /* Erase tracks of confidential data */ memset(T, 0, T_LENGTH); memset(&t_key_info, 0, sizeof(t_key_info)); memset(&t_key_ctx, 0, sizeof(t_key_ctx)); memset(&kek_key_info, 0, sizeof(kek_key_info)); memset(&kek_ctx, 0, sizeof(kek_ctx)); memset(kek, 0, sizeof(kek)); memset(saved_pk, 0, sizeof(saved_pk)); return ret; } /* wrap */